libxt_multiport.c revision c7ae1dfb28382b3bbafeda1a7a87ffa77f2799ea
1/* Shared library add-on to iptables to add multiple TCP port support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7
8#include <xtables.h>
9#include <libiptc/libiptc.h>
10/* To ensure that iptables compiles with an old kernel */
11#include "../include/linux/netfilter/xt_multiport.h"
12
13/* Function which prints out usage message. */
14static void
15help(void)
16{
17	printf(
18"multiport v%s options:\n"
19" --source-ports port[,port,port...]\n"
20" --sports ...\n"
21"				match source port(s)\n"
22" --destination-ports port[,port,port...]\n"
23" --dports ...\n"
24"				match destination port(s)\n"
25" --ports port[,port,port]\n"
26"				match both source and destination port(s)\n"
27" NOTE: this kernel does not support port ranges in multiport.\n",
28IPTABLES_VERSION);
29}
30
31static void
32help_v1(void)
33{
34	printf(
35"multiport v%s options:\n"
36" --source-ports [!] port[,port:port,port...]\n"
37" --sports ...\n"
38"				match source port(s)\n"
39" --destination-ports [!] port[,port:port,port...]\n"
40" --dports ...\n"
41"				match destination port(s)\n"
42" --ports [!] port[,port:port,port]\n"
43"				match both source and destination port(s)\n",
44IPTABLES_VERSION);
45}
46
47static struct option opts[] = {
48	{ "source-ports", 1, 0, '1' },
49	{ "sports", 1, 0, '1' }, /* synonym */
50	{ "destination-ports", 1, 0, '2' },
51	{ "dports", 1, 0, '2' }, /* synonym */
52	{ "ports", 1, 0, '3' },
53	{0}
54};
55
56static char *
57proto_to_name(u_int8_t proto)
58{
59	switch (proto) {
60	case IPPROTO_TCP:
61		return "tcp";
62	case IPPROTO_UDP:
63		return "udp";
64	case IPPROTO_UDPLITE:
65		return "udplite";
66	case IPPROTO_SCTP:
67		return "sctp";
68	case IPPROTO_DCCP:
69		return "dccp";
70	default:
71		return NULL;
72	}
73}
74
75static unsigned int
76parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
77{
78	char *buffer, *cp, *next;
79	unsigned int i;
80
81	buffer = strdup(portstring);
82	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
83
84	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next,i++)
85	{
86		next=strchr(cp, ',');
87		if (next) *next++='\0';
88		ports[i] = parse_port(cp, proto);
89	}
90	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
91	free(buffer);
92	return i;
93}
94
95static void
96parse_multi_ports_v1(const char *portstring,
97		     struct xt_multiport_v1 *multiinfo,
98		     const char *proto)
99{
100	char *buffer, *cp, *next, *range;
101	unsigned int i;
102	u_int16_t m;
103
104	buffer = strdup(portstring);
105	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
106
107	for (i=0; i<XT_MULTI_PORTS; i++)
108		multiinfo->pflags[i] = 0;
109
110	for (cp=buffer, i=0; cp && i<XT_MULTI_PORTS; cp=next, i++) {
111		next=strchr(cp, ',');
112 		if (next) *next++='\0';
113		range = strchr(cp, ':');
114		if (range) {
115			if (i == XT_MULTI_PORTS-1)
116				exit_error(PARAMETER_PROBLEM,
117					   "too many ports specified");
118			*range++ = '\0';
119		}
120		multiinfo->ports[i] = parse_port(cp, proto);
121		if (range) {
122			multiinfo->pflags[i] = 1;
123			multiinfo->ports[++i] = parse_port(range, proto);
124			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
125				exit_error(PARAMETER_PROBLEM,
126					   "invalid portrange specified");
127			m <<= 1;
128		}
129 	}
130	multiinfo->count = i;
131 	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
132 	free(buffer);
133}
134
135/* Initialize the match. */
136static void
137init(struct xt_entry_match *m, unsigned int *nfcache)
138{
139}
140
141static const char *
142check_proto(u_int16_t pnum, u_int8_t invflags)
143{
144	char *proto;
145
146	if (invflags & XT_INV_PROTO)
147		exit_error(PARAMETER_PROBLEM,
148			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
149
150	if ((proto = proto_to_name(pnum)) != NULL)
151		return proto;
152	else if (!pnum)
153		exit_error(PARAMETER_PROBLEM,
154			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
155			   "`-p sctp' or `-p dccp'");
156	else
157		exit_error(PARAMETER_PROBLEM,
158			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
159}
160
161/* Function which parses command options; returns true if it
162   ate an option */
163static int
164__parse(int c, char **argv, int invert, unsigned int *flags,
165	struct xt_entry_match **match,
166	u_int16_t pnum, u_int8_t invflags)
167{
168	const char *proto;
169	struct xt_multiport *multiinfo
170		= (struct xt_multiport *)(*match)->data;
171
172	switch (c) {
173	case '1':
174		check_inverse(argv[optind-1], &invert, &optind, 0);
175		proto = check_proto(pnum, invflags);
176		multiinfo->count = parse_multi_ports(argv[optind-1],
177						     multiinfo->ports, proto);
178		multiinfo->flags = XT_MULTIPORT_SOURCE;
179		break;
180
181	case '2':
182		check_inverse(argv[optind-1], &invert, &optind, 0);
183		proto = check_proto(pnum, invflags);
184		multiinfo->count = parse_multi_ports(argv[optind-1],
185						     multiinfo->ports, proto);
186		multiinfo->flags = XT_MULTIPORT_DESTINATION;
187		break;
188
189	case '3':
190		check_inverse(argv[optind-1], &invert, &optind, 0);
191		proto = check_proto(pnum, invflags);
192		multiinfo->count = parse_multi_ports(argv[optind-1],
193						     multiinfo->ports, proto);
194		multiinfo->flags = XT_MULTIPORT_EITHER;
195		break;
196
197	default:
198		return 0;
199	}
200
201	if (invert)
202		exit_error(PARAMETER_PROBLEM,
203			   "multiport does not support invert");
204
205	if (*flags)
206		exit_error(PARAMETER_PROBLEM,
207			   "multiport can only have one option");
208	*flags = 1;
209	return 1;
210}
211
212static int
213parse(int c, char **argv, int invert, unsigned int *flags,
214	 const void *e,
215	 unsigned int *nfcache,
216	 struct xt_entry_match **match)
217{
218	const struct ipt_entry *entry = e;
219	return __parse(c, argv, invert, flags, match, entry->ip.proto,
220		       entry->ip.invflags);
221}
222
223static int
224__parse_v1(int c, char **argv, int invert, unsigned int *flags,
225	   struct xt_entry_match **match,
226	   u_int16_t pnum, u_int8_t invflags)
227{
228	const char *proto;
229	struct xt_multiport_v1 *multiinfo
230		= (struct xt_multiport_v1 *)(*match)->data;
231
232	switch (c) {
233	case '1':
234		check_inverse(argv[optind-1], &invert, &optind, 0);
235		proto = check_proto(pnum, invflags);
236		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
237		multiinfo->flags = XT_MULTIPORT_SOURCE;
238		break;
239
240	case '2':
241		check_inverse(argv[optind-1], &invert, &optind, 0);
242		proto = check_proto(pnum, invflags);
243		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
244		multiinfo->flags = XT_MULTIPORT_DESTINATION;
245		break;
246
247	case '3':
248		check_inverse(argv[optind-1], &invert, &optind, 0);
249		proto = check_proto(pnum, invflags);
250		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
251		multiinfo->flags = XT_MULTIPORT_EITHER;
252		break;
253
254	default:
255		return 0;
256	}
257
258	if (invert)
259		multiinfo->invert = 1;
260
261	if (*flags)
262		exit_error(PARAMETER_PROBLEM,
263			   "multiport can only have one option");
264	*flags = 1;
265	return 1;
266}
267
268static int
269parse_v1(int c, char **argv, int invert, unsigned int *flags,
270	 const void *e,
271	 unsigned int *nfcache,
272	 struct xt_entry_match **match)
273{
274	const struct ipt_entry *entry = e;
275	return __parse_v1(c, argv, invert, flags, match, entry->ip.proto,
276			  entry->ip.invflags);
277}
278
279/* Final check; must specify something. */
280static void
281final_check(unsigned int flags)
282{
283	if (!flags)
284		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
285}
286
287static char *
288port_to_service(int port, u_int8_t proto)
289{
290	struct servent *service;
291
292	if ((service = getservbyport(htons(port), proto_to_name(proto))))
293		return service->s_name;
294
295	return NULL;
296}
297
298static void
299print_port(u_int16_t port, u_int8_t protocol, int numeric)
300{
301	char *service;
302
303	if (numeric || (service = port_to_service(port, protocol)) == NULL)
304		printf("%u", port);
305	else
306		printf("%s", service);
307}
308
309/* Prints out the matchinfo. */
310static void
311__print(const struct xt_entry_match *match, int numeric, u_int16_t proto)
312{
313	const struct xt_multiport *multiinfo
314		= (const struct xt_multiport *)match->data;
315	unsigned int i;
316
317	printf("multiport ");
318
319	switch (multiinfo->flags) {
320	case XT_MULTIPORT_SOURCE:
321		printf("sports ");
322		break;
323
324	case XT_MULTIPORT_DESTINATION:
325		printf("dports ");
326		break;
327
328	case XT_MULTIPORT_EITHER:
329		printf("ports ");
330		break;
331
332	default:
333		printf("ERROR ");
334		break;
335	}
336
337	for (i=0; i < multiinfo->count; i++) {
338		printf("%s", i ? "," : "");
339		print_port(multiinfo->ports[i], proto, numeric);
340	}
341	printf(" ");
342}
343
344static void
345print(const void *ip_void, const struct xt_entry_match *match, int numeric)
346{
347	const struct ipt_ip *ip = ip_void;
348	__print(match, numeric, ip->proto);
349}
350
351static void
352__print_v1(const struct xt_entry_match *match, int numeric, u_int16_t proto)
353{
354	const struct xt_multiport_v1 *multiinfo
355		= (const struct xt_multiport_v1 *)match->data;
356	unsigned int i;
357
358	printf("multiport ");
359
360	switch (multiinfo->flags) {
361	case XT_MULTIPORT_SOURCE:
362		printf("sports ");
363		break;
364
365	case XT_MULTIPORT_DESTINATION:
366		printf("dports ");
367		break;
368
369	case XT_MULTIPORT_EITHER:
370		printf("ports ");
371		break;
372
373	default:
374		printf("ERROR ");
375		break;
376	}
377
378	if (multiinfo->invert)
379		printf("! ");
380
381	for (i=0; i < multiinfo->count; i++) {
382		printf("%s", i ? "," : "");
383		print_port(multiinfo->ports[i], proto, numeric);
384		if (multiinfo->pflags[i]) {
385			printf(":");
386			print_port(multiinfo->ports[++i], proto, numeric);
387		}
388	}
389	printf(" ");
390}
391
392static void
393print_v1(const void *ip_void, const struct xt_entry_match *match, int numeric)
394{
395	const struct ipt_ip *ip = ip_void;
396	__print_v1(match, numeric, ip->proto);
397}
398
399/* Saves the union ipt_matchinfo in parsable form to stdout. */
400static void __save(const struct xt_entry_match *match, u_int16_t proto)
401{
402	const struct xt_multiport *multiinfo
403		= (const struct xt_multiport *)match->data;
404	unsigned int i;
405
406	switch (multiinfo->flags) {
407	case XT_MULTIPORT_SOURCE:
408		printf("--sports ");
409		break;
410
411	case XT_MULTIPORT_DESTINATION:
412		printf("--dports ");
413		break;
414
415	case XT_MULTIPORT_EITHER:
416		printf("--ports ");
417		break;
418	}
419
420	for (i=0; i < multiinfo->count; i++) {
421		printf("%s", i ? "," : "");
422		print_port(multiinfo->ports[i], proto, 1);
423	}
424	printf(" ");
425}
426
427static void save(const void *ip_void, const struct xt_entry_match *match)
428{
429	const struct ipt_ip *ip = ip_void;
430	__save(match, ip->proto);
431}
432
433static void __save_v1(const struct xt_entry_match *match, u_int16_t proto)
434{
435	const struct xt_multiport_v1 *multiinfo
436		= (const struct xt_multiport_v1 *)match->data;
437	unsigned int i;
438
439	switch (multiinfo->flags) {
440	case XT_MULTIPORT_SOURCE:
441		printf("--sports ");
442		break;
443
444	case XT_MULTIPORT_DESTINATION:
445		printf("--dports ");
446		break;
447
448	case XT_MULTIPORT_EITHER:
449		printf("--ports ");
450		break;
451	}
452
453	if (multiinfo->invert)
454		printf("! ");
455
456	for (i=0; i < multiinfo->count; i++) {
457		printf("%s", i ? "," : "");
458		print_port(multiinfo->ports[i], proto, 1);
459		if (multiinfo->pflags[i]) {
460			printf(":");
461			print_port(multiinfo->ports[++i], proto, 1);
462		}
463	}
464	printf(" ");
465}
466
467static void save_v1(const void *ip_void, const struct xt_entry_match *match)
468{
469	const struct ipt_ip *ip = ip_void;
470	__save_v1(match, ip->proto);
471}
472
473static struct xtables_match multiport = {
474	.next		= NULL,
475	.family		= AF_INET,
476	.name		= "multiport",
477	.revision	= 0,
478	.version	= IPTABLES_VERSION,
479	.size		= XT_ALIGN(sizeof(struct xt_multiport)),
480	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport)),
481	.help		= &help,
482	.init		= &init,
483	.parse		= &parse,
484	.final_check	= &final_check,
485	.print		= &print,
486	.save		= &save,
487	.extra_opts	= opts
488};
489
490static struct xtables_match multiport_v1 = {
491	.next		= NULL,
492	.family		= AF_INET,
493	.name		= "multiport",
494	.version	= IPTABLES_VERSION,
495	.revision	= 1,
496	.size		= XT_ALIGN(sizeof(struct xt_multiport_v1)),
497	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport_v1)),
498	.help		= &help_v1,
499	.init		= &init,
500	.parse		= &parse_v1,
501	.final_check	= &final_check,
502	.print		= &print_v1,
503	.save		= &save_v1,
504	.extra_opts	= opts
505};
506
507void
508_init(void)
509{
510	xtables_register_match(&multiport);
511	xtables_register_match(&multiport_v1);
512}
513