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