libxt_multiport.c revision d09b6d591ca7d7d7575cb6aa20384c9830f777ab
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	printf(" ");
344}
345
346static void multiport_print(const void *ip_void,
347                            const struct xt_entry_match *match, int numeric)
348{
349	const struct ipt_ip *ip = ip_void;
350	__multiport_print(match, numeric, ip->proto);
351}
352
353static void multiport_print6(const void *ip_void,
354                             const struct xt_entry_match *match, int numeric)
355{
356	const struct ip6t_ip6 *ip = ip_void;
357	__multiport_print(match, numeric, ip->proto);
358}
359
360static void __multiport_print_v1(const struct xt_entry_match *match,
361                                 int numeric, uint16_t proto)
362{
363	const struct xt_multiport_v1 *multiinfo
364		= (const struct xt_multiport_v1 *)match->data;
365	unsigned int i;
366
367	printf("multiport ");
368
369	switch (multiinfo->flags) {
370	case XT_MULTIPORT_SOURCE:
371		printf("sports ");
372		break;
373
374	case XT_MULTIPORT_DESTINATION:
375		printf("dports ");
376		break;
377
378	case XT_MULTIPORT_EITHER:
379		printf("ports ");
380		break;
381
382	default:
383		printf("ERROR ");
384		break;
385	}
386
387	if (multiinfo->invert)
388		printf("! ");
389
390	for (i=0; i < multiinfo->count; i++) {
391		printf("%s", i ? "," : "");
392		print_port(multiinfo->ports[i], proto, numeric);
393		if (multiinfo->pflags[i]) {
394			printf(":");
395			print_port(multiinfo->ports[++i], proto, numeric);
396		}
397	}
398	printf(" ");
399}
400
401static void multiport_print_v1(const void *ip_void,
402                               const struct xt_entry_match *match, int numeric)
403{
404	const struct ipt_ip *ip = ip_void;
405	__multiport_print_v1(match, numeric, ip->proto);
406}
407
408static void multiport_print6_v1(const void *ip_void,
409                                const struct xt_entry_match *match, int numeric)
410{
411	const struct ip6t_ip6 *ip = ip_void;
412	__multiport_print_v1(match, numeric, ip->proto);
413}
414
415/* Saves the union ipt_matchinfo in parsable form to stdout. */
416static void __multiport_save(const struct xt_entry_match *match,
417                             uint16_t proto)
418{
419	const struct xt_multiport *multiinfo
420		= (const struct xt_multiport *)match->data;
421	unsigned int i;
422
423	switch (multiinfo->flags) {
424	case XT_MULTIPORT_SOURCE:
425		printf("--sports ");
426		break;
427
428	case XT_MULTIPORT_DESTINATION:
429		printf("--dports ");
430		break;
431
432	case XT_MULTIPORT_EITHER:
433		printf("--ports ");
434		break;
435	}
436
437	for (i=0; i < multiinfo->count; i++) {
438		printf("%s", i ? "," : "");
439		print_port(multiinfo->ports[i], proto, 1);
440	}
441	printf(" ");
442}
443
444static void multiport_save(const void *ip_void,
445                           const struct xt_entry_match *match)
446{
447	const struct ipt_ip *ip = ip_void;
448	__multiport_save(match, ip->proto);
449}
450
451static void multiport_save6(const void *ip_void,
452                            const struct xt_entry_match *match)
453{
454	const struct ip6t_ip6 *ip = ip_void;
455	__multiport_save(match, ip->proto);
456}
457
458static void __multiport_save_v1(const struct xt_entry_match *match,
459                                uint16_t proto)
460{
461	const struct xt_multiport_v1 *multiinfo
462		= (const struct xt_multiport_v1 *)match->data;
463	unsigned int i;
464
465	if (multiinfo->invert)
466		printf("! ");
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	for (i=0; i < multiinfo->count; i++) {
483		printf("%s", i ? "," : "");
484		print_port(multiinfo->ports[i], proto, 1);
485		if (multiinfo->pflags[i]) {
486			printf(":");
487			print_port(multiinfo->ports[++i], proto, 1);
488		}
489	}
490	printf(" ");
491}
492
493static void multiport_save_v1(const void *ip_void,
494                              const struct xt_entry_match *match)
495{
496	const struct ipt_ip *ip = ip_void;
497	__multiport_save_v1(match, ip->proto);
498}
499
500static void multiport_save6_v1(const void *ip_void,
501                               const struct xt_entry_match *match)
502{
503	const struct ip6t_ip6 *ip = ip_void;
504	__multiport_save_v1(match, ip->proto);
505}
506
507static struct xtables_match multiport_mt_reg[] = {
508	{
509		.family        = NFPROTO_IPV4,
510		.name          = "multiport",
511		.revision      = 0,
512		.version       = XTABLES_VERSION,
513		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
514		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
515		.help          = multiport_help,
516		.parse         = multiport_parse,
517		.final_check   = multiport_check,
518		.print         = multiport_print,
519		.save          = multiport_save,
520		.extra_opts    = multiport_opts,
521	},
522	{
523		.family        = NFPROTO_IPV6,
524		.name          = "multiport",
525		.revision      = 0,
526		.version       = XTABLES_VERSION,
527		.size          = XT_ALIGN(sizeof(struct xt_multiport)),
528		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport)),
529		.help          = multiport_help,
530		.parse         = multiport_parse6,
531		.final_check   = multiport_check,
532		.print         = multiport_print6,
533		.save          = multiport_save6,
534		.extra_opts    = multiport_opts,
535	},
536	{
537		.family        = NFPROTO_IPV4,
538		.name          = "multiport",
539		.version       = XTABLES_VERSION,
540		.revision      = 1,
541		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
542		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
543		.help          = multiport_help_v1,
544		.parse         = multiport_parse_v1,
545		.final_check   = multiport_check,
546		.print         = multiport_print_v1,
547		.save          = multiport_save_v1,
548		.extra_opts    = multiport_opts,
549	},
550	{
551		.family        = NFPROTO_IPV6,
552		.name          = "multiport",
553		.version       = XTABLES_VERSION,
554		.revision      = 1,
555		.size          = XT_ALIGN(sizeof(struct xt_multiport_v1)),
556		.userspacesize = XT_ALIGN(sizeof(struct xt_multiport_v1)),
557		.help          = multiport_help_v1,
558		.parse         = multiport_parse6_v1,
559		.final_check   = multiport_check,
560		.print         = multiport_print6_v1,
561		.save          = multiport_save6_v1,
562		.extra_opts    = multiport_opts,
563	},
564};
565
566void
567_init(void)
568{
569	xtables_register_matches(multiport_mt_reg, ARRAY_SIZE(multiport_mt_reg));
570}
571