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