1 /* Shared library add-on to iptables to add source-NAT support. */
9 #include <limits.h> /* INT_MAX in ip_tables.h */
10 #include <linux/netfilter_ipv4/ip_tables.h>
11 #include <net/netfilter/nf_nat.h>
13 #define IPT_SNAT_OPT_SOURCE 0x01
14 #define IPT_SNAT_OPT_RANDOM 0x02
16 /* Source NAT data consists of a multi-range, indicating where to map
20 struct xt_entry_target t;
21 struct nf_nat_multi_range mr;
24 static void SNAT_help(void)
27 "SNAT target options:\n"
28 " --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
29 " Address to map source to.\n"
33 static const struct option SNAT_opts[] = {
34 { "to-source", 1, NULL, '1' },
35 { "random", 0, NULL, '2' },
39 static struct ipt_natinfo *
40 append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
44 /* One rangesize already in struct ipt_natinfo */
45 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
47 info = realloc(info, size);
49 xtables_error(OTHER_PROBLEM, "Out of memory\n");
51 info->t.u.target_size = size;
52 info->mr.range[info->mr.rangesize] = *range;
58 /* Ranges expected in network order. */
59 static struct xt_entry_target *
60 parse_to(char *arg, int portok, struct ipt_natinfo *info)
62 struct nf_nat_range range;
63 char *colon, *dash, *error;
64 const struct in_addr *ip;
66 memset(&range, 0, sizeof(range));
67 colon = strchr(arg, ':');
73 xtables_error(PARAMETER_PROBLEM,
74 "Need TCP, UDP, SCTP or DCCP with port specification");
76 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
79 if (port <= 0 || port > 65535)
80 xtables_error(PARAMETER_PROBLEM,
81 "Port `%s' not valid\n", colon+1);
83 error = strchr(colon+1, ':');
85 xtables_error(PARAMETER_PROBLEM,
86 "Invalid port:port syntax - use dash\n");
88 dash = strchr(colon, '-');
96 maxport = atoi(dash + 1);
97 if (maxport <= 0 || maxport > 65535)
98 xtables_error(PARAMETER_PROBLEM,
99 "Port `%s' not valid\n", dash+1);
101 /* People are stupid. */
102 xtables_error(PARAMETER_PROBLEM,
103 "Port range `%s' funky\n", colon+1);
104 range.min.tcp.port = htons(port);
105 range.max.tcp.port = htons(maxport);
107 /* Starts with a colon? No IP info...*/
109 return &(append_range(info, &range)->t);
113 range.flags |= IP_NAT_RANGE_MAP_IPS;
114 dash = strchr(arg, '-');
115 if (colon && dash && dash > colon)
121 ip = xtables_numeric_to_ipaddr(arg);
123 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
125 range.min_ip = ip->s_addr;
127 ip = xtables_numeric_to_ipaddr(dash+1);
129 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
131 range.max_ip = ip->s_addr;
133 range.max_ip = range.min_ip;
135 return &(append_range(info, &range)->t);
138 static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
139 const void *e, struct xt_entry_target **target)
141 const struct ipt_entry *entry = e;
142 struct ipt_natinfo *info = (void *)*target;
145 if (entry->ip.proto == IPPROTO_TCP
146 || entry->ip.proto == IPPROTO_UDP
147 || entry->ip.proto == IPPROTO_SCTP
148 || entry->ip.proto == IPPROTO_DCCP
149 || entry->ip.proto == IPPROTO_ICMP)
156 if (xtables_check_inverse(optarg, &invert, NULL, 0))
157 xtables_error(PARAMETER_PROBLEM,
158 "Unexpected `!' after --to-source");
160 if (*flags & IPT_SNAT_OPT_SOURCE) {
162 get_kernel_version();
163 if (kernel_version > LINUX_VERSION(2, 6, 10))
164 xtables_error(PARAMETER_PROBLEM,
165 "Multiple --to-source not supported");
167 *target = parse_to(optarg, portok, info);
168 /* WTF do we need this for?? */
169 if (*flags & IPT_SNAT_OPT_RANDOM)
170 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
171 *flags |= IPT_SNAT_OPT_SOURCE;
175 if (*flags & IPT_SNAT_OPT_SOURCE) {
176 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
177 *flags |= IPT_SNAT_OPT_RANDOM;
179 *flags |= IPT_SNAT_OPT_RANDOM;
187 static void SNAT_check(unsigned int flags)
189 if (!(flags & IPT_SNAT_OPT_SOURCE))
190 xtables_error(PARAMETER_PROBLEM,
191 "You must specify --to-source");
194 static void print_range(const struct nf_nat_range *r)
196 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
199 a.s_addr = r->min_ip;
200 printf("%s", xtables_ipaddr_to_numeric(&a));
201 if (r->max_ip != r->min_ip) {
202 a.s_addr = r->max_ip;
203 printf("-%s", xtables_ipaddr_to_numeric(&a));
206 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
208 printf("%hu", ntohs(r->min.tcp.port));
209 if (r->max.tcp.port != r->min.tcp.port)
210 printf("-%hu", ntohs(r->max.tcp.port));
214 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
217 struct ipt_natinfo *info = (void *)target;
221 for (i = 0; i < info->mr.rangesize; i++) {
222 print_range(&info->mr.range[i]);
224 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
229 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
231 struct ipt_natinfo *info = (void *)target;
234 for (i = 0; i < info->mr.rangesize; i++) {
235 printf("--to-source ");
236 print_range(&info->mr.range[i]);
238 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
243 static struct xtables_target snat_tg_reg = {
245 .version = XTABLES_VERSION,
246 .family = NFPROTO_IPV4,
247 .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
248 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)),
251 .final_check = SNAT_check,
254 .extra_opts = SNAT_opts,
259 xtables_register_target(&snat_tg_reg);