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