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