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
);