changing trunk/trunk to trunk
[iptables.git] / extensions / libxt_limit.c
1 /* Shared library add-on to iptables to add limit support.
2  *
3  * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
4  * Hervé Eychenne    <rv@wallfire.org>
5  */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <getopt.h>
11 #include <xtables.h>
12 #include <stddef.h>
13 #include <linux/netfilter/x_tables.h>
14 /* For 64bit kernel / 32bit userspace */
15 #include "../include/linux/netfilter/xt_limit.h"
16
17 #define XT_LIMIT_AVG    "3/hour"
18 #define XT_LIMIT_BURST  5
19
20 /* Function which prints out usage message. */
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 option limit_opts[] = {
33         { "limit", 1, NULL, '%' },
34         { "limit-burst", 1, NULL, '$' },
35         { .name = NULL }
36 };
37
38 static
39 int parse_rate(const char *rate, u_int32_t *val)
40 {
41         const char *delim;
42         u_int32_t r;
43         u_int32_t mult = 1;  /* Seconds by default. */
44
45         delim = strchr(rate, '/');
46         if (delim) {
47                 if (strlen(delim+1) == 0)
48                         return 0;
49
50                 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
51                         mult = 1;
52                 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
53                         mult = 60;
54                 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
55                         mult = 60*60;
56                 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
57                         mult = 24*60*60;
58                 else
59                         return 0;
60         }
61         r = atoi(rate);
62         if (!r)
63                 return 0;
64
65         /* This would get mapped to infinite (1/day is minimum they
66            can specify, so we're ok at that end). */
67         if (r / mult > XT_LIMIT_SCALE)
68                 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
69
70         *val = XT_LIMIT_SCALE * mult / r;
71         return 1;
72 }
73
74 /* Initialize the match. */
75 static void limit_init(struct xt_entry_match *m)
76 {
77         struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
78
79         parse_rate(XT_LIMIT_AVG, &r->avg);
80         r->burst = XT_LIMIT_BURST;
81
82 }
83
84 /* FIXME: handle overflow:
85         if (r->avg*r->burst/r->burst != r->avg)
86                 exit_error(PARAMETER_PROBLEM,
87                            "Sorry: burst too large for that avg rate.\n");
88 */
89
90 /* Function which parses command options; returns true if it
91    ate an option */
92 static int
93 limit_parse(int c, char **argv, int invert, unsigned int *flags,
94             const void *entry, struct xt_entry_match **match)
95 {
96         struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
97         unsigned int num;
98
99         switch(c) {
100         case '%':
101                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
102                 if (!parse_rate(optarg, &r->avg))
103                         exit_error(PARAMETER_PROBLEM,
104                                    "bad rate `%s'", optarg);
105                 break;
106
107         case '$':
108                 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
109                 if (string_to_number(optarg, 0, 10000, &num) == -1)
110                         exit_error(PARAMETER_PROBLEM,
111                                    "bad --limit-burst `%s'", optarg);
112                 r->burst = num;
113                 break;
114
115         default:
116                 return 0;
117         }
118
119         if (invert)
120                 exit_error(PARAMETER_PROBLEM,
121                            "limit does not support invert");
122
123         return 1;
124 }
125
126 static const struct rates
127 {
128         const char *name;
129         u_int32_t mult;
130 } rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
131               { "hour", XT_LIMIT_SCALE*60*60 },
132               { "min", XT_LIMIT_SCALE*60 },
133               { "sec", XT_LIMIT_SCALE } };
134
135 static void print_rate(u_int32_t period)
136 {
137         unsigned int i;
138
139         for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
140                 if (period > rates[i].mult
141             || rates[i].mult/period < rates[i].mult%period)
142                         break;
143         }
144
145         printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
146 }
147
148 /* Prints out the matchinfo. */
149 static void
150 limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
151 {
152         struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
153         printf("limit: avg "); print_rate(r->avg);
154         printf("burst %u ", r->burst);
155 }
156
157 /* FIXME: Make minimalist: only print rate if not default --RR */
158 static void limit_save(const void *ip, const struct xt_entry_match *match)
159 {
160         struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
161
162         printf("--limit "); print_rate(r->avg);
163         if (r->burst != XT_LIMIT_BURST)
164                 printf("--limit-burst %u ", r->burst);
165 }
166
167 static struct xtables_match limit_match = {
168         .family         = AF_UNSPEC,
169         .name           = "limit",
170         .version        = XTABLES_VERSION,
171         .size           = XT_ALIGN(sizeof(struct xt_rateinfo)),
172         .userspacesize  = offsetof(struct xt_rateinfo, prev),
173         .help           = limit_help,
174         .init           = limit_init,
175         .parse          = limit_parse,
176         .print          = limit_print,
177         .save           = limit_save,
178         .extra_opts     = limit_opts,
179 };
180
181 void _init(void)
182 {
183         xtables_register_match(&limit_match);
184 }