libipt_REDIRECT.c revision 84d758b3bc3121a5603261699c474f64672ef9f6
1c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)/* Shared library add-on to iptables to add redirect support. */
2c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <stdio.h>
3c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <netdb.h>
4c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <string.h>
5c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <stdlib.h>
6c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <getopt.h>
7c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <xtables.h>
8eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include <limits.h> /* INT_MAX in ip_tables.h */
9c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <linux/netfilter_ipv4/ip_tables.h>
10c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include <net/netfilter/nf_nat.h>
11c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
12c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define IPT_REDIRECT_OPT_DEST	0x01
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#define IPT_REDIRECT_OPT_RANDOM	0x02
14c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static void REDIRECT_help(void)
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){
17c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	printf(
18c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)"REDIRECT target options:\n"
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)" --to-ports <port>[-<port>]\n"
20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)"				Port (range) to map to.\n");
21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
23c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static const struct option REDIRECT_opts[] = {
24c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	{ "to-ports", 1, NULL, '1' },
25c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	{ "random", 0, NULL, '2' },
26c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	{ .name = NULL }
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
28c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static void REDIRECT_init(struct xt_entry_target *t)
30c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){
31c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	/* Actually, it's 0, but it's ignored at the moment. */
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci	mr->rangesize = 1;
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci/* Parses ports */
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccistatic void
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciparse_ports(const char *arg, struct nf_nat_multi_range *mr)
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	char *end;
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	unsigned int port, maxport;
443551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	switch (*end) {
52eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch	case '\0':
53c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		mr->range[0].min.tcp.port
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)			= mr->range[0].max.tcp.port
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)			= htons(port);
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		return;
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	case '-':
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
59c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
60c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)			break;
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		if (maxport < port)
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)			break;
64c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
65c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		mr->range[0].min.tcp.port = htons(port);
66c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		mr->range[0].max.tcp.port = htons(maxport);
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		return;
68c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	default:
69c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)		break;
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	}
71c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static int REDIRECT_parse(int c, char **argv, int invert, unsigned int *flags,
75c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                          const void *e, struct xt_entry_target **target)
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles){
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)	const struct ipt_entry *entry = e;
78	struct nf_nat_multi_range *mr
79		= (struct nf_nat_multi_range *)(*target)->data;
80	int portok;
81
82	if (entry->ip.proto == IPPROTO_TCP
83	    || entry->ip.proto == IPPROTO_UDP
84	    || entry->ip.proto == IPPROTO_SCTP
85	    || entry->ip.proto == IPPROTO_DCCP
86	    || entry->ip.proto == IPPROTO_ICMP)
87		portok = 1;
88	else
89		portok = 0;
90
91	switch (c) {
92	case '1':
93		if (!portok)
94			xtables_error(PARAMETER_PROBLEM,
95				   "Need TCP, UDP, SCTP or DCCP with port specification");
96
97		if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
98			xtables_error(PARAMETER_PROBLEM,
99				   "Unexpected `!' after --to-ports");
100
101		parse_ports(optarg, mr);
102		if (*flags & IPT_REDIRECT_OPT_RANDOM)
103			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
104		*flags |= IPT_REDIRECT_OPT_DEST;
105		return 1;
106
107	case '2':
108		if (*flags & IPT_REDIRECT_OPT_DEST) {
109			mr->range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
110			*flags |= IPT_REDIRECT_OPT_RANDOM;
111		} else
112			*flags |= IPT_REDIRECT_OPT_RANDOM;
113		return 1;
114
115	default:
116		return 0;
117	}
118}
119
120static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
121                           int numeric)
122{
123	const struct nf_nat_multi_range *mr = (const void *)target->data;
124	const struct nf_nat_range *r = &mr->range[0];
125
126	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
127		printf("redir ports ");
128		printf("%hu", ntohs(r->min.tcp.port));
129		if (r->max.tcp.port != r->min.tcp.port)
130			printf("-%hu", ntohs(r->max.tcp.port));
131		printf(" ");
132		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
133			printf("random ");
134	}
135}
136
137static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
138{
139	const struct nf_nat_multi_range *mr = (const void *)target->data;
140	const struct nf_nat_range *r = &mr->range[0];
141
142	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
143		printf("--to-ports ");
144		printf("%hu", ntohs(r->min.tcp.port));
145		if (r->max.tcp.port != r->min.tcp.port)
146			printf("-%hu", ntohs(r->max.tcp.port));
147		printf(" ");
148		if (mr->range[0].flags & IP_NAT_RANGE_PROTO_RANDOM)
149			printf("--random ");
150	}
151}
152
153static struct xtables_target redirect_tg_reg = {
154	.name		= "REDIRECT",
155	.version	= XTABLES_VERSION,
156	.family		= NFPROTO_IPV4,
157	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
158	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
159	.help		= REDIRECT_help,
160	.init		= REDIRECT_init,
161 	.parse		= REDIRECT_parse,
162	.print		= REDIRECT_print,
163	.save		= REDIRECT_save,
164	.extra_opts	= REDIRECT_opts,
165};
166
167void _init(void)
168{
169	xtables_register_target(&redirect_tg_reg);
170}
171