From: Timo Sirainen Date: Fri, 9 Dec 2022 21:02:21 +0000 (+0200) Subject: lib: Add "ip" type for events and "ip/bits" matching for event filters X-Git-Tag: 2.4.0~3096 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f0d3b7fcd1b090f1cb32614a3d5edf07c17b43aa;p=thirdparty%2Fdovecot%2Fcore.git lib: Add "ip" type for events and "ip/bits" matching for event filters --- diff --git a/src/lib/event-filter-parser.y b/src/lib/event-filter-parser.y index 2638195a8c..3f0d60c8b8 100644 --- a/src/lib/event-filter-parser.y +++ b/src/lib/event-filter-parser.y @@ -104,6 +104,11 @@ static struct event_filter_node *key_value(struct event_filter_parser_state *sta /* Leave a hint that this is in fact a valid number. */ node->field.value_type = EVENT_FIELD_VALUE_TYPE_INTMAX; node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT; + } else if (net_parse_range(b, &node->field.value.ip, + &node->field.value.ip_bits) == 0) { + /* Leave a hint that this is in fact a valid IP. */ + node->field.value_type = EVENT_FIELD_VALUE_TYPE_IP; + node->type = EVENT_FILTER_NODE_TYPE_EVENT_FIELD_EXACT; } else { /* This field contains no valid number. Either this is a string that contains a size unit, a diff --git a/src/lib/event-filter-private.h b/src/lib/event-filter-private.h index cdf5036f09..765da28bab 100644 --- a/src/lib/event-filter-private.h +++ b/src/lib/event-filter-private.h @@ -88,6 +88,7 @@ struct event_filter_node { bool ambiguous_unit:1; bool warned_ambiguous_unit:1; bool warned_string_inequality:1; + bool warned_ip_inequality:1; bool warned_type_mismatch:1; bool warned_timeval_not_implemented:1; }; diff --git a/src/lib/event-filter.c b/src/lib/event-filter.c index 39cf64b6bd..87e995e322 100644 --- a/src/lib/event-filter.c +++ b/src/lib/event-filter.c @@ -691,6 +691,53 @@ event_match_field(struct event *event, struct event_filter_node *node, } return FALSE; } + case EVENT_FIELD_VALUE_TYPE_IP: + if (node->op != EVENT_FILTER_OP_CMP_EQ) { + /* we only support IP equality comparisons */ + if (!node->warned_ip_inequality) { + const char *name = event->sending_name; + /* Use i_warning to prevent event filter recursions. */ + i_warning("Event filter for IP field '%s' " + "only supports equality operation " + "'=' not '%s'. (event=%s, source=%s:%u)", + wanted_field->key, + event_filter_export_query_expr_op(node->op), + name != NULL ? name : "", + source_filename, source_linenum); + node->warned_ip_inequality = TRUE; + } + return FALSE; + } + if (wanted_field->value_type == EVENT_FIELD_VALUE_TYPE_IP) { + return net_is_in_network(&field->value.ip, + &wanted_field->value.ip, + wanted_field->value.ip_bits); + } + if (use_strcmp) { + /* If the matched value was a number, it was already + matched in the previous branch. So here we have a + non-wildcard IP, which can never be a match to an + IP. */ + if (!node->warned_type_mismatch) { + const char *name = event->sending_name; + /* Use i_warning to prevent event filter recursions. */ + i_warning("Event filter matches IP field " + "'%s' against non-IP value '%s'. " + "(event=%s, source=%s:%u)", + wanted_field->key, + wanted_field->value.str, + name != NULL ? name : "", + source_filename, source_linenum); + node->warned_type_mismatch = TRUE; + } + return FALSE; + } + bool ret; + T_BEGIN { + ret = wildcard_match_icase(net_ip2addr(&field->value.ip), + wanted_field->value.str); + } T_END; + return ret; case EVENT_FIELD_VALUE_TYPE_STRLIST: /* check if the value is (or is not) on the list, only string matching makes sense here. */ diff --git a/src/lib/lib-event.c b/src/lib/lib-event.c index 4059bbbf13..8c40848ba9 100644 --- a/src/lib/lib-event.c +++ b/src/lib/lib-event.c @@ -22,6 +22,7 @@ enum event_code { EVENT_CODE_FIELD_INTMAX = 'I', EVENT_CODE_FIELD_STR = 'S', EVENT_CODE_FIELD_TIMEVAL = 'T', + EVENT_CODE_FIELD_IP = 'P', EVENT_CODE_FIELD_STRLIST = 'L', }; @@ -180,6 +181,9 @@ void event_copy_fields(struct event *to, struct event *from) case EVENT_FIELD_VALUE_TYPE_TIMEVAL: event_add_timeval(to, fld->key, &fld->value.timeval); break; + case EVENT_FIELD_VALUE_TYPE_IP: + event_add_ip(to, fld->key, &fld->value.ip); + break; case EVENT_FIELD_VALUE_TYPE_STRLIST: values = array_get(&fld->value.strlist, &count); for (unsigned int i = 0; i < count; i++) @@ -1014,6 +1018,8 @@ event_find_field_recursive_str(const struct event *event, const char *key) return t_strdup_printf("%"PRIdTIME_T".%u", field->value.timeval.tv_sec, (unsigned int)field->value.timeval.tv_usec); + case EVENT_FIELD_VALUE_TYPE_IP: + return net_ip2addr(&field->value.ip); case EVENT_FIELD_VALUE_TYPE_STRLIST: { ARRAY_TYPE(const_string) list; t_array_init(&list, 8); @@ -1158,6 +1164,25 @@ event_add_timeval(struct event *event, const char *key, return event; } +struct event * +event_add_ip(struct event *event, const char *key, const struct ip_addr *ip) +{ + struct event_field *field; + + if (ip->family == 0) { + /* ignore nonexistent IP (similar to + event_add_str(value=NULL)) */ + if (event_find_field_recursive(event, key) != NULL) + event_field_clear(event, key); + return event; + } + + field = event_get_field(event, key, TRUE); + field->value_type = EVENT_FIELD_VALUE_TYPE_IP; + field->value.ip = *ip; + return event; +} + struct event * event_add_fields(struct event *event, const struct event_add_field *fields) @@ -1168,6 +1193,8 @@ event_add_fields(struct event *event, else if (fields[i].value_timeval.tv_sec != 0) { event_add_timeval(event, fields[i].key, &fields[i].value_timeval); + } else if (fields[i].value_ip.family != 0) { + event_add_ip(event, fields[i].key, &fields[i].value_ip); } else { event_add_int(event, fields[i].key, fields[i].value_intmax); @@ -1293,6 +1320,11 @@ event_export_field_value(string_t *dest, const struct event_field *field) field->value.timeval.tv_sec, (unsigned int)field->value.timeval.tv_usec); break; + case EVENT_FIELD_VALUE_TYPE_IP: + str_append_c(dest, EVENT_CODE_FIELD_IP); + str_append_tabescaped(dest, field->key); + str_printfa(dest, "\t%s", net_ip2addr(&field->value.ip)); + break; case EVENT_FIELD_VALUE_TYPE_STRLIST: { unsigned int count; const char *const *strlist = @@ -1456,6 +1488,15 @@ event_import_field(struct event *event, enum event_code code, const char *arg, } args++; break; + case EVENT_CODE_FIELD_IP: + field->value_type = EVENT_FIELD_VALUE_TYPE_IP; + if (net_addr2ip(*args, &field->value.ip) < 0) { + *error_r = t_strdup_printf( + "Invalid field value '%s' IP for '%s'", + *args, field->key); + return FALSE; + } + break; case EVENT_CODE_FIELD_STRLIST: if (!event_import_strlist(event, field, &args, error_r)) return FALSE; @@ -1526,7 +1567,8 @@ event_import_arg(struct event *event, const char *const **_args, case EVENT_CODE_FIELD_INTMAX: case EVENT_CODE_FIELD_STR: case EVENT_CODE_FIELD_STRLIST: - case EVENT_CODE_FIELD_TIMEVAL: { + case EVENT_CODE_FIELD_TIMEVAL: + case EVENT_CODE_FIELD_IP: { args++; if (!event_import_field(event, code, arg, &args, error_r)) return FALSE; @@ -1710,6 +1752,13 @@ event_passthrough_add_timeval(const char *key, const struct timeval *tv) return &event_passthrough_vfuncs; } +static struct event_passthrough * +event_passthrough_add_ip(const char *key, const struct ip_addr *ip) +{ + event_add_ip(last_passthrough_event(), key, ip); + return &event_passthrough_vfuncs; +} + static struct event_passthrough * event_passthrough_inc_int(const char *key, intmax_t num) { @@ -1744,6 +1793,7 @@ struct event_passthrough event_passthrough_vfuncs = { .add_int = event_passthrough_add_int, .add_int_nonzero = event_passthrough_add_int_nonzero, .add_timeval = event_passthrough_add_timeval, + .add_ip = event_passthrough_add_ip, .inc_int = event_passthrough_inc_int, .strlist_append = event_passthrough_strlist_append, .strlist_replace = event_passthrough_strlist_replace, diff --git a/src/lib/lib-event.h b/src/lib/lib-event.h index 1cee5af173..b3bdf05302 100644 --- a/src/lib/lib-event.h +++ b/src/lib/lib-event.h @@ -3,6 +3,7 @@ /* event.h name is probably a bit too generic, so lets avoid using it. */ #include +#include "net.h" /* Field name for the reason_code string list. */ #define EVENT_REASON_CODE "reason_code" @@ -32,6 +33,7 @@ enum event_field_value_type { EVENT_FIELD_VALUE_TYPE_STR, EVENT_FIELD_VALUE_TYPE_INTMAX, EVENT_FIELD_VALUE_TYPE_TIMEVAL, + EVENT_FIELD_VALUE_TYPE_IP, EVENT_FIELD_VALUE_TYPE_STRLIST, }; @@ -42,6 +44,8 @@ struct event_field { const char *str; intmax_t intmax; struct timeval timeval; + struct ip_addr ip; + unsigned int ip_bits; /* set for event filters */ ARRAY_TYPE(const_string) strlist; } value; }; @@ -52,6 +56,7 @@ struct event_add_field { const char *value; intmax_t value_intmax; struct timeval value_timeval; + struct ip_addr value_ip; }; struct event_passthrough { @@ -85,6 +90,8 @@ struct event_passthrough { (*add_int_nonzero)(const char *key, intmax_t num); struct event_passthrough * (*add_timeval)(const char *key, const struct timeval *tv); + struct event_passthrough * + (*add_ip)(const char *key, const struct ip_addr *ip); struct event_passthrough * (*inc_int)(const char *key, intmax_t num); @@ -354,6 +361,8 @@ event_inc_int(struct event *event, const char *key, intmax_t num); struct event * event_add_timeval(struct event *event, const char *key, const struct timeval *tv); +struct event * +event_add_ip(struct event *event, const char *key, const struct ip_addr *ip); /* Append new value to list. If the key is not a list, it will be cleared first. NULL values are ignored. Duplicate values are ignored. */ struct event * diff --git a/src/lib/test-event-filter-parser.c b/src/lib/test-event-filter-parser.c index 03e2a65d7d..63137d89c9 100644 --- a/src/lib/test-event-filter-parser.c +++ b/src/lib/test-event-filter-parser.c @@ -52,6 +52,13 @@ static const char *values_single[] = { "foo.c", "foo.c:123", + "0", + "123", + "123*", + + "127.0.0.1", + "127.0.0.*", + /* wildcards */ "*foo", "f*o", diff --git a/src/lib/test-event-filter.c b/src/lib/test-event-filter.c index 6de95b2ed2..025472fb04 100644 --- a/src/lib/test-event-filter.c +++ b/src/lib/test-event-filter.c @@ -693,6 +693,131 @@ static void test_event_filter_numbers(void) test_end(); } +static struct ip_addr test_addr2ip(const char *addr) +{ + struct ip_addr ip; + if (net_addr2ip(addr, &ip) < 0) + i_unreached(); + return ip; +} + +static void test_event_filter_ips(void) +{ + struct event_filter *filter; + const char *error; + struct ip_addr ip; + const struct failure_context failure_ctx = { + .type = LOG_TYPE_DEBUG + }; + + test_begin("event filter: event ip matching"); + + filter = event_filter_create(); + test_assert(event_filter_parse("ip = 127.0.0.1", filter, &error) == 0); + + struct event *e = event_create(NULL); + /* ip match */ + test_assert(net_addr2ip("127.0.0.1", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* ip mismatch */ + test_assert(net_addr2ip("127.0.0.2", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* string ip match */ + event_add_str(e, "ip", "127.0.0.1"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* numeric ip mismatch */ + event_add_int(e, "ip", 2130706433); + test_expect_error_string("Event filter matches integer field 'ip' " + "against non-integer value '127.0.0.1'"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + test_expect_no_more_errors(); + event_filter_unref(&filter); + + filter = event_filter_create(); + test_assert(event_filter_parse("ip = 127.0.0.*", filter, &error) == 0); + /* wildcard match */ + test_assert(net_addr2ip("127.0.0.1", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* wildcard mismatch */ + test_assert(net_addr2ip("127.0.1.1", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* wildcard match as string */ + event_add_str(e, "ip", "127.0.0.3"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + filter = event_filter_create(); + test_assert(event_filter_parse("ip = \"127.0.0.0/16\"", filter, &error) == 0); + /* network mask match */ + test_assert(net_addr2ip("127.0.255.255", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(event_filter_match(filter, e, &failure_ctx)); + /* network mask mismatch */ + test_assert(net_addr2ip("127.1.255.255", &ip) == 0); + event_add_ip(e, "ip", &ip); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* network mask mismatch as string */ + event_add_str(e, "ip", "127.0.123.45"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + /* network mask match as string */ + event_add_str(e, "ip", "127.0.0.0/16"); + test_assert(event_filter_match(filter, e, &failure_ctx)); + event_filter_unref(&filter); + + filter = event_filter_create(); + test_assert(event_filter_parse("ip = fish", filter, &error) == 0); + event_add_ip(e, "ip", &ip); + test_expect_error_string("Event filter matches IP field 'ip' " + "against non-IP value 'fish'"); + test_assert(!event_filter_match(filter, e, &failure_ctx)); + test_expect_no_more_errors(); + event_filter_unref(&filter); + + const struct { + const char *filter; + struct ip_addr ip; + bool match; + } tests[] = { + { "ip = ::1", test_addr2ip("::1"), TRUE }, + { "ip = ::2", test_addr2ip("::1"), FALSE }, + + { "ip = \"::1/128\"", test_addr2ip("::1"), TRUE }, + { "ip = \"::1/126\"", test_addr2ip("::2"), TRUE }, + { "ip = \"::1/126\"", test_addr2ip("::3"), TRUE }, + { "ip = \"::1/126\"", test_addr2ip("::4"), FALSE }, + + { "ip = \"2001::/8\"", test_addr2ip("2001::1"), TRUE }, + { "ip = \"2001::/8\"", test_addr2ip("20ff:ffff::1"), TRUE }, + { "ip = \"2001::/8\"", test_addr2ip("2100::1"), FALSE }, + + { "ip = 2001::1", test_addr2ip("2001::1"), TRUE }, + { "ip = \"2001::1\"", test_addr2ip("2001::1"), TRUE }, + { "ip = 2001:0:0:0:0:0:0:1", test_addr2ip("2001::1"), TRUE }, + { "ip = 2001::1", test_addr2ip("2001::2"), FALSE }, + + { "ip = 2000:1190:c02a:130:a87a:ad7:5b76:3310", + test_addr2ip("2000:1190:c02a:130:a87a:ad7:5b76:3310"), TRUE }, + { "ip = 2001:1190:c02a:130:a87a:ad7:5b76:3310", + test_addr2ip("2000:1190:c02a:130:a87a:ad7:5b76:3310"), FALSE }, + + { "ip = \"fe80::1%lo\"", test_addr2ip("fe80::1%lo"), TRUE }, + }; + for (unsigned int i = 0; i < N_ELEMENTS(tests); i++) { + filter = event_filter_create(); + test_assert_idx(event_filter_parse(tests[i].filter, filter, &error) == 0, i); + event_add_ip(e, "ip", &tests[i].ip); + test_assert_idx(event_filter_match(filter, e, &failure_ctx) == tests[i].match, i); + event_filter_unref(&filter); + } + + event_unref(&e); + test_end(); +} + static void test_event_filter_size_values(void) { const char *error; @@ -963,6 +1088,7 @@ void test_event_filter(void) test_event_filter_named_separate_from_str(); test_event_filter_duration(); test_event_filter_numbers(); + test_event_filter_ips(); test_event_filter_size_values(); test_event_filter_interval_values(); test_event_filter_ambiguous_units(); diff --git a/src/lib/test-event-flatten.c b/src/lib/test-event-flatten.c index 526366c4ae..397e817da9 100644 --- a/src/lib/test-event-flatten.c +++ b/src/lib/test-event-flatten.c @@ -59,6 +59,10 @@ static void check_event_diff_fields(const struct event_field *got, unsigned int test_assert(timeval_cmp(&exp[i].value.timeval, &got[i].value.timeval) == 0); break; + case EVENT_FIELD_VALUE_TYPE_IP: + test_assert(net_ip_compare(&exp[i].value.ip, + &got[i].value.ip)); + break; case EVENT_FIELD_VALUE_TYPE_STRLIST: got_str = t_array_const_string_join(&got[i].value.strlist, ","); test_assert_strcmp(exp[i].value.str, got_str); diff --git a/src/lib/test-lib-event.c b/src/lib/test-lib-event.c index 22beb664eb..26c4c22e0e 100644 --- a/src/lib/test-lib-event.c +++ b/src/lib/test-lib-event.c @@ -8,9 +8,12 @@ static void test_event_fields(void) test_begin("event fields"); struct event *event = event_create(NULL); struct event_field *field; + struct ip_addr ip = { .family = 0 }; event_add_str(event, "key", NULL); test_assert(event_find_field_nonrecursive(event, "key") == NULL); + event_add_ip(event, "key", &ip); + test_assert(event_find_field_nonrecursive(event, "key") == NULL); event_add_str(event, "key", "value1"); field = event_find_field_nonrecursive(event, "key"); @@ -34,6 +37,19 @@ static void test_event_fields(void) field->value.timeval.tv_sec == tv.tv_sec && field->value.timeval.tv_usec == tv.tv_usec); + if (net_addr2ip("1002::4301:6", &ip) < 0) + i_unreached(); + event_add_ip(event, "key", &ip); + field = event_find_field_nonrecursive(event, "key"); + test_assert(field != NULL && field->value_type == EVENT_FIELD_VALUE_TYPE_IP && + net_ip_compare(&field->value.ip, &ip)); + + ip.family = 0; + event_add_ip(event, "key", &ip); + field = event_find_field_nonrecursive(event, "key"); + test_assert(field != NULL && field->value_type == EVENT_FIELD_VALUE_TYPE_STR && + field->value.str[0] == '\0'); + event_strlist_append(event, "key", "strlist1"); field = event_find_field_nonrecursive(event, "key"); test_assert(field != NULL && field->value_type == EVENT_FIELD_VALUE_TYPE_STRLIST && diff --git a/src/stats/event-exporter-fmt-json.c b/src/stats/event-exporter-fmt-json.c index 25c6116af4..1c563ecbec 100644 --- a/src/stats/event-exporter-fmt-json.c +++ b/src/stats/event-exporter-fmt-json.c @@ -72,6 +72,13 @@ static void append_time(string_t *dest, const struct timeval *time, } } +static void append_ip(string_t *dest, const struct ip_addr *ip) +{ + str_append_c(dest, '"'); + str_append(dest, net_ip2addr(ip)); + str_append_c(dest, '"'); +} + static void append_field_value(string_t *dest, const struct event_field *field, const struct metric_export_info *info) { @@ -86,6 +93,9 @@ static void append_field_value(string_t *dest, const struct event_field *field, append_time(dest, &field->value.timeval, info->exporter->time_format); break; + case EVENT_FIELD_VALUE_TYPE_IP: + append_ip(dest, &field->value.ip); + break; case EVENT_FIELD_VALUE_TYPE_STRLIST: append_strlist(dest, &field->value.strlist, info); break; diff --git a/src/stats/event-exporter-fmt-tab-text.c b/src/stats/event-exporter-fmt-tab-text.c index 49a065bcfd..a68f10a3c6 100644 --- a/src/stats/event-exporter-fmt-tab-text.c +++ b/src/stats/event-exporter-fmt-tab-text.c @@ -47,6 +47,11 @@ static void append_time(string_t *dest, const struct timeval *time, } } +static void append_ip(string_t *dest, const struct ip_addr *ip) +{ + str_append(dest, net_ip2addr(ip)); +} + static void append_field_str(string_t *dest, const char *str, const struct metric_export_info *info) { @@ -75,6 +80,9 @@ static void append_field_value(string_t *dest, const struct event_field *field, append_time(dest, &field->value.timeval, info->exporter->time_format); break; + case EVENT_FIELD_VALUE_TYPE_IP: + append_ip(dest, &field->value.ip); + break; case EVENT_FIELD_VALUE_TYPE_STRLIST: append_strlist(dest, &field->value.strlist); break; diff --git a/src/stats/stats-metrics.c b/src/stats/stats-metrics.c index 632c4865b6..5c92a31783 100644 --- a/src/stats/stats-metrics.c +++ b/src/stats/stats-metrics.c @@ -340,6 +340,11 @@ stats_metric_find_sub_metric(struct metric *metric, if (sub_metrics->group_value.intmax == value->intmax) return sub_metrics; break; + case METRIC_VALUE_TYPE_IP: + if (net_ip_compare(&sub_metrics->group_value.ip, + &value->ip)) + return sub_metrics; + break; case METRIC_VALUE_TYPE_BUCKET_INDEX: if (sub_metrics->group_value.intmax == value->intmax) return sub_metrics; @@ -383,6 +388,10 @@ stats_metric_group_by_discrete(const struct event_field *field, return TRUE; case EVENT_FIELD_VALUE_TYPE_TIMEVAL: return FALSE; + case EVENT_FIELD_VALUE_TYPE_IP: + value_r->type = METRIC_VALUE_TYPE_IP; + value_r->ip = field->value.ip; + return TRUE; case EVENT_FIELD_VALUE_TYPE_STRLIST: return FALSE; } @@ -399,6 +408,7 @@ stats_metric_group_by_quantized(const struct event_field *field, switch (field->value_type) { case EVENT_FIELD_VALUE_TYPE_STR: case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + case EVENT_FIELD_VALUE_TYPE_IP: case EVENT_FIELD_VALUE_TYPE_STRLIST: return FALSE; case EVENT_FIELD_VALUE_TYPE_INTMAX: @@ -433,6 +443,7 @@ stats_metric_group_by_quantized_label(const struct event_field *field, switch (field->value_type) { case EVENT_FIELD_VALUE_TYPE_STR: case EVENT_FIELD_VALUE_TYPE_TIMEVAL: + case EVENT_FIELD_VALUE_TYPE_IP: case EVENT_FIELD_VALUE_TYPE_STRLIST: i_unreached(); case EVENT_FIELD_VALUE_TYPE_INTMAX: @@ -495,6 +506,8 @@ stats_metric_group_by_value_label(const struct event_field *field, return field->value.str; case METRIC_VALUE_TYPE_INT: return dec2str(field->value.intmax); + case METRIC_VALUE_TYPE_IP: + return net_ip2addr(&field->value.ip); case METRIC_VALUE_TYPE_BUCKET_INDEX: return stats_metric_group_by_get_label(field, group_by, value); } @@ -526,6 +539,7 @@ stats_metric_get_sub_metric(struct metric *metric, } sub_metric->group_value.type = value->type; sub_metric->group_value.intmax = value->intmax; + sub_metric->group_value.ip = value->ip; memcpy(sub_metric->group_value.hash, value->hash, SHA1_RESULTLEN); return sub_metric; } @@ -622,6 +636,7 @@ stats_metric_event_field(struct event *event, const char *fieldname, switch (field->value_type) { case EVENT_FIELD_VALUE_TYPE_STR: case EVENT_FIELD_VALUE_TYPE_STRLIST: + case EVENT_FIELD_VALUE_TYPE_IP: break; case EVENT_FIELD_VALUE_TYPE_INTMAX: num = field->value.intmax; diff --git a/src/stats/stats-metrics.h b/src/stats/stats-metrics.h index 6d7d7458b8..65b8b74889 100644 --- a/src/stats/stats-metrics.h +++ b/src/stats/stats-metrics.h @@ -61,6 +61,7 @@ struct metric_field { enum metric_value_type { METRIC_VALUE_TYPE_STR, METRIC_VALUE_TYPE_INT, + METRIC_VALUE_TYPE_IP, METRIC_VALUE_TYPE_BUCKET_INDEX, }; @@ -68,6 +69,7 @@ struct metric_value { enum metric_value_type type; unsigned char hash[SHA1_RESULTLEN]; intmax_t intmax; + struct ip_addr ip; }; struct metric {