1/* iptables match extension for limiting packets per destination 2 * 3 * (C) 2003 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 12#include <stdio.h> 13#include <string.h> 14#include <stdlib.h> 15#include <getopt.h> 16#include <iptables.h> 17#include <stddef.h> 18#include <linux/netfilter_ipv4/ip_tables.h> 19#include <linux/netfilter_ipv4/ipt_dstlimit.h> 20 21#define IPT_DSTLIMIT_BURST 5 22 23/* miliseconds */ 24#define IPT_DSTLIMIT_GCINTERVAL 1000 25#define IPT_DSTLIMIT_EXPIRE 10000 26 27/* Function which prints out usage message. */ 28static void 29help(void) 30{ 31 printf( 32"dstlimit v%s options:\n" 33"--dstlimit <avg> max average match rate\n" 34" [Packets per second unless followed by \n" 35" /sec /minute /hour /day postfixes]\n" 36"--dstlimit-mode <mode> mode\n" 37" dstip\n" 38" dstip-dstport\n" 39" srcip-dstip\n" 40" srcip-dstip-dstport\n" 41"--dstlimit-name <name> name for /proc/net/ipt_dstlimit/\n" 42"[--dstlimit-burst <num>] number to match in a burst, default %u\n" 43"[--dstlimit-htable-size <num>] number of hashtable buckets\n" 44"[--dstlimit-htable-max <num>] number of hashtable entries\n" 45"[--dstlimit-htable-gcinterval] interval between garbage collection runs\n" 46"[--dstlimit-htable-expire] after which time are idle entries expired?\n" 47"\n", IPTABLES_VERSION, IPT_DSTLIMIT_BURST); 48} 49 50static struct option opts[] = { 51 { "dstlimit", 1, 0, '%' }, 52 { "dstlimit-burst", 1, 0, '$' }, 53 { "dstlimit-htable-size", 1, 0, '&' }, 54 { "dstlimit-htable-max", 1, 0, '*' }, 55 { "dstlimit-htable-gcinterval", 1, 0, '(' }, 56 { "dstlimit-htable-expire", 1, 0, ')' }, 57 { "dstlimit-mode", 1, 0, '_' }, 58 { "dstlimit-name", 1, 0, '"' }, 59 { 0 } 60}; 61 62static 63int parse_rate(const char *rate, u_int32_t *val) 64{ 65 const char *delim; 66 u_int32_t r; 67 u_int32_t mult = 1; /* Seconds by default. */ 68 69 delim = strchr(rate, '/'); 70 if (delim) { 71 if (strlen(delim+1) == 0) 72 return 0; 73 74 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) 75 mult = 1; 76 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) 77 mult = 60; 78 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) 79 mult = 60*60; 80 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) 81 mult = 24*60*60; 82 else 83 return 0; 84 } 85 r = atoi(rate); 86 if (!r) 87 return 0; 88 89 /* This would get mapped to infinite (1/day is minimum they 90 can specify, so we're ok at that end). */ 91 if (r / mult > IPT_DSTLIMIT_SCALE) 92 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate); 93 94 *val = IPT_DSTLIMIT_SCALE * mult / r; 95 return 1; 96} 97 98/* Initialize the match. */ 99static void 100init(struct ipt_entry_match *m, unsigned int *nfcache) 101{ 102 struct ipt_dstlimit_info *r = (struct ipt_dstlimit_info *)m->data; 103 104 r->cfg.burst = IPT_DSTLIMIT_BURST; 105 r->cfg.gc_interval = IPT_DSTLIMIT_GCINTERVAL; 106 r->cfg.expire = IPT_DSTLIMIT_EXPIRE; 107 108} 109 110#define PARAM_LIMIT 0x00000001 111#define PARAM_BURST 0x00000002 112#define PARAM_MODE 0x00000004 113#define PARAM_NAME 0x00000008 114#define PARAM_SIZE 0x00000010 115#define PARAM_MAX 0x00000020 116#define PARAM_GCINTERVAL 0x00000040 117#define PARAM_EXPIRE 0x00000080 118 119/* Function which parses command options; returns true if it 120 ate an option */ 121static int 122parse(int c, char **argv, int invert, unsigned int *flags, 123 const struct ipt_entry *entry, 124 unsigned int *nfcache, 125 struct ipt_entry_match **match) 126{ 127 struct ipt_dstlimit_info *r = 128 (struct ipt_dstlimit_info *)(*match)->data; 129 unsigned int num; 130 131 switch(c) { 132 case '%': 133 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 134 if (!parse_rate(optarg, &r->cfg.avg)) 135 exit_error(PARAMETER_PROBLEM, 136 "bad rate `%s'", optarg); 137 *flags |= PARAM_LIMIT; 138 break; 139 140 case '$': 141 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 142 if (string_to_number(optarg, 0, 10000, &num) == -1) 143 exit_error(PARAMETER_PROBLEM, 144 "bad --dstlimit-burst `%s'", optarg); 145 r->cfg.burst = num; 146 *flags |= PARAM_BURST; 147 break; 148 case '&': 149 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 150 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 151 exit_error(PARAMETER_PROBLEM, 152 "bad --dstlimit-htable-size: `%s'", optarg); 153 r->cfg.size = num; 154 *flags |= PARAM_SIZE; 155 break; 156 case '*': 157 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 158 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 159 exit_error(PARAMETER_PROBLEM, 160 "bad --dstlimit-htable-max: `%s'", optarg); 161 r->cfg.max = num; 162 *flags |= PARAM_MAX; 163 break; 164 case '(': 165 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 166 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 167 exit_error(PARAMETER_PROBLEM, 168 "bad --dstlimit-htable-gcinterval: `%s'", 169 optarg); 170 /* FIXME: not HZ dependent!! */ 171 r->cfg.gc_interval = num; 172 *flags |= PARAM_GCINTERVAL; 173 break; 174 case ')': 175 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 176 if (string_to_number(optarg, 0, 0xffffffff, &num) == -1) 177 exit_error(PARAMETER_PROBLEM, 178 "bad --dstlimit-htable-expire: `%s'", optarg); 179 /* FIXME: not HZ dependent */ 180 r->cfg.expire = num; 181 *flags |= PARAM_EXPIRE; 182 break; 183 case '_': 184 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 185 if (!strcmp(optarg, "dstip")) 186 r->cfg.mode = IPT_DSTLIMIT_HASH_DIP; 187 else if (!strcmp(optarg, "dstip-destport") || 188 !strcmp(optarg, "dstip-dstport")) 189 r->cfg.mode = IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT; 190 else if (!strcmp(optarg, "srcip-dstip")) 191 r->cfg.mode = IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP; 192 else if (!strcmp(optarg, "srcip-dstip-destport") || 193 !strcmp(optarg, "srcip-dstip-dstport")) 194 r->cfg.mode = IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT; 195 else 196 exit_error(PARAMETER_PROBLEM, 197 "bad --dstlimit-mode: `%s'\n", optarg); 198 *flags |= PARAM_MODE; 199 break; 200 case '"': 201 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break; 202 if (strlen(optarg) == 0) 203 exit_error(PARAMETER_PROBLEM, "Zero-length name?"); 204 strncpy(r->name, optarg, sizeof(r->name)); 205 *flags |= PARAM_NAME; 206 break; 207 default: 208 return 0; 209 } 210 211 if (invert) 212 exit_error(PARAMETER_PROBLEM, 213 "dstlimit does not support invert"); 214 215 return 1; 216} 217 218/* Final check; nothing. */ 219static void final_check(unsigned int flags) 220{ 221 if (!(flags & PARAM_LIMIT)) 222 exit_error(PARAMETER_PROBLEM, 223 "You have to specify --dstlimit"); 224 if (!(flags & PARAM_MODE)) 225 exit_error(PARAMETER_PROBLEM, 226 "You have to specify --dstlimit-mode"); 227 if (!(flags & PARAM_NAME)) 228 exit_error(PARAMETER_PROBLEM, 229 "You have to specify --dstlimit-name"); 230} 231 232static struct rates 233{ 234 const char *name; 235 u_int32_t mult; 236} rates[] = { { "day", IPT_DSTLIMIT_SCALE*24*60*60 }, 237 { "hour", IPT_DSTLIMIT_SCALE*60*60 }, 238 { "min", IPT_DSTLIMIT_SCALE*60 }, 239 { "sec", IPT_DSTLIMIT_SCALE } }; 240 241static void print_rate(u_int32_t period) 242{ 243 unsigned int i; 244 245 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) { 246 if (period > rates[i].mult 247 || rates[i].mult/period < rates[i].mult%period) 248 break; 249 } 250 251 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name); 252} 253 254/* Prints out the matchinfo. */ 255static void 256print(const struct ipt_ip *ip, 257 const struct ipt_entry_match *match, 258 int numeric) 259{ 260 struct ipt_dstlimit_info *r = 261 (struct ipt_dstlimit_info *)match->data; 262 printf("limit: avg "); print_rate(r->cfg.avg); 263 printf("burst %u ", r->cfg.burst); 264 switch (r->cfg.mode) { 265 case (IPT_DSTLIMIT_HASH_DIP): 266 printf("mode dstip "); 267 break; 268 case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT): 269 printf("mode dstip-dstport "); 270 break; 271 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP): 272 printf("mode srcip-dstip "); 273 break; 274 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT): 275 printf("mode srcip-dstip-dstport "); 276 break; 277 } 278 if (r->cfg.size) 279 printf("htable-size %u ", r->cfg.size); 280 if (r->cfg.max) 281 printf("htable-max %u ", r->cfg.max); 282 if (r->cfg.gc_interval != IPT_DSTLIMIT_GCINTERVAL) 283 printf("htable-gcinterval %u ", r->cfg.gc_interval); 284 if (r->cfg.expire != IPT_DSTLIMIT_EXPIRE) 285 printf("htable-expire %u ", r->cfg.expire); 286} 287 288/* FIXME: Make minimalist: only print rate if not default --RR */ 289static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) 290{ 291 struct ipt_dstlimit_info *r = 292 (struct ipt_dstlimit_info *)match->data; 293 294 printf("--dstlimit "); print_rate(r->cfg.avg); 295 if (r->cfg.burst != IPT_DSTLIMIT_BURST) 296 printf("--dstlimit-burst %u ", r->cfg.burst); 297 switch (r->cfg.mode) { 298 case (IPT_DSTLIMIT_HASH_DIP): 299 printf("--mode dstip "); 300 break; 301 case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT): 302 printf("--mode dstip-dstport "); 303 break; 304 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP): 305 printf("--mode srcip-dstip "); 306 break; 307 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT): 308 printf("--mode srcip-dstip-dstport "); 309 break; 310 } 311 if (r->cfg.size) 312 printf("--dstlimit-htable-size %u ", r->cfg.size); 313 if (r->cfg.max) 314 printf("--dstlimit-htable-max %u ", r->cfg.max); 315 if (r->cfg.gc_interval != IPT_DSTLIMIT_GCINTERVAL) 316 printf("--dstlimit-htable-gcinterval %u", r->cfg.gc_interval); 317 if (r->cfg.expire != IPT_DSTLIMIT_EXPIRE) 318 printf("--dstlimit-htable-expire %u ", r->cfg.expire); 319} 320 321static struct iptables_match dstlimit = { 322 .next = NULL, 323 .name = "dstlimit", 324 .version = IPTABLES_VERSION, 325 .size = IPT_ALIGN(sizeof(struct ipt_dstlimit_info)), 326 .userspacesize = IPT_ALIGN(sizeof(struct ipt_dstlimit_info)), 327 //offsetof(struct ipt_dstlimit_info, prev), 328 .help = &help, 329 .init = &init, 330 .parse = &parse, 331 .final_check = &final_check, 332 .print = &print, 333 .save = &save, 334 .extra_opts = opts 335}; 336 337void _init(void) 338{ 339 register_match(&dstlimit); 340} 341