]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add stateful object support for limit
authorPablo M. Bermudo Garay <pablombg@gmail.com>
Wed, 23 Aug 2017 20:42:56 +0000 (22:42 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 4 Sep 2017 20:29:19 +0000 (22:29 +0200)
This patch adds support for a new type of stateful object: limit.
Creation, deletion and listing operations are supported.

Signed-off-by: Pablo M. Bermudo Garay <pablombg@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/nf_tables.h
include/rule.h
include/statement.h
src/evaluate.c
src/netlink.c
src/parser_bison.y
src/rule.c
src/scanner.l
src/statement.c

index 5441b190852f076b8e4ff1de70b4e0e3c362cf50..f32894431f8230b4c53c7f8ada87ff0531c42b7c 100644 (file)
@@ -1278,7 +1278,8 @@ enum nft_ct_helper_attributes {
 #define NFT_OBJECT_COUNTER     1
 #define NFT_OBJECT_QUOTA       2
 #define NFT_OBJECT_CT_HELPER   3
-#define __NFT_OBJECT_MAX       4
+#define NFT_OBJECT_LIMIT       4
+#define __NFT_OBJECT_MAX       5
 #define NFT_OBJECT_MAX         (__NFT_OBJECT_MAX - 1)
 
 /**
index 04da000f6c796ab331c6bff1c561cda97561ad35..631a1bcdf84ec29326f4b691f32093be56de1ba2 100644 (file)
@@ -272,6 +272,14 @@ struct ct_helper {
        uint8_t l4proto;
 };
 
+struct limit {
+       uint64_t        rate;
+       uint64_t        unit;
+       uint32_t        burst;
+       uint32_t        type;
+       uint32_t        flags;
+};
+
 /**
  * struct obj - nftables stateful object statement
  *
@@ -291,6 +299,7 @@ struct obj {
                struct counter          counter;
                struct quota            quota;
                struct ct_helper        ct_helper;
+               struct limit            limit;
        };
 };
 
@@ -357,6 +366,8 @@ enum cmd_ops {
  * @CMD_OBJ_COUNTERS:  multiple counters
  * @CMD_OBJ_QUOTA:     quota
  * @CMD_OBJ_QUOTAS:    multiple quotas
+ * @CMD_OBJ_LIMIT:     limit
+ * @CMD_OBJ_LIMITS:    multiple limits
  */
 enum cmd_obj {
        CMD_OBJ_INVALID,
@@ -381,6 +392,8 @@ enum cmd_obj {
        CMD_OBJ_QUOTAS,
        CMD_OBJ_CT_HELPER,
        CMD_OBJ_CT_HELPERS,
+       CMD_OBJ_LIMIT,
+       CMD_OBJ_LIMITS,
 };
 
 struct export {
index 6d8aaa8ba72b556c019c42dbaabc9e5e50822fed..2f702c3c5d362cb607053183eda1471e194ded0f 100644 (file)
@@ -325,5 +325,6 @@ extern void stmt_list_free(struct list_head *list);
 extern void stmt_print(const struct stmt *stmt, struct output_ctx *octx);
 
 const char *get_rate(uint64_t byte_rate, uint64_t *rate);
+const char *get_unit(uint64_t u);
 
 #endif /* NFTABLES_STATEMENT_H */
index 9954d5c596229bc7f9a02da270af6ed8981a0442..e767542a868e426b9380514c342a4c00d08f098a 100644 (file)
@@ -2996,6 +2996,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
+       case CMD_OBJ_LIMIT:
                return 0;
        default:
                BUG("invalid command object type %u\n", cmd->obj);
@@ -3021,6 +3022,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
+       case CMD_OBJ_LIMIT:
                return 0;
        default:
                BUG("invalid command object type %u\n", cmd->obj);
@@ -3111,9 +3113,12 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd)
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_COUNTER);
        case CMD_OBJ_CT_HELPER:
                return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_LIMIT:
+               return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
        case CMD_OBJ_COUNTERS:
        case CMD_OBJ_QUOTAS:
        case CMD_OBJ_CT_HELPERS:
+       case CMD_OBJ_LIMITS:
        case CMD_OBJ_SETS:
                if (cmd->handle.table == NULL)
                        return 0;
index b6336e836e8873499bb00f8d651042e8afdb34da..291bbdeeaa682d1b34b16e014dc77d2df8f89171 100644 (file)
@@ -322,6 +322,13 @@ alloc_nftnl_obj(const struct handle *h, struct obj *obj)
                        nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO,
                                          obj->ct_helper.l3proto);
                break;
+       case NFT_OBJECT_LIMIT:
+               nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_RATE, obj->limit.rate);
+               nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_UNIT, obj->limit.unit);
+               nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_BURST, obj->limit.burst);
+               nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_TYPE, obj->limit.type);
+               nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS, obj->limit.flags);
+               break;
        default:
                BUG("Unknown type %d\n", obj->type);
                break;
