blob: 4d52040c7a1a34823a226a3843d6e47b8666fdda [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>
11#include <iptables.h>
Rusty Russell849779c2000-04-23 15:51:51 +000012#include <stddef.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000013#include <linux/netfilter_ipv4/ip_tables.h>
Martin Josefsson1da399c2004-05-26 15:50:57 +000014/* For 64bit kernel / 32bit userspace */
15#include "../include/linux/netfilter_ipv4/ipt_limit.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000016
17#define IPT_LIMIT_AVG "3/hour"
18#define IPT_LIMIT_BURST 5
19
20/* Function which prints out usage message. */
21static void
22help(void)
23{
24 printf(
25"limit v%s options:\n"
26"--limit avg max average match rate: default "IPT_LIMIT_AVG"\n"
27" [Packets per second unless followed by \n"
28" /sec /minute /hour /day postfixes]\n"
29"--limit-burst number number to match in a burst, default %u\n"
Harald Welte80fe35d2002-05-29 13:08:15 +000030"\n", IPTABLES_VERSION, IPT_LIMIT_BURST);
Marc Bouchere6869a82000-03-20 06:03:29 +000031}
32
33static struct option opts[] = {
34 { "limit", 1, 0, '%' },
35 { "limit-burst", 1, 0, '$' },
36 { 0 }
37};
38
39static
40int parse_rate(const char *rate, u_int32_t *val)
41{
42 const char *delim;
43 u_int32_t r;
44 u_int32_t mult = 1; /* Seconds by default. */
45
46 delim = strchr(rate, '/');
47 if (delim) {
48 if (strlen(delim+1) == 0)
49 return 0;
50
51 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
52 mult = 1;
53 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
54 mult = 60;
55 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
56 mult = 60*60;
57 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
58 mult = 24*60*60;
59 else
60 return 0;
61 }
62 r = atoi(rate);
63 if (!r)
64 return 0;
65
66 /* This would get mapped to infinite (1/day is minimum they
67 can specify, so we're ok at that end). */
68 if (r / mult > IPT_LIMIT_SCALE)
69 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
70
71 *val = IPT_LIMIT_SCALE * mult / r;
72 return 1;
73}
74
75/* Initialize the match. */
76static void
77init(struct ipt_entry_match *m, unsigned int *nfcache)
78{
79 struct ipt_rateinfo *r = (struct ipt_rateinfo *)m->data;
80
81 parse_rate(IPT_LIMIT_AVG, &r->avg);
82 r->burst = IPT_LIMIT_BURST;
83
84 /* Can't cache this */
85 *nfcache |= NFC_UNKNOWN;
86}
87
88/* FIXME: handle overflow:
89 if (r->avg*r->burst/r->burst != r->avg)
90 exit_error(PARAMETER_PROBLEM,
91 "Sorry: burst too large for that avg rate.\n");
92*/
93
94/* Function which parses command options; returns true if it
95 ate an option */
96static int
97parse(int c, char **argv, int invert, unsigned int *flags,
98 const struct ipt_entry *entry,
99 unsigned int *nfcache,
100 struct ipt_entry_match **match)
101{
102 struct ipt_rateinfo *r = (struct ipt_rateinfo *)(*match)->data;
Harald Welteb4719762001-07-23 02:14:22 +0000103 unsigned int num;
Marc Bouchere6869a82000-03-20 06:03:29 +0000104
105 switch(c) {
106 case '%':
Harald Welteb77f1da2002-03-14 11:35:58 +0000107 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000108 exit_error(PARAMETER_PROBLEM,
109 "Unexpected `!' after --limit");
110 if (!parse_rate(optarg, &r->avg))
111 exit_error(PARAMETER_PROBLEM,
112 "bad rate `%s'", optarg);
Rusty Russell7e53bf92000-03-20 07:03:28 +0000113 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000114
115 case '$':
Harald Welteb77f1da2002-03-14 11:35:58 +0000116 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000117 exit_error(PARAMETER_PROBLEM,
118 "Unexpected `!' after --limit-burst");
119
Marc Boucher06e63942001-07-23 15:34:46 +0000120 if (string_to_number(optarg, 0, 10000, &num) == -1)
Marc Bouchere6869a82000-03-20 06:03:29 +0000121 exit_error(PARAMETER_PROBLEM,
122 "bad --limit-burst `%s'", optarg);
123 r->burst = num;
124 break;
125
126 default:
127 return 0;
128 }
129
130 return 1;
131}
132
133/* Final check; nothing. */
134static void final_check(unsigned int flags)
135{
136}
137
138static struct rates
139{
140 const char *name;
141 u_int32_t mult;
142} rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
143 { "hour", IPT_LIMIT_SCALE*60*60 },
144 { "min", IPT_LIMIT_SCALE*60 },
145 { "sec", IPT_LIMIT_SCALE } };
146
147static void print_rate(u_int32_t period)
148{
149 unsigned int i;
150
151 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
152 if (period > rates[i].mult
Harald Welte1412e452001-10-16 08:26:37 +0000153 || rates[i].mult/period < rates[i].mult%period)
Marc Bouchere6869a82000-03-20 06:03:29 +0000154 break;
155 }
156
157 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
158}
159
160/* Prints out the matchinfo. */
161static void
162print(const struct ipt_ip *ip,
163 const struct ipt_entry_match *match,
164 int numeric)
165{
166 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
167 printf("limit: avg "); print_rate(r->avg);
168 printf("burst %u ", r->burst);
169}
170
171/* FIXME: Make minimalist: only print rate if not default --RR */
172static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
173{
174 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
175
176 printf("--limit "); print_rate(r->avg);
177 if (r->burst != IPT_LIMIT_BURST)
178 printf("--limit-burst %u ", r->burst);
179}
180
Harald Welte3efb6ea2001-08-06 18:50:21 +0000181static
Marc Bouchere6869a82000-03-20 06:03:29 +0000182struct iptables_match limit
183= { NULL,
184 "limit",
Harald Welte80fe35d2002-05-29 13:08:15 +0000185 IPTABLES_VERSION,
Rusty Russell73f72f52000-07-03 10:17:57 +0000186 IPT_ALIGN(sizeof(struct ipt_rateinfo)),
Rusty Russelledf14cf2000-04-19 11:26:44 +0000187 offsetof(struct ipt_rateinfo, prev),
Marc Bouchere6869a82000-03-20 06:03:29 +0000188 &help,
189 &init,
190 &parse,
191 &final_check,
192 &print,
193 &save,
194 opts
195};
196
197void _init(void)
198{
199 register_match(&limit);
200}