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