libxt_multiport.c revision 0463ee1f28946cc49815737daa0ced0c68f39f0b
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
136static const char *
137check_proto(u_int16_t pnum, u_int8_t invflags)
138{
139	char *proto;
140
141	if (invflags & XT_INV_PROTO)
142		exit_error(PARAMETER_PROBLEM,
143			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
144
145	if ((proto = proto_to_name(pnum)) != NULL)
146		return proto;
147	else if (!pnum)
148		exit_error(PARAMETER_PROBLEM,
149			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
150			   "`-p sctp' or `-p dccp'");
151	else
152		exit_error(PARAMETER_PROBLEM,
153			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
154}
155
156/* Function which parses command options; returns true if it
157   ate an option */
158static int
159__parse(int c, char **argv, int invert, unsigned int *flags,
160	struct xt_entry_match **match,
161	u_int16_t pnum, u_int8_t invflags)
162{
163	const char *proto;
164	struct xt_multiport *multiinfo
165		= (struct xt_multiport *)(*match)->data;
166
167	switch (c) {
168	case '1':
169		check_inverse(argv[optind-1], &invert, &optind, 0);
170		proto = check_proto(pnum, invflags);
171		multiinfo->count = parse_multi_ports(argv[optind-1],
172						     multiinfo->ports, proto);
173		multiinfo->flags = XT_MULTIPORT_SOURCE;
174		break;
175
176	case '2':
177		check_inverse(argv[optind-1], &invert, &optind, 0);
178		proto = check_proto(pnum, invflags);
179		multiinfo->count = parse_multi_ports(argv[optind-1],
180						     multiinfo->ports, proto);
181		multiinfo->flags = XT_MULTIPORT_DESTINATION;
182		break;
183
184	case '3':
185		check_inverse(argv[optind-1], &invert, &optind, 0);
186		proto = check_proto(pnum, invflags);
187		multiinfo->count = parse_multi_ports(argv[optind-1],
188						     multiinfo->ports, proto);
189		multiinfo->flags = XT_MULTIPORT_EITHER;
190		break;
191
192	default:
193		return 0;
194	}
195
196	if (invert)
197		exit_error(PARAMETER_PROBLEM,
198			   "multiport does not support invert");
199
200	if (*flags)
201		exit_error(PARAMETER_PROBLEM,
202			   "multiport can only have one option");
203	*flags = 1;
204	return 1;
205}
206
207static int
208parse(int c, char **argv, int invert, unsigned int *flags,
209	 const void *e,
210	 struct xt_entry_match **match)
211{
212	const struct ipt_entry *entry = e;
213	return __parse(c, argv, invert, flags, match, entry->ip.proto,
214		       entry->ip.invflags);
215}
216
217static int
218parse6(int c, char **argv, int invert, unsigned int *flags,
219	 const void *e,
220	 struct xt_entry_match **match)
221{
222	const struct ip6t_entry *entry = (const struct ip6t_entry *)e;
223	return __parse(c, argv, invert, flags, match, entry->ipv6.proto,
224		       entry->ipv6.invflags);
225}
226
227static int
228__parse_v1(int c, char **argv, int invert, unsigned int *flags,
229	   struct xt_entry_match **match,
230	   u_int16_t pnum, u_int8_t invflags)
231{
232	const char *proto;
233	struct xt_multiport_v1 *multiinfo
234		= (struct xt_multiport_v1 *)(*match)->data;
235
236	switch (c) {
237	case '1':
238		check_inverse(argv[optind-1], &invert, &optind, 0);
239		proto = check_proto(pnum, invflags);
240		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
241		multiinfo->flags = XT_MULTIPORT_SOURCE;
242		break;
243
244	case '2':
245		check_inverse(argv[optind-1], &invert, &optind, 0);
246		proto = check_proto(pnum, invflags);
247		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
248		multiinfo->flags = XT_MULTIPORT_DESTINATION;
249		break;
250
251	case '3':
252		check_inverse(argv[optind-1], &invert, &optind, 0);
253		proto = check_proto(pnum, invflags);
254		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
255		multiinfo->flags = XT_MULTIPORT_EITHER;
256		break;
257
258	default:
259		return 0;
260	}
261
262	if (invert)
263		multiinfo->invert = 1;
264
265	if (*flags)
266		exit_error(PARAMETER_PROBLEM,
267			   "multiport can only have one option");
268	*flags = 1;
269	return 1;
270}
271
272static int
273parse_v1(int c, char **argv, int invert, unsigned int *flags,
274	 const void *e,
275	 struct xt_entry_match **match)
276{
277	const struct ipt_entry *entry = e;
278	return __parse_v1(c, argv, invert, flags, match, entry->ip.proto,
279			  entry->ip.invflags);
280}
281
282static int
283parse6_v1(int c, char **argv, int invert, unsigned int *flags,
284	  const void *e,
285	  struct xt_entry_match **match)
286{
287	const struct ip6t_entry *entry = (const struct ip6t_entry *)e;
288	return __parse_v1(c, argv, invert, flags, match, entry->ipv6.proto,
289			  entry->ipv6.invflags);
290}
291
292/* Final check; must specify something. */
293static void
294final_check(unsigned int flags)
295{
296	if (!flags)
297		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
298}
299
300static char *
301port_to_service(int port, u_int8_t proto)
302{
303	struct servent *service;
304
305	if ((service = getservbyport(htons(port), proto_to_name(proto))))
306		return service->s_name;
307
308	return NULL;
309}
310
311static void
312print_port(u_int16_t port, u_int8_t protocol, int numeric)
313{
314	char *service;
315
316	if (numeric || (service = port_to_service(port, protocol)) == NULL)
317		printf("%u", port);
318	else
319		printf("%s", service);
320}
321
322/* Prints out the matchinfo. */
323static void
324__print(const struct xt_entry_match *match, int numeric, u_int16_t proto)
325{
326	const struct xt_multiport *multiinfo
327		= (const struct xt_multiport *)match->data;
328	unsigned int i;
329
330	printf("multiport ");
331
332	switch (multiinfo->flags) {
333	case XT_MULTIPORT_SOURCE:
334		printf("sports ");
335		break;
336
337	case XT_MULTIPORT_DESTINATION:
338		printf("dports ");
339		break;
340
341	case XT_MULTIPORT_EITHER:
342		printf("ports ");
343		break;
344
345	default:
346		printf("ERROR ");
347		break;
348	}
349
350	for (i=0; i < multiinfo->count; i++) {
351		printf("%s", i ? "," : "");
352		print_port(multiinfo->ports[i], proto, numeric);
353	}
354	printf(" ");
355}
356
357static void
358print(const void *ip_void, const struct xt_entry_match *match, int numeric)
359{
360	const struct ipt_ip *ip = ip_void;
361	__print(match, numeric, ip->proto);
362}
363
364static void
365print6(const void *ip_void, const struct xt_entry_match *match, int numeric)
366{
367	const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void;
368	__print(match, numeric, ip->proto);
369}
370
371static void
372__print_v1(const struct xt_entry_match *match, int numeric, u_int16_t proto)
373{
374	const struct xt_multiport_v1 *multiinfo
375		= (const struct xt_multiport_v1 *)match->data;
376	unsigned int i;
377
378	printf("multiport ");
379
380	switch (multiinfo->flags) {
381	case XT_MULTIPORT_SOURCE:
382		printf("sports ");
383		break;
384
385	case XT_MULTIPORT_DESTINATION:
386		printf("dports ");
387		break;
388
389	case XT_MULTIPORT_EITHER:
390		printf("ports ");
391		break;
392
393	default:
394		printf("ERROR ");
395		break;
396	}
397
398	if (multiinfo->invert)
399		printf("! ");
400
401	for (i=0; i < multiinfo->count; i++) {
402		printf("%s", i ? "," : "");
403		print_port(multiinfo->ports[i], proto, numeric);
404		if (multiinfo->pflags[i]) {
405			printf(":");
406			print_port(multiinfo->ports[++i], proto, numeric);
407		}
408	}
409	printf(" ");
410}
411
412static void
413print_v1(const void *ip_void, const struct xt_entry_match *match, int numeric)
414{
415	const struct ipt_ip *ip = ip_void;
416	__print_v1(match, numeric, ip->proto);
417}
418
419static void
420print6_v1(const void *ip_void, const struct xt_entry_match *match, int numeric)
421{
422	const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void;
423	__print_v1(match, numeric, ip->proto);
424}
425
426/* Saves the union ipt_matchinfo in parsable form to stdout. */
427static void __save(const struct xt_entry_match *match, u_int16_t proto)
428{
429	const struct xt_multiport *multiinfo
430		= (const struct xt_multiport *)match->data;
431	unsigned int i;
432
433	switch (multiinfo->flags) {
434	case XT_MULTIPORT_SOURCE:
435		printf("--sports ");
436		break;
437
438	case XT_MULTIPORT_DESTINATION:
439		printf("--dports ");
440		break;
441
442	case XT_MULTIPORT_EITHER:
443		printf("--ports ");
444		break;
445	}
446
447	for (i=0; i < multiinfo->count; i++) {
448		printf("%s", i ? "," : "");
449		print_port(multiinfo->ports[i], proto, 1);
450	}
451	printf(" ");
452}
453
454static void save(const void *ip_void, const struct xt_entry_match *match)
455{
456	const struct ipt_ip *ip = ip_void;
457	__save(match, ip->proto);
458}
459
460static void save6(const void *ip_void, const struct xt_entry_match *match)
461{
462	const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void;
463	__save(match, ip->proto);
464}
465
466static void __save_v1(const struct xt_entry_match *match, u_int16_t proto)
467{
468	const struct xt_multiport_v1 *multiinfo
469		= (const struct xt_multiport_v1 *)match->data;
470	unsigned int i;
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	if (multiinfo->invert)
487		printf("! ");
488
489	for (i=0; i < multiinfo->count; i++) {
490		printf("%s", i ? "," : "");
491		print_port(multiinfo->ports[i], proto, 1);
492		if (multiinfo->pflags[i]) {
493			printf(":");
494			print_port(multiinfo->ports[++i], proto, 1);
495		}
496	}
497	printf(" ");
498}
499
500static void save_v1(const void *ip_void, const struct xt_entry_match *match)
501{
502	const struct ipt_ip *ip = ip_void;
503	__save_v1(match, ip->proto);
504}
505
506static void save6_v1(const void *ip_void, const struct xt_entry_match *match)
507{
508	const struct ip6t_ip6 *ip = (const struct ip6t_ip6 *)ip_void;
509	__save_v1(match, ip->proto);
510}
511
512static struct xtables_match multiport = {
513	.family		= AF_INET,
514	.name		= "multiport",
515	.revision	= 0,
516	.version	= IPTABLES_VERSION,
517	.size		= XT_ALIGN(sizeof(struct xt_multiport)),
518	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport)),
519	.help		= &help,
520	.parse		= &parse,
521	.final_check	= &final_check,
522	.print		= &print,
523	.save		= &save,
524	.extra_opts	= opts
525};
526
527static struct xtables_match multiport6 = {
528	.family		= AF_INET6,
529	.name		= "multiport",
530	.revision	= 0,
531	.version	= IPTABLES_VERSION,
532	.size		= XT_ALIGN(sizeof(struct xt_multiport)),
533	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport)),
534	.help		= &help,
535	.parse		= &parse6,
536	.final_check	= &final_check,
537	.print		= &print6,
538	.save		= &save6,
539	.extra_opts	= opts
540};
541
542static struct xtables_match multiport_v1 = {
543	.family		= AF_INET,
544	.name		= "multiport",
545	.version	= IPTABLES_VERSION,
546	.revision	= 1,
547	.size		= XT_ALIGN(sizeof(struct xt_multiport_v1)),
548	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport_v1)),
549	.help		= &help_v1,
550	.parse		= &parse_v1,
551	.final_check	= &final_check,
552	.print		= &print_v1,
553	.save		= &save_v1,
554	.extra_opts	= opts
555};
556
557static struct xtables_match multiport6_v1 = {
558	.family		= AF_INET6,
559	.name		= "multiport",
560	.version	= IPTABLES_VERSION,
561	.revision	= 1,
562	.size		= XT_ALIGN(sizeof(struct xt_multiport_v1)),
563	.userspacesize	= XT_ALIGN(sizeof(struct xt_multiport_v1)),
564	.help		= &help_v1,
565	.parse		= &parse6_v1,
566	.final_check	= &final_check,
567	.print		= &print6_v1,
568	.save		= &save6_v1,
569	.extra_opts	= opts
570};
571
572void
573_init(void)
574{
575	xtables_register_match(&multiport);
576	xtables_register_match(&multiport6);
577	xtables_register_match(&multiport_v1);
578	xtables_register_match(&multiport6_v1);
579}
580