]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add "ip" type for events and "ip/bits" matching for event filters
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 9 Dec 2022 21:02:21 +0000 (23:02 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 27 Jan 2023 13:01:47 +0000 (13:01 +0000)
13 files changed:
src/lib/event-filter-parser.y
src/lib/event-filter-private.h
src/lib/event-filter.c
src/lib/lib-event.c
src/lib/lib-event.h
src/lib/test-event-filter-parser.c
src/lib/test-event-filter.c
src/lib/test-event-flatten.c
src/lib/test-lib-event.c
src/stats/event-exporter-fmt-json.c
src/stats/event-exporter-fmt-tab-text.c
src/stats/stats-metrics.c
src/stats/stats-metrics.h

index 2638195a8cf7c72d303d8af54a889f79619d9881..3f0d60c8b8b8db865d24ba06777b3c4a76a5dd1d 100644 (file)
@@ -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
index cdf5036f098f66ea96dface39f3f6808c93556cb..765da28babcad6077eb1a220c58c88f0bb462013 100644 (file)
@@ -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;
 };
index 39cf64b6bda6682584e2a4c6587d5c8ad25879f1..87e995e32216b9bf5705f1d31a654b17f2e8fe3e 100644 (file)
@@ -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. */
index 4059bbbf134791569448b4655e1b6399887d0572..8c40848ba979f27bcb983490b4b016ebeae4e002 100644 (file)
@@ -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,
index 1cee5af1737c076fe982f069002577221f216c6f..b3bdf05302474aa8f5ed1993f40bcb7f9b7b0cfd 100644 (file)
@@ -3,6 +3,7 @@
 /* event.h name is probably a bit too generic, so lets avoid using it. */
 
 #include <sys/time.h>
+#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 *
index 03e2a65d7dab59e657fd35151caccc92401eb750..63137d89c9b8a281cb6782153812cd582c09d464 100644 (file)
@@ -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",
index 6de95b2ed2e0ec85b28326c2b8305d4f7d0e2243..025472fb047e0ec9ab62e39177cc956384257c4e 100644 (file)
@@ -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();
index 526366c4ae60380edfce80df53f7ae87234c895c..397e817da9f3a6c9211bf0fed9ff017f2f8c6c49 100644 (file)
@@ -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);
index 22beb664eb9b8e34a6dfea30e08254d9b8f7c801..26c4c22e0e508a19ed1f15f9019b25fcdd87bf7e 100644 (file)
@@ -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 &&
index 25c6116af4f91f3abba6307c76b48853fae25b7b..1c563ecbece186669c730e5c17fb3eb2d749825a 100644 (file)
@@ -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;
index 49a065bcfd60aef6d06f36ba270a52a736257e99..a68f10a3c6904596473cb925fd36dce0510726f2 100644 (file)
@@ -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;
index 632c4865b6338ab394dabc396532f420a263dfca..5c92a317837b72fd6684cd146bc1c028c5df27fa 100644 (file)
@@ -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;
index 6d7d7458b856179250e008fd5dca0f1104d2ab30..65b8b74889820f289a684df0c34598991637b9f0 100644 (file)
@@ -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 {