blob: aae5caba4dd0050731425a028c6313eeaf4588f3 [file] [log] [blame]
gborowiakc7c191a2003-09-07 13:10:40 +00001#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <netinet/ether.h>
5#include <getopt.h>
6#include <ctype.h>
7#include "../include/ebtables_u.h"
8#include <linux/netfilter_bridge/ebt_inat.h>
9
10static int s_sub_supplied, d_sub_supplied;
11
12#define NAT_S '1'
13#define NAT_D '1'
14#define NAT_S_SUB '2'
15#define NAT_D_SUB '2'
16#define NAT_S_TARGET '3'
17#define NAT_D_TARGET '3'
18static struct option opts_s[] =
19{
20 { "isnat-list" , required_argument, 0, NAT_S },
21 { "isnat-sub" , required_argument, 0, NAT_S_SUB },
22 { "isnat-default-target" , required_argument, 0, NAT_S_TARGET },
23 { 0 }
24};
25
26static struct option opts_d[] =
27{
28 { "idnat-list" , required_argument, 0, NAT_D },
29 { "idnat-sub" , required_argument, 0, NAT_D_SUB },
30 { "idnat-default-target" , required_argument, 0, NAT_D_TARGET },
31 { 0 }
32};
33
34static void print_help_common(const char *cas)
35{
36 printf(
37"isnat options:\n"
38" --i%1.1snat-list : indexed list of MAC addresses\n"
39" --i%1.1snat-sub : /24 subnet to which the rule apply\n"
40" --i%1.1snat-default-target target : ACCEPT, DROP, RETURN or CONTINUE\n"
41"Indexed list of addresses is as follows:\n"
42"\tlist := chunk\n"
43"\tlist := list chunk\n"
44"\tchunk := pair ','\n"
45"\tpair := index '=' action\n"
46"\taction := mac_addr\n"
47"\taction := mac_addr '+'\n"
gborowiak66bbdc02003-09-07 21:02:28 +000048"\taction := '_'\n"
gborowiakc7c191a2003-09-07 13:10:40 +000049"where\n"
50"\tindex -- an integer [0..255]\n"
51"\tmac_addr -- a MAC address in format xx:xx:xx:xx:xx:xx\n"
gborowiak66bbdc02003-09-07 21:02:28 +000052"If '_' at some index is specified, packets with last %s IP address byte\n"
gborowiakc7c191a2003-09-07 13:10:40 +000053"equal to index are DROPped. If there is a MAC address, they are %1.1snatted\n"
54"to this and the target is CONTINUE. If this MAC is followed by '+', the\n"
55"target is ACCEPT.\n"
56"For example,\n"
gborowiak66bbdc02003-09-07 21:02:28 +000057"--idnat-list 2=20:21:22:23:24:25,4=_,7=30:31:32:33:34:35,\n"
gborowiakc7c191a2003-09-07 13:10:40 +000058"is valid.\n"
59"The subnet MUST be specified. Only packets with 3 first bytes of their\n"
60"%s address are considered. --i%1.1snat-sub parameter must begin with\n"
61"3 integers separated by dots. Only they are considered, the rest is ignored.\n"
62"No matter if you write '192.168.42.', '192.168.42.0', '192.168.42.12',\n"
63"'192.168.42.0/24', '192.168.42.0/23' or '192.168.42.i%1.1snat_sucks!!!',\n"
64"The numbers 192, 168 and 42 are taken and it behaves like a /24 IP subnet.\n"
65"--i%1.1snat-default-target affects only the packet not matching the subnet.\n",
66 cas, cas, cas, cas, cas, cas, cas, cas,
67 cas, cas, cas, cas, cas, cas, cas, cas
68 );
69}
70
71static void print_help_s()
72{
73 print_help_common("src");
74}
75
76static void print_help_d()
77{
78 print_help_common("dest");
79}
80
81static void init_s(struct ebt_entry_target *target)
82{
83 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
84
85 s_sub_supplied = 0;
86 memset(natinfo, 0, sizeof(struct ebt_inat_info));
87 natinfo->target = EBT_CONTINUE;
88 return;
89}
90
91static void init_d(struct ebt_entry_target *target)
92{
93 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
94
95 d_sub_supplied = 0;
96 memset(natinfo, 0, sizeof(struct ebt_inat_info));
97 natinfo->target = EBT_CONTINUE;
98 return;
99}
100
101static void parse_list(const char *arg, struct ebt_inat_info *info)
102{
103 int i;
104 char c;
105 int count = 0;
106 int now_index = 1;
107 int index;
108 char buf[4];
109 unsigned char mac[6];
110 int ibuf = 0;
111 int imac = 0;
112 int target;
113 memset(buf, 0, 4);
114 i = 0;
115 while (1) {
116 c = arg[i];
117 if (now_index) {
118 if (isdigit(c)) {
119 buf[ibuf++] = c;
120 if (ibuf > 3) {
121 print_error("Index too long at position %d", i);
122 }
123 goto next;
124 }
125 if (c == '=') {
126 if (ibuf == 0) {
127 print_error("Integer index expected before '=' at position %d", i);
128 }
129 buf[ibuf] = 0;
130 ibuf = 0;
131 index = atoi(buf);
132 if (index < 0 || 255 < index) {
133 print_error("Index out of range [0..255], namely %d", index);
134 }
135 now_index = 0;
136 memset(mac, 0, 6);
137 imac = 0;
138 target = EBT_CONTINUE;
139 goto next;
140 }
141 if (c == '\0') {
142 goto next;
143 }
144 print_error("Unexpected '%c' where integer or '=' expected", c);
145 }
146 else {
147 if (isxdigit(c)) {
148 buf[ibuf++] = c;
149 if (ibuf > 2) {
150 print_error("MAC address chunk too long at position %d", i);
151 }
152 goto next;
153 }
154 if (c == ':' || c == ',' || c == '\0') {
155 buf[ibuf] = 0;
156 ibuf = 0;
157 mac[imac++] = strtol(buf, 0, 16);
158 if (c == ',' || c == '\0') {
159 info->a[index].enabled = 1;
160 info->a[index].target = target;
161 memcpy(info->a[index].mac, mac, 6);
162 now_index = 1;
163 count++;
164 goto next;
165 }
166 if (c == ':' && imac >= 6) {
167 print_error("Too many MAC address chunks at position %d", i);
168 }
169 goto next;
170 }
gborowiak66bbdc02003-09-07 21:02:28 +0000171 if (c == '_') {
gborowiakc7c191a2003-09-07 13:10:40 +0000172 target = EBT_DROP;
173 goto next;
174 }
175 if (c == '+') {
176 target = EBT_ACCEPT;
177 goto next;
178 }
gborowiak66bbdc02003-09-07 21:02:28 +0000179 print_error("Unexpected '%c' where hex digit, '_', '+', ',' or end of string expected", c);
gborowiakc7c191a2003-09-07 13:10:40 +0000180 }
181 next:
182 if (!c) break;
183 i++;
184 }
185 if (count == 0) {
186 print_error("List empty");
187 }
188}
189
190static uint32_t parse_ip(const char *s)
191{
192 int a0, a1, a2;
193 char ip[4];
194 sscanf(s, "%d.%d.%d", &a0, &a1, &a2);
195 ip[0] = a0;
196 ip[1] = a1;
197 ip[2] = a2;
198 ip[3] = 0;
199 return *(uint32_t*)ip;
200}
201
202#define OPT_ISNAT 0x01
203#define OPT_ISNAT_SUB 0x02
204#define OPT_ISNAT_TARGET 0x04
205static int parse_s(int c, char **argv, int argc,
206 const struct ebt_u_entry *entry, unsigned int *flags,
207 struct ebt_entry_target **target)
208{
209 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
210
211 switch (c) {
212 case NAT_S:
213 check_option(flags, OPT_ISNAT);
214 parse_list(optarg, natinfo);
215 break;
216 case NAT_S_TARGET:
217 check_option(flags, OPT_ISNAT_TARGET);
218 if (FILL_TARGET(optarg, natinfo->target))
219 print_error("Illegal --isnat-default-target target");
220 break;
221 case NAT_S_SUB:
222 natinfo->ip_subnet = parse_ip(optarg);
223 s_sub_supplied = 1;
224 break;
225 default:
226 return 0;
227 }
228 return 1;
229}
230
231#define OPT_IDNAT 0x01
232#define OPT_IDNAT_SUB 0x02
233#define OPT_IDNAT_TARGET 0x04
234static int parse_d(int c, char **argv, int argc,
235 const struct ebt_u_entry *entry, unsigned int *flags,
236 struct ebt_entry_target **target)
237{
238 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
239
240 switch (c) {
241 case NAT_D:
242 check_option(flags, OPT_IDNAT);
243 parse_list(optarg, natinfo);
244 break;
245 case NAT_D_TARGET:
246 check_option(flags, OPT_IDNAT_TARGET);
247 if (FILL_TARGET(optarg, natinfo->target))
248 print_error("Illegal --idnat-default-target target");
249 break;
250 case NAT_D_SUB:
251 natinfo->ip_subnet = parse_ip(optarg);
252 d_sub_supplied = 1;
253 break;
254 default:
255 return 0;
256 }
257 return 1;
258}
259
260static void final_check_s(const struct ebt_u_entry *entry,
261 const struct ebt_entry_target *target, const char *name,
262 unsigned int hookmask, unsigned int time)
263{
264 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
265
266 if (BASE_CHAIN && natinfo->target == EBT_RETURN)
267 print_error("--isnat-default-target RETURN not allowed on base chain");
268 CLEAR_BASE_CHAIN_BIT;
269 if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
270 print_error("Wrong chain for isnat");
271 if (time == 0 && s_sub_supplied == 0)
272 print_error("No isnat subnet supplied");
273}
274
275static void final_check_d(const struct ebt_u_entry *entry,
276 const struct ebt_entry_target *target, const char *name,
277 unsigned int hookmask, unsigned int time)
278{
279 struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
280
281 if (BASE_CHAIN && natinfo->target == EBT_RETURN)
282 print_error("--idnat-default-target RETURN not allowed on base chain");
283 CLEAR_BASE_CHAIN_BIT;
284 if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
285 || strcmp(name, "nat")) &&
286 ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
287 print_error("Wrong chain for idnat");
288 if (time == 0 && d_sub_supplied == 0)
289 print_error("No idnat subnet supplied");
290}
291
292static void print_list(const struct ebt_inat_info *info)
293{
294 int i;
295 for (i = 0; i < 256; i++) {
296 if (info->a[i].enabled) {
297 printf("%d=", i);
298 if (info->a[i].target == EBT_DROP) {
gborowiak66bbdc02003-09-07 21:02:28 +0000299 printf("_");
gborowiakc7c191a2003-09-07 13:10:40 +0000300 }
301 else {
302 if (info->a[i].target == EBT_ACCEPT) {
303 printf("+");
304 }
gborowiak66bbdc02003-09-07 21:02:28 +0000305 print_mac(info->a[i].mac);
gborowiakc7c191a2003-09-07 13:10:40 +0000306 }
307 printf(",");
308 }
309 }
310}
311
312static void print_s(const struct ebt_u_entry *entry,
313 const struct ebt_entry_target *target)
314{
315 struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
316
317 unsigned char sub[4];
318 *(uint32_t*)sub = info->ip_subnet;
319 printf("--isnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
320 printf(" --isnat-list ");
321 print_list(info);
322 printf(" --isnat-default-target %s", TARGET_NAME(info->target));
323}
324
325static void print_d(const struct ebt_u_entry *entry,
326 const struct ebt_entry_target *target)
327{
328 struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
329
330 unsigned char sub[4];
331 *(uint32_t*)sub = info->ip_subnet;
332 printf("--idnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
333 printf(" --idnat-list ");
334 print_list(info);
335 printf(" --idnat-default-target %s", TARGET_NAME(info->target));
336}
337
338static int compare(const struct ebt_entry_target *t1,
339 const struct ebt_entry_target *t2)
340{
341 struct ebt_inat_info *natinfo1 = (struct ebt_inat_info *)t1->data;
342 struct ebt_inat_info *natinfo2 = (struct ebt_inat_info *)t2->data;
343
344
345 return !memcmp(natinfo1, natinfo2, sizeof(struct ebt_inat_info));
346}
347
348static struct ebt_u_target isnat_target =
349{
gborowiak66bbdc02003-09-07 21:02:28 +0000350 .name = EBT_ISNAT_TARGET,
351 .size = sizeof(struct ebt_inat_info),
352 .help = print_help_s,
353 .init = init_s,
354 .parse = parse_s,
355 .final_check = final_check_s,
356 .print = print_s,
357 .compare = compare,
358 .extra_ops = opts_s,
gborowiakc7c191a2003-09-07 13:10:40 +0000359};
360
361static struct ebt_u_target idnat_target =
362{
gborowiak66bbdc02003-09-07 21:02:28 +0000363 .name = EBT_IDNAT_TARGET,
364 .size = sizeof(struct ebt_inat_info),
365 .help = print_help_d,
366 .init = init_d,
367 .parse = parse_d,
368 .final_check = final_check_d,
369 .print = print_d,
370 .compare = compare,
371 .extra_ops = opts_d,
gborowiakc7c191a2003-09-07 13:10:40 +0000372};
373
374static void _init(void) __attribute__ ((constructor));
375static void _init(void)
376{
377 register_target(&isnat_target);
378 register_target(&idnat_target);
379}