blob: 6c88ee1cea928ba49e5266e0fbf727c26438d671 [file] [log] [blame]
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +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>
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +00005 */
Harald Welteb77f1da2002-03-14 11:35:58 +00006
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +00007#include <stdio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <getopt.h>
11#include <ip6tables.h>
12#include <stddef.h>
13#include <linux/netfilter_ipv6/ip6_tables.h>
Martin Josefsson1da399c2004-05-26 15:50:57 +000014/* For 64bit kernel / 32bit userspace */
15#include "../include/linux/netfilter_ipv6/ip6t_limit.h"
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +000016
17#define IP6T_LIMIT_AVG "3/hour"
18#define IP6T_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 "IP6T_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, IP6T_LIMIT_BURST);
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +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 > IP6T_LIMIT_SCALE)
69 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
70
71 *val = IP6T_LIMIT_SCALE * mult / r;
72 return 1;
73}
74
75/* Initialize the match. */
76static void
77init(struct ip6t_entry_match *m, unsigned int *nfcache)
78{
79 struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)m->data;
80
81 parse_rate(IP6T_LIMIT_AVG, &r->avg);
82 r->burst = IP6T_LIMIT_BURST;
83
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +000084}
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 */
94static int
95parse(int c, char **argv, int invert, unsigned int *flags,
96 const struct ip6t_entry *entry,
97 unsigned int *nfcache,
98 struct ip6t_entry_match **match)
99{
100 struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)(*match)->data;
Harald Welteb4719762001-07-23 02:14:22 +0000101 unsigned int num;
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +0000102
103 switch(c) {
104 case '%':
Phil Oester57ff0b42004-09-21 10:49:13 +0000105 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +0000106 if (!parse_rate(optarg, &r->avg))
107 exit_error(PARAMETER_PROBLEM,
108 "bad rate `%s'", optarg);
109 break;
110
111 case '$':
Phil Oester57ff0b42004-09-21 10:49:13 +0000112 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Marc Boucher06e63942001-07-23 15:34:46 +0000113 if (string_to_number(optarg, 0, 10000, &num) == -1)
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +0000114 exit_error(PARAMETER_PROBLEM,
115 "bad --limit-burst `%s'", optarg);
116 r->burst = num;
117 break;
118
119 default:
120 return 0;
121 }
122
Phil Oester57ff0b42004-09-21 10:49:13 +0000123 if (invert)
124 exit_error(PARAMETER_PROBLEM,
125 "limit does not support invert");
126
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +0000127 return 1;
128}
129
130/* Final check; nothing. */
131static void final_check(unsigned int flags)
132{
133}
134
135static struct rates
136{
137 const char *name;
138 u_int32_t mult;
139} rates[] = { { "day", IP6T_LIMIT_SCALE*24*60*60 },
140 { "hour", IP6T_LIMIT_SCALE*60*60 },
141 { "min", IP6T_LIMIT_SCALE*60 },
142 { "sec", IP6T_LIMIT_SCALE } };
143
144static 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 != 0)
151 break;
152 }
153
154 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
155}
156
157/* Prints out the matchinfo. */
158static void
159print(const struct ip6t_ip6 *ip,
160 const struct ip6t_entry_match *match,
161 int numeric)
162{
163 struct ip6t_rateinfo *r = (struct ip6t_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 */
169static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
170{
171 struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
172
173 printf("--limit "); print_rate(r->avg);
174 if (r->burst != IP6T_LIMIT_BURST)
175 printf("--limit-burst %u ", r->burst);
176}
177
Harald Welte02aa7332005-02-01 15:38:20 +0000178static struct ip6tables_match limit = {
179 .name = "limit",
180 .version = IPTABLES_VERSION,
181 .size = IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
182 .userspacesize = offsetof(struct ip6t_rateinfo, prev),
183 .help = &help,
184 .init = &init,
185 .parse = &parse,
186 .final_check = &final_check,
187 .print = &print,
188 .save = &save,
189 .extra_opts = opts,
Jan Rekorajskidc8af0f2001-02-16 15:19:51 +0000190};
191
192void _init(void)
193{
194 register_match6(&limit);
195}