1/* Shared library add-on to iptables to add redirect 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#include <linux/netfilter_ipv4/ip_tables.h>
9#include <linux/netfilter/nf_nat.h>
10
11#define IPT_REDIRECT_OPT_DEST	0x01
12#define IPT_REDIRECT_OPT_RANDOM	0x02
13
14/* Function which prints out usage message. */
15static void
16help(void)
17{
18	printf(
19"REDIRECT v%s options:\n"
20" --to-ports <port>[-<port>]\n"
21"				Port (range) to map to.\n\n",
22IPTABLES_VERSION);
23}
24
25static struct option opts[] = {
26	{ "to-ports", 1, 0, '1' },
27	{ "random", 0, 0, '2' },
28	{ 0 }
29};
30
31/* Initialize the target. */
32static void
33init(struct ipt_entry_target *t, unsigned int *nfcache)
34{
35	struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
36
37	/* Actually, it's 0, but it's ignored at the moment. */
38	mr->rangesize = 1;
39
40}
41
42/* Parses ports */
43static void
44parse_ports(const char *arg, struct ip_nat_multi_range *mr)
45{
46	const char *dash;
47	int port;
48
49	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
50
51	if (strchr(arg, '.'))
52		exit_error(PARAMETER_PROBLEM, "IP address not permitted\n");
53
54	port = atoi(arg);
55	if (port == 0 || port > 65535)
56		exit_error(PARAMETER_PROBLEM, "Port `%s' not valid\n", arg);
57
58	dash = strchr(arg, '-');
59	if (!dash) {
60		mr->range[0].min.tcp.port
61			= mr->range[0].max.tcp.port
62			= htons(port);
63	} else {
64		int maxport;
65
66		maxport = atoi(dash + 1);
67		if (maxport == 0 || maxport > 65535)
68			exit_error(PARAMETER_PROBLEM,
69				   "Port `%s' not valid\n", dash+1);
70		if (maxport < port)
71			/* People are stupid. */
72			exit_error(PARAMETER_PROBLEM,
73				   "Port range `%s' funky\n", arg);
74		mr->range[0].min.tcp.port = htons(port);
75		mr->range[0].max.tcp.port = htons(maxport);
76	}
77}
78
79/* Function which parses command options; returns true if it
80   ate an option */
81static int
82parse(int c, char **argv, int invert, unsigned int *flags,
83      const struct ipt_entry *entry,
84      struct ipt_entry_target **target)
85{
86	struct ip_nat_multi_range *mr
87		= (struct ip_nat_multi_range *)(*target)->data;
88	int portok;
89
90	if (entry->ip.proto == IPPROTO_TCP
91	    || entry->ip.proto == IPPROTO_UDP
92	    || entry->ip.proto == IPPROTO_ICMP)
93		portok = 1;
94	else
95		portok = 0;
96
97	switch (c) {
98	case '1':
99		if (!portok)
100			exit_error(PARAMETER_PROBLEM,
101				   "Need TCP or UDP with port specification");
102
103		if (check_inverse(optarg, &invert, NULL, 0))
104			exit_error(PARAMETER_PROBLEM,
105				   "Unexpected `!' after --to-ports");
106
107		parse_ports(optarg, mr);
108		if (*flags & IPT_REDIRECT_OPT_RANDOM)
109			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
110		*flags |= IPT_REDIRECT_OPT_DEST;
111		return 1;
112
113	case '2':
114		if (*flags & IPT_REDIRECT_OPT_DEST) {
115			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
116			*flags |= IPT_REDIRECT_OPT_RANDOM;
117		} else
118			*flags |= IPT_REDIRECT_OPT_RANDOM;
119		return 1;
120
121	default:
122		return 0;
123	}
124}
125
126/* Final check; don't care. */
127static void final_check(unsigned int flags)
128{
129}
130
131/* Prints out the targinfo. */
132static void
133print(const struct ipt_ip *ip,
134      const struct ipt_entry_target *target,
135      int numeric)
136{
137	struct ip_nat_multi_range *mr
138		= (struct ip_nat_multi_range *)target->data;
139	struct ip_nat_range *r = &mr->range[0];
140
141	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
142		printf("redir ports ");
143		printf("%hu", ntohs(r->min.tcp.port));
144		if (r->max.tcp.port != r->min.tcp.port)
145			printf("-%hu", ntohs(r->max.tcp.port));
146		printf(" ");
147		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
148			printf("random ");
149	}
150}
151
152/* Saves the union ipt_targinfo in parsable form to stdout. */
153static void
154save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
155{
156	struct ip_nat_multi_range *mr
157		= (struct ip_nat_multi_range *)target->data;
158	struct ip_nat_range *r = &mr->range[0];
159
160	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
161		printf("--to-ports ");
162		printf("%hu", ntohs(r->min.tcp.port));
163		if (r->max.tcp.port != r->min.tcp.port)
164			printf("-%hu", ntohs(r->max.tcp.port));
165		printf(" ");
166		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
167			printf("--random ");
168	}
169}
170
171static struct iptables_target redir = {
172	.next		= NULL,
173	.name		= "REDIRECT",
174	.version	= IPTABLES_VERSION,
175	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
176	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
177	.help		= &help,
178	.init		= &init,
179 	.parse		= &parse,
180	.final_check	= &final_check,
181	.print		= &print,
182	.save		= &save,
183	.extra_opts	= opts
184};
185
186void _init(void)
187{
188	register_target(&redir);
189}
190