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