libxt_hashlimit.c revision da75a5a4ed43d3a822b9a75d6fbbfc2e06ccfbfa
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 14#include <stdio.h> 15#include <string.h> 16#include <stdlib.h> 17#include <getopt.h> 18#include <xtables.h> 19#include <stddef.h> 20#include <linux/netfilter/x_tables.h> 21#include <linux/netfilter/xt_hashlimit.h> 22 23#define XT_HASHLIMIT_BURST 5 24 25/* miliseconds */ 26#define XT_HASHLIMIT_GCINTERVAL 1000 27#define XT_HASHLIMIT_EXPIRE 10000 28 29/* Function which prints out usage message. */ 30static void hashlimit_help(void) 31{ 32 printf( 33"hashlimit v%s options:\n" 34"--hashlimit <avg> max average match rate\n" 35" [Packets per second unless followed by \n" 36" /sec /minute /hour /day postfixes]\n" 37"--hashlimit-mode <mode> mode is a comma-separated list of\n" 38" dstip,srcip,dstport,srcport\n" 39"--hashlimit-name <name> name for /proc/net/ipt_hashlimit/\n" 40"[--hashlimit-burst <num>] number to match in a burst, default %u\n" 41"[--hashlimit-htable-size <num>] number of hashtable buckets\n" 42"[--hashlimit-htable-max <num>] number of hashtable entries\n" 43"[--hashlimit-htable-gcinterval] interval between garbage collection runs\n" 44"[--hashlimit-htable-expire] after which time are idle entries expired?\n" 45"\n", IPTABLES_VERSION, XT_HASHLIMIT_BURST); 46} 47 48static const struct option hashlimit_opts[] = { 49 { "hashlimit", 1, NULL, '%' }, 50 { "hashlimit-burst", 1, NULL, '$' }, 51 { "hashlimit-htable-size", 1, NULL, '&' }, 52 { "hashlimit-htable-max", 1, NULL, '*' }, 53 { "hashlimit-htable-gcinterval", 1, NULL, '(' }, 54 { "hashlimit-htable-expire", 1, NULL, ')' }, 55 { "hashlimit-mode", 1, NULL, '_' }, 56 { "hashlimit-name", 1, NULL, '"' }, 57 { } 58}; 59 60static 61int parse_rate(const char *rate, u_int32_t *val) 62{ 63 const char *delim; 64 u_int32_t r; 65 u_int32_t mult = 1; /* Seconds by default. */ 66 67 delim = strchr(rate, '/'); 68 if (delim) { 69 if (strlen(delim+1) == 0) 70 return 0; 71 72 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 73 mult = 1; 74 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 75 mult = 60; 76 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 77 mult = 60*60; 78 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 79 mult = 24*60*60; 80 else 81 return 0; 82 } 83 r = atoi(rate); 84 if (!r) 85 return 0; 86 87 /* This would get mapped to infinite (1/day is minimum they 88 can specify, so we're ok at that end). */ 89 if (r / mult > XT_HASHLIMIT_SCALE) 90 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate); 91 92 *val = XT_HASHLIMIT_SCALE * mult / r; 93 return 1; 94} 95 96/* Initialize the match. */ 97static void hashlimit_init(struct xt_entry_match *m) 98{ 99 struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data; 100 101 r->cfg.burst = XT_HASHLIMIT_BURST; 102 r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL; 103 r->cfg.expire = XT_HASHLIMIT_EXPIRE; 104 105} 106 107 108/* Parse a 'mode' parameter into the required bitmask */ 109static int parse_mode(struct xt_hashlimit_info *r, char *optarg) 110{ 111 char *tok; 112 char *arg = strdup(optarg); 113 114 if (!arg) 115 return -1; 116 117 r->cfg.mode = 0; 118 119 for (tok = strtok(arg, ",|"); 120 tok; 121 tok = strtok(NULL, ",|")) { 122 if (!strcmp(tok, "dstip")) 123 r->cfg.mode |= XT_HASHLIMIT_HASH_DIP; 124 else if (!strcmp(tok, "srcip")) 125 r->cfg.mode |= XT_HASHLIMIT_HASH_SIP; 126 else if (!strcmp(tok, "srcport")) 127 r->cfg.mode |= XT_HASHLIMIT_HASH_SPT; 128 else if (!strcmp(tok, "dstport")) 129 r->cfg.mode |= XT_HASHLIMIT_HASH_DPT; 130 else { 131 free(arg); 132 return -1; 133 } 134 } 135 free(arg); 136 return 0; 137} 138 139#define PARAM_LIMIT 0x00000001 140#define PARAM_BURST 0x00000002 141#define PARAM_MODE 0x00000004 142#define PARAM_NAME 0x00000008 143#define PARAM_SIZE 0x00000010 144#define PARAM_MAX 0x00000020 145#define PARAM_GCINTERVAL 0x00000040 146#define PARAM_EXPIRE 0x00000080 147 148/* Function which parses command options; returns true if it 149 ate an option */ 150static int 151hashlimit_parse(int c, char **argv, int invert, unsigned int *flags, 152 const void *entry, struct xt_entry_match **match) 153{ 154 struct xt_hashlimit_info *r = 155 (struct xt_hashlimit_info *)(*match)->data; 156 unsigned int num; 157 158 switch(c) { 159 case '%': 160 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit", 161 *flags & PARAM_LIMIT); 162 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 163 if (!parse_rate(optarg, &r->cfg.avg)) 164 exit_error(PARAMETER_PROBLEM, 165 "bad rate `%s'", optarg); 166 *flags |= PARAM_LIMIT; 167 break; 168 169 case '$': 170 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-burst", 171 *flags & PARAM_BURST); 172 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 173 if (string_to_number(optarg, 0, 10000, &num) == -1) 174 exit_error(PARAMETER_PROBLEM, 175 "bad --hashlimit-burst `%s'", optarg); 176 r->cfg.burst = num; 177 *flags |= PARAM_BURST; 178 break; 179 case '&': 180 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-size", 181 *flags & PARAM_SIZE); 182 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 183 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 184 exit_error(PARAMETER_PROBLEM, 185 "bad --hashlimit-htable-size: `%s'", optarg); 186 r->cfg.size = num; 187 *flags |= PARAM_SIZE; 188 break; 189 case '*': 190 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-htable-max", 191 *flags & PARAM_MAX); 192 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 193 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 194 exit_error(PARAMETER_PROBLEM, 195 "bad --hashlimit-htable-max: `%s'", optarg); 196 r->cfg.max = num; 197 *flags |= PARAM_MAX; 198 break; 199 case '(': 200 param_act(P_ONLY_ONCE, "hashlimit", 201 "--hashlimit-htable-gcinterval", 202 *flags & PARAM_GCINTERVAL); 203 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 204 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 205 exit_error(PARAMETER_PROBLEM, 206 "bad --hashlimit-htable-gcinterval: `%s'", 207 optarg); 208 /* FIXME: not HZ dependent!! */ 209 r->cfg.gc_interval = num; 210 *flags |= PARAM_GCINTERVAL; 211 break; 212 case ')': 213 param_act(P_ONLY_ONCE, "hashlimit", 214 "--hashlimit-htable-expire", *flags & PARAM_EXPIRE); 215 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 216 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 217 exit_error(PARAMETER_PROBLEM, 218 "bad --hashlimit-htable-expire: `%s'", optarg); 219 /* FIXME: not HZ dependent */ 220 r->cfg.expire = num; 221 *flags |= PARAM_EXPIRE; 222 break; 223 case '_': 224 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-mode", 225 *flags & PARAM_MODE); 226 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 227 if (parse_mode(r, optarg) < 0) 228 exit_error(PARAMETER_PROBLEM, 229 "bad --hashlimit-mode: `%s'\n", optarg); 230 *flags |= PARAM_MODE; 231 break; 232 case '"': 233 param_act(P_ONLY_ONCE, "hashlimit", "--hashlimit-name", 234 *flags & PARAM_NAME); 235 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 236 if (strlen(optarg) == 0) 237 exit_error(PARAMETER_PROBLEM, "Zero-length name?"); 238 strncpy(r->name, optarg, sizeof(r->name)); 239 *flags |= PARAM_NAME; 240 break; 241 default: 242 return 0; 243 } 244 245 if (invert) 246 exit_error(PARAMETER_PROBLEM, 247 "hashlimit does not support invert"); 248 249 return 1; 250} 251 252/* Final check; nothing. */ 253static void hashlimit_check(unsigned int flags) 254{ 255 if (!(flags & PARAM_LIMIT)) 256 exit_error(PARAMETER_PROBLEM, 257 "You have to specify --hashlimit"); 258 if (!(flags & PARAM_MODE)) 259 exit_error(PARAMETER_PROBLEM, 260 "You have to specify --hashlimit-mode"); 261 if (!(flags & PARAM_NAME)) 262 exit_error(PARAMETER_PROBLEM, 263 "You have to specify --hashlimit-name"); 264} 265 266static const struct rates 267{ 268 const char *name; 269 u_int32_t mult; 270} rates[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 }, 271 { "hour", XT_HASHLIMIT_SCALE*60*60 }, 272 { "min", XT_HASHLIMIT_SCALE*60 }, 273 { "sec", XT_HASHLIMIT_SCALE } }; 274 275static void print_rate(u_int32_t period) 276{ 277 unsigned int i; 278 279 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) { 280 if (period > rates[i].mult 281 || rates[i].mult/period < rates[i].mult%period) 282 break; 283 } 284 285 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); 286} 287 288static void print_mode(const struct xt_hashlimit_info *r, char separator) 289{ 290 int prevmode = 0; 291 292 if (r->cfg.mode & XT_HASHLIMIT_HASH_SIP) { 293 if (prevmode) 294 putchar(separator); 295 fputs("srcip", stdout); 296 prevmode = 1; 297 } 298 if (r->cfg.mode & XT_HASHLIMIT_HASH_SPT) { 299 if (prevmode) 300 putchar(separator); 301 fputs("srcport", stdout); 302 prevmode = 1; 303 } 304 if (r->cfg.mode & XT_HASHLIMIT_HASH_DIP) { 305 if (prevmode) 306 putchar(separator); 307 fputs("dstip", stdout); 308 prevmode = 1; 309 } 310 if (r->cfg.mode & XT_HASHLIMIT_HASH_DPT) { 311 if (prevmode) 312 putchar(separator); 313 fputs("dstport", stdout); 314 } 315 putchar(' '); 316} 317 318/* Prints out the matchinfo. */ 319static void hashlimit_print(const void *ip, 320 const struct xt_entry_match *match, int numeric) 321{ 322 struct xt_hashlimit_info *r = 323 (struct xt_hashlimit_info *)match->data; 324 fputs("limit: avg ", stdout); print_rate(r->cfg.avg); 325 printf("burst %u ", r->cfg.burst); 326 fputs("mode ", stdout); 327 print_mode(r, '-'); 328 if (r->cfg.size) 329 printf("htable-size %u ", r->cfg.size); 330 if (r->cfg.max) 331 printf("htable-max %u ", r->cfg.max); 332 if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 333 printf("htable-gcinterval %u ", r->cfg.gc_interval); 334 if (r->cfg.expire != XT_HASHLIMIT_EXPIRE) 335 printf("htable-expire %u ", r->cfg.expire); 336} 337 338/* FIXME: Make minimalist: only print rate if not default --RR */ 339static void hashlimit_save(const void *ip, const struct xt_entry_match *match) 340{ 341 struct xt_hashlimit_info *r = 342 (struct xt_hashlimit_info *)match->data; 343 344 fputs("--hashlimit ", stdout); print_rate(r->cfg.avg); 345 if (r->cfg.burst != XT_HASHLIMIT_BURST) 346 printf("--hashlimit-burst %u ", r->cfg.burst); 347 348 fputs("--hashlimit-mode ", stdout); 349 print_mode(r, ','); 350 351 printf("--hashlimit-name %s ", r->name); 352 353 if (r->cfg.size) 354 printf("--hashlimit-htable-size %u ", r->cfg.size); 355 if (r->cfg.max) 356 printf("--hashlimit-htable-max %u ", r->cfg.max); 357 if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL) 358 printf("--hashlimit-htable-gcinterval %u", r->cfg.gc_interval); 359 if (r->cfg.expire != XT_HASHLIMIT_EXPIRE) 360 printf("--hashlimit-htable-expire %u ", r->cfg.expire); 361} 362 363static struct xtables_match hashlimit_match = { 364 .family = AF_INET, 365 .name = "hashlimit", 366 .version = IPTABLES_VERSION, 367 .size = XT_ALIGN(sizeof(struct xt_hashlimit_info)), 368 .userspacesize = offsetof(struct xt_hashlimit_info, hinfo), 369 .help = hashlimit_help, 370 .init = hashlimit_init, 371 .parse = hashlimit_parse, 372 .final_check = hashlimit_check, 373 .print = hashlimit_print, 374 .save = hashlimit_save, 375 .extra_opts = hashlimit_opts, 376}; 377 378static struct xtables_match hashlimit_match6 = { 379 .family = AF_INET6, 380 .name = "hashlimit", 381 .version = IPTABLES_VERSION, 382 .size = XT_ALIGN(sizeof(struct xt_hashlimit_info)), 383 .userspacesize = offsetof(struct xt_hashlimit_info, hinfo), 384 .help = hashlimit_help, 385 .init = hashlimit_init, 386 .parse = hashlimit_parse, 387 .final_check = hashlimit_check, 388 .print = hashlimit_print, 389 .save = hashlimit_save, 390 .extra_opts = hashlimit_opts, 391}; 392 393void _init(void) 394{ 395 xtables_register_match(&hashlimit_match); 396 xtables_register_match(&hashlimit_match6); 397} 398