libipt_MASQUERADE.c revision 7278461dfad72e2008585dd0bac0e889e5bba99e
1e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher/* Shared library add-on to iptables to add masquerade support. */
2e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <stdio.h>
3e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <netdb.h>
4e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <string.h>
5e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <stdlib.h>
6e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <getopt.h>
75d9678ad3eabc34ac40dfe055d7f6a8e44445a5aJan Engelhardt#include <xtables.h>
84e41854423b529d3107c23b85434d50a75d08057Jan Engelhardt#include <limits.h> /* INT_MAX in ip_tables.h */
9e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher#include <linux/netfilter_ipv4/ip_tables.h>
10978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt#include <net/netfilter/nf_nat.h>
11e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
121d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic void MASQUERADE_help(void)
13e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
14e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	printf(
158b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt"MASQUERADE target options:\n"
16e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher" --to-ports <port>[-<port>]\n"
17ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond"				Port (range) to map to.\n"
18ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond" --random\n"
198b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt"				Randomize source port.\n");
20e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
21e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
221d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic const struct option MASQUERADE_opts[] = {
23500f483fff529dcd88ec96b9d5054be6cd6363a0Patrick McHardy	{ "to-ports", 1, NULL, '1' },
24500f483fff529dcd88ec96b9d5054be6cd6363a0Patrick McHardy	{ "random", 0, NULL, '2' },
259ee386a1b6d7704b259460152c959ab0e79e02aaMax Kellermann	{ .name = NULL }
26e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher};
27e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
281d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic void MASQUERADE_init(struct xt_entry_target *t)
29e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
30978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
31e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
32e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	/* Actually, it's 0, but it's ignored at the moment. */
33e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	mr->rangesize = 1;
34e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
35e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
36e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
37e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher/* Parses ports */
38e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
39978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardtparse_ports(const char *arg, struct nf_nat_multi_range *mr)
40e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
417278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	char *end;
427278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	unsigned int port, maxport;
43e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
44e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
45e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
467278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
477278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
48e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
497278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	switch (*end) {
507278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	case '\0':
51e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		mr->range[0].min.tcp.port
52e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher			= mr->range[0].max.tcp.port
53f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			= htons(port);
547278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		return;
557278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	case '-':
567278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX))
577278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin			break;
58e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
59e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (maxport < port)
607278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin			break;
617278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin
62f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		mr->range[0].min.tcp.port = htons(port);
63f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		mr->range[0].max.tcp.port = htons(maxport);
647278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		return;
657278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	default:
667278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin		break;
67e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
687278461dfad72e2008585dd0bac0e889e5bba99eDmitry V. Levin	xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
69e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
70e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
711d5b63d12984d12c8d87242179855e17657be16dJan Engelhardtstatic int MASQUERADE_parse(int c, char **argv, int invert, unsigned int *flags,
721d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt                            const void *e, struct xt_entry_target **target)
73e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
74ac8b2718daf8a79a59b181f6e62495f307ae86b9Yasuyuki KOZAKAI	const struct ipt_entry *entry = e;
75e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	int portok;
76978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	struct nf_nat_multi_range *mr
77978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt		= (struct nf_nat_multi_range *)(*target)->data;
78e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
79e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (entry->ip.proto == IPPROTO_TCP
8036d870c76621b94d51816d09eb8fd05e0fb0a0abPatrick McHardy	    || entry->ip.proto == IPPROTO_UDP
815a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy	    || entry->ip.proto == IPPROTO_SCTP
825a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy	    || entry->ip.proto == IPPROTO_DCCP
8336d870c76621b94d51816d09eb8fd05e0fb0a0abPatrick McHardy	    || entry->ip.proto == IPPROTO_ICMP)
84e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		portok = 1;
85e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	else
86e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		portok = 0;
87e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
88e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	switch (c) {
89e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	case '1':
90e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (!portok)
911829ed482efbc8b390cc760d012b3a4450494e1aJan Engelhardt			xtables_error(PARAMETER_PROBLEM,
925a942f9501f7ce287e1c37c553eb02a1e269e081Patrick McHardy				   "Need TCP, UDP, SCTP or DCCP with port specification");
93e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
94bf97128c7262f17a02fec41cdae75b472ba77f88Jan Engelhardt		if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
951829ed482efbc8b390cc760d012b3a4450494e1aJan Engelhardt			xtables_error(PARAMETER_PROBLEM,
96e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher				   "Unexpected `!' after --to-ports");
97e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
98e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		parse_ports(optarg, mr);
99e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		return 1;
100e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
101ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond	case '2':
102ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond		mr->range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
103ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond		return 1;
104ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond
105e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	default:
106e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		return 0;
107e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
108e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
109e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
110e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
1111d5b63d12984d12c8d87242179855e17657be16dJan EngelhardtMASQUERADE_print(const void *ip, const struct xt_entry_target *target,
1121d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt                 int numeric)
113e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
11469f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_multi_range *mr = (const void *)target->data;
11569f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_range *r = &mr->range[0];
116e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
117e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
118f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		printf("masq ports: ");
119f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell		printf("%hu", ntohs(r->min.tcp.port));
120e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (r->max.tcp.port != r->min.tcp.port)
121f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			printf("-%hu", ntohs(r->max.tcp.port));
122e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		printf(" ");
123e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
124ae4b0b3aa70c67f2eff303a3e75834e45c3794a7Eric Leblond
125e656e265bc67a55f6e51aa07118f96c058a97798Patrick McHardy	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
1269c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy		printf("random ");
127e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
128e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
129e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucherstatic void
1301d5b63d12984d12c8d87242179855e17657be16dJan EngelhardtMASQUERADE_save(const void *ip, const struct xt_entry_target *target)
131e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
13269f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_multi_range *mr = (const void *)target->data;
13369f564e3890976461de0016cd81171ff8bfa8353Jan Engelhardt	const struct nf_nat_range *r = &mr->range[0];
134e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
135e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
1364b5566b718ccd52a11d5eabe1de9b1f4ff10ce41A. van Schie		printf("--to-ports %hu", ntohs(r->min.tcp.port));
137e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		if (r->max.tcp.port != r->min.tcp.port)
138f9b2e66877b743962a36ec9c37335b9bc3f8b70fRusty Russell			printf("-%hu", ntohs(r->max.tcp.port));
139e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher		printf(" ");
140e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher	}
1419c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy
1429c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
1439c67defe98f04f72f19dfd09c8030e1de4b8bf0fPatrick McHardy		printf("--random ");
144e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
145e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
1468b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardtstatic struct xtables_target masquerade_tg_reg = {
1478caee8b9e34fed4562fcff553197c161fc9d9979Pablo Neira	.name		= "MASQUERADE",
1488b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt	.version	= XTABLES_VERSION,
14903d99486d8283552705b58dc55b6085dffc38792Jan Engelhardt	.family		= NFPROTO_IPV4,
150978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
151978e27e8f8c2e49d0528c6c4ae3a56627fbe8492Jan Engelhardt	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
1521d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.help		= MASQUERADE_help,
1531d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.init		= MASQUERADE_init,
1541d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.parse		= MASQUERADE_parse,
1551d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.print		= MASQUERADE_print,
1561d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.save		= MASQUERADE_save,
1571d5b63d12984d12c8d87242179855e17657be16dJan Engelhardt	.extra_opts	= MASQUERADE_opts,
158e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher};
159e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher
160e6869a8f59d779ff4d5a0984c86d80db7078496Marc Bouchervoid _init(void)
161e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher{
1628b7c64d6ba156a99008fcd810cba874c73294333Jan Engelhardt	xtables_register_target(&masquerade_tg_reg);
163e6869a8f59d779ff4d5a0984c86d80db7078496Marc Boucher}
164