]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add per-bytes limit
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 3 Aug 2015 13:50:03 +0000 (15:50 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 23 Sep 2015 10:16:13 +0000 (12:16 +0200)
This example show how to accept packets below the ratelimit:

... limit rate 1024 mbytes/second counter accept

You need a Linux kernel >= 4.3-rc1.

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

index 2a6a4fca10ebebf66636d75290af905074ceb23b..ebafa6550259c4ed3d767b1fbf99e8f2980c1267 100644 (file)
@@ -235,4 +235,8 @@ extern void time_print(uint64_t seconds);
 extern struct error_record *time_parse(const struct location *loc,
                                       const char *c, uint64_t *res);
 
+extern struct error_record *rate_parse(const struct location *loc,
+                                      const char *str, uint64_t *rate,
+                                      uint64_t *unit);
+
 #endif /* NFTABLES_DATATYPE_H */
index 33056dc21ff9fa2f5ba0322732873d74483f0100..db0457d92cbd258f376754c16c6d6e64d05065a4 100644 (file)
@@ -747,16 +747,25 @@ enum nft_ct_attributes {
 };
 #define NFTA_CT_MAX            (__NFTA_CT_MAX - 1)
 
+enum nft_limit_type {
+       NFT_LIMIT_PKTS,
+       NFT_LIMIT_PKT_BYTES
+};
+
 /**
  * enum nft_limit_attributes - nf_tables limit expression netlink attributes
  *
  * @NFTA_LIMIT_RATE: refill rate (NLA_U64)
  * @NFTA_LIMIT_UNIT: refill unit (NLA_U64)
+ * @NFTA_LIMIT_BURST: burst (NLA_U32)
+ * @NFTA_LIMIT_TYPE: type of limit (NLA_U32: enum nft_limit_type)
  */
 enum nft_limit_attributes {
        NFTA_LIMIT_UNSPEC,
        NFTA_LIMIT_RATE,
        NFTA_LIMIT_UNIT,
+       NFTA_LIMIT_BURST,
+       NFTA_LIMIT_TYPE,
        __NFTA_LIMIT_MAX
 };
 #define NFTA_LIMIT_MAX         (__NFTA_LIMIT_MAX - 1)
index 48e61307bbb596f496ece8a9bdf769cbc4be75ea..d2d0852e3c118f9a13b3bd15246135d194e4db1c 100644 (file)
@@ -51,6 +51,7 @@ extern struct stmt *log_stmt_alloc(const struct location *loc);
 struct limit_stmt {
        uint64_t                rate;
        uint64_t                unit;
+       enum nft_limit_type     type;
 };
 
 extern struct stmt *limit_stmt_alloc(const struct location *loc);
index f79f5d2db30818667986b20502b0e394614d99ba..e5a486fb2d5e61a38c459778081eb2739eb5e1ff 100644 (file)
@@ -976,3 +976,58 @@ void concat_type_destroy(const struct datatype *dtype)
                xfree(dtype);
        }
 }
+
+static struct error_record *time_unit_parse(const struct location *loc,
+                                           const char *str, uint64_t *unit)
+{
+       if (strcmp(str, "second") == 0)
+               *unit = 1ULL;
+       else if (strcmp(str, "minute") == 0)
+               *unit = 1ULL * 60;
+       else if (strcmp(str, "hour") == 0)
+               *unit = 1ULL * 60 * 60;
+       else if (strcmp(str, "day") == 0)
+               *unit = 1ULL * 60 * 60 * 24;
+       else if (strcmp(str, "week") == 0)
+               *unit = 1ULL * 60 * 60 * 24 * 7;
+       else
+               return error(loc, "Wrong rate format");
+
+       return NULL;
+}
+
+static struct error_record *data_unit_parse(const struct location *loc,
+                                           const char *str, uint64_t *rate)
+{
+       if (strncmp(str, "bytes", strlen("bytes")) == 0)
+               *rate = 1ULL;
+       else if (strncmp(str, "kbytes", strlen("kbytes")) == 0)
+               *rate = 1024;
+       else if (strncmp(str, "mbytes", strlen("mbytes")) == 0)
+               *rate = 1024 * 1024;
+       else
+               return error(loc, "Wrong rate format");
+
+       return NULL;
+}
+
+struct error_record *rate_parse(const struct location *loc, const char *str,
+                               uint64_t *rate, uint64_t *unit)
+{
+       struct error_record *erec;
+       const char *slash;
+
+       slash = strchr(str, '/');
+       if (!slash)
+               return error(loc, "wrong rate format");
+
+       erec = data_unit_parse(loc, str, rate);
+       if (erec != NULL)
+               return erec;
+
+       erec = time_unit_parse(loc, slash + 1, unit);
+       if (erec != NULL)
+               return erec;
+
+       return NULL;
+}
index dc6338c22798bdbd92ecd3ab5ffbedfc3a5dbf76..4c639a16b1cb57ccd5cd190631ba4c0fe0444797 100644 (file)
@@ -583,6 +583,7 @@ static void netlink_parse_limit(struct netlink_parse_ctx *ctx,
        stmt = limit_stmt_alloc(loc);
        stmt->limit.rate = nftnl_expr_get_u64(nle, NFTNL_EXPR_LIMIT_RATE);
        stmt->limit.unit = nftnl_expr_get_u64(nle, NFTNL_EXPR_LIMIT_UNIT);
+       stmt->limit.type = nftnl_expr_get_u32(nle, NFTNL_EXPR_LIMIT_TYPE);
        list_add_tail(&stmt->list, &ctx->rule->stmts);
 }
 
