1e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <stdio.h>
2e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <netdb.h>
3e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <string.h>
4e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <stdlib.h>
5e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <getopt.h>
65d9678ad3eabc34ac40dfe055d7f6a8e44445a5aJan Engelhardt#include <xtables.h>
74e41854423b529d3107c23b85434d50a75d08057Jan Engelhardt#include <limits.h> /* INT_MAX in ip_tables.h */
8e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <linux/netfilter_ipv4/ip_tables.h>
9978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt#include <net/netfilter/nf_nat.h>
10e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
11bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardtenum {
12bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	O_TO_PORTS = 0,
13bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	O_RANDOM,
14bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt};
15bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt
161d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic void MASQUERADE_help(void)
17e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
18e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	printf(
198b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt"MASQUERADE target options:\n"
20e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher" --to-ports <port>[-<port>]\n"
21ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond"				Port (range) to map to.\n"
22ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond" --random\n"
238b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt"				Randomize source port.\n");
24e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
25e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
26bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardtstatic const struct xt_option_entry MASQUERADE_opts[] = {
27bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
28bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
29bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	XTOPT_TABLEEND,
30e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher};
31e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
321d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic void MASQUERADE_init(struct xt_entry_target *t)
33e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
34978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
35e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
36e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	/* Actually, it's 0, but it's ignored at the moment. */
37e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	mr->rangesize = 1;
38e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
39e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
40e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher/* Parses ports */
41e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
42978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardtparse_ports(const char *arg, struct nf_nat_multi_range *mr)
43e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
447278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	char *end;
457278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	unsigned int port, maxport;
46e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
47e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
48e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
497278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
507278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
51e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
527278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	switch (*end) {
537278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	case '\0':
54e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		mr->range[0].min.tcp.port
55e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher			= mr->range[0].max.tcp.port
56f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			= htons(port);
577278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		return;
587278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	case '-':
597278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX))
607278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin			break;
61e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
62e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (maxport < port)
637278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin			break;
647278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin
65f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		mr->range[0].min.tcp.port = htons(port);
66f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		mr->range[0].max.tcp.port = htons(maxport);
677278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		return;
687278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	default:
697278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		break;
70e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
717278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
72e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
73e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
74bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardtstatic void MASQUERADE_parse(struct xt_option_call *cb)
75e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
76bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	const struct ipt_entry *entry = cb->xt_entry;
77e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	int portok;
78bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	struct nf_nat_multi_range *mr = cb->data;
79e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
80e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (entry->ip.proto == IPPROTO_TCP
8136d870c76621b94d51816d09eb8fd05e0fb0a0abPatrick McHardy	    || entry->ip.proto == IPPROTO_UDP
825a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy	    || entry->ip.proto == IPPROTO_SCTP
835a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy	    || entry->ip.proto == IPPROTO_DCCP
8436d870c76621b94d51816d09eb8fd05e0fb0a0abPatrick McHardy	    || entry->ip.proto == IPPROTO_ICMP)
85e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		portok = 1;
86e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	else
87e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		portok = 0;
88e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
89bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	xtables_option_parse(cb);
90bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	switch (cb->entry->id) {
91bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	case O_TO_PORTS:
92e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (!portok)
931829ed482efbc8b390cc760d012b3a4450494e1aJan Engelhardt			xtables_error(PARAMETER_PROBLEM,
945a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy				   "Need TCP, UDP, SCTP or DCCP with port specification");
95bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt		parse_ports(cb->arg, mr);
96bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt		break;
97bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	case O_RANDOM:
98ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond		mr->range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
99bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt		break;
100e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
101e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
102e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
103e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
1041d5b63d12984d12c8d87242179855e17657be16dJan EngelhardtMASQUERADE_print(const void *ip, const struct xt_entry_target *target,
1051d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt                 int numeric)
106e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
10769f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_multi_range *mr = (const void *)target->data;
10869f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_range *r = &mr->range[0];
109e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
110e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
11173866357e4a7a0fdc1b293bf8863fee2bd56da9eJan Engelhardt		printf(" masq ports: ");
112f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		printf("%hu", ntohs(r->min.tcp.port));
113e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (r->max.tcp.port != r->min.tcp.port)
114f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			printf("-%hu", ntohs(r->max.tcp.port));
115e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
116ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond
117e656e265bc67a55f6e51aa07118f96c058a97798Patrick McHardy	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
11873866357e4a7a0fdc1b293bf8863fee2bd56da9eJan Engelhardt		printf(" random");
119e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
120e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
121e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
1221d5b63d12984d12c8d87242179855e17657be16dJan EngelhardtMASQUERADE_save(const void *ip, const struct xt_entry_target *target)
123e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
12469f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_multi_range *mr = (const void *)target->data;
12569f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_range *r = &mr->range[0];
126e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
127e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
12873866357e4a7a0fdc1b293bf8863fee2bd56da9eJan Engelhardt		printf(" --to-ports %hu", ntohs(r->min.tcp.port));
129e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (r->max.tcp.port != r->min.tcp.port)
130f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			printf("-%hu", ntohs(r->max.tcp.port));
131e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
1329c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy
1339c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
13473866357e4a7a0fdc1b293bf8863fee2bd56da9eJan Engelhardt		printf(" --random");
135e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
136e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
1378b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardtstatic struct xtables_target masquerade_tg_reg = {
1388caee8b9e34fed4562fcff553197c161fc9d9979Pablo Neira	.name		= "MASQUERADE",
1398b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt	.version	= XTABLES_VERSION,
14003d99486d8283552705b58dc55b6085dffc38792Jan Engelhardt	.family		= NFPROTO_IPV4,
141978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
142978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
1431d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.help		= MASQUERADE_help,
1441d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.init		= MASQUERADE_init,
145bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	.x6_parse	= MASQUERADE_parse,
1461d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.print		= MASQUERADE_print,
1471d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.save		= MASQUERADE_save,
148bf07750fd4fc5f5e603e59e72d62696d2389e9b3Jan Engelhardt	.x6_options	= MASQUERADE_opts,
149e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher};
150e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
151e6869a8f59d779ff4d5a0984c86d80db7078496Marc Bouchervoid _init(void)
152e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
1538b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt	xtables_register_target(&masquerade_tg_reg);
154e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
155