OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / user / iptables / 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 <xtables.h>
8 #include <iptables.h>
9 #include <limits.h> /* INT_MAX in ip_tables.h */
10 #include <linux/netfilter_ipv4/ip_tables.h>
11 #include <net/netfilter/nf_nat.h>
12
13 #define IPT_SNAT_OPT_SOURCE 0x01
14 #define IPT_SNAT_OPT_RANDOM 0x02
15
16 /* Source NAT data consists of a multi-range, indicating where to map
17    to. */
18 struct ipt_natinfo
19 {
20         struct xt_entry_target t;
21         struct nf_nat_multi_range mr;
22 };
23
24 static void SNAT_help(void)
25 {
26         printf(
27 "SNAT target options:\n"
28 " --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
29 "                               Address to map source to.\n"
30 "[--random]\n");
31 }
32
33 static const struct option SNAT_opts[] = {
34         { "to-source", 1, NULL, '1' },
35         { "random", 0, NULL, '2' },
36         { .name = NULL }
37 };
38
39 static struct ipt_natinfo *
40 append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
41 {
42         unsigned int size;
43
44         /* One rangesize already in struct ipt_natinfo */
45         size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
46
47         info = realloc(info, size);
48         if (!info)
49                 xtables_error(OTHER_PROBLEM, "Out of memory\n");
50
51         info->t.u.target_size = size;
52         info->mr.range[info->mr.rangesize] = *range;
53         info->mr.rangesize++;
54
55         return info;
56 }
57
58 /* Ranges expected in network order. */
59 static struct xt_entry_target *
60 parse_to(char *arg, int portok, struct ipt_natinfo *info)
61 {
62         struct nf_nat_range range;
63         char *colon, *dash, *error;
64         const struct in_addr *ip;
65
66         memset(&range, 0, sizeof(range));
67         colon = strchr(arg, ':');
68
69         if (colon) {
70                 int port;
71
72                 if (!portok)
73                         xtables_error(PARAMETER_PROBLEM,
74                                    "Need TCP, UDP, SCTP or DCCP with port specification");
75
76                 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
77
78                 port = atoi(colon+1);
79                 if (port <= 0 || port > 65535)
80                         xtables_error(PARAMETER_PROBLEM,
81                                    "Port `%s' not valid\n", colon+1);
82
83                 error = strchr(colon+1, ':');
84                 if (error)
85                         xtables_error(PARAMETER_PROBLEM,
86                                    "Invalid port:port syntax - use dash\n");
87
88                 dash = strchr(colon, '-');
89                 if (!dash) {
90                         range.min.tcp.port
91                                 = range.max.tcp.port
92                                 = htons(port);
93                 } else {
94                         int maxport;
95
96                         maxport = atoi(dash + 1);
97                         if (maxport <= 0 || maxport > 65535)
98                                 xtables_error(PARAMETER_PROBLEM,
99                                            "Port `%s' not valid\n", dash+1);
100                         if (maxport < port)
101                                 /* People are stupid. */
102                                 xtables_error(PARAMETER_PROBLEM,
103                                            "Port range `%s' funky\n", colon+1);
104                         range.min.tcp.port = htons(port);
105                         range.max.tcp.port = htons(maxport);
106                 }
107                 /* Starts with a colon? No IP info...*/
108                 if (colon == arg)
109                         return &(append_range(info, &range)->t);
110                 *colon = '\0';
111         }
112
113         range.flags |= IP_NAT_RANGE_MAP_IPS;
114         dash = strchr(arg, '-');
115         if (colon && dash && dash > colon)
116                 dash = NULL;
117
118         if (dash)
119                 *dash = '\0';
120
121         ip = xtables_numeric_to_ipaddr(arg);
122         if (!ip)
123                 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
124                            arg);
125         range.min_ip = ip->s_addr;
126         if (dash) {
127                 ip = xtables_numeric_to_ipaddr(dash+1);
128                 if (!ip)
129                         xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
130                                    dash+1);
131                 range.max_ip = ip->s_addr;
132         } else
133                 range.max_ip = range.min_ip;
134
135         return &(append_range(info, &range)->t);
136 }
137
138 static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
139                       const void *e, struct xt_entry_target **target)
140 {
141         const struct ipt_entry *entry = e;
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             || entry->ip.proto == IPPROTO_SCTP
148             || entry->ip.proto == IPPROTO_DCCP
149             || entry->ip.proto == IPPROTO_ICMP)
150                 portok = 1;
151         else
152                 portok = 0;
153
154         switch (c) {
155         case '1':
156                 if (xtables_check_inverse(optarg, &invert, NULL, 0))
157                         xtables_error(PARAMETER_PROBLEM,
158                                    "Unexpected `!' after --to-source");
159
160                 if (*flags & IPT_SNAT_OPT_SOURCE) {
161                         if (!kernel_version)
162                                 get_kernel_version();
163                         if (kernel_version > LINUX_VERSION(2, 6, 10))
164                                 xtables_error(PARAMETER_PROBLEM,
165                                            "Multiple --to-source not supported");
166                 }
167                 *target = parse_to(optarg, portok, info);
168                 /* WTF do we need this for?? */
169                 if (*flags & IPT_SNAT_OPT_RANDOM)
170                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
171                 *flags |= IPT_SNAT_OPT_SOURCE;
172                 return 1;
173
174         case '2':
175                 if (*flags & IPT_SNAT_OPT_SOURCE) {
176                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
177                         *flags |= IPT_SNAT_OPT_RANDOM;
178                 } else
179                         *flags |= IPT_SNAT_OPT_RANDOM;
180                 return 1;
181
182         default:
183                 return 0;
184         }
185 }
186
187 static void SNAT_check(unsigned int flags)
188 {
189         if (!(flags & IPT_SNAT_OPT_SOURCE))
190                 xtables_error(PARAMETER_PROBLEM,
191                            "You must specify --to-source");
192 }
193
194 static void print_range(const struct nf_nat_range *r)
195 {
196         if (r->flags & IP_NAT_RANGE_MAP_IPS) {
197                 struct in_addr a;
198
199                 a.s_addr = r->min_ip;
200                 printf("%s", xtables_ipaddr_to_numeric(&a));
201                 if (r->max_ip != r->min_ip) {
202                         a.s_addr = r->max_ip;
203                         printf("-%s", xtables_ipaddr_to_numeric(&a));
204                 }
205         }
206         if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
207                 printf(":");
208                 printf("%hu", ntohs(r->min.tcp.port));
209                 if (r->max.tcp.port != r->min.tcp.port)
210                         printf("-%hu", ntohs(r->max.tcp.port));
211         }
212 }
213
214 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
215                        int numeric)
216 {
217         struct ipt_natinfo *info = (void *)target;
218         unsigned int i = 0;
219
220         printf("to:");
221         for (i = 0; i < info->mr.rangesize; i++) {
222                 print_range(&info->mr.range[i]);
223                 printf(" ");
224                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
225                         printf("random ");
226         }
227 }
228
229 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
230 {
231         struct ipt_natinfo *info = (void *)target;
232         unsigned int i = 0;
233
234         for (i = 0; i < info->mr.rangesize; i++) {
235                 printf("--to-source ");
236                 print_range(&info->mr.range[i]);
237                 printf(" ");
238                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
239                         printf("--random ");
240         }
241 }
242
243 static struct xtables_target snat_tg_reg = {
244         .name           = "SNAT",
245         .version        = XTABLES_VERSION,
246         .family         = NFPROTO_IPV4,
247         .size           = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
248         .userspacesize  = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
249         .help           = SNAT_help,
250         .parse          = SNAT_parse,
251         .final_check    = SNAT_check,
252         .print          = SNAT_print,
253         .save           = SNAT_save,
254         .extra_opts     = SNAT_opts,
255 };
256
257 void _init(void)
258 {
259         xtables_register_target(&snat_tg_reg);
260 }