[PATCH v4 2/2] libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps rates

Subsystems: netfilter, the rest

STALE3667d

2 messages, 2 authors, 2016-10-04 · open the first message on its own page

[PATCH v4 2/2] libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps rates

From: Vishwanath Pai <hidden>
Date: 2016-09-26 19:08:55

v2:
* Added two tests for 1000000pps support to libxt_hashlimit.t
* cfg_copy returns -EINVAL and all callers of cfg_copy will look
  for   the error value

v3:
Reorder mode, avg and burst in hashlimit_cfg2 to avoid padding

v4:
XT_HASHLIMIT_SCALE for rev2 is now XT_HASHLIMIT_SCALE_v2

--

libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps
rates

Create a new revision for the hashlimit iptables extension module. Rev 2
will support higher pps of upto 1 million, Version 1 supports only 10k.

To support this we have to increase the size of the variables avg and
burst in hashlimit_cfg to 64-bit. Create two new structs hashlimit_cfg2
and xt_hashlimit_mtinfo2 and also create newer versions of all the
functions for match, checkentry and destory.

Signed-off-by: Vishwanath Pai <redacted>
Signed-off-by: Joshua Hunt <redacted>
---
 extensions/libxt_hashlimit.c           | 457 ++++++++++++++++++++++++++-------
 extensions/libxt_hashlimit.t           |   2 +
 include/linux/netfilter/xt_hashlimit.h |  26 +-
 3 files changed, 394 insertions(+), 91 deletions(-)
diff --git a/extensions/libxt_hashlimit.c b/extensions/libxt_hashlimit.c
index 99d5e1c..8580179 100644
--- a/extensions/libxt_hashlimit.c
+++ b/extensions/libxt_hashlimit.c
@@ -18,12 +18,14 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#include <errno.h>
 #include <xtables.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_hashlimit.h>
 
 #define XT_HASHLIMIT_BURST	5
 #define XT_HASHLIMIT_BURST_MAX_v1	10000
+#define XT_HASHLIMIT_BURST_MAX		1000000
 
 #define XT_HASHLIMIT_BYTE_EXPIRE	15
 #define XT_HASHLIMIT_BYTE_EXPIRE_BURST	60
@@ -150,16 +152,70 @@ static const struct xt_option_entry hashlimit_mt_opts_v1[] = {
 };
 #undef s
 
-static uint32_t cost_to_bytes(uint32_t cost)
+#define s struct xt_hashlimit_mtinfo2
+static const struct xt_option_entry hashlimit_mt_opts[] = {
+	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
+	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
+	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
+	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
+	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
+	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
+	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.size)},
+	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.max)},
+	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.gc_interval)},
+	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
+	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, cfg.expire)},
+	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
+	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
+	XTOPT_TABLEEND,
+};
+#undef s
+
+static int
+cfg_copy(struct hashlimit_cfg2 *to, const void *from, int revision)
+{
+	if (revision == 1) {
+		struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from;
+
+		to->mode = cfg->mode;
+		to->avg = cfg->avg;
+		to->burst = cfg->burst;
+		to->size = cfg->size;
+		to->max = cfg->max;
+		to->gc_interval = cfg->gc_interval;
+		to->expire = cfg->expire;
+		to->srcmask = cfg->srcmask;
+		to->dstmask = cfg->dstmask;
+	} else if (revision == 2) {
+		memcpy(to, from, sizeof(struct hashlimit_cfg2));
+	} else {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static uint64_t cost_to_bytes(uint64_t cost)
 {
-	uint32_t r;
+	uint64_t r;
 
 	r = cost ? UINT32_MAX / cost : UINT32_MAX;
 	r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
 	return r;
 }
 
-static uint64_t bytes_to_cost(uint32_t bytes)
+static uint64_t bytes_to_cost(uint64_t bytes)
 {
 	uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
 	return UINT32_MAX / (r+1);
@@ -180,57 +236,78 @@ static void burst_error_v1(void)
 			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX_v1);
 }
 
-static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info)
+static void burst_error(void)
+{
+	xtables_error(PARAMETER_PROBLEM, "bad value for option "
+			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
+}
+
+static uint64_t parse_burst(const char *burst, int revision)
 {
 	uintmax_t v;
 	char *end;
-
-	if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) ||
-	    (*end == 0 && v > XT_HASHLIMIT_BURST_MAX_v1))
-		burst_error_v1();
+	uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
+	uint64_t burst_max = (revision == 1) ?
+			      XT_HASHLIMIT_BURST_MAX_v1 : XT_HASHLIMIT_BURST_MAX;
+
+	if (!xtables_strtoul(burst, &end, &v, 1, max) ||
+		(*end == 0 && v > burst_max)) {
+		if (revision == 1)
+			burst_error_v1();
+		else
+			burst_error();
+	}
 
 	v *= get_factor(*end);
