Add support for matching on the cgroups version 2.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
* @TYPE_TIME_DATA Date type (integer subtype)
* @TYPE_TIME_HOUR Hour type (integer subtype)
* @TYPE_TIME_DAY Day type (integer subtype)
+ * @TYPE_CGROUPV2 cgroups v2 (integer subtype)
*/
enum datatypes {
TYPE_INVALID,
TYPE_TIME_DATE,
TYPE_TIME_HOUR,
TYPE_TIME_DAY,
+ TYPE_CGROUPV2,
__TYPE_MAX
};
#define TYPE_MAX (__TYPE_MAX - 1)
extern const struct datatype boolean_type;
extern const struct datatype priority_type;
extern const struct datatype policy_type;
+extern const struct datatype cgroupv2_type;
void inet_service_type_print(const struct expr *expr, struct output_ctx *octx);
struct {
/* SOCKET */
enum nft_socket_keys key;
+ uint32_t level;
} socket;
struct {
/* EXPR_RT */
NFTA_SOCKET_UNSPEC,
NFTA_SOCKET_KEY,
NFTA_SOCKET_DREG,
+ NFTA_SOCKET_LEVEL,
__NFTA_SOCKET_MAX
};
#define NFTA_SOCKET_MAX (__NFTA_SOCKET_MAX - 1)
NFT_SOCKET_TRANSPARENT,
NFT_SOCKET_MARK,
NFT_SOCKET_WILDCARD,
+ NFT_SOCKET_CGROUPV2,
__NFT_SOCKET_MAX
};
#define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1)
extern const struct socket_template socket_templates[];
extern struct expr *socket_expr_alloc(const struct location *loc,
- enum nft_socket_keys key);
+ enum nft_socket_keys key, uint32_t level);
#endif /* NFTABLES_SOCKET_H */
#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>
[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)
.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,
+};
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);
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);
}
%token SOCKET "socket"
%token TRANSPARENT "transparent"
%token WILDCARD "wildcard"
+%token CGROUPV2 "cgroupv2"
%token TPROXY "tproxy"
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);
}
;
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,
<SCANSTATE_EXPR_SOCKET>{
"transparent" { return TRANSPARENT; }
"wildcard" { return WILDCARD; }
+ "cgroupv2" { return CGROUPV2; }
+ "level" { return LEVEL; }
}
"tproxy" { return TPROXY; }
.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
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 = {
.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;
expr = expr_alloc(loc, EXPR_SOCKET, tmpl->dtype,
tmpl->byteorder, tmpl->len);
expr->socket.key = key;
+ expr->socket.level = level;
return expr;
}