blob: 7eae53c94b0fbb937a912ecdd9f16eb745a59ab0 [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* 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>
Patrick McHardy40d54752007-04-18 07:00:36 +00009#include <linux/netfilter/nf_nat.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000010
Eric Leblondae4b0b32007-02-24 15:11:33 +000011#define IPT_SNAT_OPT_SOURCE 0x01
Patrick McHardye656e262007-04-18 12:56:05 +000012#define IPT_SNAT_OPT_RANDOM 0x02
Eric Leblondae4b0b32007-02-24 15:11:33 +000013
Marc Bouchere6869a82000-03-20 06:03:29 +000014/* Source NAT data consists of a multi-range, indicating where to map
15 to. */
16struct ipt_natinfo
17{
Yasuyuki KOZAKAI193df8e2007-07-24 05:57:28 +000018 struct xt_entry_target t;
Marc Bouchere6869a82000-03-20 06:03:29 +000019 struct ip_nat_multi_range mr;
20};
21
22/* Function which prints out usage message. */
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000023static void SNAT_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000024{
25 printf(
26"SNAT v%s options:\n"
Patrick McHardyef399a32007-05-29 11:24:45 +000027" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000028" Address to map source to.\n"
Patrick McHardyef399a32007-05-29 11:24:45 +000029"[--random]\n"
30"\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000031IPTABLES_VERSION);
Marc Bouchere6869a82000-03-20 06:03:29 +000032}
33
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000034static const struct option SNAT_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000035 { "to-source", 1, NULL, '1' },
36 { "random", 0, NULL, '2' },
37 { }
Marc Bouchere6869a82000-03-20 06:03:29 +000038};
39
Marc Bouchere6869a82000-03-20 06:03:29 +000040static struct ipt_natinfo *
41append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
42{
43 unsigned int size;
44
45 /* One rangesize already in struct ipt_natinfo */
46 size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
47
48 info = realloc(info, size);
49 if (!info)
50 exit_error(OTHER_PROBLEM, "Out of memory\n");
51
Rusty Russell228e98d2000-04-27 10:28:06 +000052 info->t.u.target_size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +000053 info->mr.range[info->mr.rangesize] = *range;
54 info->mr.rangesize++;
55
56 return info;
57}
58
59/* Ranges expected in network order. */
Yasuyuki KOZAKAI193df8e2007-07-24 05:57:28 +000060static struct xt_entry_target *
Marc Bouchere6869a82000-03-20 06:03:29 +000061parse_to(char *arg, int portok, struct ipt_natinfo *info)
62{
63 struct ip_nat_range range;
Harald Weltede5ba5d2005-02-01 15:14:15 +000064 char *colon, *dash, *error;
Jan Engelhardtbd943842008-01-20 13:38:08 +000065 const struct in_addr *ip;
Marc Bouchere6869a82000-03-20 06:03:29 +000066
67 memset(&range, 0, sizeof(range));
68 colon = strchr(arg, ':');
69
70 if (colon) {
71 int port;
72
73 if (!portok)
74 exit_error(PARAMETER_PROBLEM,
75 "Need TCP or UDP with port specification");
76
77 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
78
79 port = atoi(colon+1);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000080 if (port <= 0 || port > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000081 exit_error(PARAMETER_PROBLEM,
82 "Port `%s' not valid\n", colon+1);
83
Harald Weltede5ba5d2005-02-01 15:14:15 +000084 error = strchr(colon+1, ':');
85 if (error)
86 exit_error(PARAMETER_PROBLEM,
87 "Invalid port:port syntax - use dash\n");
88
Marc Bouchere6869a82000-03-20 06:03:29 +000089 dash = strchr(colon, '-');
90 if (!dash) {
91 range.min.tcp.port
92 = range.max.tcp.port
93 = htons(port);
94 } else {
95 int maxport;
96
97 maxport = atoi(dash + 1);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000098 if (maxport <= 0 || maxport > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000099 exit_error(PARAMETER_PROBLEM,
100 "Port `%s' not valid\n", dash+1);
101 if (maxport < port)
102 /* People are stupid. */
103 exit_error(PARAMETER_PROBLEM,
104 "Port range `%s' funky\n", colon+1);
105 range.min.tcp.port = htons(port);
106 range.max.tcp.port = htons(maxport);
107 }
108 /* Starts with a colon? No IP info...*/
109 if (colon == arg)
110 return &(append_range(info, &range)->t);
111 *colon = '\0';
112 }
113
114 range.flags |= IP_NAT_RANGE_MAP_IPS;
115 dash = strchr(arg, '-');
116 if (colon && dash && dash > colon)
117 dash = NULL;
118
119 if (dash)
120 *dash = '\0';
121
Jan Engelhardtbd943842008-01-20 13:38:08 +0000122 ip = numeric_to_ipaddr(arg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000123 if (!ip)
124 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
125 arg);
126 range.min_ip = ip->s_addr;
127 if (dash) {
Jan Engelhardtbd943842008-01-20 13:38:08 +0000128 ip = numeric_to_ipaddr(dash+1);
Marc Bouchere6869a82000-03-20 06:03:29 +0000129 if (!ip)
130 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
131 dash+1);
132 range.max_ip = ip->s_addr;
133 } else
134 range.max_ip = range.min_ip;
135
136 return &(append_range(info, &range)->t);
137}
138
139/* Function which parses command options; returns true if it
140 ate an option */
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000141static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
142 const void *e, struct xt_entry_target **target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000143{
Yasuyuki KOZAKAIac8b2712007-07-24 06:06:59 +0000144 const struct ipt_entry *entry = e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000145 struct ipt_natinfo *info = (void *)*target;
146 int portok;
147
148 if (entry->ip.proto == IPPROTO_TCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000149 || entry->ip.proto == IPPROTO_UDP
150 || entry->ip.proto == IPPROTO_ICMP)
Marc Bouchere6869a82000-03-20 06:03:29 +0000151 portok = 1;
152 else
153 portok = 0;
154
155 switch (c) {
156 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000157 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000158 exit_error(PARAMETER_PROBLEM,
159 "Unexpected `!' after --to-source");
160
Eric Leblondae4b0b32007-02-24 15:11:33 +0000161 if (*flags & IPT_SNAT_OPT_SOURCE) {
Phil Oester8cf65912005-09-19 15:00:33 +0000162 if (!kernel_version)
163 get_kernel_version();
164 if (kernel_version > LINUX_VERSION(2, 6, 10))
165 exit_error(PARAMETER_PROBLEM,
166 "Multiple --to-source not supported");
167 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000168 *target = parse_to(optarg, portok, info);
Patrick McHardye656e262007-04-18 12:56:05 +0000169 /* WTF do we need this for?? */
Eric Leblondae4b0b32007-02-24 15:11:33 +0000170 if (*flags & IPT_SNAT_OPT_RANDOM)
Patrick McHardyef399a32007-05-29 11:24:45 +0000171 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
172 *flags |= IPT_SNAT_OPT_SOURCE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000173 return 1;
174
Eric Leblondae4b0b32007-02-24 15:11:33 +0000175 case '2':
176 if (*flags & IPT_SNAT_OPT_SOURCE) {
Patrick McHardyef399a32007-05-29 11:24:45 +0000177 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000178 *flags |= IPT_SNAT_OPT_RANDOM;
179 } else
180 *flags |= IPT_SNAT_OPT_RANDOM;
181 return 1;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000182
Marc Bouchere6869a82000-03-20 06:03:29 +0000183 default:
184 return 0;
185 }
186}
187
188/* Final check; must have specfied --to-source. */
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000189static void SNAT_check(unsigned int flags)
Marc Bouchere6869a82000-03-20 06:03:29 +0000190{
Eric Leblondae4b0b32007-02-24 15:11:33 +0000191 if (!(flags & IPT_SNAT_OPT_SOURCE))
Marc Bouchere6869a82000-03-20 06:03:29 +0000192 exit_error(PARAMETER_PROBLEM,
193 "You must specify --to-source");
194}
195
196static void print_range(const struct ip_nat_range *r)
197{
198 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
199 struct in_addr a;
200
201 a.s_addr = r->min_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000202 printf("%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000203 if (r->max_ip != r->min_ip) {
204 a.s_addr = r->max_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000205 printf("-%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000206 }
207 }
208 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
209 printf(":");
210 printf("%hu", ntohs(r->min.tcp.port));
211 if (r->max.tcp.port != r->min.tcp.port)
212 printf("-%hu", ntohs(r->max.tcp.port));
213 }
214}
215
216/* Prints out the targinfo. */
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000217static void SNAT_print(const void *ip, const struct xt_entry_target *target,
218 int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000219{
220 struct ipt_natinfo *info = (void *)target;
221 unsigned int i = 0;
222
223 printf("to:");
224 for (i = 0; i < info->mr.rangesize; i++) {
225 print_range(&info->mr.range[i]);
226 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000227 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
228 printf("random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000229 }
230}
231
232/* Saves the union ipt_targinfo in parsable form to stdout. */
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000233static void SNAT_save(const void *ip, const struct xt_entry_target *target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000234{
235 struct ipt_natinfo *info = (void *)target;
236 unsigned int i = 0;
237
238 for (i = 0; i < info->mr.rangesize; i++) {
239 printf("--to-source ");
240 print_range(&info->mr.range[i]);
241 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000242 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
243 printf("--random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000244 }
245}
246
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000247static struct iptables_target snat_target = {
Pablo Neira8caee8b2004-12-28 13:11:59 +0000248 .name = "SNAT",
249 .version = IPTABLES_VERSION,
250 .size = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
251 .userspacesize = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000252 .help = SNAT_help,
253 .parse = SNAT_parse,
254 .final_check = SNAT_check,
255 .print = SNAT_print,
256 .save = SNAT_save,
257 .extra_opts = SNAT_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000258};
259
260void _init(void)
261{
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000262 register_target(&snat_target);
Marc Bouchere6869a82000-03-20 06:03:29 +0000263}