1 /*
2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 *
4 * Based on Rusty Russell's IPv4 SNAT target. Development of IPv6 NAT
5 * funded by Astaro.
6 */
7
8 #include <stdio.h>
9 #include <netdb.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <xtables.h>
13 #include <iptables.h>
14 #include <limits.h> /* INT_MAX in ip_tables.h */
15 #include <linux/netfilter_ipv6/ip6_tables.h>
16 #include <linux/netfilter/nf_nat.h>
17
18 enum {
19 O_TO_SRC = 0,
20 O_RANDOM,
21 O_PERSISTENT,
22 O_X_TO_SRC,
23 F_TO_SRC = 1 << O_TO_SRC,
24 F_RANDOM = 1 << O_RANDOM,
25 F_X_TO_SRC = 1 << O_X_TO_SRC,
26 };
27
SNAT_help(void)28 static void SNAT_help(void)
29 {
30 printf(
31 "SNAT target options:\n"
32 " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
33 " Address to map source to.\n"
34 "[--random] [--persistent]\n");
35 }
36
37 static const struct xt_option_entry SNAT_opts[] = {
38 {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
39 .flags = XTOPT_MAND | XTOPT_MULTI},
40 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
41 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
42 XTOPT_TABLEEND,
43 };
44
45 /* Ranges expected in network order. */
46 static void
parse_to(const char * orig_arg,int portok,struct nf_nat_range * range)47 parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
48 {
49 char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
50 const struct in6_addr *ip;
51
52 arg = strdup(orig_arg);
53 if (arg == NULL)
54 xtables_error(RESOURCE_PROBLEM, "strdup");
55
56 start = strchr(arg, '[');
57 if (start == NULL) {
58 start = arg;
59 /* Lets assume one colon is port information. Otherwise its an IPv6 address */
60 colon = strchr(arg, ':');
61 if (colon && strchr(colon+1, ':'))
62 colon = NULL;
63 }
64 else {
65 start++;
66 end = strchr(start, ']');
67 if (end == NULL)
68 xtables_error(PARAMETER_PROBLEM,
69 "Invalid address format");
70
71 *end = '\0';
72 colon = strchr(end + 1, ':');
73 }
74
75 if (colon) {
76 int port;
77
78 if (!portok)
79 xtables_error(PARAMETER_PROBLEM,
80 "Need TCP, UDP, SCTP or DCCP with port specification");
81
82 range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
83
84 port = atoi(colon+1);
85 if (port <= 0 || port > 65535)
86 xtables_error(PARAMETER_PROBLEM,
87 "Port `%s' not valid\n", colon+1);
88
89 error = strchr(colon+1, ':');
90 if (error)
91 xtables_error(PARAMETER_PROBLEM,
92 "Invalid port:port syntax - use dash\n");
93
94 dash = strchr(colon, '-');
95 if (!dash) {
96 range->min_proto.tcp.port
97 = range->max_proto.tcp.port
98 = htons(port);
99 } else {
100 int maxport;
101
102 maxport = atoi(dash + 1);
103 if (maxport <= 0 || maxport > 65535)
104 xtables_error(PARAMETER_PROBLEM,
105 "Port `%s' not valid\n", dash+1);
106 if (maxport < port)
107 /* People are stupid. */
108 xtables_error(PARAMETER_PROBLEM,
109 "Port range `%s' funky\n", colon+1);
110 range->min_proto.tcp.port = htons(port);
111 range->max_proto.tcp.port = htons(maxport);
112 }
113 /* Starts with colon or [] colon? No IP info...*/
114 if (colon == arg || colon == arg+2) {
115 free(arg);
116 return;
117 }
118 *colon = '\0';
119 }
120
121 range->flags |= NF_NAT_RANGE_MAP_IPS;
122 dash = strchr(start, '-');
123 if (colon && dash && dash > colon)
124 dash = NULL;
125
126 if (dash)
127 *dash = '\0';
128
129 ip = xtables_numeric_to_ip6addr(start);
130 if (!ip)
131 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
132 start);
133 range->min_addr.in6 = *ip;
134 if (dash) {
135 ip = xtables_numeric_to_ip6addr(dash + 1);
136 if (!ip)
137 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
138 dash+1);
139 range->max_addr.in6 = *ip;
140 } else
141 range->max_addr = range->min_addr;
142
143 free(arg);
144 return;
145 }
146
SNAT_parse(struct xt_option_call * cb)147 static void SNAT_parse(struct xt_option_call *cb)
148 {
149 const struct ip6t_entry *entry = cb->xt_entry;
150 struct nf_nat_range *range = cb->data;
151 int portok;
152
153 if (entry->ipv6.proto == IPPROTO_TCP ||
154 entry->ipv6.proto == IPPROTO_UDP ||
155 entry->ipv6.proto == IPPROTO_SCTP ||
156 entry->ipv6.proto == IPPROTO_DCCP ||
157 entry->ipv6.proto == IPPROTO_ICMP)
158 portok = 1;
159 else
160 portok = 0;
161
162 xtables_option_parse(cb);
163 switch (cb->entry->id) {
164 case O_TO_SRC:
165 if (cb->xflags & F_X_TO_SRC) {
166 if (!kernel_version)
167 get_kernel_version();
168 if (kernel_version > LINUX_VERSION(2, 6, 10))
169 xtables_error(PARAMETER_PROBLEM,
170 "SNAT: Multiple --to-source not supported");
171 }
172 parse_to(cb->arg, portok, range);
173 break;
174 case O_PERSISTENT:
175 range->flags |= NF_NAT_RANGE_PERSISTENT;
176 break;
177 }
178 }
179
SNAT_fcheck(struct xt_fcheck_call * cb)180 static void SNAT_fcheck(struct xt_fcheck_call *cb)
181 {
182 static const unsigned int f = F_TO_SRC | F_RANDOM;
183 struct nf_nat_range *range = cb->data;
184
185 if ((cb->xflags & f) == f)
186 range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
187 }
188
print_range(const struct nf_nat_range * range)189 static void print_range(const struct nf_nat_range *range)
190 {
191 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
192 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
193 printf("[");
194 printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
195 if (memcmp(&range->min_addr, &range->max_addr,
196 sizeof(range->min_addr)))
197 printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
198 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
199 printf("]");
200 }
201 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
202 printf(":");
203 printf("%hu", ntohs(range->min_proto.tcp.port));
204 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
205 printf("-%hu", ntohs(range->max_proto.tcp.port));
206 }
207 }
208
SNAT_print(const void * ip,const struct xt_entry_target * target,int numeric)209 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
210 int numeric)
211 {
212 const struct nf_nat_range *range = (const void *)target->data;
213
214 printf(" to:");
215 print_range(range);
216 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
217 printf(" random");
218 if (range->flags & NF_NAT_RANGE_PERSISTENT)
219 printf(" persistent");
220 }
221
SNAT_save(const void * ip,const struct xt_entry_target * target)222 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
223 {
224 const struct nf_nat_range *range = (const void *)target->data;
225
226 printf(" --to-source ");
227 print_range(range);
228 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
229 printf(" --random");
230 if (range->flags & NF_NAT_RANGE_PERSISTENT)
231 printf(" --persistent");
232 }
233
234 static struct xtables_target snat_tg_reg = {
235 .name = "SNAT",
236 .version = XTABLES_VERSION,
237 .family = NFPROTO_IPV6,
238 .revision = 1,
239 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
240 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
241 .help = SNAT_help,
242 .x6_parse = SNAT_parse,
243 .x6_fcheck = SNAT_fcheck,
244 .print = SNAT_print,
245 .save = SNAT_save,
246 .x6_options = SNAT_opts,
247 };
248
_init(void)249 void _init(void)
250 {
251 xtables_register_target(&snat_tg_reg);
252 }
253