• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Code to take an iptables-style command line and do it. */
2 
3 /*
4  * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
5  *
6  * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
7  * 		    Paul 'Rusty' Russell <rusty@rustcorp.com.au>
8  * 		    Marc Boucher <marc+nf@mbsi.ca>
9  * 		    James Morris <jmorris@intercode.com.au>
10  * 		    Harald Welte <laforge@gnumonks.org>
11  * 		    Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
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.outiface,
520 			  e->ip.proto, e->ip.flags & IPT_F_FRAG,
521 			  e->ip.invflags);
522 
523 	/* Print matchinfo part */
524 	if (e->target_offset)
525 		IPT_MATCH_ITERATE(e, print_match_save, &e->ip);
526 
527 	/* print counters for iptables -R */
528 	if (counters < 0)
529 		printf(" -c %llu %llu", (unsigned long long)e->counters.pcnt, (unsigned long long)e->counters.bcnt);
530 
531 	/* Print target name and targinfo part */
532 	target_name = iptc_get_target(e, h);
533 	t = ipt_get_target((struct ipt_entry *)e);
534 	if (t->u.user.name[0]) {
535 		const char *name = t->u.user.name;
536 		const int revision = t->u.user.revision;
537 		struct xtables_target *target, *tg, *tg2;
538 
539 		target = xtables_find_target(name, XTF_TRY_LOAD);
540 		if (!target) {
541 			fprintf(stderr, "Can't find library for target `%s'\n",
542 				name);
543 			exit(1);
544 		}
545 
546 		tg = tg2 = xtables_find_target_revision(name, XTF_TRY_LOAD,
547 							target, revision);
548 		if (!tg2)
549 			tg2 = target;
550 		printf(" -j %s", tg2->alias ? tg2->alias(t) : target_name);
551 
552 		if (tg && tg->save)
553 			tg->save(&e->ip, t);
554 		else if (target->save)
555 			printf(unsupported_rev);
556 		else {
557 			/* If the target size is greater than xt_entry_target
558 			 * there is something to be saved, we just don't know
559 			 * how to print it */
560 			if (t->u.target_size !=
561 			    sizeof(struct xt_entry_target)) {
562 				fprintf(stderr, "Target `%s' is missing "
563 						"save function\n",
564 					name);
565 				exit(1);
566 			}
567 		}
568 	} else if (target_name && (*target_name != '\0'))
569 #ifdef IPT_F_GOTO
570 		printf(" -%c %s", e->ip.flags & IPT_F_GOTO ? 'g' : 'j', target_name);
571 #else
572 		printf(" -j %s", target_name);
573 #endif
574 
575 	printf("\n");
576 }
577 
578 static int
list_rules(const xt_chainlabel chain,int rulenum,int counters,struct xtc_handle * handle)579 list_rules(const xt_chainlabel chain, int rulenum, int counters,
580 	     struct xtc_handle *handle)
581 {
582 	const char *this = NULL;
583 	int found = 0;
584 
585 	if (counters)
586 	    counters = -1;		/* iptables -c format */
587 
588 	/* Dump out chain names first,
589 	 * thereby preventing dependency conflicts */
590 	if (!rulenum) for (this = iptc_first_chain(handle);
591 	     this;
592 	     this = iptc_next_chain(handle)) {
593 		if (chain && strcmp(this, chain) != 0)
594 			continue;
595 
596 		if (iptc_builtin(this, handle)) {
597 			struct xt_counters count;
598 			printf("-P %s %s", this, iptc_get_policy(this, &count, handle));
599 			if (counters)
600 			    printf(" -c %llu %llu", (unsigned long long)count.pcnt, (unsigned long long)count.bcnt);
601 			printf("\n");
602 		} else {
603 			printf("-N %s\n", this);
604 		}
605 	}
606 
607 	for (this = iptc_first_chain(handle);
608 	     this;
609 	     this = iptc_next_chain(handle)) {
610 		const struct ipt_entry *e;
611 		int num = 0;
612 
613 		if (chain && strcmp(this, chain) != 0)
614 			continue;
615 
616 		/* Dump out rules */
617 		e = iptc_first_rule(this, handle);
618 		while(e) {
619 			num++;
620 			if (!rulenum || num == rulenum)
621 			    print_rule4(e, handle, this, counters);
622 			e = iptc_next_rule(e, handle);
623 		}
624 		found = 1;
625 	}
626 
627 	errno = ENOENT;
628 	return found;
629 }
630 
631 static struct ipt_entry *
generate_entry(const struct ipt_entry * fw,struct xtables_rule_match * matches,struct xt_entry_target * target)632 generate_entry(const struct ipt_entry *fw,
633 	       struct xtables_rule_match *matches,
634 	       struct xt_entry_target *target)
635 {
636 	unsigned int size;
637 	struct xtables_rule_match *matchp;
638 	struct ipt_entry *e;
639 
640 	size = sizeof(struct ipt_entry);
641 	for (matchp = matches; matchp; matchp = matchp->next)
642 		size += matchp->match->m->u.match_size;
643 
644 	e = xtables_malloc(size + target->u.target_size);
645 	*e = *fw;
646 	e->target_offset = size;
647 	e->next_offset = size + target->u.target_size;
648 
649 	size = 0;
650 	for (matchp = matches; matchp; matchp = matchp->next) {
651 		memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size);
652 		size += matchp->match->m->u.match_size;
653 	}
654 	memcpy(e->elems + size, target, target->u.target_size);
655 
656 	return e;
657 }
658 
do_command4(int argc,char * argv[],char ** table,struct xtc_handle ** handle,bool restore)659 int do_command4(int argc, char *argv[], char **table,
660 		struct xtc_handle **handle, bool restore)
661 {
662 	struct xt_cmd_parse_ops cmd_parse_ops = {
663 		.proto_parse	= ipv4_proto_parse,
664 		.post_parse	= ipv4_post_parse,
665 		.option_name	= ip46t_option_name,
666 		.option_invert	= ip46t_option_invert,
667 		.command_default = command_default,
668 		.print_help	= xtables_printhelp,
669 	};
670 	struct xt_cmd_parse p = {
671 		.table		= *table,
672 		.restore	= restore,
673 		.line		= line,
674 		.ops		= &cmd_parse_ops,
675 	};
676 	struct iptables_command_state cs = {
677 		.jumpto	= "",
678 		.argv	= argv,
679 	};
680 	struct xtables_args args = {
681 		.family = AF_INET,
682 	};
683 	struct ipt_entry *e = NULL;
684 	unsigned int nsaddrs = 0, ndaddrs = 0;
685 	struct in_addr *saddrs = NULL, *smasks = NULL;
686 	struct in_addr *daddrs = NULL, *dmasks = NULL;
687 	int verbose = 0;
688 	int wait = 0;
689 	const char *chain = NULL;
690 	const char *policy = NULL, *newname = NULL;
691 	unsigned int rulenum = 0, command = 0;
692 	int ret = 1;
693 
694 	do_parse(argc, argv, &p, &cs, &args);
695 
696 	command		= p.command;
697 	chain		= p.chain;
698 	*table		= p.table;
699 	rulenum		= p.rulenum;
700 	policy		= p.policy;
701 	newname		= p.newname;
702 	verbose		= p.verbose;
703 	wait		= args.wait;
704 	nsaddrs		= args.s.naddrs;
705 	ndaddrs		= args.d.naddrs;
706 	saddrs		= args.s.addr.v4;
707 	daddrs		= args.d.addr.v4;
708 	smasks		= args.s.mask.v4;
709 	dmasks		= args.d.mask.v4;
710 
711 	iface_to_mask(cs.fw.ip.iniface, cs.fw.ip.iniface_mask);
712 	iface_to_mask(cs.fw.ip.outiface, cs.fw.ip.outiface_mask);
713 
714 	/* Attempt to acquire the xtables lock */
715 	if (!restore)
716 		xtables_lock_or_exit(wait);
717 
718 	/* only allocate handle if we weren't called with a handle */
719 	if (!*handle)
720 		*handle = iptc_init(*table);
721 
722 	/* try to insmod the module if iptc_init failed */
723 	if (!*handle && xtables_load_ko(xtables_modprobe_program, false) != -1)
724 		*handle = iptc_init(*table);
725 
726 	if (!*handle)
727 		xtables_error(VERSION_PROBLEM,
728 			   "can't initialize iptables table `%s': %s",
729 			   *table, iptc_strerror(errno));
730 
731 	if (command == CMD_APPEND
732 	    || command == CMD_DELETE
733 	    || command == CMD_CHECK
734 	    || command == CMD_INSERT
735 	    || command == CMD_REPLACE) {
736 		if (cs.target && iptc_is_chain(cs.jumpto, *handle)) {
737 			fprintf(stderr,
738 				"Warning: using chain %s, not extension\n",
739 				cs.jumpto);
740 
741 			if (cs.target->t)
742 				free(cs.target->t);
743 
744 			cs.target = NULL;
745 		}
746 
747 		/* If they didn't specify a target, or it's a chain
748 		   name, use standard. */
749 		if (!cs.target
750 		    && (strlen(cs.jumpto) == 0
751 			|| iptc_is_chain(cs.jumpto, *handle))) {
752 			size_t size;
753 
754 			cs.target = xtables_find_target(XT_STANDARD_TARGET,
755 					 XTF_LOAD_MUST_SUCCEED);
756 
757 			size = sizeof(struct xt_entry_target)
758 				+ cs.target->size;
759 			cs.target->t = xtables_calloc(1, size);
760 			cs.target->t->u.target_size = size;
761 			strcpy(cs.target->t->u.user.name, cs.jumpto);
762 			if (!iptc_is_chain(cs.jumpto, *handle))
763 				cs.target->t->u.user.revision = cs.target->revision;
764 			xs_init_target(cs.target);
765 		}
766 
767 		if (!cs.target) {
768 			/* It is no chain, and we can't load a plugin.
769 			 * We cannot know if the plugin is corrupt, non
770 			 * existent OR if the user just misspelled a
771 			 * chain.
772 			 */
773 #ifdef IPT_F_GOTO
774 			if (cs.fw.ip.flags & IPT_F_GOTO)
775 				xtables_error(PARAMETER_PROBLEM,
776 					      "goto '%s' is not a chain",
777 					      cs.jumpto);
778 #endif
779 			xtables_find_target(cs.jumpto, XTF_LOAD_MUST_SUCCEED);
780 		} else {
781 			e = generate_entry(&cs.fw, cs.matches, cs.target->t);
782 		}
783 	}
784 
785 	switch (command) {
786 	case CMD_APPEND:
787 		ret = append_entry(chain, e,
788 				   nsaddrs, saddrs, smasks,
789 				   ndaddrs, daddrs, dmasks,
790 				   cs.options&OPT_VERBOSE,
791 				   *handle);
792 		break;
793 	case CMD_DELETE:
794 		ret = delete_entry(chain, e,
795 				   nsaddrs, saddrs, smasks,
796 				   ndaddrs, daddrs, dmasks,
797 				   cs.options&OPT_VERBOSE,
798 				   *handle, cs.matches, cs.target);
799 		break;
800 	case CMD_DELETE_NUM:
801 		ret = iptc_delete_num_entry(chain, rulenum - 1, *handle);
802 		break;
803 	case CMD_CHECK:
804 		ret = check_entry(chain, e,
805 				   nsaddrs, saddrs, smasks,
806 				   ndaddrs, daddrs, dmasks,
807 				   cs.options&OPT_VERBOSE,
808 				   *handle, cs.matches, cs.target);
809 		break;
810 	case CMD_REPLACE:
811 		ret = replace_entry(chain, e, rulenum - 1,
812 				    saddrs, smasks, daddrs, dmasks,
813 				    cs.options&OPT_VERBOSE, *handle);
814 		break;
815 	case CMD_INSERT:
816 		ret = insert_entry(chain, e, rulenum - 1,
817 				   nsaddrs, saddrs, smasks,
818 				   ndaddrs, daddrs, dmasks,
819 				   cs.options&OPT_VERBOSE,
820 				   *handle);
821 		break;
822 	case CMD_FLUSH:
823 		ret = flush_entries4(chain, cs.options&OPT_VERBOSE, *handle);
824 		break;
825 	case CMD_ZERO:
826 		ret = zero_entries(chain, cs.options&OPT_VERBOSE, *handle);
827 		break;
828 	case CMD_ZERO_NUM:
829 		ret = iptc_zero_counter(chain, rulenum, *handle);
830 		break;
831 	case CMD_LIST:
832 	case CMD_LIST|CMD_ZERO:
833 	case CMD_LIST|CMD_ZERO_NUM:
834 		ret = list_entries(chain,
835 				   rulenum,
836 				   cs.options&OPT_VERBOSE,
837 				   cs.options&OPT_NUMERIC,
838 				   cs.options&OPT_EXPANDED,
839 				   cs.options&OPT_LINENUMBERS,
840 				   *handle);
841 		if (ret && (command & CMD_ZERO))
842 			ret = zero_entries(chain,
843 					   cs.options&OPT_VERBOSE, *handle);
844 		if (ret && (command & CMD_ZERO_NUM))
845 			ret = iptc_zero_counter(chain, rulenum, *handle);
846 		break;
847 	case CMD_LIST_RULES:
848 	case CMD_LIST_RULES|CMD_ZERO:
849 	case CMD_LIST_RULES|CMD_ZERO_NUM:
850 		ret = list_rules(chain,
851 				   rulenum,
852 				   cs.options&OPT_VERBOSE,
853 				   *handle);
854 		if (ret && (command & CMD_ZERO))
855 			ret = zero_entries(chain,
856 					   cs.options&OPT_VERBOSE, *handle);
857 		if (ret && (command & CMD_ZERO_NUM))
858 			ret = iptc_zero_counter(chain, rulenum, *handle);
859 		break;
860 	case CMD_NEW_CHAIN:
861 		ret = iptc_create_chain(chain, *handle);
862 		break;
863 	case CMD_DELETE_CHAIN:
864 		ret = delete_chain4(chain, cs.options&OPT_VERBOSE, *handle);
865 		break;
866 	case CMD_RENAME_CHAIN:
867 		ret = iptc_rename_chain(chain, newname,	*handle);
868 		break;
869 	case CMD_SET_POLICY:
870 		ret = iptc_set_policy(chain, policy, cs.options&OPT_COUNTERS ? &cs.fw.counters : NULL, *handle);
871 		break;
872 	case CMD_NONE:
873 		/* do_parse ignored the line (eg: -4 with ip6tables-restore) */
874 		break;
875 	default:
876 		/* We should never reach this... */
877 		exit_tryhelp(2, line);
878 	}
879 
880 	if (verbose > 1)
881 		dump_entries(*handle);
882 
883 	xtables_clear_iptables_command_state(&cs);
884 
885 	if (e != NULL) {
886 		free(e);
887 		e = NULL;
888 	}
889 
890 	xtables_clear_args(&args);
891 	xtables_free_opts(1);
892 
893 	return ret;
894 }
895