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 <iptables.h>
12 #include <stddef.h>
13 #include <linux/netfilter_ipv4/ip_tables.h>
14 /* For 64bit kernel / 32bit userspace */
15 #include "../include/linux/netfilter_ipv4/ipt_limit.h"
16
17 #define IPT_LIMIT_AVG "3/hour"
18 #define IPT_LIMIT_BURST 5
19
20 /* Function which prints out usage message. */
21 static void
help(void)22 help(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"
30 "\n", IPTABLES_VERSION, IPT_LIMIT_BURST);
31 }
32
33 static struct option opts[] = {
34 { "limit", 1, 0, '%' },
35 { "limit-burst", 1, 0, '$' },
36 { 0 }
37 };
38
39 static
parse_rate(const char * rate,u_int32_t * val)40 int 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. */
76 static void
init(struct ipt_entry_match * m,unsigned int * nfcache)77 init(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 }
85
86 /* FIXME: handle overflow:
87 if (r->avg*r->burst/r->burst != r->avg)
88 exit_error(PARAMETER_PROBLEM,
89 "Sorry: burst too large for that avg rate.\n");
90 */
91
92 /* Function which parses command options; returns true if it
93 ate an option */
94 static int
parse(int c,char ** argv,int invert,unsigned int * flags,const struct ipt_entry * entry,unsigned int * nfcache,struct ipt_entry_match ** match)95 parse(int c, char **argv, int invert, unsigned int *flags,
96 const struct ipt_entry *entry,
97 unsigned int *nfcache,
98 struct ipt_entry_match **match)
99 {
100 struct ipt_rateinfo *r = (struct ipt_rateinfo *)(*match)->data;
101 unsigned int num;
102
103 switch(c) {
104 case '%':
105 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
106 if (!parse_rate(optarg, &r->avg))
107 exit_error(PARAMETER_PROBLEM,
108 "bad rate `%s'", optarg);
109 break;
110
111 case '$':
112 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
113 if (string_to_number(optarg, 0, 10000, &num) == -1)
114 exit_error(PARAMETER_PROBLEM,
115 "bad --limit-burst `%s'", optarg);
116 r->burst = num;
117 break;
118
119 default:
120 return 0;
121 }
122
123 if (invert)
124 exit_error(PARAMETER_PROBLEM,
125 "limit does not support invert");
126
127 return 1;
128 }
129
130 /* Final check; nothing. */
final_check(unsigned int flags)131 static void final_check(unsigned int flags)
132 {
133 }
134
135 static struct rates
136 {
137 const char *name;
138 u_int32_t mult;
139 } rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
140 { "hour", IPT_LIMIT_SCALE*60*60 },
141 { "min", IPT_LIMIT_SCALE*60 },
142 { "sec", IPT_LIMIT_SCALE } };
143
print_rate(u_int32_t period)144 static void print_rate(u_int32_t period)
145 {
146 unsigned int i;
147
148 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
149 if (period > rates[i].mult
150 || rates[i].mult/period < rates[i].mult%period)
151 break;
152 }
153
154 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
155 }
156
157 /* Prints out the matchinfo. */
158 static void
print(const struct ipt_ip * ip,const struct ipt_entry_match * match,int numeric)159 print(const struct ipt_ip *ip,
160 const struct ipt_entry_match *match,
161 int numeric)
162 {
163 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
164 printf("limit: avg "); print_rate(r->avg);
165 printf("burst %u ", r->burst);
166 }
167
168 /* FIXME: Make minimalist: only print rate if not default --RR */
save(const struct ipt_ip * ip,const struct ipt_entry_match * match)169 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
170 {
171 struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
172
173 printf("--limit "); print_rate(r->avg);
174 if (r->burst != IPT_LIMIT_BURST)
175 printf("--limit-burst %u ", r->burst);
176 }
177
178 static struct iptables_match limit = {
179 .next = NULL,
180 .name = "limit",
181 .version = IPTABLES_VERSION,
182 .size = IPT_ALIGN(sizeof(struct ipt_rateinfo)),
183 .userspacesize = offsetof(struct ipt_rateinfo, prev),
184 .help = &help,
185 .init = &init,
186 .parse = &parse,
187 .final_check = &final_check,
188 .print = &print,
189 .save = &save,
190 .extra_opts = opts
191 };
192
ipt_limit_init(void)193 void ipt_limit_init(void)
194 {
195 register_match(&limit);
196 }
197