-	if (v > UINT32_MAX)
+	if (v > max)
 		xtables_error(PARAMETER_PROBLEM, "bad value for option "
 			"\"--hashlimit-burst\", value \"%s\" too large "
-				"(max %umb).", burst, UINT32_MAX/1024/1024);
+				"(max %lumb).", burst, max/1024/1024);
 	return v;
 }
 
-static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+static bool parse_bytes(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
 {
 	unsigned int factor = 1;
-	uint64_t tmp;
-	int r;
+	uint64_t tmp, r;
 	const char *mode = strstr(rate, "b/s");
+	uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
+
 	if (!mode || mode == rate)
 		return false;
 
 	mode--;
-	r = atoi(rate);
+	r = atoll(rate);
 	if (r == 0)
 		return false;
 
 	factor = get_factor(*mode);
 	tmp = (uint64_t) r * factor;
-	if (tmp > UINT32_MAX)
+	if (tmp > max)
 		xtables_error(PARAMETER_PROBLEM,
-			"Rate value too large \"%llu\" (max %u)\n",
-					(unsigned long long)tmp, UINT32_MAX);
+			"Rate value too large \"%llu\" (max %lu)\n",
+					(unsigned long long)tmp, max);
 
-	*val = bytes_to_cost(tmp);
-	if (*val == 0)
+	tmp = bytes_to_cost(tmp);
+	if (tmp == 0)
 		xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate);
 
 	ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
+
+	if(revision == 1)
+		*((uint32_t*)val) = tmp;
+	else
+		*((uint64_t*)val) = tmp;
+
 	return true;
 }
 
 static
-int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
+int parse_rate(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
 {
 	const char *delim;
-	uint32_t r;
+	uint64_t tmp, r;
+	uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
 
 	ud->mult = 1;  /* Seconds by default. */
 	delim = strchr(rate, '/');
@@ -249,17 +326,23 @@ int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud)
 		else
 			return 0;
 	}
-	r = atoi(rate);
+	r = atoll(rate);
 	if (!r)
 		return 0;
 
-	*val = XT_HASHLIMIT_SCALE * ud->mult / r;
-	if (*val == 0)
+	tmp = scale * ud->mult / r;
+	if (tmp == 0)
 		/*
 		 * The rate maps to infinity. (1/day is the minimum they can
 		 * specify, so we are ok at that end).
 		 */
 		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
+
+	if(revision == 1)
+		*((uint32_t*)val) = tmp;
+	else
+		*((uint64_t*)val) = tmp;
+
 	return 1;
 }
 
@@ -294,6 +377,28 @@ static void hashlimit_mt6_init_v1(struct xt_entry_match *match)
 	info->cfg.dstmask     = 128;
 }
 
