blob: f785d2dfaf0fa021055f64436b68d9abf22f4bbe [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* 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 <xtables.h>
12#include <stddef.h>
13#include <linux/netfilter/x_tables.h>
14/* For 64bit kernel / 32bit userspace */
15#include <linux/netfilter/xt_limit.h>
16
17#define XT_LIMIT_AVG "3/hour"
18#define XT_LIMIT_BURST 5
19
20static void limit_help(void)
21{
22 printf(
23"limit match options:\n"
24"--limit avg max average match rate: default "XT_LIMIT_AVG"\n"
25" [Packets per second unless followed by \n"
26" /sec /minute /hour /day postfixes]\n"
27"--limit-burst number number to match in a burst, default %u\n",
28XT_LIMIT_BURST);
29}
30
31static const struct option limit_opts[] = {
32 { "limit", 1, NULL, '%' },
33 { "limit-burst", 1, NULL, '$' },
34 { .name = NULL }
35};
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). */
66 if (r / mult > XT_LIMIT_SCALE)
67 xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"\n", rate);
68
69 *val = XT_LIMIT_SCALE * mult / r;
70 return 1;
71}
72
73static void limit_init(struct xt_entry_match *m)
74{
75 struct xt_rateinfo *r = (struct xt_rateinfo *)m->data;
76
77 parse_rate(XT_LIMIT_AVG, &r->avg);
78 r->burst = XT_LIMIT_BURST;
79
80}
81
82/* FIXME: handle overflow:
83 if (r->avg*r->burst/r->burst != r->avg)
84 xtables_error(PARAMETER_PROBLEM,
85 "Sorry: burst too large for that avg rate.\n");
86*/
87
88static int
89limit_parse(int c, char **argv, int invert, unsigned int *flags,
90 const void *entry, struct xt_entry_match **match)
91{
92 struct xt_rateinfo *r = (struct xt_rateinfo *)(*match)->data;
93 unsigned int num;
94
95 switch(c) {
96 case '%':
97 if (xtables_check_inverse(argv[optind-1], &invert, &optind, 0)) break;
98 if (!parse_rate(optarg, &r->avg))
99 xtables_error(PARAMETER_PROBLEM,
100 "bad rate `%s'", optarg);
101 break;
102
103 case '$':
104 if (xtables_check_inverse(argv[optind-1], &invert, &optind, 0)) break;
105 if (!xtables_strtoui(optarg, NULL, &num, 0, 10000))
106 xtables_error(PARAMETER_PROBLEM,
107 "bad --limit-burst `%s'", optarg);
108 r->burst = num;
109 break;
110
111 default:
112 return 0;
113 }
114
115 if (invert)
116 xtables_error(PARAMETER_PROBLEM,
117 "limit does not support invert");
118
119 return 1;
120}
121
122static const struct rates
123{
124 const char *name;
125 u_int32_t mult;
126} 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 } };
130
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
137 || rates[i].mult/period < rates[i].mult%period)
138 break;
139 }
140
141 printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
142}
143
144static void
145limit_print(const void *ip, const struct xt_entry_match *match, int numeric)
146{
147 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
148 printf("limit: avg "); print_rate(r->avg);
149 printf("burst %u ", r->burst);
150}
151
152static void limit_save(const void *ip, const struct xt_entry_match *match)
153{
154 struct xt_rateinfo *r = (struct xt_rateinfo *)match->data;
155
156 printf("--limit "); print_rate(r->avg);
157 if (r->burst != XT_LIMIT_BURST)
158 printf("--limit-burst %u ", r->burst);
159}
160
161static struct xtables_match limit_match = {
162 .family = AF_UNSPEC,
163 .name = "limit",
164 .version = XTABLES_VERSION,
165 .size = XT_ALIGN(sizeof(struct xt_rateinfo)),
166 .userspacesize = offsetof(struct xt_rateinfo, prev),
167 .help = limit_help,
168 .init = limit_init,
169 .parse = limit_parse,
170 .print = limit_print,
171 .save = limit_save,
172 .extra_opts = limit_opts,
173};
174
175void _init(void)
176{
177 xtables_register_match(&limit_match);
178}