libxt_limit.c revision 32b8e61e4e5bd405d9ad07bf9468498dfbb19f9e
15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/* Shared library add-on to iptables to add limit support. 2ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * 3ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr> 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Hervé Eychenne <rv@wallfire.org> 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) */ 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdbool.h> 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <stdio.h> 868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <string.h> 968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <stdlib.h> 1068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include <getopt.h> 113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <xtables.h> 123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <stddef.h> 135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <linux/netfilter/x_tables.h> 145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/* For 64bit kernel / 32bit userspace */ 15868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include <linux/netfilter/xt_limit.h> 16eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch 174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)#define XT_LIMIT_AVG "3/hour" 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#define XT_LIMIT_BURST 5 195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static void limit_help(void) 215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles){ 222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) printf( 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"limit match options:\n" 245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)"--limit avg max average match rate: default "XT_LIMIT_AVG"\n" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)" [Packets per second unless followed by \n" 26ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch" /sec /minute /hour /day postfixes]\n" 27ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch"--limit-burst number number to match in a burst, default %u\n", 285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)XT_LIMIT_BURST); 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static const struct option limit_opts[] = { 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) {.name = "limit", .has_arg = true, .val = '%'}, 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) {.name = "limit-burst", .has_arg = true, .val = '$'}, 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) XT_GETOPT_TABLEEND, 355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}; 365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 374e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)static 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int parse_rate(const char *rate, u_int32_t *val) 391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{ 401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const char *delim; 411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci u_int32_t r; 421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci u_int32_t mult = 1; /* Seconds by default. */ 431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci delim = strchr(rate, '/'); 451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (delim) { 461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (strlen(delim+1) == 0) 471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci return 0; 484e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) 494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) mult = 1; 515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 524e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) mult = 60; 535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 544e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) mult = 60*60; 555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) mult = 24*60*60; 575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) else 585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 0; 595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) } 605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) r = atoi(rate); 615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!r) 625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 0; 635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) /* This would get mapped to infinite (1/day is minimum they 655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) can specify, so we're ok at that end). */ 665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (r / mult > XT_LIMIT_SCALE) 675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); 685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *val = XT_LIMIT_SCALE * mult / r; 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) return 1; 715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void limit_init(struct xt_entry_match *m) 745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){ 755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) struct xt_rateinfo *r = (struct xt_rateinfo *)m->data; 765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) parse_rate(XT_LIMIT_AVG, &r->avg); 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) r->burst = XT_LIMIT_BURST; 795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 80010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)} 81868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) 825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* FIXME: handle overflow: 838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) if (r->avg*r->burst/r->burst != r->avg) 845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xtables_error(PARAMETER_PROBLEM, 855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "Sorry: burst too large for that avg rate.\n"); 863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)*/ 878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles) 883551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)static int 893551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)limit_parse(int c, char **argv, int invert, unsigned int *flags, 903551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const void *entry, struct xt_entry_match **match) 91effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch{ 923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data; 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) unsigned int num; 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) switch(c) { 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) case '%': 975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!parse_rate(optarg, &r->avg)) 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xtables_error(PARAMETER_PROBLEM, 1004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) "bad rate `%s'", optarg); 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) break; 1023551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) case '$': 1044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) if (xtables_check_inverse(optarg, &invert, &optind, 0, argv)) break; 105868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) if (!xtables_strtoui(optarg, NULL, &num, 0, 10000)) 1063551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) xtables_error(PARAMETER_PROBLEM, 107868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) "bad --limit-burst `%s'", optarg); 1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) r->burst = num; 109868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) break; 1103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 111868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) default: 1124e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) return 0; 113868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles) } 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (invert) 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) xtables_error(PARAMETER_PROBLEM, 1173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) "limit does not support invert"); 11890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return 1; 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const struct rates 1233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles){ 1243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) const char *name; 1253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) u_int32_t mult; 1263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 }, 127eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch { "hour", XT_LIMIT_SCALE*60*60 }, 1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) { "min", XT_LIMIT_SCALE*60 }, 1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) { "sec", XT_LIMIT_SCALE } }; 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void print_rate(u_int32_t period) 1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{ 1331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci unsigned int i; 1341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci for (i = 1; i < ARRAY_SIZE(rates); ++i) 1361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (period > rates[i].mult 1371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci || rates[i].mult/period < rates[i].mult%period) 1381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci break; 1391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); 1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci} 1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci 1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void 1441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccilimit_print(const void *ip, const struct xt_entry_match *match, int numeric) 1451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci{ 1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci const struct xt_rateinfo *r = (const void *)match->data; 1471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci printf("limit: avg "); print_rate(r->avg); 1481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci printf("burst %u ", r->burst); 1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)} 1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) 151010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)static void limit_save(const void *ip, const struct xt_entry_match *match) 152424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles){ 153424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) const struct xt_rateinfo *r = (const void *)match->data; 154424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 155424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) printf("--limit "); print_rate(r->avg); 156424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) if (r->burst != XT_LIMIT_BURST) 157424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) printf("--limit-burst %u ", r->burst); 158424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 159424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 160424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)static struct xtables_match limit_match = { 161424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .family = NFPROTO_UNSPEC, 1624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) .name = "limit", 163424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .version = XTABLES_VERSION, 164424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .size = XT_ALIGN(sizeof(struct xt_rateinfo)), 1654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) .userspacesize = offsetof(struct xt_rateinfo, prev), 166424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .help = limit_help, 167424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .init = limit_init, 168424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .parse = limit_parse, 169424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .print = limit_print, 170424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .save = limit_save, 171424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) .extra_opts = limit_opts, 172424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)}; 173424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) 1744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void _init(void) 175424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles){ 176424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles) xtables_register_match(&limit_match); 177424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)} 178424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)