+static void hashlimit_mt4_init(struct xt_entry_match *match)
+{
+	struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
+
+	info->cfg.mode        = 0;
+	info->cfg.burst       = XT_HASHLIMIT_BURST;
+	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+	info->cfg.srcmask     = 32;
+	info->cfg.dstmask     = 32;
+}
+
+static void hashlimit_mt6_init(struct xt_entry_match *match)
+{
+	struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
+
+	info->cfg.mode        = 0;
+	info->cfg.burst       = XT_HASHLIMIT_BURST;
+	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
+	info->cfg.srcmask     = 128;
+	info->cfg.dstmask     = 128;
+}
+
 /* Parse a 'mode' parameter into the required bitmask */
 static int parse_mode(uint32_t *mode, const char *option_arg)
 {
@@ -330,7 +435,7 @@ static void hashlimit_parse(struct xt_option_call *cb)
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_UPTO:
-		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-upto", cb->arg);
 		break;
@@ -349,23 +454,64 @@ static void hashlimit_mt_parse_v1(struct xt_option_call *cb)
 	xtables_option_parse(cb);
 	switch (cb->entry->id) {
 	case O_BURST:
-		info->cfg.burst = parse_burst(cb->arg, info);
+		info->cfg.burst = parse_burst(cb->arg, 1);
 		break;
 	case O_UPTO:
 		if (cb->invert)
 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
-		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-upto", cb->arg);
 		break;
 	case O_ABOVE:
 		if (!cb->invert)
 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
-		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata))
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
-		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata))
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-above", cb->arg);
+		break;
+	case O_MODE:
+		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-mode", cb->arg);
+		break;
+	case O_SRCMASK:
+		info->cfg.srcmask = cb->val.hlen;
+		break;
+	case O_DSTMASK:
+		info->cfg.dstmask = cb->val.hlen;
+		break;
+	}
+}
+
+static void hashlimit_mt_parse(struct xt_option_call *cb)
+{
+	struct xt_hashlimit_mtinfo2 *info = cb->data;
+
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_BURST:
+		info->cfg.burst = parse_burst(cb->arg, 2);
+		break;
+	case O_UPTO:
+		if (cb->invert)
+			info->cfg.mode |= XT_HASHLIMIT_INVERT;
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
+			info->cfg.mode |= XT_HASHLIMIT_BYTES;
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
+			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
+			          "--hashlimit-upto", cb->arg);
+		break;
+	case O_ABOVE:
+		if (!cb->invert)
+			info->cfg.mode |= XT_HASHLIMIT_INVERT;
+		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
+			info->cfg.mode |= XT_HASHLIMIT_BYTES;
+		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
 			          "--hashlimit-above", cb->arg);
 		break;
@@ -411,7 +557,7 @@ static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb)
 		if (cb->xflags & F_BURST) {
 			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
 				xtables_error(PARAMETER_PROBLEM,
-					"burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg));
+					"burst cannot be smaller than %lub", cost_to_bytes(info->cfg.avg));
 
 			burst = info->cfg.burst;
 			burst /= cost_to_bytes(info->cfg.avg);
@@ -425,32 +571,69 @@ static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb)
 		burst_error_v1();
 }
 
-static const struct rates_v1
+static void hashlimit_mt_check(struct xt_fcheck_call *cb)
 {
+	const struct hashlimit_mt_udata *udata = cb->udata;
+	struct xt_hashlimit_mtinfo2 *info = cb->data;
+
+	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
+		xtables_error(PARAMETER_PROBLEM,
+				"You have to specify --hashlimit");
+	if (!(cb->xflags & F_HTABLE_EXPIRE))
+		info->cfg.expire = udata->mult * 1000; /* from s to msec */
+
+	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
+		uint32_t burst = 0;
+		if (cb->xflags & F_BURST) {
+			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
+				xtables_error(PARAMETER_PROBLEM,
+					"burst cannot be smaller than %lub", cost_to_bytes(info->cfg.avg));
+
+			burst = info->cfg.burst;
+			burst /= cost_to_bytes(info->cfg.avg);
+			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
+				burst++;
+			if (!(cb->xflags & F_HTABLE_EXPIRE))
+				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
+		}
+		info->cfg.burst = burst;
+	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
+		burst_error();
+}
+
+struct rates {
 	const char *name;
-	uint32_t mult;
+	uint64_t mult;
 } rates_v1[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
 		 { "hour", XT_HASHLIMIT_SCALE*60*60 },
 		 { "min", XT_HASHLIMIT_SCALE*60 },
 		 { "sec", XT_HASHLIMIT_SCALE } };
 