@@ -1728,6 +1735,18 @@ static struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx,
                obj->ct_helper.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO);
                obj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO);
                break;
+       case NFT_OBJECT_LIMIT:
+               obj->limit.rate =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_RATE);
+               obj->limit.unit =
+                       nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_UNIT);
+               obj->limit.burst =
+                       nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_BURST);
+               obj->limit.type =
+                       nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_TYPE);
+               obj->limit.flags =
+                       nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS);
+               break;
        }
        obj->type = type;
 
index d4b29029186b28b710ad1449e1f0d132c39a985c..31a7e8be2bcd80f9994e222e784897620af7dbec 100644 (file)
@@ -143,6 +143,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
        struct counter          *counter;
        struct quota            *quota;
        struct ct               *ct;
+       struct limit            *limit;
        const struct datatype   *datatype;
        struct handle_spec      handle_spec;
        struct position_spec    position_spec;
@@ -394,6 +395,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 
 %token COUNTERS                        "counters"
 %token QUOTAS                  "quotas"
+%token LIMITS                  "limits"
 
 %token LOG                     "log"
 %token PREFIX                  "prefix"
@@ -502,7 +504,7 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <set>                    map_block_alloc map_block
 %destructor { set_free($$); }  map_block_alloc
 
-%type <obj>                    obj_block_alloc counter_block quota_block ct_block
+%type <obj>                    obj_block_alloc counter_block quota_block ct_block limit_block
 %destructor { obj_free($$); }  obj_block_alloc
 
 %type <list>                   stmt_list
@@ -590,8 +592,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %type <expr>                   and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 %destructor { expr_free($$); } and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr
 
-%type <obj>                    counter_obj quota_obj ct_obj_alloc
-%destructor { obj_free($$); }  counter_obj quota_obj ct_obj_alloc
+%type <obj>                    counter_obj quota_obj ct_obj_alloc limit_obj
+%destructor { obj_free($$); }  counter_obj quota_obj ct_obj_alloc limit_obj
 
 %type <expr>                   relational_expr
 %destructor { expr_free($$); } relational_expr
@@ -662,6 +664,8 @@ static void location_update(struct location *loc, struct location *rhs, int n)
 %destructor { xfree($$); }     counter_config
 %type <quota>                  quota_config
 %destructor { xfree($$); }     quota_config
+%type <limit>                  limit_config
+%destructor { xfree($$); }     limit_config
 
 %type <expr>                   tcp_hdr_expr
 %destructor { expr_free($$); } tcp_hdr_expr
@@ -869,6 +873,10 @@ add_cmd                    :       TABLE           table_spec
 
                                $$ = cmd_alloc_obj_ct(CMD_ADD, type, &$3, &@$, $4);
                        }
+                       |       LIMIT           obj_spec        limit_obj
+                       {
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3);
+                       }
                        ;
 
 replace_cmd            :       RULE            ruleid_spec     rule
@@ -949,6 +957,10 @@ create_cmd         :       TABLE           table_spec
 
                                $$ = cmd_alloc_obj_ct(CMD_CREATE, type, &$3, &@$, $4);
                        }
+                       |       LIMIT           obj_spec        limit_obj
+                       {
+                               $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3);
+                       }
                        ;
 
 insert_cmd             :       RULE            rule_position   rule
@@ -1003,6 +1015,10 @@ delete_cmd               :       TABLE           table_spec
 
                                $$ = cmd_alloc_obj_ct(CMD_DELETE, type, &$3, &@$, $4);
                        }
+                       |       LIMIT           obj_spec
+                       {
+                               $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+                       }
                        ;
 
 list_cmd               :       TABLE           table_spec
@@ -1057,6 +1073,18 @@ list_cmd         :       TABLE           table_spec
                        {
                                $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL);
                        }
+                       |       LIMITS          ruleset_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$2, &@$, NULL);
+                       }
+                       |       LIMITS          TABLE   table_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL);
+                       }
+                       |       LIMIT           obj_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL);
+                       }
                        |       RULESET         ruleset_spec
                        {
                                $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL);
