1 #include <ctype.h>
2 #include <errno.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdbool.h>
6 #include <stdarg.h>
7 #include <string.h>
8 #include <fcntl.h>
9 #include <getopt.h>
10 #include <iptables.h>
11 #include <xtables.h>
12
13 #include <netinet/ether.h>
14
15 #include <linux/netfilter_bridge.h>
16 #include <linux/netfilter/nf_tables.h>
17 #include <libiptc/libxtc.h>
18
19 #include "xshared.h"
20 #include "xtables-multi.h"
21 #include "nft-bridge.h"
22 #include "nft.h"
23 #include "nft-shared.h"
24 /*
25 * From include/ebtables_u.h
26 */
27 #define EXEC_STYLE_PRG 0
28 #define EXEC_STYLE_DAEMON 1
29
30 #define ebt_check_option2(flags, mask) EBT_CHECK_OPTION(flags, mask)
31
32 extern int ebt_invert;
33
ebt_check_inverse2(const char option[],int argc,char ** argv)34 static int ebt_check_inverse2(const char option[], int argc, char **argv)
35 {
36 if (!option)
37 return ebt_invert;
38 if (strcmp(option, "!") == 0) {
39 if (ebt_invert == 1)
40 xtables_error(PARAMETER_PROBLEM,
41 "Double use of '!' not allowed");
42 if (optind >= argc)
43 optarg = NULL;
44 else
45 optarg = argv[optind];
46 optind++;
47 ebt_invert = 1;
48 return 1;
49 }
50 return ebt_invert;
51 }
52
53 /*
54 * Glue code to use libxtables
55 */
parse_rule_number(const char * rule)56 static int parse_rule_number(const char *rule)
57 {
58 unsigned int rule_nr;
59
60 if (!xtables_strtoui(rule, NULL, &rule_nr, 1, INT_MAX))
61 xtables_error(PARAMETER_PROBLEM,
62 "Invalid rule number `%s'", rule);
63
64 return rule_nr;
65 }
66
get_current_chain(const char * chain)67 static int get_current_chain(const char *chain)
68 {
69 if (strcmp(chain, "PREROUTING") == 0)
70 return NF_BR_PRE_ROUTING;
71 else if (strcmp(chain, "INPUT") == 0)
72 return NF_BR_LOCAL_IN;
73 else if (strcmp(chain, "FORWARD") == 0)
74 return NF_BR_FORWARD;
75 else if (strcmp(chain, "OUTPUT") == 0)
76 return NF_BR_LOCAL_OUT;
77 else if (strcmp(chain, "POSTROUTING") == 0)
78 return NF_BR_POST_ROUTING;
79
80 return -1;
81 }
82
83 /*
84 * The original ebtables parser
85 */
86
87 /* Checks whether a command has already been specified */
88 #define OPT_COMMANDS (flags & OPT_COMMAND || flags & OPT_ZERO)
89
90 #define OPT_COMMAND 0x01
91 #define OPT_TABLE 0x02
92 #define OPT_IN 0x04
93 #define OPT_OUT 0x08
94 #define OPT_JUMP 0x10
95 #define OPT_PROTOCOL 0x20
96 #define OPT_SOURCE 0x40
97 #define OPT_DEST 0x80
98 #define OPT_ZERO 0x100
99 #define OPT_LOGICALIN 0x200
100 #define OPT_LOGICALOUT 0x400
101 #define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */
102
103 /* Default command line options. Do not mess around with the already
104 * assigned numbers unless you know what you are doing */
105 extern struct option ebt_original_options[];
106 extern struct xtables_globals ebtables_globals;
107 #define opts ebtables_globals.opts
108 #define prog_name ebtables_globals.program_name
109 #define prog_vers ebtables_globals.program_version
110
print_help(void)111 static void print_help(void)
112 {
113 fprintf(stderr, "%s: Translate ebtables command to nft syntax\n"
114 "no side effects occur, the translated command is written "
115 "to standard output.\n"
116 "A '#' followed by input means no translation "
117 "is available.\n", prog_name);
118 exit(0);
119 }
120
parse_rule_range(const char * argv,int * rule_nr,int * rule_nr_end)121 static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
122 {
123 char *colon = strchr(argv, ':'), *buffer;
124
125 if (colon) {
126 *colon = '\0';
127 if (*(colon + 1) == '\0')
128 *rule_nr_end = -1; /* Until the last rule */
129 else {
130 *rule_nr_end = strtol(colon + 1, &buffer, 10);
131 if (*buffer != '\0' || *rule_nr_end == 0)
132 return -1;
133 }
134 }
135 if (colon == argv)
136 *rule_nr = 1; /* Beginning with the first rule */
137 else {
138 *rule_nr = strtol(argv, &buffer, 10);
139 if (*buffer != '\0' || *rule_nr == 0)
140 return -1;
141 }
142 if (!colon)
143 *rule_nr_end = *rule_nr;
144 return 0;
145 }
146
ebtables_parse_interface(const char * arg,char * vianame)147 static void ebtables_parse_interface(const char *arg, char *vianame)
148 {
149 unsigned char mask[IFNAMSIZ];
150 char *c;
151
152 xtables_parse_interface(arg, vianame, mask);
153
154 if ((c = strchr(vianame, '+'))) {
155 if (*(c + 1) != '\0')
156 xtables_error(PARAMETER_PROBLEM,
157 "Spurious characters after '+' wildcard");
158 }
159 }
160
print_ebt_cmd(int argc,char * argv[])161 static void print_ebt_cmd(int argc, char *argv[])
162 {
163 int i;
164
165 printf("# ");
166 for (i = 1; i < argc; i++)
167 printf("%s ", argv[i]);
168
169 printf("\n");
170 }
171
nft_rule_eb_xlate_add(struct nft_handle * h,const struct nft_xt_cmd_parse * p,const struct iptables_command_state * cs,bool append)172 static int nft_rule_eb_xlate_add(struct nft_handle *h, const struct nft_xt_cmd_parse *p,
173 const struct iptables_command_state *cs, bool append)
174 {
175 struct xt_xlate *xl = xt_xlate_alloc(10240);
176 int ret;
177
178 if (append) {
179 xt_xlate_add(xl, "add rule bridge %s %s ", p->table, p->chain);
180 } else {
181 xt_xlate_add(xl, "insert rule bridge %s %s ", p->table, p->chain);
182 }
183
184 ret = h->ops->xlate(cs, xl);
185 if (ret)
186 printf("%s\n", xt_xlate_get(xl));
187
188 xt_xlate_free(xl);
189 return ret;
190 }
191
192 /* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
do_commandeb_xlate(struct nft_handle * h,int argc,char * argv[],char ** table)193 static int do_commandeb_xlate(struct nft_handle *h, int argc, char *argv[], char **table)
194 {
195 char *buffer;
196 int c, i;
197 int rule_nr = 0;
198 int rule_nr_end = 0;
199 int ret = 0;
200 unsigned int flags = 0;
201 struct iptables_command_state cs = {
202 .argv = argv,
203 .eb.bitmask = EBT_NOPROTO,
204 };
205 char command = 'h';
206 const char *chain = NULL;
207 int exec_style = EXEC_STYLE_PRG;
208 int selected_chain = -1;
209 struct xtables_rule_match *xtrm_i;
210 struct ebt_match *match;
211 struct nft_xt_cmd_parse p = {
212 .table = *table,
213 };
214
215 /* prevent getopt to spoil our error reporting */
216 opterr = false;
217
218 printf("nft ");
219 /* Getopt saves the day */
220 while ((c = getopt_long(argc, argv,
221 "-A:D:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", opts, NULL)) != -1) {
222 cs.c = c;
223 cs.invert = ebt_invert;
224 switch (c) {
225 case 'A': /* Add a rule */
226 case 'D': /* Delete a rule */
227 case 'P': /* Define policy */
228 case 'I': /* Insert a rule */
229 case 'N': /* Make a user defined chain */
230 case 'E': /* Rename chain */
231 case 'X': /* Delete chain */
232 /* We allow -N chainname -P policy */
233 /* XXX: Not in ebtables-compat */
234 if (command == 'N' && c == 'P') {
235 command = c;
236 optind--; /* No table specified */
237 break;
238 }
239 if (OPT_COMMANDS)
240 xtables_error(PARAMETER_PROBLEM,
241 "Multiple commands are not allowed");
242 command = c;
243 chain = optarg;
244 selected_chain = get_current_chain(chain);
245 p.chain = chain;
246 flags |= OPT_COMMAND;
247
248 if (c == 'N') {
249 printf("add chain bridge %s %s\n", p.table, p.chain);
250 ret = 1;
251 break;
252 } else if (c == 'X') {
253 printf("delete chain bridge %s %s\n", p.table, p.chain);
254 ret = 1;
255 break;
256 }
257
258 if (c == 'E') {
259 break;
260 } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
261 if (optind != argc - 1)
262 xtables_error(PARAMETER_PROBLEM,
263 "No extra options allowed with -D start_nr[:end_nr]");
264 if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
265 xtables_error(PARAMETER_PROBLEM,
266 "Problem with the specified rule number(s) '%s'", argv[optind]);
267 optind++;
268 } else if (c == 'I') {
269 if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
270 rule_nr = 1;
271 else {
272 rule_nr = parse_rule_number(argv[optind]);
273 optind++;
274 }
275 p.rulenum = rule_nr;
276 } else if (c == 'P') {
277 break;
278 }
279 break;
280 case 'L': /* List */
281 printf("list table bridge %s\n", p.table);
282 ret = 1;
283 break;
284 case 'F': /* Flush */
285 if (p.chain) {
286 printf("flush chain bridge %s %s\n", p.table, p.chain);
287 } else {
288 printf("flush table bridge %s\n", p.table);
289 }
290 ret = 1;
291 break;
292 case 'Z': /* Zero counters */
293 if (c == 'Z') {
294 if ((flags & OPT_ZERO) || (flags & OPT_COMMAND && command != 'L'))
295 print_zero:
296 xtables_error(PARAMETER_PROBLEM,
297 "Command -Z only allowed together with command -L");
298 flags |= OPT_ZERO;
299 } else {
300 if (flags & OPT_COMMAND)
301 xtables_error(PARAMETER_PROBLEM,
302 "Multiple commands are not allowed");
303 command = c;
304 flags |= OPT_COMMAND;
305 if (flags & OPT_ZERO && c != 'L')
306 goto print_zero;
307 }
308 break;
309 case 'V': /* Version */
310 if (OPT_COMMANDS)
311 xtables_error(PARAMETER_PROBLEM,
312 "Multiple commands are not allowed");
313 if (exec_style == EXEC_STYLE_DAEMON)
314 xtables_error(PARAMETER_PROBLEM,
315 "%s %s\n", prog_name, prog_vers);
316 printf("%s %s\n", prog_name, prog_vers);
317 exit(0);
318 case 'h':
319 if (OPT_COMMANDS)
320 xtables_error(PARAMETER_PROBLEM,
321 "Multiple commands are not allowed");
322 print_help();
323 break;
324 case 't': /* Table */
325 if (OPT_COMMANDS)
326 xtables_error(PARAMETER_PROBLEM,
327 "Please put the -t option first");
328 ebt_check_option2(&flags, OPT_TABLE);
329 if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
330 xtables_error(PARAMETER_PROBLEM,
331 "Table name length cannot exceed %d characters",
332 EBT_TABLE_MAXNAMELEN - 1);
333 *table = optarg;
334 p.table = optarg;
335 break;
336 case 'i': /* Input interface */
337 case 2 : /* Logical input interface */
338 case 'o': /* Output interface */
339 case 3 : /* Logical output interface */
340 case 'j': /* Target */
341 case 'p': /* Net family protocol */
342 case 's': /* Source mac */
343 case 'd': /* Destination mac */
344 case 'c': /* Set counters */
345 if (!OPT_COMMANDS)
346 xtables_error(PARAMETER_PROBLEM,
347 "No command specified");
348 if (command != 'A' && command != 'D' && command != 'I')
349 xtables_error(PARAMETER_PROBLEM,
350 "Command and option do not match");
351 if (c == 'i') {
352 ebt_check_option2(&flags, OPT_IN);
353 if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
354 xtables_error(PARAMETER_PROBLEM,
355 "Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
356 if (ebt_check_inverse2(optarg, argc, argv))
357 cs.eb.invflags |= EBT_IIN;
358
359 ebtables_parse_interface(optarg, cs.eb.in);
360 break;
361 } else if (c == 2) {
362 ebt_check_option2(&flags, OPT_LOGICALIN);
363 if (selected_chain > 2 && selected_chain < NF_BR_BROUTING)
364 xtables_error(PARAMETER_PROBLEM,
365 "Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
366 if (ebt_check_inverse2(optarg, argc, argv))
367 cs.eb.invflags |= EBT_ILOGICALIN;
368
369 ebtables_parse_interface(optarg, cs.eb.logical_in);
370 break;
371 } else if (c == 'o') {
372 ebt_check_option2(&flags, OPT_OUT);
373 if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
374 xtables_error(PARAMETER_PROBLEM,
375 "Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
376 if (ebt_check_inverse2(optarg, argc, argv))
377 cs.eb.invflags |= EBT_IOUT;
378
379 ebtables_parse_interface(optarg, cs.eb.out);
380 break;
381 } else if (c == 3) {
382 ebt_check_option2(&flags, OPT_LOGICALOUT);
383 if (selected_chain < 2 || selected_chain == NF_BR_BROUTING)
384 xtables_error(PARAMETER_PROBLEM,
385 "Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
386 if (ebt_check_inverse2(optarg, argc, argv))
387 cs.eb.invflags |= EBT_ILOGICALOUT;
388
389 ebtables_parse_interface(optarg, cs.eb.logical_out);
390 break;
391 } else if (c == 'j') {
392 ebt_check_option2(&flags, OPT_JUMP);
393 command_jump(&cs, optarg);
394 break;
395 } else if (c == 's') {
396 ebt_check_option2(&flags, OPT_SOURCE);
397 if (ebt_check_inverse2(optarg, argc, argv))
398 cs.eb.invflags |= EBT_ISOURCE;
399
400 if (xtables_parse_mac_and_mask(optarg,
401 cs.eb.sourcemac,
402 cs.eb.sourcemsk))
403 xtables_error(PARAMETER_PROBLEM, "Problem with specified source mac '%s'", optarg);
404 cs.eb.bitmask |= EBT_SOURCEMAC;
405 break;
406 } else if (c == 'd') {
407 ebt_check_option2(&flags, OPT_DEST);
408 if (ebt_check_inverse2(optarg, argc, argv))
409 cs.eb.invflags |= EBT_IDEST;
410
411 if (xtables_parse_mac_and_mask(optarg,
412 cs.eb.destmac,
413 cs.eb.destmsk))
414 xtables_error(PARAMETER_PROBLEM, "Problem with specified destination mac '%s'", optarg);
415 cs.eb.bitmask |= EBT_DESTMAC;
416 break;
417 } else if (c == 'c') {
418 ebt_check_option2(&flags, OPT_COUNT);
419 if (ebt_check_inverse2(optarg, argc, argv))
420 xtables_error(PARAMETER_PROBLEM,
421 "Unexpected '!' after -c");
422 if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
423 xtables_error(PARAMETER_PROBLEM,
424 "Option -c needs 2 arguments");
425
426 cs.counters.pcnt = strtoull(optarg, &buffer, 10);
427 if (*buffer != '\0')
428 xtables_error(PARAMETER_PROBLEM,
429 "Packet counter '%s' invalid",
430 optarg);
431 cs.counters.bcnt = strtoull(argv[optind], &buffer, 10);
432 if (*buffer != '\0')
433 xtables_error(PARAMETER_PROBLEM,
434 "Packet counter '%s' invalid",
435 argv[optind]);
436 optind++;
437 break;
438 }
439 ebt_check_option2(&flags, OPT_PROTOCOL);
440 if (ebt_check_inverse2(optarg, argc, argv))
441 cs.eb.invflags |= EBT_IPROTO;
442
443 cs.eb.bitmask &= ~((unsigned int)EBT_NOPROTO);
444 i = strtol(optarg, &buffer, 16);
445 if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
446 xtables_error(PARAMETER_PROBLEM,
447 "Problem with the specified protocol");
448 if (*buffer != '\0') {
449 struct xt_ethertypeent *ent;
450
451 if (!strcasecmp(optarg, "LENGTH")) {
452 cs.eb.bitmask |= EBT_802_3;
453 break;
454 }
455 ent = xtables_getethertypebyname(optarg);
456 if (!ent)
457 xtables_error(PARAMETER_PROBLEM,
458 "Problem with the specified Ethernet protocol '%s', perhaps "XT_PATH_ETHERTYPES " is missing", optarg);
459 cs.eb.ethproto = ent->e_ethertype;
460 } else
461 cs.eb.ethproto = i;
462
463 if (cs.eb.ethproto < 0x0600)
464 xtables_error(PARAMETER_PROBLEM,
465 "Sorry, protocols have values above or equal to 0x0600");
466 break;
467 case 4 : /* Lc */
468 ebt_check_option2(&flags, LIST_C);
469 if (command != 'L')
470 xtables_error(PARAMETER_PROBLEM,
471 "Use --Lc with -L");
472 flags |= LIST_C;
473 break;
474 case 5 : /* Ln */
475 ebt_check_option2(&flags, LIST_N);
476 if (command != 'L')
477 xtables_error(PARAMETER_PROBLEM,
478 "Use --Ln with -L");
479 if (flags & LIST_X)
480 xtables_error(PARAMETER_PROBLEM,
481 "--Lx is not compatible with --Ln");
482 flags |= LIST_N;
483 break;
484 case 6 : /* Lx */
485 ebt_check_option2(&flags, LIST_X);
486 if (command != 'L')
487 xtables_error(PARAMETER_PROBLEM,
488 "Use --Lx with -L");
489 if (flags & LIST_N)
490 xtables_error(PARAMETER_PROBLEM,
491 "--Lx is not compatible with --Ln");
492 flags |= LIST_X;
493 break;
494 case 12 : /* Lmac2 */
495 ebt_check_option2(&flags, LIST_MAC2);
496 if (command != 'L')
497 xtables_error(PARAMETER_PROBLEM,
498 "Use --Lmac2 with -L");
499 flags |= LIST_MAC2;
500 break;
501 case 1 :
502 if (!strcmp(optarg, "!"))
503 ebt_check_inverse2(optarg, argc, argv);
504 else
505 xtables_error(PARAMETER_PROBLEM,
506 "Bad argument : '%s'", optarg);
507 /* ebt_ebt_check_inverse2() did optind++ */
508 optind--;
509 continue;
510 default:
511 ebt_check_inverse2(optarg, argc, argv);
512
513 if (ebt_command_default(&cs))
514 xtables_error(PARAMETER_PROBLEM,
515 "Unknown argument: '%s'",
516 argv[optind - 1]);
517
518 if (command != 'A' && command != 'I' &&
519 command != 'D')
520 xtables_error(PARAMETER_PROBLEM,
521 "Extensions only for -A, -I, -D");
522 }
523 ebt_invert = 0;
524 }
525
526 /* Do the final checks */
527 if (command == 'A' || command == 'I' || command == 'D') {
528 for (xtrm_i = cs.matches; xtrm_i; xtrm_i = xtrm_i->next)
529 xtables_option_mfcall(xtrm_i->match);
530
531 for (match = cs.match_list; match; match = match->next) {
532 if (match->ismatch)
533 continue;
534
535 xtables_option_tfcall(match->u.watcher);
536 }
537
538 if (cs.target != NULL)
539 xtables_option_tfcall(cs.target);
540 }
541
542 cs.eb.ethproto = htons(cs.eb.ethproto);
543
544 if (command == 'P') {
545 return 0;
546 } else if (command == 'A') {
547 ret = nft_rule_eb_xlate_add(h, &p, &cs, true);
548 if (!ret)
549 print_ebt_cmd(argc, argv);
550 } else if (command == 'I') {
551 ret = nft_rule_eb_xlate_add(h, &p, &cs, false);
552 if (!ret)
553 print_ebt_cmd(argc, argv);
554 }
555
556 ebt_cs_clean(&cs);
557 return ret;
558 }
559
dummy_compat_rev(const char * name,uint8_t rev,int opt)560 static int dummy_compat_rev(const char *name, uint8_t rev, int opt)
561 {
562 return 1;
563 }
564
xtables_eb_xlate_main(int argc,char * argv[])565 int xtables_eb_xlate_main(int argc, char *argv[])
566 {
567 int ret;
568 char *table = "filter";
569 struct nft_handle h;
570
571 nft_init_eb(&h, argv[0]);
572 ebtables_globals.compat_rev = dummy_compat_rev;
573
574 ret = do_commandeb_xlate(&h, argc, argv, &table);
575 if (!ret)
576 fprintf(stderr, "Translation not implemented\n");
577
578 exit(!ret);
579 }
580
581