-static uint32_t print_rate(uint32_t period)
+static const struct rates rates[] = { 
+	{ "day", XT_HASHLIMIT_SCALE_v2*24*60*60 },
+	{ "hour", XT_HASHLIMIT_SCALE_v2*60*60 },
+	{ "min", XT_HASHLIMIT_SCALE_v2*60 },
+	{ "sec", XT_HASHLIMIT_SCALE_v2 } };
+
+static uint32_t print_rate(uint32_t period, int revision)
 {
 	unsigned int i;
+	const struct rates *_rates = (revision == 1) ? rates_v1 : rates;
+	uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
 
 	if (period == 0) {
 		printf(" %f", INFINITY);
 		return 0;
 	}
 
-	for (i = 1; i < ARRAY_SIZE(rates_v1); ++i)
-		if (period > rates_v1[i].mult
-            || rates_v1[i].mult/period < rates_v1[i].mult%period)
+	for (i = 1; i < ARRAY_SIZE(rates); ++i)
+		if (period > _rates[i].mult
+            || _rates[i].mult/period < _rates[i].mult%period)
 			break;
 
-	printf(" %u/%s", rates_v1[i-1].mult / period, rates_v1[i-1].name);
+	printf(" %lu/%s", _rates[i-1].mult / period, _rates[i-1].name);
 	/* return in msec */
-	return rates_v1[i-1].mult / XT_HASHLIMIT_SCALE * 1000;
+	return _rates[i-1].mult / scale * 1000;
 }
 
 static const struct {
@@ -462,7 +645,7 @@ static const struct {
 	{ "", 1 },
 };
 
-static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix)
+static uint32_t print_bytes(uint64_t avg, uint64_t burst, const char *prefix)
 {
 	unsigned int i;
 	unsigned long long r;
@@ -523,7 +706,7 @@ static void hashlimit_print(const void *ip,
 	uint32_t quantum;
 
 	fputs(" limit: avg", stdout);
-	quantum = print_rate(r->cfg.avg);
+	quantum = print_rate(r->cfg.avg, 1);
 	printf(" burst %u", r->cfg.burst);
 	fputs(" mode", stdout);
 	print_mode(r->cfg.mode, '-');
@@ -538,39 +721,39 @@ static void hashlimit_print(const void *ip,
 }
 
 static void
-hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
+hashlimit_mt_print(const struct hashlimit_cfg2 *cfg, unsigned int dmask, int revision)
 {
 	uint32_t quantum;
 
-	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
+	if (cfg->mode & XT_HASHLIMIT_INVERT)
 		fputs(" limit: above", stdout);
 	else
 		fputs(" limit: up to", stdout);
 
-	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
-		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "");
+	if (cfg->mode & XT_HASHLIMIT_BYTES) {
+		quantum = print_bytes(cfg->avg, cfg->burst, "");
 	} else {
-		quantum = print_rate(info->cfg.avg);
-		printf(" burst %u", info->cfg.burst);
+		quantum = print_rate(cfg->avg, revision);
+		printf(" burst %llu", cfg->burst);
 	}
-	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
+	if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
 		fputs(" mode", stdout);
-		print_mode(info->cfg.mode, '-');
+		print_mode(cfg->mode, '-');
 	}
-	if (info->cfg.size != 0)
-		printf(" htable-size %u", info->cfg.size);
-	if (info->cfg.max != 0)
-		printf(" htable-max %u", info->cfg.max);
-	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
-		printf(" htable-gcinterval %u", info->cfg.gc_interval);
-	if (info->cfg.expire != quantum)
-		printf(" htable-expire %u", info->cfg.expire);
-
-	if (info->cfg.srcmask != dmask)
-		printf(" srcmask %u", info->cfg.srcmask);
-	if (info->cfg.dstmask != dmask)
-		printf(" dstmask %u", info->cfg.dstmask);
+	if (cfg->size != 0)
+		printf(" htable-size %u", cfg->size);
+	if (cfg->max != 0)
+		printf(" htable-max %u", cfg->max);
+	if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
+		printf(" htable-gcinterval %u", cfg->gc_interval);
+	if (cfg->expire != quantum)
+		printf(" htable-expire %u", cfg->expire);
+
+	if (cfg->srcmask != dmask)
+		printf(" srcmask %u", cfg->srcmask);
+	if (cfg->dstmask != dmask)
+		printf(" dstmask %u", cfg->dstmask);
 }
 
 static void
