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 #define _BSD_SOURCE 1 7 #define _ISOC99_SOURCE 1 8 #include <math.h> 9 #include <stdio.h> 10 #include <string.h> 11 #include <stdlib.h> 12 #include <xtables.h> 13 #include <linux/netfilter/x_tables.h> 14 #include <linux/netfilter/xt_limit.h> 15 16 #define XT_LIMIT_AVG "3/hour" 17 #define XT_LIMIT_BURST 5 18 19 enum { 20 O_LIMIT = 0, 21 O_BURST, 22 }; 23 24 static void limit_help(void) 25 { 26 printf( 27 "limit match options:\n" 28 "--limit avg max average match rate: default "XT_LIMIT_AVG"\n" 29 " [Packets per second unless followed by \n" 30 " /sec /minute /hour /day postfixes]\n" 31 "--limit-burst number number to match in a burst, default %u\n", 32 XT_LIMIT_BURST); 33 } 34 35 static const struct xt_option_entry limit_opts[] = { 36 {.name = "limit", .id = O_LIMIT, .type = XTTYPE_STRING}, 37 {.name = "limit-burst", .id = O_BURST, .type = XTTYPE_UINT32, 38 .flags = XTOPT_PUT, XTOPT_POINTER(struct xt_rateinfo, burst), 39 .min = 0, .max = 10000}, 40 XTOPT_TABLEEND, 41 }; 42 43 static 44 int parse_rate(const char *rate, uint32_t *val) 45 { 46 const char *delim; 47 uint32_t r; 48 uint32_t mult = 1; /* Seconds by default. */ 49 50 delim = strchr(rate, '/'); 51 if (delim) { 52 if (strlen(delim+1) == 0) 53 return 0; 54 55 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 56 mult = 1; 57 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 58 mult = 60; 59 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 60 mult = 60*60; 61 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 62 mult = 24*60*60; 63 else 64 return 0; 65 } 66 r = atoi(rate); 67 if (!r) 68 return 0; 69 70 *val = XT_LIMIT_SCALE * mult / r; 71 if (*val == 0) 72 /* 73 * The rate maps to infinity. (1/day is the minimum they can 74 * specify, so we are ok at that end). 75 */ 76 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate); 77 return 1; 78 } 79 80 static void limit_init(struct xt_entry_match *m) 81 { 82 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data; 83 84 parse_rate(XT_LIMIT_AVG, &r->avg); 85 r->burst = XT_LIMIT_BURST; 86 87 } 88 89 /* FIXME: handle overflow: 90 if (r->avg*r->burst/r->burst != r->avg) 91 xtables_error(PARAMETER_PROBLEM, 92 "Sorry: burst too large for that avg rate.\n"); 93 */ 94 95 static void limit_parse(struct xt_option_call *cb) 96 { 97 struct xt_rateinfo *r = cb->data; 98 99 xtables_option_parse(cb); 100 switch (cb->entry->id) { 101 case O_LIMIT: 102 if (!parse_rate(cb->arg, &r->avg)) 103 xtables_error(PARAMETER_PROBLEM, 104 "bad rate \"%s\"'", cb->arg); 105 break; 106 } 107 if (cb->invert) 108 xtables_error(PARAMETER_PROBLEM, 109 "limit does not support invert"); 110 } 111 112 static const struct rates 113 { 114 const char *name; 115 uint32_t mult; 116 } rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 }, 117 { "hour", XT_LIMIT_SCALE*60*60 }, 118 { "min", XT_LIMIT_SCALE*60 }, 119 { "sec", XT_LIMIT_SCALE } }; 120 121 static void print_rate(uint32_t period) 122 { 123 unsigned int i; 124 125 if (period == 0) { 126 printf(" %f", INFINITY); 127 return; 128 } 129 130 for (i = 1; i < ARRAY_SIZE(rates); ++i) 131 if (period > rates[i].mult 132 || rates[i].mult/period < rates[i].mult%period) 133 break; 134 135 printf(" %u/%s", rates[i-1].mult / period, rates[i-1].name); 136 } 137 138 static void 139 limit_print(const void *ip, const struct xt_entry_match *match, int numeric) 140 { 141 const struct xt_rateinfo *r = (const void *)match->data; 142 printf(" limit: avg"); print_rate(r->avg); 143 printf(" burst %u", r->burst); 144 } 145 146 static void limit_save(const void *ip, const struct xt_entry_match *match) 147 { 148 const struct xt_rateinfo *r = (const void *)match->data; 149 150 printf(" --limit"); print_rate(r->avg); 151 if (r->burst != XT_LIMIT_BURST) 152 printf(" --limit-burst %u", r->burst); 153 } 154 155 static const struct rates rates_xlate[] = { 156 { "day", XT_LIMIT_SCALE * 24 * 60 * 60 }, 157 { "hour", XT_LIMIT_SCALE * 60 * 60 }, 158 { "minute", XT_LIMIT_SCALE * 60 }, 159 { "second", XT_LIMIT_SCALE } 160 }; 161 162 static void print_rate_xlate(uint32_t period, struct xt_xlate *xl) 163 { 164 unsigned int i; 165 166 if (period == 0) { 167 xt_xlate_add(xl, " %f", INFINITY); 168 return; 169 } 170 171 for (i = 1; i < ARRAY_SIZE(rates); ++i) 172 if (period > rates_xlate[i].mult || 173 rates_xlate[i].mult / period < rates_xlate[i].mult % period) 174 break; 175 176 xt_xlate_add(xl, " %u/%s", rates_xlate[i - 1].mult / period, 177 rates_xlate[i - 1].name); 178 } 179 180 static int limit_xlate(struct xt_xlate *xl, 181 const struct xt_xlate_mt_params *params) 182 { 183 const struct xt_rateinfo *r = (const void *)params->match->data; 184 185 xt_xlate_add(xl, "limit rate"); 186 print_rate_xlate(r->avg, xl); 187 if (r->burst != 0) 188 xt_xlate_add(xl, " burst %u packets", r->burst); 189 190 return 1; 191 } 192 193 static struct xtables_match limit_match = { 194 .family = NFPROTO_UNSPEC, 195 .name = "limit", 196 .version = XTABLES_VERSION, 197 .size = XT_ALIGN(sizeof(struct xt_rateinfo)), 198 .userspacesize = offsetof(struct xt_rateinfo, prev), 199 .help = limit_help, 200 .init = limit_init, 201 .x6_parse = limit_parse, 202 .print = limit_print, 203 .save = limit_save, 204 .x6_options = limit_opts, 205 .xlate = limit_xlate, 206 }; 207 208 void _init(void) 209 { 210 xtables_register_match(&limit_match); 211 } 212