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