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#include <iptables.h>
8/* To ensure that iptables compiles with an old kernel */
9#include "../include/linux/netfilter_ipv4/ipt_multiport.h"
10
11/* Function which prints out usage message. */
12#if 0
13static void
14help(void)
15{
16	printf(
17"multiport v%s options:\n"
18" --source-ports port[,port,port...]\n"
19" --sports ...\n"
20"				match source port(s)\n"
21" --destination-ports port[,port,port...]\n"
22" --dports ...\n"
23"				match destination port(s)\n"
24" --ports port[,port,port]\n"
25"				match both source and destination port(s)\n"
26" NOTE: this kernel does not support port ranges in multiport.\n",
27IPTABLES_VERSION);
28}
29#endif
30
31static void
32help_v1(void)
33{
34	printf(
35"multiport v%s 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",
44IPTABLES_VERSION);
45}
46
47static struct option opts[] = {
48	{ "source-ports", 1, 0, '1' },
49	{ "sports", 1, 0, '1' }, /* synonym */
50	{ "destination-ports", 1, 0, '2' },
51	{ "dports", 1, 0, '2' }, /* synonym */
52	{ "ports", 1, 0, '3' },
53	{0}
54};
55
56static char *
57proto_to_name(u_int8_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
75#if 0
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<IPT_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#endif
96
97static void
98parse_multi_ports_v1(const char *portstring,
99		     struct ipt_multiport_v1 *multiinfo,
100		     const char *proto)
101{
102	char *buffer, *cp, *next, *range;
103	unsigned int i;
104	u_int16_t m;
105
106	buffer = strdup(portstring);
107	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
108
109	for (i=0; i<IPT_MULTI_PORTS; i++)
110		multiinfo->pflags[i] = 0;
111
112	for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next, i++) {
113		next=strchr(cp, ',');
114 		if (next) *next++='\0';
115		range = strchr(cp, ':');
116		if (range) {
117			if (i == IPT_MULTI_PORTS-1)
118				exit_error(PARAMETER_PROBLEM,
119					   "too many ports specified");
120			*range++ = '\0';
121		}
122		multiinfo->ports[i] = parse_port(cp, proto);
123		if (range) {
124			multiinfo->pflags[i] = 1;
125			multiinfo->ports[++i] = parse_port(range, proto);
126			if (multiinfo->ports[i-1] >= multiinfo->ports[i])
127				exit_error(PARAMETER_PROBLEM,
128					   "invalid portrange specified");
129			m <<= 1;
130		}
131 	}
132	multiinfo->count = i;
133 	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
134 	free(buffer);
135}
136
137/* Initialize the match. */
138static void
139init(struct ipt_entry_match *m, unsigned int *nfcache)
140{
141}
142
143static const char *
144check_proto(const struct ipt_entry *entry)
145{
146	char *proto;
147
148	if (entry->ip.invflags & IPT_INV_PROTO)
149		exit_error(PARAMETER_PROBLEM,
150			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
151
152	if ((proto = proto_to_name(entry->ip.proto)) != NULL)
153		return proto;
154	else if (!entry->ip.proto)
155		exit_error(PARAMETER_PROBLEM,
156			   "multiport needs `-p tcp', `-p udp', `-p udplite', "
157			   "`-p sctp' or `-p dccp'");
158	else
159		exit_error(PARAMETER_PROBLEM,
160			   "multiport only works with TCP, UDP, UDPLITE, SCTP and DCCP");
161}
162
163/* Function which parses command options; returns true if it
164   ate an option */
165#if 0
166static int
167parse(int c, char **argv, int invert, unsigned int *flags,
168      const struct ipt_entry *entry,
169      unsigned int *nfcache,
170      struct ipt_entry_match **match)
171{
172	const char *proto;
173	struct ipt_multiport *multiinfo
174		= (struct ipt_multiport *)(*match)->data;
175
176	switch (c) {
177	case '1':
178		check_inverse(argv[optind-1], &invert, &optind, 0);
179		proto = check_proto(entry);
180		multiinfo->count = parse_multi_ports(argv[optind-1],
181						     multiinfo->ports, proto);
182		multiinfo->flags = IPT_MULTIPORT_SOURCE;
183		break;
184
185	case '2':
186		check_inverse(argv[optind-1], &invert, &optind, 0);
187		proto = check_proto(entry);
188		multiinfo->count = parse_multi_ports(argv[optind-1],
189						     multiinfo->ports, proto);
190		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
191		break;
192
193	case '3':
194		check_inverse(argv[optind-1], &invert, &optind, 0);
195		proto = check_proto(entry);
196		multiinfo->count = parse_multi_ports(argv[optind-1],
197						     multiinfo->ports, proto);
198		multiinfo->flags = IPT_MULTIPORT_EITHER;
199		break;
200
201	default:
202		return 0;
203	}
204
205	if (invert)
206		exit_error(PARAMETER_PROBLEM,
207			   "multiport does not support invert");
208
209	if (*flags)
210		exit_error(PARAMETER_PROBLEM,
211			   "multiport can only have one option");
212	*flags = 1;
213	return 1;
214}
215#endif
216
217static int
218parse_v1(int c, char **argv, int invert, unsigned int *flags,
219	 const struct ipt_entry *entry,
220	 unsigned int *nfcache,
221	 struct ipt_entry_match **match)
222{
223	const char *proto;
224	struct ipt_multiport_v1 *multiinfo
225		= (struct ipt_multiport_v1 *)(*match)->data;
226
227	switch (c) {
228	case '1':
229		check_inverse(argv[optind-1], &invert, &optind, 0);
230		proto = check_proto(entry);
231		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
232		multiinfo->flags = IPT_MULTIPORT_SOURCE;
233		break;
234
235	case '2':
236		check_inverse(argv[optind-1], &invert, &optind, 0);
237		proto = check_proto(entry);
238		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
239		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
240		break;
241
242	case '3':
243		check_inverse(argv[optind-1], &invert, &optind, 0);
244		proto = check_proto(entry);
245		parse_multi_ports_v1(argv[optind-1], multiinfo, proto);
246		multiinfo->flags = IPT_MULTIPORT_EITHER;
247		break;
248
249	default:
250		return 0;
251	}
252
253	if (invert)
254		multiinfo->invert = 1;
255
256	if (*flags)
257		exit_error(PARAMETER_PROBLEM,
258			   "multiport can only have one option");
259	*flags = 1;
260	return 1;
261}
262
263/* Final check; must specify something. */
264static void
265final_check(unsigned int flags)
266{
267	if (!flags)
268		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
269}
270
271static char *
272port_to_service(int port, u_int8_t proto)
273{
274	struct servent *service;
275
276	if ((service = getservbyport(htons(port), proto_to_name(proto))))
277		return service->s_name;
278
279	return NULL;
280}
281
282static void
283print_port(u_int16_t port, u_int8_t protocol, int numeric)
284{
285	char *service;
286
287	if (numeric || (service = port_to_service(port, protocol)) == NULL)
288		printf("%u", port);
289	else
290		printf("%s", service);
291}
292
293/* Prints out the matchinfo. */
294#if 0
295static void
296print(const struct ipt_ip *ip,
297      const struct ipt_entry_match *match,
298      int numeric)
299{
300	const struct ipt_multiport *multiinfo
301		= (const struct ipt_multiport *)match->data;
302	unsigned int i;
303
304	printf("multiport ");
305
306	switch (multiinfo->flags) {
307	case IPT_MULTIPORT_SOURCE:
308		printf("sports ");
309		break;
310
311	case IPT_MULTIPORT_DESTINATION:
312		printf("dports ");
313		break;
314
315	case IPT_MULTIPORT_EITHER:
316		printf("ports ");
317		break;
318
319	default:
320		printf("ERROR ");
321		break;
322	}
323
324	for (i=0; i < multiinfo->count; i++) {
325		printf("%s", i ? "," : "");
326		print_port(multiinfo->ports[i], ip->proto, numeric);
327	}
328	printf(" ");
329}
330#endif
331
332static void
333print_v1(const struct ipt_ip *ip,
334	 const struct ipt_entry_match *match,
335	 int numeric)
336{
337	const struct ipt_multiport_v1 *multiinfo
338		= (const struct ipt_multiport_v1 *)match->data;
339	unsigned int i;
340
341	printf("multiport ");
342
343	switch (multiinfo->flags) {
344	case IPT_MULTIPORT_SOURCE:
345		printf("sports ");
346		break;
347
348	case IPT_MULTIPORT_DESTINATION:
349		printf("dports ");
350		break;
351
352	case IPT_MULTIPORT_EITHER:
353		printf("ports ");
354		break;
355
356	default:
357		printf("ERROR ");
358		break;
359	}
360
361	if (multiinfo->invert)
362		printf("! ");
363
364	for (i=0; i < multiinfo->count; i++) {
365		printf("%s", i ? "," : "");
366		print_port(multiinfo->ports[i], ip->proto, numeric);
367		if (multiinfo->pflags[i]) {
368			printf(":");
369			print_port(multiinfo->ports[++i], ip->proto, numeric);
370		}
371	}
372	printf(" ");
373}
374
375/* Saves the union ipt_matchinfo in parsable form to stdout. */
376#if 0
377static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
378{
379	const struct ipt_multiport *multiinfo
380		= (const struct ipt_multiport *)match->data;
381	unsigned int i;
382
383	switch (multiinfo->flags) {
384	case IPT_MULTIPORT_SOURCE:
385		printf("--sports ");
386		break;
387
388	case IPT_MULTIPORT_DESTINATION:
389		printf("--dports ");
390		break;
391
392	case IPT_MULTIPORT_EITHER:
393		printf("--ports ");
394		break;
395	}
396
397	for (i=0; i < multiinfo->count; i++) {
398		printf("%s", i ? "," : "");
399		print_port(multiinfo->ports[i], ip->proto, 1);
400	}
401	printf(" ");
402}
403#endif
404
405static void save_v1(const struct ipt_ip *ip,
406		    const struct ipt_entry_match *match)
407{
408	const struct ipt_multiport_v1 *multiinfo
409		= (const struct ipt_multiport_v1 *)match->data;
410	unsigned int i;
411
412	switch (multiinfo->flags) {
413	case IPT_MULTIPORT_SOURCE:
414		printf("--sports ");
415		break;
416
417	case IPT_MULTIPORT_DESTINATION:
418		printf("--dports ");
419		break;
420
421	case IPT_MULTIPORT_EITHER:
422		printf("--ports ");
423		break;
424	}
425
426	if (multiinfo->invert)
427		printf("! ");
428
429	for (i=0; i < multiinfo->count; i++) {
430		printf("%s", i ? "," : "");
431		print_port(multiinfo->ports[i], ip->proto, 1);
432		if (multiinfo->pflags[i]) {
433			printf(":");
434			print_port(multiinfo->ports[++i], ip->proto, 1);
435		}
436	}
437	printf(" ");
438}
439
440#if 0
441static struct iptables_match multiport = {
442	.next		= NULL,
443	.name		= "multiport",
444	.revision	= 0,
445	.version	= IPTABLES_VERSION,
446	.size		= IPT_ALIGN(sizeof(struct ipt_multiport)),
447	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_multiport)),
448	.help		= &help,
449	.init		= &init,
450	.parse		= &parse,
451	.final_check	= &final_check,
452	.print		= &print,
453	.save		= &save,
454	.extra_opts	= opts
455};
456#endif
457
458static struct iptables_match multiport_v1 = {
459	.next		= NULL,
460	.name		= "multiport",
461	.version	= IPTABLES_VERSION,
462	.revision	= 1,
463	.size		= IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
464	.userspacesize	= IPT_ALIGN(sizeof(struct ipt_multiport_v1)),
465	.help		= &help_v1,
466	.init		= &init,
467	.parse		= &parse_v1,
468	.final_check	= &final_check,
469	.print		= &print_v1,
470	.save		= &save_v1,
471	.extra_opts	= opts
472};
473
474void
475_init(void)
476{
477#if 0
478	register_match(&multiport);
479#endif
480	register_match(&multiport_v1);
481}
482