]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add cgroupsv2 support
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 20 Apr 2021 23:26:46 +0000 (01:26 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Sun, 2 May 2021 22:18:04 +0000 (00:18 +0200)
Add support for matching on the cgroups version 2.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/datatype.h
include/expression.h
include/linux/netfilter/nf_tables.h
include/socket.h
src/datatype.c
src/netlink_delinearize.c
src/netlink_linearize.c
src/parser_bison.y
src/parser_json.c
src/scanner.l
src/socket.c

index a16f8f2bf5c461f5358d00154da0cbe9e2fc6828..448be57fbc7fe3c3b2065e1316160bb556a1419f 100644 (file)
@@ -48,6 +48,7 @@
  * @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,
@@ -96,6 +97,7 @@ enum datatypes {
        TYPE_TIME_DATE,
        TYPE_TIME_HOUR,
        TYPE_TIME_DAY,
+       TYPE_CGROUPV2,
        __TYPE_MAX
 };
 #define TYPE_MAX               (__TYPE_MAX - 1)
@@ -271,6 +273,7 @@ extern const struct datatype time_type;
 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);
 
index 2d07f3d96bebcea75c30bfd49025f7d28d3c3041..7e626c48d5eaade033bc09e1221b724b4a8601fd 100644 (file)
@@ -325,6 +325,7 @@ struct expr {
                struct {
                        /* SOCKET */
                        enum nft_socket_keys    key;
+                       uint32_t                level;
                } socket;
                struct {
                        /* EXPR_RT */
index b1633e7ba529694e20056148f63897ca2baa39c7..8c85ef8e994d05a9b7a0c7f97669d6013e86dd39 100644 (file)
@@ -1014,6 +1014,7 @@ enum nft_socket_attributes {
        NFTA_SOCKET_UNSPEC,
        NFTA_SOCKET_KEY,
        NFTA_SOCKET_DREG,
+       NFTA_SOCKET_LEVEL,
        __NFTA_SOCKET_MAX
 };
 #define NFTA_SOCKET_MAX                (__NFTA_SOCKET_MAX - 1)
@@ -1029,6 +1030,7 @@ enum nft_socket_keys {
        NFT_SOCKET_TRANSPARENT,
        NFT_SOCKET_MARK,
        NFT_SOCKET_WILDCARD,
+       NFT_SOCKET_CGROUPV2,
        __NFT_SOCKET_MAX
 };
 #define NFT_SOCKET_MAX (__NFT_SOCKET_MAX - 1)
index fbfddd117822d3661b2ae2200eacd14a3352bf0f..79938ccfad83230419c0a5ca580171d57e378cbe 100644 (file)
@@ -19,6 +19,6 @@ struct socket_template {
 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 */
index fae1aa262599e306265c1814302d6211fe991473..c4e66c4633f83acb2035eddc26e5321c3d43c57a 100644 (file)
@@ -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>
@@ -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,
+};
index d82d9f51f482f6d9bf1440ac0ad19e914b07c93b..4dd5bdc0787fcd93329c86adbc9efc0de88780fc 100644 (file)
@@ -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);
index 21bc492e85f43b4475fde6c9fbad69cb3556b73b..7b35aae1f913a57df61443fcb449b8e642677807 100644 (file)
@@ -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);
 }
 
index ce4857b2ab555f1aad326534e4105fbde6984f36..b50b60649d2ec71a2163eab5dad9c5faf8ff81ca 100644 (file)
@@ -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);
                        }
                        ;
 
index ddbf9d9c027b159d724aa7e08b6848d7871436e6..17bb10c34cbc9aa3010e9e858660642beb7b9f8b 100644 (file)
@@ -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,
index a9232db8978e7fcb462473b48173bcc04ce862b0..72469b4e07b055dc57cb506535fb0983b491ceb7 100644 (file)
@@ -292,6 +292,8 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 <SCANSTATE_EXPR_SOCKET>{
        "transparent"           { return TRANSPARENT; }
        "wildcard"              { return WILDCARD; }
+       "cgroupv2"              { return CGROUPV2; }
+       "level"                 { return LEVEL; }
 }
 "tproxy"               { return TPROXY; }
 
index 673e5d0f570d6ca9ea380105f2de55c04200d659..eb0751536120597d646b6aa9fa5f2cf2bb048b59 100644 (file)
@@ -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;
 }