libxt_dccp.c revision 181dead3f13befe02769ef479bcbb51801b7fc4e
1/* Shared library add-on to iptables for DCCP matching
2 *
3 * (C) 2005 by Harald Welte <laforge@netfilter.org>
4 *
5 * This program is distributed under the terms of GNU GPL v2, 1991
6 *
7 */
8#include <stdio.h>
9#include <string.h>
10#include <stdlib.h>
11#include <getopt.h>
12#include <netdb.h>
13#include <ctype.h>
14
15#include <xtables.h>
16#include <linux/dccp.h>
17#include <linux/netfilter/x_tables.h>
18#include <linux/netfilter/xt_dccp.h>
19
20#if 0
21#define DEBUGP(format, first...) printf(format, ##first)
22#define static
23#else
24#define DEBUGP(format, fist...)
25#endif
26
27/* Initialize the match. */
28static void dccp_init(struct xt_entry_match *m)
29{
30	struct xt_dccp_info *einfo = (struct xt_dccp_info *)m->data;
31
32	memset(einfo, 0, sizeof(struct xt_dccp_info));
33}
34
35static void dccp_help(void)
36{
37	printf(
38"DCCP match v%s options\n"
39" --source-port [!] port[:port]                          match source port(s)\n"
40" --sport ...\n"
41" --destination-port [!] port[:port]                     match destination port(s)\n"
42" --dport ...\n"
43,
44	IPTABLES_VERSION);
45}
46
47static const struct option dccp_opts[] = {
48	{ .name = "source-port", .has_arg = 1, .val = '1' },
49	{ .name = "sport", .has_arg = 1, .val = '1' },
50	{ .name = "destination-port", .has_arg = 1, .val = '2' },
51	{ .name = "dport", .has_arg = 1, .val = '2' },
52	{ .name = "dccp-types", .has_arg = 1, .val = '3' },
53	{ .name = "dccp-option", .has_arg = 1, .val = '4' },
54	{ }
55};
56
57static void
58parse_dccp_ports(const char *portstring,
59		 u_int16_t *ports)
60{
61	char *buffer;
62	char *cp;
63
64	buffer = strdup(portstring);
65	DEBUGP("%s\n", portstring);
66	if ((cp = strchr(buffer, ':')) == NULL) {
67		ports[0] = ports[1] = parse_port(buffer, "dccp");
68	}
69	else {
70		*cp = '\0';
71		cp++;
72
73		ports[0] = buffer[0] ? parse_port(buffer, "dccp") : 0;
74		ports[1] = cp[0] ? parse_port(cp, "dccp") : 0xFFFF;
75
76		if (ports[0] > ports[1])
77			exit_error(PARAMETER_PROBLEM,
78				   "invalid portrange (min > max)");
79	}
80	free(buffer);
81}
82
83static const char *const dccp_pkt_types[] = {
84	[DCCP_PKT_REQUEST] 	= "REQUEST",
85	[DCCP_PKT_RESPONSE]	= "RESPONSE",
86	[DCCP_PKT_DATA]		= "DATA",
87	[DCCP_PKT_ACK]		= "ACK",
88	[DCCP_PKT_DATAACK]	= "DATAACK",
89	[DCCP_PKT_CLOSEREQ]	= "CLOSEREQ",
90	[DCCP_PKT_CLOSE]	= "CLOSE",
91	[DCCP_PKT_RESET]	= "RESET",
92	[DCCP_PKT_SYNC]		= "SYNC",
93	[DCCP_PKT_SYNCACK]	= "SYNCACK",
94	[DCCP_PKT_INVALID]	= "INVALID",
95};
96
97static u_int16_t
98parse_dccp_types(const char *typestring)
99{
100	u_int16_t typemask = 0;
101	char *ptr, *buffer;
102
103	buffer = strdup(typestring);
104
105	for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
106		unsigned int i;
107		for (i = 0; i < sizeof(dccp_pkt_types)/sizeof(char *); i++) {
108			if (!strcasecmp(dccp_pkt_types[i], ptr)) {
109				typemask |= (1 << i);
110				break;
111			}
112		}
113		if (i == sizeof(dccp_pkt_types)/sizeof(char *))
114			exit_error(PARAMETER_PROBLEM,
115				   "Unknown DCCP type `%s'", ptr);
116	}
117
118	free(buffer);
119	return typemask;
120}
121
122static u_int8_t parse_dccp_option(char *optstring)
123{
124	unsigned int ret;
125
126	if (string_to_number(optstring, 1, 255, &ret) == -1)
127		exit_error(PARAMETER_PROBLEM, "Bad DCCP option `%s'",
128			   optstring);
129
130	return (u_int8_t)ret;
131}
132
133static int
134dccp_parse(int c, char **argv, int invert, unsigned int *flags,
135           const void *entry, struct xt_entry_match **match)
136{
137	struct xt_dccp_info *einfo
138		= (struct xt_dccp_info *)(*match)->data;
139
140	switch (c) {
141	case '1':
142		if (*flags & XT_DCCP_SRC_PORTS)
143			exit_error(PARAMETER_PROBLEM,
144			           "Only one `--source-port' allowed");
145		einfo->flags |= XT_DCCP_SRC_PORTS;
146		check_inverse(optarg, &invert, &optind, 0);
147		parse_dccp_ports(argv[optind-1], einfo->spts);
148		if (invert)
149			einfo->invflags |= XT_DCCP_SRC_PORTS;
150		*flags |= XT_DCCP_SRC_PORTS;
151		break;
152
153	case '2':
154		if (*flags & XT_DCCP_DEST_PORTS)
155			exit_error(PARAMETER_PROBLEM,
156				   "Only one `--destination-port' allowed");
157		einfo->flags |= XT_DCCP_DEST_PORTS;
158		check_inverse(optarg, &invert, &optind, 0);
159		parse_dccp_ports(argv[optind-1], einfo->dpts);
160		if (invert)
161			einfo->invflags |= XT_DCCP_DEST_PORTS;
162		*flags |= XT_DCCP_DEST_PORTS;
163		break;
164
165	case '3':
166		if (*flags & XT_DCCP_TYPE)
167			exit_error(PARAMETER_PROBLEM,
168				   "Only one `--dccp-types' allowed");
169		einfo->flags |= XT_DCCP_TYPE;
170		check_inverse(optarg, &invert, &optind, 0);
171		einfo->typemask = parse_dccp_types(argv[optind-1]);
172		if (invert)
173			einfo->invflags |= XT_DCCP_TYPE;
174		*flags |= XT_DCCP_TYPE;
175		break;
176
177	case '4':
178		if (*flags & XT_DCCP_OPTION)
179			exit_error(PARAMETER_PROBLEM,
180				   "Only one `--dccp-option' allowed");
181		einfo->flags |= XT_DCCP_OPTION;
182		check_inverse(optarg, &invert, &optind, 0);
183		einfo->option = parse_dccp_option(argv[optind-1]);
184		if (invert)
185			einfo->invflags |= XT_DCCP_OPTION;
186		*flags |= XT_DCCP_OPTION;
187		break;
188	default:
189		return 0;
190	}
191	return 1;
192}
193
194static char *
195port_to_service(int port)
196{
197	struct servent *service;
198
199	if ((service = getservbyport(htons(port), "dccp")))
200		return service->s_name;
201
202	return NULL;
203}
204
205static void
206print_port(u_int16_t port, int numeric)
207{
208	char *service;
209
210	if (numeric || (service = port_to_service(port)) == NULL)
211		printf("%u", port);
212	else
213		printf("%s", service);
214}
215
216static void
217print_ports(const char *name, u_int16_t min, u_int16_t max,
218	    int invert, int numeric)
219{
220	const char *inv = invert ? "!" : "";
221
222	if (min != 0 || max != 0xFFFF || invert) {
223		printf("%s", name);
224		if (min == max) {
225			printf(":%s", inv);
226			print_port(min, numeric);
227		} else {
228			printf("s:%s", inv);
229			print_port(min, numeric);
230			printf(":");
231			print_port(max, numeric);
232		}
233		printf(" ");
234	}
235}
236
237static void
238print_types(u_int16_t types, int inverted, int numeric)
239{
240	int have_type = 0;
241
242	if (inverted)
243		printf("! ");
244
245	while (types) {
246		unsigned int i;
247
248		for (i = 0; !(types & (1 << i)); i++);
249
250		if (have_type)
251			printf(",");
252		else
253			have_type = 1;
254
255		if (numeric)
256			printf("%u", i);
257		else
258			printf("%s", dccp_pkt_types[i]);
259
260		types &= ~(1 << i);
261	}
262}
263
264static void
265print_option(u_int8_t option, int invert, int numeric)
266{
267	if (option || invert)
268		printf("option=%s%u ", invert ? "!" : "", option);
269}
270
271/* Prints out the matchinfo. */
272static void
273dccp_print(const void *ip, const struct xt_entry_match *match, int numeric)
274{
275	const struct xt_dccp_info *einfo =
276		(const struct xt_dccp_info *)match->data;
277
278	printf("dccp ");
279
280	if (einfo->flags & XT_DCCP_SRC_PORTS) {
281		print_ports("spt", einfo->spts[0], einfo->spts[1],
282			einfo->invflags & XT_DCCP_SRC_PORTS,
283			numeric);
284	}
285
286	if (einfo->flags & XT_DCCP_DEST_PORTS) {
287		print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
288			einfo->invflags & XT_DCCP_DEST_PORTS,
289			numeric);
290	}
291
292	if (einfo->flags & XT_DCCP_TYPE) {
293		print_types(einfo->typemask,
294			   einfo->invflags & XT_DCCP_TYPE,
295			   numeric);
296	}
297
298	if (einfo->flags & XT_DCCP_OPTION) {
299		print_option(einfo->option,
300			     einfo->invflags & XT_DCCP_OPTION, numeric);
301	}
302}
303
304/* Saves the union ipt_matchinfo in parsable form to stdout. */
305static void dccp_save(const void *ip, const struct xt_entry_match *match)
306{
307	const struct xt_dccp_info *einfo =
308		(const struct xt_dccp_info *)match->data;
309
310	if (einfo->flags & XT_DCCP_SRC_PORTS) {
311		if (einfo->invflags & XT_DCCP_SRC_PORTS)
312			printf("! ");
313		if (einfo->spts[0] != einfo->spts[1])
314			printf("--sport %u:%u ",
315			       einfo->spts[0], einfo->spts[1]);
316		else
317			printf("--sport %u ", einfo->spts[0]);
318	}
319
320	if (einfo->flags & XT_DCCP_DEST_PORTS) {
321		if (einfo->invflags & XT_DCCP_DEST_PORTS)
322			printf("! ");
323		if (einfo->dpts[0] != einfo->dpts[1])
324			printf("--dport %u:%u ",
325			       einfo->dpts[0], einfo->dpts[1]);
326		else
327			printf("--dport %u ", einfo->dpts[0]);
328	}
329
330	if (einfo->flags & XT_DCCP_TYPE) {
331		printf("--dccp-type ");
332		print_types(einfo->typemask, einfo->invflags & XT_DCCP_TYPE,0);
333	}
334
335	if (einfo->flags & XT_DCCP_OPTION) {
336		printf("--dccp-option %s%u ",
337			einfo->typemask & XT_DCCP_OPTION ? "! " : "",
338			einfo->option);
339	}
340}
341
342static struct xtables_match dccp_match = {
343	.name		= "dccp",
344	.family		= AF_INET,
345	.version	= IPTABLES_VERSION,
346	.size		= XT_ALIGN(sizeof(struct xt_dccp_info)),
347	.userspacesize	= XT_ALIGN(sizeof(struct xt_dccp_info)),
348	.help		= dccp_help,
349	.init		= dccp_init,
350	.parse		= dccp_parse,
351	.print		= dccp_print,
352	.save		= dccp_save,
353	.extra_opts	= dccp_opts,
354};
355
356static struct xtables_match dccp_match6 = {
357	.name		= "dccp",
358	.family		= AF_INET6,
359	.version	= IPTABLES_VERSION,
360	.size		= XT_ALIGN(sizeof(struct xt_dccp_info)),
361	.userspacesize	= XT_ALIGN(sizeof(struct xt_dccp_info)),
362	.help		= dccp_help,
363	.init		= dccp_init,
364	.parse		= dccp_parse,
365	.print		= dccp_print,
366	.save		= dccp_save,
367	.extra_opts	= dccp_opts,
368};
369
370void _init(void)
371{
372	xtables_register_match(&dccp_match);
373	xtables_register_match(&dccp_match6);
374}
375
376