Export host statistics in prometheus text format on /metrics.
[darkstat] / hosts_db.c
1 /* darkstat 3
2 * copyright (c) 2001-2014 Emil Mikulic.
3 *
4 * hosts_db.c: database of hosts, ports, protocols.
5 *
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
8 */
9
10 #include "cdefs.h"
11 #include "conv.h"
12 #include "decode.h"
13 #include "dns.h"
14 #include "err.h"
15 #include "hosts_db.h"
16 #include "db.h"
17 #include "html.h"
18 #include "ncache.h"
19 #include "now.h"
20 #include "opt.h"
21 #include "str.h"
22
23 #include <netdb.h> /* struct addrinfo */
24 #include <assert.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h> /* memset(), strcmp() */
29 #include <time.h>
30 #include <unistd.h>
31
32 int hosts_db_show_macs = 0;
33
34 /* FIXME: specify somewhere more sane/tunable */
35 #define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */
36
37 typedef uint32_t (hash_func_t)(const struct hashtable *, const void *);
38 typedef void (free_func_t)(struct bucket *);
39 typedef const void * (key_func_t)(const struct bucket *);
40 typedef int (find_func_t)(const struct bucket *, const void *);
41 typedef struct bucket * (make_func_t)(const void *);
42 typedef void (format_cols_func_t)(struct str *);
43 typedef void (format_row_func_t)(struct str *, const struct bucket *);
44
45 struct hashtable {
46 uint8_t bits; /* size of hashtable in bits */
47 uint32_t size, mask;
48 uint32_t count, count_max, count_keep; /* items in table */
49 uint32_t coeff; /* coefficient for Fibonacci hashing */
50 struct bucket **table;
51
52 struct {
53 uint64_t inserts, searches, deletions, rehashes;
54 } stats;
55
56 hash_func_t *hash_func;
57 /* returns hash value of given key (passed as void*) */
58
59 free_func_t *free_func;
60 /* free of bucket payload */
61
62 key_func_t *key_func;
63 /* returns pointer to key of bucket (to pass to hash_func) */
64
65 find_func_t *find_func;
66 /* returns true if given bucket matches key (passed as void*) */
67
68 make_func_t *make_func;
69 /* returns bucket containing new record with key (passed as void*) */
70
71 format_cols_func_t *format_cols_func;
72 /* append table columns to str */
73
74 format_row_func_t *format_row_func;
75 /* format record and append to str */
76 };
77
78 static void hashtable_reduce(struct hashtable *ht);
79 static void hashtable_free(struct hashtable *h);
80
81 #define HOST_BITS 1 /* initial size of hosts table */
82 #define PORT_BITS 1 /* initial size of ports tables */
83 #define PROTO_BITS 1 /* initial size of proto table */
84
85 /* We only use one hosts_db hashtable and this is it. */
86 static struct hashtable *hosts_db = NULL;
87
88 /* phi^-1 (reciprocal of golden ratio) = (sqrt(5) - 1) / 2 */
89 static const double phi_1 =
90 0.61803398874989490252573887119069695472717285156250;
91
92 /* Co-prime of u, using phi^-1 */
93 static uint32_t coprime(const uint32_t u) {
94 return ( (uint32_t)( (double)(u) * phi_1 ) | 1U );
95 }
96
97 /*
98 * This is the "recommended" IPv4 hash function, as seen in FreeBSD's
99 * src/sys/netinet/tcp_hostcache.c 1.1
100 */
101 static uint32_t ipv4_hash(const struct addr *const a) {
102 uint32_t ip = a->ip.v4;
103 return ( (ip) ^ ((ip) >> 7) ^ ((ip) >> 17) );
104 }
105
106 #ifndef s6_addr32
107 # ifdef sun
108 /*
109 * http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/netinet/in.h#130
110 */
111 # define s6_addr32 _S6_un._S6_u32
112 # else
113 /* Covers OpenBSD and FreeBSD. The macro __USE_GNU has
114 * taken care of GNU/Linux and GNU/kfreebsd. */
115 # define s6_addr32 __u6_addr.__u6_addr32
116 # endif
117 #endif
118
119 /*
120 * This is the IPv6 hash function used by FreeBSD in the same file as above,
121 * svn rev 122922.
122 */
123 static uint32_t ipv6_hash(const struct addr *const a) {
124 const struct in6_addr *const ip6 = &(a->ip.v6);
125 return ( ip6->s6_addr32[0] ^ ip6->s6_addr32[1] ^
126 ip6->s6_addr32[2] ^ ip6->s6_addr32[3] );
127 }
128
129 /* ---------------------------------------------------------------------------
130 * hash_func collection
131 */
132 static uint32_t
133 hash_func_host(const struct hashtable *h _unused_, const void *key)
134 {
135 const struct addr *a = key;
136 if (a->family == IPv4)
137 return (ipv4_hash(a));
138 else {
139 assert(a->family == IPv6);
140 return (ipv6_hash(a));
141 }
142 }
143
144 #define CASTKEY(type) (*((const type *)key))
145
146 static uint32_t
147 hash_func_short(const struct hashtable *h, const void *key)
148 {
149 return (CASTKEY(uint16_t) * h->coeff);
150 }
151
152 static uint32_t
153 hash_func_byte(const struct hashtable *h, const void *key)
154 {
155 return (CASTKEY(uint8_t) * h->coeff);
156 }
157
158 /* ---------------------------------------------------------------------------
159 * key_func collection
160 */
161
162 static const void *
163 key_func_host(const struct bucket *b)
164 {
165 return &(b->u.host.addr);
166 }
167
168 static const void *
169 key_func_port_tcp(const struct bucket *b)
170 {
171 return &(b->u.port_tcp.port);
172 }
173
174 static const void *
175 key_func_port_udp(const struct bucket *b)
176 {
177 return &(b->u.port_udp.port);
178 }
179
180 static const void *
181 key_func_ip_proto(const struct bucket *b)
182 {
183 return &(b->u.ip_proto.proto);
184 }
185
186 /* ---------------------------------------------------------------------------
187 * find_func collection
188 */
189
190 static int
191 find_func_host(const struct bucket *b, const void *key)
192 {
193 return (addr_equal(key, &(b->u.host.addr)));
194 }
195
196 static int
197 find_func_port_tcp(const struct bucket *b, const void *key)
198 {
199 return (b->u.port_tcp.port == CASTKEY(uint16_t));
200 }
201
202 static int
203 find_func_port_udp(const struct bucket *b, const void *key)
204 {
205 return (b->u.port_udp.port == CASTKEY(uint16_t));
206 }
207
208 static int
209 find_func_ip_proto(const struct bucket *b, const void *key)
210 {
211 return (b->u.ip_proto.proto == CASTKEY(uint8_t));
212 }
213
214 /* ---------------------------------------------------------------------------
215 * make_func collection
216 */
217
218 #define MAKE_BUCKET(name_bucket, name_content, type) struct { \
219 struct bucket *next; \
220 uint64_t in, out, total; \
221 union { struct type t; } u; } _custom_bucket; \
222 struct bucket *name_bucket = xcalloc(1, sizeof(_custom_bucket)); \
223 struct type *name_content = &(name_bucket->u.type); \
224 name_bucket->next = NULL; \
225 name_bucket->in = name_bucket->out = name_bucket->total = 0;
226
227 static struct bucket *
228 make_func_host(const void *key)
229 {
230 MAKE_BUCKET(b, h, host);
231 h->addr = CASTKEY(struct addr);
232 h->dns = NULL;
233 h->last_seen_mono = 0;
234 memset(&h->mac_addr, 0, sizeof(h->mac_addr));
235 h->ports_tcp = NULL;
236 h->ports_tcp_remote = NULL;
237 h->ports_udp = NULL;
238 h->ports_udp_remote = NULL;
239 h->ip_protos = NULL;
240 return (b);
241 }
242
243 static void
244 free_func_host(struct bucket *b)
245 {
246 struct host *h = &(b->u.host);
247 if (h->dns != NULL) free(h->dns);
248 hashtable_free(h->ports_tcp);
249 hashtable_free(h->ports_tcp_remote);
250 hashtable_free(h->ports_udp);
251 hashtable_free(h->ports_udp_remote);
252 hashtable_free(h->ip_protos);
253 }
254
255 static struct bucket *
256 make_func_port_tcp(const void *key)
257 {
258 MAKE_BUCKET(b, p, port_tcp);
259 p->port = CASTKEY(uint16_t);
260 p->syn = 0;
261 return (b);
262 }
263
264 static struct bucket *
265 make_func_port_udp(const void *key)
266 {
267 MAKE_BUCKET(b, p, port_udp);
268 p->port = CASTKEY(uint16_t);
269 return (b);
270 }
271
272 static struct bucket *
273 make_func_ip_proto(const void *key)
274 {
275 MAKE_BUCKET(b, p, ip_proto);
276 p->proto = CASTKEY(uint8_t);
277 return (b);
278 }
279
280 static void
281 free_func_simple(struct bucket *b _unused_)
282 {
283 /* nop */
284 }
285
286 /* ---------------------------------------------------------------------------
287 * format_func collection (ordered by struct)
288 */
289
290 static void
291 format_cols_host(struct str *buf)
292 {
293 /* FIXME: don't clobber parts of the query string
294 * specifically "full" and "start"
295 * when setting sort direction
296 */
297 str_append(buf,
298 "<table>\n"
299 "<tr>\n"
300 " <th>IP</th>\n"
301 " <th>Hostname</th>\n");
302 if (hosts_db_show_macs) str_append(buf,
303 " <th>MAC Address</th>\n");
304 str_append(buf,
305 " <th><a href=\"?sort=in\">In</a></th>\n"
306 " <th><a href=\"?sort=out\">Out</a></th>\n"
307 " <th><a href=\"?sort=total\">Total</a></th>\n");
308 if (opt_want_lastseen) str_append(buf,
309 " <th><a href=\"?sort=lastseen\">Last seen</a></th>\n");
310 str_append(buf,
311 "</tr>\n");
312 }
313
314 static void
315 format_row_host(struct str *buf, const struct bucket *b)
316 {
317 const char *ip = addr_to_str(&(b->u.host.addr));
318
319 str_appendf(buf,
320 "<tr>\n"
321 " <td><a href=\"./%s/\">%s</a></td>\n"
322 " <td>%s</td>\n",
323 ip, ip,
324 (b->u.host.dns == NULL) ? "" : b->u.host.dns);
325
326 if (hosts_db_show_macs)
327 str_appendf(buf,
328 " <td><tt>%x:%x:%x:%x:%x:%x</tt></td>\n",
329 b->u.host.mac_addr[0],
330 b->u.host.mac_addr[1],
331 b->u.host.mac_addr[2],
332 b->u.host.mac_addr[3],
333 b->u.host.mac_addr[4],
334 b->u.host.mac_addr[5]);
335
336 str_appendf(buf,
337 " <td class=\"num\">%'qu</td>\n"
338 " <td class=\"num\">%'qu</td>\n"
339 " <td class=\"num\">%'qu</td>\n",
340 (qu)b->in,
341 (qu)b->out,
342 (qu)b->total);
343
344 if (opt_want_lastseen) {
345 int64_t last = b->u.host.last_seen_mono;
346 int64_t now = (int64_t)now_mono();
347 struct str *last_str = NULL;
348
349 if ((now >= last) && (last != 0))
350 last_str = length_of_time(now - last);
351
352 str_append(buf, " <td class=\"num\">");
353 if (last_str == NULL) {
354 if (last == 0)
355 str_append(buf, "(never)");
356 else
357 str_appendf(buf, "(clock error: last = %qd, now = %qu)",
358 (qd)last,
359 (qu)now);
360 } else {
361 str_appendstr(buf, last_str);
362 str_free(last_str);
363 }
364 str_append(buf, "</td>");
365 }
366
367 str_appendf(buf, "</tr>\n");
368
369 /* Only resolve hosts "on demand" */
370 if (b->u.host.dns == NULL)
371 dns_queue(&(b->u.host.addr));
372 }
373
374 static void
375 format_cols_port_tcp(struct str *buf)
376 {
377 str_append(buf,
378 "<table>\n"
379 "<tr>\n"
380 " <th>Port</td>\n"
381 " <th>Service</td>\n"
382 " <th>In</td>\n"
383 " <th>Out</td>\n"
384 " <th>Total</td>\n"
385 " <th>SYNs</td>\n"
386 "</tr>\n"
387 );
388 }
389
390 static void
391 format_row_port_tcp(struct str *buf, const struct bucket *b)
392 {
393 const struct port_tcp *p = &(b->u.port_tcp);
394
395 str_appendf(buf,
396 "<tr>\n"
397 " <td class=\"num\">%u</td>\n"
398 " <td>%s</td>\n"
399 " <td class=\"num\">%'qu</td>\n"
400 " <td class=\"num\">%'qu</td>\n"
401 " <td class=\"num\">%'qu</td>\n"
402 " <td class=\"num\">%'qu</td>\n"
403 "</tr>\n",
404 p->port,
405 getservtcp(p->port),
406 (qu)b->in,
407 (qu)b->out,
408 (qu)b->total,
409 (qu)p->syn
410 );
411 }
412
413 static void
414 format_cols_port_udp(struct str *buf)
415 {
416 str_append(buf,
417 "<table>\n"
418 "<tr>\n"
419 " <th>Port</td>\n"
420 " <th>Service</td>\n"
421 " <th>In</td>\n"
422 " <th>Out</td>\n"
423 " <th>Total</td>\n"
424 "</tr>\n"
425 );
426 }
427
428 static void
429 format_row_port_udp(struct str *buf, const struct bucket *b)
430 {
431 const struct port_udp *p = &(b->u.port_udp);
432
433 str_appendf(buf,
434 "<tr>\n"
435 " <td class=\"num\">%u</td>\n"
436 " <td>%s</td>\n"
437 " <td class=\"num\">%'qu</td>\n"
438 " <td class=\"num\">%'qu</td>\n"
439 " <td class=\"num\">%'qu</td>\n"
440 "</tr>\n",
441 p->port,
442 getservudp(p->port),
443 (qu)b->in,
444 (qu)b->out,
445 (qu)b->total
446 );
447 }
448
449 static void
450 format_cols_ip_proto(struct str *buf)
451 {
452 str_append(buf,
453 "<table>\n"
454 "<tr>\n"
455 " <th>#</td>\n"
456 " <th>Protocol</td>\n"
457 " <th>In</td>\n"
458 " <th>Out</td>\n"
459 " <th>Total</td>\n"
460 "</tr>\n"
461 );
462 }
463
464 static void
465 format_row_ip_proto(struct str *buf, const struct bucket *b)
466 {
467 const struct ip_proto *p = &(b->u.ip_proto);
468
469 str_appendf(buf,
470 "<tr>\n"
471 " <td class=\"num\">%u</td>\n"
472 " <td>%s</td>\n"
473 " <td class=\"num\">%'qu</td>\n"
474 " <td class=\"num\">%'qu</td>\n"
475 " <td class=\"num\">%'qu</td>\n"
476 "</tr>\n",
477 p->proto,
478 getproto(p->proto),
479 (qu)b->in,
480 (qu)b->out,
481 (qu)b->total
482 );
483 }
484
485 /* ---------------------------------------------------------------------------
486 * Initialise a hashtable.
487 */
488 static struct hashtable *
489 hashtable_make(const uint8_t bits,
490 const unsigned int count_max,
491 const unsigned int count_keep,
492 hash_func_t *hash_func,
493 free_func_t *free_func,
494 key_func_t *key_func,
495 find_func_t *find_func,
496 make_func_t *make_func,
497 format_cols_func_t *format_cols_func,
498 format_row_func_t *format_row_func)
499 {
500 struct hashtable *hash;
501 assert(bits > 0);
502
503 hash = xmalloc(sizeof(*hash));
504 hash->bits = bits;
505 hash->count_max = count_max;
506 hash->count_keep = count_keep;
507 hash->size = 1U << bits;
508 hash->mask = hash->size - 1;
509 hash->coeff = coprime(hash->size);
510 hash->hash_func = hash_func;
511 hash->free_func = free_func;
512 hash->key_func = key_func;
513 hash->find_func = find_func;
514 hash->make_func = make_func;
515 hash->format_cols_func = format_cols_func;
516 hash->format_row_func = format_row_func;
517 hash->count = 0;
518 hash->table = xcalloc(hash->size, sizeof(*hash->table));
519 memset(&(hash->stats), 0, sizeof(hash->stats));
520 return (hash);
521 }
522
523 /* ---------------------------------------------------------------------------
524 * Initialise global hosts_db.
525 */
526 void
527 hosts_db_init(void)
528 {
529 assert(hosts_db == NULL);
530 hosts_db = hashtable_make(HOST_BITS, opt_hosts_max, opt_hosts_keep,
531 hash_func_host, free_func_host, key_func_host, find_func_host,
532 make_func_host, format_cols_host, format_row_host);
533 }
534
535 static void
536 hashtable_rehash(struct hashtable *h, const uint8_t bits)
537 {
538 struct bucket **old_table, **new_table;
539 uint32_t i, old_size;
540 assert(h != NULL);
541 assert(bits > 0);
542
543 h->stats.rehashes++;
544 old_size = h->size;
545 old_table = h->table;
546
547 h->bits = bits;
548 h->size = 1U << bits;
549 h->mask = h->size - 1;
550 h->coeff = coprime(h->size);
551 new_table = xcalloc(h->size, sizeof(*new_table));
552
553 for (i=0; i<old_size; i++) {
554 struct bucket *next, *b = old_table[i];
555 while (b != NULL) {
556 uint32_t pos = h->hash_func(h, h->key_func(b)) & h->mask;
557 next = b->next;
558 b->next = new_table[pos];
559 new_table[pos] = b;
560 b = next;
561 }
562 }
563
564 free(h->table);
565 h->table = new_table;
566 }
567
568 static void
569 hashtable_insert(struct hashtable *h, struct bucket *b)
570 {
571 uint32_t pos;
572 assert(h != NULL);
573 assert(b != NULL);
574 assert(b->next == NULL);
575
576 /* Rehash on 80% occupancy */
577 if ((h->count > h->size) ||
578 ((h->size - h->count) < h->size / 5))
579 hashtable_rehash(h, h->bits+1);
580
581 pos = h->hash_func(h, h->key_func(b)) & h->mask;
582 if (h->table[pos] == NULL)
583 h->table[pos] = b;
584 else {
585 /* Insert at top of chain. */
586 b->next = h->table[pos];
587 h->table[pos] = b;
588 }
589 h->count++;
590 h->stats.inserts++;
591 }
592
593 /* Return bucket matching key, or NULL if no such entry. */
594 static struct bucket *
595 hashtable_search(struct hashtable *h, const void *key)
596 {
597 uint32_t pos;
598 struct bucket *b;
599
600 h->stats.searches++;
601 pos = h->hash_func(h, key) & h->mask;
602 b = h->table[pos];
603 while (b != NULL) {
604 if (h->find_func(b, key))
605 return (b);
606 else
607 b = b->next;
608 }
609 return (NULL);
610 }
611
612 typedef enum { NO_REDUCE = 0, ALLOW_REDUCE = 1 } reduce_bool;
613 /* Search for a key. If it's not there, make and insert a bucket for it. */
614 static struct bucket *
615 hashtable_find_or_insert(struct hashtable *h, const void *key,
616 const reduce_bool allow_reduce)
617 {
618 struct bucket *b = hashtable_search(h, key);
619
620 if (b == NULL) {
621 /* Not found, so insert after checking occupancy. */
622 if (allow_reduce && (h->count >= h->count_max))
623 hashtable_reduce(h);
624 b = h->make_func(key);
625 hashtable_insert(h, b);
626 }
627 return (b);
628 }
629
630 /*
631 * Frees the hashtable and the buckets. The contents are assumed to be
632 * "simple" -- i.e. no "destructor" action is required beyond simply freeing
633 * the bucket.
634 */
635 static void
636 hashtable_free(struct hashtable *h)
637 {
638 uint32_t i;
639
640 if (h == NULL)
641 return;
642 for (i=0; i<h->size; i++) {
643 struct bucket *tmp, *b = h->table[i];
644 while (b != NULL) {
645 tmp = b;
646 b = b->next;
647 h->free_func(tmp);
648 free(tmp);
649 }
650 }
651 free(h->table);
652 free(h);
653 }
654
655 /* ---------------------------------------------------------------------------
656 * Return existing host or insert a new one.
657 */
658 struct bucket *
659 host_get(const struct addr *const a)
660 {
661 return (hashtable_find_or_insert(hosts_db, a, NO_REDUCE));
662 }
663
664 /* ---------------------------------------------------------------------------
665 * Find host, returns NULL if not in DB.
666 */
667 struct bucket *
668 host_find(const struct addr *const a)
669 {
670 return (hashtable_search(hosts_db, a));
671 }
672
673 /* ---------------------------------------------------------------------------
674 * Find host, returns NULL if not in DB.
675 */
676 static struct bucket *
677 host_search(const char *ipstr)
678 {
679 struct addr a;
680 struct addrinfo hints, *ai;
681
682 memset(&hints, 0, sizeof(hints));
683 hints.ai_family = AF_UNSPEC;
684 hints.ai_flags = AI_NUMERICHOST;
685
686 if (getaddrinfo(ipstr, NULL, &hints, &ai))
687 return (NULL); /* invalid addr */
688
689 if (ai->ai_family == AF_INET) {
690 a.family = IPv4;
691 a.ip.v4 = ((const struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
692 }
693 else if (ai->ai_family == AF_INET6) {
694 a.family = IPv6;
695 memcpy(&(a.ip.v6),
696 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr.s6_addr,
697 sizeof(a.ip.v6));
698 } else {
699 freeaddrinfo(ai);
700 return (NULL); /* unknown family */
701 }
702 freeaddrinfo(ai);
703
704 verbosef("search(%s) turned into %s", ipstr, addr_to_str(&a));
705 return (hashtable_search(hosts_db, &a));
706 }
707
708 /* ---------------------------------------------------------------------------
709 * Reduce a hashtable to the top <keep> entries.
710 */
711 static void
712 hashtable_reduce(struct hashtable *ht)
713 {
714 uint32_t i, pos, rmd;
715 const struct bucket **table;
716 uint64_t cutoff;
717
718 assert(ht->count_keep < ht->count);
719
720 /* Fill table with pointers to buckets in hashtable. */
721 table = xcalloc(ht->count, sizeof(*table));
722 for (pos=0, i=0; i<ht->size; i++) {
723 struct bucket *b = ht->table[i];
724 while (b != NULL) {
725 table[pos++] = b;
726 b = b->next;
727 }
728 }
729 assert(pos == ht->count);
730 qsort_buckets(table, ht->count, 0, ht->count_keep, TOTAL);
731 cutoff = table[ht->count_keep]->total;
732 free(table);
733
734 /* Remove all elements with total <= cutoff. */
735 rmd = 0;
736 for (i=0; i<ht->size; i++) {
737 struct bucket *last = NULL, *next, *b = ht->table[i];
738 while (b != NULL) {
739 next = b->next;
740 if (b->total <= cutoff) {
741 /* Remove this one. */
742 ht->free_func(b);
743 free(b);
744 if (last == NULL)
745 ht->table[i] = next;
746 else
747 last->next = next;
748 rmd++;
749 ht->count--;
750 } else {
751 last = b;
752 }
753 b = next;
754 }
755 }
756 verbosef("hashtable_reduce: removed %u buckets, left %u",
757 rmd, ht->count);
758 hashtable_rehash(ht, ht->bits); /* is this needed? */
759 }
760
761 /* Reduce hosts_db if needed. */
762 void hosts_db_reduce(void)
763 {
764 if (hosts_db->count >= hosts_db->count_max)
765 hashtable_reduce(hosts_db);
766 }
767
768 /* ---------------------------------------------------------------------------
769 * Reset hosts_db to empty.
770 */
771 void
772 hosts_db_reset(void)
773 {
774 unsigned int i;
775
776 for (i=0; i<hosts_db->size; i++) {
777 struct bucket *next, *b = hosts_db->table[i];
778 while (b != NULL) {
779 next = b->next;
780 hosts_db->free_func(b);
781 free(b);
782 b = next;
783 }
784 hosts_db->table[i] = NULL;
785 }
786 verbosef("hosts_db reset to empty, freed %u hosts", hosts_db->count);
787 hosts_db->count = 0;
788 }
789
790 /* ---------------------------------------------------------------------------
791 * Deallocate hosts_db.
792 */
793 void hosts_db_free(void)
794 {
795 uint32_t i;
796
797 assert(hosts_db != NULL);
798 for (i=0; i<hosts_db->size; i++) {
799 struct bucket *tmp, *b = hosts_db->table[i];
800 while (b != NULL) {
801 tmp = b;
802 b = b->next;
803 hosts_db->free_func(tmp);
804 free(tmp);
805 }
806 }
807 free(hosts_db->table);
808 free(hosts_db);
809 hosts_db = NULL;
810 }
811
812 /* ---------------------------------------------------------------------------
813 * Find or create a port_tcp inside a host.
814 */
815 struct bucket *
816 host_get_port_tcp(struct bucket *host, const uint16_t port)
817 {
818 struct host *h = &host->u.host;
819 if (h->ports_tcp == NULL)
820 h->ports_tcp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
821 hash_func_short, free_func_simple, key_func_port_tcp,
822 find_func_port_tcp, make_func_port_tcp,
823 format_cols_port_tcp, format_row_port_tcp);
824 return (hashtable_find_or_insert(h->ports_tcp, &port, ALLOW_REDUCE));
825 }
826
827 struct bucket *
828 host_get_port_tcp_remote(struct bucket *host, const uint16_t port)
829 {
830 struct host *h = &host->u.host;
831 if (h->ports_tcp_remote == NULL)
832 h->ports_tcp_remote = hashtable_make(
833 PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
834 free_func_simple, key_func_port_tcp, find_func_port_tcp,
835 make_func_port_tcp, format_cols_port_tcp, format_row_port_tcp);
836 return (hashtable_find_or_insert(h->ports_tcp_remote, &port, ALLOW_REDUCE));
837 }
838
839 /* ---------------------------------------------------------------------------
840 * Find or create a port_udp inside a host.
841 */
842 struct bucket *
843 host_get_port_udp(struct bucket *host, const uint16_t port)
844 {
845 struct host *h = &host->u.host;
846 if (h->ports_udp == NULL)
847 h->ports_udp = hashtable_make(PORT_BITS, opt_ports_max, opt_ports_keep,
848 hash_func_short, free_func_simple, key_func_port_udp,
849 find_func_port_udp, make_func_port_udp,
850 format_cols_port_udp, format_row_port_udp);
851 return (hashtable_find_or_insert(h->ports_udp, &port, ALLOW_REDUCE));
852 }
853
854 struct bucket *
855 host_get_port_udp_remote(struct bucket *host, const uint16_t port)
856 {
857 struct host *h = &host->u.host;
858 if (h->ports_udp_remote == NULL)
859 h->ports_udp_remote = hashtable_make(
860 PORT_BITS, opt_ports_max, opt_ports_keep, hash_func_short,
861 free_func_simple, key_func_port_udp, find_func_port_udp,
862 make_func_port_udp, format_cols_port_udp, format_row_port_udp);
863 return (hashtable_find_or_insert(h->ports_udp_remote, &port, ALLOW_REDUCE));
864 }
865
866 /* ---------------------------------------------------------------------------
867 * Find or create an ip_proto inside a host.
868 */
869 struct bucket *
870 host_get_ip_proto(struct bucket *host, const uint8_t proto)
871 {
872 struct host *h = &host->u.host;
873 static const unsigned int PROTOS_MAX = 512, PROTOS_KEEP = 256;
874 assert(h != NULL);
875 if (h->ip_protos == NULL)
876 h->ip_protos = hashtable_make(PROTO_BITS, PROTOS_MAX, PROTOS_KEEP,
877 hash_func_byte, free_func_simple, key_func_ip_proto,
878 find_func_ip_proto, make_func_ip_proto,
879 format_cols_ip_proto, format_row_ip_proto);
880 return (hashtable_find_or_insert(h->ip_protos, &proto, ALLOW_REDUCE));
881 }
882
883 static struct str *html_hosts_main(const char *qs);
884 static struct str *html_hosts_detail(const char *ip);
885
886 /* ---------------------------------------------------------------------------
887 * Web interface: delegate the /hosts/ space.
888 */
889 struct str *
890 html_hosts(const char *uri, const char *query)
891 {
892 unsigned int i, num_elems;
893 char **elem = split('/', uri, &num_elems);
894 struct str *buf = NULL;
895
896 assert(num_elems >= 1);
897 assert(strcmp(elem[0], "hosts") == 0);
898
899 if (num_elems == 1)
900 /* /hosts/ */
901 buf = html_hosts_main(query);
902 else if (num_elems == 2)
903 /* /hosts/<IP of host>/ */
904 buf = html_hosts_detail(elem[1]);
905
906 for (i=0; i<num_elems; i++)
907 free(elem[i]);
908 free(elem);
909 return (buf); /* FIXME: a NULL here becomes 404 Not Found, we might want
910 other codes to be possible */
911 }
912
913 /* ---------------------------------------------------------------------------
914 * Get an array of pointers to all the buckets in the hashtable,
915 * or NULL if the hashtable is NULL or empty.
916 * The returned pointer should be free'd by the caller.
917 */
918 const struct bucket **
919 hashtable_list_buckets(struct hashtable *ht)
920 {
921 const struct bucket **table;
922 unsigned int i, pos;
923
924 if ((ht == NULL) || (ht->count == 0)) {
925 return NULL;
926 }
927
928 /* Fill table with pointers to buckets in hashtable. */
929 table = xcalloc(ht->count, sizeof(*table));
930 for (pos=0, i=0; i<ht->size; i++) {
931 struct bucket *b = ht->table[i];
932 while (b != NULL) {
933 table[pos++] = b;
934 b = b->next;
935 }
936 }
937 assert(pos == ht->count);
938 return table;
939 }
940
941 typedef void (hashtable_foreach_func_t)(const struct bucket *, const void *);
942
943 /* ---------------------------------------------------------------------------
944 * Loop over all buckets in the given hashtable, calling the supplied function
945 * with each bucket and the supplied user_data.
946 */
947 static void
948 hashtable_foreach(struct hashtable *ht,
949 hashtable_foreach_func_t *hashtable_foreach_func,
950 const void *user_data)
951 {
952 const struct bucket **table;
953 unsigned int i;
954
955 table = hashtable_list_buckets(ht);
956 if (table == NULL)
957 return;
958
959 for (i = 0; i<ht->count; i++) {
960 const struct bucket *b = table[i];
961 (*hashtable_foreach_func)(b, user_data);
962 }
963 free(table);
964 }
965
966 /* ---------------------------------------------------------------------------
967 * Format hashtable into HTML.
968 */
969 static void
970 format_table(struct str *buf, struct hashtable *ht, unsigned int start,
971 const enum sort_dir sort, const int full)
972 {
973 const struct bucket **table;
974 unsigned int i, end;
975 int alt = 0;
976
977 table = hashtable_list_buckets(ht);
978
979 if (table == NULL) {
980 str_append(buf, "<p>The table is empty.</p>\n");
981 return;
982 }
983
984 if (full) {
985 /* full report overrides start and end */
986 start = 0;
987 end = ht->count;
988 } else
989 end = MIN(ht->count, (uint32_t)start+MAX_ENTRIES);
990
991 str_appendf(buf, "(%u-%u of %u)<br>\n", start+1, end, ht->count);
992 qsort_buckets(table, ht->count, start, end, sort);
993 ht->format_cols_func(buf);
994
995 for (i=start; i<end; i++) {
996 ht->format_row_func(buf, table[i]);
997 alt = !alt; /* alternate class for table rows */
998 }
999 free(table);
1000 str_append(buf, "</table>\n");
1001 }
1002
1003 /* ---------------------------------------------------------------------------
1004 * Web interface: sorted table of hosts.
1005 */
1006 static struct str *
1007 html_hosts_main(const char *qs)
1008 {
1009 struct str *buf = str_make();
1010 char *qs_start, *qs_sort, *qs_full, *ep;
1011 const char *sortstr;
1012 int start, full = 0;
1013 enum sort_dir sort;
1014
1015 /* parse query string */
1016 qs_start = qs_get(qs, "start");
1017 qs_sort = qs_get(qs, "sort");
1018 qs_full = qs_get(qs, "full");
1019 if (qs_full != NULL) {
1020 full = 1;
1021 free(qs_full);
1022 }
1023
1024 /* validate sort */
1025 if (qs_sort == NULL) sort = TOTAL;
1026 else if (strcmp(qs_sort, "total") == 0) sort = TOTAL;
1027 else if (strcmp(qs_sort, "in") == 0) sort = IN;
1028 else if (strcmp(qs_sort, "out") == 0) sort = OUT;
1029 else if (strcmp(qs_sort, "lastseen") == 0) sort = LASTSEEN;
1030 else {
1031 str_append(buf, "Error: invalid value for \"sort\".\n");
1032 goto done;
1033 }
1034
1035 /* parse start */
1036 if (qs_start == NULL)
1037 start = 0;
1038 else {
1039 start = (int)strtoul(qs_start, &ep, 10);
1040 if (*ep != '\0') {
1041 str_append(buf, "Error: \"start\" is not a number.\n");
1042 goto done;
1043 }
1044 if ((errno == ERANGE) ||
1045 (start < 0) || (start >= (int)hosts_db->count)) {
1046 str_append(buf, "Error: \"start\" is out of bounds.\n");
1047 goto done;
1048 }
1049 }
1050
1051 #define PREV "&lt;&lt;&lt; prev page"
1052 #define NEXT "next page &gt;&gt;&gt;"
1053 #define FULL "full table"
1054
1055 html_open(buf, "Hosts", /*path_depth=*/1, /*want_graph_js=*/0);
1056 format_table(buf, hosts_db, start, sort, full);
1057
1058 /* <prev | full | stats | next> */
1059 sortstr = qs_sort;
1060 if (sortstr == NULL) sortstr = "total";
1061 if (start > 0) {
1062 int prev = start - MAX_ENTRIES;
1063 if (prev < 0)
1064 prev = 0;
1065 str_appendf(buf, "<a href=\"?start=%d&sort=%s\">" PREV "</a>",
1066 prev, sortstr);
1067 } else
1068 str_append(buf, PREV);
1069
1070 if (full)
1071 str_append(buf, " | " FULL);
1072 else
1073 str_appendf(buf, " | <a href=\"?full=yes&sort=%s\">" FULL "</a>",
1074 sortstr);
1075
1076 if (start+MAX_ENTRIES < (int)hosts_db->count)
1077 str_appendf(buf, " | <a href=\"?start=%d&sort=%s\">" NEXT "</a>",
1078 start+MAX_ENTRIES, sortstr);
1079 else
1080 str_append(buf, " | " NEXT);
1081
1082 str_append(buf, "<br>\n");
1083
1084 html_close(buf);
1085 done:
1086 if (qs_start != NULL) free(qs_start);
1087 if (qs_sort != NULL) free(qs_sort);
1088 return buf;
1089 #undef PREV
1090 #undef NEXT
1091 #undef FULL
1092 }
1093
1094 /* ---------------------------------------------------------------------------
1095 * Web interface: detailed view of a single host.
1096 */
1097 static struct str *html_hosts_detail(const char *ip) {
1098 struct bucket *h;
1099 struct str *buf, *ls_len;
1100 char ls_when[100];
1101 const char *canonical;
1102 time_t last_seen_real;
1103
1104 h = host_search(ip);
1105 if (h == NULL)
1106 return (NULL); /* no such host */
1107
1108 canonical = addr_to_str(&(h->u.host.addr));
1109
1110 /* Overview. */
1111 buf = str_make();
1112 html_open(buf, ip, /*path_depth=*/2, /*want_graph_js=*/0);
1113 if (strcmp(ip, canonical) != 0)
1114 str_appendf(buf, "(canonically <b>%s</b>)\n", canonical);
1115 str_appendf(buf,
1116 "<p>\n"
1117 "<b>Hostname:</b> %s<br>\n",
1118 (h->u.host.dns == NULL)?"(resolving...)":h->u.host.dns);
1119
1120 /* Resolve host "on demand" */
1121 if (h->u.host.dns == NULL)
1122 dns_queue(&(h->u.host.addr));
1123
1124 if (hosts_db_show_macs)
1125 str_appendf(buf,
1126 "<b>MAC Address:</b> "
1127 "<tt>%x:%x:%x:%x:%x:%x</tt><br>\n",
1128 h->u.host.mac_addr[0],
1129 h->u.host.mac_addr[1],
1130 h->u.host.mac_addr[2],
1131 h->u.host.mac_addr[3],
1132 h->u.host.mac_addr[4],
1133 h->u.host.mac_addr[5]);
1134
1135 str_append(buf,
1136 "</p>\n"
1137 "<p>\n"
1138 "<b>Last seen:</b> ");
1139
1140 if (h->u.host.last_seen_mono == 0) {
1141 str_append(buf, "(never)");
1142 } else {
1143 last_seen_real = mono_to_real(h->u.host.last_seen_mono);
1144 if (strftime(ls_when, sizeof(ls_when),
1145 "%Y-%m-%d %H:%M:%S %Z%z", localtime(&last_seen_real)) != 0)
1146 str_append(buf, ls_when);
1147
1148 if (h->u.host.last_seen_mono <= now_mono()) {
1149 ls_len =
1150 length_of_time((int64_t)now_mono() - h->u.host.last_seen_mono);
1151 str_append(buf, " (");
1152 str_appendstr(buf, ls_len);
1153 str_free(ls_len);
1154 str_append(buf, " ago)");
1155 } else {
1156 str_appendf(buf, " (in the future, possible clock problem, "
1157 "last = %qd, now = %qu)",
1158 (qd)h->u.host.last_seen_mono,
1159 (qu)now_mono());
1160 }
1161 }
1162
1163 str_appendf(buf,
1164 "</p>\n"
1165 "<p>\n"
1166 " <b>In:</b> %'qu<br>\n"
1167 " <b>Out:</b> %'qu<br>\n"
1168 " <b>Total:</b> %'qu<br>\n"
1169 "</p>\n",
1170 (qu)h->in,
1171 (qu)h->out,
1172 (qu)h->total);
1173
1174 str_append(buf, "<h3>TCP ports on this host</h3>\n");
1175 format_table(buf, h->u.host.ports_tcp, 0,TOTAL,0);
1176
1177 str_append(buf, "<h3>TCP ports on remote hosts</h3>\n");
1178 format_table(buf, h->u.host.ports_tcp_remote, 0,TOTAL,0);
1179
1180 str_append(buf, "<h3>UDP ports on this host</h3>\n");
1181 format_table(buf, h->u.host.ports_udp, 0,TOTAL,0);
1182
1183 str_append(buf, "<h3>UDP ports on remote hosts</h3>\n");
1184 format_table(buf, h->u.host.ports_udp_remote, 0,TOTAL,0);
1185
1186 str_append(buf, "<h3>IP protocols</h3>\n");
1187 format_table(buf, h->u.host.ip_protos, 0,TOTAL,0);
1188
1189 str_append(buf, "<br>\n");
1190 html_close(buf);
1191 return buf;
1192 }
1193
1194 /* ---------------------------------------------------------------------------
1195 * Database import and export code:
1196 * Initially written and contributed by Ben Stewart.
1197 * copyright (c) 2007-2014 Ben Stewart, Emil Mikulic.
1198 */
1199 static int hosts_db_export_ip(const struct hashtable *h, const int fd);
1200 static int hosts_db_export_tcp(const char magic, const struct hashtable *h,
1201 const int fd);
1202 static int hosts_db_export_udp(const char magic, const struct hashtable *h,
1203 const int fd);
1204
1205 static const char
1206 export_proto_ip = 'P',
1207 export_proto_tcp = 'T',
1208 export_proto_tcp_remote = 't',
1209 export_proto_udp = 'U',
1210 export_proto_udp_remote = 'u';
1211
1212 static const unsigned char
1213 export_tag_host_ver1[] = {'H', 'S', 'T', 0x01},
1214 export_tag_host_ver2[] = {'H', 'S', 'T', 0x02},
1215 export_tag_host_ver3[] = {'H', 'S', 'T', 0x03},
1216 export_tag_host_ver4[] = {'H', 'S', 'T', 0x04};
1217
1218 static void text_metrics_counter(struct str *buf, const char *metric, const char *type, const char *help);
1219 static void text_metrics_format_host(const struct bucket *b, const void *user_data);
1220
1221 /* ---------------------------------------------------------------------------
1222 * Web interface: export stats in Prometheus text format on /metrics
1223 */
1224 struct str *
1225 text_metrics()
1226 {
1227 struct str *buf = str_make();
1228
1229 text_metrics_counter(buf,
1230 "host_bytes_total",
1231 "counter",
1232 "Total number of network bytes by host and direction.");
1233 hashtable_foreach(hosts_db, &text_metrics_format_host, (void *)buf);
1234
1235 return buf;
1236 }
1237
1238 static void
1239 text_metrics_counter(struct str *buf,
1240 const char *metric,
1241 const char *type,
1242 const char *help)
1243 {
1244 str_appendf(buf, "# HELP %s %s\n", metric, help);
1245 str_appendf(buf, "# TYPE %s %s\n", metric, type);
1246 }
1247
1248 static void
1249 text_metrics_format_host_key(struct str *buf, const struct bucket *b) {
1250 const char *ip = addr_to_str(&(b->u.host.addr));
1251
1252 str_appendf(buf,
1253 "host_bytes_total{interface=\"%s\",ip=\"%s\"",
1254 title_interfaces, ip);
1255
1256 if (hosts_db_show_macs)
1257 str_appendf(buf, ",mac=\"%x:%x:%x:%x:%x:%x\"",
1258 b->u.host.mac_addr[0],
1259 b->u.host.mac_addr[1],
1260 b->u.host.mac_addr[2],
1261 b->u.host.mac_addr[3],
1262 b->u.host.mac_addr[4],
1263 b->u.host.mac_addr[5]);
1264 }
1265
1266 static void
1267 text_metrics_format_host(const struct bucket *b,
1268 const void *user_data)
1269 {
1270 struct str *buf = (struct str *)user_data;
1271
1272 text_metrics_format_host_key(buf, b);
1273 str_appendf(buf, ",dir=\"in\"} %qu\n", (qu)b->in);
1274
1275 text_metrics_format_host_key(buf, b);
1276 str_appendf(buf, ",dir=\"out\"} %qu\n", (qu)b->out);
1277 }
1278
1279 /* ---------------------------------------------------------------------------
1280 * Load a host's ip_proto table from a file.
1281 * Returns 0 on failure, 1 on success.
1282 */
1283 static int
1284 hosts_db_import_ip(const int fd, struct bucket *host)
1285 {
1286 uint8_t count, i;
1287
1288 if (!expect8(fd, export_proto_ip)) return 0;
1289 if (!read8(fd, &count)) return 0;
1290
1291 for (i=0; i<count; i++) {
1292 struct bucket *b;
1293 uint8_t proto;
1294 uint64_t in, out;
1295
1296 if (!read8(fd, &proto)) return 0;
1297 if (!read64(fd, &in)) return 0;
1298 if (!read64(fd, &out)) return 0;
1299
1300 /* Store data */
1301 b = host_get_ip_proto(host, proto);
1302 b->in = in;
1303 b->out = out;
1304 b->total = in + out;
1305 assert(b->u.ip_proto.proto == proto); /* should be done by make fn */
1306 }
1307 return 1;
1308 }
1309
1310 /* ---------------------------------------------------------------------------
1311 * Load a host's port_tcp{,_remote} table from a file.
1312 * Returns 0 on failure, 1 on success.
1313 */
1314 static int hosts_db_import_tcp(const int fd, const char magic,
1315 struct bucket *host,
1316 struct bucket *(get_port_fn)(struct bucket *host,
1317 uint16_t port)) {
1318 uint16_t count, i;
1319
1320 if (!expect8(fd, magic)) return 0;
1321 if (!read16(fd, &count)) return 0;
1322
1323 for (i=0; i<count; i++) {
1324 struct bucket *b;
1325 uint16_t port;
1326 uint64_t in, out, syn;
1327
1328 if (!read16(fd, &port)) return 0;
1329 if (!read64(fd, &syn)) return 0;
1330 if (!read64(fd, &in)) return 0;
1331 if (!read64(fd, &out)) return 0;
1332
1333 /* Store data */
1334 b = get_port_fn(host, port);
1335 b->in = in;
1336 b->out = out;
1337 b->total = in + out;
1338 assert(b->u.port_tcp.port == port); /* done by make_func_port_tcp */
1339 b->u.port_tcp.syn = syn;
1340 }
1341 return 1;
1342 }
1343
1344 /* ---------------------------------------------------------------------------
1345 * Load a host's port_tcp table from a file.
1346 * Returns 0 on failure, 1 on success.
1347 */
1348 static int hosts_db_import_udp(const int fd, const char magic,
1349 struct bucket *host,
1350 struct bucket *(get_port_fn)(struct bucket *host,
1351 uint16_t port)) {
1352 uint16_t count, i;
1353
1354 if (!expect8(fd, magic)) return 0;
1355 if (!read16(fd, &count)) return 0;
1356
1357 for (i=0; i<count; i++) {
1358 struct bucket *b;
1359 uint16_t port;
1360 uint64_t in, out;
1361
1362 if (!read16(fd, &port)) return 0;
1363 if (!read64(fd, &in)) return 0;
1364 if (!read64(fd, &out)) return 0;
1365
1366 /* Store data */
1367 b = get_port_fn(host, port);
1368 b->in = in;
1369 b->out = out;
1370 b->total = in + out;
1371 assert(b->u.port_udp.port == port); /* done by make_func */
1372 }
1373 return 1;
1374 }
1375
1376 /* ---------------------------------------------------------------------------
1377 * Load all hosts from a file.
1378 * Returns 0 on failure, 1 on success.
1379 */
1380 static int
1381 hosts_db_import_host(const int fd)
1382 {
1383 struct bucket *host;
1384 struct addr a;
1385 uint8_t hostname_len;
1386 uint64_t in, out;
1387 unsigned int pos = xtell(fd);
1388 char hdr[4];
1389 int ver = 0;
1390
1391 if (!readn(fd, hdr, sizeof(hdr))) return 0;
1392 if (memcmp(hdr, export_tag_host_ver4, sizeof(hdr)) == 0)
1393 ver = 4;
1394 else if (memcmp(hdr, export_tag_host_ver3, sizeof(hdr)) == 0)
1395 ver = 3;
1396 else if (memcmp(hdr, export_tag_host_ver2, sizeof(hdr)) == 0)
1397 ver = 2;
1398 else if (memcmp(hdr, export_tag_host_ver1, sizeof(hdr)) == 0)
1399 ver = 1;
1400 else {
1401 warnx("bad host header: %02x%02x%02x%02x",
1402 hdr[0], hdr[1], hdr[2], hdr[3]);
1403 return 0;
1404 }
1405
1406 if (ver >= 3) {
1407 if (!readaddr(fd, &a))
1408 return 0;
1409 } else {
1410 assert((ver == 1) || (ver == 2));
1411 if (!readaddr_ipv4(fd, &a))
1412 return 0;
1413 }
1414 verbosef("at file pos %u, importing host %s", pos, addr_to_str(&a));
1415 host = host_get(&a);
1416 assert(addr_equal(&(host->u.host.addr), &a));
1417
1418 if (ver > 1) {
1419 uint64_t t;
1420 if (!read64(fd, &t)) return 0;
1421 host->u.host.last_seen_mono = real_to_mono(t);
1422 }
1423
1424 assert(sizeof(host->u.host.mac_addr) == 6);
1425 if (!readn(fd, host->u.host.mac_addr, sizeof(host->u.host.mac_addr)))
1426 return 0;
1427
1428 /* HOSTNAME */
1429 assert(host->u.host.dns == NULL); /* make fn? */
1430 if (!read8(fd, &hostname_len)) return 0;
1431 if (hostname_len > 0) {
1432 host->u.host.dns = xmalloc(hostname_len + 1);
1433 host->u.host.dns[0] = '\0';
1434
1435 /* At this point, the hostname is attached to a host which is in our
1436 * hosts_db, so if we bail out due to an import error, this pointer
1437 * isn't lost and leaked, it can be cleaned up in hosts_db_{free,reset}
1438 */
1439
1440 if (!readn(fd, host->u.host.dns, hostname_len)) return 0;
1441 host->u.host.dns[hostname_len] = '\0';
1442 }
1443
1444 if (!read64(fd, &in)) return 0;
1445 if (!read64(fd, &out)) return 0;
1446
1447 host->in = in;
1448 host->out = out;
1449 host->total = in + out;
1450
1451 /* Host's port and proto subtables: */
1452 if (!hosts_db_import_ip(fd, host)) return 0;
1453 if (!hosts_db_import_tcp(fd, export_proto_tcp, host, host_get_port_tcp))
1454 return 0;
1455 if (!hosts_db_import_udp(fd, export_proto_udp, host, host_get_port_udp))
1456 return 0;
1457
1458 if (ver == 4) {
1459 if (!hosts_db_import_tcp(fd, export_proto_tcp_remote, host,
1460 host_get_port_tcp_remote))
1461 return 0;
1462 if (!hosts_db_import_udp(fd, export_proto_udp_remote, host,
1463 host_get_port_udp_remote))
1464 return 0;
1465 }
1466 return 1;
1467 }
1468
1469 /* ---------------------------------------------------------------------------
1470 * Database Import: Grab hosts_db from a file provided by the caller.
1471 *
1472 * This function will retrieve the data sans the header. We expect the caller
1473 * to have validated the header of the hosts_db segment, and left the file
1474 * sitting at the start of the data.
1475 */
1476 int hosts_db_import(const int fd)
1477 {
1478 uint32_t host_count, i;
1479
1480 if (!read32(fd, &host_count)) return 0;
1481
1482 for (i=0; i<host_count; i++)
1483 if (!hosts_db_import_host(fd)) return 0;
1484
1485 return 1;
1486 }
1487
1488 /* ---------------------------------------------------------------------------
1489 * Database Export: Dump hosts_db into a file provided by the caller.
1490 * The caller is responsible for writing out export_tag_hosts_ver1 first.
1491 */
1492 int hosts_db_export(const int fd)
1493 {
1494 uint32_t i;
1495 struct bucket *b;
1496
1497 if (!write32(fd, hosts_db->count)) return 0;
1498
1499 for (i = 0; i<hosts_db->size; i++)
1500 for (b = hosts_db->table[i]; b != NULL; b = b->next) {
1501 /* For each host: */
1502 if (!writen(fd, export_tag_host_ver4, sizeof(export_tag_host_ver4)))
1503 return 0;
1504
1505 if (!writeaddr(fd, &(b->u.host.addr)))
1506 return 0;
1507
1508 if (!write64(fd, (uint64_t)mono_to_real(b->u.host.last_seen_mono)))
1509 return 0;
1510
1511 assert(sizeof(b->u.host.mac_addr) == 6);
1512 if (!writen(fd, b->u.host.mac_addr, sizeof(b->u.host.mac_addr)))
1513 return 0;
1514
1515 /* HOSTNAME */
1516 if (b->u.host.dns == NULL) {
1517 if (!write8(fd, 0)) return 0;
1518 } else {
1519 int dnslen = strlen(b->u.host.dns);
1520
1521 if (dnslen > 255) {
1522 warnx("found a very long hostname: \"%s\"\n"
1523 "wasn't expecting one longer than 255 chars (this one is %d)",
1524 b->u.host.dns, dnslen);
1525 dnslen = 255;
1526 }
1527
1528 if (!write8(fd, (uint8_t)dnslen)) return 0;
1529 if (!writen(fd, b->u.host.dns, dnslen)) return 0;
1530 }
1531
1532 if (!write64(fd, b->in)) return 0;
1533 if (!write64(fd, b->out)) return 0;
1534
1535 if (!hosts_db_export_ip(b->u.host.ip_protos, fd)) return 0;
1536 if (!hosts_db_export_tcp(export_proto_tcp, b->u.host.ports_tcp, fd))
1537 return 0;
1538 if (!hosts_db_export_udp(export_proto_udp, b->u.host.ports_udp, fd))
1539 return 0;
1540 if (!hosts_db_export_tcp(export_proto_tcp_remote,
1541 b->u.host.ports_tcp_remote, fd))
1542 return 0;
1543 if (!hosts_db_export_udp(export_proto_udp_remote,
1544 b->u.host.ports_udp_remote, fd))
1545 return 0;
1546 }
1547 return 1;
1548 }
1549
1550 /* ---------------------------------------------------------------------------
1551 * Dump the ip_proto table of a host.
1552 */
1553 static int
1554 hosts_db_export_ip(const struct hashtable *h, const int fd)
1555 {
1556 uint32_t i, written = 0;
1557 struct bucket *b;
1558
1559 /* IP DATA */
1560 if (!write8(fd, export_proto_ip)) return 0;
1561
1562 /* If no data, write a IP Proto count of 0 and we're done. */
1563 if (h == NULL) {
1564 if (!write8(fd, 0)) return 0;
1565 return 1;
1566 }
1567
1568 assert(h->count < 256);
1569 if (!write8(fd, (uint8_t)h->count)) return 0;
1570
1571 for (i = 0; i<h->size; i++)
1572 for (b = h->table[i]; b != NULL; b = b->next) {
1573 /* For each ip_proto bucket: */
1574
1575 if (!write8(fd, b->u.ip_proto.proto)) return 0;
1576 if (!write64(fd, b->in)) return 0;
1577 if (!write64(fd, b->out)) return 0;
1578 written++;
1579 }
1580 assert(written == h->count);
1581 return 1;
1582 }
1583
1584 /* ---------------------------------------------------------------------------
1585 * Dump the port_tcp table of a host.
1586 */
1587 static int
1588 hosts_db_export_tcp(const char magic, const struct hashtable *h, const int fd)
1589 {
1590 struct bucket *b;
1591 uint32_t i, written = 0;
1592
1593 /* TCP DATA */
1594 if (!write8(fd, magic)) return 0;
1595
1596 /* If no data, write a count of 0 and we're done. */
1597 if (h == NULL) {
1598 if (!write16(fd, 0)) return 0;
1599 return 1;
1600 }
1601
1602 assert(h->count < 65536);
1603 if (!write16(fd, (uint16_t)h->count)) return 0;
1604
1605 for (i = 0; i<h->size; i++)
1606 for (b = h->table[i]; b != NULL; b = b->next) {
1607 if (!write16(fd, b->u.port_tcp.port)) return 0;
1608 if (!write64(fd, b->u.port_tcp.syn)) return 0;
1609 if (!write64(fd, b->in)) return 0;
1610 if (!write64(fd, b->out)) return 0;
1611 written++;
1612 }
1613 assert(written == h->count);
1614 return 1;
1615 }
1616
1617 /* ---------------------------------------------------------------------------
1618 * Dump the port_udp table of a host.
1619 */
1620 static int
1621 hosts_db_export_udp(const char magic, const struct hashtable *h, const int fd)
1622 {
1623 struct bucket *b;
1624 uint32_t i, written = 0;
1625
1626 /* UDP DATA */
1627 if (!write8(fd, magic)) return 0;
1628
1629 /* If no data, write a count of 0 and we're done. */
1630 if (h == NULL) {
1631 if (!write16(fd, 0)) return 0;
1632 return 1;
1633 }
1634
1635 assert(h->count < 65536);
1636 if (!write16(fd, (uint16_t)h->count)) return 0;
1637
1638 for (i = 0; i<h->size; i++)
1639 for (b = h->table[i]; b != NULL; b = b->next) {
1640 if (!write16(fd, b->u.port_udp.port)) return 0;
1641 if (!write64(fd, b->in)) return 0;
1642 if (!write64(fd, b->out)) return 0;
1643 written++;
1644 }
1645 assert(written == h->count);
1646 return 1;
1647 }
1648
1649 /* vim:set ts=3 sw=3 tw=80 expandtab: */