xref: /aosp_15_r20/external/iptables/iptables/iptables.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /* Code to take an iptables-style command line and do it. */
2 
3 /*
4  * Author: [email protected] and [email protected]
5  *
6  * (C) 2000-2002 by the netfilter coreteam <[email protected]>:
7  * 		    Paul 'Rusty' Russell <[email protected]>
8  * 		    Marc Boucher <[email protected]>
9  * 		    James Morris <[email protected]>
10  * 		    Harald Welte <[email protected]>
11  * 		    Jozsef Kadlecsik <[email protected]>
12  *
13  *	This program is free software; you can redistribute it and/or modify
14  *	it under the terms of the GNU General Public License as published by
15  *	the Free Software Foundation; either version 2 of the License, or
16  *	(at your option) any later version.
17  *
18  *	This program is distributed in the hope that it will be useful,
19  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *	GNU General Public License for more details.
22  *
23  *	You should have received a copy of the GNU General Public License
24  *	along with this program; if not, write to the Free Software
25  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27 #include "config.h"
28 #include <getopt.h>
29 #include <string.h>
30 #include <netdb.h>
31 #include <errno.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 #include <unistd.h>
39 #include <iptables.h>
40 #include <xtables.h>
41 #include <fcntl.h>
42 #include "xshared.h"
43 
44 static const char unsupported_rev[] = " [unsupported revision]";
45 
46 static struct option original_opts[] = {
47 	{.name = "append",        .has_arg = 1, .val = 'A'},
48 	{.name = "delete",        .has_arg = 1, .val = 'D'},
49 	{.name = "check",         .has_arg = 1, .val = 'C'},
50 	{.name = "insert",        .has_arg = 1, .val = 'I'},
51 	{.name = "replace",       .has_arg = 1, .val = 'R'},
52 	{.name = "list",          .has_arg = 2, .val = 'L'},
53 	{.name = "list-rules",    .has_arg = 2, .val = 'S'},
54 	{.name = "flush",         .has_arg = 2, .val = 'F'},
55 	{.name = "zero",          .has_arg = 2, .val = 'Z'},
56 	{.name = "new-chain",     .has_arg = 1, .val = 'N'},
57 	{.name = "delete-chain",  .has_arg = 2, .val = 'X'},
58 	{.name = "rename-chain",  .has_arg = 1, .val = 'E'},
59 	{.name = "policy",        .has_arg = 1, .val = 'P'},
60 	{.name = "source",        .has_arg = 1, .val = 's'},
61 	{.name = "destination",   .has_arg = 1, .val = 'd'},
62 	{.name = "src",           .has_arg = 1, .val = 's'}, /* synonym */
63 	{.name = "dst",           .has_arg = 1, .val = 'd'}, /* synonym */
64 	{.name = "protocol",      .has_arg = 1, .val = 'p'},
65 	{.name = "in-interface",  .has_arg = 1, .val = 'i'},
66 	{.name = "jump",          .has_arg = 1, .val = 'j'},
67 	{.name = "table",         .has_arg = 1, .val = 't'},
68 	{.name = "match",         .has_arg = 1, .val = 'm'},
69 	{.name = "numeric",       .has_arg = 0, .val = 'n'},
70 	{.name = "out-interface", .has_arg = 1, .val = 'o'},
71 	{.name = "verbose",       .has_arg = 0, .val = 'v'},
72 	{.name = "wait",          .has_arg = 2, .val = 'w'},
73 	{.name = "wait-interval", .has_arg = 2, .val = 'W'},
74 	{.name = "exact",         .has_arg = 0, .val = 'x'},
75 	{.name = "fragments",     .has_arg = 0, .val = 'f'},
76 	{.name = "version",       .has_arg = 0, .val = 'V'},
77 	{.name = "help",          .has_arg = 2, .val = 'h'},
78 	{.name = "line-numbers",  .has_arg = 0, .val = '0'},
79 	{.name = "modprobe",      .has_arg = 1, .val = 'M'},
80 	{.name = "set-counters",  .has_arg = 1, .val = 'c'},
81 	{.name = "goto",          .has_arg = 1, .val = 'g'},
82 	{.name = "ipv4",          .has_arg = 0, .val = '4'},
83 	{.name = "ipv6",          .has_arg = 0, .val = '6'},
84 	{NULL},
85 };
86 
87 struct xtables_globals iptables_globals = {
88 	.option_offset = 0,
89 	.program_version = PACKAGE_VERSION " (legacy)",
90 	.orig_opts = original_opts,
91 	.compat_rev = xtables_compatible_revision,
92 };
93 
94 /*
95  *	All functions starting with "parse" should succeed, otherwise
96  *	the program fails.
97  *	Most routines return pointers to static data that may change
98  *	between calls to the same or other routines with a few exceptions:
99  *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
100  *	return global static data.
101 */
102 
103 /* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
104 
105 
106 static int
print_match(const struct xt_entry_match * m,const struct ipt_ip * ip,int numeric)107 print_match(const struct xt_entry_match *m,
108 	    const struct ipt_ip *ip,
109 	    int numeric)
110 {
111 	const char *name = m->u.user.name;
112 	const int revision = m->u.user.revision;
113 	struct xtables_match *match, *mt;
114 
115 	match = xtables_find_match(name, XTF_TRY_LOAD, NULL);
116 	if (match) {
117 		mt = xtables_find_match_revision(name, XTF_TRY_LOAD,
118 						 match, revision);
119 		if (mt && mt->print)
120 			mt->print(ip, m, numeric);
121 		else if (match->print)
122 			printf("%s%s ", match->name, unsupported_rev);
123 		else
124 			printf("%s ", match->name);
125 
126 		if (match->next == match)
127 			free(match);
128 	} else {
129 		if (name[0])
130 			printf("UNKNOWN match `%s' ", name);
131 	}
132 	/* Don't stop iterating. */
133 	return 0;
134 }
135 
136 /* e is called `fw' here for historical reasons */
137 static void
print_firewall(const struct ipt_entry * fw,const char * targname,unsigned int num,unsigned int format,struct xtc_handle * const handle)138 print_firewall(const struct ipt_entry *fw,
139 	       const char *targname,
140 	       unsigned int num,
141 	       unsigned int format,
142 	       struct xtc_handle *const handle)
143 {
144 	struct xtables_target *target, *tg;
145 	const struct xt_entry_target *t;
146 
147 	if (!iptc_is_chain(targname, handle))
148 		target = xtables_find_target(targname, XTF_TRY_LOAD);
149 	else
150 		target = xtables_find_target(XT_STANDARD_TARGET,
151 		         XTF_LOAD_MUST_SUCCEED);
152 
153 	t = ipt_get_target((struct ipt_entry *)fw);
154 
155 	print_rule_details(num, &fw->counters, targname, fw->ip.proto,
156 			   fw->ip.flags, fw->ip.invflags, format);
157 
158 	print_fragment(fw->ip.flags, fw->ip.invflags, format, false);
159 
160 	print_ifaces(fw->ip.iniface, fw->ip.outiface, fw->ip.invflags, format);
161 
162 	print_ipv4_addresses(fw, format);
163 
164 	if (format & FMT_NOTABLE)
165 		fputs("  ", stdout);
166 
167 #ifdef IPT_F_GOTO
168 	if(fw->ip.flags & IPT_F_GOTO)
169 		printf("[goto] ");
170 #endif
171 
172 	IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
173 
174 	if (target) {
175 		const int revision = t->u.user.revision;
176 
177 		tg = xtables_find_target_revision(targname, XTF_TRY_LOAD,
178 						  target, revision);
179 		if (tg && tg->print)
180 			/* Print the target information. */
181 			tg->print(&fw->ip, t, format & FMT_NUMERIC);
182 		else if (target->print)
183 			printf(" %s%s", target->name, unsupported_rev);
184 
185 		if (target->next == target)
186 			free(target);
187 	} else if (t->u.target_size != sizeof(*t))
188 		printf("[%u bytes of unknown target data] ",
189 		       (unsigned int)(t->u.target_size - sizeof(*t)));
190 
191 	if (!(format & FMT_NONEWLINE))
192 		fputc('\n', stdout);
193 }
194 
195 static void
print_firewall_line(const struct ipt_entry * fw,struct xtc_handle * const h)196 print_firewall_line(const struct ipt_entry *fw,
197 		    struct xtc_handle *const h)
198 {
199 	struct xt_entry_target *t;
200 
201 	t = ipt_get_target((struct ipt_entry *)fw);
202 	print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
203 }
204 
205 static int
append_entry(const xt_chainlabel chain,struct ipt_entry * fw,unsigned int nsaddrs,const struct in_addr saddrs[],const struct in_addr smasks[],unsigned int ndaddrs,const struct in_addr daddrs[],const struct in_addr dmasks[],int verbose,struct xtc_handle * handle)206 append_entry(const xt_chainlabel chain,
207 	     struct ipt_entry *fw,
208 	     unsigned int nsaddrs,
209 	     const struct in_addr saddrs[],
210 	     const struct in_addr smasks[],
211 	     unsigned int ndaddrs,
212 	     const struct in_addr daddrs[],
213 	     const struct in_addr dmasks[],
214 	     int verbose,
215 	     struct xtc_handle *handle)
216 {
217 	unsigned int i, j;
218 	int ret = 1;
219 
220 	for (i = 0; i < nsaddrs; i++) {
221 		fw->ip.src.s_addr = saddrs[i].s_addr;
222 		fw->ip.smsk.s_addr = smasks[i].s_addr;
223 		for (j = 0; j < ndaddrs; j++) {
224 			fw->ip.dst.s_addr = daddrs[j].s_addr;
225 			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
226 			if (verbose)
227 				print_firewall_line(fw, handle);
228 			ret &= iptc_append_entry(chain, fw, handle);
229 		}
230 	}
231 
232 	return ret;
233 }
234 
235 static int
replace_entry(const xt_chainlabel chain,struct ipt_entry * fw,unsigned int rulenum,const struct in_addr * saddr,const struct in_addr * smask,const struct in_addr * daddr,const struct in_addr * dmask,int verbose,struct xtc_handle * handle)236 replace_entry(const xt_chainlabel chain,
237 	      struct ipt_entry *fw,
238 	      unsigned int rulenum,
239 	      const struct in_addr *saddr, const struct in_addr *smask,
240 	      const struct in_addr *daddr, const struct in_addr *dmask,
241 	      int verbose,
242 	      struct xtc_handle *handle)
243 {
244 	fw->ip.src.s_addr = saddr->s_addr;
245 	fw->ip.dst.s_addr = daddr->s_addr;
246 	fw->ip.smsk.s_addr = smask->s_addr;
247 	fw->ip.dmsk.s_addr = dmask->s_addr;
248 
249 	if (verbose)
250 		print_firewall_line(fw, handle);
251 	return iptc_replace_entry(chain, fw, rulenum, handle);
252 }
253 
254 static int
insert_entry(const xt_chainlabel chain,struct ipt_entry * fw,unsigned int rulenum,unsigned int nsaddrs,const struct in_addr saddrs[],const struct in_addr smasks[],unsigned int ndaddrs,const struct in_addr daddrs[],const struct in_addr dmasks[],int verbose,struct xtc_handle * handle)255 insert_entry(const xt_chainlabel chain,
256 	     struct ipt_entry *fw,
257 	     unsigned int rulenum,
258 	     unsigned int nsaddrs,
259 	     const struct in_addr saddrs[],
260 	     const struct in_addr smasks[],
261 	     unsigned int ndaddrs,
262 	     const struct in_addr daddrs[],
263 	     const struct in_addr dmasks[],
264 	     int verbose,
265 	     struct xtc_handle *handle)
266 {
267 	unsigned int i, j;
268 	int ret = 1;
269 
270 	for (i = 0; i < nsaddrs; i++) {
271 		fw->ip.src.s_addr = saddrs[i].s_addr;
272 		fw->ip.smsk.s_addr = smasks[i].s_addr;
273 		for (j = 0; j < ndaddrs; j++) {
274 			fw->ip.dst.s_addr = daddrs[j].s_addr;
275 			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
276 			if (verbose)
277 				print_firewall_line(fw, handle);
278 			ret &= iptc_insert_entry(chain, fw, rulenum, handle);
279 		}
280 	}
281 
282 	return ret;
283 }
284 
285 static int
delete_entry(const xt_chainlabel chain,struct ipt_entry * fw,unsigned int nsaddrs,const struct in_addr saddrs[],const struct in_addr smasks[],unsigned int ndaddrs,const struct in_addr daddrs[],const struct in_addr dmasks[],int verbose,struct xtc_handle * handle,struct xtables_rule_match * matches,const struct xtables_target * target)286 delete_entry(const xt_chainlabel chain,
287 	     struct ipt_entry *fw,
288 	     unsigned int nsaddrs,
289 	     const struct in_addr saddrs[],
290 	     const struct in_addr smasks[],
291 	     unsigned int ndaddrs,
292 	     const struct in_addr daddrs[],
293 	     const struct in_addr dmasks[],
294 	     int verbose,
295 	     struct xtc_handle *handle,
296 	     struct xtables_rule_match *matches,
297 	     const struct xtables_target *target)
298 {
299 	unsigned int i, j;
300 	int ret = 1;
301 	unsigned char *mask;
302 
303 	mask = make_delete_mask(matches, target, sizeof(*fw));
304 	for (i = 0; i < nsaddrs; i++) {
305 		fw->ip.src.s_addr = saddrs[i].s_addr;
306 		fw->ip.smsk.s_addr = smasks[i].s_addr;
307 		for (j = 0; j < ndaddrs; j++) {
308 			fw->ip.dst.s_addr = daddrs[j].s_addr;
309 			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
310 			if (verbose)
311 				print_firewall_line(fw, handle);
312 			ret &= iptc_delete_entry(chain, fw, mask, handle);
313 		}
314 	}
315 	free(mask);
316 
317 	return ret;
318 }
319 
320 static int
check_entry(const xt_chainlabel chain,struct ipt_entry * fw,unsigned int nsaddrs,const struct in_addr * saddrs,const struct in_addr * smasks,unsigned int ndaddrs,const struct in_addr * daddrs,const struct in_addr * dmasks,bool verbose,struct xtc_handle * handle,struct xtables_rule_match * matches,const struct xtables_target * target)321 check_entry(const xt_chainlabel chain, struct ipt_entry *fw,
322 	    unsigned int nsaddrs, const struct in_addr *saddrs,
323 	    const struct in_addr *smasks, unsigned int ndaddrs,
324 	    const struct in_addr *daddrs, const struct in_addr *dmasks,
325 	    bool verbose, struct xtc_handle *handle,
326 	    struct xtables_rule_match *matches,
327 	    const struct xtables_target *target)
328 {
329 	unsigned int i, j;
330 	int ret = 1;
331 	unsigned char *mask;
332 
333 	mask = make_delete_mask(matches, target, sizeof(*fw));
334 	for (i = 0; i < nsaddrs; i++) {
335 		fw->ip.src.s_addr = saddrs[i].s_addr;
336 		fw->ip.smsk.s_addr = smasks[i].s_addr;
337 		for (j = 0; j < ndaddrs; j++) {
338 			fw->ip.dst.s_addr = daddrs[j].s_addr;
339 			fw->ip.dmsk.s_addr = dmasks[j].s_addr;
340 			if (verbose)
341 				print_firewall_line(fw, handle);
342 			ret &= iptc_check_entry(chain, fw, mask, handle);
343 		}
344 	}
345 
346 	free(mask);
347 	return ret;
348 }
349 
350 int
for_each_chain4(int (* fn)(const xt_chainlabel,int,struct xtc_handle *),int verbose,int builtinstoo,struct xtc_handle * handle)351 for_each_chain4(int (*fn)(const xt_chainlabel, int, struct xtc_handle *),
352 	       int verbose, int builtinstoo, struct xtc_handle *handle)
353 {
354         int ret = 1;
355 	const char *chain;
356 	char *chains;
357 	unsigned int i, chaincount = 0;
358 
359 	chain = iptc_first_chain(handle);
360 	while (chain) {
361 		chaincount++;
362 		chain = iptc_next_chain(handle);
363         }
364 
365 	chains = xtables_malloc(sizeof(xt_chainlabel) * chaincount);
366 	i = 0;
367 	chain = iptc_first_chain(handle);
368 	while (chain) {
369 		strcpy(chains + i*sizeof(xt_chainlabel), chain);
370 		i++;
371 		chain = iptc_next_chain(handle);
372         }
373 
374 	for (i = 0; i < chaincount; i++) {
375 		if (!builtinstoo
376 		    && iptc_builtin(chains + i*sizeof(xt_chainlabel),
377 				    handle) == 1)
378 			continue;
379 	        ret &= fn(chains + i*sizeof(xt_chainlabel), verbose, handle);
380 	}
381 
382 	free(chains);
383         return ret;
384 }
385 
386 int
flush_entries4(const xt_chainlabel chain,int verbose,struct xtc_handle * handle)387 flush_entries4(const xt_chainlabel chain, int verbose,
388 	      struct xtc_handle *handle)
389 {
390 	if (!chain)
391 		return for_each_chain4(flush_entries4, verbose, 1, handle);
392 
393 	if (verbose)
394 		fprintf(stdout, "Flushing chain `%s'\n", chain);
395 	return iptc_flush_entries(chain, handle);
396 }
397 
398 static int
zero_entries(const xt_chainlabel chain,int verbose,struct xtc_handle * handle)399 zero_entries(const xt_chainlabel chain, int verbose,
400 	     struct xtc_handle *handle)
401 {
402 	if (!chain)
403 		return for_each_chain4(zero_entries, verbose, 1, handle);
404 
405 	if (verbose)
406 		fprintf(stdout, "Zeroing chain `%s'\n", chain);
407 	return iptc_zero_entries(chain, handle);
408 }
409 
410 int
delete_chain4(const xt_chainlabel chain,int verbose,struct xtc_handle * handle)411 delete_chain4(const xt_chainlabel chain, int verbose,
412 	     struct xtc_handle *handle)
413 {
414 	if (!chain)
415 		return for_each_chain4(delete_chain4, verbose, 0, handle);
416 
417 	if (verbose)
418 		fprintf(stdout, "Deleting chain `%s'\n", chain);
419 	return iptc_delete_chain(chain, handle);
420 }
421 
422 static int
list_entries(const xt_chainlabel chain,int rulenum,int verbose,int numeric,int expanded,int linenumbers,struct xtc_handle * handle)423 list_entries(const xt_chainlabel chain, int rulenum, int verbose, int numeric,
424 	     int expanded, int linenumbers, struct xtc_handle *handle)
425 {
426 	int found = 0;
427 	unsigned int format;
428 	const char *this;
429 
430 	format = FMT_OPTIONS;
431 	if (!verbose)
432 		format |= FMT_NOCOUNTS;
433 	else
434 		format |= FMT_VIA;
435 
436 	if (numeric)
437 		format |= FMT_NUMERIC;
438 
439 	if (!expanded)
440 		format |= FMT_KILOMEGAGIGA;
441 
442 	if (linenumbers)
443 		format |= FMT_LINENUMBERS;
444 
445 	for (this = iptc_first_chain(handle);
446 	     this;
447 	     this = iptc_next_chain(handle)) {
448 		const struct ipt_entry *i;
449 		unsigned int num;
450 
451 		if (chain && strcmp(chain, this) != 0)
452 			continue;
453 
454 		if (found) printf("\n");
455 
456 		if (!rulenum) {
457 			struct xt_counters counters;
458 			unsigned int urefs;
459 			const char *pol;
460 			int refs = -1;
461 
462 			pol = iptc_get_policy(this, &counters, handle);
463 			if (!pol && iptc_get_references(&urefs, this, handle))
464 				refs = urefs;
465 
466 			print_header(format, this, pol, &counters, refs, 0);
467 		}
468 		i = iptc_first_rule(this, handle);
469 
470 		num = 0;
471 		while (i) {
472 			num++;
473 			if (!rulenum || num == rulenum)
474 				print_firewall(i,
475 					       iptc_get_target(i, handle),
476 					       num,
477 					       format,
478 					       handle);
479 			i = iptc_next_rule(i, handle);
480 		}
481 		found = 1;
482 	}
483 
484 	errno = ENOENT;
485 	return found;
486 }
487 
488 #define IP_PARTS_NATIVE(n)			\
489 (unsigned int)((n)>>24)&0xFF,			\
490 (unsigned int)((n)>>16)&0xFF,			\
491 (unsigned int)((n)>>8)&0xFF,			\
492 (unsigned int)((n)&0xFF)
493 
494 #define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
495 
496 /* We want this to be readable, so only print out necessary fields.
497  * Because that's the kind of world I want to live in.
498  */
print_rule4(const struct ipt_entry * e,struct xtc_handle * h,const char * chain,int counters)499 void print_rule4(const struct ipt_entry *e,
500 		struct xtc_handle *h, const char *chain, int counters)
501 {
502 	const struct xt_entry_target *t;
503 	const char *target_name;
504 
505 	/* print counters for iptables-save */
506 	if (counters > 0)
507 		printf("[%llu:%llu] ", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
508 
509 	/* print chain name */
510 	printf("-A %s", chain);
511 
512 	/* Print IP part. */
513 	save_ipv4_addr('s', &e->ip.src, &e->ip.smsk,
514 		       e->ip.invflags & IPT_INV_SRCIP);
515 
516 	save_ipv4_addr('d', &e->ip.dst, &e->ip.dmsk,
517 			e->ip.invflags & IPT_INV_DSTIP);
518 
519 	save_rule_details(e->ip.iniface, e->ip.iniface_mask,
520 			  e->ip.outiface, e->ip.outiface_mask,
521 			  e->ip.proto, e->ip.flags & IPT_F_FRAG,
522 			  e->ip.invflags);
523 
524 	/* Print matchinfo part */
525 	if (e->target_offset)
526 		IPT_MATCH_ITERATE(e, print_match_save, &e->ip);
527 
528 	/* print counters for iptables -R */
529 	if (counters < 0)
530 		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
531 
532 	/* Print target name and targinfo part */
533 	target_name = iptc_get_target(e, h);
534 	t = ipt_get_target((struct ipt_entry *)e);
535 	if (t->u.user.name[0]) {
536 		const char *name = t->u.user.name;
537 		const int revision = t->u.user.revision;
538 		struct xtables_target *target, *tg, *tg2;
539 
540 		target = xtables_find_target(name, XTF_TRY_LOAD);
541 		if (!target) {
542 			fprintf(stderr, "Can't find library for target `%s'\n",
543 				name);
544 			exit(1);
545 		}
546 
547 		tg = tg2 = xtables_find_target_revision(name, XTF_TRY_LOAD,
548 							target, revision);
549 		if (!tg2)
550 			tg2 = target;
551 		printf(" -j %s", tg2->alias ? tg2->alias(t) : target_name);
552 
553 		if (tg && tg->save)
554 			tg->save(&e->ip, t);
555 		else if (target->save)
556 			printf(unsupported_rev);
557 		else {
558 			/* If the target size is greater than xt_entry_target
559 			 * there is something to be saved, we just don't know
560 			 * how to print it */
561 			if (t->u.target_size !=
562 			    sizeof(struct xt_entry_target)) {
563 				fprintf(stderr, "Target `%s' is missing "
564 						"save function\n",
565 					name);
566 				exit(1);
567 			}
568 		}
569 	} else if (target_name && (*target_name != '\0'))
570 #ifdef IPT_F_GOTO
571 		printf(" -%c %s", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
572 #else
573 		printf(" -j %s", target_name);
574 #endif
575 
576 	printf("\n");
577 }
578 
579 static int
list_rules(const xt_chainlabel chain,int rulenum,int counters,struct xtc_handle * handle)580 list_rules(const xt_chainlabel chain, int rulenum, int counters,
581 	     struct xtc_handle *handle)
582 {
583 	const char *this = NULL;
584 	int found = 0;
585 
586 	if (counters)
587 	    counters = -1;		/* iptables -c format */
588 
589 	/* Dump out chain names first,
590 	 * thereby preventing dependency conflicts */
591 	if (!rulenum) for (this = iptc_first_chain(handle);
592 	     this;
593 	     this = iptc_next_chain(handle)) {
594 		if (chain && strcmp(this, chain) != 0)
595 			continue;
596 
597 		if (iptc_builtin(this, handle)) {
598 			struct xt_counters count;
599 			printf("-P %s %s", this, iptc_get_policy(this, &count, handle));
600 			if (counters)
601 			    printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
602 			printf("\n");
603 		} else {
604 			printf("-N %s\n", this);
605 		}
606 	}
607 
608 	for (this = iptc_first_chain(handle);
609 	     this;
610 	     this = iptc_next_chain(handle)) {
611 		const struct ipt_entry *e;
612 		int num = 0;
613 
614 		if (chain && strcmp(this, chain) != 0)
615 			continue;
616 
617 		/* Dump out rules */
618 		e = iptc_first_rule(this, handle);
619 		while(e) {
620 			num++;
621 			if (!rulenum || num == rulenum)
622 			    print_rule4(e, handle, this, counters);
623 			e = iptc_next_rule(e, handle);
624 		}
625 		found = 1;
626 	}
627 
628 	errno = ENOENT;
629 	return found;
630 }
631 
632 static struct ipt_entry *
generate_entry(const struct ipt_entry * fw,struct xtables_rule_match * matches,struct xt_entry_target * target)633 generate_entry(const struct ipt_entry *fw,
634 	       struct xtables_rule_match *matches,
635 	       struct xt_entry_target *target)
636 {
637 	unsigned int size;
638 	struct xtables_rule_match *matchp;
639 	struct ipt_entry *e;
640 
641 	size = sizeof(struct ipt_entry);
642 	for (matchp = matches; matchp; matchp = matchp->next)
643 		size += matchp->match->m->u.match_size;
644 
645 	e = xtables_malloc(size + target->u.target_size);
646 	*e = *fw;
647 	e->target_offset = size;
648 	e->next_offset = size + target->u.target_size;
649 
650 	size = 0;
651 	for (matchp = matches; matchp; matchp = matchp->next) {
652 		memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
653 		size += matchp->match->m->u.match_size;
654 	}
655 	memcpy(e->elems + size, target, target->u.target_size);
656 
657 	return e;
658 }
659 
do_command4(int argc,char * argv[],char ** table,struct xtc_handle ** handle,bool restore)660 int do_command4(int argc, char *argv[], char **table,
661 		struct xtc_handle **handle, bool restore)
662 {
663 	struct xt_cmd_parse_ops cmd_parse_ops = {
664 		.proto_parse	= ipv4_proto_parse,
665 		.post_parse	= ipv4_post_parse,
666 	};
667 	struct xt_cmd_parse p = {
668 		.table		= *table,
669 		.restore	= restore,
670 		.line		= line,
671 		.ops		= &cmd_parse_ops,
672 	};
673 	struct iptables_command_state cs = {
674 		.jumpto	= "",
675 		.argv	= argv,
676 	};
677 	struct xtables_args args = {
678 		.family = AF_INET,
679 	};
680 	struct ipt_entry *e = NULL;
681 	unsigned int nsaddrs = 0, ndaddrs = 0;
682 	struct in_addr *saddrs = NULL, *smasks = NULL;
683 	struct in_addr *daddrs = NULL, *dmasks = NULL;
684 	int verbose = 0;
685 	int wait = 0;
686 	const char *chain = NULL;
687 	const char *policy = NULL, *newname = NULL;
688 	unsigned int rulenum = 0, command = 0;
689 	int ret = 1;
690 
691 	do_parse(argc, argv, &p, &cs, &args);
692 
693 	command		= p.command;
694 	chain		= p.chain;
695 	*table		= p.table;
696 	rulenum		= p.rulenum;
697 	policy		= p.policy;
698 	newname		= p.newname;
699 	verbose		= p.verbose;
700 	wait		= args.wait;
701 	nsaddrs		= args.s.naddrs;
702 	ndaddrs		= args.d.naddrs;
703 	saddrs		= args.s.addr.v4;
704 	daddrs		= args.d.addr.v4;
705 	smasks		= args.s.mask.v4;
706 	dmasks		= args.d.mask.v4;
707 
708 	/* Attempt to acquire the xtables lock */
709 	if (!restore)
710 		xtables_lock_or_exit(wait);
711 
712 	/* only allocate handle if we weren't called with a handle */
713 	if (!*handle)
714 		*handle = iptc_init(*table);
715 
716 	/* try to insmod the module if iptc_init failed */
717 	if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1)
718 		*handle = iptc_init(*table);
719 
720 	if (!*handle)
721 		xtables_error(VERSION_PROBLEM,
722 			   "can't initialize iptables table `%s': %s",
723 			   *table, iptc_strerror(errno));
724 
725 	if (command == CMD_APPEND
726 	    || command == CMD_DELETE
727 	    || command == CMD_CHECK
728 	    || command == CMD_INSERT
729 	    || command == CMD_REPLACE) {
730 		if (cs.target && iptc_is_chain(cs.jumpto, *handle)) {
731 			fprintf(stderr,
732 				"Warning: using chain %s, not extension\n",
733 				cs.jumpto);
734 
735 			if (cs.target->t)
736 				free(cs.target->t);
737 
738 			cs.target = NULL;
739 		}
740 
741 		/* If they didn't specify a target, or it's a chain
742 		   name, use standard. */
743 		if (!cs.target
744 		    && (strlen(cs.jumpto) == 0
745 			|| iptc_is_chain(cs.jumpto, *handle))) {
746 			size_t size;
747 
748 			cs.target = xtables_find_target(XT_STANDARD_TARGET,
749 					 XTF_LOAD_MUST_SUCCEED);
750 
751 			size = sizeof(struct xt_entry_target)
752 				+ cs.target->size;
753 			cs.target->t = xtables_calloc(1, size);
754 			cs.target->t->u.target_size = size;
755 			strcpy(cs.target->t->u.user.name, cs.jumpto);
756 			if (!iptc_is_chain(cs.jumpto, *handle))
757 				cs.target->t->u.user.revision = cs.target->revision;
758 			xs_init_target(cs.target);
759 		}
760 
761 		if (!cs.target) {
762 			/* It is no chain, and we can't load a plugin.
763 			 * We cannot know if the plugin is corrupt, non
764 			 * existent OR if the user just misspelled a
765 			 * chain.
766 			 */
767 #ifdef IPT_F_GOTO
768 			if (cs.fw.ip.flags & IPT_F_GOTO)
769 				xtables_error(PARAMETER_PROBLEM,
770 					      "goto '%s' is not a chain",
771 					      cs.jumpto);
772 #endif
773 			xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
774 		} else {
775 			e = generate_entry(&cs.fw, cs.matches, cs.target->t);
776 		}
777 	}
778 
779 	switch (command) {
780 	case CMD_APPEND:
781 		ret = append_entry(chain, e,
782 				   nsaddrs, saddrs, smasks,
783 				   ndaddrs, daddrs, dmasks,
784 				   cs.options&OPT_VERBOSE,
785 				   *handle);
786 		break;
787 	case CMD_DELETE:
788 		ret = delete_entry(chain, e,
789 				   nsaddrs, saddrs, smasks,
790 				   ndaddrs, daddrs, dmasks,
791 				   cs.options&OPT_VERBOSE,
792 				   *handle, cs.matches, cs.target);
793 		break;
794 	case CMD_DELETE_NUM:
795 		ret = iptc_delete_num_entry(chain, rulenum - 1, *handle);
796 		break;
797 	case CMD_CHECK:
798 		ret = check_entry(chain, e,
799 				   nsaddrs, saddrs, smasks,
800 				   ndaddrs, daddrs, dmasks,
801 				   cs.options&OPT_VERBOSE,
802 				   *handle, cs.matches, cs.target);
803 		break;
804 	case CMD_REPLACE:
805 		ret = replace_entry(chain, e, rulenum - 1,
806 				    saddrs, smasks, daddrs, dmasks,
807 				    cs.options&OPT_VERBOSE, *handle);
808 		break;
809 	case CMD_INSERT:
810 		ret = insert_entry(chain, e, rulenum - 1,
811 				   nsaddrs, saddrs, smasks,
812 				   ndaddrs, daddrs, dmasks,
813 				   cs.options&OPT_VERBOSE,
814 				   *handle);
815 		break;
816 	case CMD_FLUSH:
817 		ret = flush_entries4(chain, cs.options&OPT_VERBOSE, *handle);
818 		break;
819 	case CMD_ZERO:
820 		ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle);
821 		break;
822 	case CMD_ZERO_NUM:
823 		ret = iptc_zero_counter(chain, rulenum, *handle);
824 		break;
825 	case CMD_LIST:
826 	case CMD_LIST|CMD_ZERO:
827 	case CMD_LIST|CMD_ZERO_NUM:
828 		ret = list_entries(chain,
829 				   rulenum,
830 				   cs.options&OPT_VERBOSE,
831 				   cs.options&OPT_NUMERIC,
832 				   cs.options&OPT_EXPANDED,
833 				   cs.options&OPT_LINENUMBERS,
834 				   *handle);
835 		if (ret && (command & CMD_ZERO))
836 			ret = zero_entries(chain,
837 					   cs.options&OPT_VERBOSE, *handle);
838 		if (ret && (command & CMD_ZERO_NUM))
839 			ret = iptc_zero_counter(chain, rulenum, *handle);
840 		break;
841 	case CMD_LIST_RULES:
842 	case CMD_LIST_RULES|CMD_ZERO:
843 	case CMD_LIST_RULES|CMD_ZERO_NUM:
844 		ret = list_rules(chain,
845 				   rulenum,
846 				   cs.options&OPT_VERBOSE,
847 				   *handle);
848 		if (ret && (command & CMD_ZERO))
849 			ret = zero_entries(chain,
850 					   cs.options&OPT_VERBOSE, *handle);
851 		if (ret && (command & CMD_ZERO_NUM))
852 			ret = iptc_zero_counter(chain, rulenum, *handle);
853 		break;
854 	case CMD_NEW_CHAIN:
855 		ret = iptc_create_chain(chain, *handle);
856 		break;
857 	case CMD_DELETE_CHAIN:
858 		ret = delete_chain4(chain, cs.options&OPT_VERBOSE, *handle);
859 		break;
860 	case CMD_RENAME_CHAIN:
861 		ret = iptc_rename_chain(chain, newname,	*handle);
862 		break;
863 	case CMD_SET_POLICY:
864 		ret = iptc_set_policy(chain, policy, cs.options&OPT_COUNTERS ? &cs.fw.counters : NULL, *handle);
865 		break;
866 	case CMD_NONE:
867 		/* do_parse ignored the line (eg: -4 with ip6tables-restore) */
868 		break;
869 	default:
870 		/* We should never reach this... */
871 		exit_tryhelp(2, line);
872 	}
873 
874 	if (verbose > 1)
875 		dump_entries(*handle);
876 
877 	xtables_clear_iptables_command_state(&cs);
878 
879 	if (e != NULL) {
880 		free(e);
881 		e = NULL;
882 	}
883 
884 	free(saddrs);
885 	free(smasks);
886 	free(daddrs);
887 	free(dmasks);
888 	xtables_free_opts(1);
889 
890 	return ret;
891 }
892