@@ -1318,6 +1346,17 @@ table_block              :       /* empty */     { $$ = $<table>-1; }
                                list_add_tail(&$5->list, &$1->objs);
                                $$ = $1;
                        }
+                       |       table_block     LIMIT           obj_identifier
+                                       obj_block_alloc '{'     limit_block     '}'
+                                       stmt_separator
+                       {
+                               $4->location = @3;
+                               $4->type = NFT_OBJECT_LIMIT;
+                               handle_merge(&$4->handle, &$3);
+                               handle_free(&$3);
+                               list_add_tail(&$4->list, &$1->objs);
+                               $$ = $1;
+                       }
                        ;
 
 chain_block_alloc      :       /* empty */
@@ -1525,6 +1564,15 @@ ct_block         :       /* empty */     { $$ = $<obj>-1; }
                        }
                        ;
 
+limit_block            :       /* empty */     { $$ = $<obj>-1; }
+                       |       limit_block     common_block
+                       |       limit_block     stmt_separator
+                       |       limit_block     limit_config
+                       {
+                               $1->limit = *$2;
+                               $$ = $1;
+                       }
+                       ;
 
 type_identifier                :       STRING  { $$ = $1; }
                        |       MARK    { $$ = xstrdup("mark"); }
@@ -2004,6 +2052,12 @@ limit_stmt               :       LIMIT   RATE    limit_mode      NUM     SLASH   time_unit       limit_burst
                                $$->limit.type  = NFT_LIMIT_PKT_BYTES;
                                $$->limit.flags = $3;
                        }
+                       |       LIMIT   NAME    stmt_expr
+                       {
+                               $$ = objref_stmt_alloc(&@$);
+                               $$->objref.type = NFT_OBJECT_LIMIT;
+                               $$->objref.expr = $3;
+                       }
                        ;
 
 quota_mode             :       OVER            { $$ = NFT_QUOTA_F_INV; }
@@ -2768,6 +2822,47 @@ ct_obj_alloc             :
                        }
                        ;
 
+limit_config           :       RATE    limit_mode      NUM     SLASH   time_unit       limit_burst
+                       {
+                               struct limit *limit;
+                               limit = xzalloc(sizeof(*limit));
+                               limit->rate     = $3;
+                               limit->unit     = $5;
+                               limit->burst    = $6;
+                               limit->type     = NFT_LIMIT_PKTS;
+                               limit->flags    = $2;
+                               $$ = limit;
+                       }
+                       |       RATE    limit_mode      NUM     STRING  limit_burst
+                       {
+                               struct limit *limit;
+                               struct error_record *erec;
+                               uint64_t rate, unit;
+
+                               erec = rate_parse(&@$, $4, &rate, &unit);
+                               if (erec != NULL) {
+                                       erec_queue(erec, state->msgs);
+                                       YYERROR;
+                               }
+
+                               limit = xzalloc(sizeof(*limit));
+                               limit->rate     = rate * $3;
+                               limit->unit     = unit;
+                               limit->burst    = $5;
+                               limit->type     = NFT_LIMIT_PKT_BYTES;
+                               limit->flags    = $2;
+                               $$ = limit;
+                       }
+                       ;
+
+limit_obj              :       limit_config
+                       {
+                               $$ = obj_alloc(&@$);
+                               $$->type = NFT_OBJECT_LIMIT;
+                               $$->limit = *$1;
+                       }
+                       ;
+
 relational_expr                :       expr    /* implicit */  rhs_expr
                        {
                                $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2);
index 44d36c16ea47f5d19483de65c7fbdf137fc26d1d..1bb7b4756171c30e2941595e4cbf3412a6d7a158 100644 (file)
@@ -962,6 +962,7 @@ void cmd_free(struct cmd *cmd)
                case CMD_OBJ_COUNTER:
                case CMD_OBJ_QUOTA:
                case CMD_OBJ_CT_HELPER:
+               case CMD_OBJ_LIMIT:
                        obj_free(cmd->object);
                        break;
                default:
@@ -1050,6 +1051,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl)
        case CMD_OBJ_COUNTER:
        case CMD_OBJ_QUOTA:
        case CMD_OBJ_CT_HELPER:
+       case CMD_OBJ_LIMIT:
                return netlink_add_obj(ctx, &cmd->handle, cmd->object, flags);
        default:
                BUG("invalid command object type %u\n", cmd->obj);
