libipt_REDIRECT.c revision 63ef52ac6bf8d555779456166009bd2f6b0a1081
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	return 0;
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