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