blob: 1df9114e03b2e147f76e36580e7749e26b89ca1a [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Shared library add-on to iptables to add limit support.
2 *
Jan Engelhardt81bd5882008-09-04 17:49:18 +02003 * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
4 * 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 */
Jan Engelhardta2a7f2b2008-09-01 14:20:13 +020015#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
Jan Engelhardt181dead2007-10-04 16:27:07 +000020static void limit_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000021{
22 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020023"limit match options:\n"
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000024"--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000025" [Packets per second unless followed by \n"
26" /sec /minute /hour /day postfixes]\n"
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020027"--limit-burst number number to match in a burst, default %u\n",
28XT_LIMIT_BURST);
Marc Bouchere6869a82000-03-20 06:03:29 +000029}
30
Jan Engelhardt181dead2007-10-04 16:27:07 +000031static const struct option limit_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000032 { "limit", 1, NULL, '%' },
33 { "limit-burst", 1, NULL, '$' },
Max Kellermann9ee386a2008-01-29 13:48:05 +000034 { .name = NULL }
Marc Bouchere6869a82000-03-20 06:03:29 +000035};
36
37static
38int parse_rate(const char *rate, u_int32_t *val)
39{
40 const char *delim;
41 u_int32_t r;
42 u_int32_t mult = 1; /* Seconds by default. */
43
44 delim = strchr(rate, '/');
45 if (delim) {
46 if (strlen(delim+1) == 0)
47 return 0;
48
49 if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
50 mult = 1;
51 else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
52 mult = 60;
53 else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
54 mult = 60*60;
55 else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
56 mult = 24*60*60;
57 else
58 return 0;
59 }
60 r = atoi(rate);
61 if (!r)
62 return 0;
63
64 /* This would get mapped to infinite (1/day is minimum they
65 can specify, so we're ok at that end). */
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000066 if (r / mult > XT_LIMIT_SCALE)
Marc Bouchere6869a82000-03-20 06:03:29 +000067 exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
68
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000069 *val = XT_LIMIT_SCALE * mult / r;
Marc Bouchere6869a82000-03-20 06:03:29 +000070 return 1;
71}
72
Jan Engelhardt181dead2007-10-04 16:27:07 +000073static void limit_init(struct xt_entry_match *m)
Marc Bouchere6869a82000-03-20 06:03:29 +000074{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000075 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
Marc Bouchere6869a82000-03-20 06:03:29 +000076
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000077 parse_rate(XT_LIMIT_AVG, &r->avg);
78 r->burst = XT_LIMIT_BURST;
Marc Bouchere6869a82000-03-20 06:03:29 +000079
Marc Bouchere6869a82000-03-20 06:03:29 +000080}
81
82/* FIXME: handle overflow:
83 if (r->avg*r->burst/r->burst != r->avg)
84 exit_error(PARAMETER_PROBLEM,
85 "Sorry: burst too large for that avg rate.\n");
86*/
87
Marc Bouchere6869a82000-03-20 06:03:29 +000088static int
Jan Engelhardt181dead2007-10-04 16:27:07 +000089limit_parse(int c, char **argv, int invert, unsigned int *flags,
90 const void *entry, struct xt_entry_match **match)
Marc Bouchere6869a82000-03-20 06:03:29 +000091{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +000092 struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
Harald Welteb4719762001-07-23 02:14:22 +000093 unsigned int num;
Marc Bouchere6869a82000-03-20 06:03:29 +000094
95 switch(c) {
96 case '%':
Phil Oester35160ee2004-09-21 10:43:45 +000097 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Marc Bouchere6869a82000-03-20 06:03:29 +000098 if (!parse_rate(optarg, &r->avg))
99 exit_error(PARAMETER_PROBLEM,
100 "bad rate `%s'", optarg);
Rusty Russell7e53bf92000-03-20 07:03:28 +0000101 break;
Marc Bouchere6869a82000-03-20 06:03:29 +0000102
103 case '$':
Phil Oester35160ee2004-09-21 10:43:45 +0000104 if (check_inverse(argv[optind-1], &invert, &optind, 0)) break;
Jan Engelhardt5f2922c2009-01-27 18:43:01 +0100105 if (!xtables_strtoui(optarg, NULL, &num, 0, 10000))
Marc Bouchere6869a82000-03-20 06:03:29 +0000106 exit_error(PARAMETER_PROBLEM,
107 "bad --limit-burst `%s'", optarg);
108 r->burst = num;
109 break;
110
111 default:
112 return 0;
113 }
114
Phil Oester35160ee2004-09-21 10:43:45 +0000115 if (invert)
116 exit_error(PARAMETER_PROBLEM,
117 "limit does not support invert");
118
Marc Bouchere6869a82000-03-20 06:03:29 +0000119 return 1;
120}
121
Jan Engelhardt0e2abed2007-10-04 16:25:58 +0000122static const struct rates
Marc Bouchere6869a82000-03-20 06:03:29 +0000123{
124 const char *name;
125 u_int32_t mult;
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000126} rates[] = { { "day", XT_LIMIT_SCALE*24*60*60 },
127 { "hour", XT_LIMIT_SCALE*60*60 },
128 { "min", XT_LIMIT_SCALE*60 },
129 { "sec", XT_LIMIT_SCALE } };
Marc Bouchere6869a82000-03-20 06:03:29 +0000130
131static void print_rate(u_int32_t period)
132{
133 unsigned int i;
134
135 for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
136 if (period > rates[i].mult
Harald Welte1412e452001-10-16 08:26:37 +0000137 || rates[i].mult/period < rates[i].mult%period)
Marc Bouchere6869a82000-03-20 06:03:29 +0000138 break;
139 }
140
141 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
142}
143
Marc Bouchere6869a82000-03-20 06:03:29 +0000144static void
Jan Engelhardt181dead2007-10-04 16:27:07 +0000145limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000146{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000147 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000148 printf("limit: avg "); print_rate(r->avg);
149 printf("burst %u ", r->burst);
150}
151
Jan Engelhardt181dead2007-10-04 16:27:07 +0000152static void limit_save(const void *ip, const struct xt_entry_match *match)
Marc Bouchere6869a82000-03-20 06:03:29 +0000153{
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000154 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000155
156 printf("--limit "); print_rate(r->avg);
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000157 if (r->burst != XT_LIMIT_BURST)
Marc Bouchere6869a82000-03-20 06:03:29 +0000158 printf("--limit-burst %u ", r->burst);
159}
160
Jan Engelhardt181dead2007-10-04 16:27:07 +0000161static struct xtables_match limit_match = {
Jan Engelhardt23545c22008-02-14 04:23:04 +0100162 .family = AF_UNSPEC,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000163 .name = "limit",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200164 .version = XTABLES_VERSION,
Yasuyuki KOZAKAI4489c0d2007-07-24 07:11:26 +0000165 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
166 .userspacesize = offsetof(struct xt_rateinfo, prev),
Jan Engelhardt181dead2007-10-04 16:27:07 +0000167 .help = limit_help,
168 .init = limit_init,
169 .parse = limit_parse,
170 .print = limit_print,
171 .save = limit_save,
172 .extra_opts = limit_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000173};
174
175void _init(void)
176{
Jan Engelhardt181dead2007-10-04 16:27:07 +0000177 xtables_register_match(&limit_match);
Marc Bouchere6869a82000-03-20 06:03:29 +0000178}