1 /* iptables match extension for limiting packets per destination
3 * (C) 2003 by Harald Welte <laforge@netfilter.org>
5 * Development of this code was funded by Astaro AG, http://www.astaro.com/
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>
18 #include <linux/netfilter_ipv4/ip_tables.h>
19 #include <linux/netfilter_ipv4/ipt_dstlimit.h>
21 #define IPT_DSTLIMIT_BURST 5
24 #define IPT_DSTLIMIT_GCINTERVAL 1000
25 #define IPT_DSTLIMIT_EXPIRE 10000
27 /* Function which prints out usage message. */
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"
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);
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, '"' },
63 int parse_rate(const char *rate, u_int32_t *val)
67 u_int32_t mult = 1; /* Seconds by default. */
69 delim = strchr(rate, '/');
71 if (strlen(delim+1) == 0)
74 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
76 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
78 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
80 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
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);
94 *val = IPT_DSTLIMIT_SCALE * mult / r;
98 /* Initialize the match. */
100 init(struct ipt_entry_match *m, unsigned int *nfcache)
102 struct ipt_dstlimit_info *r = (struct ipt_dstlimit_info *)m->data;
104 r->cfg.burst = IPT_DSTLIMIT_BURST;
105 r->cfg.gc_interval = IPT_DSTLIMIT_GCINTERVAL;
106 r->cfg.expire = IPT_DSTLIMIT_EXPIRE;
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
119 /* Function which parses command options; returns true if it
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)
127 struct ipt_dstlimit_info *r =
128 (struct ipt_dstlimit_info *)(*match)->data;
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;
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);
146 *flags |= PARAM_BURST;
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);
154 *flags |= PARAM_SIZE;
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);
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'",
170 /* FIXME: not HZ dependent!! */
171 r->cfg.gc_interval = num;
172 *flags |= PARAM_GCINTERVAL;
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 */
181 *flags |= PARAM_EXPIRE;
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;
196 exit_error(PARAMETER_PROBLEM,
197 "bad --dstlimit-mode: `%s'\n", optarg);
198 *flags |= PARAM_MODE;
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;
212 exit_error(PARAMETER_PROBLEM,
213 "dstlimit does not support invert");
218 /* Final check; nothing. */
219 static void final_check(unsigned int flags)
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");
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 } };
241 static void print_rate(u_int32_t period)
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)
251 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
254 /* Prints out the matchinfo. */
256 print(const struct ipt_ip *ip,
257 const struct ipt_entry_match *match,
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 ");
268 case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
269 printf("mode dstip-dstport ");
271 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP):
272 printf("mode srcip-dstip ");
274 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
275 printf("mode srcip-dstip-dstport ");
279 printf("htable-size %u ", r->cfg.size);
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);
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)
291 struct ipt_dstlimit_info *r =
292 (struct ipt_dstlimit_info *)match->data;
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 ");
301 case (IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
302 printf("--mode dstip-dstport ");
304 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP):
305 printf("--mode srcip-dstip ");
307 case (IPT_DSTLIMIT_HASH_SIP|IPT_DSTLIMIT_HASH_DIP|IPT_DSTLIMIT_HASH_DPT):
308 printf("--mode srcip-dstip-dstport ");
312 printf("--dstlimit-htable-size %u ", r->cfg.size);
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);
321 static struct iptables_match 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),
331 .final_check = &final_check,
339 register_match(&dstlimit);