@@ -578,8 +761,15 @@ hashlimit_mt4_print_v1(const void *ip, const struct xt_entry_match *match,
                    int numeric)
 {
 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+	struct hashlimit_cfg2 cfg;
+	int ret;
+
+	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
 
-	hashlimit_mt_print(info, 32);
+	if (ret)
+		xtables_error(OTHER_PROBLEM, "unknown revision");
+
+	hashlimit_mt_print(&cfg, 32, 1);
 }
 
 static void
@@ -587,8 +777,33 @@ hashlimit_mt6_print_v1(const void *ip, const struct xt_entry_match *match,
                    int numeric)
 {
 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+	struct hashlimit_cfg2 cfg;
+	int ret;
+
+	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
 
-	hashlimit_mt_print(info, 128);
+	if (ret)
+		xtables_error(OTHER_PROBLEM, "unknown revision");
+
+	hashlimit_mt_print(&cfg, 128, 1);
+}
+
+static void
+hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
+                   int numeric)
+{
+	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
+
+	hashlimit_mt_print(&info->cfg, 32, 2);
+}
+
+static void
+hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
+                   int numeric)
+{
+	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
+
+	hashlimit_mt_print(&info->cfg, 128, 2);
 }
 
 static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
@@ -597,7 +812,7 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
 	uint32_t quantum;
 
 	fputs(" --hashlimit", stdout);
-	quantum = print_rate(r->cfg.avg);
+	quantum = print_rate(r->cfg.avg, 1);
 	printf(" --hashlimit-burst %u", r->cfg.burst);
 
 	fputs(" --hashlimit-mode", stdout);
@@ -616,59 +831,89 @@ static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
 }
 
 static void
-hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
+hashlimit_mt_save(const struct hashlimit_cfg2 *cfg, const char* name, unsigned int dmask, int revision)
 {
 	uint32_t quantum;
 
-	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
+	if (cfg->mode & XT_HASHLIMIT_INVERT)
 		fputs(" --hashlimit-above", stdout);
 	else
 		fputs(" --hashlimit-upto", stdout);
 
-	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
-		quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-");
+	if (cfg->mode & XT_HASHLIMIT_BYTES) {
+		quantum = print_bytes(cfg->avg, cfg->burst, "--hashlimit-");
 	} else {
-		quantum = print_rate(info->cfg.avg);
-		printf(" --hashlimit-burst %u", info->cfg.burst);
+		quantum = print_rate(cfg->avg, revision);
+		printf(" --hashlimit-burst %llu", cfg->burst);
 	}
 
-	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
+	if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
 		fputs(" --hashlimit-mode", stdout);
-		print_mode(info->cfg.mode, ',');
+		print_mode(cfg->mode, ',');
 	}
 