index b2cb98dd5a6db7fde98c606ad906e51332b99e52..47092d33c0706b9d08e5ef5ea299267791c8782c 100644 (file)
@@ -708,6 +708,7 @@ static void netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
        nle = alloc_nft_expr("limit");
        nftnl_expr_set_u64(nle, NFTNL_EXPR_LIMIT_RATE, stmt->limit.rate);
        nftnl_expr_set_u64(nle, NFTNL_EXPR_LIMIT_UNIT, stmt->limit.unit);
+       nftnl_expr_set_u32(nle, NFTNL_EXPR_LIMIT_TYPE, stmt->limit.type);
        nftnl_rule_add_expr(ctx->nlr, nle);
 }
 
index cfb6b7070b98414625ea409a56947289bfb084b1..ec44a2cdd3f25fa18ec9871afbe0dd7b8b6d9e64 100644 (file)
@@ -1446,6 +1446,23 @@ limit_stmt               :       LIMIT   RATE    NUM     SLASH   time_unit
                                $$ = limit_stmt_alloc(&@$);
                                $$->limit.rate  = $3;
                                $$->limit.unit  = $5;
+                               $$->limit.type  = NFT_LIMIT_PKTS;
+                       }
+                       |       LIMIT RATE      NUM     STRING
+                       {
+                               struct error_record *erec;
+                               uint64_t rate, unit;
+
+                               erec = rate_parse(&@$, $4, &rate, &unit);
+                               if (erec != NULL) {
+                                       erec_queue(erec, state->msgs);
+                                       YYERROR;
+                               }
+
+                               $$ = limit_stmt_alloc(&@$);
+                               $$->limit.rate  = rate * $3;
+                               $$->limit.unit  = unit;
+                               $$->limit.type  = NFT_LIMIT_PKT_BYTES;
                        }
                        ;
 
index 9ebc5938fd65540983023d73ccbac5b875362f2f..ba7b8be2b2db7bf945973c104d17ac97d33a2aac 100644 (file)
@@ -185,10 +185,49 @@ static const char *get_unit(uint64_t u)
        return "error";
 }
 
+static const char *data_unit[] = {
+       "bytes",
+       "kbytes",
+       "mbytes",
+       NULL
+};
+
+static const char *get_rate(uint64_t byte_rate, uint64_t *rate)
+{
+       uint64_t res, prev, rest;
+       int i;
+
+       res = prev = byte_rate;
+       for (i = 0;; i++) {
+               rest = res % 1024;
+               res /= 1024;
+               if (res <= 1 && rest != 0)
+                       break;
+               if (data_unit[i + 1] == NULL)
+                       break;
+               prev = res;
+       }
+       *rate = prev;
+       return data_unit[i];
+}
+
 static void limit_stmt_print(const struct stmt *stmt)
 {
-       printf("limit rate %" PRIu64 "/%s",
-              stmt->limit.rate, get_unit(stmt->limit.unit));
+       const char *data_unit;
+       uint64_t rate;
+
+       switch (stmt->limit.type) {
+       case NFT_LIMIT_PKTS:
+               printf("limit rate %" PRIu64 "/%s",
+                      stmt->limit.rate, get_unit(stmt->limit.unit));
+               break;
+       case NFT_LIMIT_PKT_BYTES:
+               data_unit = get_rate(stmt->limit.rate, &rate);
+
+               printf("limit rate %" PRIu64 " %s/%s",
+                      rate, data_unit, get_unit(stmt->limit.unit));
+               break;
+       }
 }
 
 static const struct stmt_ops limit_stmt_ops = {