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