• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Shared library add-on to iptables to add source-NAT support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <iptables.h>
8 #include <linux/netfilter_ipv4/ip_tables.h>
9 #include <linux/netfilter_ipv4/ip_nat_rule.h>
10 #include <netinet/in.h>
11 
12 /* Source NAT data consists of a multi-range, indicating where to map
13    to. */
14 struct ipt_natinfo
15 {
16 	struct ipt_entry_target t;
17 	struct ip_nat_multi_range mr;
18 };
19 
20 /* Function which prints out usage message. */
21 static void
help(void)22 help(void)
23 {
24 	printf(
25 "SNAT v%s options:\n"
26 " --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
27 "				Address to map source to.\n"
28 "				(You can use this more than once)\n\n",
29 IPTABLES_VERSION);
30 }
31 
32 static struct option opts[] = {
33 	{ "to-source", 1, 0, '1' },
34 	{ 0 }
35 };
36 
37 static struct ipt_natinfo *
append_range(struct ipt_natinfo * info,const struct ip_nat_range * range)38 append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
39 {
40 	unsigned int size;
41 
42 	/* One rangesize already in struct ipt_natinfo */
43 	size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
44 
45 	info = realloc(info, size);
46 	if (!info)
47 		exit_error(OTHER_PROBLEM, "Out of memory\n");
48 
49 	info->t.u.target_size = size;
50 	info->mr.range[info->mr.rangesize] = *range;
51 	info->mr.rangesize++;
52 
53 	return info;
54 }
55 
56 /* Ranges expected in network order. */
57 static struct ipt_entry_target *
parse_to(char * arg,int portok,struct ipt_natinfo * info)58 parse_to(char *arg, int portok, struct ipt_natinfo *info)
59 {
60 	struct ip_nat_range range;
61 	char *colon, *dash, *error;
62 	struct in_addr *ip;
63 
64 	memset(&range, 0, sizeof(range));
65 	colon = strchr(arg, ':');
66 
67 	if (colon) {
68 		int port;
69 
70 		if (!portok)
71 			exit_error(PARAMETER_PROBLEM,
72 				   "Need TCP or UDP with port specification");
73 
74 		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
75 
76 		port = atoi(colon+1);
77 		if (port <= 0 || port > 65535)
78 			exit_error(PARAMETER_PROBLEM,
79 				   "Port `%s' not valid\n", colon+1);
80 
81 		error = strchr(colon+1, ':');
82 		if (error)
83 			exit_error(PARAMETER_PROBLEM,
84 				   "Invalid port:port syntax - use dash\n");
85 
86 		dash = strchr(colon, '-');
87 		if (!dash) {
88 			range.min.tcp.port
89 				= range.max.tcp.port
90 				= htons(port);
91 		} else {
92 			int maxport;
93 
94 			maxport = atoi(dash + 1);
95 			if (maxport <= 0 || maxport > 65535)
96 				exit_error(PARAMETER_PROBLEM,
97 					   "Port `%s' not valid\n", dash+1);
98 			if (maxport < port)
99 				/* People are stupid. */
100 				exit_error(PARAMETER_PROBLEM,
101 					   "Port range `%s' funky\n", colon+1);
102 			range.min.tcp.port = htons(port);
103 			range.max.tcp.port = htons(maxport);
104 		}
105 		/* Starts with a colon? No IP info...*/
106 		if (colon == arg)
107 			return &(append_range(info, &range)->t);
108 		*colon = '\0';
109 	}
110 
111 	range.flags |= IP_NAT_RANGE_MAP_IPS;
112 	dash = strchr(arg, '-');
113 	if (colon && dash && dash > colon)
114 		dash = NULL;
115 
116 	if (dash)
117 		*dash = '\0';
118 
119 	ip = dotted_to_addr(arg);
120 	if (!ip)
121 		exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
122 			   arg);
123 	range.min_ip = ip->s_addr;
124 	if (dash) {
125 		ip = dotted_to_addr(dash+1);
126 		if (!ip)
127 			exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
128 				   dash+1);
129 		range.max_ip = ip->s_addr;
130 	} else
131 		range.max_ip = range.min_ip;
132 
133 	return &(append_range(info, &range)->t);
134 }
135 
136 /* Function which parses command options; returns true if it
137    ate an option */
138 static int
parse(int c,char ** argv,int invert,unsigned int * flags,const struct ipt_entry * entry,struct ipt_entry_target ** target)139 parse(int c, char **argv, int invert, unsigned int *flags,
140       const struct ipt_entry *entry,
141       struct ipt_entry_target **target)
142 {
143 	struct ipt_natinfo *info = (void *)*target;
144 	int portok;
145 
146 	if (entry->ip.proto == IPPROTO_TCP
147 	    || entry->ip.proto == IPPROTO_UDP
148 	    || entry->ip.proto == IPPROTO_ICMP)
149 		portok = 1;
150 	else
151 		portok = 0;
152 
153 	switch (c) {
154 	case '1':
155 		if (check_inverse(optarg, &invert, NULL, 0))
156 			exit_error(PARAMETER_PROBLEM,
157 				   "Unexpected `!' after --to-source");
158 
159 		if (*flags) {
160 			if (!kernel_version)
161 				get_kernel_version();
162 			if (kernel_version > LINUX_VERSION(2, 6, 10))
163 				exit_error(PARAMETER_PROBLEM,
164 					   "Multiple --to-source not supported");
165 		}
166 		*target = parse_to(optarg, portok, info);
167 		*flags = 1;
168 		return 1;
169 
170 	default:
171 		return 0;
172 	}
173 }
174 
175 /* Final check; must have specfied --to-source. */
final_check(unsigned int flags)176 static void final_check(unsigned int flags)
177 {
178 	if (!flags)
179 		exit_error(PARAMETER_PROBLEM,
180 			   "You must specify --to-source");
181 }
182 
print_range(const struct ip_nat_range * r)183 static void print_range(const struct ip_nat_range *r)
184 {
185 	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
186 		struct in_addr a;
187 
188 		a.s_addr = r->min_ip;
189 		printf("%s", addr_to_dotted(&a));
190 		if (r->max_ip != r->min_ip) {
191 			a.s_addr = r->max_ip;
192 			printf("-%s", addr_to_dotted(&a));
193 		}
194 	}
195 	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
196 		printf(":");
197 		printf("%hu", ntohs(r->min.tcp.port));
198 		if (r->max.tcp.port != r->min.tcp.port)
199 			printf("-%hu", ntohs(r->max.tcp.port));
200 	}
201 }
202 
203 /* Prints out the targinfo. */
204 static void
print(const struct ipt_ip * ip,const struct ipt_entry_target * target,int numeric)205 print(const struct ipt_ip *ip,
206       const struct ipt_entry_target *target,
207       int numeric)
208 {
209 	struct ipt_natinfo *info = (void *)target;
210 	unsigned int i = 0;
211 
212 	printf("to:");
213 	for (i = 0; i < info->mr.rangesize; i++) {
214 		print_range(&info->mr.range[i]);
215 		printf(" ");
216 	}
217 }
218 
219 /* Saves the union ipt_targinfo in parsable form to stdout. */
220 static void
save(const struct ipt_ip * ip,const struct ipt_entry_target * target)221 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
222 {
223 	struct ipt_natinfo *info = (void *)target;
224 	unsigned int i = 0;
225 
226 	for (i = 0; i < info->mr.rangesize; i++) {
227 		printf("--to-source ");
228 		print_range(&info->mr.range[i]);
229 		printf(" ");
230 	}
231 }
232 
233 static struct iptables_target snat = {
234 	.next		= NULL,
235 	.name		= "SNAT",
236 	.version	= IPTABLES_VERSION,
237 	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
238 	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
239 	.help		= &help,
240 	.parse		= &parse,
241 	.final_check	= &final_check,
242 	.print		= &print,
243 	.save		= &save,
244 	.extra_opts	= opts
245 };
246 
ipt_SNAT_init(void)247 void ipt_SNAT_init(void)
248 {
249 	register_target(&snat);
250 }
251