2 * copyright (c) 2001-2008 Emil Mikulic.
4 * hosts_db.c: database of hosts, ports, protocols.
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
21 #include <arpa/inet.h> /* inet_aton() */
26 #include <string.h> /* memset(), strcmp() */
29 int show_mac_addrs
= 0;
30 extern const char *interface
;
32 /* FIXME: specify somewhere more sane/tunable */
33 #define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */
35 typedef uint32_t (hash_func_t
)(const struct hashtable
*, const void *);
36 typedef void (free_func_t
)(struct bucket
*);
37 typedef const void * (key_func_t
)(const struct bucket
*);
38 typedef int (find_func_t
)(const struct bucket
*, const void *);
39 typedef struct bucket
* (make_func_t
)(const void *);
40 typedef void (format_cols_func_t
)(struct str
*);
41 typedef void (format_row_func_t
)(struct str
*, const struct bucket
*,
45 uint8_t bits
; /* size of hashtable in bits */
47 uint32_t count
, count_max
, count_keep
; /* items in table */
48 uint32_t coeff
; /* coefficient for Fibonacci hashing */
49 struct bucket
**table
;
52 uint64_t inserts
, searches
, deletions
, rehashes
;
55 hash_func_t
*hash_func
;
56 /* returns hash value of given key (passed as void*) */
58 free_func_t
*free_func
;
59 /* free of bucket payload */
62 /* returns pointer to key of bucket (to pass to hash_func) */
64 find_func_t
*find_func
;
65 /* returns true if given bucket matches key (passed as void*) */
67 make_func_t
*make_func
;
68 /* returns bucket containing new record with key (passed as void*) */
70 format_cols_func_t
*format_cols_func
;
71 /* append table columns to str */
73 format_row_func_t
*format_row_func
;
74 /* format record and append to str */
77 static void hashtable_reduce(struct hashtable
*ht
);
78 static void hashtable_free(struct hashtable
*h
);
80 #define HOST_BITS 1 /* initial size of hosts table */
81 #define PORT_BITS 1 /* initial size of ports tables */
82 #define PROTO_BITS 1 /* initial size of proto table */
84 /* We only use one hosts_db hashtable and this is it. */
85 static struct hashtable
*hosts_db
= NULL
;
87 /* phi^-1 (reciprocal of golden ratio) = (sqrt(5) - 1) / 2 */
88 static const double phi_1
=
89 0.61803398874989490252573887119069695472717285156250;
91 /* Co-prime of u, using phi^-1 */
92 inline static uint32_t
93 coprime(const uint32_t u
)
95 return ( (uint32_t)( (double)(u
) * phi_1
) | 1U );
99 * This is the "recommended" IPv4 hash function, as seen in FreeBSD's
100 * src/sys/netinet/tcp_hostcache.c 1.1
102 inline static uint32_t
103 ipv4_hash(const uint32_t ip
)
105 return ( (ip
) ^ ((ip
) >> 7) ^ ((ip
) >> 17) );
108 /* ---------------------------------------------------------------------------
109 * hash_func collection
111 #define CASTKEY(type) (*((const type *)key))
114 hash_func_host(const struct hashtable
*h _unused_
, const void *key
)
116 return (ipv4_hash(CASTKEY(in_addr_t
)));
120 hash_func_short(const struct hashtable
*h
, const void *key
)
122 return (CASTKEY(uint16_t) * h
->coeff
);
126 hash_func_byte(const struct hashtable
*h
, const void *key
)
128 return (CASTKEY(uint8_t) * h
->coeff
);
131 /* ---------------------------------------------------------------------------
132 * key_func collection
136 key_func_host(const struct bucket
*b
)
138 return &(b
->u
.host
.ip
);
142 key_func_port_tcp(const struct bucket
*b
)
144 return &(b
->u
.port_tcp
.port
);
148 key_func_port_udp(const struct bucket
*b
)
150 return &(b
->u
.port_udp
.port
);
154 key_func_ip_proto(const struct bucket
*b
)
156 return &(b
->u
.ip_proto
.proto
);
159 /* ---------------------------------------------------------------------------
160 * find_func collection
164 find_func_host(const struct bucket
*b
, const void *key
)
166 return (b
->u
.host
.ip
== CASTKEY(in_addr_t
));
170 find_func_port_tcp(const struct bucket
*b
, const void *key
)
172 return (b
->u
.port_tcp
.port
== CASTKEY(uint16_t));
176 find_func_port_udp(const struct bucket
*b
, const void *key
)
178 return (b
->u
.port_udp
.port
== CASTKEY(uint16_t));
182 find_func_ip_proto(const struct bucket
*b
, const void *key
)
184 return (b
->u
.ip_proto
.proto
== CASTKEY(uint8_t));
187 /* ---------------------------------------------------------------------------
188 * make_func collection
191 #define MAKE_BUCKET(name_bucket, name_content, type) struct { \
192 struct bucket *next; \
193 uint64_t in, out, total; \
194 union { struct type t; } u; } _custom_bucket; \
195 struct bucket *name_bucket = xmalloc(sizeof(_custom_bucket)); \
196 struct type *name_content = &(name_bucket->u.type); \
197 name_bucket->next = NULL; \
198 name_bucket->in = name_bucket->out = name_bucket->total = 0;
200 static struct bucket
*
201 make_func_host(const void *key
)
203 MAKE_BUCKET(b
, h
, host
);
204 h
->ip
= CASTKEY(in_addr_t
);
207 memset(&h
->mac_addr
, 0, sizeof(h
->mac_addr
));
215 free_func_host(struct bucket
*b
)
217 struct host
*h
= &(b
->u
.host
);
218 if (h
->dns
!= NULL
) free(h
->dns
);
219 hashtable_free(h
->ports_tcp
);
220 hashtable_free(h
->ports_udp
);
221 hashtable_free(h
->ip_protos
);
224 static struct bucket
*
225 make_func_port_tcp(const void *key
)
227 MAKE_BUCKET(b
, p
, port_tcp
);
228 p
->port
= CASTKEY(uint16_t);
233 static struct bucket
*
234 make_func_port_udp(const void *key
)
236 MAKE_BUCKET(b
, p
, port_udp
);
237 p
->port
= CASTKEY(uint16_t);
241 static struct bucket
*
242 make_func_ip_proto(const void *key
)
244 MAKE_BUCKET(b
, p
, ip_proto
);
245 p
->proto
= CASTKEY(uint8_t);
250 free_func_simple(struct bucket
*b _unused_
)
255 /* ---------------------------------------------------------------------------
256 * format_func collection (ordered by struct)
260 format_cols_host(struct str
*buf
)
262 /* FIXME: don't clobber parts of the query string
263 * specifically "full" and "start"
264 * when setting sort direction
270 " <th>Hostname</th>\n");
271 if (show_mac_addrs
) str_append(buf
,
272 " <th>MAC Address</th>\n");
274 " <th><a href=\"?sort=in\">In</a></th>\n"
275 " <th><a href=\"?sort=out\">Out</a></th>\n"
276 " <th><a href=\"?sort=total\">Total</a></th>\n"
277 " <th>Last seen</th>\n"
282 format_row_host(struct str
*buf
, const struct bucket
*b
,
283 const char *css_class
)
285 const char *ip
= ip_to_str( b
->u
.host
.ip
);
286 struct str
*lastseen
= NULL
;
290 "<tr class=\"%s\">\n"
291 " <td><a href=\"%s/\">%s</a></td>\n"
295 (b
->u
.host
.dns
== NULL
) ? "" : b
->u
.host
.dns
);
299 " <td><tt>%x:%x:%x:%x:%x:%x</tt></td>\n",
300 b
->u
.host
.mac_addr
[0],
301 b
->u
.host
.mac_addr
[1],
302 b
->u
.host
.mac_addr
[2],
303 b
->u
.host
.mac_addr
[3],
304 b
->u
.host
.mac_addr
[4],
305 b
->u
.host
.mac_addr
[5]);
307 last_t
= b
->u
.host
.last_seen
;
309 lastseen
= length_of_time(now
- last_t
);
312 " <td class=\"num\">%'qu</td>\n"
313 " <td class=\"num\">%'qu</td>\n"
314 " <td class=\"num\">%'qu</td>\n"
315 " <td class=\"num\">",
316 b
->in
, b
->out
, b
->total
);
318 if (lastseen
== NULL
)
319 str_append(buf
, "(clock error)");
321 str_appendstr(buf
, lastseen
);
329 /* Only resolve hosts "on demand" */
330 if (b
->u
.host
.dns
== NULL
)
331 dns_queue(b
->u
.host
.ip
);
335 format_cols_port_tcp(struct str
*buf
)
341 " <th>Service</td>\n"
351 format_row_port_tcp(struct str
*buf
, const struct bucket
*b
,
352 const char *css_class
)
354 const struct port_tcp
*p
= &(b
->u
.port_tcp
);
357 "<tr class=\"%s\">\n"
358 " <td class=\"num\">%u</td>\n"
360 " <td class=\"num\">%'qu</td>\n"
361 " <td class=\"num\">%'qu</td>\n"
362 " <td class=\"num\">%'qu</td>\n"
363 " <td class=\"num\">%'qu</td>\n"
366 p
->port
, getservtcp(p
->port
), b
->in
, b
->out
, b
->total
, p
->syn
371 format_cols_port_udp(struct str
*buf
)
377 " <th>Service</td>\n"
386 format_row_port_udp(struct str
*buf
, const struct bucket
*b
,
387 const char *css_class
)
389 const struct port_udp
*p
= &(b
->u
.port_udp
);
392 "<tr class=\"%s\">\n"
393 " <td class=\"num\">%u</td>\n"
395 " <td class=\"num\">%'qu</td>\n"
396 " <td class=\"num\">%'qu</td>\n"
397 " <td class=\"num\">%'qu</td>\n"
400 p
->port
, getservudp(p
->port
), b
->in
, b
->out
, b
->total
405 format_cols_ip_proto(struct str
*buf
)
411 " <th>Protocol</td>\n"
420 format_row_ip_proto(struct str
*buf
, const struct bucket
*b
,
421 const char *css_class
)
423 const struct ip_proto
*p
= &(b
->u
.ip_proto
);
426 "<tr class=\"%s\">\n"
427 " <td class=\"num\">%u</td>\n"
429 " <td class=\"num\">%'qu</td>\n"
430 " <td class=\"num\">%'qu</td>\n"
431 " <td class=\"num\">%'qu</td>\n"
434 p
->proto
, getproto(p
->proto
),
435 b
->in
, b
->out
, b
->total
439 /* ---------------------------------------------------------------------------
440 * Initialise a hashtable.
442 static struct hashtable
*
443 hashtable_make(const uint8_t bits
,
444 const unsigned int count_max
,
445 const unsigned int count_keep
,
446 hash_func_t
*hash_func
,
447 free_func_t
*free_func
,
448 key_func_t
*key_func
,
449 find_func_t
*find_func
,
450 make_func_t
*make_func
,
451 format_cols_func_t
*format_cols_func
,
452 format_row_func_t
*format_row_func
)
454 struct hashtable
*hash
;
457 hash
= xmalloc(sizeof(*hash
));
459 hash
->count_max
= count_max
;
460 hash
->count_keep
= count_keep
;
461 hash
->size
= 1U << bits
;
462 hash
->mask
= hash
->size
- 1;
463 hash
->coeff
= coprime(hash
->size
);
464 hash
->hash_func
= hash_func
;
465 hash
->free_func
= free_func
;
466 hash
->key_func
= key_func
;
467 hash
->find_func
= find_func
;
468 hash
->make_func
= make_func
;
469 hash
->format_cols_func
= format_cols_func
;
470 hash
->format_row_func
= format_row_func
;
472 hash
->table
= xcalloc(hash
->size
, sizeof(*hash
->table
));
473 memset(&(hash
->stats
), 0, sizeof(hash
->stats
));
477 /* ---------------------------------------------------------------------------
478 * Initialise global hosts_db.
483 assert(hosts_db
== NULL
);
484 hosts_db
= hashtable_make(HOST_BITS
, hosts_max
, hosts_keep
,
485 hash_func_host
, free_func_host
, key_func_host
, find_func_host
,
486 make_func_host
, format_cols_host
, format_row_host
);
490 hashtable_rehash(struct hashtable
*h
, const uint8_t bits
)
492 struct bucket
**old_table
, **new_table
;
493 uint32_t i
, old_size
;
499 old_table
= h
->table
;
502 h
->size
= 1U << bits
;
503 h
->mask
= h
->size
- 1;
504 h
->coeff
= coprime(h
->size
);
505 new_table
= xcalloc(h
->size
, sizeof(*new_table
));
507 for (i
=0; i
<old_size
; i
++) {
508 struct bucket
*next
, *b
= old_table
[i
];
510 uint32_t pos
= h
->hash_func(h
, h
->key_func(b
)) & h
->mask
;
512 b
->next
= new_table
[pos
];
519 h
->table
= new_table
;
523 hashtable_insert(struct hashtable
*h
, struct bucket
*b
)
528 assert(b
->next
== NULL
);
530 /* Rehash on 80% occupancy */
531 if ((h
->count
> h
->size
) ||
532 ((h
->size
- h
->count
) < h
->size
/ 5))
533 hashtable_rehash(h
, h
->bits
+1);
535 pos
= h
->hash_func(h
, h
->key_func(b
)) & h
->mask
;
536 if (h
->table
[pos
] == NULL
)
539 /* Insert at top of chain. */
540 b
->next
= h
->table
[pos
];
547 /* Return bucket matching key, or NULL if no such entry. */
548 static struct bucket
*
549 hashtable_search(struct hashtable
*h
, const void *key
)
555 pos
= h
->hash_func(h
, key
) & h
->mask
;
558 if (h
->find_func(b
, key
))
566 /* Search for a key. If it's not there, make and insert a bucket for it. */
567 static struct bucket
*
568 hashtable_find_or_insert(struct hashtable
*h
, const void *key
)
570 struct bucket
*b
= hashtable_search(h
, key
);
573 /* Not found, so insert after checking occupancy. */
574 /*assert(h->count <= h->count_max);*/
575 if (h
->count
>= h
->count_max
) hashtable_reduce(h
);
576 b
= h
->make_func(key
);
577 hashtable_insert(h
, b
);
583 * Frees the hashtable and the buckets. The contents are assumed to be
584 * "simple" -- i.e. no "destructor" action is required beyond simply freeing
588 hashtable_free(struct hashtable
*h
)
594 for (i
=0; i
<h
->size
; i
++) {
595 struct bucket
*tmp
, *b
= h
->table
[i
];
607 /* ---------------------------------------------------------------------------
608 * Return existing host or insert a new one.
611 host_get(const in_addr_t ip
)
613 return (hashtable_find_or_insert(hosts_db
, &ip
));
616 /* ---------------------------------------------------------------------------
617 * Find host, returns NULL if not in DB.
620 host_find(const in_addr_t ip
)
622 return (hashtable_search(hosts_db
, &ip
));
625 /* ---------------------------------------------------------------------------
626 * Find host, returns NULL if not in DB.
628 static struct bucket
*
629 host_search(const char *ipstr
)
633 if (inet_aton(ipstr
, &addr
) != 1)
634 return (NULL
); /* invalid addr */
635 return (hashtable_search(hosts_db
, &(addr
.s_addr
)));
638 /* ---------------------------------------------------------------------------
639 * Reduce a hashtable to the top <keep> entries.
642 hashtable_reduce(struct hashtable
*ht
)
644 uint32_t i
, pos
, rmd
;
645 const struct bucket
**table
;
648 assert(ht
->count_keep
< ht
->count
);
650 /* Fill table with pointers to buckets in hashtable. */
651 table
= xmalloc(sizeof(*table
) * ht
->count
);
652 for (pos
=0, i
=0; i
<ht
->size
; i
++) {
653 struct bucket
*b
= ht
->table
[i
];
659 assert(pos
== ht
->count
);
660 qsort_buckets(table
, ht
->count
, 0, ht
->count_keep
, TOTAL
);
661 cutoff
= table
[ht
->count_keep
]->total
;
664 /* Remove all elements with total <= cutoff. */
666 for (i
=0; i
<ht
->size
; i
++) {
667 struct bucket
*last
= NULL
, *next
, *b
= ht
->table
[i
];
670 if (b
->total
<= cutoff
) {
671 /* Remove this one. */
686 verbosef("hashtable_reduce: removed %u buckets, left %u",
688 hashtable_rehash(ht
, ht
->bits
); /* is this needed? */
691 /* ---------------------------------------------------------------------------
692 * Reset hosts_db to empty.
699 for (i
=0; i
<hosts_db
->size
; i
++) {
700 struct bucket
*next
, *b
= hosts_db
->table
[i
];
703 hosts_db
->free_func(b
);
707 hosts_db
->table
[i
] = NULL
;
709 verbosef("hosts_db reset to empty, freed %u hosts", hosts_db
->count
);
713 /* ---------------------------------------------------------------------------
714 * Deallocate hosts_db.
716 void hosts_db_free(void)
720 assert(hosts_db
!= NULL
);
721 for (i
=0; i
<hosts_db
->size
; i
++) {
722 struct bucket
*tmp
, *b
= hosts_db
->table
[i
];
726 hosts_db
->free_func(tmp
);
730 free(hosts_db
->table
);
735 /* ---------------------------------------------------------------------------
736 * Find or create a port_tcp inside a host.
739 host_get_port_tcp(struct bucket
*host
, const uint16_t port
)
741 struct host
*h
= &host
->u
.host
;
743 if (h
->ports_tcp
== NULL
)
744 h
->ports_tcp
= hashtable_make(PORT_BITS
, ports_max
, ports_keep
,
745 hash_func_short
, free_func_simple
, key_func_port_tcp
,
746 find_func_port_tcp
, make_func_port_tcp
,
747 format_cols_port_tcp
, format_row_port_tcp
);
748 return (hashtable_find_or_insert(h
->ports_tcp
, &port
));
751 /* ---------------------------------------------------------------------------
752 * Find or create a port_udp inside a host.
755 host_get_port_udp(struct bucket
*host
, const uint16_t port
)
757 struct host
*h
= &host
->u
.host
;
759 if (h
->ports_udp
== NULL
)
760 h
->ports_udp
= hashtable_make(PORT_BITS
, ports_max
, ports_keep
,
761 hash_func_short
, free_func_simple
, key_func_port_udp
,
762 find_func_port_udp
, make_func_port_udp
,
763 format_cols_port_udp
, format_row_port_udp
);
764 return (hashtable_find_or_insert(h
->ports_udp
, &port
));
767 /* ---------------------------------------------------------------------------
768 * Find or create an ip_proto inside a host.
771 host_get_ip_proto(struct bucket
*host
, const uint8_t proto
)
773 struct host
*h
= &host
->u
.host
;
775 if (h
->ip_protos
== NULL
)
776 h
->ip_protos
= hashtable_make(PROTO_BITS
, ports_max
, ports_keep
,
777 hash_func_byte
, free_func_simple
, key_func_ip_proto
,
778 find_func_ip_proto
, make_func_ip_proto
,
779 format_cols_ip_proto
, format_row_ip_proto
);
780 return (hashtable_find_or_insert(h
->ip_protos
, &proto
));
783 static struct str
*html_hosts_main(const char *qs
);
784 static struct str
*html_hosts_detail(const char *ip
);
786 /* ---------------------------------------------------------------------------
787 * Web interface: delegate the /hosts/ space.
790 html_hosts(const char *uri
, const char *query
)
793 char **elem
= split('/', uri
, &num_elems
);
794 struct str
*buf
= NULL
;
796 assert(num_elems
>= 1);
797 assert(strcmp(elem
[0], "hosts") == 0);
801 buf
= html_hosts_main(query
);
802 else if (num_elems
== 2)
803 /* /hosts/<IP of host>/ */
804 buf
= html_hosts_detail(elem
[1]);
806 for (i
=0; i
<num_elems
; i
++)
809 return (buf
); /* FIXME: a NULL here becomes 404 Not Found, we might want
810 other codes to be possible */
813 /* ---------------------------------------------------------------------------
814 * Format hashtable into HTML.
817 format_table(struct str
*buf
, struct hashtable
*ht
, int start
,
818 const enum sort_dir sort
, const int full
)
820 const struct bucket
**table
;
821 uint32_t i
, pos
, end
;
824 if ((ht
== NULL
) || (ht
->count
== 0)) {
825 str_append(buf
, "<p>The table is empty.</p>\n");
829 /* Fill table with pointers to buckets in hashtable. */
830 table
= xmalloc(sizeof(*table
) * ht
->count
);
831 for (pos
=0, i
=0; i
<ht
->size
; i
++) {
832 struct bucket
*b
= ht
->table
[i
];
838 assert(pos
== ht
->count
);
841 /* full report overrides start and end */
845 end
= min(ht
->count
, (uint32_t)start
+MAX_ENTRIES
);
847 str_appendf(buf
, "(%u-%u of %u)<br/>\n", start
+1, end
, ht
->count
);
848 qsort_buckets(table
, ht
->count
, start
, end
, sort
);
849 ht
->format_cols_func(buf
);
851 for (i
=start
; i
<end
; i
++) {
852 ht
->format_row_func(buf
, table
[i
], alt
? "alt1" : "alt2");
853 alt
= !alt
; /* alternate class for table rows */
856 str_append(buf
, "</table>\n");
859 /* ---------------------------------------------------------------------------
860 * Web interface: sorted table of hosts.
863 html_hosts_main(const char *qs
)
865 struct str
*buf
= str_make();
866 char *qs_start
, *qs_sort
, *qs_full
, *ep
;
871 /* parse query string */
872 qs_start
= qs_get(qs
, "start");
873 qs_sort
= qs_get(qs
, "sort");
874 qs_full
= qs_get(qs
, "full");
875 if (qs_full
!= NULL
) {
881 if (qs_sort
== NULL
) sort
= TOTAL
;
882 else if (strcmp(qs_sort
, "total") == 0) sort
= TOTAL
;
883 else if (strcmp(qs_sort
, "in") == 0) sort
= IN
;
884 else if (strcmp(qs_sort
, "out") == 0) sort
= OUT
;
886 str_append(buf
, "Error: invalid value for \"sort\".\n");
891 if (qs_start
== NULL
)
894 start
= (int)strtoul(qs_start
, &ep
, 10);
896 str_append(buf
, "Error: \"start\" is not a number.\n");
899 if ((errno
== ERANGE
) ||
900 (start
< 0) || (start
>= (int)hosts_db
->count
)) {
901 str_append(buf
, "Error: \"start\" is out of bounds.\n");
906 #define PREV "<<< prev page"
907 #define NEXT "next page >>>"
908 #define FULL "full table"
910 str_append(buf
, html_header_1
);
911 str_appendf(buf
, " <title>darkstat3: Hosts (%s)</title>\n", interface
);
912 str_append(buf
, html_header_2
);
913 str_appendf(buf
, "<h2 class=\"pageheader\">Hosts (%s)</h2>\n", interface
);
914 format_table(buf
, hosts_db
, start
, sort
, full
);
916 /* <prev | full | stats | next> */
918 if (sortstr
== NULL
) sortstr
= "total";
920 int prev
= max(start
- MAX_ENTRIES
, 0);
921 str_appendf(buf
, "<a href=\"?start=%d&sort=%s\">" PREV
"</a>",
924 str_append(buf
, PREV
);
927 str_append(buf
, " | " FULL
);
929 str_appendf(buf
, " | <a href=\"?full=yes&sort=%s\">" FULL
"</a>",
932 if (start
+MAX_ENTRIES
< (int)hosts_db
->count
)
933 str_appendf(buf
, " | <a href=\"?start=%d&sort=%s\">" NEXT
"</a>",
934 start
+MAX_ENTRIES
, sortstr
);
936 str_append(buf
, " | " NEXT
);
938 str_append(buf
, "<br/>\n");
939 str_append(buf
, html_footer
);
941 if (qs_start
!= NULL
) free(qs_start
);
942 if (qs_sort
!= NULL
) free(qs_sort
);
949 /* ---------------------------------------------------------------------------
950 * Web interface: detailed view of a single host.
953 html_hosts_detail(const char *ip
)
956 struct str
*buf
, *ls_len
;
962 return (NULL
); /* no such host */
966 str_append(buf
, html_header_1
);
967 str_appendf(buf
, " <title>%s</title>\n", ip
);
968 str_append(buf
, html_header_2
);
972 "<b>Hostname:</b> %s<br/>\n",
974 (h
->u
.host
.dns
== NULL
)?"(resolving...)":h
->u
.host
.dns
);
976 /* Resolve host "on demand" */
977 if (h
->u
.host
.dns
== NULL
)
978 dns_queue(h
->u
.host
.ip
);
982 "<b>MAC Address:</b> "
983 "<tt>%x:%x:%x:%x:%x:%x</tt><br/>\n",
984 h
->u
.host
.mac_addr
[0],
985 h
->u
.host
.mac_addr
[1],
986 h
->u
.host
.mac_addr
[2],
987 h
->u
.host
.mac_addr
[3],
988 h
->u
.host
.mac_addr
[4],
989 h
->u
.host
.mac_addr
[5]);
994 "<b>Last seen:</b> ");
996 ls
= h
->u
.host
.last_seen
;
997 if (strftime(ls_when
, sizeof(ls_when
),
998 "%Y-%m-%d %H:%M:%S %Z%z", localtime(&ls
)) != 0)
999 str_append(buf
, ls_when
);
1001 if (h
->u
.host
.last_seen
<= now
) {
1002 ls_len
= length_of_time(now
- h
->u
.host
.last_seen
);
1003 str_append(buf
, " (");
1004 str_appendstr(buf
, ls_len
);
1006 str_append(buf
, " ago)");
1008 str_append(buf
, " (in the future, possible clock problem)");
1014 " <b>In:</b> %'qu<br/>\n"
1015 " <b>Out:</b> %'qu<br/>\n"
1016 " <b>Total:</b> %'qu<br/>\n"
1018 h
->in
, h
->out
, h
->total
);
1020 str_append(buf
, "<h3>TCP ports</h3>\n");
1021 format_table(buf
, h
->u
.host
.ports_tcp
, 0,TOTAL
,0);
1023 str_append(buf
, "<h3>UDP ports</h3>\n");
1024 format_table(buf
, h
->u
.host
.ports_udp
, 0,TOTAL
,0);
1026 str_append(buf
, "<h3>IP protocols</h3>\n");
1027 format_table(buf
, h
->u
.host
.ip_protos
, 0,TOTAL
,0);
1029 str_append(buf
, html_footer
);
1033 /* ---------------------------------------------------------------------------
1034 * Database import and export code:
1035 * Initially written and contributed by Ben Stewart.
1036 * copyright (c) 2007 Ben Stewart, Emil Mikulic.
1038 static int hosts_db_export_ip(const struct hashtable
*h
, const int fd
);
1039 static int hosts_db_export_tcp(const struct hashtable
*h
, const int fd
);
1040 static int hosts_db_export_udp(const struct hashtable
*h
, const int fd
);
1043 export_proto_ip
= 'P',
1044 export_proto_tcp
= 'T',
1045 export_proto_udp
= 'U';
1047 static const unsigned char
1048 export_tag_host_ver1
[] = {'H', 'S', 'T', 0x01},
1049 export_tag_host_ver2
[] = {'H', 'S', 'T', 0x02};
1051 /* ---------------------------------------------------------------------------
1052 * Load a host's ip_proto table from a file.
1053 * Returns 0 on failure, 1 on success.
1056 hosts_db_import_ip(const int fd
, struct bucket
*host
)
1060 if (!expect8(fd
, export_proto_ip
)) return 0;
1061 if (!read8(fd
, &count
)) return 0;
1063 for (i
=0; i
<count
; i
++) {
1068 if (!read8(fd
, &proto
)) return 0;
1069 if (!read64(fd
, &in
)) return 0;
1070 if (!read64(fd
, &out
)) return 0;
1073 b
= host_get_ip_proto(host
, proto
);
1076 b
->total
= in
+ out
;
1077 assert(b
->u
.ip_proto
.proto
== proto
); /* should be done by make fn */
1082 /* ---------------------------------------------------------------------------
1083 * Load a host's port_tcp table from a file.
1084 * Returns 0 on failure, 1 on success.
1087 hosts_db_import_tcp(const int fd
, struct bucket
*host
)
1091 if (!expect8(fd
, export_proto_tcp
)) return 0;
1092 if (!read16(fd
, &count
)) return 0;
1094 for (i
=0; i
<count
; i
++) {
1097 uint64_t in
, out
, syn
;
1099 if (!read16(fd
, &port
)) return 0;
1100 if (!read64(fd
, &syn
)) return 0;
1101 if (!read64(fd
, &in
)) return 0;
1102 if (!read64(fd
, &out
)) return 0;
1105 b
= host_get_port_tcp(host
, port
);
1108 b
->total
= in
+ out
;
1109 assert(b
->u
.port_tcp
.port
== port
); /* done by make_func_port_tcp */
1110 b
->u
.port_tcp
.syn
= syn
;
1115 /* ---------------------------------------------------------------------------
1116 * Load a host's port_tcp table from a file.
1117 * Returns 0 on failure, 1 on success.
1120 hosts_db_import_udp(const int fd
, struct bucket
*host
)
1124 if (!expect8(fd
, export_proto_udp
)) return 0;
1125 if (!read16(fd
, &count
)) return 0;
1127 for (i
=0; i
<count
; i
++) {
1132 if (!read16(fd
, &port
)) return 0;
1133 if (!read64(fd
, &in
)) return 0;
1134 if (!read64(fd
, &out
)) return 0;
1137 b
= host_get_port_udp(host
, port
);
1140 b
->total
= in
+ out
;
1141 assert(b
->u
.port_udp
.port
== port
); /* done by make_func */
1146 /* ---------------------------------------------------------------------------
1147 * Load all hosts from a file.
1148 * Returns 0 on failure, 1 on success.
1151 hosts_db_import_host(const int fd
)
1153 struct bucket
*host
;
1155 uint8_t hostname_len
;
1157 unsigned int pos
= xtell(fd
);
1161 if (!readn(fd
, hdr
, sizeof(hdr
))) return 0;
1162 if (memcmp(hdr
, export_tag_host_ver2
, sizeof(hdr
)) == 0)
1164 else if (memcmp(hdr
, export_tag_host_ver1
, sizeof(hdr
)) == 0)
1167 warnx("bad host header: %02x%02x%02x%02x",
1168 hdr
[0], hdr
[1], hdr
[2], hdr
[3]);
1172 if (!readaddr(fd
, &addr
)) return 0;
1173 verbosef("at file pos %u, importing host %s", pos
, ip_to_str(addr
));
1174 host
= host_get(addr
);
1175 assert(host
->u
.host
.ip
== addr
); /* make fn? */
1179 if (!read64(fd
, &t
)) return 0;
1180 host
->u
.host
.last_seen
= (time_t)t
;
1183 assert(sizeof(host
->u
.host
.mac_addr
) == 6);
1184 if (!readn(fd
, host
->u
.host
.mac_addr
, sizeof(host
->u
.host
.mac_addr
)))
1188 assert(host
->u
.host
.dns
== NULL
); /* make fn? */
1189 if (!read8(fd
, &hostname_len
)) return 0;
1190 if (hostname_len
> 0) {
1191 host
->u
.host
.dns
= xmalloc(hostname_len
+ 1);
1192 host
->u
.host
.dns
[0] = '\0';
1194 /* At this point, the hostname is attached to a host which is in our
1195 * hosts_db, so if we bail out due to an import error, this pointer
1196 * isn't lost and leaked, it can be cleaned up in hosts_db_{free,reset}
1199 if (!readn(fd
, host
->u
.host
.dns
, hostname_len
)) return 0;
1200 host
->u
.host
.dns
[hostname_len
] = '\0';
1203 if (!read64(fd
, &in
)) return 0;
1204 if (!read64(fd
, &out
)) return 0;
1208 host
->total
= in
+ out
;
1210 /* Host's port and proto subtables: */
1211 if (!hosts_db_import_ip(fd
, host
)) return 0;
1212 if (!hosts_db_import_tcp(fd
, host
)) return 0;
1213 if (!hosts_db_import_udp(fd
, host
)) return 0;
1217 /* ---------------------------------------------------------------------------
1218 * Database Import: Grab hosts_db from a file provided by the caller.
1220 * This function will retrieve the data sans the header. We expect the caller
1221 * to have validated the header of the hosts_db segment, and left the file
1222 * sitting at the start of the data.
1224 int hosts_db_import(const int fd
)
1226 uint32_t host_count
, i
;
1228 if (!read32(fd
, &host_count
)) return 0;
1230 for (i
=0; i
<host_count
; i
++)
1231 if (!hosts_db_import_host(fd
)) return 0;
1236 /* ---------------------------------------------------------------------------
1237 * Database Export: Dump hosts_db into a file provided by the caller.
1238 * The caller is responsible for writing out export_tag_hosts_ver1 first.
1240 int hosts_db_export(const int fd
)
1245 if (!write32(fd
, hosts_db
->count
)) return 0;
1247 for (i
= 0; i
<hosts_db
->size
; i
++)
1248 for (b
= hosts_db
->table
[i
]; b
!= NULL
; b
= b
->next
) {
1249 /* For each host: */
1250 if (!writen(fd
, export_tag_host_ver2
, sizeof(export_tag_host_ver2
)))
1253 if (!writeaddr(fd
, b
->u
.host
.ip
)) return 0;
1255 if (!write64(fd
, (uint64_t)(b
->u
.host
.last_seen
))) return 0;
1257 assert(sizeof(b
->u
.host
.mac_addr
) == 6);
1258 if (!writen(fd
, b
->u
.host
.mac_addr
, sizeof(b
->u
.host
.mac_addr
)))
1262 if (b
->u
.host
.dns
== NULL
) {
1263 if (!write8(fd
, 0)) return 0;
1265 int dnslen
= strlen(b
->u
.host
.dns
);
1268 warnx("found a very long hostname: \"%s\"\n"
1269 "wasn't expecting one longer than 255 chars (this one is %d)",
1270 b
->u
.host
.dns
, dnslen
);
1274 if (!write8(fd
, (uint8_t)dnslen
)) return 0;
1275 if (!writen(fd
, b
->u
.host
.dns
, dnslen
)) return 0;
1278 if (!write64(fd
, b
->in
)) return 0;
1279 if (!write64(fd
, b
->out
)) return 0;
1281 if (!hosts_db_export_ip(b
->u
.host
.ip_protos
, fd
)) return 0;
1282 if (!hosts_db_export_tcp(b
->u
.host
.ports_tcp
, fd
)) return 0;
1283 if (!hosts_db_export_udp(b
->u
.host
.ports_udp
, fd
)) return 0;
1288 /* ---------------------------------------------------------------------------
1289 * Dump the ip_proto table of a host.
1292 hosts_db_export_ip(const struct hashtable
*h
, const int fd
)
1294 uint32_t i
, written
= 0;
1298 if (!write8(fd
, export_proto_ip
)) return 0;
1300 /* If no data, write a IP Proto count of 0 and we're done. */
1302 if (!write8(fd
, 0)) return 0;
1306 assert(h
->count
< 256);
1307 if (!write8(fd
, (uint8_t)h
->count
)) return 0;
1309 for (i
= 0; i
<h
->size
; i
++)
1310 for (b
= h
->table
[i
]; b
!= NULL
; b
= b
->next
) {
1311 /* For each ip_proto bucket: */
1313 if (!write8(fd
, b
->u
.ip_proto
.proto
)) return 0;
1314 if (!write64(fd
, b
->in
)) return 0;
1315 if (!write64(fd
, b
->out
)) return 0;
1318 assert(written
== h
->count
);
1322 /* ---------------------------------------------------------------------------
1323 * Dump the port_tcp table of a host.
1326 hosts_db_export_tcp(const struct hashtable
*h
, const int fd
)
1329 uint32_t i
, written
= 0;
1332 if (!write8(fd
, export_proto_tcp
)) return 0;
1334 /* If no data, write a count of 0 and we're done. */
1336 if (!write16(fd
, 0)) return 0;
1340 assert(h
->count
< 65536);
1341 if (!write16(fd
, (uint16_t)h
->count
)) return 0;
1343 for (i
= 0; i
<h
->size
; i
++)
1344 for (b
= h
->table
[i
]; b
!= NULL
; b
= b
->next
) {
1345 if (!write16(fd
, b
->u
.port_tcp
.port
)) return 0;
1346 if (!write64(fd
, b
->u
.port_tcp
.syn
)) return 0;
1347 if (!write64(fd
, b
->in
)) return 0;
1348 if (!write64(fd
, b
->out
)) return 0;
1351 assert(written
== h
->count
);
1355 /* ---------------------------------------------------------------------------
1356 * Dump the port_udp table of a host.
1359 hosts_db_export_udp(const struct hashtable
*h
, const int fd
)
1362 uint32_t i
, written
= 0;
1365 if (!write8(fd
, export_proto_udp
)) return 0;
1367 /* If no data, write a count of 0 and we're done. */
1369 if (!write16(fd
, 0)) return 0;
1373 assert(h
->count
< 65536);
1374 if (!write16(fd
, (uint16_t)h
->count
)) return 0;
1376 for (i
= 0; i
<h
->size
; i
++)
1377 for (b
= h
->table
[i
]; b
!= NULL
; b
= b
->next
) {
1378 if (!write16(fd
, b
->u
.port_udp
.port
)) return 0;
1379 if (!write64(fd
, b
->in
)) return 0;
1380 if (!write64(fd
, b
->out
)) return 0;
1383 assert(written
== h
->count
);
1387 /* vim:set ts=3 sw=3 tw=78 expandtab: */