diff options
Diffstat (limited to 'src/datatype.c')
-rw-r--r-- | src/datatype.c | 571 |
1 files changed, 473 insertions, 98 deletions
diff --git a/src/datatype.c b/src/datatype.c index 7382307e..d398a9c8 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -8,8 +8,8 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ -#include <stdlib.h> -#include <string.h> +#include <nft.h> + #include <inttypes.h> #include <ctype.h> /* isdigit */ #include <errno.h> @@ -18,6 +18,8 @@ #include <linux/types.h> #include <linux/netfilter.h> #include <linux/icmpv6.h> +#include <dirent.h> +#include <sys/stat.h> #include <nftables.h> #include <datatype.h> @@ -26,6 +28,8 @@ #include <erec.h> #include <netlink.h> #include <json.h> +#include <misspell.h> +#include "nftutils.h" #include <netinet/ip_icmp.h> @@ -60,6 +64,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = { [TYPE_CT_DIR] = &ct_dir_type, [TYPE_CT_STATUS] = &ct_status_type, [TYPE_ICMP6_TYPE] = &icmp6_type_type, + [TYPE_CT_LABEL] = &ct_label_type, [TYPE_PKTTYPE] = &pkttype_type, [TYPE_ICMP_CODE] = &icmp_code_type, [TYPE_ICMPV6_CODE] = &icmpv6_code_type, @@ -69,11 +74,13 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = { [TYPE_ECN] = &ecn_type, [TYPE_FIB_ADDR] = &fib_addr_type, [TYPE_BOOLEAN] = &boolean_type, + [TYPE_CT_EVENTBIT] = &ct_event_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, + [TYPE_CGROUPV2] = &cgroupv2_type, }; const struct datatype *datatype_lookup(enum datatypes type) @@ -120,6 +127,7 @@ struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym, struct expr **res) { const struct datatype *dtype = sym->dtype; + struct error_record *erec; assert(sym->etype == EXPR_SYMBOL); @@ -133,11 +141,54 @@ struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym, res); } while ((dtype = dtype->basetype)); - return error(&sym->location, - "Can't parse symbolic %s expressions", + dtype = sym->dtype; + if (dtype->err) { + erec = dtype->err(sym); + if (erec) + return erec; + } + + return error(&sym->location, "Could not parse symbolic %s expression", sym->dtype->desc); } +static struct error_record *__symbol_parse_fuzzy(const struct expr *sym, + const struct symbol_table *tbl) +{ + const struct symbolic_constant *s; + struct string_misspell_state st; + + string_misspell_init(&st); + + for (s = tbl->symbols; s->identifier != NULL; s++) { + string_misspell_update(sym->identifier, s->identifier, + (void *)s->identifier, &st); + } + + if (st.obj) { + return error(&sym->location, + "Could not parse %s expression; did you you mean `%s`?", + sym->dtype->desc, st.obj); + } + + return NULL; +} + +static struct error_record *symbol_parse_fuzzy(const struct expr *sym, + const struct symbol_table *tbl) +{ + struct error_record *erec; + + if (!tbl) + return NULL; + + erec = __symbol_parse_fuzzy(sym, tbl); + if (erec) + return erec; + + return NULL; +} + struct error_record *symbolic_constant_parse(struct parse_ctx *ctx, const struct expr *sym, const struct symbol_table *tbl, @@ -160,8 +211,16 @@ struct error_record *symbolic_constant_parse(struct parse_ctx *ctx, do { if (dtype->basetype->parse) { erec = dtype->basetype->parse(ctx, sym, res); - if (erec != NULL) - return erec; + if (erec != NULL) { + struct error_record *fuzzy_erec; + + fuzzy_erec = symbol_parse_fuzzy(sym, tbl); + if (!fuzzy_erec) + return erec; + + erec_destroy(erec); + return fuzzy_erec; + } if (*res) return NULL; goto out; @@ -318,15 +377,33 @@ static void verdict_type_print(const struct expr *expr, struct output_ctx *octx) } } -static struct error_record *verdict_type_parse(struct parse_ctx *ctx, - const struct expr *sym, - struct expr **res) +static struct error_record *verdict_type_error(const struct expr *sym) { - *res = constant_expr_alloc(&sym->location, &string_type, - BYTEORDER_HOST_ENDIAN, - (strlen(sym->identifier) + 1) * BITS_PER_BYTE, - sym->identifier); - return NULL; + /* Skip jump and goto from fuzzy match to provide better error + * reporting, fall back to `jump chain' if no clue. + */ + static const char *verdict_array[] = { + "continue", "break", "return", "accept", "drop", "queue", + "stolen", NULL, + }; + struct string_misspell_state st; + int i; + + string_misspell_init(&st); + + for (i = 0; verdict_array[i] != NULL; i++) { + string_misspell_update(sym->identifier, verdict_array[i], + (void *)verdict_array[i], &st); + } + + if (st.obj) { + return error(&sym->location, "Could not parse %s; did you mean `%s'?", + sym->dtype->desc, st.obj); + } + + /* assume user would like to jump to chain as a hint. */ + return error(&sym->location, "Could not parse %s; did you mean `jump %s'?", + sym->dtype->desc, sym->identifier); } const struct datatype verdict_type = { @@ -334,7 +411,7 @@ const struct datatype verdict_type = { .name = "verdict", .desc = "netfilter verdict", .print = verdict_type_print, - .parse = verdict_type_parse, + .err = verdict_type_error, }; static const struct symbol_table nfproto_tbl = { @@ -407,6 +484,22 @@ const struct datatype integer_type = { .parse = integer_type_parse, }; +static void xinteger_type_print(const struct expr *expr, struct output_ctx *octx) +{ + nft_gmp_print(octx, "0x%Zx", expr->value); +} + +/* Alias of integer_type to print raw payload expressions in hexadecimal. */ +const struct datatype xinteger_type = { + .type = TYPE_INTEGER, + .name = "integer", + .desc = "integer", + .basetype = &integer_type, + .print = xinteger_type_print, + .json = integer_type_json, + .parse = integer_type_parse, +}; + static void string_type_print(const struct expr *expr, struct output_ctx *octx) { unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); @@ -509,27 +602,34 @@ static struct error_record *ipaddr_type_parse(struct parse_ctx *ctx, const struct expr *sym, struct expr **res) { - struct addrinfo *ai, hints = { .ai_family = AF_INET, - .ai_socktype = SOCK_DGRAM}; - struct in_addr *addr; - int err; + struct in_addr addr; - err = getaddrinfo(sym->identifier, NULL, &hints, &ai); - if (err != 0) - return error(&sym->location, "Could not resolve hostname: %s", - gai_strerror(err)); + if (nft_input_no_dns(ctx->input)) { + if (inet_pton(AF_INET, sym->identifier, &addr) != 1) + return error(&sym->location, "Invalid IPv4 address"); + } else { + struct addrinfo *ai, hints = { .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM}; + int err; - if (ai->ai_next != NULL) { + err = getaddrinfo(sym->identifier, NULL, &hints, &ai); + if (err != 0) + return error(&sym->location, "Could not resolve hostname: %s", + gai_strerror(err)); + + if (ai->ai_next != NULL) { + freeaddrinfo(ai); + return error(&sym->location, + "Hostname resolves to multiple addresses"); + } + assert(ai->ai_addr->sa_family == AF_INET); + addr = ((struct sockaddr_in *) (void *) ai->ai_addr)->sin_addr; freeaddrinfo(ai); - return error(&sym->location, - "Hostname resolves to multiple addresses"); } - addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr; *res = constant_expr_alloc(&sym->location, &ipaddr_type, BYTEORDER_BIG_ENDIAN, - sizeof(*addr) * BITS_PER_BYTE, addr); - freeaddrinfo(ai); + sizeof(addr) * BITS_PER_BYTE, &addr); return NULL; } @@ -568,27 +668,35 @@ static struct error_record *ip6addr_type_parse(struct parse_ctx *ctx, const struct expr *sym, struct expr **res) { - struct addrinfo *ai, hints = { .ai_family = AF_INET6, - .ai_socktype = SOCK_DGRAM}; - struct in6_addr *addr; - int err; + struct in6_addr addr; + + if (nft_input_no_dns(ctx->input)) { + if (inet_pton(AF_INET6, sym->identifier, &addr) != 1) + return error(&sym->location, "Invalid IPv6 address"); + } else { + struct addrinfo *ai, hints = { .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM}; + int err; + + err = getaddrinfo(sym->identifier, NULL, &hints, &ai); + if (err != 0) + return error(&sym->location, "Could not resolve hostname: %s", + gai_strerror(err)); - err = getaddrinfo(sym->identifier, NULL, &hints, &ai); - if (err != 0) - return error(&sym->location, "Could not resolve hostname: %s", - gai_strerror(err)); + if (ai->ai_next != NULL) { + freeaddrinfo(ai); + return error(&sym->location, + "Hostname resolves to multiple addresses"); + } - if (ai->ai_next != NULL) { + assert(ai->ai_addr->sa_family == AF_INET6); + addr = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_addr; freeaddrinfo(ai); - return error(&sym->location, - "Hostname resolves to multiple addresses"); } - addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; *res = constant_expr_alloc(&sym->location, &ip6addr_type, BYTEORDER_BIG_ENDIAN, - sizeof(*addr) * BITS_PER_BYTE, addr); - freeaddrinfo(ai); + sizeof(addr) * BITS_PER_BYTE, &addr); return NULL; } @@ -607,23 +715,36 @@ const struct datatype ip6addr_type = { static void inet_protocol_type_print(const struct expr *expr, struct output_ctx *octx) { - struct protoent *p; + if (!nft_output_numeric_proto(octx) && + mpz_cmp_ui(expr->value, UINT8_MAX) <= 0) { + char name[NFT_PROTONAME_MAXSIZE]; - if (!nft_output_numeric_proto(octx)) { - p = getprotobynumber(mpz_get_uint8(expr->value)); - if (p != NULL) { - nft_print(octx, "%s", p->p_name); + if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) { + nft_print(octx, "%s", name); return; } } integer_type_print(expr, octx); } +static void inet_protocol_type_describe(struct output_ctx *octx) +{ + uint8_t protonum; + + for (protonum = 0; protonum < UINT8_MAX; protonum++) { + char name[NFT_PROTONAME_MAXSIZE]; + + if (!nft_getprotobynumber(protonum, name, sizeof(name))) + continue; + + nft_print(octx, "\t%-30s\t%u\n", name, protonum); + } +} + static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx, const struct expr *sym, struct expr **res) { - struct protoent *p; uint8_t proto; uintmax_t i; char *end; @@ -636,11 +757,13 @@ static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx, proto = i; } else { - p = getprotobyname(sym->identifier); - if (p == NULL) + int r; + + r = nft_getprotobyname(sym->identifier); + if (r < 0) return error(&sym->location, "Could not resolve protocol name"); - proto = p->p_proto; + proto = r; } *res = constant_expr_alloc(&sym->location, &inet_protocol_type, @@ -658,22 +781,24 @@ const struct datatype inet_protocol_type = { .print = inet_protocol_type_print, .json = inet_protocol_type_json, .parse = inet_protocol_type_parse, + .describe = inet_protocol_type_describe, }; static void inet_service_print(const struct expr *expr, struct output_ctx *octx) { uint16_t port = mpz_get_be16(expr->value); - const struct servent *s = getservbyport(port, NULL); + char name[NFT_SERVNAME_MAXSIZE]; - if (s == NULL) + if (!nft_getservbyport(port, NULL, name, sizeof(name))) nft_print(octx, "%hu", ntohs(port)); else - nft_print(octx, "\"%s\"", s->s_name); + nft_print(octx, "\"%s\"", name); } void inet_service_type_print(const struct expr *expr, struct output_ctx *octx) { - if (nft_output_service(octx)) { + if (nft_output_service(octx) && + mpz_cmp_ui(expr->value, UINT16_MAX) <= 0) { inet_service_print(expr, octx); return; } @@ -703,7 +828,12 @@ static struct error_record *inet_service_type_parse(struct parse_ctx *ctx, return error(&sym->location, "Could not resolve service: %s", gai_strerror(err)); - port = ((struct sockaddr_in *)ai->ai_addr)->sin_port; + if (ai->ai_addr->sa_family == AF_INET) { + port = ((struct sockaddr_in *)(void *)ai->ai_addr)->sin_port; + } else { + assert(ai->ai_addr->sa_family == AF_INET6); + port = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_port; + } freeaddrinfo(ai); } @@ -727,19 +857,48 @@ const struct datatype inet_service_type = { #define RT_SYM_TAB_INITIAL_SIZE 16 +static FILE *open_iproute2_db(const char *filename, char **path) +{ + FILE *ret; + + if (filename[0] == '/') + return fopen(filename, "r"); + + if (asprintf(path, "/etc/iproute2/%s", filename) == -1) + goto fail; + + ret = fopen(*path, "r"); + if (ret) + return ret; + + free(*path); + if (asprintf(path, "/usr/share/iproute2/%s", filename) == -1) + goto fail; + + ret = fopen(*path, "r"); + if (ret) + return ret; + + free(*path); +fail: + *path = NULL; + return NULL; +} + struct symbol_table *rt_symbol_table_init(const char *filename) { + char buf[512], namebuf[512], *p, *path = NULL; struct symbolic_constant s; struct symbol_table *tbl; unsigned int size, nelems, val; - char buf[512], namebuf[512], *p; FILE *f; size = RT_SYM_TAB_INITIAL_SIZE; tbl = xmalloc(sizeof(*tbl) + size * sizeof(s)); + tbl->base = BASE_DECIMAL; nelems = 0; - f = fopen(filename, "r"); + f = open_iproute2_db(filename, &path); if (f == NULL) goto out; @@ -749,12 +908,15 @@ struct symbol_table *rt_symbol_table_init(const char *filename) p++; if (*p == '#' || *p == '\n' || *p == '\0') continue; - if (sscanf(p, "0x%x %511s\n", &val, namebuf) != 2 && - sscanf(p, "0x%x %511s #", &val, namebuf) != 2 && - sscanf(p, "%u %511s\n", &val, namebuf) != 2 && - sscanf(p, "%u %511s #", &val, namebuf) != 2) { + if (sscanf(p, "0x%x %511s\n", &val, namebuf) == 2 || + sscanf(p, "0x%x %511s #", &val, namebuf) == 2) { + tbl->base = BASE_HEXADECIMAL; + } else if (sscanf(p, "%u %511s\n", &val, namebuf) == 2 || + sscanf(p, "%u %511s #", &val, namebuf) == 2) { + tbl->base = BASE_DECIMAL; + } else { fprintf(stderr, "iproute database '%s' corrupted\n", - filename); + path ?: filename); break; } @@ -771,6 +933,8 @@ struct symbol_table *rt_symbol_table_init(const char *filename) fclose(f); out: + if (path) + free(path); tbl->symbols[nelems] = SYMBOL_LIST_END; return tbl; } @@ -780,13 +944,40 @@ void rt_symbol_table_free(const struct symbol_table *tbl) const struct symbolic_constant *s; for (s = tbl->symbols; s->identifier != NULL; s++) - xfree(s->identifier); - xfree(tbl); + free_const(s->identifier); + free_const(tbl); +} + +void rt_symbol_table_describe(struct output_ctx *octx, const char *name, + const struct symbol_table *tbl, + const struct datatype *type) +{ + char *path = NULL; + FILE *f; + + if (!tbl || !tbl->symbols[0].identifier) + return; + + f = open_iproute2_db(name, &path); + if (f) + fclose(f); + if (!path && asprintf(&path, "%s%s", + name[0] == '/' ? "" : "unknown location of ", + name) < 0) + return; + + nft_print(octx, "\npre-defined symbolic constants from %s ", path); + if (tbl->base == BASE_DECIMAL) + nft_print(octx, "(in decimal):\n"); + else + nft_print(octx, "(in hexadecimal):\n"); + symbol_table_print(tbl, type, type->byteorder, octx); + free(path); } void mark_table_init(struct nft_ctx *ctx) { - ctx->output.tbl.mark = rt_symbol_table_init("/etc/iproute2/rt_marks"); + ctx->output.tbl.mark = rt_symbol_table_init("rt_marks"); } void mark_table_exit(struct nft_ctx *ctx) @@ -806,10 +997,17 @@ static struct error_record *mark_type_parse(struct parse_ctx *ctx, return symbolic_constant_parse(ctx, sym, ctx->tbl->mark, res); } +static void mark_type_describe(struct output_ctx *octx) +{ + rt_symbol_table_describe(octx, "rt_marks", + octx->tbl.mark, &mark_type); +} + const struct datatype mark_type = { .type = TYPE_MARK, .name = "mark", .desc = "packet mark", + .describe = mark_type_describe, .size = 4 * BITS_PER_BYTE, .byteorder = BYTEORDER_HOST_ENDIAN, .basetype = &integer_type, @@ -817,9 +1015,9 @@ const struct datatype mark_type = { .print = mark_type_print, .json = mark_type_json, .parse = mark_type_parse, - .flags = DTYPE_F_PREFIX, }; +/* symbol table for private datatypes for reject statement. */ static const struct symbol_table icmp_code_tbl = { .base = BASE_DECIMAL, .symbols = { @@ -835,16 +1033,17 @@ static const struct symbol_table icmp_code_tbl = { }, }; -const struct datatype icmp_code_type = { - .type = TYPE_ICMP_CODE, +/* private datatype for reject statement. */ +const struct datatype reject_icmp_code_type = { .name = "icmp_code", - .desc = "icmp code", + .desc = "reject icmp code", .size = BITS_PER_BYTE, .byteorder = BYTEORDER_BIG_ENDIAN, .basetype = &integer_type, .sym_tbl = &icmp_code_tbl, }; +/* symbol table for private datatypes for reject statement. */ static const struct symbol_table icmpv6_code_tbl = { .base = BASE_DECIMAL, .symbols = { @@ -858,16 +1057,17 @@ static const struct symbol_table icmpv6_code_tbl = { }, }; -const struct datatype icmpv6_code_type = { - .type = TYPE_ICMPV6_CODE, +/* private datatype for reject statement. */ +const struct datatype reject_icmpv6_code_type = { .name = "icmpv6_code", - .desc = "icmpv6 code", + .desc = "reject icmpv6 code", .size = BITS_PER_BYTE, .byteorder = BYTEORDER_BIG_ENDIAN, .basetype = &integer_type, .sym_tbl = &icmpv6_code_tbl, }; +/* symbol table for private datatypes for reject statement. */ static const struct symbol_table icmpx_code_tbl = { .base = BASE_DECIMAL, .symbols = { @@ -879,6 +1079,60 @@ static const struct symbol_table icmpx_code_tbl = { }, }; +/* private datatype for reject statement. */ +const struct datatype reject_icmpx_code_type = { + .name = "icmpx_code", + .desc = "reject icmpx code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .sym_tbl = &icmpx_code_tbl, +}; + +/* Backward compatible parser for the reject statement. */ +static struct error_record *icmp_code_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, &icmp_code_tbl, res); +} + +const struct datatype icmp_code_type = { + .type = TYPE_ICMP_CODE, + .name = "icmp_code", + .desc = "icmp code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .parse = icmp_code_parse, +}; + +/* Backward compatible parser for the reject statement. */ +static struct error_record *icmpv6_code_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, &icmpv6_code_tbl, res); +} + +const struct datatype icmpv6_code_type = { + .type = TYPE_ICMPV6_CODE, + .name = "icmpv6_code", + .desc = "icmpv6 code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .parse = icmpv6_code_parse, +}; + +/* Backward compatible parser for the reject statement. */ +static struct error_record *icmpx_code_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, &icmpx_code_tbl, res); +} + const struct datatype icmpx_code_type = { .type = TYPE_ICMPX_CODE, .name = "icmpx_code", @@ -886,12 +1140,18 @@ const struct datatype icmpx_code_type = { .size = BITS_PER_BYTE, .byteorder = BYTEORDER_BIG_ENDIAN, .basetype = &integer_type, - .sym_tbl = &icmpx_code_tbl, + .parse = icmpx_code_parse, }; void time_print(uint64_t ms, struct output_ctx *octx) { uint64_t days, hours, minutes, seconds; + bool printed = false; + + if (nft_output_seconds(octx)) { + nft_print(octx, "%" PRIu64 "s", ms / 1000); + return; + } days = ms / 86400000; ms %= 86400000; @@ -905,16 +1165,29 @@ void time_print(uint64_t ms, struct output_ctx *octx) seconds = ms / 1000; ms %= 1000; - if (days > 0) + if (days > 0) { nft_print(octx, "%" PRIu64 "d", days); - if (hours > 0) + printed = true; + } + if (hours > 0) { nft_print(octx, "%" PRIu64 "h", hours); - if (minutes > 0) + printed = true; + } + if (minutes > 0) { nft_print(octx, "%" PRIu64 "m", minutes); - if (seconds > 0) + printed = true; + } + if (seconds > 0) { nft_print(octx, "%" PRIu64 "s", seconds); - if (ms > 0) + printed = true; + } + if (ms > 0) { nft_print(octx, "%" PRIu64 "ms", ms); + printed = true; + } + + if (!printed) + nft_print(octx, "0s"); } enum { @@ -1029,6 +1302,7 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx, struct expr **res) { struct error_record *erec; + uint32_t s32; uint64_t s; erec = time_parse(&sym->location, sym->identifier, &s); @@ -1038,9 +1312,10 @@ static struct error_record *time_type_parse(struct parse_ctx *ctx, if (s > UINT32_MAX) return error(&sym->location, "value too large"); + s32 = s; *res = constant_expr_alloc(&sym->location, &time_type, BYTEORDER_HOST_ENDIAN, - sizeof(uint32_t) * BITS_PER_BYTE, &s); + sizeof(uint32_t) * BITS_PER_BYTE, &s32); return NULL; } @@ -1049,7 +1324,7 @@ const struct datatype time_type = { .name = "time", .desc = "relative time", .byteorder = BYTEORDER_HOST_ENDIAN, - .size = 8 * BITS_PER_BYTE, + .size = 4 * BITS_PER_BYTE, .basetype = &integer_type, .print = time_type_print, .json = time_type_json, @@ -1064,17 +1339,18 @@ static struct error_record *concat_type_parse(struct parse_ctx *ctx, sym->dtype->desc); } -static struct datatype *dtype_alloc(void) +static struct datatype *datatype_alloc(void) { struct datatype *dtype; dtype = xzalloc(sizeof(*dtype)); dtype->flags = DTYPE_F_ALLOC; + dtype->refcnt = 1; return dtype; } -struct datatype *datatype_get(const struct datatype *ptr) +const struct datatype *datatype_get(const struct datatype *ptr) { struct datatype *dtype = (struct datatype *)ptr; @@ -1087,24 +1363,31 @@ struct datatype *datatype_get(const struct datatype *ptr) return dtype; } +void __datatype_set(struct expr *expr, const struct datatype *dtype) +{ + const struct datatype *dtype_free; + + dtype_free = expr->dtype; + expr->dtype = dtype; + datatype_free(dtype_free); +} + void datatype_set(struct expr *expr, const struct datatype *dtype) { - if (dtype == expr->dtype) - return; - datatype_free(expr->dtype); - expr->dtype = datatype_get(dtype); + if (dtype != expr->dtype) + __datatype_set(expr, datatype_get(dtype)); } -static struct datatype *dtype_clone(const struct datatype *orig_dtype) +struct datatype *datatype_clone(const struct datatype *orig_dtype) { struct datatype *dtype; - dtype = xzalloc(sizeof(*dtype)); + dtype = xmalloc(sizeof(*dtype)); *dtype = *orig_dtype; dtype->name = xstrdup(orig_dtype->name); dtype->desc = xstrdup(orig_dtype->desc); dtype->flags = DTYPE_F_ALLOC | orig_dtype->flags; - dtype->refcnt = 0; + dtype->refcnt = 1; return dtype; } @@ -1117,12 +1400,15 @@ void datatype_free(const struct datatype *ptr) return; if (!(dtype->flags & DTYPE_F_ALLOC)) return; + + assert(dtype->refcnt != 0); + if (--dtype->refcnt > 0) return; - xfree(dtype->name); - xfree(dtype->desc); - xfree(dtype); + free_const(dtype->name); + free_const(dtype->desc); + free(dtype); } const struct datatype *concat_type_alloc(uint32_t type) @@ -1151,7 +1437,7 @@ const struct datatype *concat_type_alloc(uint32_t type) } strncat(desc, ")", sizeof(desc) - strlen(desc) - 1); - dtype = dtype_alloc(); + dtype = datatype_alloc(); dtype->type = type; dtype->size = size; dtype->subtypes = subtypes; @@ -1163,15 +1449,15 @@ const struct datatype *concat_type_alloc(uint32_t type) } const struct datatype *set_datatype_alloc(const struct datatype *orig_dtype, - unsigned int byteorder) + enum byteorder byteorder) { struct datatype *dtype; /* Restrict dynamic datatype allocation to generic integer datatype. */ if (orig_dtype != &integer_type) - return orig_dtype; + return datatype_get(orig_dtype); - dtype = dtype_clone(orig_dtype); + dtype = datatype_clone(orig_dtype); dtype->byteorder = byteorder; return dtype; @@ -1316,3 +1602,92 @@ const struct datatype policy_type = { .desc = "policy type", .parse = policy_type_parse, }; + +#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup" + +static char *cgroupv2_get_path(const char *path, uint64_t id) +{ + char dent_name[PATH_MAX + 1]; + char *cgroup_path = NULL; + struct dirent *dent; + struct stat st; + DIR *d; + + d = opendir(path); + if (!d) + return NULL; + + while ((dent = readdir(d)) != NULL) { + if (!strcmp(dent->d_name, ".") || + !strcmp(dent->d_name, "..")) + continue; + + snprintf(dent_name, sizeof(dent_name), "%s/%s", + path, dent->d_name); + dent_name[sizeof(dent_name) - 1] = '\0'; + + if (dent->d_ino == id) { + cgroup_path = xstrdup(dent_name); + break; + } + + if (stat(dent_name, &st) >= 0 && S_ISDIR(st.st_mode)) { + cgroup_path = cgroupv2_get_path(dent_name, id); + if (cgroup_path) + break; + } + } + closedir(d); + + return cgroup_path; +} + +static void cgroupv2_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + uint64_t id = mpz_get_uint64(expr->value); + char *cgroup_path; + + cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id); + if (cgroup_path) + nft_print(octx, "\"%s\"", + &cgroup_path[strlen(SYSFS_CGROUPSV2_PATH) + 1]); + else + nft_print(octx, "%" PRIu64, id); + + free(cgroup_path); +} + +static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + char cgroupv2_path[PATH_MAX + 1]; + struct stat st; + uint64_t ino; + + snprintf(cgroupv2_path, sizeof(cgroupv2_path), "%s/%s", + SYSFS_CGROUPSV2_PATH, sym->identifier); + cgroupv2_path[sizeof(cgroupv2_path) - 1] = '\0'; + + if (stat(cgroupv2_path, &st) < 0) + return error(&sym->location, "cgroupv2 path fails: %s", + strerror(errno)); + + ino = st.st_ino; + *res = constant_expr_alloc(&sym->location, &cgroupv2_type, + BYTEORDER_HOST_ENDIAN, + sizeof(ino) * BITS_PER_BYTE, &ino); + return NULL; +} + +const struct datatype cgroupv2_type = { + .type = TYPE_CGROUPV2, + .name = "cgroupsv2", + .desc = "cgroupsv2 path", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 8 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = cgroupv2_type_print, + .parse = cgroupv2_type_parse, +}; |