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