1 #include <stdbool.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <xtables.h>
5 #include <linux/netfilter/xt_recent.h>
6 
7 enum {
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
29 static 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
55 static 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 
recent_help(void)82 static 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>.\n");
108 }
109 
110 enum {
111 	XT_RECENT_REV_0 = 0,
112 	XT_RECENT_REV_1,
113 };
114 
recent_init(struct xt_entry_match * match,unsigned int rev)115 static 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 
recent_parse(struct xt_option_call * cb)130 static 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 
recent_check(struct xt_fcheck_call * cb)171 static 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 
recent_print(const void * ip,const struct xt_entry_match * match,unsigned int family)179 static 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 
recent_save(const void * ip,const struct xt_entry_match * match,unsigned int family)220 static 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 
recent_init_v0(struct xt_entry_match * match)261 static void recent_init_v0(struct xt_entry_match *match)
262 {
263 	recent_init(match, XT_RECENT_REV_0);
264 }
265 
recent_init_v1(struct xt_entry_match * match)266 static void recent_init_v1(struct xt_entry_match *match)
267 {
268 	recent_init(match, XT_RECENT_REV_1);
269 }
270 
recent_save_v0(const void * ip,const struct xt_entry_match * match)271 static void recent_save_v0(const void *ip, const struct xt_entry_match *match)
272 {
273 	recent_save(ip, match, NFPROTO_UNSPEC);
274 }
275 
recent_save_v4(const void * ip,const struct xt_entry_match * match)276 static void recent_save_v4(const void *ip, const struct xt_entry_match *match)
277 {
278 	recent_save(ip, match, NFPROTO_IPV4);
279 }
280 
recent_save_v6(const void * ip,const struct xt_entry_match * match)281 static void recent_save_v6(const void *ip, const struct xt_entry_match *match)
282 {
283 	recent_save(ip, match, NFPROTO_IPV6);
284 }
285 
recent_print_v0(const void * ip,const struct xt_entry_match * match,int numeric)286 static 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 
recent_print_v4(const void * ip,const struct xt_entry_match * match,int numeric)292 static 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 
recent_print_v6(const void * ip,const struct xt_entry_match * match,int numeric)298 static 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 
304 static 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 
_init(void)352 void _init(void)
353 {
354 	xtables_register_matches(recent_mt_reg, ARRAY_SIZE(recent_mt_reg));
355 }
356