]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add dup statement support
authorPablo Neira Ayuso <pablo@netfilter.org>
Tue, 29 Sep 2015 16:21:54 +0000 (18:21 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 30 Sep 2015 15:32:10 +0000 (17:32 +0200)
This allows you to clone packets to destination address, eg.

... dup to 172.20.0.2
... dup to 172.20.0.2 device eth1
... dup to ip saddr map { 192.168.0.2 : 172.20.0.2, ... } device eth1

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

index db0457d92cbd258f376754c16c6d6e64d05065a4..5ebe3d85263325b73ab253a14379617883d7b5e4 100644 (file)
@@ -935,6 +935,20 @@ enum nft_redir_attributes {
 };
 #define NFTA_REDIR_MAX         (__NFTA_REDIR_MAX - 1)
 
+/**
+ * enum nft_tee_attributes - nf_tables tee expression netlink attributes
+ *
+ * @NFTA_DUP_SREG_ADDR: source register of destination (NLA_U32: nft_registers)
+ * @NFTA_DUP_SREG_DEV: output interface name (NLA_U32: nft_register)
+ */
+enum nft_tee_attributes {
+       NFTA_DUP_UNSPEC,
+       NFTA_DUP_SREG_ADDR,
+       NFTA_DUP_SREG_DEV,
+       __NFTA_DUP_MAX
+};
+#define NFTA_DUP_MAX           (__NFTA_DUP_MAX - 1)
+
 /**
  * enum nft_gen_attributes - nf_tables ruleset generation attributes
  *
index 459221fb344aae3e58e0736145b2dba2baec2b60..abe74ec0338ce8b34df84bda16bc33cfebaa2b6a 100644 (file)
@@ -26,4 +26,6 @@ struct meta_template {
 extern struct expr *meta_expr_alloc(const struct location *loc,
                                    enum nft_meta_keys key);
 
+const struct datatype ifindex_type;
+
 #endif /* NFTABLES_META_H */
index bead0a6f17bdf26e39a3a19f075938abb2b8e32d..8b035d3156c63a70e9b2f1394ae7e2463e62cf31 100644 (file)
@@ -105,6 +105,13 @@ struct ct_stmt {
 extern struct stmt *ct_stmt_alloc(const struct location *loc,
                                  enum nft_ct_keys key,
                                  struct expr *expr);
+struct dup_stmt {
+       struct expr             *to;
+       struct expr             *dev;
+};
+
+struct stmt *dup_stmt_alloc(const struct location *loc);
+uint32_t dup_stmt_type(const char *type);
 
 struct set_stmt {
        struct expr             *set;
@@ -131,6 +138,7 @@ extern struct stmt *set_stmt_alloc(const struct location *loc);
  * @STMT_QUEUE:                QUEUE statement
  * @STMT_CT:           conntrack statement
  * @STMT_SET:          set statement
+ * @STMT_DUP:          dup statement
  */
 enum stmt_types {
        STMT_INVALID,
@@ -147,6 +155,7 @@ enum stmt_types {
        STMT_QUEUE,
        STMT_CT,
        STMT_SET,
+       STMT_DUP,
 };
 
 /**
@@ -197,6 +206,7 @@ struct stmt {
                struct queue_stmt       queue;
                struct ct_stmt          ct;
                struct set_stmt         set;
+               struct dup_stmt         dup;
        };
 };
 
index 581f364166a03b1fc1784c0b029efe5f3bb9b932..e8eafc6436f6fde75ec03fbc03a75ba143b0b35e 100644 (file)
@@ -20,6 +20,7 @@
 #include <netinet/ip_icmp.h>
 #include <netinet/icmp6.h>
 #include <net/ethernet.h>
+#include <net/if.h>
 
 #include <expression.h>
 #include <statement.h>
@@ -1617,7 +1618,7 @@ static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt)
        }
 }
 
-static int nat_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
+static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt,
                             struct expr **expr)
 {
        struct proto_ctx *pctx = &ctx->pctx;
@@ -1659,7 +1660,7 @@ static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
                return err;
 
        if (stmt->nat.addr != NULL) {
-               err = nat_evaluate_addr(ctx, stmt, &stmt->nat.addr);
+               err = evaluate_addr(ctx, stmt, &stmt->nat.addr);
                if (err < 0)
                        return err;
        }
@@ -1703,6 +1704,32 @@ static int stmt_evaluate_redir(struct eval_ctx *ctx, struct stmt *stmt)
        return 0;
 }
 
+static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       int err;
+
+       switch (ctx->pctx.family) {
+       case NFPROTO_IPV4:
+       case NFPROTO_IPV6:
+               if (stmt->dup.to == NULL)
+                       return stmt_error(ctx, stmt,
+                                         "missing destination address");
+               err = evaluate_addr(ctx, stmt, &stmt->dup.to);
+               if (err < 0)
+                       return err;
+
+               if (stmt->dup.dev != NULL) {
+                       err = stmt_evaluate_arg(ctx, stmt, &ifindex_type,
+                                               sizeof(uint32_t) * BITS_PER_BYTE,
+                                               &stmt->dup.dev);
+                       if (err < 0)
+                               return err;
+               }
+               break;
+       }
+       return 0;
+}
+
 static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt)
 {
        if (stmt->queue.queue != NULL) {
@@ -1786,6 +1813,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
                return stmt_evaluate_redir(ctx, stmt);
        case STMT_QUEUE:
                return stmt_evaluate_queue(ctx, stmt);
+       case STMT_DUP:
+               return stmt_evaluate_dup(ctx, stmt);
        case STMT_SET:
                return stmt_evaluate_set(ctx, stmt);
        default:
index bfc12580d4afe80975ffbb76ba7e3719156c1704..6f79c401c929e58a9e3a2b9587deb20e99054164 100644 (file)
@@ -160,7 +160,7 @@ static struct error_record *ifindex_type_parse(const struct expr *sym,
        return NULL;
 }
 
-static const struct datatype ifindex_type = {
+const struct datatype ifindex_type = {
        .type           = TYPE_IFINDEX,
        .name           = "iface_index",
        .desc           = "network interface index",
index 2360681d5d128e15a1eb23a691e5e8823bd19d46..09f5932a0e79b53f95cb02e639e45aa7828f8e61 100644 (file)
@@ -749,6 +749,52 @@ static void netlink_parse_redir(struct netlink_parse_ctx *ctx,
        list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
+static void netlink_parse_dup(struct netlink_parse_ctx *ctx,
+                             const struct location *loc,
+                             const struct nftnl_expr *nle)
+{
+       enum nft_registers reg1, reg2;
+       struct expr *addr, *dev;
+       struct stmt *stmt;
+
+       stmt = dup_stmt_alloc(loc);
+
+       reg1 = netlink_parse_register(nle, NFTNL_EXPR_DUP_SREG_ADDR);
+       if (reg1) {
+               addr = netlink_get_register(ctx, loc, reg1);
+               if (addr == NULL)
+                       return netlink_error(ctx, loc,
+                                            "DUP statement has no destination expression");
+
+               switch (ctx->table->handle.family) {
+               case NFPROTO_IPV4:
+                       expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN);
+                       break;
+               case NFPROTO_IPV6:
+                       expr_set_type(addr, &ip6addr_type,
+                                     BYTEORDER_BIG_ENDIAN);
+                       break;
+               }
+               stmt->dup.to = addr;
+       }
+
+       reg2 = netlink_parse_register(nle, NFTNL_EXPR_DUP_SREG_DEV);
+       if (reg2) {
+               dev = netlink_get_register(ctx, loc, reg2);
+               if (dev == NULL)
+                       return netlink_error(ctx, loc,
+                                            "DUP statement has no output expression");
+
+               expr_set_type(dev, &ifindex_type, BYTEORDER_HOST_ENDIAN);
+               if (stmt->dup.to == NULL)
+                       stmt->dup.to = dev;
+               else
+                       stmt->dup.dev = dev;
+       }
+
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
 static void netlink_parse_queue(struct netlink_parse_ctx *ctx,
                              const struct location *loc,
                              const struct nftnl_expr *nle)
@@ -837,6 +883,7 @@ static const struct {
        { .name = "nat",        .parse = netlink_parse_nat },
        { .name = "masq",       .parse = netlink_parse_masq },
        { .name = "redir",      .parse = netlink_parse_redir },
+       { .name = "dup",        .parse = netlink_parse_dup },
        { .name = "queue",      .parse = netlink_parse_queue },
        { .name = "dynset",     .parse = netlink_parse_dynset },
 };
@@ -1460,6 +1507,12 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r
                case STMT_SET:
                        expr_postprocess(&rctx, &stmt->set.key);
                        break;
+               case STMT_DUP:
+                       if (stmt->dup.to != NULL)
+                               expr_postprocess(&rctx, &stmt->dup.to);
+                       if (stmt->dup.dev != NULL)
+                               expr_postprocess(&rctx, &stmt->dup.dev);
+                       break;
                default:
                        break;
                }
index f697ea52210da09672a413867e3070d97ef96a1a..aa44eea5a23e01227222e6ae1908920257bca1f5 100644 (file)
 #include <netlink.h>
 #include <gmputil.h>
 #include <utils.h>
+#include <netinet/in.h>
+
+#include <linux/netfilter.h>
+
 
 struct netlink_linearize_ctx {
        struct nftnl_rule       *nlr;
@@ -859,6 +863,37 @@ static void netlink_gen_redir_stmt(struct netlink_linearize_ctx *ctx,
        nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
+static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx,
+                                const struct stmt *stmt)
+{
+       struct nftnl_expr *nle;
+       enum nft_registers sreg1, sreg2;
+
+       nle = alloc_nft_expr("dup");
+
+       if (stmt->dup.to != NULL) {
+               if (stmt->dup.to->dtype == &ifindex_type) {
+                       sreg1 = get_register(ctx, stmt->dup.to);
+                       netlink_gen_expr(ctx, stmt->dup.to, sreg1);
+                       netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_DEV, sreg1);
+               } else {
+                       sreg1 = get_register(ctx, stmt->dup.to);
+                       netlink_gen_expr(ctx, stmt->dup.to, sreg1);
+                       netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_ADDR, sreg1);
+               }
+       }
+       if (stmt->dup.dev != NULL) {
+               sreg2 = get_register(ctx, stmt->dup.dev);
+               netlink_gen_expr(ctx, stmt->dup.dev, sreg2);
+               netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_DEV, sreg2);
+               release_register(ctx, stmt->dup.dev);
+       }
+       if (stmt->dup.to != NULL)
+               release_register(ctx, stmt->dup.to);
+
+       nftnl_rule_add_expr(ctx->nlr, nle);
+}
+
 static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx,
                                 const struct stmt *stmt)
 {
@@ -949,6 +984,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
                return netlink_gen_masq_stmt(ctx, stmt);
        case STMT_REDIR:
                return netlink_gen_redir_stmt(ctx, stmt);
+       case STMT_DUP:
+               return netlink_gen_dup_stmt(ctx, stmt);
        case STMT_QUEUE:
                return netlink_gen_queue_stmt(ctx, stmt);
        case STMT_CT:
index 36910a7f89176a1c430604af9743062841d33b07..3c371ba3ba35b78d4ebad1dd5514593ffc31aa36 100644 (file)
@@ -393,6 +393,9 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %token BYPASS                  "bypass"
 %token FANOUT                  "fanout"
 
+%token DUP                     "dup"
+%token ON                      "on"
+
 %token POSITION                        "position"
 %token COMMENT                 "comment"
 
@@ -460,6 +463,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <stmt>                   queue_stmt queue_stmt_alloc
 %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc
 %type <val>                    queue_stmt_flags queue_stmt_flag
+%type <stmt>                   dup_stmt
+%destructor { stmt_free($$); } dup_stmt
 %type <stmt>                   set_stmt
 %destructor { stmt_free($$); } set_stmt
 %type <val>                    set_stmt_op
@@ -1310,6 +1315,7 @@ stmt                      :       verdict_stmt
                        |       ct_stmt
                        |       masq_stmt
                        |       redir_stmt
+                       |       dup_stmt
                        |       set_stmt
                        ;
 
@@ -1609,6 +1615,19 @@ redir_stmt_arg           :       TO      expr
                        }
                        ;
 
+dup_stmt               :       DUP     TO      expr
+                       {
+                               $$ = dup_stmt_alloc(&@$);
+                               $$->dup.to = $3;
+                       }
+                       |       DUP     TO      expr    DEVICE  expr
+                       {
+                               $$ = dup_stmt_alloc(&@$);
+                               $$->dup.to = $3;
+                               $$->dup.dev = $5;
+                       }
+                       ;
+
 nf_nat_flags           :       nf_nat_flag
                        |       nf_nat_flags    COMMA   nf_nat_flag
                        {
index bd8e5726d54c10998a68a87e8513459bbc420e51..b827489a599aeaed71a6d85a6eff8af1ddd6b561 100644 (file)
@@ -457,6 +457,8 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 "proto-dst"            { return PROTO_DST; }
 "label"                        { return LABEL; }
 
+"dup"                  { return DUP; }
+
 "xml"                  { return XML; }
 "json"                 { return JSON; }
 
index d620d1ba3605388961f0c32ac4570edffed4043a..2d1a3e6bd1340298dcad87cb1fe175f3136adff9 100644 (file)
@@ -455,3 +455,35 @@ struct stmt *set_stmt_alloc(const struct location *loc)
 {
        return stmt_alloc(loc, &set_stmt_ops);
 }
+
+static void dup_stmt_print(const struct stmt *stmt)
+{
+       printf("dup");
+       if (stmt->dup.to != NULL) {
+               printf(" to ");
+               expr_print(stmt->dup.to);
+
+               if (stmt->dup.dev != NULL) {
+                       printf(" device ");
+                       expr_print(stmt->dup.dev);
+               }
+       }
+}
+
+static void dup_stmt_destroy(struct stmt *stmt)
+{
+       expr_free(stmt->dup.to);
+       expr_free(stmt->dup.dev);
+}
+
+static const struct stmt_ops dup_stmt_ops = {
+       .type           = STMT_DUP,
+       .name           = "dup",
+       .print          = dup_stmt_print,
+       .destroy        = dup_stmt_destroy,
+};
+
+struct stmt *dup_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &dup_stmt_ops);
+}