iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_dstlimit.c
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. */
28 static void
29 help(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
50 static 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
62 static
63 int 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. */
99 static void
100 init(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 */
121 static int
122 parse(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. */
219 static 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
232 static 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
241 static 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. */
255 static void
256 print(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 */
289 static 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
321 static 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
337 void _init(void)
338 {
339         register_match(&dstlimit);
340 }