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