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