-	printf(" --hashlimit-name %s", info->name);
-
-	if (info->cfg.size != 0)
-		printf(" --hashlimit-htable-size %u", info->cfg.size);
-	if (info->cfg.max != 0)
-		printf(" --hashlimit-htable-max %u", info->cfg.max);
-	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
-		printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
-	if (info->cfg.expire != quantum)
-		printf(" --hashlimit-htable-expire %u", info->cfg.expire);
-
-	if (info->cfg.srcmask != dmask)
-		printf(" --hashlimit-srcmask %u", info->cfg.srcmask);
-	if (info->cfg.dstmask != dmask)
-		printf(" --hashlimit-dstmask %u", info->cfg.dstmask);
+	printf(" --hashlimit-name %s", name);
+
+	if (cfg->size != 0)
+		printf(" --hashlimit-htable-size %u", cfg->size);
+	if (cfg->max != 0)
+		printf(" --hashlimit-htable-max %u", cfg->max);
+	if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
+		printf(" --hashlimit-htable-gcinterval %u", cfg->gc_interval);
+	if (cfg->expire != quantum)
+		printf(" --hashlimit-htable-expire %u", cfg->expire);
+
+	if (cfg->srcmask != dmask)
+		printf(" --hashlimit-srcmask %u", cfg->srcmask);
+	if (cfg->dstmask != dmask)
+		printf(" --hashlimit-dstmask %u", cfg->dstmask);
 }
 
 static void
 hashlimit_mt4_save_v1(const void *ip, const struct xt_entry_match *match)
 {
 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+	struct hashlimit_cfg2 cfg;
+	int ret;
+
+	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
 
-	hashlimit_mt_save(info, 32);
+	if (ret)
+		xtables_error(OTHER_PROBLEM, "unknown revision");
+
+	hashlimit_mt_save(&cfg, info->name, 32, 1);
 }
 
 static void
 hashlimit_mt6_save_v1(const void *ip, const struct xt_entry_match *match)
 {
 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
+	struct hashlimit_cfg2 cfg;
+	int ret;
+
+	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
+
+	if (ret)
+		xtables_error(OTHER_PROBLEM, "unknown revision");
+
+	hashlimit_mt_save(&cfg, info->name, 128, 1);
+}
 
-	hashlimit_mt_save(info, 128);
+static void
+hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
+
+	hashlimit_mt_save(&info->cfg, info->name, 32, 2);
+}
+
+static void
+hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
+
+	hashlimit_mt_save(&info->cfg, info->name, 128, 2);
 }
 
 static struct xtables_match hashlimit_mt_reg[] = {
@@ -720,6 +965,38 @@ static struct xtables_match hashlimit_mt_reg[] = {
 		.x6_options    = hashlimit_mt_opts_v1,
 		.udata_size    = sizeof(struct hashlimit_mt_udata),
 	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "hashlimit",
+		.revision      = 2,
+		.family        = NFPROTO_IPV4,
+		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
+		.userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
+		.help          = hashlimit_mt_help,
+		.init          = hashlimit_mt4_init,
+		.x6_parse      = hashlimit_mt_parse,
+		.x6_fcheck     = hashlimit_mt_check,
+		.print         = hashlimit_mt4_print,
+		.save          = hashlimit_mt4_save,
+		.x6_options    = hashlimit_mt_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
+	},
+	{
+		.version       = XTABLES_VERSION,
+		.name          = "hashlimit",
+		.revision      = 2,
+		.family        = NFPROTO_IPV6,
+		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
+		.userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
+		.help          = hashlimit_mt_help,
+		.init          = hashlimit_mt6_init,
+		.x6_parse      = hashlimit_mt_parse,
+		.x6_fcheck     = hashlimit_mt_check,
+		.print         = hashlimit_mt6_print,
+		.save          = hashlimit_mt6_save,
+		.x6_options    = hashlimit_mt_opts,
+		.udata_size    = sizeof(struct hashlimit_mt_udata),
+	},
 };
 
 void _init(void)
