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