1/*
2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 *
4 * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6 NAT
5 * funded by Astaro.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <stdlib.h>
11#include <xtables.h>
12#include <limits.h> /* INT_MAX in ip_tables.h */
13#include <linux/netfilter_ipv6/ip6_tables.h>
14#include <linux/netfilter/nf_nat.h>
15
16enum {
17	O_TO_PORTS = 0,
18	O_RANDOM,
19	F_TO_PORTS = 1 << O_TO_PORTS,
20	F_RANDOM   = 1 << O_RANDOM,
21};
22
23static void REDIRECT_help(void)
24{
25	printf(
26"REDIRECT target options:\n"
27" --to-ports <port>[-<port>]\n"
28"				Port (range) to map to.\n"
29" [--random]\n");
30}
31
32static const struct xt_option_entry REDIRECT_opts[] = {
33	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
34	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
35	XTOPT_TABLEEND,
36};
37
38/* Parses ports */
39static void
40parse_ports(const char *arg, struct nf_nat_range *range)
41{
42	char *end = "";
43	unsigned int port, maxport;
44
45	range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
46
47	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX) &&
48	    (port = xtables_service_to_port(arg, NULL)) == (unsigned)-1)
49		xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
50
51	switch (*end) {
52	case '\0':
53		range->min_proto.tcp.port
54			= range->max_proto.tcp.port
55			= htons(port);
56		return;
57	case '-':
58		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX) &&
59		    (maxport = xtables_service_to_port(end + 1, NULL)) == (unsigned)-1)
60			break;
61
62		if (maxport < port)
63			break;
64
65		range->min_proto.tcp.port = htons(port);
66		range->max_proto.tcp.port = htons(maxport);
67		return;
68	default:
69		break;
70	}
71	xtables_param_act(XTF_BAD_VALUE, "REDIRECT", "--to-ports", arg);
72}
73
74static void REDIRECT_parse(struct xt_option_call *cb)
75{
76	const struct ip6t_entry *entry = cb->xt_entry;
77	struct nf_nat_range *range = (void *)(*cb->target)->data;
78	int portok;
79
80	if (entry->ipv6.proto == IPPROTO_TCP
81	    || entry->ipv6.proto == IPPROTO_UDP
82	    || entry->ipv6.proto == IPPROTO_SCTP
83	    || entry->ipv6.proto == IPPROTO_DCCP
84	    || entry->ipv6.proto == IPPROTO_ICMP)
85		portok = 1;
86	else
87		portok = 0;
88
89	xtables_option_parse(cb);
90	switch (cb->entry->id) {
91	case O_TO_PORTS:
92		if (!portok)
93			xtables_error(PARAMETER_PROBLEM,
94				   "Need TCP, UDP, SCTP or DCCP with port specification");
95		parse_ports(cb->arg, range);
96		if (cb->xflags & F_RANDOM)
97			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
98		break;
99	case O_RANDOM:
100		if (cb->xflags & F_TO_PORTS)
101			range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
102		break;
103	}
104}
105
106static void REDIRECT_print(const void *ip, const struct xt_entry_target *target,
107                           int numeric)
108{
109	const struct nf_nat_range *range = (const void *)target->data;
110
111	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
112		printf(" redir ports ");
113		printf("%hu", ntohs(range->min_proto.tcp.port));
114		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
115			printf("-%hu", ntohs(range->max_proto.tcp.port));
116		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
117			printf(" random");
118	}
119}
120
121static void REDIRECT_save(const void *ip, const struct xt_entry_target *target)
122{
123	const struct nf_nat_range *range = (const void *)target->data;
124
125	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
126		printf(" --to-ports ");
127		printf("%hu", ntohs(range->min_proto.tcp.port));
128		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
129			printf("-%hu", ntohs(range->max_proto.tcp.port));
130		if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
131			printf(" --random");
132	}
133}
134
135static struct xtables_target redirect_tg_reg = {
136	.name		= "REDIRECT",
137	.version	= XTABLES_VERSION,
138	.family		= NFPROTO_IPV6,
139	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
140	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
141	.help		= REDIRECT_help,
142	.x6_parse	= REDIRECT_parse,
143	.print		= REDIRECT_print,
144	.save		= REDIRECT_save,
145	.x6_options	= REDIRECT_opts,
146};
147
148void _init(void)
149{
150	xtables_register_target(&redirect_tg_reg);
151}
152