blob: 944fe677652e917e96f2484c4370a5d795bb3b13 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* 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 <xtables.h>
8#include <iptables.h>
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>
12
13#define IPT_SNAT_OPT_SOURCE 0x01
14#define IPT_SNAT_OPT_RANDOM 0x02
15
16/* Source NAT data consists of a multi-range, indicating where to map
17 to. */
18struct ipt_natinfo
19{
20 struct xt_entry_target t;
21 struct nf_nat_multi_range mr;
22};
23
24static void SNAT_help(void)
25{
26 printf(
27"SNAT target options:\n"
28" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
29" Address to map source to.\n"
30"[--random]\n");
31}
32
33static const struct option SNAT_opts[] = {
34 { "to-source", 1, NULL, '1' },
35 { "random", 0, NULL, '2' },
36 { .name = NULL }
37};
38
39static struct ipt_natinfo *
40append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
41{
42 unsigned int size;
43
44 /* One rangesize already in struct ipt_natinfo */
45 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
46
47 info = realloc(info, size);
48 if (!info)
49 xtables_error(OTHER_PROBLEM, "Out of memory\n");
50
51 info->t.u.target_size = size;
52 info->mr.range[info->mr.rangesize] = *range;
53 info->mr.rangesize++;
54
55 return info;
56}
57
58/* Ranges expected in network order. */
59static struct xt_entry_target *
60parse_to(char *arg, int portok, struct ipt_natinfo *info)
61{
62 struct nf_nat_range range;
63 char *colon, *dash, *error;
64 const struct in_addr *ip;
65
66 memset(&range, 0, sizeof(range));
67 colon = strchr(arg, ':');
68
69 if (colon) {
70 int port;
71
72 if (!portok)
73 xtables_error(PARAMETER_PROBLEM,
74 "Need TCP, UDP, SCTP or DCCP with port specification");
75
76 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
77
78 port = atoi(colon+1);
79 if (port <= 0 || port > 65535)
80 xtables_error(PARAMETER_PROBLEM,
81 "Port `%s' not valid\n", colon+1);
82
83 error = strchr(colon+1, ':');
84 if (error)
85 xtables_error(PARAMETER_PROBLEM,
86 "Invalid port:port syntax - use dash\n");
87
88 dash = strchr(colon, '-');
89 if (!dash) {
90 range.min.tcp.port
91 = range.max.tcp.port
92 = htons(port);
93 } else {
94 int maxport;
95
96 maxport = atoi(dash + 1);
97 if (maxport <= 0 || maxport > 65535)
98 xtables_error(PARAMETER_PROBLEM,
99 "Port `%s' not valid\n", dash+1);
100 if (maxport < port)
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);
106 }
107 /* Starts with a colon? No IP info...*/
108 if (colon == arg)
109 return &(append_range(info, &range)->t);
110 *colon = '\0';
111 }
112
113 range.flags |= IP_NAT_RANGE_MAP_IPS;
114 dash = strchr(arg, '-');
115 if (colon && dash && dash > colon)
116 dash = NULL;
117
118 if (dash)
119 *dash = '\0';
120
121 ip = xtables_numeric_to_ipaddr(arg);
122 if (!ip)
123 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
124 arg);
125 range.min_ip = ip->s_addr;
126 if (dash) {
127 ip = xtables_numeric_to_ipaddr(dash+1);
128 if (!ip)
129 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
130 dash+1);
131 range.max_ip = ip->s_addr;
132 } else
133 range.max_ip = range.min_ip;
134
135 return &(append_range(info, &range)->t);
136}
137
138static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
139 const void *e, struct xt_entry_target **target)
140{
141 const struct ipt_entry *entry = e;
142 struct ipt_natinfo *info = (void *)*target;
143 int portok;
144
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)
150 portok = 1;
151 else
152 portok = 0;
153
154 switch (c) {
155 case '1':
156 if (xtables_check_inverse(optarg, &invert, NULL, 0))
157 xtables_error(PARAMETER_PROBLEM,
158 "Unexpected `!' after --to-source");
159
160 if (*flags & IPT_SNAT_OPT_SOURCE) {
161 if (!kernel_version)
162 get_kernel_version();
163 if (kernel_version > LINUX_VERSION(2, 6, 10))
164 xtables_error(PARAMETER_PROBLEM,
165 "Multiple --to-source not supported");
166 }
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;
172 return 1;
173
174 case '2':
175 if (*flags & IPT_SNAT_OPT_SOURCE) {
176 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
177 *flags |= IPT_SNAT_OPT_RANDOM;
178 } else
179 *flags |= IPT_SNAT_OPT_RANDOM;
180 return 1;
181
182 default:
183 return 0;
184 }
185}
186
187static void SNAT_check(unsigned int flags)
188{
189 if (!(flags & IPT_SNAT_OPT_SOURCE))
190 xtables_error(PARAMETER_PROBLEM,
191 "You must specify --to-source");
192}
193
194static void print_range(const struct nf_nat_range *r)
195{
196 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
197 struct in_addr a;
198
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));
204 }
205 }
206 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
207 printf(":");
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));
211 }
212}
213
214static void SNAT_print(const void *ip, const struct xt_entry_target *target,
215 int numeric)
216{
217 struct ipt_natinfo *info = (void *)target;
218 unsigned int i = 0;
219
220 printf("to:");
221 for (i = 0; i < info->mr.rangesize; i++) {
222 print_range(&info->mr.range[i]);
223 printf(" ");
224 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
225 printf("random ");
226 }
227}
228
229static void SNAT_save(const void *ip, const struct xt_entry_target *target)
230{
231 struct ipt_natinfo *info = (void *)target;
232 unsigned int i = 0;
233
234 for (i = 0; i < info->mr.rangesize; i++) {
235 printf("--to-source ");
236 print_range(&info->mr.range[i]);
237 printf(" ");
238 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
239 printf("--random ");
240 }
241}
242
243static struct xtables_target snat_tg_reg = {
244 .name = "SNAT",
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)),
249 .help = SNAT_help,
250 .parse = SNAT_parse,
251 .final_check = SNAT_check,
252 .print = SNAT_print,
253 .save = SNAT_save,
254 .extra_opts = SNAT_opts,
255};
256
257void _init(void)
258{
259 xtables_register_target(&snat_tg_reg);
260}