1 /* Shared library add-on to iptables to add limit support. 2 * 3 * Jrme de Vivie <devivie (at) info.enserb.u-bordeaux.fr> 4 * Herv Eychenne <rv (at) wallfire.org> 5 */ 6 #include <stdio.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <xtables.h> 10 #include <linux/netfilter/x_tables.h> 11 #include <linux/netfilter/xt_limit.h> 12 13 #define XT_LIMIT_AVG "3/hour" 14 #define XT_LIMIT_BURST 5 15 16 enum { 17 O_LIMIT = 0, 18 O_BURST, 19 }; 20 21 static void limit_help(void) 22 { 23 printf( 24 "limit match options:\n" 25 "--limit avg max average match rate: default "XT_LIMIT_AVG"\n" 26 " [Packets per second unless followed by \n" 27 " /sec /minute /hour /day postfixes]\n" 28 "--limit-burst number number to match in a burst, default %u\n", 29 XT_LIMIT_BURST); 30 } 31 32 static const struct xt_option_entry limit_opts[] = { 33 {.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING}, 34 {.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32, 35 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst), 36 .min = 0, .max = 10000}, 37 XTOPT_TABLEEND, 38 }; 39 40 static 41 int parse_rate(const char *rate, uint32_t *val) 42 { 43 const char *delim; 44 uint32_t r; 45 uint32_t mult = 1; /* Seconds by default. */ 46 47 delim = strchr(rate, '/'); 48 if (delim) { 49 if (strlen(delim+1) == 0) 50 return 0; 51 52 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 53 mult = 1; 54 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 55 mult = 60; 56 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 57 mult = 60*60; 58 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 59 mult = 24*60*60; 60 else 61 return 0; 62 } 63 r = atoi(rate); 64 if (!r) 65 return 0; 66 67 /* This would get mapped to infinite (1/day is minimum they 68 can specify, so we're ok at that end). */ 69 if (r / mult > XT_LIMIT_SCALE) 70 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); 71 72 *val = XT_LIMIT_SCALE * mult / r; 73 return 1; 74 } 75 76 static void limit_init(struct xt_entry_match *m) 77 { 78 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data; 79 80 parse_rate(XT_LIMIT_AVG, &r->avg); 81 r->burst = XT_LIMIT_BURST; 82 83 } 84 85 /* FIXME: handle overflow: 86 if (r->avg*r->burst/r->burst != r->avg) 87 xtables_error(PARAMETER_PROBLEM, 88 "Sorry: burst too large for that avg rate.\n"); 89 */ 90 91 static void limit_parse(struct xt_option_call *cb) 92 { 93 struct xt_rateinfo *r = cb->data; 94 95 xtables_option_parse(cb); 96 switch (cb->entry->id) { 97 case O_LIMIT: 98 if (!parse_rate(cb->arg, &r->avg)) 99 xtables_error(PARAMETER_PROBLEM, 100 "bad rate \"%s\"'", cb->arg); 101 break; 102 } 103 if (cb->invert) 104 xtables_error(PARAMETER_PROBLEM, 105 "limit does not support invert"); 106 } 107 108 static const struct rates 109 { 110 const char *name; 111 uint32_t mult; 112 } rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 }, 113 { "hour", XT_LIMIT_SCALE*60*60 }, 114 { "min", XT_LIMIT_SCALE*60 }, 115 { "sec", XT_LIMIT_SCALE } }; 116 117 static void print_rate(uint32_t period) 118 { 119 unsigned int i; 120 121 for (i = 1; i < ARRAY_SIZE(rates); ++i) 122 if (period > rates[i].mult 123 || rates[i].mult/period < rates[i].mult%period) 124 break; 125 126 printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name); 127 } 128 129 static void 130 limit_print(const void *ip, const struct xt_entry_match *match, int numeric) 131 { 132 const struct xt_rateinfo *r = (const void *)match->data; 133 printf(" limit: avg"); print_rate(r->avg); 134 printf(" burst %u", r->burst); 135 } 136 137 static void limit_save(const void *ip, const struct xt_entry_match *match) 138 { 139 const struct xt_rateinfo *r = (const void *)match->data; 140 141 printf(" --limit"); print_rate(r->avg); 142 if (r->burst != XT_LIMIT_BURST) 143 printf(" --limit-burst %u", r->burst); 144 } 145 146 static struct xtables_match limit_match = { 147 .family = NFPROTO_UNSPEC, 148 .name = "limit", 149 .version = XTABLES_VERSION, 150 .size = XT_ALIGN(sizeof(struct xt_rateinfo)), 151 .userspacesize = offsetof(struct xt_rateinfo, prev), 152 .help = limit_help, 153 .init = limit_init, 154 .x6_parse = limit_parse, 155 .print = limit_print, 156 .save = limit_save, 157 .x6_options = limit_opts, 158 }; 159 160 void _init(void) 161 { 162 xtables_register_match(&limit_match); 163 } 164