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