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