blob: b2a4925facb3951c48299fd6bcff8aea06316cdd [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,
72 "Need TCP or UDP with port specification");
73
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
145 || entry->ip.proto == IPPROTO_ICMP)
Marc Bouchere6869a82000-03-20 06:03:29 +0000146 portok = 1;
147 else
148 portok = 0;
149
150 switch (c) {
151 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000152 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000153 exit_error(PARAMETER_PROBLEM,
154 "Unexpected `!' after --to-source");
155
Eric Leblondae4b0b32007-02-24 15:11:33 +0000156 if (*flags & IPT_SNAT_OPT_SOURCE) {
Phil Oester8cf65912005-09-19 15:00:33 +0000157 if (!kernel_version)
158 get_kernel_version();
159 if (kernel_version > LINUX_VERSION(2, 6, 10))
160 exit_error(PARAMETER_PROBLEM,
161 "Multiple --to-source not supported");
162 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000163 *target = parse_to(optarg, portok, info);
Patrick McHardye656e262007-04-18 12:56:05 +0000164 /* WTF do we need this for?? */
Eric Leblondae4b0b32007-02-24 15:11:33 +0000165 if (*flags & IPT_SNAT_OPT_RANDOM)
Patrick McHardyef399a32007-05-29 11:24:45 +0000166 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
167 *flags |= IPT_SNAT_OPT_SOURCE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000168 return 1;
169
Eric Leblondae4b0b32007-02-24 15:11:33 +0000170 case '2':
171 if (*flags & IPT_SNAT_OPT_SOURCE) {
Patrick McHardyef399a32007-05-29 11:24:45 +0000172 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000173 *flags |= IPT_SNAT_OPT_RANDOM;
174 } else
175 *flags |= IPT_SNAT_OPT_RANDOM;
176 return 1;
Eric Leblondae4b0b32007-02-24 15:11:33 +0000177
Marc Bouchere6869a82000-03-20 06:03:29 +0000178 default:
179 return 0;
180 }
181}
182
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000183static void SNAT_check(unsigned int flags)
Marc Bouchere6869a82000-03-20 06:03:29 +0000184{
Eric Leblondae4b0b32007-02-24 15:11:33 +0000185 if (!(flags & IPT_SNAT_OPT_SOURCE))
Marc Bouchere6869a82000-03-20 06:03:29 +0000186 exit_error(PARAMETER_PROBLEM,
187 "You must specify --to-source");
188}
189
190static void print_range(const struct ip_nat_range *r)
191{
192 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
193 struct in_addr a;
194
195 a.s_addr = r->min_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000196 printf("%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000197 if (r->max_ip != r->min_ip) {
198 a.s_addr = r->max_ip;
Jan Engelhardt08b16162008-01-20 13:36:08 +0000199 printf("-%s", ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000200 }
201 }
202 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
203 printf(":");
204 printf("%hu", ntohs(r->min.tcp.port));
205 if (r->max.tcp.port != r->min.tcp.port)
206 printf("-%hu", ntohs(r->max.tcp.port));
207 }
208}
209
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000210static void SNAT_print(const void *ip, const struct xt_entry_target *target,
211 int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000212{
213 struct ipt_natinfo *info = (void *)target;
214 unsigned int i = 0;
215
216 printf("to:");
217 for (i = 0; i < info->mr.rangesize; i++) {
218 print_range(&info->mr.range[i]);
219 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000220 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
221 printf("random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000222 }
223}
224
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000225static void SNAT_save(const void *ip, const struct xt_entry_target *target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000226{
227 struct ipt_natinfo *info = (void *)target;
228 unsigned int i = 0;
229
230 for (i = 0; i < info->mr.rangesize; i++) {
231 printf("--to-source ");
232 print_range(&info->mr.range[i]);
233 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000234 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
235 printf("--random ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000236 }
237}
238
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200239static struct xtables_target snat_tg_reg = {
Pablo Neira8caee8b2004-12-28 13:11:59 +0000240 .name = "SNAT",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200241 .version = XTABLES_VERSION,
242 .family = PF_INET,
243 .size = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
244 .userspacesize = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000245 .help = SNAT_help,
246 .parse = SNAT_parse,
247 .final_check = SNAT_check,
248 .print = SNAT_print,
249 .save = SNAT_save,
250 .extra_opts = SNAT_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000251};
252
253void _init(void)
254{
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200255 xtables_register_target(&snat_tg_reg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000256}