libipt_NETMAP.c revision 7ac405297ec38449b30e3b05fd6bf2082fd3d803
1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/* Shared library add-on to iptables to add static NAT support.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)   Author: Svenning Soerensen <svenning@post5.tele.dk>
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)*/
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <stdbool.h>
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <stdio.h>
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <netdb.h>
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <string.h>
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <stdlib.h>
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <getopt.h>
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <xtables.h>
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <net/netfilter/nf_nat.h>
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#define MODULENAME "NETMAP"
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static const struct option NETMAP_opts[] = {
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	{.name = "to", .has_arg = true, .val = '1'},
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	XT_GETOPT_TABLEEND,
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void NETMAP_help(void)
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	printf(MODULENAME" target options:\n"
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	       "  --%s address[/mask]\n"
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	       "				Network address to map to.\n\n",
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	       NETMAP_opts[0].name);
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static uint32_t
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)bits2netmask(int bits)
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	uint32_t netmask, bm;
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (bits >= 32 || bits < 0)
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		return(~0);
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1)
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		netmask |= bm;
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	return htonl(netmask);
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static int
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)netmask2bits(uint32_t netmask)
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	uint32_t bm;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	int bits;
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	netmask = ntohl(netmask);
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		bits++;
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (netmask)
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		return -1; /* holes in netmask */
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	return bits;
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void NETMAP_init(struct xt_entry_target *t)
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *)t->data;
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
58cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	/* Actually, it's 0, but it's ignored at the moment. */
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	mr->rangesize = 1;
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)/* Parses network address */
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)parse_to(char *arg, struct nf_nat_range *range)
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	char *slash;
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	const struct in_addr *ip;
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	uint32_t netmask;
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	unsigned int bits;
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	range->flags |= IP_NAT_RANGE_MAP_IPS;
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	slash = strchr(arg, '/');
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (slash)
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		*slash = '\0';
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	ip = xtables_numeric_to_ipaddr(arg);
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (!ip)
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			   arg);
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	range->min_ip = ip->s_addr;
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (slash) {
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		if (strchr(slash+1, '.')) {
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			ip = xtables_numeric_to_ipmask(slash+1);
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			if (!ip)
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)				xtables_error(PARAMETER_PROBLEM, "Bad netmask \"%s\"\n",
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)					   slash+1);
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			netmask = ip->s_addr;
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		}
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		else {
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			if (!xtables_strtoui(slash+1, NULL, &bits, 0, 32))
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)				xtables_error(PARAMETER_PROBLEM, "Bad netmask \"%s\"\n",
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)					   slash+1);
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			netmask = bits2netmask(bits);
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		}
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		/* Don't allow /0 (/1 is probably insane, too) */
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		if (netmask == 0)
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM, "Netmask needed\n");
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	}
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	else
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		netmask = ~0;
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (range->min_ip & ~netmask) {
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		if (slash)
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			*slash = '/';
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM, "Bad network address \"%s\"\n",
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			   arg);
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	}
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	range->max_ip = range->min_ip | ~netmask;
110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static int NETMAP_parse(int c, char **argv, int invert, unsigned int *flags,
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                        const void *entry, struct xt_entry_target **target)
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	struct nf_nat_multi_range *mr
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		= (struct nf_nat_multi_range *)(*target)->data;
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	switch (c) {
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	case '1':
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			xtables_error(PARAMETER_PROBLEM,
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)				   "Unexpected `!' after --%s", NETMAP_opts[0].name);
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		parse_to(optarg, &mr->range[0]);
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		*flags = 1;
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		return 1;
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	default:
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		return 0;
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	}
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void NETMAP_check(unsigned int flags)
134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (!flags)
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		xtables_error(PARAMETER_PROBLEM,
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)			   MODULENAME" needs --%s", NETMAP_opts[0].name);
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void NETMAP_print(const void *ip, const struct xt_entry_target *target,
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                         int numeric)
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	const struct nf_nat_multi_range *mr = (const void *)target->data;
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	const struct nf_nat_range *r = &mr->range[0];
145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	struct in_addr a;
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	int bits;
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	a.s_addr = r->min_ip;
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	printf("%s", xtables_ipaddr_to_numeric(&a));
150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	a.s_addr = ~(r->min_ip ^ r->max_ip);
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	bits = netmask2bits(a.s_addr);
152cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	if (bits < 0)
153cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		printf("/%s", xtables_ipaddr_to_numeric(&a));
154cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	else
155cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)		printf("/%d", bits);
156cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
157cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static void NETMAP_save(const void *ip, const struct xt_entry_target *target)
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
160cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	printf("--%s ", NETMAP_opts[0].name);
161cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	NETMAP_print(ip, target, 0);
162cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
163cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
164cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)static struct xtables_target netmap_tg_reg = {
165cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.name		= MODULENAME,
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.version	= XTABLES_VERSION,
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.family		= NFPROTO_IPV4,
168cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.help		= NETMAP_help,
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.init		= NETMAP_init,
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.parse		= NETMAP_parse,
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.final_check	= NETMAP_check,
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.print		= NETMAP_print,
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.save		= NETMAP_save,
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	.extra_opts	= NETMAP_opts,
177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void _init(void)
180cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles){
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)	xtables_register_target(&netmap_tg_reg);
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
183cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)