blob: 65a8df3ffc4990f2ec72c619d09391eeb21fa1f2 [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Shared library add-on to iptables to add limit support.
2 *
3 * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
Harald Welteb77f1da2002-03-14 11:35:58 +00004 * Hervé Eychenne <rv@wallfire.org>
Marc Bouchere6869a82000-03-20 06:03:29 +00005 */
Harald Welteb77f1da2002-03-14 11:35:58 +00006
Marc Bouchere6869a82000-03-20 06:03:29 +00007#include <stdio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <getopt.h>
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000011#include <xtables.h>
Rusty Russell849779c2000-04-23 15:51:51 +000012#include <stddef.h>
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000013#include <linux/netfilter/x_tables.h>
Martin Josefsson1da399c2004-05-26 15:50:57 +000014/* For 64bit kernel / 32bit userspace */
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000015#include "../include/linux/netfilter/xt_limit.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000016
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000017#define XT_LIMIT_AVG "3/hour"
18#define XT_LIMIT_BURST 5
Marc Bouchere6869a82000-03-20 06:03:29 +000019
20/* Function which prints out usage message. */
Jan Engelhardt181dead2007-10-04 16:27:07 +000021static void limit_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000022{
23 printf(
24"limit v%s options:\n"
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000025"--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000026" [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"
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000029"\n", IPTABLES_VERSION, XT_LIMIT_BURST);
Marc Bouchere6869a82000-03-20 06:03:29 +000030}
31
Jan Engelhardt181dead2007-10-04 16:27:07 +000032static const struct option limit_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000033 { "limit", 1, NULL, '%' },
34 { "limit-burst", 1, NULL, '$' },
Max Kellermann9ee386a2008-01-29 13:48:05 +000035 { .name = NULL }
Marc Bouchere6869a82000-03-20 06:03:29 +000036};
37
38static
39int 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). */
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000067 if (r / mult > XT_LIMIT_SCALE)
Marc Bouchere6869a82000-03-20 06:03:29 +000068 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
69
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000070 *val = XT_LIMIT_SCALE * mult / r;
Marc Bouchere6869a82000-03-20 06:03:29 +000071 return 1;
72}
73
74/* Initialize the match. */
Jan Engelhardt181dead2007-10-04 16:27:07 +000075static void limit_init(struct xt_entry_match *m)
Marc Bouchere6869a82000-03-20 06:03:29 +000076{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000077 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
Marc Bouchere6869a82000-03-20 06:03:29 +000078
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000079 parse_rate(XT_LIMIT_AVG, &r->avg);
80 r->burst = XT_LIMIT_BURST;
Marc Bouchere6869a82000-03-20 06:03:29 +000081
Marc Bouchere6869a82000-03-20 06:03:29 +000082}
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 */
92static int
Jan Engelhardt181dead2007-10-04 16:27:07 +000093limit_parse(int c, char **argv, int invert, unsigned int *flags,
94 const void *entry, struct xt_entry_match **match)
Marc Bouchere6869a82000-03-20 06:03:29 +000095{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000096 struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
Harald Welteb4719762001-07-23 02:14:22 +000097 unsigned int num;
Marc Bouchere6869a82000-03-20 06:03:29 +000098
99 switch(c) {
100 case '%':
Phil Oester35160ee2004-09-21 10:43:45 +0000101 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000102 if (!parse_rate(optarg, &r->avg))
103 exit_error(PARAMETER_PROBLEM,
104 "bad rate `%s'", optarg);
Rusty Russell7e53bf92000-03-20 07:03:28 +0000105 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000106
107 case '$':
Phil Oester35160ee2004-09-21 10:43:45 +0000108 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Marc Boucher06e63942001-07-23 15:34:46 +0000109 if (string_to_number(optarg, 0, 10000, &num) == -1)
Marc Bouchere6869a82000-03-20 06:03:29 +0000110 exit_error(PARAMETER_PROBLEM,
111 "bad --limit-burst `%s'", optarg);
112 r->burst = num;
113 break;
114
115 default:
116 return 0;
117 }
118
Phil Oester35160ee2004-09-21 10:43:45 +0000119 if (invert)
120 exit_error(PARAMETER_PROBLEM,
121 "limit does not support invert");
122
Marc Bouchere6869a82000-03-20 06:03:29 +0000123 return 1;
124}
125
Jan Engelhardt0e2abed2007-10-04 16:25:58 +0000126static const struct rates
Marc Bouchere6869a82000-03-20 06:03:29 +0000127{
128 const char *name;
129 u_int32_t mult;
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000130} 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 } };
Marc Bouchere6869a82000-03-20 06:03:29 +0000134
135static 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
Harald Welte1412e452001-10-16 08:26:37 +0000141 || rates[i].mult/period < rates[i].mult%period)
Marc Bouchere6869a82000-03-20 06:03:29 +0000142 break;
143 }
144
145 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
146}
147
148/* Prints out the matchinfo. */
149static void
Jan Engelhardt181dead2007-10-04 16:27:07 +0000150limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000151{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000152 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000153 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 */
Jan Engelhardt181dead2007-10-04 16:27:07 +0000158static void limit_save(const void *ip, const struct xt_entry_match *match)
Marc Bouchere6869a82000-03-20 06:03:29 +0000159{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000160 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000161
162 printf("--limit "); print_rate(r->avg);
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000163 if (r->burst != XT_LIMIT_BURST)
Marc Bouchere6869a82000-03-20 06:03:29 +0000164 printf("--limit-burst %u ", r->burst);
165}
166
Jan Engelhardt181dead2007-10-04 16:27:07 +0000167static struct xtables_match limit_match = {
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000168 .family = AF_INET,
Pablo Neira8caee8b2004-12-28 13:11:59 +0000169 .name = "limit",
170 .version = IPTABLES_VERSION,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000171 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
172 .userspacesize = offsetof(struct xt_rateinfo, prev),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000173 .help = limit_help,
174 .init = limit_init,
175 .parse = limit_parse,
176 .print = limit_print,
177 .save = limit_save,
178 .extra_opts = limit_opts,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000179};
180
Jan Engelhardt181dead2007-10-04 16:27:07 +0000181static struct xtables_match limit_match6 = {
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000182 .family = AF_INET6,
183 .name = "limit",
184 .version = IPTABLES_VERSION,
185 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
186 .userspacesize = offsetof(struct xt_rateinfo, prev),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000187 .help = limit_help,
188 .init = limit_init,
189 .parse = limit_parse,
190 .print = limit_print,
191 .save = limit_save,
192 .extra_opts = limit_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000193};
194
195void _init(void)
196{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000197 xtables_register_match(&limit_match);
198 xtables_register_match(&limit_match6);
Marc Bouchere6869a82000-03-20 06:03:29 +0000199}