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