libxt_recent.c revision 74ded7257e5da5e309844d386290f24ae91950a6
1#include <stdbool.h>
2#include <stdio.h>
3#include <string.h>
4#include <xtables.h>
5#include <linux/netfilter/xt_recent.h>
6
7enum {
8	O_SET = 0,
9	O_RCHECK,
10	O_UPDATE,
11	O_REMOVE,
12	O_SECONDS,
13	O_REAP,
14	O_HITCOUNT,
15	O_RTTL,
16	O_NAME,
17	O_RSOURCE,
18	O_RDEST,
19	O_MASK,
20	F_SET    = 1 << O_SET,
21	F_RCHECK = 1 << O_RCHECK,
22	F_UPDATE = 1 << O_UPDATE,
23	F_REMOVE = 1 << O_REMOVE,
24	F_SECONDS = 1 << O_SECONDS,
25	F_ANY_OP = F_SET | F_RCHECK | F_UPDATE | F_REMOVE,
26};
27
28#define s struct xt_recent_mtinfo
29static const struct xt_option_entry recent_opts_v0[] = {
30	{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
31	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
32	{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
33	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
34	{.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE,
35	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
36	{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
37	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
38	{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
39	 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds), .min = 1},
40	{.name = "reap", .id = O_REAP, .type = XTTYPE_NONE,
41	 .also = F_SECONDS },
42	{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
43	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
44	{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
45	 .excl = F_SET | F_REMOVE},
46	{.name = "name", .id = O_NAME, .type = XTTYPE_STRING,
47	 .flags = XTOPT_PUT, XTOPT_POINTER(s, name)},
48	{.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE},
49	{.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE},
50	XTOPT_TABLEEND,
51};
52#undef s
53
54#define s struct xt_recent_mtinfo_v1
55static const struct xt_option_entry recent_opts_v1[] = {
56	{.name = "set", .id = O_SET, .type = XTTYPE_NONE,
57	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
58	{.name = "rcheck", .id = O_RCHECK, .type = XTTYPE_NONE,
59	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
60	{.name = "update", .id = O_UPDATE, .type = XTTYPE_NONE,
61	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
62	{.name = "remove", .id = O_REMOVE, .type = XTTYPE_NONE,
63	 .excl = F_ANY_OP, .flags = XTOPT_INVERT},
64	{.name = "seconds", .id = O_SECONDS, .type = XTTYPE_UINT32,
65	 .flags = XTOPT_PUT, XTOPT_POINTER(s, seconds)},
66	{.name = "hitcount", .id = O_HITCOUNT, .type = XTTYPE_UINT32,
67	 .flags = XTOPT_PUT, XTOPT_POINTER(s, hit_count)},
68	{.name = "rttl", .id = O_RTTL, .type = XTTYPE_NONE,
69	 .excl = F_SET | F_REMOVE},
70	{.name = "name", .id = O_NAME, .type = XTTYPE_STRING,
71	 .flags = XTOPT_PUT, XTOPT_POINTER(s, name)},
72	{.name = "rsource", .id = O_RSOURCE, .type = XTTYPE_NONE},
73	{.name = "rdest", .id = O_RDEST, .type = XTTYPE_NONE},
74	{.name = "mask", .id = O_MASK, .type = XTTYPE_HOST,
75	 .flags = XTOPT_PUT, XTOPT_POINTER(s, mask)},
76	XTOPT_TABLEEND,
77};
78#undef s
79
80static void recent_help(void)
81{
82	printf(
83"recent match options:\n"
84"[!] --set                       Add source address to list, always matches.\n"
85"[!] --rcheck                    Match if source address in list.\n"
86"[!] --update                    Match if source address in list, also update last-seen time.\n"
87"[!] --remove                    Match if source address in list, also removes that address from list.\n"
88"    --seconds seconds           For check and update commands above.\n"
89"                                Specifies that the match will only occur if source address last seen within\n"
90"                                the last 'seconds' seconds.\n"
91"    --reap                      Purge entries older then 'seconds'.\n"
92"                                Can only be used in conjunction with the seconds option.\n"
93"    --hitcount hits             For check and update commands above.\n"
94"                                Specifies that the match will only occur if source address seen hits times.\n"
95"                                May be used in conjunction with the seconds option.\n"
96"    --rttl                      For check and update commands above.\n"
97"                                Specifies that the match will only occur if the source address and the TTL\n"
98"                                match between this packet and the one which was set.\n"
99"                                Useful if you have problems with people spoofing their source address in order\n"
100"                                to DoS you via this module.\n"
101"    --name name                 Name of the recent list to be used.  DEFAULT used if none given.\n"
102"    --rsource                   Match/Save the source address of each packet in the recent list table (default).\n"
103"    --rdest                     Match/Save the destination address of each packet in the recent list table.\n"
104"    --mask netmask              Netmask that will be applied to this recent list.\n"
105"xt_recent by: Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n");
106}
107
108enum {
109	XT_RECENT_REV_0 = 0,
110	XT_RECENT_REV_1,
111};
112
113static void recent_init(struct xt_entry_match *match, unsigned int rev)
114{
115	struct xt_recent_mtinfo *info = (struct xt_recent_mtinfo *)match->data;
116	struct xt_recent_mtinfo_v1 *info_v1 =
117		(struct xt_recent_mtinfo_v1 *)match->data;
118
119	strncpy(info->name,"DEFAULT", XT_RECENT_NAME_LEN);
120	/* even though XT_RECENT_NAME_LEN is currently defined as 200,
121	 * better be safe, than sorry */
122	info->name[XT_RECENT_NAME_LEN-1] = '\0';
123	info->side = XT_RECENT_SOURCE;
124	if (rev == XT_RECENT_REV_1)
125		memset(&info_v1->mask, 0xFF, sizeof(info_v1->mask));
126}
127
128static void recent_parse(struct xt_option_call *cb)
129{
130	struct xt_recent_mtinfo *info = cb->data;
131
132	xtables_option_parse(cb);
133	switch (cb->entry->id) {
134	case O_SET:
135		info->check_set |= XT_RECENT_SET;
136		if (cb->invert)
137			info->invert = true;
138		break;
139	case O_RCHECK:
140		info->check_set |= XT_RECENT_CHECK;
141		if (cb->invert)
142			info->invert = true;
143		break;
144	case O_UPDATE:
145		info->check_set |= XT_RECENT_UPDATE;
146		if (cb->invert)
147			info->invert = true;
148		break;
149	case O_REMOVE:
150		info->check_set |= XT_RECENT_REMOVE;
151		if (cb->invert)
152			info->invert = true;
153		break;
154	case O_RTTL:
155		info->check_set |= XT_RECENT_TTL;
156		break;
157	case O_RSOURCE:
158		info->side = XT_RECENT_SOURCE;
159		break;
160	case O_RDEST:
161		info->side = XT_RECENT_DEST;
162		break;
163	case O_REAP:
164		info->check_set |= XT_RECENT_REAP;
165		break;
166	}
167}
168
169static void recent_check(struct xt_fcheck_call *cb)
170{
171	if (!(cb->xflags & F_ANY_OP))
172		xtables_error(PARAMETER_PROBLEM,
173			"recent: you must specify one of `--set', `--rcheck' "
174			"`--update' or `--remove'");
175}
176
177static void recent_print(const void *ip, const struct xt_entry_match *match,
178                         unsigned int family)
179{
180	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
181
182	if (info->invert)
183		printf(" !");
184
185	printf(" recent:");
186	if (info->check_set & XT_RECENT_SET)
187		printf(" SET");
188	if (info->check_set & XT_RECENT_CHECK)
189		printf(" CHECK");
190	if (info->check_set & XT_RECENT_UPDATE)
191		printf(" UPDATE");
192	if (info->check_set & XT_RECENT_REMOVE)
193		printf(" REMOVE");
194	if(info->seconds) printf(" seconds: %d", info->seconds);
195	if (info->check_set & XT_RECENT_REAP)
196		printf(" reap");
197	if(info->hit_count) printf(" hit_count: %d", info->hit_count);
198	if (info->check_set & XT_RECENT_TTL)
199		printf(" TTL-Match");
200	if(info->name) printf(" name: %s", info->name);
201	if (info->side == XT_RECENT_SOURCE)
202		printf(" side: source");
203	if (info->side == XT_RECENT_DEST)
204		printf(" side: dest");
205
206	switch(family) {
207	case NFPROTO_IPV4:
208		printf(" mask: %s",
209			xtables_ipaddr_to_numeric(&info->mask.in));
210		break;
211	case NFPROTO_IPV6:
212		printf(" mask: %s",
213			xtables_ip6addr_to_numeric(&info->mask.in6));
214		break;
215	}
216}
217
218static void recent_save(const void *ip, const struct xt_entry_match *match,
219			unsigned int family)
220{
221	const struct xt_recent_mtinfo_v1 *info = (const void *)match->data;
222
223	if (info->invert)
224		printf(" !");
225
226	if (info->check_set & XT_RECENT_SET)
227		printf(" --set");
228	if (info->check_set & XT_RECENT_CHECK)
229		printf(" --rcheck");
230	if (info->check_set & XT_RECENT_UPDATE)
231		printf(" --update");
232	if (info->check_set & XT_RECENT_REMOVE)
233		printf(" --remove");
234	if(info->seconds) printf(" --seconds %d", info->seconds);
235	if (info->check_set & XT_RECENT_REAP)
236		printf(" --reap");
237	if(info->hit_count) printf(" --hitcount %d", info->hit_count);
238	if (info->check_set & XT_RECENT_TTL)
239		printf(" --rttl");
240	if(info->name) printf(" --name %s",info->name);
241
242	switch(family) {
243	case NFPROTO_IPV4:
244		printf(" --mask %s",
245			xtables_ipaddr_to_numeric(&info->mask.in));
246		break;
247	case NFPROTO_IPV6:
248		printf(" --mask %s",
249			xtables_ip6addr_to_numeric(&info->mask.in6));
250		break;
251	}
252
253	if (info->side == XT_RECENT_SOURCE)
254		printf(" --rsource");
255	if (info->side == XT_RECENT_DEST)
256		printf(" --rdest");
257}
258
259static void recent_init_v0(struct xt_entry_match *match)
260{
261	recent_init(match, XT_RECENT_REV_0);
262}
263
264static void recent_init_v1(struct xt_entry_match *match)
265{
266	recent_init(match, XT_RECENT_REV_1);
267}
268
269static void recent_save_v0(const void *ip, const struct xt_entry_match *match)
270{
271	recent_save(ip, match, NFPROTO_UNSPEC);
272}
273
274static void recent_save_v4(const void *ip, const struct xt_entry_match *match)
275{
276	recent_save(ip, match, NFPROTO_IPV4);
277}
278
279static void recent_save_v6(const void *ip, const struct xt_entry_match *match)
280{
281	recent_save(ip, match, NFPROTO_IPV6);
282}
283
284static void recent_print_v0(const void *ip, const struct xt_entry_match *match,
285			    int numeric)
286{
287	recent_print(ip, match, NFPROTO_UNSPEC);
288}
289
290static void recent_print_v4(const void *ip, const struct xt_entry_match *match,
291                         int numeric)
292{
293	recent_print(ip, match, NFPROTO_IPV4);
294}
295
296static void recent_print_v6(const void *ip, const struct xt_entry_match *match,
297                         int numeric)
298{
299	recent_print(ip, match, NFPROTO_IPV6);
300}
301
302static struct xtables_match recent_mt_reg[] = {
303	{
304		.name          = "recent",
305		.version       = XTABLES_VERSION,
306		.revision      = 0,
307		.family        = NFPROTO_UNSPEC,
308		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
309		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo)),
310		.help          = recent_help,
311		.init          = recent_init_v0,
312		.x6_parse      = recent_parse,
313		.x6_fcheck     = recent_check,
314		.print         = recent_print_v0,
315		.save          = recent_save_v0,
316		.x6_options    = recent_opts_v0,
317	},
318	{
319		.name          = "recent",
320		.version       = XTABLES_VERSION,
321		.revision      = 1,
322		.family        = NFPROTO_IPV4,
323		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
324		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
325		.help          = recent_help,
326		.init          = recent_init_v1,
327		.x6_parse      = recent_parse,
328		.x6_fcheck     = recent_check,
329		.print         = recent_print_v4,
330		.save          = recent_save_v4,
331		.x6_options    = recent_opts_v1,
332	},
333	{
334		.name          = "recent",
335		.version       = XTABLES_VERSION,
336		.revision      = 1,
337		.family        = NFPROTO_IPV6,
338		.size          = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
339		.userspacesize = XT_ALIGN(sizeof(struct xt_recent_mtinfo_v1)),
340		.help          = recent_help,
341		.init          = recent_init_v1,
342		.x6_parse      = recent_parse,
343		.x6_fcheck     = recent_check,
344		.print         = recent_print_v6,
345		.save          = recent_save_v6,
346		.x6_options    = recent_opts_v1,
347	},
348};
349
350void _init(void)
351{
352	xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
353}
354