blob: 9609ad9b5c94af0bf926caf322e42d2f6b5c03e0 [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>
Jan Engelhardt5d9678a2008-11-20 10:15:35 +01007#include <xtables.h>
Marc Bouchere6869a82000-03-20 06:03:29 +00008#include <iptables.h>
Jan Engelhardt4e418542009-02-21 03:46:37 +01009#include <limits.h> /* INT_MAX in ip_tables.h */
Marc Bouchere6869a82000-03-20 06:03:29 +000010#include <linux/netfilter_ipv4/ip_tables.h>
Jan Engelhardt978e27e2009-02-21 04:42:32 +010011#include <net/netfilter/nf_nat.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000012
Eric Leblondae4b0b32007-02-24 15:11:33 +000013#define IPT_SNAT_OPT_SOURCE 0x01
Patrick McHardye656e262007-04-18 12:56:05 +000014#define IPT_SNAT_OPT_RANDOM 0x02
Eric Leblondae4b0b32007-02-24 15:11:33 +000015
Marc Bouchere6869a82000-03-20 06:03:29 +000016/* Source NAT data consists of a multi-range, indicating where to map
17 to. */
18struct ipt_natinfo
19{
Yasuyuki KOZAKAI193df8e2007-07-24 05:57:28 +000020 struct xt_entry_target t;
Jan Engelhardt978e27e2009-02-21 04:42:32 +010021 struct nf_nat_multi_range mr;
Marc Bouchere6869a82000-03-20 06:03:29 +000022};
23
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000024static void SNAT_help(void)
Marc Bouchere6869a82000-03-20 06:03:29 +000025{
26 printf(
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020027"SNAT target options:\n"
Patrick McHardyef399a32007-05-29 11:24:45 +000028" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000029" Address to map source to.\n"
Patrick McHardy467fa9f2009-04-17 18:11:09 +020030"[--random] [--persistent]\n");
Marc Bouchere6869a82000-03-20 06:03:29 +000031}
32
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +000033static const struct option SNAT_opts[] = {
Patrick McHardy500f4832007-09-08 15:59:04 +000034 { "to-source", 1, NULL, '1' },
35 { "random", 0, NULL, '2' },
Patrick McHardy467fa9f2009-04-17 18:11:09 +020036 { "persistent", 0, NULL, '3' },
Max Kellermann9ee386a2008-01-29 13:48:05 +000037 { .name = NULL }
Marc Bouchere6869a82000-03-20 06:03:29 +000038};
39
Marc Bouchere6869a82000-03-20 06:03:29 +000040static struct ipt_natinfo *
Jan Engelhardt978e27e2009-02-21 04:42:32 +010041append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
Marc Bouchere6869a82000-03-20 06:03:29 +000042{
43 unsigned int size;
44
45 /* One rangesize already in struct ipt_natinfo */
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020046 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
Marc Bouchere6869a82000-03-20 06:03:29 +000047
48 info = realloc(info, size);
49 if (!info)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010050 xtables_error(OTHER_PROBLEM, "Out of memory\n");
Marc Bouchere6869a82000-03-20 06:03:29 +000051
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{
Jan Engelhardt978e27e2009-02-21 04:42:32 +010063 struct nf_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)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010074 xtables_error(PARAMETER_PROBLEM,
Patrick McHardy5a942f92008-11-04 13:22:40 +010075 "Need TCP, UDP, SCTP or DCCP with port specification");
Marc Bouchere6869a82000-03-20 06:03:29 +000076
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)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010081 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +000082 "Port `%s' not valid\n", colon+1);
83
Harald Weltede5ba5d2005-02-01 15:14:15 +000084 error = strchr(colon+1, ':');
85 if (error)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010086 xtables_error(PARAMETER_PROBLEM,
Harald Weltede5ba5d2005-02-01 15:14:15 +000087 "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)
Jan Engelhardt1829ed42009-02-21 03:29:44 +010099 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +0000100 "Port `%s' not valid\n", dash+1);
101 if (maxport < port)
102 /* People are stupid. */
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100103 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +0000104 "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 Engelhardt1e01b0b2009-01-30 04:20:32 +0100122 ip = xtables_numeric_to_ipaddr(arg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000123 if (!ip)
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100124 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
Marc Bouchere6869a82000-03-20 06:03:29 +0000125 arg);
126 range.min_ip = ip->s_addr;
127 if (dash) {
Jan Engelhardt1e01b0b2009-01-30 04:20:32 +0100128 ip = xtables_numeric_to_ipaddr(dash+1);
Marc Bouchere6869a82000-03-20 06:03:29 +0000129 if (!ip)
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100130 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
Marc Bouchere6869a82000-03-20 06:03:29 +0000131 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
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000139static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
140 const void *e, struct xt_entry_target **target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000141{
Yasuyuki KOZAKAIac8b2712007-07-24 06:06:59 +0000142 const struct ipt_entry *entry = e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000143 struct ipt_natinfo *info = (void *)*target;
144 int portok;
145
146 if (entry->ip.proto == IPPROTO_TCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000147 || entry->ip.proto == IPPROTO_UDP
Patrick McHardy5a942f92008-11-04 13:22:40 +0100148 || entry->ip.proto == IPPROTO_SCTP
149 || entry->ip.proto == IPPROTO_DCCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000150 || 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':
Jan Engelhardt0f16c722009-01-30 04:55:38 +0100157 if (xtables_check_inverse(optarg, &invert, NULL, 0))
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100158 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +0000159 "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))
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100165 xtables_error(PARAMETER_PROBLEM,
Phil Oester8cf65912005-09-19 15:00:33 +0000166 "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
Patrick McHardy467fa9f2009-04-17 18:11:09 +0200183 case '3':
184 info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
185 return 1;
186
Marc Bouchere6869a82000-03-20 06:03:29 +0000187 default:
188 return 0;
189 }
190}
191
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000192static void SNAT_check(unsigned int flags)
Marc Bouchere6869a82000-03-20 06:03:29 +0000193{
Eric Leblondae4b0b32007-02-24 15:11:33 +0000194 if (!(flags & IPT_SNAT_OPT_SOURCE))
Jan Engelhardt1829ed42009-02-21 03:29:44 +0100195 xtables_error(PARAMETER_PROBLEM,
Marc Bouchere6869a82000-03-20 06:03:29 +0000196 "You must specify --to-source");
197}
198
Jan Engelhardt978e27e2009-02-21 04:42:32 +0100199static void print_range(const struct nf_nat_range *r)
Marc Bouchere6869a82000-03-20 06:03:29 +0000200{
201 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
202 struct in_addr a;
203
204 a.s_addr = r->min_ip;
Jan Engelhardte44ea7f2009-01-30 03:55:09 +0100205 printf("%s", xtables_ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000206 if (r->max_ip != r->min_ip) {
207 a.s_addr = r->max_ip;
Jan Engelhardte44ea7f2009-01-30 03:55:09 +0100208 printf("-%s", xtables_ipaddr_to_numeric(&a));
Marc Bouchere6869a82000-03-20 06:03:29 +0000209 }
210 }
211 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
212 printf(":");
213 printf("%hu", ntohs(r->min.tcp.port));
214 if (r->max.tcp.port != r->min.tcp.port)
215 printf("-%hu", ntohs(r->max.tcp.port));
216 }
217}
218
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000219static void SNAT_print(const void *ip, const struct xt_entry_target *target,
220 int numeric)
Marc Bouchere6869a82000-03-20 06:03:29 +0000221{
222 struct ipt_natinfo *info = (void *)target;
223 unsigned int i = 0;
224
225 printf("to:");
226 for (i = 0; i < info->mr.rangesize; i++) {
227 print_range(&info->mr.range[i]);
228 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000229 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
230 printf("random ");
Patrick McHardy467fa9f2009-04-17 18:11:09 +0200231 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
232 printf("persistent ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000233 }
234}
235
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000236static void SNAT_save(const void *ip, const struct xt_entry_target *target)
Marc Bouchere6869a82000-03-20 06:03:29 +0000237{
238 struct ipt_natinfo *info = (void *)target;
239 unsigned int i = 0;
240
241 for (i = 0; i < info->mr.rangesize; i++) {
242 printf("--to-source ");
243 print_range(&info->mr.range[i]);
244 printf(" ");
Patrick McHardy9c67def2007-04-18 14:00:11 +0000245 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
246 printf("--random ");
Patrick McHardy467fa9f2009-04-17 18:11:09 +0200247 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
248 printf("--persistent ");
Marc Bouchere6869a82000-03-20 06:03:29 +0000249 }
250}
251
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200252static struct xtables_target snat_tg_reg = {
Pablo Neira8caee8b2004-12-28 13:11:59 +0000253 .name = "SNAT",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200254 .version = XTABLES_VERSION,
Jan Engelhardt03d99482008-11-18 12:27:54 +0100255 .family = NFPROTO_IPV4,
Jan Engelhardt978e27e2009-02-21 04:42:32 +0100256 .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
257 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
Jan Engelhardt1d5b63d2007-10-04 16:29:00 +0000258 .help = SNAT_help,
259 .parse = SNAT_parse,
260 .final_check = SNAT_check,
261 .print = SNAT_print,
262 .save = SNAT_save,
263 .extra_opts = SNAT_opts,
Marc Bouchere6869a82000-03-20 06:03:29 +0000264};
265
266void _init(void)
267{
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +0200268 xtables_register_target(&snat_tg_reg);
Marc Bouchere6869a82000-03-20 06:03:29 +0000269}