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