Home | History | Annotate | Download | only in extensions
      1 /* ip6tables match extension for limiting packets per destination
      2  *
      3  * (C) 2003-2004 by Harald Welte <laforge (at) netfilter.org>
      4  *
      5  * Development of this code was funded by Astaro AG, http://www.astaro.com/
      6  *
      7  * Based on ipt_limit.c by
      8  * Jrme de Vivie   <devivie (at) info.enserb.u-bordeaux.fr>
      9  * Herv Eychenne    <rv (at) wallfire.org>
     10  *
     11  * Error corections by nmalykh (at) bilim.com (22.01.2005)
     12  */
     13 #include <stdbool.h>
     14 #include <stdint.h>
     15 #include <stdio.h>
     16 #include <string.h>
     17 #include <stdlib.h>
     18 #include <xtables.h>
     19 #include <linux/netfilter/x_tables.h>
     20 #include <linux/netfilter/xt_hashlimit.h>
     21 
     22 #define XT_HASHLIMIT_BURST	5
     23 
     24 /* miliseconds */
     25 #define XT_HASHLIMIT_GCINTERVAL	1000
     26 #define XT_HASHLIMIT_EXPIRE	10000
     27 
     28 static void hashlimit_help(void)
     29 {
     30 	printf(
     31 "hashlimit match options:\n"
     32 "--hashlimit <avg>		max average match rate\n"
     33 "                                [Packets per second unless followed by \n"
     34 "                                /sec /minute /hour /day postfixes]\n"
     35 "--hashlimit-mode <mode>		mode is a comma-separated list of\n"
     36 "					dstip,srcip,dstport,srcport\n"
     37 "--hashlimit-name <name>		name for /proc/net/ipt_hashlimit/\n"
     38 "[--hashlimit-burst <num>]	number to match in a burst, default %u\n"
     39 "[--hashlimit-htable-size <num>]	number of hashtable buckets\n"
     40 "[--hashlimit-htable-max <num>]	number of hashtable entries\n"
     41 "[--hashlimit-htable-gcinterval]	interval between garbage collection runs\n"
     42 "[--hashlimit-htable-expire]	after which time are idle entries expired?\n",
     43 XT_HASHLIMIT_BURST);
     44 }
     45 
     46 enum {
     47 	O_UPTO = 0,
     48 	O_ABOVE,
     49 	O_LIMIT,
     50 	O_MODE,
     51 	O_SRCMASK,
     52 	O_DSTMASK,
     53 	O_NAME,
     54 	O_BURST,
     55 	O_HTABLE_SIZE,
     56 	O_HTABLE_MAX,
     57 	O_HTABLE_GCINT,
     58 	O_HTABLE_EXPIRE,
     59 	F_UPTO  = 1 << O_UPTO,
     60 	F_ABOVE = 1 << O_ABOVE,
     61 };
     62 
     63 static void hashlimit_mt_help(void)
     64 {
     65 	printf(
     66 "hashlimit match options:\n"
     67 "  --hashlimit-upto <avg>           max average match rate\n"
     68 "                                   [Packets per second unless followed by \n"
     69 "                                   /sec /minute /hour /day postfixes]\n"
     70 "  --hashlimit-above <avg>          min average match rate\n"
     71 "  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
     72 "                                   dstip,srcip,dstport,srcport (or none)\n"
     73 "  --hashlimit-srcmask <length>     source address grouping prefix length\n"
     74 "  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
     75 "  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
     76 "  --hashlimit-burst <num>	    number to match in a burst, default %u\n"
     77 "  --hashlimit-htable-size <num>    number of hashtable buckets\n"
     78 "  --hashlimit-htable-max <num>     number of hashtable entries\n"
     79 "  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
     80 "  --hashlimit-htable-expire        after which time are idle entries expired?\n"
     81 "\n", XT_HASHLIMIT_BURST);
     82 }
     83 
     84 #define s struct xt_hashlimit_info
     85 static const struct xt_option_entry hashlimit_opts[] = {
     86 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
     87 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
     88 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
     89 	 .min = 1, .max = 10000, .flags = XTOPT_PUT,
     90 	 XTOPT_POINTER(s, cfg.burst)},
     91 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
     92 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
     93 	 XTOPT_POINTER(s, cfg.size)},
     94 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
     95 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
     96 	 XTOPT_POINTER(s, cfg.max)},
     97 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
     98 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
     99 	 XTOPT_POINTER(s, cfg.gc_interval)},
    100 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
    101 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
    102 	 XTOPT_POINTER(s, cfg.expire)},
    103 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING,
    104 	 .flags = XTOPT_MAND},
    105 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
    106 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
    107 	XTOPT_TABLEEND,
    108 };
    109 #undef s
    110 
    111 #define s struct xt_hashlimit_mtinfo1
    112 static const struct xt_option_entry hashlimit_mt_opts[] = {
    113 	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
    114 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
    115 	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
    116 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
    117 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
    118 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
    119 	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
    120 	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
    121 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
    122 	 .min = 1, .max = 10000, .flags = XTOPT_PUT,
    123 	 XTOPT_POINTER(s, cfg.burst)},
    124 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
    125 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
    126 	 XTOPT_POINTER(s, cfg.size)},
    127 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
    128 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
    129 	 XTOPT_POINTER(s, cfg.max)},
    130 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
    131 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
    132 	 XTOPT_POINTER(s, cfg.gc_interval)},
    133 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
    134 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
    135 	 XTOPT_POINTER(s, cfg.expire)},
    136 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
    137 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
    138 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
    139 	XTOPT_TABLEEND,
    140 };
    141 #undef s
    142 
    143 static
    144 int parse_rate(const char *rate, uint32_t *val)
    145 {
    146 	const char *delim;
    147 	uint32_t r;
    148 	uint32_t mult = 1;  /* Seconds by default. */
    149 
    150 	delim = strchr(rate, '/');
    151 	if (delim) {
    152 		if (strlen(delim+1) == 0)
    153 			return 0;
    154 
    155 		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
    156 			mult = 1;
    157 		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
    158 			mult = 60;
    159 		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
    160 			mult = 60*60;
    161 		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
    162 			mult = 24*60*60;
    163 		else
    164 			return 0;
    165 	}
    166 	r = atoi(rate);
    167 	if (!r)
    168 		return 0;
    169 
    170 	/* This would get mapped to infinite (1/day is minimum they
    171            can specify, so we're ok at that end). */
    172 	if (r / mult > XT_HASHLIMIT_SCALE)
    173 		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
    174 
    175 	*val = XT_HASHLIMIT_SCALE * mult / r;
    176 	return 1;
    177 }
    178 
    179 static void hashlimit_init(struct xt_entry_match *m)
    180 {
    181 	struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
    182 
    183 	r->cfg.burst = XT_HASHLIMIT_BURST;
    184 	r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
    185 	r->cfg.expire = XT_HASHLIMIT_EXPIRE;
    186 
    187 }
    188 
    189 static void hashlimit_mt4_init(struct xt_entry_match *match)
    190 {
    191 	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
    192 
    193 	info->cfg.mode        = 0;
    194 	info->cfg.burst       = XT_HASHLIMIT_BURST;
    195 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
    196 	info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
    197 	info->cfg.srcmask     = 32;
    198 	info->cfg.dstmask     = 32;
    199 }
    200 
    201 static void hashlimit_mt6_init(struct xt_entry_match *match)
    202 {
    203 	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
    204 
    205 	info->cfg.mode        = 0;
    206 	info->cfg.burst       = XT_HASHLIMIT_BURST;
    207 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
    208 	info->cfg.expire      = XT_HASHLIMIT_EXPIRE;
    209 	info->cfg.srcmask     = 128;
    210 	info->cfg.dstmask     = 128;
    211 }
    212 
    213 /* Parse a 'mode' parameter into the required bitmask */
    214 static int parse_mode(uint32_t *mode, const char *option_arg)
    215 {
    216 	char *tok;
    217 	char *arg = strdup(option_arg);
    218 
    219 	if (!arg)
    220 		return -1;
    221 
    222 	for (tok = strtok(arg, ",|");
    223 	     tok;
    224 	     tok = strtok(NULL, ",|")) {
    225 		if (!strcmp(tok, "dstip"))
    226 			*mode |= XT_HASHLIMIT_HASH_DIP;
    227 		else if (!strcmp(tok, "srcip"))
    228 			*mode |= XT_HASHLIMIT_HASH_SIP;
    229 		else if (!strcmp(tok, "srcport"))
    230 			*mode |= XT_HASHLIMIT_HASH_SPT;
    231 		else if (!strcmp(tok, "dstport"))
    232 			*mode |= XT_HASHLIMIT_HASH_DPT;
    233 		else {
    234 			free(arg);
    235 			return -1;
    236 		}
    237 	}
    238 	free(arg);
    239 	return 0;
    240 }
    241 
    242 static void hashlimit_parse(struct xt_option_call *cb)
    243 {
    244 	struct xt_hashlimit_info *info = cb->data;
    245 
    246 	xtables_option_parse(cb);
    247 	switch (cb->entry->id) {
    248 	case O_UPTO:
    249 		if (cb->invert)
    250 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
    251 		if (!parse_rate(cb->arg, &info->cfg.avg))
    252 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    253 			          "--hashlimit-upto", cb->arg);
    254 		break;
    255 	case O_ABOVE:
    256 		if (!cb->invert)
    257 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
    258 		if (!parse_rate(cb->arg, &info->cfg.avg))
    259 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    260 			          "--hashlimit-above", cb->arg);
    261 		break;
    262 	case O_MODE:
    263 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
    264 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    265 			          "--hashlimit-mode", cb->arg);
    266 		break;
    267 	}
    268 }
    269 
    270 static void hashlimit_mt_parse(struct xt_option_call *cb)
    271 {
    272 	struct xt_hashlimit_mtinfo1 *info = cb->data;
    273 
    274 	xtables_option_parse(cb);
    275 	switch (cb->entry->id) {
    276 	case O_UPTO:
    277 		if (cb->invert)
    278 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
    279 		if (!parse_rate(cb->arg, &info->cfg.avg))
    280 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    281 			          "--hashlimit-upto", cb->arg);
    282 		break;
    283 	case O_ABOVE:
    284 		if (!cb->invert)
    285 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
    286 		if (!parse_rate(cb->arg, &info->cfg.avg))
    287 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    288 			          "--hashlimit-above", cb->arg);
    289 		break;
    290 	case O_MODE:
    291 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
    292 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
    293 			          "--hashlimit-mode", cb->arg);
    294 		break;
    295 	case O_SRCMASK:
    296 		info->cfg.srcmask = cb->val.hlen;
    297 		break;
    298 	case O_DSTMASK:
    299 		info->cfg.dstmask = cb->val.hlen;
    300 		break;
    301 	}
    302 }
    303 
    304 static void hashlimit_check(struct xt_fcheck_call *cb)
    305 {
    306 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
    307 		xtables_error(PARAMETER_PROBLEM,
    308 				"You have to specify --hashlimit");
    309 }
    310 
    311 static const struct rates
    312 {
    313 	const char *name;
    314 	uint32_t mult;
    315 } rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
    316 	      { "hour", XT_HASHLIMIT_SCALE*60*60 },
    317 	      { "min", XT_HASHLIMIT_SCALE*60 },
    318 	      { "sec", XT_HASHLIMIT_SCALE } };
    319 
    320 static void print_rate(uint32_t period)
    321 {
    322 	unsigned int i;
    323 
    324 	for (i = 1; i < ARRAY_SIZE(rates); ++i)
    325 		if (period > rates[i].mult
    326             || rates[i].mult/period < rates[i].mult%period)
    327 			break;
    328 
    329 	printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name);
    330 }
    331 
    332 static void print_mode(unsigned int mode, char separator)
    333 {
    334 	bool prevmode = false;
    335 
    336 	putchar(' ');
    337 	if (mode & XT_HASHLIMIT_HASH_SIP) {
    338 		fputs("srcip", stdout);
    339 		prevmode = 1;
    340 	}
    341 	if (mode & XT_HASHLIMIT_HASH_SPT) {
    342 		if (prevmode)
    343 			putchar(separator);
    344 		fputs("srcport", stdout);
    345 		prevmode = 1;
    346 	}
    347 	if (mode & XT_HASHLIMIT_HASH_DIP) {
    348 		if (prevmode)
    349 			putchar(separator);
    350 		fputs("dstip", stdout);
    351 		prevmode = 1;
    352 	}
    353 	if (mode & XT_HASHLIMIT_HASH_DPT) {
    354 		if (prevmode)
    355 			putchar(separator);
    356 		fputs("dstport", stdout);
    357 	}
    358 }
    359 
    360 static void hashlimit_print(const void *ip,
    361                             const struct xt_entry_match *match, int numeric)
    362 {
    363 	const struct xt_hashlimit_info *r = (const void *)match->data;
    364 	fputs(" limit: avg", stdout); print_rate(r->cfg.avg);
    365 	printf(" burst %u", r->cfg.burst);
    366 	fputs(" mode", stdout);
    367 	print_mode(r->cfg.mode, '-');
    368 	if (r->cfg.size)
    369 		printf(" htable-size %u", r->cfg.size);
    370 	if (r->cfg.max)
    371 		printf(" htable-max %u", r->cfg.max);
    372 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
    373 		printf(" htable-gcinterval %u", r->cfg.gc_interval);
    374 	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
    375 		printf(" htable-expire %u", r->cfg.expire);
    376 }
    377 
    378 static void
    379 hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
    380 {
    381 	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
    382 		fputs(" limit: above", stdout);
    383 	else
    384 		fputs(" limit: up to", stdout);
    385 	print_rate(info->cfg.avg);
    386 	printf(" burst %u", info->cfg.burst);
    387 	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
    388 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
    389 		fputs(" mode", stdout);
    390 		print_mode(info->cfg.mode, '-');
    391 	}
    392 	if (info->cfg.size != 0)
    393 		printf(" htable-size %u", info->cfg.size);
    394 	if (info->cfg.max != 0)
    395 		printf(" htable-max %u", info->cfg.max);
    396 	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
    397 		printf(" htable-gcinterval %u", info->cfg.gc_interval);
    398 	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
    399 		printf(" htable-expire %u", info->cfg.expire);
    400 
    401 	if (info->cfg.srcmask != dmask)
    402 		printf(" srcmask %u", info->cfg.srcmask);
    403 	if (info->cfg.dstmask != dmask)
    404 		printf(" dstmask %u", info->cfg.dstmask);
    405 }
    406 
    407 static void
    408 hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
    409                    int numeric)
    410 {
    411 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
    412 
    413 	hashlimit_mt_print(info, 32);
    414 }
    415 
    416 static void
    417 hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
    418                    int numeric)
    419 {
    420 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
    421 
    422 	hashlimit_mt_print(info, 128);
    423 }
    424 
    425 static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
    426 {
    427 	const struct xt_hashlimit_info *r = (const void *)match->data;
    428 
    429 	fputs(" --hashlimit", stdout); print_rate(r->cfg.avg);
    430 	printf(" --hashlimit-burst %u", r->cfg.burst);
    431 
    432 	fputs(" --hashlimit-mode", stdout);
    433 	print_mode(r->cfg.mode, ',');
    434 
    435 	printf(" --hashlimit-name %s", r->name);
    436 
    437 	if (r->cfg.size)
    438 		printf(" --hashlimit-htable-size %u", r->cfg.size);
    439 	if (r->cfg.max)
    440 		printf(" --hashlimit-htable-max %u", r->cfg.max);
    441 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
    442 		printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
    443 	if (r->cfg.expire != XT_HASHLIMIT_EXPIRE)
    444 		printf(" --hashlimit-htable-expire %u", r->cfg.expire);
    445 }
    446 
    447 static void
    448 hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask)
    449 {
    450 	if (info->cfg.mode & XT_HASHLIMIT_INVERT)
    451 		fputs(" --hashlimit-above", stdout);
    452 	else
    453 		fputs(" --hashlimit-upto", stdout);
    454 	print_rate(info->cfg.avg);
    455 	printf(" --hashlimit-burst %u", info->cfg.burst);
    456 
    457 	if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
    458 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
    459 		fputs(" --hashlimit-mode", stdout);
    460 		print_mode(info->cfg.mode, ',');
    461 	}
    462 
    463 	printf(" --hashlimit-name %s", info->name);
    464 
    465 	if (info->cfg.size != 0)
    466 		printf(" --hashlimit-htable-size %u", info->cfg.size);
    467 	if (info->cfg.max != 0)
    468 		printf(" --hashlimit-htable-max %u", info->cfg.max);
    469 	if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
    470 		printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval);
    471 	if (info->cfg.expire != XT_HASHLIMIT_EXPIRE)
    472 		printf(" --hashlimit-htable-expire %u", info->cfg.expire);
    473 
    474 	if (info->cfg.srcmask != dmask)
    475 		printf(" --hashlimit-srcmask %u", info->cfg.srcmask);
    476 	if (info->cfg.dstmask != dmask)
    477 		printf(" --hashlimit-dstmask %u", info->cfg.dstmask);
    478 }
    479 
    480 static void
    481 hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
    482 {
    483 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
    484 
    485 	hashlimit_mt_save(info, 32);
    486 }
    487 
    488 static void
    489 hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
    490 {
    491 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
    492 
    493 	hashlimit_mt_save(info, 128);
    494 }
    495 
    496 static struct xtables_match hashlimit_mt_reg[] = {
    497 	{
    498 		.family        = NFPROTO_UNSPEC,
    499 		.name          = "hashlimit",
    500 		.version       = XTABLES_VERSION,
    501 		.revision      = 0,
    502 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
    503 		.userspacesize = offsetof(struct xt_hashlimit_info, hinfo),
    504 		.help          = hashlimit_help,
    505 		.init          = hashlimit_init,
    506 		.x6_parse      = hashlimit_parse,
    507 		.x6_fcheck     = hashlimit_check,
    508 		.print         = hashlimit_print,
    509 		.save          = hashlimit_save,
    510 		.x6_options    = hashlimit_mt_opts,
    511 	},
    512 	{
    513 		.version       = XTABLES_VERSION,
    514 		.name          = "hashlimit",
    515 		.revision      = 1,
    516 		.family        = NFPROTO_IPV4,
    517 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
    518 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
    519 		.help          = hashlimit_mt_help,
    520 		.init          = hashlimit_mt4_init,
    521 		.x6_parse      = hashlimit_mt_parse,
    522 		.x6_fcheck     = hashlimit_check,
    523 		.print         = hashlimit_mt4_print,
    524 		.save          = hashlimit_mt4_save,
    525 		.x6_options    = hashlimit_mt_opts,
    526 	},
    527 	{
    528 		.version       = XTABLES_VERSION,
    529 		.name          = "hashlimit",
    530 		.revision      = 1,
    531 		.family        = NFPROTO_IPV6,
    532 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
    533 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
    534 		.help          = hashlimit_mt_help,
    535 		.init          = hashlimit_mt6_init,
    536 		.x6_parse      = hashlimit_mt_parse,
    537 		.x6_fcheck     = hashlimit_check,
    538 		.print         = hashlimit_mt6_print,
    539 		.save          = hashlimit_mt6_save,
    540 		.x6_options    = hashlimit_mt_opts,
    541 	},
    542 };
    543 
    544 void _init(void)
    545 {
    546 	xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
    547 }
    548