libipt_MASQUERADE.c revision 7278461dfad72e2008585dd0bac0e889e5bba99e
1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/* Shared library add-on to iptables to add masquerade support. */
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <stdio.h>
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <netdb.h>
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <string.h>
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <stdlib.h>
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <getopt.h>
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com#include <xtables.h>
870149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com#include <limits.h> /* INT_MAX in ip_tables.h */
970149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com#include <linux/netfilter_ipv4/ip_tables.h>
10bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com#include <net/netfilter/nf_nat.h>
11bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com
12bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.comstatic void MASQUERADE_help(void)
13bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com{
14bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	printf(
15bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com"MASQUERADE target options:\n"
16bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com" --to-ports <port>[-<port>]\n"
17bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com"				Port (range) to map to.\n"
18bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com" --random\n"
19bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com"				Randomize source port.\n");
20bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com}
21bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com
22bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.comstatic const struct option MASQUERADE_opts[] = {
23bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	{ "to-ports", 1, NULL, '1' },
24bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	{ "random", 0, NULL, '2' },
2570149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	{ .name = NULL }
2670149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com};
2770149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com
2870149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.comstatic void MASQUERADE_init(struct xt_entry_target *t)
2970149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com{
3070149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
31160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com
32160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	/* Actually, it's 0, but it's ignored at the moment. */
33160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	mr->rangesize = 1;
34160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com
35160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com}
36160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com
37160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com/* Parses ports */
38fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.comstatic void
39bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.comparse_ports(const char *arg, struct nf_nat_multi_range *mr)
40bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com{
41bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	char *end;
42bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	unsigned int port, maxport;
4370149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com
4470149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
4570149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com
4670149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
4770149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com		xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
4870149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com
4970149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	switch (*end) {
5070149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com	case '\0':
5170149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com		mr->range[0].min.tcp.port
52160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com			= mr->range[0].max.tcp.port
53160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com			= htons(port);
54160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com		return;
55160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	case '-':
56160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com		if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX))
57160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com			break;
58160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com
59160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com		if (maxport < port)
6070149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com			break;
6170149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com
6270149060a74bb212e67eb140be7cbf97a7cd36a8reed@android.com		mr->range[0].min.tcp.port = htons(port);
63e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		mr->range[0].max.tcp.port = htons(maxport);
64e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		return;
65a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	default:
66a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com		break;
67a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	}
68a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
69a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com}
70a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com
71a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.comstatic int MASQUERADE_parse(int c, char **argv, int invert, unsigned int *flags,
72a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com                            const void *e, struct xt_entry_target **target)
73a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com{
74a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	const struct ipt_entry *entry = e;
75a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	int portok;
76a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	struct nf_nat_multi_range *mr
77e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		= (struct nf_nat_multi_range *)(*target)->data;
78e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
79e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	if (entry->ip.proto == IPPROTO_TCP
80fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com	    || entry->ip.proto == IPPROTO_UDP
81e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	    || entry->ip.proto == IPPROTO_SCTP
82a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	    || entry->ip.proto == IPPROTO_DCCP
83e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	    || entry->ip.proto == IPPROTO_ICMP)
84e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		portok = 1;
85e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	else
86e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		portok = 0;
87e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
88a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	switch (c) {
89a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	case '1':
90a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com		if (!portok)
91a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com			xtables_error(PARAMETER_PROBLEM,
92a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com				   "Need TCP, UDP, SCTP or DCCP with port specification");
93a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com
94a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com		if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
95a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com			xtables_error(PARAMETER_PROBLEM,
96e28ff55d980d2992618b6b721c848aba96cf759areed@android.com				   "Unexpected `!' after --to-ports");
97e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
98fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com		parse_ports(optarg, mr);
99e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		return 1;
100e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
101e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	case '2':
102e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		mr->range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
103e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		return 1;
104e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
105e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	default:
106e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		return 0;
107e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	}
108e28ff55d980d2992618b6b721c848aba96cf759areed@android.com}
109e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
110e28ff55d980d2992618b6b721c848aba96cf759areed@android.comstatic void
111e28ff55d980d2992618b6b721c848aba96cf759areed@android.comMASQUERADE_print(const void *ip, const struct xt_entry_target *target,
112e28ff55d980d2992618b6b721c848aba96cf759areed@android.com                 int numeric)
113e28ff55d980d2992618b6b721c848aba96cf759areed@android.com{
114e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	const struct nf_nat_multi_range *mr = (const void *)target->data;
115e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	const struct nf_nat_range *r = &mr->range[0];
116e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
117fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
118e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf("masq ports: ");
119e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf("%hu", ntohs(r->min.tcp.port));
120e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		if (r->max.tcp.port != r->min.tcp.port)
121e28ff55d980d2992618b6b721c848aba96cf759areed@android.com			printf("-%hu", ntohs(r->max.tcp.port));
122e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf(" ");
123e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	}
124e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
125e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
126e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf("random ");
127a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com}
128a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com
129a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.comstatic void
130e28ff55d980d2992618b6b721c848aba96cf759areed@android.comMASQUERADE_save(const void *ip, const struct xt_entry_target *target)
131e28ff55d980d2992618b6b721c848aba96cf759areed@android.com{
132e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	const struct nf_nat_multi_range *mr = (const void *)target->data;
133e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	const struct nf_nat_range *r = &mr->range[0];
134e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
135e28ff55d980d2992618b6b721c848aba96cf759areed@android.com	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
136e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf("--to-ports %hu", ntohs(r->min.tcp.port));
137e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		if (r->max.tcp.port != r->min.tcp.port)
138e28ff55d980d2992618b6b721c848aba96cf759areed@android.com			printf("-%hu", ntohs(r->max.tcp.port));
139a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com		printf(" ");
140a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	}
141a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com
142a3d901099d7d295cd7d9df4114e874d9ccfff447reed@android.com	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM)
143e28ff55d980d2992618b6b721c848aba96cf759areed@android.com		printf("--random ");
144e28ff55d980d2992618b6b721c848aba96cf759areed@android.com}
145e28ff55d980d2992618b6b721c848aba96cf759areed@android.com
146e28ff55d980d2992618b6b721c848aba96cf759areed@android.comstatic struct xtables_target masquerade_tg_reg = {
147160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.name		= "MASQUERADE",
148160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.version	= XTABLES_VERSION,
149160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.family		= NFPROTO_IPV4,
150160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
151160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
152160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.help		= MASQUERADE_help,
153160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.init		= MASQUERADE_init,
154160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.parse		= MASQUERADE_parse,
155160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.print		= MASQUERADE_print,
156160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.save		= MASQUERADE_save,
157160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com	.extra_opts	= MASQUERADE_opts,
158160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com};
159160f2c0e1724003767dbbea05bc8046ac5dd78d4reed@google.com
1600e51577a14f903ffeafa117a75954baeb173ffb9humper@google.comvoid _init(void)
161bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com{
162bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com	xtables_register_target(&masquerade_tg_reg);
163bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com}
164bddbc45b0b7e279a3e6f151638dfbf3c4e4ad3c1reed@google.com