diff --git a/extensions/libxt_hashlimit.t b/extensions/libxt_hashlimit.t
index 59d6613..d27c861 100644
--- a/extensions/libxt_hashlimit.t
+++ b/extensions/libxt_hashlimit.t
@@ -1,10 +1,12 @@
 :INPUT,FORWARD,OUTPUT
 -m hashlimit --hashlimit-above 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-above 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 -m hashlimit --hashlimit-above 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 -m hashlimit --hashlimit-above 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 # kernel says "xt_hashlimit: overflow, try lower: 864000000/5"
 -m hashlimit --hashlimit-above 1/day --hashlimit-burst 5 --hashlimit-name mini1;;FAIL
 -m hashlimit --hashlimit-upto 1/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
+-m hashlimit --hashlimit-upto 1000000/sec --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 -m hashlimit --hashlimit-upto 1/min --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 -m hashlimit --hashlimit-upto 1/hour --hashlimit-burst 5 --hashlimit-name mini1;=;OK
 # kernel says "xt_hashlimit: overflow, try lower: 864000000/5"
diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h
index 141efbd..d9808b5 100644
--- a/include/linux/netfilter/xt_hashlimit.h
+++ b/include/linux/netfilter/xt_hashlimit.h
@@ -5,8 +5,10 @@
 
 /* timings are in milliseconds. */
 #define XT_HASHLIMIT_SCALE 10000
+#define XT_HASHLIMIT_SCALE_v2 1000000llu
 /* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
-   seconds, or one packet every 59 hours. */
+ * seconds, or one packet every 59 hours.
+ */
 
 /* packet length accounting is done in 16-byte steps */
 #define XT_HASHLIMIT_BYTE_SHIFT 4
@@ -61,6 +63,20 @@ struct hashlimit_cfg1 {
 	__u8 srcmask, dstmask;
 };
 
+struct hashlimit_cfg2 {
+	__u64 avg;		/* Average secs between packets * scale */
+	__u64 burst;		/* Period multiplier for upper limit. */
+	__u32 mode;		/* bitmask of XT_HASHLIMIT_HASH_* */
+
+	/* user specified */
+	__u32 size;		/* how many buckets */
+	__u32 max;		/* max number of entries */
+	__u32 gc_interval;	/* gc interval */
+	__u32 expire;		/* when do entries expire? */
+
+	__u8 srcmask, dstmask;
+};
+
 struct xt_hashlimit_mtinfo1 {
 	char name[IFNAMSIZ];
 	struct hashlimit_cfg1 cfg;
@@ -69,4 +85,12 @@ struct xt_hashlimit_mtinfo1 {
 	struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
 };
 
+struct xt_hashlimit_mtinfo2 {
+	char name[NAME_MAX];
+	struct hashlimit_cfg2 cfg;
+
+	/* Used internally by the kernel */
+	struct xt_hashlimit_htable *hinfo __attribute__((aligned(8)));
+};
+
 #endif /*_XT_HASHLIMIT_H*/
-- 
1.9.1

Re: [PATCH v4 2/2] libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps rates

From: Pablo Neira Ayuso <pablo@netfilter.org>
Date: 2016-10-04 06:57:57

On Mon, Sep 26, 2016 at 03:08:52PM -0400, Vishwanath Pai wrote:
libxt_hashlimit: Create revision 2 of xt_hashlimit to support higher pps
rates

Create a new revision for the hashlimit iptables extension module. Rev 2
will support higher pps of upto 1 million, Version 1 supports only 10k.

To support this we have to increase the size of the variables avg and
burst in hashlimit_cfg to 64-bit. Create two new structs hashlimit_cfg2
and xt_hashlimit_mtinfo2 and also create newer versions of all the
functions for match, checkentry and destory.
Also applied, thanks.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help