From 38228087252c1d5da9dc88a09d3539e9882d808e Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 21 Apr 2021 01:26:46 +0200 Subject: src: add cgroupsv2 support Add support for matching on the cgroups version 2. Signed-off-by: Pablo Neira Ayuso --- src/datatype.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++ src/netlink_delinearize.c | 5 +-- src/netlink_linearize.c | 1 + src/parser_bison.y | 7 +++- src/parser_json.c | 2 +- src/scanner.l | 2 ++ src/socket.c | 18 ++++++++-- 7 files changed, 119 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/datatype.c b/src/datatype.c index fae1aa26..c4e66c46 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -74,6 +76,7 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = { [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) @@ -1331,3 +1334,91 @@ const struct datatype policy_type = { .desc = "policy type", .parse = policy_type_parse, }; + +#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup" + +static const char *cgroupv2_get_path(const char *path, uint64_t id) +{ + const char *cgroup_path = NULL; + char dent_name[PATH_MAX + 1]; + 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); + const char *cgroup_path; + + cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id); + if (cgroup_path) + nft_print(octx, "\"%s\"", cgroup_path); + else + nft_print(octx, "%lu", id); + + xfree(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, +}; diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index d82d9f51..4dd5bdc0 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -731,11 +731,12 @@ static void netlink_parse_socket(struct netlink_parse_ctx *ctx, const struct nftnl_expr *nle) { enum nft_registers dreg; - uint32_t key; + uint32_t key, level; struct expr * expr; key = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_KEY); - expr = socket_expr_alloc(loc, key); + level = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_LEVEL); + expr = socket_expr_alloc(loc, key, level); dreg = netlink_parse_register(nle, NFTNL_EXPR_SOCKET_DREG); netlink_set_register(ctx, dreg, expr); diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 21bc492e..7b35aae1 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -248,6 +248,7 @@ static void netlink_gen_socket(struct netlink_linearize_ctx *ctx, nle = alloc_nft_expr("socket"); netlink_put_register(nle, NFTNL_EXPR_SOCKET_DREG, dreg); nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_KEY, expr->socket.key); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_LEVEL, expr->socket.level); nft_rule_add_expr(ctx, nle, &expr->location); } diff --git a/src/parser_bison.y b/src/parser_bison.y index ce4857b2..b50b6064 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -224,6 +224,7 @@ int nft_lex(void *, void *, void *); %token SOCKET "socket" %token TRANSPARENT "transparent" %token WILDCARD "wildcard" +%token CGROUPV2 "cgroupv2" %token TPROXY "tproxy" @@ -4833,7 +4834,11 @@ meta_stmt : META meta_key SET stmt_expr socket_expr : SOCKET socket_key close_scope_socket { - $$ = socket_expr_alloc(&@$, $2); + $$ = socket_expr_alloc(&@$, $2, 0); + } + | SOCKET CGROUPV2 LEVEL NUM close_scope_socket + { + $$ = socket_expr_alloc(&@$, NFT_SOCKET_CGROUPV2, $4); } ; diff --git a/src/parser_json.c b/src/parser_json.c index ddbf9d9c..17bb10c3 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -435,7 +435,7 @@ static struct expr *json_parse_socket_expr(struct json_ctx *ctx, return NULL; } - return socket_expr_alloc(int_loc, keyval); + return socket_expr_alloc(int_loc, keyval, 0); } static int json_parse_payload_field(const struct proto_desc *desc, diff --git a/src/scanner.l b/src/scanner.l index a9232db8..72469b4e 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -292,6 +292,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) { "transparent" { return TRANSPARENT; } "wildcard" { return WILDCARD; } + "cgroupv2" { return CGROUPV2; } + "level" { return LEVEL; } } "tproxy" { return TPROXY; } diff --git a/src/socket.c b/src/socket.c index 673e5d0f..eb075153 100644 --- a/src/socket.c +++ b/src/socket.c @@ -32,21 +32,31 @@ const struct socket_template socket_templates[] = { .len = BITS_PER_BYTE, .byteorder = BYTEORDER_HOST_ENDIAN, }, + [NFT_SOCKET_CGROUPV2] = { + .token = "cgroupv2", + .dtype = &cgroupv2_type, + .len = 8 * BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + }, }; static void socket_expr_print(const struct expr *expr, struct output_ctx *octx) { nft_print(octx, "socket %s", socket_templates[expr->socket.key].token); + if (expr->socket.key == NFT_SOCKET_CGROUPV2) + nft_print(octx, " level %u", expr->socket.level); } static bool socket_expr_cmp(const struct expr *e1, const struct expr *e2) { - return e1->socket.key == e2->socket.key; + return e1->socket.key == e2->socket.key && + e1->socket.level == e2->socket.level; } static void socket_expr_clone(struct expr *new, const struct expr *expr) { new->socket.key = expr->socket.key; + new->socket.level = expr->socket.level; } #define NFTNL_UDATA_SOCKET_KEY 0 @@ -95,7 +105,7 @@ static struct expr *socket_expr_parse_udata(const struct nftnl_udata *attr) key = nftnl_udata_get_u32(ud[NFTNL_UDATA_SOCKET_KEY]); - return socket_expr_alloc(&internal_location, key); + return socket_expr_alloc(&internal_location, key, 0); } const struct expr_ops socket_expr_ops = { @@ -109,7 +119,8 @@ const struct expr_ops socket_expr_ops = { .parse_udata = socket_expr_parse_udata, }; -struct expr *socket_expr_alloc(const struct location *loc, enum nft_socket_keys key) +struct expr *socket_expr_alloc(const struct location *loc, + enum nft_socket_keys key, uint32_t level) { const struct socket_template *tmpl = &socket_templates[key]; struct expr *expr; @@ -117,6 +128,7 @@ struct expr *socket_expr_alloc(const struct location *loc, enum nft_socket_keys expr = expr_alloc(loc, EXPR_SOCKET, tmpl->dtype, tmpl->byteorder, tmpl->len); expr->socket.key = key; + expr->socket.level = level; return expr; } -- cgit v1.2.3