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