libxt_hashlimit.c revision 9d69da4bdb1d546218d168b72f12ac8aa042e3d8
1/* ip6tables match extension for limiting packets per destination 2 * 3 * (C) 2003-2004 by Harald Welte <laforge@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 * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> 9 * Hervé Eychenne <rv@wallfire.org> 10 * 11 * Error corections by nmalykh@bilim.com (22.01.2005) 12 */ 13#include <math.h> 14#include <stdbool.h> 15#include <stdint.h> 16#include <stdio.h> 17#include <string.h> 18#include <stdlib.h> 19#include <xtables.h> 20#include <linux/netfilter/x_tables.h> 21#include <linux/netfilter/xt_hashlimit.h> 22 23#define XT_HASHLIMIT_BURST 5 24#define XT_HASHLIMIT_BURST_MAX 10000 25 26#define XT_HASHLIMIT_BYTE_EXPIRE 15 27#define XT_HASHLIMIT_BYTE_EXPIRE_BURST 60 28 29/* miliseconds */ 30#define XT_HASHLIMIT_GCINTERVAL 1000 31 32struct hashlimit_mt_udata { 33 uint32_t mult; 34}; 35 36static void hashlimit_help(void) 37{ 38 printf( 39"hashlimit match options:\n" 40"--hashlimit <avg> max average match rate\n" 41" [Packets per second unless followed by \n" 42" /sec /minute /hour /day postfixes]\n" 43"--hashlimit-mode <mode> mode is a comma-separated list of\n" 44" dstip,srcip,dstport,srcport\n" 45"--hashlimit-name <name> name for /proc/net/ipt_hashlimit/\n" 46"[--hashlimit-burst <num>] number to match in a burst, default %u\n" 47"[--hashlimit-htable-size <num>] number of hashtable buckets\n" 48"[--hashlimit-htable-max <num>] number of hashtable entries\n" 49"[--hashlimit-htable-gcinterval] interval between garbage collection runs\n" 50"[--hashlimit-htable-expire] after which time are idle entries expired?\n", 51XT_HASHLIMIT_BURST); 52} 53 54enum { 55 O_UPTO = 0, 56 O_ABOVE, 57 O_LIMIT, 58 O_MODE, 59 O_SRCMASK, 60 O_DSTMASK, 61 O_NAME, 62 O_BURST, 63 O_HTABLE_SIZE, 64 O_HTABLE_MAX, 65 O_HTABLE_GCINT, 66 O_HTABLE_EXPIRE, 67 F_BURST = 1 << O_BURST, 68 F_UPTO = 1 << O_UPTO, 69 F_ABOVE = 1 << O_ABOVE, 70 F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE, 71}; 72 73static void hashlimit_mt_help(void) 74{ 75 printf( 76"hashlimit match options:\n" 77" --hashlimit-upto <avg> max average match rate\n" 78" [Packets per second unless followed by \n" 79" /sec /minute /hour /day postfixes]\n" 80" --hashlimit-above <avg> min average match rate\n" 81" --hashlimit-mode <mode> mode is a comma-separated list of\n" 82" dstip,srcip,dstport,srcport (or none)\n" 83" --hashlimit-srcmask <length> source address grouping prefix length\n" 84" --hashlimit-dstmask <length> destination address grouping prefix length\n" 85" --hashlimit-name <name> name for /proc/net/ipt_hashlimit\n" 86" --hashlimit-burst <num> number to match in a burst, default %u\n" 87" --hashlimit-htable-size <num> number of hashtable buckets\n" 88" --hashlimit-htable-max <num> number of hashtable entries\n" 89" --hashlimit-htable-gcinterval interval between garbage collection runs\n" 90" --hashlimit-htable-expire after which time are idle entries expired?\n" 91"\n", XT_HASHLIMIT_BURST); 92} 93 94#define s struct xt_hashlimit_info 95static const struct xt_option_entry hashlimit_opts[] = { 96 {.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE, 97 .type = XTTYPE_STRING}, 98 {.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32, 99 .min = 1, .max = XT_HASHLIMIT_BURST_MAX, .flags = XTOPT_PUT, 100 XTOPT_POINTER(s, cfg.burst)}, 101 {.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE, 102 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 103 XTOPT_POINTER(s, cfg.size)}, 104 {.name = "hashlimit-htable-max", .id = O_HTABLE_MAX, 105 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 106 XTOPT_POINTER(s, cfg.max)}, 107 {.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT, 108 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 109 XTOPT_POINTER(s, cfg.gc_interval)}, 110 {.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE, 111 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 112 XTOPT_POINTER(s, cfg.expire)}, 113 {.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING, 114 .flags = XTOPT_MAND}, 115 {.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING, 116 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1}, 117 XTOPT_TABLEEND, 118}; 119#undef s 120 121#define s struct xt_hashlimit_mtinfo1 122static const struct xt_option_entry hashlimit_mt_opts[] = { 123 {.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE, 124 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, 125 {.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO, 126 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, 127 {.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE, 128 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */ 129 {.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN}, 130 {.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN}, 131 {.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING}, 132 {.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE, 133 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 134 XTOPT_POINTER(s, cfg.size)}, 135 {.name = "hashlimit-htable-max", .id = O_HTABLE_MAX, 136 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 137 XTOPT_POINTER(s, cfg.max)}, 138 {.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT, 139 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 140 XTOPT_POINTER(s, cfg.gc_interval)}, 141 {.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE, 142 .type = XTTYPE_UINT32, .flags = XTOPT_PUT, 143 XTOPT_POINTER(s, cfg.expire)}, 144 {.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING}, 145 {.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING, 146 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1}, 147 XTOPT_TABLEEND, 148}; 149#undef s 150 151static uint32_t cost_to_bytes(uint32_t cost) 152{ 153 uint32_t r; 154 155 r = cost ? UINT32_MAX / cost : UINT32_MAX; 156 r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT; 157 return r; 158} 159 160static uint64_t bytes_to_cost(uint32_t bytes) 161{ 162 uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT; 163 return UINT32_MAX / (r+1); 164} 165 166static uint32_t get_factor(int chr) 167{ 168 switch (chr) { 169 case 'm': return 1024 * 1024; 170 case 'k': return 1024; 171 } 172 return 1; 173} 174 175static void burst_error(void) 176{ 177 xtables_error(PARAMETER_PROBLEM, "bad value for option " 178 "\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX); 179} 180 181static uint32_t parse_burst(const char *burst, struct xt_hashlimit_mtinfo1 *info) 182{ 183 uintmax_t v; 184 char *end; 185 186 if (!xtables_strtoul(burst, &end, &v, 1, UINT32_MAX) || 187 (*end == 0 && v > XT_HASHLIMIT_BURST_MAX)) 188 burst_error(); 189 190 v *= get_factor(*end); 191 if (v > UINT32_MAX) 192 xtables_error(PARAMETER_PROBLEM, "bad value for option " 193 "\"--hashlimit-burst\", value \"%s\" too large " 194 "(max %umb).", burst, UINT32_MAX/1024/1024); 195 return v; 196} 197 198static bool parse_bytes(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud) 199{ 200 unsigned int factor = 1; 201 uint64_t tmp; 202 int r; 203 const char *mode = strstr(rate, "b/s"); 204 if (!mode || mode == rate) 205 return false; 206 207 mode--; 208 r = atoi(rate); 209 if (r == 0) 210 return false; 211 212 factor = get_factor(*mode); 213 tmp = (uint64_t) r * factor; 214 if (tmp > UINT32_MAX) 215 xtables_error(PARAMETER_PROBLEM, 216 "Rate value too large \"%llu\" (max %u)\n", 217 (unsigned long long)tmp, UINT32_MAX); 218 219 *val = bytes_to_cost(tmp); 220 if (*val == 0) 221 xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"\n", rate); 222 223 ud->mult = XT_HASHLIMIT_BYTE_EXPIRE; 224 return true; 225} 226 227static 228int parse_rate(const char *rate, uint32_t *val, struct hashlimit_mt_udata *ud) 229{ 230 const char *delim; 231 uint32_t r; 232 233 ud->mult = 1; /* Seconds by default. */ 234 delim = strchr(rate, '/'); 235 if (delim) { 236 if (strlen(delim+1) == 0) 237 return 0; 238 239 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 240 ud->mult = 1; 241 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 242 ud->mult = 60; 243 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 244 ud->mult = 60*60; 245 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 246 ud->mult = 24*60*60; 247 else 248 return 0; 249 } 250 r = atoi(rate); 251 if (!r) 252 return 0; 253 254 *val = XT_HASHLIMIT_SCALE * ud->mult / r; 255 if (*val == 0) 256 /* 257 * The rate maps to infinity. (1/day is the minimum they can 258 * specify, so we are ok at that end). 259 */ 260 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); 261 return 1; 262} 263 264static void hashlimit_init(struct xt_entry_match *m) 265{ 266 struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data; 267 268 r->cfg.burst = XT_HASHLIMIT_BURST; 269 r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; 270 271} 272 273static void hashlimit_mt4_init(struct xt_entry_match *match) 274{ 275 struct xt_hashlimit_mtinfo1 *info = (void *)match->data; 276 277 info->cfg.mode = 0; 278 info->cfg.burst = XT_HASHLIMIT_BURST; 279 info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; 280 info->cfg.srcmask = 32; 281 info->cfg.dstmask = 32; 282} 283 284static void hashlimit_mt6_init(struct xt_entry_match *match) 285{ 286 struct xt_hashlimit_mtinfo1 *info = (void *)match->data; 287 288 info->cfg.mode = 0; 289 info->cfg.burst = XT_HASHLIMIT_BURST; 290 info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; 291 info->cfg.srcmask = 128; 292 info->cfg.dstmask = 128; 293} 294 295/* Parse a 'mode' parameter into the required bitmask */ 296static int parse_mode(uint32_t *mode, const char *option_arg) 297{ 298 char *tok; 299 char *arg = strdup(option_arg); 300 301 if (!arg) 302 return -1; 303 304 for (tok = strtok(arg, ",|"); 305 tok; 306 tok = strtok(NULL, ",|")) { 307 if (!strcmp(tok, "dstip")) 308 *mode |= XT_HASHLIMIT_HASH_DIP; 309 else if (!strcmp(tok, "srcip")) 310 *mode |= XT_HASHLIMIT_HASH_SIP; 311 else if (!strcmp(tok, "srcport")) 312 *mode |= XT_HASHLIMIT_HASH_SPT; 313 else if (!strcmp(tok, "dstport")) 314 *mode |= XT_HASHLIMIT_HASH_DPT; 315 else { 316 free(arg); 317 return -1; 318 } 319 } 320 free(arg); 321 return 0; 322} 323 324static void hashlimit_parse(struct xt_option_call *cb) 325{ 326 struct xt_hashlimit_info *info = cb->data; 327 328 xtables_option_parse(cb); 329 switch (cb->entry->id) { 330 case O_UPTO: 331 if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) 332 xtables_param_act(XTF_BAD_VALUE, "hashlimit", 333 "--hashlimit-upto", cb->arg); 334 break; 335 case O_MODE: 336 if (parse_mode(&info->cfg.mode, cb->arg) < 0) 337 xtables_param_act(XTF_BAD_VALUE, "hashlimit", 338 "--hashlimit-mode", cb->arg); 339 break; 340 } 341} 342 343static void hashlimit_mt_parse(struct xt_option_call *cb) 344{ 345 struct xt_hashlimit_mtinfo1 *info = cb->data; 346 347 xtables_option_parse(cb); 348 switch (cb->entry->id) { 349 case O_BURST: 350 info->cfg.burst = parse_burst(cb->arg, info); 351 break; 352 case O_UPTO: 353 if (cb->invert) 354 info->cfg.mode |= XT_HASHLIMIT_INVERT; 355 if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata)) 356 info->cfg.mode |= XT_HASHLIMIT_BYTES; 357 else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) 358 xtables_param_act(XTF_BAD_VALUE, "hashlimit", 359 "--hashlimit-upto", cb->arg); 360 break; 361 case O_ABOVE: 362 if (!cb->invert) 363 info->cfg.mode |= XT_HASHLIMIT_INVERT; 364 if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata)) 365 info->cfg.mode |= XT_HASHLIMIT_BYTES; 366 else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata)) 367 xtables_param_act(XTF_BAD_VALUE, "hashlimit", 368 "--hashlimit-above", cb->arg); 369 break; 370 case O_MODE: 371 if (parse_mode(&info->cfg.mode, cb->arg) < 0) 372 xtables_param_act(XTF_BAD_VALUE, "hashlimit", 373 "--hashlimit-mode", cb->arg); 374 break; 375 case O_SRCMASK: 376 info->cfg.srcmask = cb->val.hlen; 377 break; 378 case O_DSTMASK: 379 info->cfg.dstmask = cb->val.hlen; 380 break; 381 } 382} 383 384static void hashlimit_check(struct xt_fcheck_call *cb) 385{ 386 const struct hashlimit_mt_udata *udata = cb->udata; 387 struct xt_hashlimit_info *info = cb->data; 388 389 if (!(cb->xflags & (F_UPTO | F_ABOVE))) 390 xtables_error(PARAMETER_PROBLEM, 391 "You have to specify --hashlimit"); 392 if (!(cb->xflags & F_HTABLE_EXPIRE)) 393 info->cfg.expire = udata->mult * 1000; /* from s to msec */ 394} 395 396static void hashlimit_mt_check(struct xt_fcheck_call *cb) 397{ 398 const struct hashlimit_mt_udata *udata = cb->udata; 399 struct xt_hashlimit_mtinfo1 *info = cb->data; 400 401 if (!(cb->xflags & (F_UPTO | F_ABOVE))) 402 xtables_error(PARAMETER_PROBLEM, 403 "You have to specify --hashlimit"); 404 if (!(cb->xflags & F_HTABLE_EXPIRE)) 405 info->cfg.expire = udata->mult * 1000; /* from s to msec */ 406 407 if (info->cfg.mode & XT_HASHLIMIT_BYTES) { 408 uint32_t burst = 0; 409 if (cb->xflags & F_BURST) { 410 if (info->cfg.burst < cost_to_bytes(info->cfg.avg)) 411 xtables_error(PARAMETER_PROBLEM, 412 "burst cannot be smaller than %ub", cost_to_bytes(info->cfg.avg)); 413 414 burst = info->cfg.burst; 415 burst /= cost_to_bytes(info->cfg.avg); 416 if (info->cfg.burst % cost_to_bytes(info->cfg.avg)) 417 burst++; 418 if (!(cb->xflags & F_HTABLE_EXPIRE)) 419 info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000; 420 } 421 info->cfg.burst = burst; 422 } else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX) 423 burst_error(); 424} 425 426static const struct rates 427{ 428 const char *name; 429 uint32_t mult; 430} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 }, 431 { "hour", XT_HASHLIMIT_SCALE*60*60 }, 432 { "min", XT_HASHLIMIT_SCALE*60 }, 433 { "sec", XT_HASHLIMIT_SCALE } }; 434 435static uint32_t print_rate(uint32_t period) 436{ 437 unsigned int i; 438 439 if (period == 0) { 440 printf(" %f", INFINITY); 441 return 0; 442 } 443 444 for (i = 1; i < ARRAY_SIZE(rates); ++i) 445 if (period > rates[i].mult 446 || rates[i].mult/period < rates[i].mult%period) 447 break; 448 449 printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name); 450 /* return in msec */ 451 return rates[i-1].mult / XT_HASHLIMIT_SCALE * 1000; 452} 453 454static const struct { 455 const char *name; 456 uint32_t thresh; 457} units[] = { 458 { "m", 1024 * 1024 }, 459 { "k", 1024 }, 460 { "", 1 }, 461}; 462 463static uint32_t print_bytes(uint32_t avg, uint32_t burst, const char *prefix) 464{ 465 unsigned int i; 466 unsigned long long r; 467 468 r = cost_to_bytes(avg); 469 470 for (i = 0; i < ARRAY_SIZE(units) -1; ++i) 471 if (r >= units[i].thresh && 472 bytes_to_cost(r & ~(units[i].thresh - 1)) == avg) 473 break; 474 printf(" %llu%sb/s", r/units[i].thresh, units[i].name); 475 476 if (burst == 0) 477 return XT_HASHLIMIT_BYTE_EXPIRE * 1000; 478 479 r *= burst; 480 printf(" %s", prefix); 481 for (i = 0; i < ARRAY_SIZE(units) -1; ++i) 482 if (r >= units[i].thresh) 483 break; 484 485 printf("burst %llu%sb", r / units[i].thresh, units[i].name); 486 return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000; 487} 488 489static void print_mode(unsigned int mode, char separator) 490{ 491 bool prevmode = false; 492 493 putchar(' '); 494 if (mode & XT_HASHLIMIT_HASH_SIP) { 495 fputs("srcip", stdout); 496 prevmode = 1; 497 } 498 if (mode & XT_HASHLIMIT_HASH_SPT) { 499 if (prevmode) 500 putchar(separator); 501 fputs("srcport", stdout); 502 prevmode = 1; 503 } 504 if (mode & XT_HASHLIMIT_HASH_DIP) { 505 if (prevmode) 506 putchar(separator); 507 fputs("dstip", stdout); 508 prevmode = 1; 509 } 510 if (mode & XT_HASHLIMIT_HASH_DPT) { 511 if (prevmode) 512 putchar(separator); 513 fputs("dstport", stdout); 514 } 515} 516 517static void hashlimit_print(const void *ip, 518 const struct xt_entry_match *match, int numeric) 519{ 520 const struct xt_hashlimit_info *r = (const void *)match->data; 521 uint32_t quantum; 522 523 fputs(" limit: avg", stdout); 524 quantum = print_rate(r->cfg.avg); 525 printf(" burst %u", r->cfg.burst); 526 fputs(" mode", stdout); 527 print_mode(r->cfg.mode, '-'); 528 if (r->cfg.size) 529 printf(" htable-size %u", r->cfg.size); 530 if (r->cfg.max) 531 printf(" htable-max %u", r->cfg.max); 532 if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 533 printf(" htable-gcinterval %u", r->cfg.gc_interval); 534 if (r->cfg.expire != quantum) 535 printf(" htable-expire %u", r->cfg.expire); 536} 537 538static void 539hashlimit_mt_print(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) 540{ 541 uint32_t quantum; 542 543 if (info->cfg.mode & XT_HASHLIMIT_INVERT) 544 fputs(" limit: above", stdout); 545 else 546 fputs(" limit: up to", stdout); 547 548 if (info->cfg.mode & XT_HASHLIMIT_BYTES) { 549 quantum = print_bytes(info->cfg.avg, info->cfg.burst, ""); 550 } else { 551 quantum = print_rate(info->cfg.avg); 552 printf(" burst %u", info->cfg.burst); 553 } 554 if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | 555 XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { 556 fputs(" mode", stdout); 557 print_mode(info->cfg.mode, '-'); 558 } 559 if (info->cfg.size != 0) 560 printf(" htable-size %u", info->cfg.size); 561 if (info->cfg.max != 0) 562 printf(" htable-max %u", info->cfg.max); 563 if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 564 printf(" htable-gcinterval %u", info->cfg.gc_interval); 565 if (info->cfg.expire != quantum) 566 printf(" htable-expire %u", info->cfg.expire); 567 568 if (info->cfg.srcmask != dmask) 569 printf(" srcmask %u", info->cfg.srcmask); 570 if (info->cfg.dstmask != dmask) 571 printf(" dstmask %u", info->cfg.dstmask); 572} 573 574static void 575hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match, 576 int numeric) 577{ 578 const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; 579 580 hashlimit_mt_print(info, 32); 581} 582 583static void 584hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match, 585 int numeric) 586{ 587 const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; 588 589 hashlimit_mt_print(info, 128); 590} 591 592static void hashlimit_save(const void *ip, const struct xt_entry_match *match) 593{ 594 const struct xt_hashlimit_info *r = (const void *)match->data; 595 uint32_t quantum; 596 597 fputs(" --hashlimit", stdout); 598 quantum = print_rate(r->cfg.avg); 599 printf(" --hashlimit-burst %u", r->cfg.burst); 600 601 fputs(" --hashlimit-mode", stdout); 602 print_mode(r->cfg.mode, ','); 603 604 printf(" --hashlimit-name %s", r->name); 605 606 if (r->cfg.size) 607 printf(" --hashlimit-htable-size %u", r->cfg.size); 608 if (r->cfg.max) 609 printf(" --hashlimit-htable-max %u", r->cfg.max); 610 if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 611 printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval); 612 if (r->cfg.expire != quantum) 613 printf(" --hashlimit-htable-expire %u", r->cfg.expire); 614} 615 616static void 617hashlimit_mt_save(const struct xt_hashlimit_mtinfo1 *info, unsigned int dmask) 618{ 619 uint32_t quantum; 620 621 if (info->cfg.mode & XT_HASHLIMIT_INVERT) 622 fputs(" --hashlimit-above", stdout); 623 else 624 fputs(" --hashlimit-upto", stdout); 625 626 if (info->cfg.mode & XT_HASHLIMIT_BYTES) { 627 quantum = print_bytes(info->cfg.avg, info->cfg.burst, "--hashlimit-"); 628 } else { 629 quantum = print_rate(info->cfg.avg); 630 printf(" --hashlimit-burst %u", info->cfg.burst); 631 } 632 633 if (info->cfg.mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | 634 XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) { 635 fputs(" --hashlimit-mode", stdout); 636 print_mode(info->cfg.mode, ','); 637 } 638 639 printf(" --hashlimit-name %s", info->name); 640 641 if (info->cfg.size != 0) 642 printf(" --hashlimit-htable-size %u", info->cfg.size); 643 if (info->cfg.max != 0) 644 printf(" --hashlimit-htable-max %u", info->cfg.max); 645 if (info->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 646 printf(" --hashlimit-htable-gcinterval %u", info->cfg.gc_interval); 647 if (info->cfg.expire != quantum) 648 printf(" --hashlimit-htable-expire %u", info->cfg.expire); 649 650 if (info->cfg.srcmask != dmask) 651 printf(" --hashlimit-srcmask %u", info->cfg.srcmask); 652 if (info->cfg.dstmask != dmask) 653 printf(" --hashlimit-dstmask %u", info->cfg.dstmask); 654} 655 656static void 657hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match) 658{ 659 const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; 660 661 hashlimit_mt_save(info, 32); 662} 663 664static void 665hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match) 666{ 667 const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data; 668 669 hashlimit_mt_save(info, 128); 670} 671 672static struct xtables_match hashlimit_mt_reg[] = { 673 { 674 .family = NFPROTO_UNSPEC, 675 .name = "hashlimit", 676 .version = XTABLES_VERSION, 677 .revision = 0, 678 .size = XT_ALIGN(sizeof(struct xt_hashlimit_info)), 679 .userspacesize = offsetof(struct xt_hashlimit_info, hinfo), 680 .help = hashlimit_help, 681 .init = hashlimit_init, 682 .x6_parse = hashlimit_parse, 683 .x6_fcheck = hashlimit_check, 684 .print = hashlimit_print, 685 .save = hashlimit_save, 686 .x6_options = hashlimit_opts, 687 .udata_size = sizeof(struct hashlimit_mt_udata), 688 }, 689 { 690 .version = XTABLES_VERSION, 691 .name = "hashlimit", 692 .revision = 1, 693 .family = NFPROTO_IPV4, 694 .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)), 695 .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo), 696 .help = hashlimit_mt_help, 697 .init = hashlimit_mt4_init, 698 .x6_parse = hashlimit_mt_parse, 699 .x6_fcheck = hashlimit_mt_check, 700 .print = hashlimit_mt4_print, 701 .save = hashlimit_mt4_save, 702 .x6_options = hashlimit_mt_opts, 703 .udata_size = sizeof(struct hashlimit_mt_udata), 704 }, 705 { 706 .version = XTABLES_VERSION, 707 .name = "hashlimit", 708 .revision = 1, 709 .family = NFPROTO_IPV6, 710 .size = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)), 711 .userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo), 712 .help = hashlimit_mt_help, 713 .init = hashlimit_mt6_init, 714 .x6_parse = hashlimit_mt_parse, 715 .x6_fcheck = hashlimit_mt_check, 716 .print = hashlimit_mt6_print, 717 .save = hashlimit_mt6_save, 718 .x6_options = hashlimit_mt_opts, 719 .udata_size = sizeof(struct hashlimit_mt_udata), 720 }, 721}; 722 723void _init(void) 724{ 725 xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg)); 726} 727