]> git.ipfire.org Git - thirdparty/libnftnl.git/commitdiff
expr: add inner support
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 2 Jan 2023 14:15:48 +0000 (15:15 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 2 Jan 2023 14:15:48 +0000 (15:15 +0100)
This patch adds support for the inner expression which allows you to
match on the inner tunnel headers, eg. VxLAN.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/libnftnl/expr.h
include/linux/netfilter/nf_tables.h
src/Makefile.am
src/expr/inner.c [new file with mode: 0644]
src/expr/payload.c
src/expr_ops.c

index 00c63ab9d19b70b87755b7eeb2b123b2ffd94923..9873228dd794a17055042294df16e4041af4fddb 100644 (file)
@@ -316,6 +316,13 @@ enum {
        NFTNL_EXPR_LAST_SET,
 };
 
+enum {
+       NFTNL_EXPR_INNER_TYPE = NFTNL_EXPR_BASE,
+       NFTNL_EXPR_INNER_FLAGS,
+       NFTNL_EXPR_INNER_HDRSIZE,
+       NFTNL_EXPR_INNER_EXPR,
+};
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index 0ae912054cf1e4b8099e563926cc502522d3525c..4608646f210325a907e9fd431cd2222ec862e20d 100644 (file)
@@ -753,13 +753,14 @@ enum nft_dynset_attributes {
  * @NFT_PAYLOAD_LL_HEADER: link layer header
  * @NFT_PAYLOAD_NETWORK_HEADER: network header
  * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header
- * @NFT_PAYLOAD_INNER_HEADER: inner header
+ * @NFT_PAYLOAD_INNER_HEADER: inner header / payload
  */
 enum nft_payload_bases {
        NFT_PAYLOAD_LL_HEADER,
        NFT_PAYLOAD_NETWORK_HEADER,
        NFT_PAYLOAD_TRANSPORT_HEADER,
        NFT_PAYLOAD_INNER_HEADER,
+       NFT_PAYLOAD_TUN_HEADER,
 };
 
 /**
@@ -779,6 +780,31 @@ enum nft_payload_csum_flags {
        NFT_PAYLOAD_L4CSUM_PSEUDOHDR = (1 << 0),
 };
 
+enum nft_inner_type {
+       NFT_INNER_UNSPEC        = 0,
+       NFT_INNER_VXLAN,
+};
+
+enum nft_inner_flags {
+       NFT_INNER_HDRSIZE       = (1 << 0),
+       NFT_INNER_LL            = (1 << 1),
+       NFT_INNER_NH            = (1 << 2),
+       NFT_INNER_TH            = (1 << 3),
+};
+#define NFT_INNER_MASK         (NFT_INNER_HDRSIZE | NFT_INNER_LL | \
+                                NFT_INNER_NH |  NFT_INNER_TH)
+
+enum nft_inner_attributes {
+       NFTA_INNER_UNSPEC,
+       NFTA_INNER_NUM,
+       NFTA_INNER_TYPE,
+       NFTA_INNER_FLAGS,
+       NFTA_INNER_HDRSIZE,
+       NFTA_INNER_EXPR,
+       __NFTA_INNER_MAX
+};
+#define NFTA_INNER_MAX (__NFTA_INNER_MAX - 1)
+
 /**
  * enum nft_payload_attributes - nf_tables payload expression netlink attributes
  *
@@ -898,7 +924,8 @@ enum nft_meta_keys {
        NFT_META_OIF,
        NFT_META_IIFNAME,
        NFT_META_OIFNAME,
-       NFT_META_IIFTYPE,
+       NFT_META_IFTYPE,
+#define NFT_META_IIFTYPE       NFT_META_IFTYPE
        NFT_META_OIFTYPE,
        NFT_META_SKUID,
        NFT_META_SKGID,
@@ -925,6 +952,7 @@ enum nft_meta_keys {
        NFT_META_TIME_HOUR,
        NFT_META_SDIF,
        NFT_META_SDIFNAME,
+       __NFT_META_IIFTYPE,
 };
 
 /**
index c3b0ab974bd212069035cc1ae8892996c35d2949..3cd259c04d1c33f3057466f67573211bda835848 100644 (file)
@@ -40,6 +40,7 @@ libnftnl_la_SOURCES = utils.c         \
                      expr/lookup.c     \
                      expr/dynset.c     \
                      expr/immediate.c  \
+                     expr/inner.c      \
                      expr/match.c      \
                      expr/meta.c       \
                      expr/numgen.c     \
diff --git a/src/expr/inner.c b/src/expr/inner.c
new file mode 100644 (file)
index 0000000..7daae4f
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * (C) 2012-2022 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include "internal.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <libmnl/libmnl.h>
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <libnftnl/expr.h>
+#include <libnftnl/rule.h>
+
+struct nftnl_expr_inner {
+       uint32_t        type;
+       uint32_t        flags;
+       uint32_t        hdrsize;
+       struct nftnl_expr *expr;
+};
+
+static void nftnl_expr_inner_free(const struct nftnl_expr *e)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+       if (inner->expr)
+               nftnl_expr_free(inner->expr);
+}
+
+static int
+nftnl_expr_inner_set(struct nftnl_expr *e, uint16_t type,
+                    const void *data, uint32_t data_len)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+       switch(type) {
+       case NFTNL_EXPR_INNER_TYPE:
+               memcpy(&inner->type, data, sizeof(inner->type));
+               break;
+       case NFTNL_EXPR_INNER_FLAGS:
+               memcpy(&inner->flags, data, sizeof(inner->flags));
+               break;
+       case NFTNL_EXPR_INNER_HDRSIZE:
+               memcpy(&inner->hdrsize, data, sizeof(inner->hdrsize));
+               break;
+       case NFTNL_EXPR_INNER_EXPR:
+               if (inner->expr)
+                       nftnl_expr_free(inner->expr);
+
+               inner->expr = (void *)data;
+               break;
+       default:
+               return -1;
+       }
+       return 0;
+}
+
+static const void *
+nftnl_expr_inner_get(const struct nftnl_expr *e, uint16_t type,
+                    uint32_t *data_len)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+
+       switch(type) {
+       case NFTNL_EXPR_INNER_FLAGS:
+               *data_len = sizeof(inner->flags);
+               return &inner->flags;
+       case NFTNL_EXPR_INNER_TYPE:
+               *data_len = sizeof(inner->type);
+               return &inner->type;
+       case NFTNL_EXPR_INNER_HDRSIZE:
+               *data_len = sizeof(inner->hdrsize);
+               return &inner->hdrsize;
+       case NFTNL_EXPR_INNER_EXPR:
+               return inner->expr;
+       }
+       return NULL;
+}
+
+static void
+nftnl_expr_inner_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+       struct nlattr *nest;
+
+       mnl_attr_put_u32(nlh, NFTA_INNER_NUM, htonl(0));
+       if (e->flags & (1 << NFTNL_EXPR_INNER_TYPE))
+               mnl_attr_put_u32(nlh, NFTA_INNER_TYPE, htonl(inner->type));
+       if (e->flags & (1 << NFTNL_EXPR_INNER_FLAGS))
+               mnl_attr_put_u32(nlh, NFTA_INNER_FLAGS, htonl(inner->flags));
+       if (e->flags & (1 << NFTNL_EXPR_INNER_HDRSIZE))
+               mnl_attr_put_u32(nlh, NFTA_INNER_HDRSIZE, htonl(inner->hdrsize));
+       if (e->flags & (1 << NFTNL_EXPR_INNER_EXPR)) {
+               nest = mnl_attr_nest_start(nlh, NFTA_INNER_EXPR);
+               nftnl_expr_build_payload(nlh, inner->expr);
+               mnl_attr_nest_end(nlh, nest);
+       }
+}
+
+static int nftnl_inner_parse_cb(const struct nlattr *attr, void *data)
+{
+       const struct nlattr **tb = data;
+       int type = mnl_attr_get_type(attr);
+
+       if (mnl_attr_type_valid(attr, NFTA_INNER_MAX) < 0)
+               return MNL_CB_OK;
+
+       switch(type) {
+       case NFTA_INNER_NUM:
+       case NFTA_INNER_TYPE:
+       case NFTA_INNER_HDRSIZE:
+       case NFTA_INNER_FLAGS:
+               if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
+                       abi_breakage();
+               break;
+       case NFTA_INNER_EXPR:
+               if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
+                       abi_breakage();
+               break;
+       }
+
+       tb[type] = attr;
+
+       return MNL_CB_OK;
+}
+
+static int
+nftnl_expr_inner_parse(struct nftnl_expr *e, struct nlattr *attr)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+       struct nlattr *tb[NFTA_INNER_MAX + 1] = {};
+       struct nftnl_expr *expr;
+       int err;
+
+       err = mnl_attr_parse_nested(attr, nftnl_inner_parse_cb, tb);
+       if (err < 0)
+               return err;
+
+       if (tb[NFTA_INNER_HDRSIZE]) {
+               inner->hdrsize =
+                       ntohl(mnl_attr_get_u32(tb[NFTA_INNER_HDRSIZE]));
+               e->flags |= (1 << NFTNL_EXPR_INNER_HDRSIZE);
+       }
+       if (tb[NFTA_INNER_FLAGS]) {
+               inner->flags =
+                       ntohl(mnl_attr_get_u32(tb[NFTA_INNER_FLAGS]));
+               e->flags |= (1 << NFTNL_EXPR_INNER_FLAGS);
+       }
+       if (tb[NFTA_INNER_TYPE]) {
+               inner->type =
+                       ntohl(mnl_attr_get_u32(tb[NFTA_INNER_TYPE]));
+               e->flags |= (1 << NFTNL_EXPR_INNER_TYPE);
+       }
+       if (tb[NFTA_INNER_EXPR]) {
+               expr = nftnl_expr_parse(tb[NFTA_INNER_EXPR]);
+               if (!expr)
+                       return -1;
+
+               if (inner->expr)
+                       nftnl_expr_free(inner->expr);
+
+               inner->expr = expr;
+               e->flags |= (1 << NFTNL_EXPR_INNER_EXPR);
+       }
+
+       return 0;
+}
+
+static int
+nftnl_expr_inner_snprintf(char *buf, size_t remain, uint32_t flags,
+                         const struct nftnl_expr *e)
+{
+       struct nftnl_expr_inner *inner = nftnl_expr_data(e);
+       uint32_t offset = 0;
+       int ret;
+
+       ret = snprintf(buf, remain, "type %u hdrsize %u flags %x [",
+                      inner->type, inner->hdrsize, inner->flags);
+       SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+       ret = snprintf(buf + offset, remain, " %s ", inner->expr->ops->name);
+       SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+       ret = nftnl_expr_snprintf(buf + offset, remain, inner->expr,
+                                 NFTNL_OUTPUT_DEFAULT, 0);
+       SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+       ret = snprintf(buf + offset, remain, "] ");
+       SNPRINTF_BUFFER_SIZE(ret, remain, offset);
+
+       return offset;
+}
+
+struct expr_ops expr_ops_inner = {
+       .name           = "inner",
+       .alloc_len      = sizeof(struct nftnl_expr_inner),
+       .max_attr       = NFTA_INNER_MAX,
+       .free           = nftnl_expr_inner_free,
+       .set            = nftnl_expr_inner_set,
+       .get            = nftnl_expr_inner_get,
+       .parse          = nftnl_expr_inner_parse,
+       .build          = nftnl_expr_inner_build,
+       .output         = nftnl_expr_inner_snprintf,
+};
index 88fff5f14ddda7d2f4c369632f6d7a9e66ff1ec8..c633e33bedd3eff908174c59820ccf797d9036b1 100644 (file)
@@ -203,11 +203,12 @@ nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr)
        return 0;
 }
 
-static const char *base2str_array[NFT_PAYLOAD_INNER_HEADER + 1] = {
+static const char *base2str_array[NFT_PAYLOAD_TUN_HEADER + 1] = {
        [NFT_PAYLOAD_LL_HEADER]         = "link",
        [NFT_PAYLOAD_NETWORK_HEADER]    = "network",
        [NFT_PAYLOAD_TRANSPORT_HEADER]  = "transport",
        [NFT_PAYLOAD_INNER_HEADER]      = "inner",
+       [NFT_PAYLOAD_TUN_HEADER]        = "tunnel",
 };
 
 static const char *base2str(enum nft_payload_bases base)
index 7248e4f98b0a1d650fc2f99c4c56f92f60c91755..b85f47209725c8938dd11add546560bec27d5bf9 100644 (file)
@@ -14,6 +14,7 @@ extern struct expr_ops expr_ops_dup;
 extern struct expr_ops expr_ops_exthdr;
 extern struct expr_ops expr_ops_fwd;
 extern struct expr_ops expr_ops_immediate;
+extern struct expr_ops expr_ops_inner;
 extern struct expr_ops expr_ops_last;
 extern struct expr_ops expr_ops_limit;
 extern struct expr_ops expr_ops_log;
@@ -58,6 +59,7 @@ static struct expr_ops *expr_ops[] = {
        &expr_ops_exthdr,
        &expr_ops_fwd,
        &expr_ops_immediate,
+       &expr_ops_inner,
        &expr_ops_last,
        &expr_ops_limit,
        &expr_ops_log,