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