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