diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/datatype.c | 3 | ||||
-rw-r--r--[-rwxr-xr-x] | src/evaluate.c | 54 | ||||
-rw-r--r-- | src/main.c | 12 | ||||
-rw-r--r-- | src/meta.c | 261 | ||||
-rw-r--r-- | src/parser_bison.y | 9 | ||||
-rw-r--r-- | src/scanner.l | 1 |
6 files changed, 339 insertions, 1 deletions
diff --git a/src/datatype.c b/src/datatype.c index c5a01346..873f7d4d 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -71,6 +71,9 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = { [TYPE_BOOLEAN] = &boolean_type, [TYPE_IFNAME] = &ifname_type, [TYPE_IGMP_TYPE] = &igmp_type_type, + [TYPE_TIME_DATE] = &date_type, + [TYPE_TIME_HOUR] = &hour_type, + [TYPE_TIME_DAY] = &day_type, }; const struct datatype *datatype_lookup(enum datatypes type) diff --git a/src/evaluate.c b/src/evaluate.c index a707f5e7..8d5f5f80 100755..100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -1683,14 +1683,68 @@ static int binop_transfer(struct eval_ctx *ctx, struct expr **expr) return 0; } +static bool lhs_is_meta_hour(const struct expr *meta) +{ + if (meta->etype != EXPR_META) + return false; + + return meta->meta.key == NFT_META_TIME_HOUR || + meta->meta.key == NFT_META_TIME_DAY; +} + +static void swap_values(struct expr *range) +{ + struct expr *left_tmp; + + left_tmp = range->left; + range->left = range->right; + range->right = left_tmp; +} + +static bool range_needs_swap(const struct expr *range) +{ + const struct expr *right = range->right; + const struct expr *left = range->left; + + return mpz_cmp(left->value, right->value) > 0; +} + static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) { struct expr *rel = *expr, *left, *right; + struct expr *range; + int ret; if (expr_evaluate(ctx, &rel->left) < 0) return -1; left = rel->left; + if (rel->right->etype == EXPR_RANGE && lhs_is_meta_hour(rel->left)) { + ret = __expr_evaluate_range(ctx, &rel->right); + if (ret) + return ret; + + range = rel->right; + + /* + * We may need to do this for proper cross-day ranges, + * e.g. meta hour 23:15-03:22 + */ + if (range_needs_swap(range)) { + if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) + nft_print(&ctx->nft->output, + "Inverting range values for cross-day hour matching\n\n"); + + if (rel->op == OP_EQ || rel->op == OP_IMPLICIT) { + swap_values(range); + rel->op = OP_NEQ; + } else if (rel->op == OP_NEQ) { + swap_values(range); + rel->op = OP_EQ; + } + } + } + if (expr_evaluate(ctx, &rel->right) < 0) return -1; right = rel->right; @@ -42,9 +42,10 @@ enum opt_vals { OPT_GUID = 'u', OPT_NUMERIC_PRIO = 'y', OPT_NUMERIC_PROTO = 'p', + OPT_NUMERIC_TIME = 't', OPT_INVALID = '?', }; -#define OPTSTRING "hvcf:iI:jvnsNaeSupyp" +#define OPTSTRING "hvcf:iI:jvnsNaeSupypt" static const struct option options[] = { { @@ -115,6 +116,10 @@ static const struct option options[] = { .val = OPT_NUMERIC_PRIO, }, { + .name = "numeric-time", + .val = OPT_NUMERIC_TIME, + }, + { .name = NULL } }; @@ -140,6 +145,7 @@ static void show_help(const char *name) " -S, --service Translate ports to service names as described in /etc/services.\n" " -p, --numeric-protocol Print layer 4 protocols numerically.\n" " -y, --numeric-priority Print chain priority numerically.\n" +" -t, --numeric-time Print time values numerically.\n" " -a, --handle Output rule handle.\n" " -e, --echo Echo what has been added, inserted or replaced.\n" " -I, --includepath <directory> Add <directory> to the paths searched for include files. Default is: %s\n" @@ -229,6 +235,7 @@ int main(int argc, char * const *argv) break; case OPT_NUMERIC: output_flags |= NFT_CTX_OUTPUT_NUMERIC_ALL; + output_flags |= NFT_CTX_OUTPUT_NUMERIC_TIME; break; case OPT_STATELESS: output_flags |= NFT_CTX_OUTPUT_STATELESS; @@ -291,6 +298,9 @@ int main(int argc, char * const *argv) case OPT_NUMERIC_PROTO: output_flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO; break; + case OPT_NUMERIC_TIME: + output_flags |= NFT_CTX_OUTPUT_NUMERIC_TIME; + break; case OPT_INVALID: exit(EXIT_FAILURE); } @@ -37,6 +37,10 @@ #include <iface.h> #include <json.h> +#define _XOPEN_SOURCE +#define __USE_XOPEN +#include <time.h> + static void tchandle_type_print(const struct expr *expr, struct output_ctx *octx) { @@ -375,6 +379,254 @@ const struct datatype ifname_type = { .basetype = &string_type, }; +static void date_type_print(const struct expr *expr, struct output_ctx *octx) +{ + uint64_t tstamp = mpz_get_uint64(expr->value); + struct tm *tm, *cur_tm; + char timestr[21]; + + /* Convert from nanoseconds to seconds */ + tstamp /= 1000000000L; + + if (!nft_output_seconds(octx)) { + /* Obtain current tm, to add tm_gmtoff to the timestamp */ + cur_tm = localtime((time_t *) &tstamp); + + if (cur_tm) + tstamp += cur_tm->tm_gmtoff; + + if ((tm = gmtime((time_t *) &tstamp)) != NULL && + strftime(timestr, sizeof(timestr) - 1, "%F %T", tm)) + nft_print(octx, "\"%s\"", timestr); + else + nft_print(octx, "Error converting timestamp to printed time"); + + return; + } + + /* + * Do our own printing. The default print function will print in + * nanoseconds, which is ugly. + */ + nft_print(octx, "%lu", tstamp); +} + +static time_t parse_iso_date(const char *sym) +{ + struct tm tm, *cur_tm; + time_t ts; + + memset(&tm, 0, sizeof(struct tm)); + + if (strptime(sym, "%F %T", &tm)) + goto success; + if (strptime(sym, "%F %R", &tm)) + goto success; + if (strptime(sym, "%F", &tm)) + goto success; + + return -1; + +success: + /* + * Overwriting TZ is problematic if we're parsing hour types in this same process, + * hence I'd rather use timegm() which doesn't take into account the TZ env variable, + * even though it's Linux-specific. + */ + ts = timegm(&tm); + + /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */ + cur_tm = localtime(&ts); + + if (ts == (time_t) -1 || cur_tm == NULL) + return ts; + + /* Substract tm_gmtoff to get the current time */ + return ts - cur_tm->tm_gmtoff; +} + +static struct error_record *date_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + const char *endptr = sym->identifier; + time_t tstamp; + + if ((tstamp = parse_iso_date(sym->identifier)) != -1) + goto success; + + tstamp = strtoul(sym->identifier, (char **) &endptr, 10); + if (*endptr == '\0' && endptr != sym->identifier) + goto success; + + return error(&sym->location, "Cannot parse date"); + +success: + /* Convert to nanoseconds */ + tstamp *= 1000000000L; + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(uint64_t) * BITS_PER_BYTE, + &tstamp); + return NULL; +} + +static const struct symbol_table day_type_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("Sunday", 0), + SYMBOL("Monday", 1), + SYMBOL("Tuesday", 2), + SYMBOL("Wednesday", 3), + SYMBOL("Thursday", 4), + SYMBOL("Friday", 5), + SYMBOL("Saturday", 6), + SYMBOL_LIST_END, + }, +}; + +static void day_type_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(&day_type_tbl, expr, true, octx); +} + +static void __hour_type_print_r(int hours, int minutes, int seconds, char *out, size_t buflen) +{ + if (minutes == 60) + return __hour_type_print_r(++hours, 0, seconds, out, buflen); + else if (minutes > 60) + return __hour_type_print_r((int) (minutes / 60), minutes % 60, seconds, out, buflen); + + if (seconds == 60) + return __hour_type_print_r(hours, ++minutes, 0, out, buflen); + else if (seconds > 60) + return __hour_type_print_r(hours, (int) (seconds / 60), seconds % 60, out, buflen); + + if (seconds == 0) + snprintf(out, buflen, "%02d:%02d", hours, minutes); + else + snprintf(out, buflen, "%02d:%02d:%02d", hours, minutes, seconds); +} + +static void hour_type_print(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t seconds = mpz_get_uint32(expr->value); + struct tm *cur_tm; + char out[32]; + time_t ts; + + if (!nft_output_seconds(octx)) { + /* Obtain current tm, so that we can add tm_gmtoff */ + ts = time(NULL); + cur_tm = localtime(&ts); + + if (cur_tm) + seconds = (seconds + cur_tm->tm_gmtoff) % 86400; + + __hour_type_print_r(0, 0, seconds, out, sizeof(out)); + nft_print(octx, "\"%s\"", out); + + return; + } + + expr_basetype(expr)->print(expr, octx); +} + +static struct error_record *hour_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct error_record *er; + struct tm tm, *cur_tm; + uint64_t result = 0; + char *endptr; + time_t ts; + + memset(&tm, 0, sizeof(struct tm)); + + /* First, try to parse it as a number */ + result = strtoul(sym->identifier, (char **) &endptr, 10); + if (*endptr == '\0' && endptr != sym->identifier) + goto success; + + result = 0; + + /* Obtain current tm, so that we can substract tm_gmtoff */ + ts = time(NULL); + cur_tm = localtime(&ts); + + endptr = strptime(sym->identifier, "%T", &tm); + if (endptr && *endptr == '\0') + goto convert; + + endptr = strptime(sym->identifier, "%R", &tm); + if (endptr && *endptr == '\0') + goto convert; + + if (endptr && *endptr) + return error(&sym->location, "Can't parse trailing input: \"%s\"\n", endptr); + + if ((er = time_parse(&sym->location, sym->identifier, &result)) == NULL) { + result /= 1000; + goto convert; + } + + return er; + +convert: + /* Convert the hour to the number of seconds since midnight */ + if (result == 0) + result = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec; + + /* Substract tm_gmtoff to get the current time */ + if (cur_tm) { + if ((long int) result >= cur_tm->tm_gmtoff) + result = (result - cur_tm->tm_gmtoff) % 86400; + else + result = 86400 - cur_tm->tm_gmtoff + result; + } + +success: + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(uint32_t) * BITS_PER_BYTE, + &result); + return NULL; +} + +const struct datatype date_type = { + .type = TYPE_TIME_DATE, + .name = "time", + .desc = "Relative time of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(uint64_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = date_type_print, + .parse = date_type_parse, +}; + +const struct datatype day_type = { + .type = TYPE_TIME_DAY, + .name = "day", + .desc = "Day of week of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 1 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = day_type_print, + .sym_tbl = &day_type_tbl, +}; + +const struct datatype hour_type = { + .type = TYPE_TIME_HOUR, + .name = "hour", + .desc = "Hour of day of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(uint64_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = hour_type_print, + .parse = hour_type_parse, +}; + const struct meta_template meta_templates[] = { [NFT_META_LEN] = META_TEMPLATE("length", &integer_type, 4 * 8, BYTEORDER_HOST_ENDIAN), @@ -448,6 +700,15 @@ const struct meta_template meta_templates[] = { [NFT_META_BRI_IIFVPROTO] = META_TEMPLATE("ibrvproto", ðertype_type, 2 * BITS_PER_BYTE, BYTEORDER_BIG_ENDIAN), + [NFT_META_TIME_NS] = META_TEMPLATE("time", &date_type, + 8 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_TIME_DAY] = META_TEMPLATE("day", &day_type, + 1 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_TIME_HOUR] = META_TEMPLATE("hour", &hour_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), }; static bool meta_key_is_unqualified(enum nft_meta_keys key) diff --git a/src/parser_bison.y b/src/parser_bison.y index 5fb3a60a..b7db1a21 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -431,6 +431,7 @@ int nft_lex(void *, void *, void *); %token IIFGROUP "iifgroup" %token OIFGROUP "oifgroup" %token CGROUP "cgroup" +%token TIME "time" %token CLASSID "classid" %token NEXTHOP "nexthop" @@ -1827,6 +1828,11 @@ data_type_atom_expr : type_identifier dtype->size, NULL); xfree($1); } + | TIME + { + $$ = constant_expr_alloc(&@1, &time_type, time_type.byteorder, + time_type.size, NULL); + } ; data_type_expr : data_type_atom_expr @@ -4050,6 +4056,9 @@ meta_key_unqualified : MARK { $$ = NFT_META_MARK; } | OIFGROUP { $$ = NFT_META_OIFGROUP; } | CGROUP { $$ = NFT_META_CGROUP; } | IPSEC { $$ = NFT_META_SECPATH; } + | TIME { $$ = NFT_META_TIME_NS; } + | DAY { $$ = NFT_META_TIME_DAY; } + | HOUR { $$ = NFT_META_TIME_HOUR; } ; meta_stmt : META meta_key SET stmt_expr diff --git a/src/scanner.l b/src/scanner.l index c1adcbdd..fdf84ba7 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -426,6 +426,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "sack3" { return SACK3; } "sack-permitted" { return SACK_PERMITTED; } "timestamp" { return TIMESTAMP; } +"time" { return TIME; } "kind" { return KIND; } "count" { return COUNT; } |