xref: /aosp_15_r20/external/iptables/extensions/libxt_hashlimit.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1*a71a9546SAutomerger Merge Worker /* ip6tables match extension for limiting packets per destination
2*a71a9546SAutomerger Merge Worker  *
3*a71a9546SAutomerger Merge Worker  * (C) 2003-2004 by Harald Welte <[email protected]>
4*a71a9546SAutomerger Merge Worker  *
5*a71a9546SAutomerger Merge Worker  * Development of this code was funded by Astaro AG, http://www.astaro.com/
6*a71a9546SAutomerger Merge Worker  *
7*a71a9546SAutomerger Merge Worker  * Based on ipt_limit.c by
8*a71a9546SAutomerger Merge Worker  * Jérôme de Vivie   <[email protected]>
9*a71a9546SAutomerger Merge Worker  * Hervé Eychenne    <[email protected]>
10*a71a9546SAutomerger Merge Worker  *
11*a71a9546SAutomerger Merge Worker  * Error corections by [email protected] (22.01.2005)
12*a71a9546SAutomerger Merge Worker  */
13*a71a9546SAutomerger Merge Worker #define _BSD_SOURCE 1
14*a71a9546SAutomerger Merge Worker #define _DEFAULT_SOURCE 1
15*a71a9546SAutomerger Merge Worker #define _ISOC99_SOURCE 1
16*a71a9546SAutomerger Merge Worker #include <inttypes.h>
17*a71a9546SAutomerger Merge Worker #include <math.h>
18*a71a9546SAutomerger Merge Worker #include <stdbool.h>
19*a71a9546SAutomerger Merge Worker #include <stdio.h>
20*a71a9546SAutomerger Merge Worker #include <string.h>
21*a71a9546SAutomerger Merge Worker #include <stdlib.h>
22*a71a9546SAutomerger Merge Worker #include <errno.h>
23*a71a9546SAutomerger Merge Worker #include <xtables.h>
24*a71a9546SAutomerger Merge Worker #include <linux/netfilter/x_tables.h>
25*a71a9546SAutomerger Merge Worker #include <linux/netfilter/xt_hashlimit.h>
26*a71a9546SAutomerger Merge Worker 
27*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_BURST	5
28*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_BURST_MAX_v1	10000
29*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_BURST_MAX		1000000
30*a71a9546SAutomerger Merge Worker 
31*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_BYTE_EXPIRE	15
32*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_BYTE_EXPIRE_BURST	60
33*a71a9546SAutomerger Merge Worker 
34*a71a9546SAutomerger Merge Worker /* miliseconds */
35*a71a9546SAutomerger Merge Worker #define XT_HASHLIMIT_GCINTERVAL	1000
36*a71a9546SAutomerger Merge Worker 
37*a71a9546SAutomerger Merge Worker struct hashlimit_mt_udata {
38*a71a9546SAutomerger Merge Worker 	uint32_t mult;
39*a71a9546SAutomerger Merge Worker };
40*a71a9546SAutomerger Merge Worker 
hashlimit_help(void)41*a71a9546SAutomerger Merge Worker static void hashlimit_help(void)
42*a71a9546SAutomerger Merge Worker {
43*a71a9546SAutomerger Merge Worker 	printf(
44*a71a9546SAutomerger Merge Worker "hashlimit match options:\n"
45*a71a9546SAutomerger Merge Worker "--hashlimit <avg>		max average match rate\n"
46*a71a9546SAutomerger Merge Worker "                                [Packets per second unless followed by \n"
47*a71a9546SAutomerger Merge Worker "                                /sec /minute /hour /day postfixes]\n"
48*a71a9546SAutomerger Merge Worker "--hashlimit-mode <mode>		mode is a comma-separated list of\n"
49*a71a9546SAutomerger Merge Worker "					dstip,srcip,dstport,srcport\n"
50*a71a9546SAutomerger Merge Worker "--hashlimit-name <name>		name for /proc/net/ipt_hashlimit/\n"
51*a71a9546SAutomerger Merge Worker "[--hashlimit-burst <num>]	number to match in a burst, default %u\n"
52*a71a9546SAutomerger Merge Worker "[--hashlimit-htable-size <num>]	number of hashtable buckets\n"
53*a71a9546SAutomerger Merge Worker "[--hashlimit-htable-max <num>]	number of hashtable entries\n"
54*a71a9546SAutomerger Merge Worker "[--hashlimit-htable-gcinterval]	interval between garbage collection runs\n"
55*a71a9546SAutomerger Merge Worker "[--hashlimit-htable-expire]	after which time are idle entries expired?\n",
56*a71a9546SAutomerger Merge Worker XT_HASHLIMIT_BURST);
57*a71a9546SAutomerger Merge Worker }
58*a71a9546SAutomerger Merge Worker 
59*a71a9546SAutomerger Merge Worker enum {
60*a71a9546SAutomerger Merge Worker 	O_UPTO = 0,
61*a71a9546SAutomerger Merge Worker 	O_ABOVE,
62*a71a9546SAutomerger Merge Worker 	O_LIMIT,
63*a71a9546SAutomerger Merge Worker 	O_MODE,
64*a71a9546SAutomerger Merge Worker 	O_SRCMASK,
65*a71a9546SAutomerger Merge Worker 	O_DSTMASK,
66*a71a9546SAutomerger Merge Worker 	O_NAME,
67*a71a9546SAutomerger Merge Worker 	O_BURST,
68*a71a9546SAutomerger Merge Worker 	O_HTABLE_SIZE,
69*a71a9546SAutomerger Merge Worker 	O_HTABLE_MAX,
70*a71a9546SAutomerger Merge Worker 	O_HTABLE_GCINT,
71*a71a9546SAutomerger Merge Worker 	O_HTABLE_EXPIRE,
72*a71a9546SAutomerger Merge Worker 	O_RATEMATCH,
73*a71a9546SAutomerger Merge Worker 	O_INTERVAL,
74*a71a9546SAutomerger Merge Worker 	F_BURST         = 1 << O_BURST,
75*a71a9546SAutomerger Merge Worker 	F_UPTO          = 1 << O_UPTO,
76*a71a9546SAutomerger Merge Worker 	F_ABOVE         = 1 << O_ABOVE,
77*a71a9546SAutomerger Merge Worker 	F_HTABLE_EXPIRE = 1 << O_HTABLE_EXPIRE,
78*a71a9546SAutomerger Merge Worker 	F_RATEMATCH	= 1 << O_RATEMATCH,
79*a71a9546SAutomerger Merge Worker };
80*a71a9546SAutomerger Merge Worker 
hashlimit_mt_help(void)81*a71a9546SAutomerger Merge Worker static void hashlimit_mt_help(void)
82*a71a9546SAutomerger Merge Worker {
83*a71a9546SAutomerger Merge Worker 	printf(
84*a71a9546SAutomerger Merge Worker "hashlimit match options:\n"
85*a71a9546SAutomerger Merge Worker "  --hashlimit-upto <avg>           max average match rate\n"
86*a71a9546SAutomerger Merge Worker "                                   [Packets per second unless followed by \n"
87*a71a9546SAutomerger Merge Worker "                                   /sec /minute /hour /day postfixes]\n"
88*a71a9546SAutomerger Merge Worker "  --hashlimit-above <avg>          min average match rate\n"
89*a71a9546SAutomerger Merge Worker "  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
90*a71a9546SAutomerger Merge Worker "                                   dstip,srcip,dstport,srcport (or none)\n"
91*a71a9546SAutomerger Merge Worker "  --hashlimit-srcmask <length>     source address grouping prefix length\n"
92*a71a9546SAutomerger Merge Worker "  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
93*a71a9546SAutomerger Merge Worker "  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
94*a71a9546SAutomerger Merge Worker "  --hashlimit-burst <num>	    number to match in a burst, default %u\n"
95*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-size <num>    number of hashtable buckets\n"
96*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-max <num>     number of hashtable entries\n"
97*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
98*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-expire        after which time are idle entries expired?\n"
99*a71a9546SAutomerger Merge Worker "\n", XT_HASHLIMIT_BURST);
100*a71a9546SAutomerger Merge Worker }
101*a71a9546SAutomerger Merge Worker 
hashlimit_mt_help_v3(void)102*a71a9546SAutomerger Merge Worker static void hashlimit_mt_help_v3(void)
103*a71a9546SAutomerger Merge Worker {
104*a71a9546SAutomerger Merge Worker 	printf(
105*a71a9546SAutomerger Merge Worker "hashlimit match options:\n"
106*a71a9546SAutomerger Merge Worker "  --hashlimit-upto <avg>           max average match rate\n"
107*a71a9546SAutomerger Merge Worker "                                   [Packets per second unless followed by \n"
108*a71a9546SAutomerger Merge Worker "                                   /sec /minute /hour /day postfixes]\n"
109*a71a9546SAutomerger Merge Worker "  --hashlimit-above <avg>          min average match rate\n"
110*a71a9546SAutomerger Merge Worker "  --hashlimit-mode <mode>          mode is a comma-separated list of\n"
111*a71a9546SAutomerger Merge Worker "                                   dstip,srcip,dstport,srcport (or none)\n"
112*a71a9546SAutomerger Merge Worker "  --hashlimit-srcmask <length>     source address grouping prefix length\n"
113*a71a9546SAutomerger Merge Worker "  --hashlimit-dstmask <length>     destination address grouping prefix length\n"
114*a71a9546SAutomerger Merge Worker "  --hashlimit-name <name>          name for /proc/net/ipt_hashlimit\n"
115*a71a9546SAutomerger Merge Worker "  --hashlimit-burst <num>	    number to match in a burst, default %u\n"
116*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-size <num>    number of hashtable buckets\n"
117*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-max <num>     number of hashtable entries\n"
118*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-gcinterval    interval between garbage collection runs\n"
119*a71a9546SAutomerger Merge Worker "  --hashlimit-htable-expire        after which time are idle entries expired?\n"
120*a71a9546SAutomerger Merge Worker "  --hashlimit-rate-match           rate match the flow without rate-limiting it\n"
121*a71a9546SAutomerger Merge Worker "  --hashlimit-rate-interval        interval in seconds for hashlimit-rate-match\n"
122*a71a9546SAutomerger Merge Worker "\n", XT_HASHLIMIT_BURST);
123*a71a9546SAutomerger Merge Worker }
124*a71a9546SAutomerger Merge Worker 
125*a71a9546SAutomerger Merge Worker #define s struct xt_hashlimit_info
126*a71a9546SAutomerger Merge Worker static const struct xt_option_entry hashlimit_opts[] = {
127*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
128*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING},
129*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_UINT32,
130*a71a9546SAutomerger Merge Worker 	 .min = 1, .max = XT_HASHLIMIT_BURST_MAX_v1, .flags = XTOPT_PUT,
131*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.burst)},
132*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
133*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
134*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.size)},
135*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
136*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
137*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.max)},
138*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
139*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
140*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.gc_interval)},
141*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
142*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
143*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.expire)},
144*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING,
145*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND},
146*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
147*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
148*a71a9546SAutomerger Merge Worker 	XTOPT_TABLEEND,
149*a71a9546SAutomerger Merge Worker };
150*a71a9546SAutomerger Merge Worker #undef s
151*a71a9546SAutomerger Merge Worker 
152*a71a9546SAutomerger Merge Worker #define s struct xt_hashlimit_mtinfo1
153*a71a9546SAutomerger Merge Worker static const struct xt_option_entry hashlimit_mt_opts_v1[] = {
154*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
155*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
156*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
157*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
158*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
159*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
160*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
161*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
162*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
163*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
164*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
165*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.size)},
166*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
167*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
168*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.max)},
169*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
170*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
171*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.gc_interval)},
172*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
173*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
174*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.expire)},
175*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
176*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
177*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
178*a71a9546SAutomerger Merge Worker 	XTOPT_TABLEEND,
179*a71a9546SAutomerger Merge Worker };
180*a71a9546SAutomerger Merge Worker #undef s
181*a71a9546SAutomerger Merge Worker 
182*a71a9546SAutomerger Merge Worker #define s struct xt_hashlimit_mtinfo2
183*a71a9546SAutomerger Merge Worker static const struct xt_option_entry hashlimit_mt_opts_v2[] = {
184*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
185*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
186*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
187*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
188*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
189*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
190*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
191*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
192*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
193*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
194*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
195*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.size)},
196*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
197*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
198*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.max)},
199*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
200*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
201*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.gc_interval)},
202*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
203*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
204*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.expire)},
205*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
206*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
207*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
208*a71a9546SAutomerger Merge Worker 	XTOPT_TABLEEND,
209*a71a9546SAutomerger Merge Worker };
210*a71a9546SAutomerger Merge Worker #undef s
211*a71a9546SAutomerger Merge Worker 
212*a71a9546SAutomerger Merge Worker #define s struct xt_hashlimit_mtinfo3
213*a71a9546SAutomerger Merge Worker static const struct xt_option_entry hashlimit_mt_opts[] = {
214*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-upto", .id = O_UPTO, .excl = F_ABOVE,
215*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
216*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-above", .id = O_ABOVE, .excl = F_UPTO,
217*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT},
218*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit", .id = O_UPTO, .excl = F_ABOVE,
219*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_STRING, .flags = XTOPT_INVERT}, /* old name */
220*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-srcmask", .id = O_SRCMASK, .type = XTTYPE_PLEN},
221*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-dstmask", .id = O_DSTMASK, .type = XTTYPE_PLEN},
222*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-burst", .id = O_BURST, .type = XTTYPE_STRING},
223*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-size", .id = O_HTABLE_SIZE,
224*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
225*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.size)},
226*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-max", .id = O_HTABLE_MAX,
227*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
228*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.max)},
229*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-gcinterval", .id = O_HTABLE_GCINT,
230*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
231*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.gc_interval)},
232*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-htable-expire", .id = O_HTABLE_EXPIRE,
233*a71a9546SAutomerger Merge Worker 	 .type = XTTYPE_UINT32, .flags = XTOPT_PUT,
234*a71a9546SAutomerger Merge Worker 	 XTOPT_POINTER(s, cfg.expire)},
235*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-mode", .id = O_MODE, .type = XTTYPE_STRING},
236*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-name", .id = O_NAME, .type = XTTYPE_STRING,
237*a71a9546SAutomerger Merge Worker 	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, name), .min = 1},
238*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-rate-match", .id = O_RATEMATCH, .type = XTTYPE_NONE},
239*a71a9546SAutomerger Merge Worker 	{.name = "hashlimit-rate-interval", .id = O_INTERVAL, .type = XTTYPE_STRING},
240*a71a9546SAutomerger Merge Worker 	XTOPT_TABLEEND,
241*a71a9546SAutomerger Merge Worker };
242*a71a9546SAutomerger Merge Worker #undef s
243*a71a9546SAutomerger Merge Worker 
244*a71a9546SAutomerger Merge Worker static int
cfg_copy(struct hashlimit_cfg3 * to,const void * from,int revision)245*a71a9546SAutomerger Merge Worker cfg_copy(struct hashlimit_cfg3 *to, const void *from, int revision)
246*a71a9546SAutomerger Merge Worker {
247*a71a9546SAutomerger Merge Worker 	if (revision == 1) {
248*a71a9546SAutomerger Merge Worker 		struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from;
249*a71a9546SAutomerger Merge Worker 
250*a71a9546SAutomerger Merge Worker 		to->mode = cfg->mode;
251*a71a9546SAutomerger Merge Worker 		to->avg = cfg->avg;
252*a71a9546SAutomerger Merge Worker 		to->burst = cfg->burst;
253*a71a9546SAutomerger Merge Worker 		to->size = cfg->size;
254*a71a9546SAutomerger Merge Worker 		to->max = cfg->max;
255*a71a9546SAutomerger Merge Worker 		to->gc_interval = cfg->gc_interval;
256*a71a9546SAutomerger Merge Worker 		to->expire = cfg->expire;
257*a71a9546SAutomerger Merge Worker 		to->srcmask = cfg->srcmask;
258*a71a9546SAutomerger Merge Worker 		to->dstmask = cfg->dstmask;
259*a71a9546SAutomerger Merge Worker 	} else if (revision == 2) {
260*a71a9546SAutomerger Merge Worker 		struct hashlimit_cfg2 *cfg = (struct hashlimit_cfg2 *)from;
261*a71a9546SAutomerger Merge Worker 
262*a71a9546SAutomerger Merge Worker 		to->mode = cfg->mode;
263*a71a9546SAutomerger Merge Worker 		to->avg = cfg->avg;
264*a71a9546SAutomerger Merge Worker 		to->burst = cfg->burst;
265*a71a9546SAutomerger Merge Worker 		to->size = cfg->size;
266*a71a9546SAutomerger Merge Worker 		to->max = cfg->max;
267*a71a9546SAutomerger Merge Worker 		to->gc_interval = cfg->gc_interval;
268*a71a9546SAutomerger Merge Worker 		to->expire = cfg->expire;
269*a71a9546SAutomerger Merge Worker 		to->srcmask = cfg->srcmask;
270*a71a9546SAutomerger Merge Worker 		to->dstmask = cfg->dstmask;
271*a71a9546SAutomerger Merge Worker 	} else if (revision == 3) {
272*a71a9546SAutomerger Merge Worker 		memcpy(to, from, sizeof(struct hashlimit_cfg3));
273*a71a9546SAutomerger Merge Worker 	} else {
274*a71a9546SAutomerger Merge Worker 		return -EINVAL;
275*a71a9546SAutomerger Merge Worker 	}
276*a71a9546SAutomerger Merge Worker 
277*a71a9546SAutomerger Merge Worker 	return 0;
278*a71a9546SAutomerger Merge Worker }
279*a71a9546SAutomerger Merge Worker 
cost_to_bytes(uint64_t cost)280*a71a9546SAutomerger Merge Worker static uint64_t cost_to_bytes(uint64_t cost)
281*a71a9546SAutomerger Merge Worker {
282*a71a9546SAutomerger Merge Worker 	uint64_t r;
283*a71a9546SAutomerger Merge Worker 
284*a71a9546SAutomerger Merge Worker 	r = cost ? UINT32_MAX / cost : UINT32_MAX;
285*a71a9546SAutomerger Merge Worker 	r = (r - 1) << XT_HASHLIMIT_BYTE_SHIFT;
286*a71a9546SAutomerger Merge Worker 	return r;
287*a71a9546SAutomerger Merge Worker }
288*a71a9546SAutomerger Merge Worker 
bytes_to_cost(uint64_t bytes)289*a71a9546SAutomerger Merge Worker static uint64_t bytes_to_cost(uint64_t bytes)
290*a71a9546SAutomerger Merge Worker {
291*a71a9546SAutomerger Merge Worker 	uint32_t r = bytes >> XT_HASHLIMIT_BYTE_SHIFT;
292*a71a9546SAutomerger Merge Worker 	return UINT32_MAX / (r+1);
293*a71a9546SAutomerger Merge Worker }
294*a71a9546SAutomerger Merge Worker 
get_factor(int chr)295*a71a9546SAutomerger Merge Worker static uint32_t get_factor(int chr)
296*a71a9546SAutomerger Merge Worker {
297*a71a9546SAutomerger Merge Worker 	switch (chr) {
298*a71a9546SAutomerger Merge Worker 	case 'm': return 1024 * 1024;
299*a71a9546SAutomerger Merge Worker 	case 'k': return 1024;
300*a71a9546SAutomerger Merge Worker 	}
301*a71a9546SAutomerger Merge Worker 	return 1;
302*a71a9546SAutomerger Merge Worker }
303*a71a9546SAutomerger Merge Worker 
burst_error_v1(void)304*a71a9546SAutomerger Merge Worker static void burst_error_v1(void)
305*a71a9546SAutomerger Merge Worker {
306*a71a9546SAutomerger Merge Worker 	xtables_error(PARAMETER_PROBLEM, "bad value for option "
307*a71a9546SAutomerger Merge Worker 			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX_v1);
308*a71a9546SAutomerger Merge Worker }
309*a71a9546SAutomerger Merge Worker 
burst_error(void)310*a71a9546SAutomerger Merge Worker static void burst_error(void)
311*a71a9546SAutomerger Merge Worker {
312*a71a9546SAutomerger Merge Worker 	xtables_error(PARAMETER_PROBLEM, "bad value for option "
313*a71a9546SAutomerger Merge Worker 			"\"--hashlimit-burst\", or out of range (1-%u).", XT_HASHLIMIT_BURST_MAX);
314*a71a9546SAutomerger Merge Worker }
315*a71a9546SAutomerger Merge Worker 
parse_burst(const char * burst,int revision)316*a71a9546SAutomerger Merge Worker static uint64_t parse_burst(const char *burst, int revision)
317*a71a9546SAutomerger Merge Worker {
318*a71a9546SAutomerger Merge Worker 	uintmax_t v;
319*a71a9546SAutomerger Merge Worker 	char *end;
320*a71a9546SAutomerger Merge Worker 	uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
321*a71a9546SAutomerger Merge Worker 	uint64_t burst_max = (revision == 1) ?
322*a71a9546SAutomerger Merge Worker 			      XT_HASHLIMIT_BURST_MAX_v1 : XT_HASHLIMIT_BURST_MAX;
323*a71a9546SAutomerger Merge Worker 
324*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoul(burst, &end, &v, 1, max) ||
325*a71a9546SAutomerger Merge Worker 		(*end == 0 && v > burst_max)) {
326*a71a9546SAutomerger Merge Worker 		if (revision == 1)
327*a71a9546SAutomerger Merge Worker 			burst_error_v1();
328*a71a9546SAutomerger Merge Worker 		else
329*a71a9546SAutomerger Merge Worker 			burst_error();
330*a71a9546SAutomerger Merge Worker 	}
331*a71a9546SAutomerger Merge Worker 
332*a71a9546SAutomerger Merge Worker 	v *= get_factor(*end);
333*a71a9546SAutomerger Merge Worker 	if (v > max)
334*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM, "bad value for option "
335*a71a9546SAutomerger Merge Worker 			"\"--hashlimit-burst\", value \"%s\" too large "
336*a71a9546SAutomerger Merge Worker 				"(max %"PRIu64"mb).", burst, max/1024/1024);
337*a71a9546SAutomerger Merge Worker 	return v;
338*a71a9546SAutomerger Merge Worker }
339*a71a9546SAutomerger Merge Worker 
parse_bytes(const char * rate,void * val,struct hashlimit_mt_udata * ud,int revision)340*a71a9546SAutomerger Merge Worker static bool parse_bytes(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
341*a71a9546SAutomerger Merge Worker {
342*a71a9546SAutomerger Merge Worker 	unsigned int factor = 1;
343*a71a9546SAutomerger Merge Worker 	uint64_t tmp, r;
344*a71a9546SAutomerger Merge Worker 	const char *mode = strstr(rate, "b/s");
345*a71a9546SAutomerger Merge Worker 	uint64_t max = (revision == 1) ? UINT32_MAX : UINT64_MAX;
346*a71a9546SAutomerger Merge Worker 
347*a71a9546SAutomerger Merge Worker 	if (!mode || mode == rate)
348*a71a9546SAutomerger Merge Worker 		return false;
349*a71a9546SAutomerger Merge Worker 
350*a71a9546SAutomerger Merge Worker 	mode--;
351*a71a9546SAutomerger Merge Worker 	r = atoll(rate);
352*a71a9546SAutomerger Merge Worker 	if (r == 0)
353*a71a9546SAutomerger Merge Worker 		return false;
354*a71a9546SAutomerger Merge Worker 
355*a71a9546SAutomerger Merge Worker 	factor = get_factor(*mode);
356*a71a9546SAutomerger Merge Worker 	tmp = (uint64_t) r * factor;
357*a71a9546SAutomerger Merge Worker 	if (tmp > max)
358*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
359*a71a9546SAutomerger Merge Worker 			      "Rate value too large \"%"PRIu64"\" (max %"PRIu64")",
360*a71a9546SAutomerger Merge Worker 			      tmp, max);
361*a71a9546SAutomerger Merge Worker 
362*a71a9546SAutomerger Merge Worker 	tmp = bytes_to_cost(tmp);
363*a71a9546SAutomerger Merge Worker 	if (tmp == 0)
364*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM, "Rate too high \"%s\"", rate);
365*a71a9546SAutomerger Merge Worker 
366*a71a9546SAutomerger Merge Worker 	ud->mult = XT_HASHLIMIT_BYTE_EXPIRE;
367*a71a9546SAutomerger Merge Worker 
368*a71a9546SAutomerger Merge Worker 	if(revision == 1)
369*a71a9546SAutomerger Merge Worker 		*((uint32_t*)val) = tmp;
370*a71a9546SAutomerger Merge Worker 	else
371*a71a9546SAutomerger Merge Worker 		*((uint64_t*)val) = tmp;
372*a71a9546SAutomerger Merge Worker 
373*a71a9546SAutomerger Merge Worker 	return true;
374*a71a9546SAutomerger Merge Worker }
375*a71a9546SAutomerger Merge Worker 
376*a71a9546SAutomerger Merge Worker static
parse_rate(const char * rate,void * val,struct hashlimit_mt_udata * ud,int revision)377*a71a9546SAutomerger Merge Worker int parse_rate(const char *rate, void *val, struct hashlimit_mt_udata *ud, int revision)
378*a71a9546SAutomerger Merge Worker {
379*a71a9546SAutomerger Merge Worker 	const char *delim;
380*a71a9546SAutomerger Merge Worker 	uint64_t tmp, r;
381*a71a9546SAutomerger Merge Worker 	uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
382*a71a9546SAutomerger Merge Worker 
383*a71a9546SAutomerger Merge Worker 	ud->mult = 1;  /* Seconds by default. */
384*a71a9546SAutomerger Merge Worker 	delim = strchr(rate, '/');
385*a71a9546SAutomerger Merge Worker 	if (delim) {
386*a71a9546SAutomerger Merge Worker 		if (strlen(delim+1) == 0)
387*a71a9546SAutomerger Merge Worker 			return 0;
388*a71a9546SAutomerger Merge Worker 
389*a71a9546SAutomerger Merge Worker 		if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
390*a71a9546SAutomerger Merge Worker 			ud->mult = 1;
391*a71a9546SAutomerger Merge Worker 		else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
392*a71a9546SAutomerger Merge Worker 			ud->mult = 60;
393*a71a9546SAutomerger Merge Worker 		else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
394*a71a9546SAutomerger Merge Worker 			ud->mult = 60*60;
395*a71a9546SAutomerger Merge Worker 		else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
396*a71a9546SAutomerger Merge Worker 			ud->mult = 24*60*60;
397*a71a9546SAutomerger Merge Worker 		else
398*a71a9546SAutomerger Merge Worker 			return 0;
399*a71a9546SAutomerger Merge Worker 	}
400*a71a9546SAutomerger Merge Worker 	r = atoll(rate);
401*a71a9546SAutomerger Merge Worker 	if (!r)
402*a71a9546SAutomerger Merge Worker 		return 0;
403*a71a9546SAutomerger Merge Worker 
404*a71a9546SAutomerger Merge Worker 	tmp = scale * ud->mult / r;
405*a71a9546SAutomerger Merge Worker 	if (tmp == 0)
406*a71a9546SAutomerger Merge Worker 		/*
407*a71a9546SAutomerger Merge Worker 		 * The rate maps to infinity. (1/day is the minimum they can
408*a71a9546SAutomerger Merge Worker 		 * specify, so we are ok at that end).
409*a71a9546SAutomerger Merge Worker 		 */
410*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM, "Rate too fast \"%s\"", rate);
411*a71a9546SAutomerger Merge Worker 
412*a71a9546SAutomerger Merge Worker 	if(revision == 1)
413*a71a9546SAutomerger Merge Worker 		*((uint32_t*)val) = tmp;
414*a71a9546SAutomerger Merge Worker 	else
415*a71a9546SAutomerger Merge Worker 		*((uint64_t*)val) = tmp;
416*a71a9546SAutomerger Merge Worker 
417*a71a9546SAutomerger Merge Worker 	return 1;
418*a71a9546SAutomerger Merge Worker }
419*a71a9546SAutomerger Merge Worker 
parse_interval(const char * rate,uint32_t * val)420*a71a9546SAutomerger Merge Worker static int parse_interval(const char *rate, uint32_t *val)
421*a71a9546SAutomerger Merge Worker {
422*a71a9546SAutomerger Merge Worker 	int r = atoi(rate);
423*a71a9546SAutomerger Merge Worker 	if (r <= 0)
424*a71a9546SAutomerger Merge Worker 		return 0;
425*a71a9546SAutomerger Merge Worker 
426*a71a9546SAutomerger Merge Worker 	*val = r;
427*a71a9546SAutomerger Merge Worker 	return 1;
428*a71a9546SAutomerger Merge Worker }
429*a71a9546SAutomerger Merge Worker 
hashlimit_init(struct xt_entry_match * m)430*a71a9546SAutomerger Merge Worker static void hashlimit_init(struct xt_entry_match *m)
431*a71a9546SAutomerger Merge Worker {
432*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_info *r = (struct xt_hashlimit_info *)m->data;
433*a71a9546SAutomerger Merge Worker 
434*a71a9546SAutomerger Merge Worker 	r->cfg.burst = XT_HASHLIMIT_BURST;
435*a71a9546SAutomerger Merge Worker 	r->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
436*a71a9546SAutomerger Merge Worker 
437*a71a9546SAutomerger Merge Worker }
438*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_init_v1(struct xt_entry_match * match)439*a71a9546SAutomerger Merge Worker static void hashlimit_mt4_init_v1(struct xt_entry_match *match)
440*a71a9546SAutomerger Merge Worker {
441*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
442*a71a9546SAutomerger Merge Worker 
443*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
444*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
445*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
446*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 32;
447*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 32;
448*a71a9546SAutomerger Merge Worker }
449*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_init_v1(struct xt_entry_match * match)450*a71a9546SAutomerger Merge Worker static void hashlimit_mt6_init_v1(struct xt_entry_match *match)
451*a71a9546SAutomerger Merge Worker {
452*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo1 *info = (void *)match->data;
453*a71a9546SAutomerger Merge Worker 
454*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
455*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
456*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
457*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 128;
458*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 128;
459*a71a9546SAutomerger Merge Worker }
460*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_init_v2(struct xt_entry_match * match)461*a71a9546SAutomerger Merge Worker static void hashlimit_mt4_init_v2(struct xt_entry_match *match)
462*a71a9546SAutomerger Merge Worker {
463*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
464*a71a9546SAutomerger Merge Worker 
465*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
466*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
467*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
468*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 32;
469*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 32;
470*a71a9546SAutomerger Merge Worker }
471*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_init_v2(struct xt_entry_match * match)472*a71a9546SAutomerger Merge Worker static void hashlimit_mt6_init_v2(struct xt_entry_match *match)
473*a71a9546SAutomerger Merge Worker {
474*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo2 *info = (void *)match->data;
475*a71a9546SAutomerger Merge Worker 
476*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
477*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
478*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
479*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 128;
480*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 128;
481*a71a9546SAutomerger Merge Worker }
482*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_init(struct xt_entry_match * match)483*a71a9546SAutomerger Merge Worker static void hashlimit_mt4_init(struct xt_entry_match *match)
484*a71a9546SAutomerger Merge Worker {
485*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo3 *info = (void *)match->data;
486*a71a9546SAutomerger Merge Worker 
487*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
488*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
489*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
490*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 32;
491*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 32;
492*a71a9546SAutomerger Merge Worker 	info->cfg.interval    = 0;
493*a71a9546SAutomerger Merge Worker }
494*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_init(struct xt_entry_match * match)495*a71a9546SAutomerger Merge Worker static void hashlimit_mt6_init(struct xt_entry_match *match)
496*a71a9546SAutomerger Merge Worker {
497*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo3 *info = (void *)match->data;
498*a71a9546SAutomerger Merge Worker 
499*a71a9546SAutomerger Merge Worker 	info->cfg.mode        = 0;
500*a71a9546SAutomerger Merge Worker 	info->cfg.burst       = XT_HASHLIMIT_BURST;
501*a71a9546SAutomerger Merge Worker 	info->cfg.gc_interval = XT_HASHLIMIT_GCINTERVAL;
502*a71a9546SAutomerger Merge Worker 	info->cfg.srcmask     = 128;
503*a71a9546SAutomerger Merge Worker 	info->cfg.dstmask     = 128;
504*a71a9546SAutomerger Merge Worker 	info->cfg.interval    = 0;
505*a71a9546SAutomerger Merge Worker }
506*a71a9546SAutomerger Merge Worker 
507*a71a9546SAutomerger Merge Worker /* Parse a 'mode' parameter into the required bitmask */
parse_mode(uint32_t * mode,const char * option_arg)508*a71a9546SAutomerger Merge Worker static int parse_mode(uint32_t *mode, const char *option_arg)
509*a71a9546SAutomerger Merge Worker {
510*a71a9546SAutomerger Merge Worker 	char *tok;
511*a71a9546SAutomerger Merge Worker 	char *arg = xtables_strdup(option_arg);
512*a71a9546SAutomerger Merge Worker 
513*a71a9546SAutomerger Merge Worker 	for (tok = strtok(arg, ",|");
514*a71a9546SAutomerger Merge Worker 	     tok;
515*a71a9546SAutomerger Merge Worker 	     tok = strtok(NULL, ",|")) {
516*a71a9546SAutomerger Merge Worker 		if (!strcmp(tok, "dstip"))
517*a71a9546SAutomerger Merge Worker 			*mode |= XT_HASHLIMIT_HASH_DIP;
518*a71a9546SAutomerger Merge Worker 		else if (!strcmp(tok, "srcip"))
519*a71a9546SAutomerger Merge Worker 			*mode |= XT_HASHLIMIT_HASH_SIP;
520*a71a9546SAutomerger Merge Worker 		else if (!strcmp(tok, "srcport"))
521*a71a9546SAutomerger Merge Worker 			*mode |= XT_HASHLIMIT_HASH_SPT;
522*a71a9546SAutomerger Merge Worker 		else if (!strcmp(tok, "dstport"))
523*a71a9546SAutomerger Merge Worker 			*mode |= XT_HASHLIMIT_HASH_DPT;
524*a71a9546SAutomerger Merge Worker 		else {
525*a71a9546SAutomerger Merge Worker 			free(arg);
526*a71a9546SAutomerger Merge Worker 			return -1;
527*a71a9546SAutomerger Merge Worker 		}
528*a71a9546SAutomerger Merge Worker 	}
529*a71a9546SAutomerger Merge Worker 	free(arg);
530*a71a9546SAutomerger Merge Worker 	return 0;
531*a71a9546SAutomerger Merge Worker }
532*a71a9546SAutomerger Merge Worker 
hashlimit_parse(struct xt_option_call * cb)533*a71a9546SAutomerger Merge Worker static void hashlimit_parse(struct xt_option_call *cb)
534*a71a9546SAutomerger Merge Worker {
535*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_info *info = cb->data;
536*a71a9546SAutomerger Merge Worker 
537*a71a9546SAutomerger Merge Worker 	xtables_option_parse(cb);
538*a71a9546SAutomerger Merge Worker 	switch (cb->entry->id) {
539*a71a9546SAutomerger Merge Worker 	case O_UPTO:
540*a71a9546SAutomerger Merge Worker 		if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
541*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
542*a71a9546SAutomerger Merge Worker 			          "--hashlimit-upto", cb->arg);
543*a71a9546SAutomerger Merge Worker 		break;
544*a71a9546SAutomerger Merge Worker 	case O_MODE:
545*a71a9546SAutomerger Merge Worker 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
546*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
547*a71a9546SAutomerger Merge Worker 			          "--hashlimit-mode", cb->arg);
548*a71a9546SAutomerger Merge Worker 		break;
549*a71a9546SAutomerger Merge Worker 	}
550*a71a9546SAutomerger Merge Worker }
551*a71a9546SAutomerger Merge Worker 
hashlimit_mt_parse_v1(struct xt_option_call * cb)552*a71a9546SAutomerger Merge Worker static void hashlimit_mt_parse_v1(struct xt_option_call *cb)
553*a71a9546SAutomerger Merge Worker {
554*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo1 *info = cb->data;
555*a71a9546SAutomerger Merge Worker 
556*a71a9546SAutomerger Merge Worker 	xtables_option_parse(cb);
557*a71a9546SAutomerger Merge Worker 	switch (cb->entry->id) {
558*a71a9546SAutomerger Merge Worker 	case O_BURST:
559*a71a9546SAutomerger Merge Worker 		info->cfg.burst = parse_burst(cb->arg, 1);
560*a71a9546SAutomerger Merge Worker 		break;
561*a71a9546SAutomerger Merge Worker 	case O_UPTO:
562*a71a9546SAutomerger Merge Worker 		if (cb->invert)
563*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
564*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
565*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
566*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
567*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
568*a71a9546SAutomerger Merge Worker 			          "--hashlimit-upto", cb->arg);
569*a71a9546SAutomerger Merge Worker 		break;
570*a71a9546SAutomerger Merge Worker 	case O_ABOVE:
571*a71a9546SAutomerger Merge Worker 		if (!cb->invert)
572*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
573*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 1))
574*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
575*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 1))
576*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
577*a71a9546SAutomerger Merge Worker 			          "--hashlimit-above", cb->arg);
578*a71a9546SAutomerger Merge Worker 		break;
579*a71a9546SAutomerger Merge Worker 	case O_MODE:
580*a71a9546SAutomerger Merge Worker 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
581*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
582*a71a9546SAutomerger Merge Worker 			          "--hashlimit-mode", cb->arg);
583*a71a9546SAutomerger Merge Worker 		break;
584*a71a9546SAutomerger Merge Worker 	case O_SRCMASK:
585*a71a9546SAutomerger Merge Worker 		info->cfg.srcmask = cb->val.hlen;
586*a71a9546SAutomerger Merge Worker 		break;
587*a71a9546SAutomerger Merge Worker 	case O_DSTMASK:
588*a71a9546SAutomerger Merge Worker 		info->cfg.dstmask = cb->val.hlen;
589*a71a9546SAutomerger Merge Worker 		break;
590*a71a9546SAutomerger Merge Worker 	}
591*a71a9546SAutomerger Merge Worker }
592*a71a9546SAutomerger Merge Worker 
hashlimit_mt_parse_v2(struct xt_option_call * cb)593*a71a9546SAutomerger Merge Worker static void hashlimit_mt_parse_v2(struct xt_option_call *cb)
594*a71a9546SAutomerger Merge Worker {
595*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo2 *info = cb->data;
596*a71a9546SAutomerger Merge Worker 
597*a71a9546SAutomerger Merge Worker 	xtables_option_parse(cb);
598*a71a9546SAutomerger Merge Worker 	switch (cb->entry->id) {
599*a71a9546SAutomerger Merge Worker 	case O_BURST:
600*a71a9546SAutomerger Merge Worker 		info->cfg.burst = parse_burst(cb->arg, 2);
601*a71a9546SAutomerger Merge Worker 		break;
602*a71a9546SAutomerger Merge Worker 	case O_UPTO:
603*a71a9546SAutomerger Merge Worker 		if (cb->invert)
604*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
605*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
606*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
607*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
608*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
609*a71a9546SAutomerger Merge Worker 			          "--hashlimit-upto", cb->arg);
610*a71a9546SAutomerger Merge Worker 		break;
611*a71a9546SAutomerger Merge Worker 	case O_ABOVE:
612*a71a9546SAutomerger Merge Worker 		if (!cb->invert)
613*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
614*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
615*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
616*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
617*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
618*a71a9546SAutomerger Merge Worker 			          "--hashlimit-above", cb->arg);
619*a71a9546SAutomerger Merge Worker 		break;
620*a71a9546SAutomerger Merge Worker 	case O_MODE:
621*a71a9546SAutomerger Merge Worker 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
622*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
623*a71a9546SAutomerger Merge Worker 			          "--hashlimit-mode", cb->arg);
624*a71a9546SAutomerger Merge Worker 		break;
625*a71a9546SAutomerger Merge Worker 	case O_SRCMASK:
626*a71a9546SAutomerger Merge Worker 		info->cfg.srcmask = cb->val.hlen;
627*a71a9546SAutomerger Merge Worker 		break;
628*a71a9546SAutomerger Merge Worker 	case O_DSTMASK:
629*a71a9546SAutomerger Merge Worker 		info->cfg.dstmask = cb->val.hlen;
630*a71a9546SAutomerger Merge Worker 		break;
631*a71a9546SAutomerger Merge Worker 	}
632*a71a9546SAutomerger Merge Worker }
633*a71a9546SAutomerger Merge Worker 
hashlimit_mt_parse(struct xt_option_call * cb)634*a71a9546SAutomerger Merge Worker static void hashlimit_mt_parse(struct xt_option_call *cb)
635*a71a9546SAutomerger Merge Worker {
636*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo3 *info = cb->data;
637*a71a9546SAutomerger Merge Worker 
638*a71a9546SAutomerger Merge Worker 	xtables_option_parse(cb);
639*a71a9546SAutomerger Merge Worker 	switch (cb->entry->id) {
640*a71a9546SAutomerger Merge Worker 	case O_BURST:
641*a71a9546SAutomerger Merge Worker 		info->cfg.burst = parse_burst(cb->arg, 2);
642*a71a9546SAutomerger Merge Worker 		break;
643*a71a9546SAutomerger Merge Worker 	case O_UPTO:
644*a71a9546SAutomerger Merge Worker 		if (cb->invert)
645*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
646*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
647*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
648*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
649*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
650*a71a9546SAutomerger Merge Worker 			          "--hashlimit-upto", cb->arg);
651*a71a9546SAutomerger Merge Worker 		break;
652*a71a9546SAutomerger Merge Worker 	case O_ABOVE:
653*a71a9546SAutomerger Merge Worker 		if (!cb->invert)
654*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_INVERT;
655*a71a9546SAutomerger Merge Worker 		if (parse_bytes(cb->arg, &info->cfg.avg, cb->udata, 2))
656*a71a9546SAutomerger Merge Worker 			info->cfg.mode |= XT_HASHLIMIT_BYTES;
657*a71a9546SAutomerger Merge Worker 		else if (!parse_rate(cb->arg, &info->cfg.avg, cb->udata, 2))
658*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
659*a71a9546SAutomerger Merge Worker 			          "--hashlimit-above", cb->arg);
660*a71a9546SAutomerger Merge Worker 		break;
661*a71a9546SAutomerger Merge Worker 	case O_MODE:
662*a71a9546SAutomerger Merge Worker 		if (parse_mode(&info->cfg.mode, cb->arg) < 0)
663*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
664*a71a9546SAutomerger Merge Worker 			          "--hashlimit-mode", cb->arg);
665*a71a9546SAutomerger Merge Worker 		break;
666*a71a9546SAutomerger Merge Worker 	case O_SRCMASK:
667*a71a9546SAutomerger Merge Worker 		info->cfg.srcmask = cb->val.hlen;
668*a71a9546SAutomerger Merge Worker 		break;
669*a71a9546SAutomerger Merge Worker 	case O_DSTMASK:
670*a71a9546SAutomerger Merge Worker 		info->cfg.dstmask = cb->val.hlen;
671*a71a9546SAutomerger Merge Worker 		break;
672*a71a9546SAutomerger Merge Worker 	case O_RATEMATCH:
673*a71a9546SAutomerger Merge Worker 		info->cfg.mode |= XT_HASHLIMIT_RATE_MATCH;
674*a71a9546SAutomerger Merge Worker 		break;
675*a71a9546SAutomerger Merge Worker 	case O_INTERVAL:
676*a71a9546SAutomerger Merge Worker 		if (!parse_interval(cb->arg, &info->cfg.interval))
677*a71a9546SAutomerger Merge Worker 			xtables_param_act(XTF_BAD_VALUE, "hashlimit",
678*a71a9546SAutomerger Merge Worker 				"--hashlimit-rate-interval", cb->arg);
679*a71a9546SAutomerger Merge Worker 	}
680*a71a9546SAutomerger Merge Worker }
681*a71a9546SAutomerger Merge Worker 
hashlimit_check(struct xt_fcheck_call * cb)682*a71a9546SAutomerger Merge Worker static void hashlimit_check(struct xt_fcheck_call *cb)
683*a71a9546SAutomerger Merge Worker {
684*a71a9546SAutomerger Merge Worker 	const struct hashlimit_mt_udata *udata = cb->udata;
685*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_info *info = cb->data;
686*a71a9546SAutomerger Merge Worker 
687*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
688*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
689*a71a9546SAutomerger Merge Worker 				"You have to specify --hashlimit");
690*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & F_HTABLE_EXPIRE))
691*a71a9546SAutomerger Merge Worker 		info->cfg.expire = udata->mult * 1000; /* from s to msec */
692*a71a9546SAutomerger Merge Worker }
693*a71a9546SAutomerger Merge Worker 
hashlimit_mt_check_v1(struct xt_fcheck_call * cb)694*a71a9546SAutomerger Merge Worker static void hashlimit_mt_check_v1(struct xt_fcheck_call *cb)
695*a71a9546SAutomerger Merge Worker {
696*a71a9546SAutomerger Merge Worker 	const struct hashlimit_mt_udata *udata = cb->udata;
697*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo1 *info = cb->data;
698*a71a9546SAutomerger Merge Worker 
699*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
700*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
701*a71a9546SAutomerger Merge Worker 				"You have to specify --hashlimit");
702*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & F_HTABLE_EXPIRE))
703*a71a9546SAutomerger Merge Worker 		info->cfg.expire = udata->mult * 1000; /* from s to msec */
704*a71a9546SAutomerger Merge Worker 
705*a71a9546SAutomerger Merge Worker 	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
706*a71a9546SAutomerger Merge Worker 		uint32_t burst = 0;
707*a71a9546SAutomerger Merge Worker 		if (cb->xflags & F_BURST) {
708*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
709*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
710*a71a9546SAutomerger Merge Worker 					"burst cannot be smaller than %"PRIu64"b",
711*a71a9546SAutomerger Merge Worker 					cost_to_bytes(info->cfg.avg));
712*a71a9546SAutomerger Merge Worker 
713*a71a9546SAutomerger Merge Worker 			burst = info->cfg.burst;
714*a71a9546SAutomerger Merge Worker 			burst /= cost_to_bytes(info->cfg.avg);
715*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
716*a71a9546SAutomerger Merge Worker 				burst++;
717*a71a9546SAutomerger Merge Worker 			if (!(cb->xflags & F_HTABLE_EXPIRE))
718*a71a9546SAutomerger Merge Worker 				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
719*a71a9546SAutomerger Merge Worker 		}
720*a71a9546SAutomerger Merge Worker 		info->cfg.burst = burst;
721*a71a9546SAutomerger Merge Worker 	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX_v1)
722*a71a9546SAutomerger Merge Worker 		burst_error_v1();
723*a71a9546SAutomerger Merge Worker }
724*a71a9546SAutomerger Merge Worker 
hashlimit_mt_check_v2(struct xt_fcheck_call * cb)725*a71a9546SAutomerger Merge Worker static void hashlimit_mt_check_v2(struct xt_fcheck_call *cb)
726*a71a9546SAutomerger Merge Worker {
727*a71a9546SAutomerger Merge Worker 	const struct hashlimit_mt_udata *udata = cb->udata;
728*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo2 *info = cb->data;
729*a71a9546SAutomerger Merge Worker 
730*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
731*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
732*a71a9546SAutomerger Merge Worker 				"You have to specify --hashlimit");
733*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & F_HTABLE_EXPIRE))
734*a71a9546SAutomerger Merge Worker 		info->cfg.expire = udata->mult * 1000; /* from s to msec */
735*a71a9546SAutomerger Merge Worker 
736*a71a9546SAutomerger Merge Worker 	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
737*a71a9546SAutomerger Merge Worker 		uint32_t burst = 0;
738*a71a9546SAutomerger Merge Worker 		if (cb->xflags & F_BURST) {
739*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
740*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
741*a71a9546SAutomerger Merge Worker 					"burst cannot be smaller than %"PRIu64"b",
742*a71a9546SAutomerger Merge Worker 					cost_to_bytes(info->cfg.avg));
743*a71a9546SAutomerger Merge Worker 
744*a71a9546SAutomerger Merge Worker 			burst = info->cfg.burst;
745*a71a9546SAutomerger Merge Worker 			burst /= cost_to_bytes(info->cfg.avg);
746*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
747*a71a9546SAutomerger Merge Worker 				burst++;
748*a71a9546SAutomerger Merge Worker 			if (!(cb->xflags & F_HTABLE_EXPIRE))
749*a71a9546SAutomerger Merge Worker 				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
750*a71a9546SAutomerger Merge Worker 		}
751*a71a9546SAutomerger Merge Worker 		info->cfg.burst = burst;
752*a71a9546SAutomerger Merge Worker 	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
753*a71a9546SAutomerger Merge Worker 		burst_error();
754*a71a9546SAutomerger Merge Worker }
755*a71a9546SAutomerger Merge Worker 
hashlimit_mt_check(struct xt_fcheck_call * cb)756*a71a9546SAutomerger Merge Worker static void hashlimit_mt_check(struct xt_fcheck_call *cb)
757*a71a9546SAutomerger Merge Worker {
758*a71a9546SAutomerger Merge Worker 	const struct hashlimit_mt_udata *udata = cb->udata;
759*a71a9546SAutomerger Merge Worker 	struct xt_hashlimit_mtinfo3 *info = cb->data;
760*a71a9546SAutomerger Merge Worker 
761*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & (F_UPTO | F_ABOVE)))
762*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
763*a71a9546SAutomerger Merge Worker 				"You have to specify --hashlimit");
764*a71a9546SAutomerger Merge Worker 	if (!(cb->xflags & F_HTABLE_EXPIRE))
765*a71a9546SAutomerger Merge Worker 		info->cfg.expire = udata->mult * 1000; /* from s to msec */
766*a71a9546SAutomerger Merge Worker 
767*a71a9546SAutomerger Merge Worker 	if (info->cfg.mode & XT_HASHLIMIT_BYTES) {
768*a71a9546SAutomerger Merge Worker 		uint32_t burst = 0;
769*a71a9546SAutomerger Merge Worker 		if (cb->xflags & F_BURST) {
770*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst < cost_to_bytes(info->cfg.avg))
771*a71a9546SAutomerger Merge Worker 				xtables_error(PARAMETER_PROBLEM,
772*a71a9546SAutomerger Merge Worker 					"burst cannot be smaller than %"PRIu64"b", cost_to_bytes(info->cfg.avg));
773*a71a9546SAutomerger Merge Worker 
774*a71a9546SAutomerger Merge Worker 			burst = info->cfg.burst;
775*a71a9546SAutomerger Merge Worker 			burst /= cost_to_bytes(info->cfg.avg);
776*a71a9546SAutomerger Merge Worker 			if (info->cfg.burst % cost_to_bytes(info->cfg.avg))
777*a71a9546SAutomerger Merge Worker 				burst++;
778*a71a9546SAutomerger Merge Worker 			if (!(cb->xflags & F_HTABLE_EXPIRE))
779*a71a9546SAutomerger Merge Worker 				info->cfg.expire = XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
780*a71a9546SAutomerger Merge Worker 		}
781*a71a9546SAutomerger Merge Worker 		info->cfg.burst = burst;
782*a71a9546SAutomerger Merge Worker 	} else if (info->cfg.burst > XT_HASHLIMIT_BURST_MAX)
783*a71a9546SAutomerger Merge Worker 		burst_error();
784*a71a9546SAutomerger Merge Worker 
785*a71a9546SAutomerger Merge Worker 	if (cb->xflags & F_RATEMATCH) {
786*a71a9546SAutomerger Merge Worker 		if (!(info->cfg.mode & XT_HASHLIMIT_BYTES))
787*a71a9546SAutomerger Merge Worker 			info->cfg.avg /= udata->mult;
788*a71a9546SAutomerger Merge Worker 
789*a71a9546SAutomerger Merge Worker 		if (info->cfg.interval == 0) {
790*a71a9546SAutomerger Merge Worker 			if (info->cfg.mode & XT_HASHLIMIT_BYTES)
791*a71a9546SAutomerger Merge Worker 				info->cfg.interval = 1;
792*a71a9546SAutomerger Merge Worker 			else
793*a71a9546SAutomerger Merge Worker 				info->cfg.interval = udata->mult;
794*a71a9546SAutomerger Merge Worker 		}
795*a71a9546SAutomerger Merge Worker 	}
796*a71a9546SAutomerger Merge Worker }
797*a71a9546SAutomerger Merge Worker 
798*a71a9546SAutomerger Merge Worker struct rates {
799*a71a9546SAutomerger Merge Worker 	const char *name;
800*a71a9546SAutomerger Merge Worker 	uint64_t mult;
801*a71a9546SAutomerger Merge Worker } rates_v1[] = { { "day", XT_HASHLIMIT_SCALE*24*60*60 },
802*a71a9546SAutomerger Merge Worker 		 { "hour", XT_HASHLIMIT_SCALE*60*60 },
803*a71a9546SAutomerger Merge Worker 		 { "min", XT_HASHLIMIT_SCALE*60 },
804*a71a9546SAutomerger Merge Worker 		 { "sec", XT_HASHLIMIT_SCALE } };
805*a71a9546SAutomerger Merge Worker 
806*a71a9546SAutomerger Merge Worker static const struct rates rates[] = {
807*a71a9546SAutomerger Merge Worker 	{ "day", XT_HASHLIMIT_SCALE_v2*24*60*60 },
808*a71a9546SAutomerger Merge Worker 	{ "hour", XT_HASHLIMIT_SCALE_v2*60*60 },
809*a71a9546SAutomerger Merge Worker 	{ "min", XT_HASHLIMIT_SCALE_v2*60 },
810*a71a9546SAutomerger Merge Worker 	{ "sec", XT_HASHLIMIT_SCALE_v2 } };
811*a71a9546SAutomerger Merge Worker 
print_rate(uint64_t period,int revision)812*a71a9546SAutomerger Merge Worker static uint32_t print_rate(uint64_t period, int revision)
813*a71a9546SAutomerger Merge Worker {
814*a71a9546SAutomerger Merge Worker 	unsigned int i;
815*a71a9546SAutomerger Merge Worker 	const struct rates *_rates = (revision == 1) ? rates_v1 : rates;
816*a71a9546SAutomerger Merge Worker 	uint64_t scale = (revision == 1) ? XT_HASHLIMIT_SCALE : XT_HASHLIMIT_SCALE_v2;
817*a71a9546SAutomerger Merge Worker 
818*a71a9546SAutomerger Merge Worker 	if (period == 0) {
819*a71a9546SAutomerger Merge Worker 		printf(" %f", INFINITY);
820*a71a9546SAutomerger Merge Worker 		return 0;
821*a71a9546SAutomerger Merge Worker 	}
822*a71a9546SAutomerger Merge Worker 
823*a71a9546SAutomerger Merge Worker 	for (i = 1; i < ARRAY_SIZE(rates); ++i)
824*a71a9546SAutomerger Merge Worker 		if (period > _rates[i].mult
825*a71a9546SAutomerger Merge Worker             || _rates[i].mult/period < _rates[i].mult%period)
826*a71a9546SAutomerger Merge Worker 			break;
827*a71a9546SAutomerger Merge Worker 
828*a71a9546SAutomerger Merge Worker 	printf(" %"PRIu64"/%s", _rates[i-1].mult / period, _rates[i-1].name);
829*a71a9546SAutomerger Merge Worker 	/* return in msec */
830*a71a9546SAutomerger Merge Worker 	return _rates[i-1].mult / scale * 1000;
831*a71a9546SAutomerger Merge Worker }
832*a71a9546SAutomerger Merge Worker 
833*a71a9546SAutomerger Merge Worker static const struct {
834*a71a9546SAutomerger Merge Worker 	const char *name;
835*a71a9546SAutomerger Merge Worker 	uint32_t thresh;
836*a71a9546SAutomerger Merge Worker } units[] = {
837*a71a9546SAutomerger Merge Worker 	{ "m", 1024 * 1024 },
838*a71a9546SAutomerger Merge Worker 	{ "k", 1024 },
839*a71a9546SAutomerger Merge Worker 	{ "", 1 },
840*a71a9546SAutomerger Merge Worker };
841*a71a9546SAutomerger Merge Worker 
print_bytes(uint64_t avg,uint64_t burst,const char * prefix)842*a71a9546SAutomerger Merge Worker static uint32_t print_bytes(uint64_t avg, uint64_t burst, const char *prefix)
843*a71a9546SAutomerger Merge Worker {
844*a71a9546SAutomerger Merge Worker 	unsigned int i;
845*a71a9546SAutomerger Merge Worker 	unsigned long long r;
846*a71a9546SAutomerger Merge Worker 
847*a71a9546SAutomerger Merge Worker 	r = cost_to_bytes(avg);
848*a71a9546SAutomerger Merge Worker 
849*a71a9546SAutomerger Merge Worker 	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
850*a71a9546SAutomerger Merge Worker 		if (r >= units[i].thresh &&
851*a71a9546SAutomerger Merge Worker 		    bytes_to_cost(r & ~(units[i].thresh - 1)) == avg)
852*a71a9546SAutomerger Merge Worker 			break;
853*a71a9546SAutomerger Merge Worker 	printf(" %llu%sb/s", r/units[i].thresh, units[i].name);
854*a71a9546SAutomerger Merge Worker 
855*a71a9546SAutomerger Merge Worker 	if (burst == 0)
856*a71a9546SAutomerger Merge Worker 		return XT_HASHLIMIT_BYTE_EXPIRE * 1000;
857*a71a9546SAutomerger Merge Worker 
858*a71a9546SAutomerger Merge Worker 	r *= burst;
859*a71a9546SAutomerger Merge Worker 	printf(" %s", prefix);
860*a71a9546SAutomerger Merge Worker 	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
861*a71a9546SAutomerger Merge Worker 		if (r >= units[i].thresh)
862*a71a9546SAutomerger Merge Worker 			break;
863*a71a9546SAutomerger Merge Worker 
864*a71a9546SAutomerger Merge Worker 	printf("burst %llu%sb", r / units[i].thresh, units[i].name);
865*a71a9546SAutomerger Merge Worker 	return XT_HASHLIMIT_BYTE_EXPIRE_BURST * 1000;
866*a71a9546SAutomerger Merge Worker }
867*a71a9546SAutomerger Merge Worker 
print_mode(unsigned int mode,char separator)868*a71a9546SAutomerger Merge Worker static void print_mode(unsigned int mode, char separator)
869*a71a9546SAutomerger Merge Worker {
870*a71a9546SAutomerger Merge Worker 	bool prevmode = false;
871*a71a9546SAutomerger Merge Worker 
872*a71a9546SAutomerger Merge Worker 	putchar(' ');
873*a71a9546SAutomerger Merge Worker 	if (mode & XT_HASHLIMIT_HASH_SIP) {
874*a71a9546SAutomerger Merge Worker 		fputs("srcip", stdout);
875*a71a9546SAutomerger Merge Worker 		prevmode = 1;
876*a71a9546SAutomerger Merge Worker 	}
877*a71a9546SAutomerger Merge Worker 	if (mode & XT_HASHLIMIT_HASH_SPT) {
878*a71a9546SAutomerger Merge Worker 		if (prevmode)
879*a71a9546SAutomerger Merge Worker 			putchar(separator);
880*a71a9546SAutomerger Merge Worker 		fputs("srcport", stdout);
881*a71a9546SAutomerger Merge Worker 		prevmode = 1;
882*a71a9546SAutomerger Merge Worker 	}
883*a71a9546SAutomerger Merge Worker 	if (mode & XT_HASHLIMIT_HASH_DIP) {
884*a71a9546SAutomerger Merge Worker 		if (prevmode)
885*a71a9546SAutomerger Merge Worker 			putchar(separator);
886*a71a9546SAutomerger Merge Worker 		fputs("dstip", stdout);
887*a71a9546SAutomerger Merge Worker 		prevmode = 1;
888*a71a9546SAutomerger Merge Worker 	}
889*a71a9546SAutomerger Merge Worker 	if (mode & XT_HASHLIMIT_HASH_DPT) {
890*a71a9546SAutomerger Merge Worker 		if (prevmode)
891*a71a9546SAutomerger Merge Worker 			putchar(separator);
892*a71a9546SAutomerger Merge Worker 		fputs("dstport", stdout);
893*a71a9546SAutomerger Merge Worker 	}
894*a71a9546SAutomerger Merge Worker }
895*a71a9546SAutomerger Merge Worker 
hashlimit_print(const void * ip,const struct xt_entry_match * match,int numeric)896*a71a9546SAutomerger Merge Worker static void hashlimit_print(const void *ip,
897*a71a9546SAutomerger Merge Worker                             const struct xt_entry_match *match, int numeric)
898*a71a9546SAutomerger Merge Worker {
899*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_info *r = (const void *)match->data;
900*a71a9546SAutomerger Merge Worker 	uint32_t quantum;
901*a71a9546SAutomerger Merge Worker 
902*a71a9546SAutomerger Merge Worker 	fputs(" limit: avg", stdout);
903*a71a9546SAutomerger Merge Worker 	quantum = print_rate(r->cfg.avg, 1);
904*a71a9546SAutomerger Merge Worker 	printf(" burst %u", r->cfg.burst);
905*a71a9546SAutomerger Merge Worker 	fputs(" mode", stdout);
906*a71a9546SAutomerger Merge Worker 	print_mode(r->cfg.mode, '-');
907*a71a9546SAutomerger Merge Worker 	if (r->cfg.size)
908*a71a9546SAutomerger Merge Worker 		printf(" htable-size %u", r->cfg.size);
909*a71a9546SAutomerger Merge Worker 	if (r->cfg.max)
910*a71a9546SAutomerger Merge Worker 		printf(" htable-max %u", r->cfg.max);
911*a71a9546SAutomerger Merge Worker 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
912*a71a9546SAutomerger Merge Worker 		printf(" htable-gcinterval %u", r->cfg.gc_interval);
913*a71a9546SAutomerger Merge Worker 	if (r->cfg.expire != quantum)
914*a71a9546SAutomerger Merge Worker 		printf(" htable-expire %u", r->cfg.expire);
915*a71a9546SAutomerger Merge Worker }
916*a71a9546SAutomerger Merge Worker 
917*a71a9546SAutomerger Merge Worker static void
hashlimit_mt_print(const struct hashlimit_cfg3 * cfg,unsigned int dmask,int revision)918*a71a9546SAutomerger Merge Worker hashlimit_mt_print(const struct hashlimit_cfg3 *cfg, unsigned int dmask, int revision)
919*a71a9546SAutomerger Merge Worker {
920*a71a9546SAutomerger Merge Worker 	uint64_t quantum;
921*a71a9546SAutomerger Merge Worker 	uint64_t period;
922*a71a9546SAutomerger Merge Worker 
923*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_INVERT)
924*a71a9546SAutomerger Merge Worker 		fputs(" limit: above", stdout);
925*a71a9546SAutomerger Merge Worker 	else
926*a71a9546SAutomerger Merge Worker 		fputs(" limit: up to", stdout);
927*a71a9546SAutomerger Merge Worker 
928*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_BYTES) {
929*a71a9546SAutomerger Merge Worker 		quantum = print_bytes(cfg->avg, cfg->burst, "");
930*a71a9546SAutomerger Merge Worker 	} else {
931*a71a9546SAutomerger Merge Worker 		if (revision == 3) {
932*a71a9546SAutomerger Merge Worker 			period = cfg->avg;
933*a71a9546SAutomerger Merge Worker 			if (cfg->interval != 0)
934*a71a9546SAutomerger Merge Worker 				period *= cfg->interval;
935*a71a9546SAutomerger Merge Worker 
936*a71a9546SAutomerger Merge Worker 			quantum = print_rate(period, revision);
937*a71a9546SAutomerger Merge Worker 		} else {
938*a71a9546SAutomerger Merge Worker 			quantum = print_rate(cfg->avg, revision);
939*a71a9546SAutomerger Merge Worker 		}
940*a71a9546SAutomerger Merge Worker 		printf(" burst %llu", cfg->burst);
941*a71a9546SAutomerger Merge Worker 	}
942*a71a9546SAutomerger Merge Worker 	if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
943*a71a9546SAutomerger Merge Worker 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
944*a71a9546SAutomerger Merge Worker 		fputs(" mode", stdout);
945*a71a9546SAutomerger Merge Worker 		print_mode(cfg->mode, '-');
946*a71a9546SAutomerger Merge Worker 	}
947*a71a9546SAutomerger Merge Worker 	if (cfg->size != 0)
948*a71a9546SAutomerger Merge Worker 		printf(" htable-size %u", cfg->size);
949*a71a9546SAutomerger Merge Worker 	if (cfg->max != 0)
950*a71a9546SAutomerger Merge Worker 		printf(" htable-max %u", cfg->max);
951*a71a9546SAutomerger Merge Worker 	if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
952*a71a9546SAutomerger Merge Worker 		printf(" htable-gcinterval %u", cfg->gc_interval);
953*a71a9546SAutomerger Merge Worker 	if (cfg->expire != quantum)
954*a71a9546SAutomerger Merge Worker 		printf(" htable-expire %u", cfg->expire);
955*a71a9546SAutomerger Merge Worker 
956*a71a9546SAutomerger Merge Worker 	if (cfg->srcmask != dmask)
957*a71a9546SAutomerger Merge Worker 		printf(" srcmask %u", cfg->srcmask);
958*a71a9546SAutomerger Merge Worker 	if (cfg->dstmask != dmask)
959*a71a9546SAutomerger Merge Worker 		printf(" dstmask %u", cfg->dstmask);
960*a71a9546SAutomerger Merge Worker 
961*a71a9546SAutomerger Merge Worker 	if ((revision == 3) && (cfg->mode & XT_HASHLIMIT_RATE_MATCH))
962*a71a9546SAutomerger Merge Worker 		printf(" rate-match");
963*a71a9546SAutomerger Merge Worker 
964*a71a9546SAutomerger Merge Worker 	if ((revision == 3) && (cfg->mode & XT_HASHLIMIT_RATE_MATCH))
965*a71a9546SAutomerger Merge Worker 		if (cfg->interval != 1)
966*a71a9546SAutomerger Merge Worker 			printf(" rate-interval %u", cfg->interval);
967*a71a9546SAutomerger Merge Worker }
968*a71a9546SAutomerger Merge Worker 
969*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)970*a71a9546SAutomerger Merge Worker hashlimit_mt4_print_v1(const void *ip, const struct xt_entry_match *match,
971*a71a9546SAutomerger Merge Worker                    int numeric)
972*a71a9546SAutomerger Merge Worker {
973*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
974*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
975*a71a9546SAutomerger Merge Worker 	int ret;
976*a71a9546SAutomerger Merge Worker 
977*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
978*a71a9546SAutomerger Merge Worker 
979*a71a9546SAutomerger Merge Worker 	if (ret)
980*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
981*a71a9546SAutomerger Merge Worker 
982*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&cfg, 32, 1);
983*a71a9546SAutomerger Merge Worker }
984*a71a9546SAutomerger Merge Worker 
985*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_print_v1(const void * ip,const struct xt_entry_match * match,int numeric)986*a71a9546SAutomerger Merge Worker hashlimit_mt6_print_v1(const void *ip, const struct xt_entry_match *match,
987*a71a9546SAutomerger Merge Worker                    int numeric)
988*a71a9546SAutomerger Merge Worker {
989*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
990*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
991*a71a9546SAutomerger Merge Worker 	int ret;
992*a71a9546SAutomerger Merge Worker 
993*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
994*a71a9546SAutomerger Merge Worker 
995*a71a9546SAutomerger Merge Worker 	if (ret)
996*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
997*a71a9546SAutomerger Merge Worker 
998*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&cfg, 128, 1);
999*a71a9546SAutomerger Merge Worker }
1000*a71a9546SAutomerger Merge Worker 
1001*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_print_v2(const void * ip,const struct xt_entry_match * match,int numeric)1002*a71a9546SAutomerger Merge Worker hashlimit_mt4_print_v2(const void *ip, const struct xt_entry_match *match,
1003*a71a9546SAutomerger Merge Worker                    int numeric)
1004*a71a9546SAutomerger Merge Worker {
1005*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
1006*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1007*a71a9546SAutomerger Merge Worker 	int ret;
1008*a71a9546SAutomerger Merge Worker 
1009*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 2);
1010*a71a9546SAutomerger Merge Worker 
1011*a71a9546SAutomerger Merge Worker 	if (ret)
1012*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1013*a71a9546SAutomerger Merge Worker 
1014*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&cfg, 32, 2);
1015*a71a9546SAutomerger Merge Worker }
1016*a71a9546SAutomerger Merge Worker 
1017*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_print_v2(const void * ip,const struct xt_entry_match * match,int numeric)1018*a71a9546SAutomerger Merge Worker hashlimit_mt6_print_v2(const void *ip, const struct xt_entry_match *match,
1019*a71a9546SAutomerger Merge Worker                    int numeric)
1020*a71a9546SAutomerger Merge Worker {
1021*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
1022*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1023*a71a9546SAutomerger Merge Worker 	int ret;
1024*a71a9546SAutomerger Merge Worker 
1025*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 2);
1026*a71a9546SAutomerger Merge Worker 
1027*a71a9546SAutomerger Merge Worker 	if (ret)
1028*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1029*a71a9546SAutomerger Merge Worker 
1030*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&cfg, 128, 2);
1031*a71a9546SAutomerger Merge Worker }
1032*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_print(const void * ip,const struct xt_entry_match * match,int numeric)1033*a71a9546SAutomerger Merge Worker hashlimit_mt4_print(const void *ip, const struct xt_entry_match *match,
1034*a71a9546SAutomerger Merge Worker                    int numeric)
1035*a71a9546SAutomerger Merge Worker {
1036*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info = (const void *)match->data;
1037*a71a9546SAutomerger Merge Worker 
1038*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&info->cfg, 32, 3);
1039*a71a9546SAutomerger Merge Worker }
1040*a71a9546SAutomerger Merge Worker 
1041*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_print(const void * ip,const struct xt_entry_match * match,int numeric)1042*a71a9546SAutomerger Merge Worker hashlimit_mt6_print(const void *ip, const struct xt_entry_match *match,
1043*a71a9546SAutomerger Merge Worker                    int numeric)
1044*a71a9546SAutomerger Merge Worker {
1045*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info = (const void *)match->data;
1046*a71a9546SAutomerger Merge Worker 
1047*a71a9546SAutomerger Merge Worker 	hashlimit_mt_print(&info->cfg, 128, 3);
1048*a71a9546SAutomerger Merge Worker }
1049*a71a9546SAutomerger Merge Worker 
hashlimit_save(const void * ip,const struct xt_entry_match * match)1050*a71a9546SAutomerger Merge Worker static void hashlimit_save(const void *ip, const struct xt_entry_match *match)
1051*a71a9546SAutomerger Merge Worker {
1052*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_info *r = (const void *)match->data;
1053*a71a9546SAutomerger Merge Worker 	uint32_t quantum;
1054*a71a9546SAutomerger Merge Worker 
1055*a71a9546SAutomerger Merge Worker 	fputs(" --hashlimit", stdout);
1056*a71a9546SAutomerger Merge Worker 	quantum = print_rate(r->cfg.avg, 1);
1057*a71a9546SAutomerger Merge Worker 	printf(" --hashlimit-burst %u", r->cfg.burst);
1058*a71a9546SAutomerger Merge Worker 
1059*a71a9546SAutomerger Merge Worker 	fputs(" --hashlimit-mode", stdout);
1060*a71a9546SAutomerger Merge Worker 	print_mode(r->cfg.mode, ',');
1061*a71a9546SAutomerger Merge Worker 
1062*a71a9546SAutomerger Merge Worker 	printf(" --hashlimit-name %s", r->name);
1063*a71a9546SAutomerger Merge Worker 
1064*a71a9546SAutomerger Merge Worker 	if (r->cfg.size)
1065*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-size %u", r->cfg.size);
1066*a71a9546SAutomerger Merge Worker 	if (r->cfg.max)
1067*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-max %u", r->cfg.max);
1068*a71a9546SAutomerger Merge Worker 	if (r->cfg.gc_interval != XT_HASHLIMIT_GCINTERVAL)
1069*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-gcinterval %u", r->cfg.gc_interval);
1070*a71a9546SAutomerger Merge Worker 	if (r->cfg.expire != quantum)
1071*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-expire %u", r->cfg.expire);
1072*a71a9546SAutomerger Merge Worker }
1073*a71a9546SAutomerger Merge Worker 
1074*a71a9546SAutomerger Merge Worker static void
hashlimit_mt_save(const struct hashlimit_cfg3 * cfg,const char * name,unsigned int dmask,int revision)1075*a71a9546SAutomerger Merge Worker hashlimit_mt_save(const struct hashlimit_cfg3 *cfg, const char* name, unsigned int dmask, int revision)
1076*a71a9546SAutomerger Merge Worker {
1077*a71a9546SAutomerger Merge Worker 	uint32_t quantum;
1078*a71a9546SAutomerger Merge Worker 
1079*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_INVERT)
1080*a71a9546SAutomerger Merge Worker 		fputs(" --hashlimit-above", stdout);
1081*a71a9546SAutomerger Merge Worker 	else
1082*a71a9546SAutomerger Merge Worker 		fputs(" --hashlimit-upto", stdout);
1083*a71a9546SAutomerger Merge Worker 
1084*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_BYTES) {
1085*a71a9546SAutomerger Merge Worker 		quantum = print_bytes(cfg->avg, cfg->burst, "--hashlimit-");
1086*a71a9546SAutomerger Merge Worker 	} else {
1087*a71a9546SAutomerger Merge Worker 		quantum = print_rate(cfg->avg, revision);
1088*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-burst %llu", cfg->burst);
1089*a71a9546SAutomerger Merge Worker 	}
1090*a71a9546SAutomerger Merge Worker 
1091*a71a9546SAutomerger Merge Worker 	if (cfg->mode & (XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT |
1092*a71a9546SAutomerger Merge Worker 	    XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT)) {
1093*a71a9546SAutomerger Merge Worker 		fputs(" --hashlimit-mode", stdout);
1094*a71a9546SAutomerger Merge Worker 		print_mode(cfg->mode, ',');
1095*a71a9546SAutomerger Merge Worker 	}
1096*a71a9546SAutomerger Merge Worker 
1097*a71a9546SAutomerger Merge Worker 	printf(" --hashlimit-name %s", name);
1098*a71a9546SAutomerger Merge Worker 
1099*a71a9546SAutomerger Merge Worker 	if (cfg->size != 0)
1100*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-size %u", cfg->size);
1101*a71a9546SAutomerger Merge Worker 	if (cfg->max != 0)
1102*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-max %u", cfg->max);
1103*a71a9546SAutomerger Merge Worker 	if (cfg->gc_interval != XT_HASHLIMIT_GCINTERVAL)
1104*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-gcinterval %u", cfg->gc_interval);
1105*a71a9546SAutomerger Merge Worker 	if (cfg->expire != quantum)
1106*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-htable-expire %u", cfg->expire);
1107*a71a9546SAutomerger Merge Worker 
1108*a71a9546SAutomerger Merge Worker 	if (cfg->srcmask != dmask)
1109*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-srcmask %u", cfg->srcmask);
1110*a71a9546SAutomerger Merge Worker 	if (cfg->dstmask != dmask)
1111*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-dstmask %u", cfg->dstmask);
1112*a71a9546SAutomerger Merge Worker 
1113*a71a9546SAutomerger Merge Worker 	if ((revision == 3) && (cfg->mode & XT_HASHLIMIT_RATE_MATCH))
1114*a71a9546SAutomerger Merge Worker 		printf(" --hashlimit-rate-match");
1115*a71a9546SAutomerger Merge Worker 
1116*a71a9546SAutomerger Merge Worker 	if ((revision == 3) && (cfg->mode & XT_HASHLIMIT_RATE_MATCH))
1117*a71a9546SAutomerger Merge Worker 		if (cfg->interval != 1)
1118*a71a9546SAutomerger Merge Worker 			printf(" --hashlimit-rate-interval %u", cfg->interval);
1119*a71a9546SAutomerger Merge Worker }
1120*a71a9546SAutomerger Merge Worker 
1121*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_save_v1(const void * ip,const struct xt_entry_match * match)1122*a71a9546SAutomerger Merge Worker hashlimit_mt4_save_v1(const void *ip, const struct xt_entry_match *match)
1123*a71a9546SAutomerger Merge Worker {
1124*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
1125*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1126*a71a9546SAutomerger Merge Worker 	int ret;
1127*a71a9546SAutomerger Merge Worker 
1128*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
1129*a71a9546SAutomerger Merge Worker 
1130*a71a9546SAutomerger Merge Worker 	if (ret)
1131*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1132*a71a9546SAutomerger Merge Worker 
1133*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&cfg, info->name, 32, 1);
1134*a71a9546SAutomerger Merge Worker }
1135*a71a9546SAutomerger Merge Worker 
1136*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_save_v1(const void * ip,const struct xt_entry_match * match)1137*a71a9546SAutomerger Merge Worker hashlimit_mt6_save_v1(const void *ip, const struct xt_entry_match *match)
1138*a71a9546SAutomerger Merge Worker {
1139*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info = (const void *)match->data;
1140*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1141*a71a9546SAutomerger Merge Worker 	int ret;
1142*a71a9546SAutomerger Merge Worker 
1143*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 1);
1144*a71a9546SAutomerger Merge Worker 
1145*a71a9546SAutomerger Merge Worker 	if (ret)
1146*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1147*a71a9546SAutomerger Merge Worker 
1148*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&cfg, info->name, 128, 1);
1149*a71a9546SAutomerger Merge Worker }
1150*a71a9546SAutomerger Merge Worker 
1151*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_save_v2(const void * ip,const struct xt_entry_match * match)1152*a71a9546SAutomerger Merge Worker hashlimit_mt4_save_v2(const void *ip, const struct xt_entry_match *match)
1153*a71a9546SAutomerger Merge Worker {
1154*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
1155*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1156*a71a9546SAutomerger Merge Worker 	int ret;
1157*a71a9546SAutomerger Merge Worker 
1158*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 2);
1159*a71a9546SAutomerger Merge Worker 
1160*a71a9546SAutomerger Merge Worker 	if (ret)
1161*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1162*a71a9546SAutomerger Merge Worker 
1163*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&cfg, info->name, 32, 2);
1164*a71a9546SAutomerger Merge Worker }
1165*a71a9546SAutomerger Merge Worker 
1166*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_save_v2(const void * ip,const struct xt_entry_match * match)1167*a71a9546SAutomerger Merge Worker hashlimit_mt6_save_v2(const void *ip, const struct xt_entry_match *match)
1168*a71a9546SAutomerger Merge Worker {
1169*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info = (const void *)match->data;
1170*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1171*a71a9546SAutomerger Merge Worker 	int ret;
1172*a71a9546SAutomerger Merge Worker 
1173*a71a9546SAutomerger Merge Worker 	ret = cfg_copy(&cfg, (const void *)&info->cfg, 2);
1174*a71a9546SAutomerger Merge Worker 
1175*a71a9546SAutomerger Merge Worker 	if (ret)
1176*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1177*a71a9546SAutomerger Merge Worker 
1178*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&cfg, info->name, 128, 2);
1179*a71a9546SAutomerger Merge Worker }
1180*a71a9546SAutomerger Merge Worker 
1181*a71a9546SAutomerger Merge Worker static void
hashlimit_mt4_save(const void * ip,const struct xt_entry_match * match)1182*a71a9546SAutomerger Merge Worker hashlimit_mt4_save(const void *ip, const struct xt_entry_match *match)
1183*a71a9546SAutomerger Merge Worker {
1184*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info = (const void *)match->data;
1185*a71a9546SAutomerger Merge Worker 
1186*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&info->cfg, info->name, 32, 3);
1187*a71a9546SAutomerger Merge Worker }
1188*a71a9546SAutomerger Merge Worker 
1189*a71a9546SAutomerger Merge Worker static void
hashlimit_mt6_save(const void * ip,const struct xt_entry_match * match)1190*a71a9546SAutomerger Merge Worker hashlimit_mt6_save(const void *ip, const struct xt_entry_match *match)
1191*a71a9546SAutomerger Merge Worker {
1192*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info = (const void *)match->data;
1193*a71a9546SAutomerger Merge Worker 
1194*a71a9546SAutomerger Merge Worker 	hashlimit_mt_save(&info->cfg, info->name, 128, 3);
1195*a71a9546SAutomerger Merge Worker }
1196*a71a9546SAutomerger Merge Worker 
1197*a71a9546SAutomerger Merge Worker static const struct rates rates_v1_xlate[] = {
1198*a71a9546SAutomerger Merge Worker 	{ "day", XT_HASHLIMIT_SCALE * 24 * 60 * 60 },
1199*a71a9546SAutomerger Merge Worker 	{ "hour", XT_HASHLIMIT_SCALE * 60 * 60 },
1200*a71a9546SAutomerger Merge Worker 	{ "minute", XT_HASHLIMIT_SCALE * 60 },
1201*a71a9546SAutomerger Merge Worker 	{ "second", XT_HASHLIMIT_SCALE } };
1202*a71a9546SAutomerger Merge Worker 
1203*a71a9546SAutomerger Merge Worker static const struct rates rates_xlate[] = {
1204*a71a9546SAutomerger Merge Worker 	{ "day", XT_HASHLIMIT_SCALE_v2 * 24 * 60 * 60 },
1205*a71a9546SAutomerger Merge Worker 	{ "hour", XT_HASHLIMIT_SCALE_v2 * 60 * 60 },
1206*a71a9546SAutomerger Merge Worker 	{ "minute", XT_HASHLIMIT_SCALE_v2 * 60 },
1207*a71a9546SAutomerger Merge Worker 	{ "second", XT_HASHLIMIT_SCALE_v2 } };
1208*a71a9546SAutomerger Merge Worker 
print_packets_rate_xlate(struct xt_xlate * xl,uint64_t avg,int revision)1209*a71a9546SAutomerger Merge Worker static void print_packets_rate_xlate(struct xt_xlate *xl, uint64_t avg,
1210*a71a9546SAutomerger Merge Worker 				     int revision)
1211*a71a9546SAutomerger Merge Worker {
1212*a71a9546SAutomerger Merge Worker 	unsigned int i;
1213*a71a9546SAutomerger Merge Worker 	const struct rates *_rates = (revision == 1) ?
1214*a71a9546SAutomerger Merge Worker 		rates_v1_xlate : rates_xlate;
1215*a71a9546SAutomerger Merge Worker 
1216*a71a9546SAutomerger Merge Worker 	for (i = 1; i < ARRAY_SIZE(rates); ++i)
1217*a71a9546SAutomerger Merge Worker 		if (avg > _rates[i].mult ||
1218*a71a9546SAutomerger Merge Worker 		    _rates[i].mult / avg < _rates[i].mult % avg)
1219*a71a9546SAutomerger Merge Worker 			break;
1220*a71a9546SAutomerger Merge Worker 
1221*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " %" PRIu64 "/%s ",
1222*a71a9546SAutomerger Merge Worker 		     _rates[i-1].mult / avg, _rates[i-1].name);
1223*a71a9546SAutomerger Merge Worker }
1224*a71a9546SAutomerger Merge Worker 
print_bytes_rate_xlate(struct xt_xlate * xl,const struct hashlimit_cfg3 * cfg)1225*a71a9546SAutomerger Merge Worker static void print_bytes_rate_xlate(struct xt_xlate *xl,
1226*a71a9546SAutomerger Merge Worker 				   const struct hashlimit_cfg3 *cfg)
1227*a71a9546SAutomerger Merge Worker {
1228*a71a9546SAutomerger Merge Worker 	unsigned int i;
1229*a71a9546SAutomerger Merge Worker 	unsigned long long r;
1230*a71a9546SAutomerger Merge Worker 
1231*a71a9546SAutomerger Merge Worker 	r = cost_to_bytes(cfg->avg);
1232*a71a9546SAutomerger Merge Worker 
1233*a71a9546SAutomerger Merge Worker 	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
1234*a71a9546SAutomerger Merge Worker 		if (r >= units[i].thresh &&
1235*a71a9546SAutomerger Merge Worker 		    bytes_to_cost(r & ~(units[i].thresh - 1)) == cfg->avg)
1236*a71a9546SAutomerger Merge Worker 			break;
1237*a71a9546SAutomerger Merge Worker 
1238*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " %llu %sbytes/second", r / units[i].thresh,
1239*a71a9546SAutomerger Merge Worker 		     units[i].name);
1240*a71a9546SAutomerger Merge Worker 
1241*a71a9546SAutomerger Merge Worker 	r *= cfg->burst;
1242*a71a9546SAutomerger Merge Worker 	for (i = 0; i < ARRAY_SIZE(units) -1; ++i)
1243*a71a9546SAutomerger Merge Worker 		if (r >= units[i].thresh)
1244*a71a9546SAutomerger Merge Worker 			break;
1245*a71a9546SAutomerger Merge Worker 
1246*a71a9546SAutomerger Merge Worker 	if (cfg->burst > 0)
1247*a71a9546SAutomerger Merge Worker 		xt_xlate_add(xl, " burst %llu %sbytes", r / units[i].thresh,
1248*a71a9546SAutomerger Merge Worker 			     units[i].name);
1249*a71a9546SAutomerger Merge Worker }
1250*a71a9546SAutomerger Merge Worker 
hashlimit_print_subnet_xlate(struct xt_xlate * xl,uint32_t nsub,int family)1251*a71a9546SAutomerger Merge Worker static void hashlimit_print_subnet_xlate(struct xt_xlate *xl,
1252*a71a9546SAutomerger Merge Worker 					 uint32_t nsub, int family)
1253*a71a9546SAutomerger Merge Worker {
1254*a71a9546SAutomerger Merge Worker 	char sep = (family == NFPROTO_IPV4) ? '.' : ':';
1255*a71a9546SAutomerger Merge Worker 	char *fmt = (family == NFPROTO_IPV4) ? "%u" : "%04x";
1256*a71a9546SAutomerger Merge Worker 	unsigned int nblocks = (family == NFPROTO_IPV4) ? 4 : 8;
1257*a71a9546SAutomerger Merge Worker 	unsigned int nbits = (family == NFPROTO_IPV4) ? 8 : 16;
1258*a71a9546SAutomerger Merge Worker 	unsigned int acm, i;
1259*a71a9546SAutomerger Merge Worker 
1260*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " and ");
1261*a71a9546SAutomerger Merge Worker 	while (nblocks--) {
1262*a71a9546SAutomerger Merge Worker 		acm = 0;
1263*a71a9546SAutomerger Merge Worker 
1264*a71a9546SAutomerger Merge Worker 		for (i = 0; i < nbits; i++) {
1265*a71a9546SAutomerger Merge Worker 			acm <<= 1;
1266*a71a9546SAutomerger Merge Worker 
1267*a71a9546SAutomerger Merge Worker 			if (nsub > 0) {
1268*a71a9546SAutomerger Merge Worker 				acm++;
1269*a71a9546SAutomerger Merge Worker 				nsub--;
1270*a71a9546SAutomerger Merge Worker 			}
1271*a71a9546SAutomerger Merge Worker 		}
1272*a71a9546SAutomerger Merge Worker 
1273*a71a9546SAutomerger Merge Worker 		xt_xlate_add_nospc(xl, fmt, acm);
1274*a71a9546SAutomerger Merge Worker 		if (nblocks > 0)
1275*a71a9546SAutomerger Merge Worker 			xt_xlate_add(xl, "%c", sep);
1276*a71a9546SAutomerger Merge Worker 	}
1277*a71a9546SAutomerger Merge Worker }
1278*a71a9546SAutomerger Merge Worker 
1279*a71a9546SAutomerger Merge Worker static const char *const hashlimit_modes4_xlate[] = {
1280*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_DIP]	= "ip daddr",
1281*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_DPT]	= "tcp dport",
1282*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_SIP]	= "ip saddr",
1283*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_SPT]	= "tcp sport",
1284*a71a9546SAutomerger Merge Worker };
1285*a71a9546SAutomerger Merge Worker 
1286*a71a9546SAutomerger Merge Worker static const char *const hashlimit_modes6_xlate[] = {
1287*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_DIP]	= "ip6 daddr",
1288*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_DPT]	= "tcp dport",
1289*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_SIP]	= "ip6 saddr",
1290*a71a9546SAutomerger Merge Worker 	[XT_HASHLIMIT_HASH_SPT]	= "tcp sport",
1291*a71a9546SAutomerger Merge Worker };
1292*a71a9546SAutomerger Merge Worker 
hashlimit_mode_xlate(struct xt_xlate * xl,uint32_t mode,int family,unsigned int nsrc,unsigned int ndst)1293*a71a9546SAutomerger Merge Worker static int hashlimit_mode_xlate(struct xt_xlate *xl,
1294*a71a9546SAutomerger Merge Worker 				uint32_t mode, int family,
1295*a71a9546SAutomerger Merge Worker 				unsigned int nsrc, unsigned int ndst)
1296*a71a9546SAutomerger Merge Worker {
1297*a71a9546SAutomerger Merge Worker 	const char * const *_modes = (family == NFPROTO_IPV4) ?
1298*a71a9546SAutomerger Merge Worker 		hashlimit_modes4_xlate : hashlimit_modes6_xlate;
1299*a71a9546SAutomerger Merge Worker 	bool prevopt = false;
1300*a71a9546SAutomerger Merge Worker 	unsigned int mask;
1301*a71a9546SAutomerger Merge Worker 
1302*a71a9546SAutomerger Merge Worker 	mode &= ~XT_HASHLIMIT_INVERT & ~XT_HASHLIMIT_BYTES;
1303*a71a9546SAutomerger Merge Worker 
1304*a71a9546SAutomerger Merge Worker 	for (mask = 1; mode > 0; mask <<= 1) {
1305*a71a9546SAutomerger Merge Worker 		if (!(mode & mask))
1306*a71a9546SAutomerger Merge Worker 			continue;
1307*a71a9546SAutomerger Merge Worker 
1308*a71a9546SAutomerger Merge Worker 		if (!prevopt) {
1309*a71a9546SAutomerger Merge Worker 			xt_xlate_add(xl, " ");
1310*a71a9546SAutomerger Merge Worker 			prevopt = true;
1311*a71a9546SAutomerger Merge Worker 		}
1312*a71a9546SAutomerger Merge Worker 		else {
1313*a71a9546SAutomerger Merge Worker 			xt_xlate_add(xl, " . ");
1314*a71a9546SAutomerger Merge Worker 		}
1315*a71a9546SAutomerger Merge Worker 
1316*a71a9546SAutomerger Merge Worker 		xt_xlate_add(xl, "%s", _modes[mask]);
1317*a71a9546SAutomerger Merge Worker 
1318*a71a9546SAutomerger Merge Worker 		if (mask == XT_HASHLIMIT_HASH_DIP &&
1319*a71a9546SAutomerger Merge Worker 		    ((family == NFPROTO_IPV4 && ndst != 32) ||
1320*a71a9546SAutomerger Merge Worker 		     (family == NFPROTO_IPV6 && ndst != 128)))
1321*a71a9546SAutomerger Merge Worker 			hashlimit_print_subnet_xlate(xl, ndst, family);
1322*a71a9546SAutomerger Merge Worker 		else if (mask == XT_HASHLIMIT_HASH_SIP &&
1323*a71a9546SAutomerger Merge Worker 			 ((family == NFPROTO_IPV4 && nsrc != 32) ||
1324*a71a9546SAutomerger Merge Worker 			  (family == NFPROTO_IPV6 && nsrc != 128)))
1325*a71a9546SAutomerger Merge Worker 			hashlimit_print_subnet_xlate(xl, nsrc, family);
1326*a71a9546SAutomerger Merge Worker 
1327*a71a9546SAutomerger Merge Worker 		mode &= ~mask;
1328*a71a9546SAutomerger Merge Worker 	}
1329*a71a9546SAutomerger Merge Worker 
1330*a71a9546SAutomerger Merge Worker 	return prevopt;
1331*a71a9546SAutomerger Merge Worker }
1332*a71a9546SAutomerger Merge Worker 
hashlimit_mt_xlate(struct xt_xlate * xl,const char * name,const struct hashlimit_cfg3 * cfg,int revision,int family)1333*a71a9546SAutomerger Merge Worker static int hashlimit_mt_xlate(struct xt_xlate *xl, const char *name,
1334*a71a9546SAutomerger Merge Worker 			      const struct hashlimit_cfg3 *cfg,
1335*a71a9546SAutomerger Merge Worker 			      int revision, int family)
1336*a71a9546SAutomerger Merge Worker {
1337*a71a9546SAutomerger Merge Worker 	int ret = 1;
1338*a71a9546SAutomerger Merge Worker 
1339*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, "meter %s {", name);
1340*a71a9546SAutomerger Merge Worker 	ret = hashlimit_mode_xlate(xl, cfg->mode, family,
1341*a71a9546SAutomerger Merge Worker 				   cfg->srcmask, cfg->dstmask);
1342*a71a9546SAutomerger Merge Worker 	if (cfg->expire != 1000)
1343*a71a9546SAutomerger Merge Worker 		xt_xlate_add(xl, " timeout %us", cfg->expire / 1000);
1344*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " limit rate");
1345*a71a9546SAutomerger Merge Worker 
1346*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_INVERT)
1347*a71a9546SAutomerger Merge Worker 		xt_xlate_add(xl, " over");
1348*a71a9546SAutomerger Merge Worker 
1349*a71a9546SAutomerger Merge Worker 	if (cfg->mode & XT_HASHLIMIT_BYTES)
1350*a71a9546SAutomerger Merge Worker 		print_bytes_rate_xlate(xl, cfg);
1351*a71a9546SAutomerger Merge Worker 	else {
1352*a71a9546SAutomerger Merge Worker 		print_packets_rate_xlate(xl, cfg->avg, revision);
1353*a71a9546SAutomerger Merge Worker 		if (cfg->burst != XT_HASHLIMIT_BURST)
1354*a71a9546SAutomerger Merge Worker 			xt_xlate_add(xl, "burst %" PRIu64 " packets", (uint64_t)cfg->burst);
1355*a71a9546SAutomerger Merge Worker 
1356*a71a9546SAutomerger Merge Worker 	}
1357*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, "}");
1358*a71a9546SAutomerger Merge Worker 
1359*a71a9546SAutomerger Merge Worker 	return ret;
1360*a71a9546SAutomerger Merge Worker }
1361*a71a9546SAutomerger Merge Worker 
hashlimit_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1362*a71a9546SAutomerger Merge Worker static int hashlimit_xlate(struct xt_xlate *xl,
1363*a71a9546SAutomerger Merge Worker 			   const struct xt_xlate_mt_params *params)
1364*a71a9546SAutomerger Merge Worker {
1365*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_info *info = (const void *)params->match->data;
1366*a71a9546SAutomerger Merge Worker 	int ret = 1;
1367*a71a9546SAutomerger Merge Worker 
1368*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, "meter %s {", info->name);
1369*a71a9546SAutomerger Merge Worker 	ret = hashlimit_mode_xlate(xl, info->cfg.mode, NFPROTO_IPV4, 32, 32);
1370*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " timeout %us limit rate", info->cfg.expire / 1000);
1371*a71a9546SAutomerger Merge Worker 	print_packets_rate_xlate(xl, info->cfg.avg, 1);
1372*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, " burst %u packets", info->cfg.burst);
1373*a71a9546SAutomerger Merge Worker 	xt_xlate_add(xl, "}");
1374*a71a9546SAutomerger Merge Worker 
1375*a71a9546SAutomerger Merge Worker 	return ret;
1376*a71a9546SAutomerger Merge Worker }
1377*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_xlate_v1(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1378*a71a9546SAutomerger Merge Worker static int hashlimit_mt4_xlate_v1(struct xt_xlate *xl,
1379*a71a9546SAutomerger Merge Worker 				  const struct xt_xlate_mt_params *params)
1380*a71a9546SAutomerger Merge Worker {
1381*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info =
1382*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1383*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1384*a71a9546SAutomerger Merge Worker 
1385*a71a9546SAutomerger Merge Worker 	if (cfg_copy(&cfg, (const void *)&info->cfg, 1))
1386*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1387*a71a9546SAutomerger Merge Worker 
1388*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &cfg, 1, NFPROTO_IPV4);
1389*a71a9546SAutomerger Merge Worker }
1390*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_xlate_v1(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1391*a71a9546SAutomerger Merge Worker static int hashlimit_mt6_xlate_v1(struct xt_xlate *xl,
1392*a71a9546SAutomerger Merge Worker 				  const struct xt_xlate_mt_params *params)
1393*a71a9546SAutomerger Merge Worker {
1394*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo1 *info =
1395*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1396*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1397*a71a9546SAutomerger Merge Worker 
1398*a71a9546SAutomerger Merge Worker 	if (cfg_copy(&cfg, (const void *)&info->cfg, 1))
1399*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1400*a71a9546SAutomerger Merge Worker 
1401*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &cfg, 1, NFPROTO_IPV6);
1402*a71a9546SAutomerger Merge Worker }
1403*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_xlate_v2(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1404*a71a9546SAutomerger Merge Worker static int hashlimit_mt4_xlate_v2(struct xt_xlate *xl,
1405*a71a9546SAutomerger Merge Worker 				  const struct xt_xlate_mt_params *params)
1406*a71a9546SAutomerger Merge Worker {
1407*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info =
1408*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1409*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1410*a71a9546SAutomerger Merge Worker 
1411*a71a9546SAutomerger Merge Worker 	if (cfg_copy(&cfg, (const void *)&info->cfg, 2))
1412*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1413*a71a9546SAutomerger Merge Worker 
1414*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &cfg, 2, NFPROTO_IPV4);
1415*a71a9546SAutomerger Merge Worker }
1416*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_xlate_v2(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1417*a71a9546SAutomerger Merge Worker static int hashlimit_mt6_xlate_v2(struct xt_xlate *xl,
1418*a71a9546SAutomerger Merge Worker 				  const struct xt_xlate_mt_params *params)
1419*a71a9546SAutomerger Merge Worker {
1420*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo2 *info =
1421*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1422*a71a9546SAutomerger Merge Worker 	struct hashlimit_cfg3 cfg;
1423*a71a9546SAutomerger Merge Worker 
1424*a71a9546SAutomerger Merge Worker 	if (cfg_copy(&cfg, (const void *)&info->cfg, 2))
1425*a71a9546SAutomerger Merge Worker 		xtables_error(OTHER_PROBLEM, "unknown revision");
1426*a71a9546SAutomerger Merge Worker 
1427*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &cfg, 2, NFPROTO_IPV6);
1428*a71a9546SAutomerger Merge Worker }
1429*a71a9546SAutomerger Merge Worker 
hashlimit_mt4_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1430*a71a9546SAutomerger Merge Worker static int hashlimit_mt4_xlate(struct xt_xlate *xl,
1431*a71a9546SAutomerger Merge Worker 			       const struct xt_xlate_mt_params *params)
1432*a71a9546SAutomerger Merge Worker {
1433*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info =
1434*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1435*a71a9546SAutomerger Merge Worker 
1436*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &info->cfg, 3, NFPROTO_IPV4);
1437*a71a9546SAutomerger Merge Worker }
1438*a71a9546SAutomerger Merge Worker 
hashlimit_mt6_xlate(struct xt_xlate * xl,const struct xt_xlate_mt_params * params)1439*a71a9546SAutomerger Merge Worker static int hashlimit_mt6_xlate(struct xt_xlate *xl,
1440*a71a9546SAutomerger Merge Worker 			       const struct xt_xlate_mt_params *params)
1441*a71a9546SAutomerger Merge Worker {
1442*a71a9546SAutomerger Merge Worker 	const struct xt_hashlimit_mtinfo3 *info =
1443*a71a9546SAutomerger Merge Worker 		(const void *)params->match->data;
1444*a71a9546SAutomerger Merge Worker 
1445*a71a9546SAutomerger Merge Worker 	return hashlimit_mt_xlate(xl, info->name, &info->cfg, 3, NFPROTO_IPV6);
1446*a71a9546SAutomerger Merge Worker }
1447*a71a9546SAutomerger Merge Worker 
1448*a71a9546SAutomerger Merge Worker static struct xtables_match hashlimit_mt_reg[] = {
1449*a71a9546SAutomerger Merge Worker 	{
1450*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_UNSPEC,
1451*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1452*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1453*a71a9546SAutomerger Merge Worker 		.revision      = 0,
1454*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_info)),
1455*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_info, hinfo),
1456*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_help,
1457*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_init,
1458*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_parse,
1459*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_check,
1460*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_print,
1461*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_save,
1462*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_opts,
1463*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1464*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_xlate,
1465*a71a9546SAutomerger Merge Worker 	},
1466*a71a9546SAutomerger Merge Worker 	{
1467*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1468*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1469*a71a9546SAutomerger Merge Worker 		.revision      = 1,
1470*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV4,
1471*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
1472*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
1473*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help,
1474*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt4_init_v1,
1475*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse_v1,
1476*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check_v1,
1477*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt4_print_v1,
1478*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt4_save_v1,
1479*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts_v1,
1480*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1481*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt4_xlate_v1,
1482*a71a9546SAutomerger Merge Worker 	},
1483*a71a9546SAutomerger Merge Worker 	{
1484*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1485*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1486*a71a9546SAutomerger Merge Worker 		.revision      = 1,
1487*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV6,
1488*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo1)),
1489*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo1, hinfo),
1490*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help,
1491*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt6_init_v1,
1492*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse_v1,
1493*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check_v1,
1494*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt6_print_v1,
1495*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt6_save_v1,
1496*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts_v1,
1497*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1498*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt6_xlate_v1,
1499*a71a9546SAutomerger Merge Worker 	},
1500*a71a9546SAutomerger Merge Worker 	{
1501*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1502*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1503*a71a9546SAutomerger Merge Worker 		.revision      = 2,
1504*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV4,
1505*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
1506*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
1507*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help,
1508*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt4_init_v2,
1509*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse_v2,
1510*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check_v2,
1511*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt4_print_v2,
1512*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt4_save_v2,
1513*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts_v2,
1514*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1515*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt4_xlate_v2,
1516*a71a9546SAutomerger Merge Worker 	},
1517*a71a9546SAutomerger Merge Worker 	{
1518*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1519*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1520*a71a9546SAutomerger Merge Worker 		.revision      = 2,
1521*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV6,
1522*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo2)),
1523*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo2, hinfo),
1524*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help,
1525*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt6_init_v2,
1526*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse_v2,
1527*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check_v2,
1528*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt6_print_v2,
1529*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt6_save_v2,
1530*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts_v2,
1531*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1532*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt6_xlate_v2,
1533*a71a9546SAutomerger Merge Worker 	},
1534*a71a9546SAutomerger Merge Worker 	{
1535*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1536*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1537*a71a9546SAutomerger Merge Worker 		.revision      = 3,
1538*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV4,
1539*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo3)),
1540*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo3, hinfo),
1541*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help_v3,
1542*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt4_init,
1543*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse,
1544*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check,
1545*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt4_print,
1546*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt4_save,
1547*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts,
1548*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1549*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt4_xlate,
1550*a71a9546SAutomerger Merge Worker 	},
1551*a71a9546SAutomerger Merge Worker 	{
1552*a71a9546SAutomerger Merge Worker 		.version       = XTABLES_VERSION,
1553*a71a9546SAutomerger Merge Worker 		.name          = "hashlimit",
1554*a71a9546SAutomerger Merge Worker 		.revision      = 3,
1555*a71a9546SAutomerger Merge Worker 		.family        = NFPROTO_IPV6,
1556*a71a9546SAutomerger Merge Worker 		.size          = XT_ALIGN(sizeof(struct xt_hashlimit_mtinfo3)),
1557*a71a9546SAutomerger Merge Worker 		.userspacesize = offsetof(struct xt_hashlimit_mtinfo3, hinfo),
1558*a71a9546SAutomerger Merge Worker 		.help          = hashlimit_mt_help_v3,
1559*a71a9546SAutomerger Merge Worker 		.init          = hashlimit_mt6_init,
1560*a71a9546SAutomerger Merge Worker 		.x6_parse      = hashlimit_mt_parse,
1561*a71a9546SAutomerger Merge Worker 		.x6_fcheck     = hashlimit_mt_check,
1562*a71a9546SAutomerger Merge Worker 		.print         = hashlimit_mt6_print,
1563*a71a9546SAutomerger Merge Worker 		.save          = hashlimit_mt6_save,
1564*a71a9546SAutomerger Merge Worker 		.x6_options    = hashlimit_mt_opts,
1565*a71a9546SAutomerger Merge Worker 		.udata_size    = sizeof(struct hashlimit_mt_udata),
1566*a71a9546SAutomerger Merge Worker 		.xlate         = hashlimit_mt6_xlate,
1567*a71a9546SAutomerger Merge Worker 	},
1568*a71a9546SAutomerger Merge Worker };
1569*a71a9546SAutomerger Merge Worker 
_init(void)1570*a71a9546SAutomerger Merge Worker void _init(void)
1571*a71a9546SAutomerger Merge Worker {
1572*a71a9546SAutomerger Merge Worker 	xtables_register_matches(hashlimit_mt_reg, ARRAY_SIZE(hashlimit_mt_reg));
1573*a71a9546SAutomerger Merge Worker }
1574