hosts_db.c (darkstat-3.0.719) | : | hosts_db.c (darkstat-3.0.721) | ||
---|---|---|---|---|
skipping to change at line 43 | skipping to change at line 43 | |||
/* FIXME: specify somewhere more sane/tunable */ | /* FIXME: specify somewhere more sane/tunable */ | |||
#define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */ | #define MAX_ENTRIES 30 /* in an HTML table rendered from a hashtable */ | |||
typedef uint32_t (hash_func_t)(const struct hashtable *, const void *); | typedef uint32_t (hash_func_t)(const struct hashtable *, const void *); | |||
typedef void (free_func_t)(struct bucket *); | typedef void (free_func_t)(struct bucket *); | |||
typedef const void * (key_func_t)(const struct bucket *); | typedef const void * (key_func_t)(const struct bucket *); | |||
typedef int (find_func_t)(const struct bucket *, const void *); | typedef int (find_func_t)(const struct bucket *, const void *); | |||
typedef struct bucket * (make_func_t)(const void *); | typedef struct bucket * (make_func_t)(const void *); | |||
typedef void (format_cols_func_t)(struct str *); | typedef void (format_cols_func_t)(struct str *); | |||
typedef void (format_row_func_t)(struct str *, const struct bucket *, | typedef void (format_row_func_t)(struct str *, const struct bucket *); | |||
const char *); | ||||
struct hashtable { | struct hashtable { | |||
uint8_t bits; /* size of hashtable in bits */ | uint8_t bits; /* size of hashtable in bits */ | |||
uint32_t size, mask; | uint32_t size, mask; | |||
uint32_t count, count_max, count_keep; /* items in table */ | uint32_t count, count_max, count_keep; /* items in table */ | |||
uint32_t coeff; /* coefficient for Fibonacci hashing */ | uint32_t coeff; /* coefficient for Fibonacci hashing */ | |||
struct bucket **table; | struct bucket **table; | |||
struct { | struct { | |||
uint64_t inserts, searches, deletions, rehashes; | uint64_t inserts, searches, deletions, rehashes; | |||
skipping to change at line 316 | skipping to change at line 315 | |||
" <th><a href=\"?sort=in\">In</a></th>\n" | " <th><a href=\"?sort=in\">In</a></th>\n" | |||
" <th><a href=\"?sort=out\">Out</a></th>\n" | " <th><a href=\"?sort=out\">Out</a></th>\n" | |||
" <th><a href=\"?sort=total\">Total</a></th>\n"); | " <th><a href=\"?sort=total\">Total</a></th>\n"); | |||
if (opt_want_lastseen) str_append(buf, | if (opt_want_lastseen) str_append(buf, | |||
" <th><a href=\"?sort=lastseen\">Last seen</a></th>\n"); | " <th><a href=\"?sort=lastseen\">Last seen</a></th>\n"); | |||
str_append(buf, | str_append(buf, | |||
"</tr>\n"); | "</tr>\n"); | |||
} | } | |||
static void | static void | |||
format_row_host(struct str *buf, const struct bucket *b, | format_row_host(struct str *buf, const struct bucket *b) | |||
const char *css_class) | ||||
{ | { | |||
const char *ip = addr_to_str(&(b->u.host.addr)); | const char *ip = addr_to_str(&(b->u.host.addr)); | |||
str_appendf(buf, | str_appendf(buf, | |||
"<tr class=\"%s\">\n" | "<tr>\n" | |||
" <td><a href=\"./%s/\">%s</a></td>\n" | " <td><a href=\"./%s/\">%s</a></td>\n" | |||
" <td>%s</td>\n", | " <td>%s</td>\n", | |||
css_class, | ||||
ip, ip, | ip, ip, | |||
(b->u.host.dns == NULL) ? "" : b->u.host.dns); | (b->u.host.dns == NULL) ? "" : b->u.host.dns); | |||
if (hosts_db_show_macs) | if (hosts_db_show_macs) | |||
str_appendf(buf, | str_appendf(buf, | |||
" <td><tt>%x:%x:%x:%x:%x:%x</tt></td>\n", | " <td><tt>%x:%x:%x:%x:%x:%x</tt></td>\n", | |||
b->u.host.mac_addr[0], | b->u.host.mac_addr[0], | |||
b->u.host.mac_addr[1], | b->u.host.mac_addr[1], | |||
b->u.host.mac_addr[2], | b->u.host.mac_addr[2], | |||
b->u.host.mac_addr[3], | b->u.host.mac_addr[3], | |||
skipping to change at line 394 | skipping to change at line 391 | |||
" <th>Service</td>\n" | " <th>Service</td>\n" | |||
" <th>In</td>\n" | " <th>In</td>\n" | |||
" <th>Out</td>\n" | " <th>Out</td>\n" | |||
" <th>Total</td>\n" | " <th>Total</td>\n" | |||
" <th>SYNs</td>\n" | " <th>SYNs</td>\n" | |||
"</tr>\n" | "</tr>\n" | |||
); | ); | |||
} | } | |||
static void | static void | |||
format_row_port_tcp(struct str *buf, const struct bucket *b, | format_row_port_tcp(struct str *buf, const struct bucket *b) | |||
const char *css_class) | ||||
{ | { | |||
const struct port_tcp *p = &(b->u.port_tcp); | const struct port_tcp *p = &(b->u.port_tcp); | |||
str_appendf(buf, | str_appendf(buf, | |||
"<tr class=\"%s\">\n" | "<tr>\n" | |||
" <td class=\"num\">%u</td>\n" | " <td class=\"num\">%u</td>\n" | |||
" <td>%s</td>\n" | " <td>%s</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
"</tr>\n", | "</tr>\n", | |||
css_class, | ||||
p->port, | p->port, | |||
getservtcp(p->port), | getservtcp(p->port), | |||
(qu)b->in, | (qu)b->in, | |||
(qu)b->out, | (qu)b->out, | |||
(qu)b->total, | (qu)b->total, | |||
(qu)p->syn | (qu)p->syn | |||
); | ); | |||
} | } | |||
static void | static void | |||
skipping to change at line 434 | skipping to change at line 429 | |||
" <th>Port</td>\n" | " <th>Port</td>\n" | |||
" <th>Service</td>\n" | " <th>Service</td>\n" | |||
" <th>In</td>\n" | " <th>In</td>\n" | |||
" <th>Out</td>\n" | " <th>Out</td>\n" | |||
" <th>Total</td>\n" | " <th>Total</td>\n" | |||
"</tr>\n" | "</tr>\n" | |||
); | ); | |||
} | } | |||
static void | static void | |||
format_row_port_udp(struct str *buf, const struct bucket *b, | format_row_port_udp(struct str *buf, const struct bucket *b) | |||
const char *css_class) | ||||
{ | { | |||
const struct port_udp *p = &(b->u.port_udp); | const struct port_udp *p = &(b->u.port_udp); | |||
str_appendf(buf, | str_appendf(buf, | |||
"<tr class=\"%s\">\n" | "<tr>\n" | |||
" <td class=\"num\">%u</td>\n" | " <td class=\"num\">%u</td>\n" | |||
" <td>%s</td>\n" | " <td>%s</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
"</tr>\n", | "</tr>\n", | |||
css_class, | ||||
p->port, | p->port, | |||
getservudp(p->port), | getservudp(p->port), | |||
(qu)b->in, | (qu)b->in, | |||
(qu)b->out, | (qu)b->out, | |||
(qu)b->total | (qu)b->total | |||
); | ); | |||
} | } | |||
static void | static void | |||
format_cols_ip_proto(struct str *buf) | format_cols_ip_proto(struct str *buf) | |||
skipping to change at line 472 | skipping to change at line 465 | |||
" <th>#</td>\n" | " <th>#</td>\n" | |||
" <th>Protocol</td>\n" | " <th>Protocol</td>\n" | |||
" <th>In</td>\n" | " <th>In</td>\n" | |||
" <th>Out</td>\n" | " <th>Out</td>\n" | |||
" <th>Total</td>\n" | " <th>Total</td>\n" | |||
"</tr>\n" | "</tr>\n" | |||
); | ); | |||
} | } | |||
static void | static void | |||
format_row_ip_proto(struct str *buf, const struct bucket *b, | format_row_ip_proto(struct str *buf, const struct bucket *b) | |||
const char *css_class) | ||||
{ | { | |||
const struct ip_proto *p = &(b->u.ip_proto); | const struct ip_proto *p = &(b->u.ip_proto); | |||
str_appendf(buf, | str_appendf(buf, | |||
"<tr class=\"%s\">\n" | "<tr>\n" | |||
" <td class=\"num\">%u</td>\n" | " <td class=\"num\">%u</td>\n" | |||
" <td>%s</td>\n" | " <td>%s</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
" <td class=\"num\">%'qu</td>\n" | " <td class=\"num\">%'qu</td>\n" | |||
"</tr>\n", | "</tr>\n", | |||
css_class, | ||||
p->proto, | p->proto, | |||
getproto(p->proto), | getproto(p->proto), | |||
(qu)b->in, | (qu)b->in, | |||
(qu)b->out, | (qu)b->out, | |||
(qu)b->total | (qu)b->total | |||
); | ); | |||
} | } | |||
/* --------------------------------------------------------------------------- | /* --------------------------------------------------------------------------- | |||
* Initialise a hashtable. | * Initialise a hashtable. | |||
skipping to change at line 923 | skipping to change at line 914 | |||
buf = html_hosts_detail(elem[1]); | buf = html_hosts_detail(elem[1]); | |||
for (i=0; i<num_elems; i++) | for (i=0; i<num_elems; i++) | |||
free(elem[i]); | free(elem[i]); | |||
free(elem); | free(elem); | |||
return (buf); /* FIXME: a NULL here becomes 404 Not Found, we might want | return (buf); /* FIXME: a NULL here becomes 404 Not Found, we might want | |||
other codes to be possible */ | other codes to be possible */ | |||
} | } | |||
/* --------------------------------------------------------------------------- | /* --------------------------------------------------------------------------- | |||
* Format hashtable into HTML. | * Get an array of pointers to all the buckets in the hashtable, | |||
* or NULL if the hashtable is NULL or empty. | ||||
* The returned pointer should be free'd by the caller. | ||||
*/ | */ | |||
static void | const struct bucket ** | |||
format_table(struct str *buf, struct hashtable *ht, unsigned int start, | hashtable_list_buckets(struct hashtable *ht) | |||
const enum sort_dir sort, const int full) | ||||
{ | { | |||
const struct bucket **table; | const struct bucket **table; | |||
unsigned int i, pos, end; | unsigned int i, pos; | |||
int alt = 0; | ||||
if ((ht == NULL) || (ht->count == 0)) { | if ((ht == NULL) || (ht->count == 0)) { | |||
str_append(buf, "<p>The table is empty.</p>\n"); | return NULL; | |||
return; | ||||
} | } | |||
/* Fill table with pointers to buckets in hashtable. */ | /* Fill table with pointers to buckets in hashtable. */ | |||
table = xcalloc(ht->count, sizeof(*table)); | table = xcalloc(ht->count, sizeof(*table)); | |||
for (pos=0, i=0; i<ht->size; i++) { | for (pos=0, i=0; i<ht->size; i++) { | |||
struct bucket *b = ht->table[i]; | struct bucket *b = ht->table[i]; | |||
while (b != NULL) { | while (b != NULL) { | |||
table[pos++] = b; | table[pos++] = b; | |||
b = b->next; | b = b->next; | |||
} | } | |||
} | } | |||
assert(pos == ht->count); | assert(pos == ht->count); | |||
return table; | ||||
} | ||||
typedef void (hashtable_foreach_func_t)(const struct bucket *, const void *); | ||||
/* --------------------------------------------------------------------------- | ||||
* Loop over all buckets in the given hashtable, calling the supplied function | ||||
* with each bucket and the supplied user_data. | ||||
*/ | ||||
static void | ||||
hashtable_foreach(struct hashtable *ht, | ||||
hashtable_foreach_func_t *hashtable_foreach_func, | ||||
const void *user_data) | ||||
{ | ||||
const struct bucket **table; | ||||
unsigned int i; | ||||
table = hashtable_list_buckets(ht); | ||||
if (table == NULL) | ||||
return; | ||||
for (i = 0; i<ht->count; i++) { | ||||
const struct bucket *b = table[i]; | ||||
(*hashtable_foreach_func)(b, user_data); | ||||
} | ||||
free(table); | ||||
} | ||||
/* --------------------------------------------------------------------------- | ||||
* Format hashtable into HTML. | ||||
*/ | ||||
static void | ||||
format_table(struct str *buf, struct hashtable *ht, unsigned int start, | ||||
const enum sort_dir sort, const int full) | ||||
{ | ||||
const struct bucket **table; | ||||
unsigned int i, end; | ||||
int alt = 0; | ||||
table = hashtable_list_buckets(ht); | ||||
if (table == NULL) { | ||||
str_append(buf, "<p>The table is empty.</p>\n"); | ||||
return; | ||||
} | ||||
if (full) { | if (full) { | |||
/* full report overrides start and end */ | /* full report overrides start and end */ | |||
start = 0; | start = 0; | |||
end = ht->count; | end = ht->count; | |||
} else | } else | |||
end = MIN(ht->count, (uint32_t)start+MAX_ENTRIES); | end = MIN(ht->count, (uint32_t)start+MAX_ENTRIES); | |||
str_appendf(buf, "(%u-%u of %u)<br>\n", start+1, end, ht->count); | str_appendf(buf, "(%u-%u of %u)<br>\n", start+1, end, ht->count); | |||
qsort_buckets(table, ht->count, start, end, sort); | qsort_buckets(table, ht->count, start, end, sort); | |||
ht->format_cols_func(buf); | ht->format_cols_func(buf); | |||
for (i=start; i<end; i++) { | for (i=start; i<end; i++) { | |||
ht->format_row_func(buf, table[i], alt ? "alt1" : "alt2"); | ht->format_row_func(buf, table[i]); | |||
alt = !alt; /* alternate class for table rows */ | alt = !alt; /* alternate class for table rows */ | |||
} | } | |||
free(table); | free(table); | |||
str_append(buf, "</table>\n"); | str_append(buf, "</table>\n"); | |||
} | } | |||
/* --------------------------------------------------------------------------- | /* --------------------------------------------------------------------------- | |||
* Web interface: sorted table of hosts. | * Web interface: sorted table of hosts. | |||
*/ | */ | |||
static struct str * | static struct str * | |||
skipping to change at line 1183 | skipping to change at line 1218 | |||
export_proto_tcp_remote = 't', | export_proto_tcp_remote = 't', | |||
export_proto_udp = 'U', | export_proto_udp = 'U', | |||
export_proto_udp_remote = 'u'; | export_proto_udp_remote = 'u'; | |||
static const unsigned char | static const unsigned char | |||
export_tag_host_ver1[] = {'H', 'S', 'T', 0x01}, | export_tag_host_ver1[] = {'H', 'S', 'T', 0x01}, | |||
export_tag_host_ver2[] = {'H', 'S', 'T', 0x02}, | export_tag_host_ver2[] = {'H', 'S', 'T', 0x02}, | |||
export_tag_host_ver3[] = {'H', 'S', 'T', 0x03}, | export_tag_host_ver3[] = {'H', 'S', 'T', 0x03}, | |||
export_tag_host_ver4[] = {'H', 'S', 'T', 0x04}; | export_tag_host_ver4[] = {'H', 'S', 'T', 0x04}; | |||
static void text_metrics_counter(struct str *buf, const char *metric, const char | ||||
*type, const char *help); | ||||
static void text_metrics_format_host(const struct bucket *b, const void *user_da | ||||
ta); | ||||
/* --------------------------------------------------------------------------- | ||||
* Web interface: export stats in Prometheus text format on /metrics | ||||
*/ | ||||
struct str * | ||||
text_metrics() | ||||
{ | ||||
struct str *buf = str_make(); | ||||
text_metrics_counter(buf, | ||||
"host_bytes_total", | ||||
"counter", | ||||
"Total number of network bytes by host and direction."); | ||||
hashtable_foreach(hosts_db, &text_metrics_format_host, (void *)buf); | ||||
return buf; | ||||
} | ||||
static void | ||||
text_metrics_counter(struct str *buf, | ||||
const char *metric, | ||||
const char *type, | ||||
const char *help) | ||||
{ | ||||
str_appendf(buf, "# HELP %s %s\n", metric, help); | ||||
str_appendf(buf, "# TYPE %s %s\n", metric, type); | ||||
} | ||||
static void | ||||
text_metrics_format_host_key(struct str *buf, const struct bucket *b) { | ||||
const char *ip = addr_to_str(&(b->u.host.addr)); | ||||
str_appendf(buf, | ||||
"host_bytes_total{interface=\"%s\",ip=\"%s\"", | ||||
title_interfaces, ip); | ||||
if (hosts_db_show_macs) | ||||
str_appendf(buf, ",mac=\"%x:%x:%x:%x:%x:%x\"", | ||||
b->u.host.mac_addr[0], | ||||
b->u.host.mac_addr[1], | ||||
b->u.host.mac_addr[2], | ||||
b->u.host.mac_addr[3], | ||||
b->u.host.mac_addr[4], | ||||
b->u.host.mac_addr[5]); | ||||
} | ||||
static void | ||||
text_metrics_format_host(const struct bucket *b, | ||||
const void *user_data) | ||||
{ | ||||
struct str *buf = (struct str *)user_data; | ||||
text_metrics_format_host_key(buf, b); | ||||
str_appendf(buf, ",dir=\"in\"} %qu\n", (qu)b->in); | ||||
text_metrics_format_host_key(buf, b); | ||||
str_appendf(buf, ",dir=\"out\"} %qu\n", (qu)b->out); | ||||
} | ||||
/* --------------------------------------------------------------------------- | /* --------------------------------------------------------------------------- | |||
* Load a host's ip_proto table from a file. | * Load a host's ip_proto table from a file. | |||
* Returns 0 on failure, 1 on success. | * Returns 0 on failure, 1 on success. | |||
*/ | */ | |||
static int | static int | |||
hosts_db_import_ip(const int fd, struct bucket *host) | hosts_db_import_ip(const int fd, struct bucket *host) | |||
{ | { | |||
uint8_t count, i; | uint8_t count, i; | |||
if (!expect8(fd, export_proto_ip)) return 0; | if (!expect8(fd, export_proto_ip)) return 0; | |||
End of changes. 20 change blocks. | ||||
27 lines changed or deleted | 125 lines changed or added |