{"id":805168,"url":"http://patchwork.ozlabs.org/api/1.2/patches/805168/?format=json","web_url":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170823204256.31634-2-pablombg@gmail.com/","project":{"id":26,"url":"http://patchwork.ozlabs.org/api/1.2/projects/26/?format=json","name":"Netfilter Development","link_name":"netfilter-devel","list_id":"netfilter-devel.vger.kernel.org","list_email":"netfilter-devel@vger.kernel.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20170823204256.31634-2-pablombg@gmail.com>","list_archive_url":null,"date":"2017-08-23T20:42:56","name":"[nft,2/2] src: limit stateful object support","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"e7ab5d2fdb972cb59be33cc7b8a0aa7225701d53","submitter":{"id":67698,"url":"http://patchwork.ozlabs.org/api/1.2/people/67698/?format=json","name":"Pablo M. Bermudo Garay","email":"pablombg@gmail.com"},"delegate":{"id":6139,"url":"http://patchwork.ozlabs.org/api/1.2/users/6139/?format=json","username":"pablo","first_name":"Pablo","last_name":"Neira","email":"pablo@netfilter.org"},"mbox":"http://patchwork.ozlabs.org/project/netfilter-devel/patch/20170823204256.31634-2-pablombg@gmail.com/mbox/","series":[],"comments":"http://patchwork.ozlabs.org/api/patches/805168/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/805168/checks/","tags":{},"related":[],"headers":{"Return-Path":"<netfilter-devel-owner@vger.kernel.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netfilter-devel-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)","ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=gmail.com header.i=@gmail.com\n\theader.b=\"W86iDaKX\"; dkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xczsF3xCjz9t34\n\tfor <incoming@patchwork.ozlabs.org>;\n\tThu, 24 Aug 2017 06:43:13 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S932643AbdHWUnN (ORCPT <rfc822;incoming@patchwork.ozlabs.org>);\n\tWed, 23 Aug 2017 16:43:13 -0400","from mail-wr0-f194.google.com ([209.85.128.194]:35419 \"EHLO\n\tmail-wr0-f194.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1754207AbdHWUnM (ORCPT\n\t<rfc822;netfilter-devel@vger.kernel.org>);\n\tWed, 23 Aug 2017 16:43:12 -0400","by mail-wr0-f194.google.com with SMTP id n53so380289wrb.2\n\tfor <netfilter-devel@vger.kernel.org>;\n\tWed, 23 Aug 2017 13:43:11 -0700 (PDT)","from localhost.localdomain (75.red-88-9-82.dynamicip.rima-tde.net.\n\t[88.9.82.75]) by smtp.gmail.com with ESMTPSA id\n\td10sm2615917wmh.4.2017.08.23.13.43.08\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tWed, 23 Aug 2017 13:43:09 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=gmail.com; s=20161025;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=kpjADKUiynqASaLWaAKwK1nV3DlZIeaMK6kpluUN11s=;\n\tb=W86iDaKXnfZNbdiOVa0Td+dEMJkYxx3zgebbKZLg2zOxSwV4opWBAznOeRwHpKJhka\n\tazvRmj8VtHyzNZO/5aA7mCY+bUWefe1XLUkOJ0zs3wtV9gosRalQS4BADsWVpCE4C/9n\n\tfHpo69ppynJ668zwZTpt/FJfAbzeuUa+MNZ7xQeKz35anu4PeDJzERzleNWge6ALgo5a\n\ttCUwZbGgW4FY2yAeHiXtlqqJi4Qj3FSLRkooTqUq4i/jnEUGEOqU+h1Qc2f7FAtttpG2\n\tXMrSmkMkiYOgZyzrHoUyZbzNc0JlS4x6vEGMDUT9MFZcHQ5EVMmdpX8D3pLKtu+WUp9Y\n\tLw4Q==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=kpjADKUiynqASaLWaAKwK1nV3DlZIeaMK6kpluUN11s=;\n\tb=C6uxMsahOK7lMkqlPXHoCy8YYnYZ+HPBp5HaJ6XoHnCd/gBTaYjU6282wP2yaVJvUz\n\tW0AXSrIq+1sGGcDr2huyVuEfISWMaAHMrhfx2iPFnckf69VSu8ip6YDNDHeJ8PE1Lp9n\n\tUyy3TBxMJQI9hAQzwkyj/QOjbekb4EwRNo/K+1w9aBKL7aOLLU6yl9MPJFAHGFR+2tQv\n\tS/ldd/DCiD4fx2Zky9nmDFQPZyQeT4KsrQ7kSsKMQbbj0Gres8ANAgPifq+IsoKGPpFd\n\tGTVu8IudeX9WioQr5MqebfR/SCP/YeWtalo4YdYh0CFpEdHI4MV40jIISTOLeZsEaNgi\n\tKbDg==","X-Gm-Message-State":"AHYfb5gGcYuQW1LhRFfgItt7bsUe10T4GoLWourbB5FqqYYdDb2vOGR+\n\tnihOax76x3q4nSR2Lo4=","X-Received":"by 10.223.138.165 with SMTP id y34mr2351088wry.209.1503520990417;\n\tWed, 23 Aug 2017 13:43:10 -0700 (PDT)","From":"\"Pablo M. Bermudo Garay\" <pablombg@gmail.com>","To":"netfilter-devel@vger.kernel.org","Cc":"pablo@netfilter.org, \"Pablo M. Bermudo Garay\" <pablombg@gmail.com>","Subject":"[PATCH nft 2/2] src: limit stateful object support","Date":"Wed, 23 Aug 2017 22:42:56 +0200","Message-Id":"<20170823204256.31634-2-pablombg@gmail.com>","X-Mailer":"git-send-email 2.14.1","In-Reply-To":"<20170823204256.31634-1-pablombg@gmail.com>","References":"<20170823204256.31634-1-pablombg@gmail.com>","Sender":"netfilter-devel-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netfilter-devel.vger.kernel.org>","X-Mailing-List":"netfilter-devel@vger.kernel.org"},"content":"This patch adds support for a new type of stateful object: limit.\nCreation, deletion and listing operations are supported.\n\nSigned-off-by: Pablo M. Bermudo Garay <pablombg@gmail.com>\n---\n include/linux/netfilter/nf_tables.h |   3 +-\n include/rule.h                      |  13 +++++\n include/statement.h                 |   1 +\n src/evaluate.c                      |   5 ++\n src/netlink.c                       |  19 +++++++\n src/parser_bison.y                  | 101 ++++++++++++++++++++++++++++++++++--\n src/rule.c                          |  43 ++++++++++++++-\n src/scanner.l                       |   1 +\n src/statement.c                     |   3 +-\n 9 files changed, 183 insertions(+), 6 deletions(-)","diff":"diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h\nindex 5441b19..f328944 100644\n--- a/include/linux/netfilter/nf_tables.h\n+++ b/include/linux/netfilter/nf_tables.h\n@@ -1278,7 +1278,8 @@ enum nft_ct_helper_attributes {\n #define NFT_OBJECT_COUNTER\t1\n #define NFT_OBJECT_QUOTA\t2\n #define NFT_OBJECT_CT_HELPER\t3\n-#define __NFT_OBJECT_MAX\t4\n+#define NFT_OBJECT_LIMIT\t4\n+#define __NFT_OBJECT_MAX\t5\n #define NFT_OBJECT_MAX\t\t(__NFT_OBJECT_MAX - 1)\n \n /**\ndiff --git a/include/rule.h b/include/rule.h\nindex 10ac0e2..94f7bb5 100644\n--- a/include/rule.h\n+++ b/include/rule.h\n@@ -272,6 +272,14 @@ struct ct_helper {\n \tuint8_t l4proto;\n };\n \n+struct limit {\n+\tuint64_t\trate;\n+\tuint64_t\tunit;\n+\tuint32_t\tburst;\n+\tuint32_t\ttype;\n+\tuint32_t\tflags;\n+};\n+\n /**\n  * struct obj - nftables stateful object statement\n  *\n@@ -291,6 +299,7 @@ struct obj {\n \t\tstruct counter\t\tcounter;\n \t\tstruct quota\t\tquota;\n \t\tstruct ct_helper\tct_helper;\n+\t\tstruct limit\t\tlimit;\n \t};\n };\n \n@@ -357,6 +366,8 @@ enum cmd_ops {\n  * @CMD_OBJ_COUNTERS:\tmultiple counters\n  * @CMD_OBJ_QUOTA:\tquota\n  * @CMD_OBJ_QUOTAS:\tmultiple quotas\n+ * @CMD_OBJ_LIMIT:\tlimit\n+ * @CMD_OBJ_LIMITS:\tmultiple limits\n  */\n enum cmd_obj {\n \tCMD_OBJ_INVALID,\n@@ -381,6 +392,8 @@ enum cmd_obj {\n \tCMD_OBJ_QUOTAS,\n \tCMD_OBJ_CT_HELPER,\n \tCMD_OBJ_CT_HELPERS,\n+\tCMD_OBJ_LIMIT,\n+\tCMD_OBJ_LIMITS,\n };\n \n struct export {\ndiff --git a/include/statement.h b/include/statement.h\nindex 6d8aaa8..2f702c3 100644\n--- a/include/statement.h\n+++ b/include/statement.h\n@@ -325,5 +325,6 @@ extern void stmt_list_free(struct list_head *list);\n extern void stmt_print(const struct stmt *stmt, struct output_ctx *octx);\n \n const char *get_rate(uint64_t byte_rate, uint64_t *rate);\n+const char *get_unit(uint64_t u);\n \n #endif /* NFTABLES_STATEMENT_H */\ndiff --git a/src/evaluate.c b/src/evaluate.c\nindex 3989d5e..a92a66d 100644\n--- a/src/evaluate.c\n+++ b/src/evaluate.c\n@@ -2997,6 +2997,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)\n \tcase CMD_OBJ_COUNTER:\n \tcase CMD_OBJ_QUOTA:\n \tcase CMD_OBJ_CT_HELPER:\n+\tcase CMD_OBJ_LIMIT:\n \t\treturn 0;\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n@@ -3022,6 +3023,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)\n \tcase CMD_OBJ_COUNTER:\n \tcase CMD_OBJ_QUOTA:\n \tcase CMD_OBJ_CT_HELPER:\n+\tcase CMD_OBJ_LIMIT:\n \t\treturn 0;\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n@@ -3111,9 +3113,12 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)\n \t\treturn cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_COUNTER);\n \tcase CMD_OBJ_CT_HELPER:\n \t\treturn cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);\n+\tcase CMD_OBJ_LIMIT:\n+\t\treturn cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);\n \tcase CMD_OBJ_COUNTERS:\n \tcase CMD_OBJ_QUOTAS:\n \tcase CMD_OBJ_CT_HELPERS:\n+\tcase CMD_OBJ_LIMITS:\n \tcase CMD_OBJ_SETS:\n \t\tif (cmd->handle.table == NULL)\n \t\t\treturn 0;\ndiff --git a/src/netlink.c b/src/netlink.c\nindex f6eb08f..a165809 100644\n--- a/src/netlink.c\n+++ b/src/netlink.c\n@@ -328,6 +328,13 @@ alloc_nftnl_obj(const struct handle *h, struct obj *obj)\n \t\t\tnftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO,\n \t\t\t\t\t  obj->ct_helper.l3proto);\n \t\tbreak;\n+\tcase NFT_OBJECT_LIMIT:\n+\t\tnftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_RATE, obj->limit.rate);\n+\t\tnftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_UNIT, obj->limit.unit);\n+\t\tnftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_BURST, obj->limit.burst);\n+\t\tnftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_TYPE, obj->limit.type);\n+\t\tnftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS, obj->limit.flags);\n+\t\tbreak;\n \tdefault:\n \t\tBUG(\"Unknown type %d\\n\", obj->type);\n \t\tbreak;\n@@ -1743,6 +1750,18 @@ static struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,\n \t\tobj->ct_helper.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO);\n \t\tobj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO);\n \t\tbreak;\n+\tcase NFT_OBJECT_LIMIT:\n+\t\tobj->limit.rate =\n+\t\t\tnftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_RATE);\n+\t\tobj->limit.unit =\n+\t\t\tnftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_UNIT);\n+\t\tobj->limit.burst =\n+\t\t\tnftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_BURST);\n+\t\tobj->limit.type =\n+\t\t\tnftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_TYPE);\n+\t\tobj->limit.flags =\n+\t\t\tnftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS);\n+\t\tbreak;\n \t}\n \tobj->type = type;\n \ndiff --git a/src/parser_bison.y b/src/parser_bison.y\nindex ca86df5..e410298 100644\n--- a/src/parser_bison.y\n+++ b/src/parser_bison.y\n@@ -142,6 +142,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n \tstruct counter\t\t*counter;\n \tstruct quota\t\t*quota;\n \tstruct ct\t\t*ct;\n+\tstruct limit\t\t*limit;\n \tconst struct datatype\t*datatype;\n \tstruct handle_spec\thandle_spec;\n \tstruct position_spec\tposition_spec;\n@@ -393,6 +394,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n \n %token COUNTERS\t\t\t\"counters\"\n %token QUOTAS\t\t\t\"quotas\"\n+%token LIMITS\t\t\t\"limits\"\n \n %token LOG\t\t\t\"log\"\n %token PREFIX\t\t\t\"prefix\"\n@@ -501,7 +503,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n %type <set>\t\t\tmap_block_alloc map_block\n %destructor { set_free($$); }\tmap_block_alloc\n \n-%type <obj>\t\t\tobj_block_alloc counter_block quota_block ct_block\n+%type <obj>\t\t\tobj_block_alloc counter_block quota_block ct_block limit_block\n %destructor { obj_free($$); }\tobj_block_alloc\n \n %type <list>\t\t\tstmt_list\n@@ -589,8 +591,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n %type <expr>\t\t\tand_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr\n %destructor { expr_free($$); }\tand_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr\n \n-%type <obj>\t\t\tcounter_obj quota_obj ct_obj_alloc\n-%destructor { obj_free($$); }\tcounter_obj quota_obj ct_obj_alloc\n+%type <obj>\t\t\tcounter_obj quota_obj ct_obj_alloc limit_obj\n+%destructor { obj_free($$); }\tcounter_obj quota_obj ct_obj_alloc limit_obj\n \n %type <expr>\t\t\trelational_expr\n %destructor { expr_free($$); }\trelational_expr\n@@ -661,6 +663,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)\n %destructor { xfree($$); }\tcounter_config\n %type <quota>\t\t\tquota_config\n %destructor { xfree($$); }\tquota_config\n+%type <limit>\t\t\tlimit_config\n+%destructor { xfree($$); }\tlimit_config\n \n %type <expr>\t\t\ttcp_hdr_expr\n %destructor { expr_free($$); }\ttcp_hdr_expr\n@@ -864,6 +868,10 @@ add_cmd\t\t\t:\tTABLE\t\ttable_spec\n \n \t\t\t\t$$ = cmd_alloc_obj_ct(CMD_ADD, type, &$3, &@$, $4);\n \t\t\t}\n+\t\t\t|\tLIMIT\t\tobj_spec\tlimit_obj\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);\n+\t\t\t}\n \t\t\t;\n \n replace_cmd\t\t:\tRULE\t\truleid_spec\trule\n@@ -943,6 +951,10 @@ create_cmd\t\t:\tTABLE\t\ttable_spec\n \n \t\t\t\t$$ = cmd_alloc_obj_ct(CMD_CREATE, type, &$3, &@$, $4);\n \t\t\t}\n+\t\t\t|\tLIMIT\t\tobj_spec\tlimit_obj\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3);\n+\t\t\t}\n \t\t\t;\n \n insert_cmd\t\t:\tRULE\t\trule_position\trule\n@@ -996,6 +1008,10 @@ delete_cmd\t\t:\tTABLE\t\ttable_spec\n \n \t\t\t\t$$ = cmd_alloc_obj_ct(CMD_DELETE, type, &$3, &@$, $4);\n \t\t\t}\n+\t\t\t|\tLIMIT\t\tobj_spec\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);\n+\t\t\t}\n \t\t\t;\n \n list_cmd\t\t:\tTABLE\t\ttable_spec\n@@ -1050,6 +1066,18 @@ list_cmd\t\t:\tTABLE\t\ttable_spec\n \t\t\t{\n \t\t\t\t$$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);\n \t\t\t}\n+\t\t\t|\tLIMITS\t\truleset_spec\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$2, &@$, NULL);\n+\t\t\t}\n+\t\t\t|\tLIMITS\t\tTABLE\ttable_spec\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL);\n+\t\t\t}\n+\t\t\t|\tLIMIT\t\tobj_spec\n+\t\t\t{\n+\t\t\t\t$$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL);\n+\t\t\t}\n \t\t\t|\tRULESET\t\truleset_spec\n \t\t\t{\n \t\t\t\t$$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);\n@@ -1311,6 +1339,17 @@ table_block\t\t:\t/* empty */\t{ $$ = $<table>-1; }\n \t\t\t\tlist_add_tail(&$5->list, &$1->objs);\n \t\t\t\t$$ = $1;\n \t\t\t}\n+\t\t\t|\ttable_block\tLIMIT\t\tobj_identifier\n+\t\t\t\t\tobj_block_alloc\t'{'\tlimit_block\t'}'\n+\t\t\t\t\tstmt_separator\n+\t\t\t{\n+\t\t\t\t$4->location = @3;\n+\t\t\t\t$4->type = NFT_OBJECT_LIMIT;\n+\t\t\t\thandle_merge(&$4->handle, &$3);\n+\t\t\t\thandle_free(&$3);\n+\t\t\t\tlist_add_tail(&$4->list, &$1->objs);\n+\t\t\t\t$$ = $1;\n+\t\t\t}\n \t\t\t;\n \n chain_block_alloc\t:\t/* empty */\n@@ -1516,6 +1555,15 @@ ct_block\t\t:\t/* empty */\t{ $$ = $<obj>-1; }\n \t\t\t}\n \t\t\t;\n \n+limit_block\t\t:\t/* empty */\t{ $$ = $<obj>-1; }\n+\t\t\t|       limit_block     common_block\n+\t\t\t|       limit_block     stmt_separator\n+\t\t\t|       limit_block     limit_config\n+\t\t\t{\n+\t\t\t\t$1->limit = *$2;\n+\t\t\t\t$$ = $1;\n+\t\t\t}\n+\t\t\t;\n \n type_identifier\t\t:\tSTRING\t{ $$ = $1; }\n \t\t\t|\tMARK\t{ $$ = xstrdup(\"mark\"); }\n@@ -1989,6 +2037,12 @@ limit_stmt\t\t:\tLIMIT\tRATE\tlimit_mode\tNUM\tSLASH\ttime_unit\tlimit_burst\n \t\t\t\t$$->limit.type\t= NFT_LIMIT_PKT_BYTES;\n \t\t\t\t$$->limit.flags = $3;\n \t\t\t}\n+\t\t\t|\tLIMIT\tNAME\tstmt_expr\n+\t\t\t{\n+\t\t\t\t$$ = objref_stmt_alloc(&@$);\n+\t\t\t\t$$->objref.type = NFT_OBJECT_LIMIT;\n+\t\t\t\t$$->objref.expr = $3;\n+\t\t\t}\n \t\t\t;\n \n quota_mode\t\t:\tOVER\t\t{ $$ = NFT_QUOTA_F_INV; }\n@@ -2745,6 +2799,47 @@ ct_obj_alloc\t\t:\n \t\t\t}\n \t\t\t;\n \n+limit_config\t\t:\tRATE\tlimit_mode\tNUM\tSLASH\ttime_unit\tlimit_burst\n+\t\t\t{\n+\t\t\t\tstruct limit *limit;\n+\t\t\t\tlimit = xzalloc(sizeof(*limit));\n+\t\t\t\tlimit->rate\t= $3;\n+\t\t\t\tlimit->unit\t= $5;\n+\t\t\t\tlimit->burst\t= $6;\n+\t\t\t\tlimit->type\t= NFT_LIMIT_PKTS;\n+\t\t\t\tlimit->flags\t= $2;\n+\t\t\t\t$$ = limit;\n+\t\t\t}\n+\t\t\t|\tRATE\tlimit_mode\tNUM\tSTRING\tlimit_burst\n+\t\t\t{\n+\t\t\t\tstruct limit *limit;\n+\t\t\t\tstruct error_record *erec;\n+\t\t\t\tuint64_t rate, unit;\n+\n+\t\t\t\terec = rate_parse(&@$, $4, &rate, &unit);\n+\t\t\t\tif (erec != NULL) {\n+\t\t\t\t\terec_queue(erec, state->msgs);\n+\t\t\t\t\tYYERROR;\n+\t\t\t\t}\n+\n+\t\t\t\tlimit = xzalloc(sizeof(*limit));\n+\t\t\t\tlimit->rate\t= rate * $3;\n+\t\t\t\tlimit->unit\t= unit;\n+\t\t\t\tlimit->burst\t= $5;\n+\t\t\t\tlimit->type\t= NFT_LIMIT_PKT_BYTES;\n+\t\t\t\tlimit->flags\t= $2;\n+\t\t\t\t$$ = limit;\n+\t\t\t}\n+\t\t\t;\n+\n+limit_obj\t\t:\tlimit_config\n+\t\t\t{\n+\t\t\t\t$$ = obj_alloc(&@$);\n+\t\t\t\t$$->type = NFT_OBJECT_LIMIT;\n+\t\t\t\t$$->limit = *$1;\n+\t\t\t}\n+\t\t\t;\n+\n relational_expr\t\t:\texpr\t/* implicit */\trhs_expr\n \t\t\t{\n \t\t\t\t$$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);\ndiff --git a/src/rule.c b/src/rule.c\nindex ef12bec..ae973bd 100644\n--- a/src/rule.c\n+++ b/src/rule.c\n@@ -959,6 +959,7 @@ void cmd_free(struct cmd *cmd)\n \t\tcase CMD_OBJ_COUNTER:\n \t\tcase CMD_OBJ_QUOTA:\n \t\tcase CMD_OBJ_CT_HELPER:\n+\t\tcase CMD_OBJ_LIMIT:\n \t\t\tobj_free(cmd->object);\n \t\t\tbreak;\n \t\tdefault:\n@@ -1046,6 +1047,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)\n \tcase CMD_OBJ_COUNTER:\n \tcase CMD_OBJ_QUOTA:\n \tcase CMD_OBJ_CT_HELPER:\n+\tcase CMD_OBJ_LIMIT:\n \t\treturn netlink_add_obj(ctx, &cmd->handle, cmd->object, flags);\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n@@ -1132,6 +1134,9 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)\n \tcase CMD_OBJ_CT_HELPER:\n \t\treturn netlink_delete_obj(ctx, &cmd->handle, &cmd->location,\n \t\t\t\t\t  NFT_OBJECT_CT_HELPER);\n+\tcase CMD_OBJ_LIMIT:\n+\t\treturn netlink_delete_obj(ctx, &cmd->handle, &cmd->location,\n+\t\t\t\t\t  NFT_OBJECT_LIMIT);\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n \t}\n@@ -1292,6 +1297,37 @@ static void obj_print_data(const struct obj *obj,\n \t\tprintf(\"\\t\\tl3proto %s\", family2str(obj->ct_helper.l3proto));\n \t\tbreak;\n \t\t}\n+\tcase NFT_OBJECT_LIMIT: {\n+\t\tbool inv = obj->limit.flags & NFT_LIMIT_F_INV;\n+\t\tconst char *data_unit;\n+\t\tuint64_t rate;\n+\n+\t\tprintf(\" %s {%s%s%s\", obj->handle.obj,\n+\t\t\t\t      opts->nl, opts->tab, opts->tab);\n+\t\tswitch (obj->limit.type) {\n+\t\tcase NFT_LIMIT_PKTS:\n+\t\t\tprintf(\"limit rate %s%\" PRIu64 \"/%s\",\n+\t\t\t       inv ? \"over \" : \"\", obj->limit.rate,\n+\t\t\t       get_unit(obj->limit.unit));\n+\t\t\tif (obj->limit.burst > 0)\n+\t\t\t\tprintf(\" burst %u packets\", obj->limit.burst);\n+\t\t\tbreak;\n+\t\tcase NFT_LIMIT_PKT_BYTES:\n+\t\t\tdata_unit = get_rate(obj->limit.rate, &rate);\n+\n+\t\t\tprintf(\"limit rate %s%\" PRIu64 \" %s/%s\",\n+\t\t\t       inv ? \"over \" : \"\", rate, data_unit,\n+\t\t\t       get_unit(obj->limit.unit));\n+\t\t\tif (obj->limit.burst > 0) {\n+\t\t\t\tuint64_t burst;\n+\n+\t\t\t\tdata_unit = get_rate(obj->limit.burst, &burst);\n+\t\t\t\tprintf(\" burst %\"PRIu64\" %s\", burst, data_unit);\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\t}\n+\t\tbreak;\n \tdefault:\n \t\tprintf(\"unknown {%s\", opts->nl);\n \t\tbreak;\n@@ -1302,11 +1338,12 @@ static const char *obj_type_name_array[] = {\n \t[NFT_OBJECT_COUNTER]\t= \"counter\",\n \t[NFT_OBJECT_QUOTA]\t= \"quota\",\n \t[NFT_OBJECT_CT_HELPER]\t= \"\",\n+\t[NFT_OBJECT_LIMIT]\t= \"limit\",\n };\n \n const char *obj_type_name(enum stmt_types type)\n {\n-\tassert(type <= NFT_OBJECT_CT_HELPER && obj_type_name_array[type]);\n+\tassert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]);\n \n \treturn obj_type_name_array[type];\n }\n@@ -1315,6 +1352,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {\n \t[NFT_OBJECT_COUNTER]\t= CMD_OBJ_COUNTER,\n \t[NFT_OBJECT_QUOTA]\t= CMD_OBJ_QUOTA,\n \t[NFT_OBJECT_CT_HELPER]\t= CMD_OBJ_CT_HELPER,\n+\t[NFT_OBJECT_LIMIT]\t= CMD_OBJ_LIMIT,\n };\n \n uint32_t obj_type_to_cmd(uint32_t type)\n@@ -1546,6 +1584,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)\n \tcase CMD_OBJ_CT_HELPER:\n \tcase CMD_OBJ_CT_HELPERS:\n \t\treturn do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);\n+\tcase CMD_OBJ_LIMIT:\n+\tcase CMD_OBJ_LIMITS:\n+\t\treturn do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);\n \tdefault:\n \t\tBUG(\"invalid command object type %u\\n\", cmd->obj);\n \t}\ndiff --git a/src/scanner.l b/src/scanner.l\nindex b6ba32d..ef424e4 100644\n--- a/src/scanner.l\n+++ b/src/scanner.l\n@@ -300,6 +300,7 @@ addrstring\t({macaddr}|{ip4addr}|{ip6addr})\n \n \"counters\"\t\t{ return COUNTERS; }\n \"quotas\"\t\t{ return QUOTAS; }\n+\"limits\"\t\t{ return LIMITS; }\n \n \"log\"\t\t\t{ return LOG; }\n \"prefix\"\t\t{ return PREFIX; }\ndiff --git a/src/statement.c b/src/statement.c\nindex 58f8aaf..0b2c28b 100644\n--- a/src/statement.c\n+++ b/src/statement.c\n@@ -175,6 +175,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = {\n \t[NFT_OBJECT_COUNTER]\t= \"counter\",\n \t[NFT_OBJECT_QUOTA]\t= \"quota\",\n \t[NFT_OBJECT_CT_HELPER]\t= \"cthelper\",\n+\t[NFT_OBJECT_LIMIT]\t= \"limit\",\n };\n \n static const char *objref_type_name(uint32_t type)\n@@ -286,7 +287,7 @@ struct stmt *log_stmt_alloc(const struct location *loc)\n \treturn stmt_alloc(loc, &log_stmt_ops);\n }\n \n-static const char *get_unit(uint64_t u)\n+const char *get_unit(uint64_t u)\n {\n \tswitch (u) {\n \tcase 1: return \"second\";\n","prefixes":["nft","2/2"]}