blob: 7d04761b712b3811422efe621549484855fc962d [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
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000022static void SNAT_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000023{
24 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020025"SNAT target options:\n"
Patrick McHardyef399a32007-05-29 11:24:45 +000026" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000027" Address to map source to.\n"
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020028"[--random]\n");
Marc Bouchere6869a82000-03-20 06:03:29 +000029}
30
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000031static const struct option SNAT_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000032 { "to-source", 1, NULL, '1' },
33 { "random", 0, NULL, '2' },
Max Kellermann9ee386a2008-01-29 13:48:05 +000034 { .name = NULL }
Marc Bouchere6869a82000-03-20 06:03:29 +000035};
36
Marc Bouchere6869a82000-03-20 06:03:29 +000037static struct ipt_natinfo *
38append_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 */
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020043 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
Marc Bouchere6869a82000-03-20 06:03:29 +000044
45 info = realloc(info, size);
46 if (!info)
47 exit_error(OTHER_PROBLEM, "Out of memory\n");
48
Rusty Russell228e98d2000-04-27 10:28:06 +000049 info->t.u.target_size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +000050 info->mr.range[info->mr.rangesize] = *range;
51 info->mr.rangesize++;
52
53 return info;
54}
55
56/* Ranges expected in network order. */
Yasuyuki KOZAKAI193df8e2007-07-24 05:57:28 +000057static struct xt_entry_target *
Marc Bouchere6869a82000-03-20 06:03:29 +000058parse_to(char *arg, int portok, struct ipt_natinfo *info)
59{
60 struct ip_nat_range range;
Harald Weltede5ba5d2005-02-01 15:14:15 +000061 char *colon, *dash, *error;
Jan Engelhardtbd943842008-01-20 13:38:08 +000062 const struct in_addr *ip;
Marc Bouchere6869a82000-03-20 06:03:29 +000063
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,
Patrick McHardy5a942f92008-11-04 13:22:40 +010072 "Need TCP, UDP, SCTP or DCCP with port specification");
Marc Bouchere6869a82000-03-20 06:03:29 +000073
74 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
75
76 port = atoi(colon+1);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000077 if (port <= 0 || port > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000078 exit_error(PARAMETER_PROBLEM,
79 "Port `%s' not valid\n", colon+1);
80
Harald Weltede5ba5d2005-02-01 15:14:15 +000081 error = strchr(colon+1, ':');
82 if (error)
83 exit_error(PARAMETER_PROBLEM,
84 "Invalid port:port syntax - use dash\n");
85
Marc Bouchere6869a82000-03-20 06:03:29 +000086 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);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000095 if (maxport <= 0 || maxport > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000096 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
Jan Engelhardtbd943842008-01-20 13:38:08 +0000119 ip = numeric_to_ipaddr(arg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000120 if (!ip)
121 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
122 arg);
123 range.min_ip = ip->s_addr;
124 if (dash) {
Jan Engelhardtbd943842008-01-20 13:38:08 +0000125 ip = numeric_to_ipaddr(dash+1);
Marc Bouchere6869a82000-03-20 06:03:29 +0000126 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
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000136static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
137 const void *e, struct xt_entry_target **target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000138{
Yasuyuki KOZAKAIac8b2712007-07-24 06:06:59 +0000139 const struct ipt_entry *entry = e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000140 struct ipt_natinfo *info = (void *)*target;
141 int portok;
142
143 if (entry->ip.proto == IPPROTO_TCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000144 || entry->ip.proto == IPPROTO_UDP
Patrick McHardy5a942f92008-11-04 13:22:40 +0100145 || entry->ip.proto == IPPROTO_SCTP
146 || entry->ip.proto == IPPROTO_DCCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000147 || entry->ip.proto == IPPROTO_ICMP)
Marc Bouchere6869a82000-03-20 06:03:29 +0000148 portok = 1;
149 else
150 portok = 0;
151
152 switch (c) {
153 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000154 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000155 exit_error(PARAMETER_PROBLEM,
156 "Unexpected `!' after --to-source");
157
Eric Leblondae4b0b32007-02-24 15:11:33 +0000158 if (*flags & IPT_SNAT_OPT_SOURCE) {
Phil Oester8cf65912005-09-19 15:00:33 +0000159 if (!kernel_version)
160 get_kernel_version();
161 if (kernel_version > LINUX_VERSION(2, 6, 10))
162 exit_error(PARAMETER_PROBLEM,
163 "Multiple --to-source not supported");
164 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000165 *target = parse_to(optarg, portok, info);
Patrick McHardye656e262007-04-18 12:56:05 +0000166 /* WTF do we need this for?? */
Eric Leblondae4b0b32007-02-24 15:11:33 +0000167 if (*flags & IPT_SNAT_OPT_RANDOM)
Patrick McHardyef399a32007-05-29 11:24:45 +0000168 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
169 *flags |= IPT_SNAT_OPT_SOURCE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000170 return 1;
171
Eric Leblondae4b0b32007-02-24 15:11:33 +0000172 case '2':
173 if (*flags & IPT_SNAT_OPT_SOURCE) {
Patrick McHardyef399a32007-05-29 11:24:45 +0000174 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000175 *flags |= IPT_SNAT_OPT_RANDOM;
176 } else
177 *flags |= IPT_SNAT_OPT_RANDOM;
178 return 1;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000179
Marc Bouchere6869a82000-03-20 06:03:29 +0000180 default:
181 return 0;
182 }
183}
184
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000185static void SNAT_check(unsigned int flags)
Marc Bouchere6869a82000-03-20 06:03:29 +0000186{
Eric Leblondae4b0b32007-02-24 15:11:33 +0000187 if (!(flags & IPT_SNAT_OPT_SOURCE))
Marc Bouchere6869a82000-03-20 06:03:29 +0000188 exit_error(PARAMETER_PROBLEM,
189 "You must specify --to-source");
190}
191
192static void print_range(const struct ip_nat_range *r)
193{
194 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
195 struct in_addr a;
196
197 a.s_addr = r->min_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000198 printf("%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000199 if (r->max_ip != r->min_ip) {
200 a.s_addr = r->max_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000201 printf("-%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000202 }
203 }
204 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
205 printf(":");
206 printf("%hu", ntohs(r->min.tcp.port));
207 if (r->max.tcp.port != r->min.tcp.port)
208 printf("-%hu", ntohs(r->max.tcp.port));
209 }
210}
211
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000212static void SNAT_print(const void *ip, const struct xt_entry_target *target,
213 int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000214{
215 struct ipt_natinfo *info = (void *)target;
216 unsigned int i = 0;
217
218 printf("to:");
219 for (i = 0; i < info->mr.rangesize; i++) {
220 print_range(&info->mr.range[i]);
221 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000222 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
223 printf("random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000224 }
225}
226
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000227static void SNAT_save(const void *ip, const struct xt_entry_target *target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000228{
229 struct ipt_natinfo *info = (void *)target;
230 unsigned int i = 0;
231
232 for (i = 0; i < info->mr.rangesize; i++) {
233 printf("--to-source ");
234 print_range(&info->mr.range[i]);
235 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000236 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
237 printf("--random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000238 }
239}
240
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200241static struct xtables_target snat_tg_reg = {
Pablo Neira8caee8b2004-12-28 13:11:59 +0000242 .name = "SNAT",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200243 .version = XTABLES_VERSION,
244 .family = PF_INET,
245 .size = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
246 .userspacesize = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000247 .help = SNAT_help,
248 .parse = SNAT_parse,
249 .final_check = SNAT_check,
250 .print = SNAT_print,
251 .save = SNAT_save,
252 .extra_opts = SNAT_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000253};
254
255void _init(void)
256{
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200257 xtables_register_target(&snat_tg_reg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000258}