xref: /aosp_15_r20/external/iptables/iptables/xtables-eb-translate.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
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