@@ -1136,6 +1138,9 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_CT_HELPER:
                return netlink_delete_obj(ctx, &cmd->handle, &cmd->location,
                                          NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_LIMIT:
+               return netlink_delete_obj(ctx, &cmd->handle, &cmd->location,
+                                         NFT_OBJECT_LIMIT);
        default:
                BUG("invalid command object type %u\n", cmd->obj);
        }
@@ -1296,6 +1301,37 @@ static void obj_print_data(const struct obj *obj,
                printf("\t\tl3proto %s", family2str(obj->ct_helper.l3proto));
                break;
                }
+       case NFT_OBJECT_LIMIT: {
+               bool inv = obj->limit.flags & NFT_LIMIT_F_INV;
+               const char *data_unit;
+               uint64_t rate;
+
+               printf(" %s {%s%s%s", obj->handle.obj,
+                                     opts->nl, opts->tab, opts->tab);
+               switch (obj->limit.type) {
+               case NFT_LIMIT_PKTS:
+                       printf("limit rate %s%" PRIu64 "/%s",
+                              inv ? "over " : "", obj->limit.rate,
+                              get_unit(obj->limit.unit));
+                       if (obj->limit.burst > 0)
+                               printf(" burst %u packets", obj->limit.burst);
+                       break;
+               case NFT_LIMIT_PKT_BYTES:
+                       data_unit = get_rate(obj->limit.rate, &rate);
+
+                       printf("limit rate %s%" PRIu64 " %s/%s",
+                              inv ? "over " : "", rate, data_unit,
+                              get_unit(obj->limit.unit));
+                       if (obj->limit.burst > 0) {
+                               uint64_t burst;
+
+                               data_unit = get_rate(obj->limit.burst, &burst);
+                               printf(" burst %"PRIu64" %s", burst, data_unit);
+                       }
+                       break;
+               }
+               }
+               break;
        default:
                printf("unknown {%s", opts->nl);
                break;
@@ -1306,11 +1342,12 @@ static const char *obj_type_name_array[] = {
        [NFT_OBJECT_COUNTER]    = "counter",
        [NFT_OBJECT_QUOTA]      = "quota",
        [NFT_OBJECT_CT_HELPER]  = "",
+       [NFT_OBJECT_LIMIT]      = "limit",
 };
 
 const char *obj_type_name(enum stmt_types type)
 {
-       assert(type <= NFT_OBJECT_CT_HELPER && obj_type_name_array[type]);
+       assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]);
 
        return obj_type_name_array[type];
 }
@@ -1319,6 +1356,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = {
        [NFT_OBJECT_COUNTER]    = CMD_OBJ_COUNTER,
        [NFT_OBJECT_QUOTA]      = CMD_OBJ_QUOTA,
        [NFT_OBJECT_CT_HELPER]  = CMD_OBJ_CT_HELPER,
+       [NFT_OBJECT_LIMIT]      = CMD_OBJ_LIMIT,
 };
 
 uint32_t obj_type_to_cmd(uint32_t type)
@@ -1550,6 +1588,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
        case CMD_OBJ_CT_HELPER:
        case CMD_OBJ_CT_HELPERS:
                return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER);
+       case CMD_OBJ_LIMIT:
+       case CMD_OBJ_LIMITS:
+               return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT);
        default:
                BUG("invalid command object type %u\n", cmd->obj);
        }
index f0021c5c0fbae510eb0f7a2ea7ed40b8ddabc52e..0cfb6c50e4185774cf405698128c63aa156e9374 100644 (file)
@@ -300,6 +300,7 @@ addrstring  ({macaddr}|{ip4addr}|{ip6addr})
 
 "counters"             { return COUNTERS; }
 "quotas"               { return QUOTAS; }
+"limits"               { return LIMITS; }
 
 "log"                  { return LOG; }
 "prefix"               { return PREFIX; }
index 58f8aaf696c5a3c5e98407a9cb410bb330f9b07e..0b2c28bc374af86118244734273f2eda1902c03d 100644 (file)
@@ -175,6 +175,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = {
        [NFT_OBJECT_COUNTER]    = "counter",
        [NFT_OBJECT_QUOTA]      = "quota",
        [NFT_OBJECT_CT_HELPER]  = "cthelper",
+       [NFT_OBJECT_LIMIT]      = "limit",
 };
 
 static const char *objref_type_name(uint32_t type)
@@ -286,7 +287,7 @@ struct stmt *log_stmt_alloc(const struct location *loc)
        return stmt_alloc(loc, &log_stmt_ops);
 }
 
-static const char *get_unit(uint64_t u)
+const char *get_unit(uint64_t u)
 {
        switch (u) {
        case 1: return "second";