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