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
28#include <getopt.h>
29#include <string.h>
30#include <netdb.h>
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <dlfcn.h>
35#include <ctype.h>
36#include <stdarg.h>
37#include <limits.h>
38#include <unistd.h>
39#include <iptables.h>
40#include <fcntl.h>
41#include <sys/wait.h>
42
43#ifndef TRUE
44#define TRUE 1
45#endif
46#ifndef FALSE
47#define FALSE 0
48#endif
49
50#ifndef IPT_LIB_DIR
51#define IPT_LIB_DIR "/usr/local/lib/iptables"
52#endif
53
54#ifndef PROC_SYS_MODPROBE
55#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
56#endif
57
58#define FMT_NUMERIC	0x0001
59#define FMT_NOCOUNTS	0x0002
60#define FMT_KILOMEGAGIGA 0x0004
61#define FMT_OPTIONS	0x0008
62#define FMT_NOTABLE	0x0010
63#define FMT_NOTARGET	0x0020
64#define FMT_VIA		0x0040
65#define FMT_NONEWLINE	0x0080
66#define FMT_LINENUMBERS 0x0100
67
68#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
69			| FMT_NUMERIC | FMT_NOTABLE)
70#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
71
72
73#define CMD_NONE		0x0000U
74#define CMD_INSERT		0x0001U
75#define CMD_DELETE		0x0002U
76#define CMD_DELETE_NUM		0x0004U
77#define CMD_REPLACE		0x0008U
78#define CMD_APPEND		0x0010U
79#define CMD_LIST		0x0020U
80#define CMD_FLUSH		0x0040U
81#define CMD_ZERO		0x0080U
82#define CMD_NEW_CHAIN		0x0100U
83#define CMD_DELETE_CHAIN	0x0200U
84#define CMD_SET_POLICY		0x0400U
85#define CMD_CHECK		0x0800U
86#define CMD_RENAME_CHAIN	0x1000U
87#define NUMBER_OF_CMD	13
88static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
89				 'N', 'X', 'P', 'E' };
90
91#define OPTION_OFFSET 256
92
93#define OPT_NONE	0x00000U
94#define OPT_NUMERIC	0x00001U
95#define OPT_SOURCE	0x00002U
96#define OPT_DESTINATION	0x00004U
97#define OPT_PROTOCOL	0x00008U
98#define OPT_JUMP	0x00010U
99#define OPT_VERBOSE	0x00020U
100#define OPT_EXPANDED	0x00040U
101#define OPT_VIANAMEIN	0x00080U
102#define OPT_VIANAMEOUT	0x00100U
103#define OPT_FRAGMENT    0x00200U
104#define OPT_LINENUMBERS 0x00400U
105#define OPT_COUNTERS	0x00800U
106#define NUMBER_OF_OPT	12
107static const char optflags[NUMBER_OF_OPT]
108= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
109
110static struct option original_opts[] = {
111	{ "append", 1, 0, 'A' },
112	{ "delete", 1, 0,  'D' },
113	{ "insert", 1, 0,  'I' },
114	{ "replace", 1, 0,  'R' },
115	{ "list", 2, 0,  'L' },
116	{ "flush", 2, 0,  'F' },
117	{ "zero", 2, 0,  'Z' },
118	{ "new-chain", 1, 0,  'N' },
119	{ "delete-chain", 2, 0,  'X' },
120	{ "rename-chain", 2, 0,  'E' },
121	{ "policy", 1, 0,  'P' },
122	{ "source", 1, 0, 's' },
123	{ "destination", 1, 0,  'd' },
124	{ "src", 1, 0,  's' }, /* synonym */
125	{ "dst", 1, 0,  'd' }, /* synonym */
126	{ "protocol", 1, 0,  'p' },
127	{ "in-interface", 1, 0, 'i' },
128	{ "jump", 1, 0, 'j' },
129	{ "table", 1, 0, 't' },
130	{ "match", 1, 0, 'm' },
131	{ "numeric", 0, 0, 'n' },
132	{ "out-interface", 1, 0, 'o' },
133	{ "verbose", 0, 0, 'v' },
134	{ "exact", 0, 0, 'x' },
135	{ "fragments", 0, 0, 'f' },
136	{ "version", 0, 0, 'V' },
137	{ "help", 2, 0, 'h' },
138	{ "line-numbers", 0, 0, '0' },
139	{ "modprobe", 1, 0, 'M' },
140	{ "set-counters", 1, 0, 'c' },
141	{ 0 }
142};
143
144#ifndef __OPTIMIZE__
145struct ipt_entry_target *
146ipt_get_target(struct ipt_entry *e)
147{
148	return (void *)e + e->target_offset;
149}
150#endif
151
152static struct option *opts = original_opts;
153static unsigned int global_option_offset = 0;
154
155/* Table of legal combinations of commands and options.  If any of the
156 * given commands make an option legal, that option is legal (applies to
157 * CMD_LIST and CMD_ZERO only).
158 * Key:
159 *  +  compulsory
160 *  x  illegal
161 *     optional
162 */
163
164static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
165/* Well, it's better than "Re: Linux vs FreeBSD" */
166{
167	/*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
168/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
169/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
170/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
171/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
172/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
173/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
174/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
175/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
176/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
177/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
178/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
179/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
180/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
181};
182
183static int inverse_for_options[NUMBER_OF_OPT] =
184{
185/* -n */ 0,
186/* -s */ IPT_INV_SRCIP,
187/* -d */ IPT_INV_DSTIP,
188/* -p */ IPT_INV_PROTO,
189/* -j */ 0,
190/* -v */ 0,
191/* -x */ 0,
192/* -i */ IPT_INV_VIA_IN,
193/* -o */ IPT_INV_VIA_OUT,
194/* -f */ IPT_INV_FRAG,
195/*--line*/ 0
196};
197
198const char *program_version;
199const char *program_name;
200
201/* Keeping track of external matches and targets: linked lists.  */
202struct iptables_match *iptables_matches = NULL;
203struct iptables_target *iptables_targets = NULL;
204
205/* Extra debugging from libiptc */
206extern void dump_entries(const iptc_handle_t handle);
207
208/* A few hardcoded protocols for 'all' and in case the user has no
209   /etc/protocols */
210struct pprot {
211	char *name;
212	u_int8_t num;
213};
214
215/* Primitive headers... */
216/* defined in netinet/in.h */
217
218static const struct pprot chain_protos[] = {
219	{ "tcp", IPPROTO_TCP },
220	{ "udp", IPPROTO_UDP },
221	{ "icmp", IPPROTO_ICMP },
222	{ "esp", IPPROTO_ESP },
223	{ "ah", IPPROTO_AH },
224	{ "all", 0 },
225};
226
227static char *
228proto_to_name(u_int8_t proto, int nolookup)
229{
230	unsigned int i;
231
232	if (proto && !nolookup) {
233		struct protoent *pent = getprotobynumber(proto);
234		if (pent)
235			return pent->p_name;
236	}
237
238	for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
239		if (chain_protos[i].num == proto)
240			return chain_protos[i].name;
241
242	return NULL;
243}
244
245struct in_addr *
246dotted_to_addr(const char *dotted)
247{
248	static struct in_addr addr;
249	unsigned char *addrp;
250	char *p, *q;
251	unsigned int onebyte;
252	int i;
253	char buf[20];
254
255	/* copy dotted string, because we need to modify it */
256	strncpy(buf, dotted, sizeof(buf) - 1);
257	addrp = (unsigned char *) &(addr.s_addr);
258
259	p = buf;
260	for (i = 0; i < 3; i++) {
261		if ((q = strchr(p, '.')) == NULL)
262			return (struct in_addr *) NULL;
263
264		*q = '\0';
265		if (string_to_number(p, 0, 255, &onebyte) == -1)
266			return (struct in_addr *) NULL;
267
268		addrp[i] = (unsigned char) onebyte;
269		p = q + 1;
270	}
271
272	/* we've checked 3 bytes, now we check the last one */
273	if (string_to_number(p, 0, 255, &onebyte) == -1)
274		return (struct in_addr *) NULL;
275
276	addrp[3] = (unsigned char) onebyte;
277
278	return &addr;
279}
280
281static struct in_addr *
282network_to_addr(const char *name)
283{
284	struct netent *net;
285	static struct in_addr addr;
286
287	if ((net = getnetbyname(name)) != NULL) {
288		if (net->n_addrtype != AF_INET)
289			return (struct in_addr *) NULL;
290		addr.s_addr = htonl((unsigned long) net->n_net);
291		return &addr;
292	}
293
294	return (struct in_addr *) NULL;
295}
296
297static void
298inaddrcpy(struct in_addr *dst, struct in_addr *src)
299{
300	/* memcpy(dst, src, sizeof(struct in_addr)); */
301	dst->s_addr = src->s_addr;
302}
303
304void
305exit_error(enum exittype status, char *msg, ...)
306{
307	va_list args;
308
309	va_start(args, msg);
310	fprintf(stderr, "%s v%s: ", program_name, program_version);
311	vfprintf(stderr, msg, args);
312	va_end(args);
313	fprintf(stderr, "\n");
314	if (status == PARAMETER_PROBLEM)
315		exit_tryhelp(status);
316	if (status == VERSION_PROBLEM)
317		fprintf(stderr,
318			"Perhaps iptables or your kernel needs to be upgraded.\n");
319	exit(status);
320}
321
322void
323exit_tryhelp(int status)
324{
325	fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
326			program_name, program_name );
327	exit(status);
328}
329
330void
331exit_printhelp(void)
332{
333	struct iptables_match *m = NULL;
334	struct iptables_target *t = NULL;
335
336	printf("%s v%s\n\n"
337"Usage: %s -[AD] chain rule-specification [options]\n"
338"       %s -[RI] chain rulenum rule-specification [options]\n"
339"       %s -D chain rulenum [options]\n"
340"       %s -[LFZ] [chain] [options]\n"
341"       %s -[NX] chain\n"
342"       %s -E old-chain-name new-chain-name\n"
343"       %s -P chain target [options]\n"
344"       %s -h (print this help information)\n\n",
345	       program_name, program_version, program_name, program_name,
346	       program_name, program_name, program_name, program_name,
347	       program_name, program_name);
348
349	printf(
350"Commands:\n"
351"Either long or short options are allowed.\n"
352"  --append  -A chain		Append to chain\n"
353"  --delete  -D chain		Delete matching rule from chain\n"
354"  --delete  -D chain rulenum\n"
355"				Delete rule rulenum (1 = first) from chain\n"
356"  --insert  -I chain [rulenum]\n"
357"				Insert in chain as rulenum (default 1=first)\n"
358"  --replace -R chain rulenum\n"
359"				Replace rule rulenum (1 = first) in chain\n"
360"  --list    -L [chain]		List the rules in a chain or all chains\n"
361"  --flush   -F [chain]		Delete all rules in  chain or all chains\n"
362"  --zero    -Z [chain]		Zero counters in chain or all chains\n"
363"  --new     -N chain		Create a new user-defined chain\n"
364"  --delete-chain\n"
365"            -X [chain]		Delete a user-defined chain\n"
366"  --policy  -P chain target\n"
367"				Change policy on chain to target\n"
368"  --rename-chain\n"
369"            -E old-chain new-chain\n"
370"				Change chain name, (moving any references)\n"
371
372"Options:\n"
373"  --proto	-p [!] proto	protocol: by number or name, eg. `tcp'\n"
374"  --source	-s [!] address[/mask]\n"
375"				source specification\n"
376"  --destination -d [!] address[/mask]\n"
377"				destination specification\n"
378"  --in-interface -i [!] input name[+]\n"
379"				network interface name ([+] for wildcard)\n"
380"  --jump	-j target\n"
381"				target for rule (may load target extension)\n"
382"  --match	-m match\n"
383"				extended match (may load extension)\n"
384"  --numeric	-n		numeric output of addresses and ports\n"
385"  --out-interface -o [!] output name[+]\n"
386"				network interface name ([+] for wildcard)\n"
387"  --table	-t table	table to manipulate (default: `filter')\n"
388"  --verbose	-v		verbose mode\n"
389"  --line-numbers		print line numbers when listing\n"
390"  --exact	-x		expand numbers (display exact values)\n"
391"[!] --fragment	-f		match second or further fragments only\n"
392"  --modprobe=<command>		try to insert modules using this command\n"
393"  --set-counters PKTS BYTES	set the counter during insert/append\n"
394"[!] --version	-V		print package version.\n");
395
396	/* Print out any special helps. A user might like to be able
397	   to add a --help to the commandline, and see expected
398	   results. So we call help for all matches & targets */
399	for (t=iptables_targets;t;t=t->next) {
400		printf("\n");
401		t->help();
402	}
403	for (m=iptables_matches;m;m=m->next) {
404		printf("\n");
405		m->help();
406	}
407	exit(0);
408}
409
410static void
411generic_opt_check(int command, int options)
412{
413	int i, j, legal = 0;
414
415	/* Check that commands are valid with options.  Complicated by the
416	 * fact that if an option is legal with *any* command given, it is
417	 * legal overall (ie. -z and -l).
418	 */
419	for (i = 0; i < NUMBER_OF_OPT; i++) {
420		legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
421
422		for (j = 0; j < NUMBER_OF_CMD; j++) {
423			if (!(command & (1<<j)))
424				continue;
425
426			if (!(options & (1<<i))) {
427				if (commands_v_options[j][i] == '+')
428					exit_error(PARAMETER_PROBLEM,
429						   "You need to supply the `-%c' "
430						   "option for this command\n",
431						   optflags[i]);
432			} else {
433				if (commands_v_options[j][i] != 'x')
434					legal = 1;
435				else if (legal == 0)
436					legal = -1;
437			}
438		}
439		if (legal == -1)
440			exit_error(PARAMETER_PROBLEM,
441				   "Illegal option `-%c' with this command\n",
442				   optflags[i]);
443	}
444}
445
446static char
447opt2char(int option)
448{
449	const char *ptr;
450	for (ptr = optflags; option > 1; option >>= 1, ptr++);
451
452	return *ptr;
453}
454
455static char
456cmd2char(int option)
457{
458	const char *ptr;
459	for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
460
461	return *ptr;
462}
463
464static void
465add_command(int *cmd, const int newcmd, const int othercmds, int invert)
466{
467	if (invert)
468		exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
469	if (*cmd & (~othercmds))
470		exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
471			   cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
472	*cmd |= newcmd;
473}
474
475int
476check_inverse(const char option[], int *invert, int *optind, int argc)
477{
478	if (option && strcmp(option, "!") == 0) {
479		if (*invert)
480			exit_error(PARAMETER_PROBLEM,
481				   "Multiple `!' flags not allowed");
482		*invert = TRUE;
483		if (optind) {
484			*optind = *optind+1;
485			if (argc && *optind > argc)
486				exit_error(PARAMETER_PROBLEM,
487					   "no argument following `!'");
488		}
489
490		return TRUE;
491	}
492	return FALSE;
493}
494
495static void *
496fw_calloc(size_t count, size_t size)
497{
498	void *p;
499
500	if ((p = calloc(count, size)) == NULL) {
501		perror("iptables: calloc failed");
502		exit(1);
503	}
504	return p;
505}
506
507static void *
508fw_malloc(size_t size)
509{
510	void *p;
511
512	if ((p = malloc(size)) == NULL) {
513		perror("iptables: malloc failed");
514		exit(1);
515	}
516	return p;
517}
518
519static struct in_addr *
520host_to_addr(const char *name, unsigned int *naddr)
521{
522	struct hostent *host;
523	struct in_addr *addr;
524	unsigned int i;
525
526	*naddr = 0;
527	if ((host = gethostbyname(name)) != NULL) {
528		if (host->h_addrtype != AF_INET ||
529		    host->h_length != sizeof(struct in_addr))
530			return (struct in_addr *) NULL;
531
532		while (host->h_addr_list[*naddr] != (char *) NULL)
533			(*naddr)++;
534		addr = fw_calloc(*naddr, sizeof(struct in_addr));
535		for (i = 0; i < *naddr; i++)
536			inaddrcpy(&(addr[i]),
537				  (struct in_addr *) host->h_addr_list[i]);
538		return addr;
539	}
540
541	return (struct in_addr *) NULL;
542}
543
544static char *
545addr_to_host(const struct in_addr *addr)
546{
547	struct hostent *host;
548
549	if ((host = gethostbyaddr((char *) addr,
550				  sizeof(struct in_addr), AF_INET)) != NULL)
551		return (char *) host->h_name;
552
553	return (char *) NULL;
554}
555
556/*
557 *	All functions starting with "parse" should succeed, otherwise
558 *	the program fails.
559 *	Most routines return pointers to static data that may change
560 *	between calls to the same or other routines with a few exceptions:
561 *	"host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
562 *	return global static data.
563*/
564
565static struct in_addr *
566parse_hostnetwork(const char *name, unsigned int *naddrs)
567{
568	struct in_addr *addrp, *addrptmp;
569
570	if ((addrptmp = dotted_to_addr(name)) != NULL ||
571	    (addrptmp = network_to_addr(name)) != NULL) {
572		addrp = fw_malloc(sizeof(struct in_addr));
573		inaddrcpy(addrp, addrptmp);
574		*naddrs = 1;
575		return addrp;
576	}
577	if ((addrp = host_to_addr(name, naddrs)) != NULL)
578		return addrp;
579
580	exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
581}
582
583static struct in_addr *
584parse_mask(char *mask)
585{
586	static struct in_addr maskaddr;
587	struct in_addr *addrp;
588	unsigned int bits;
589
590	if (mask == NULL) {
591		/* no mask at all defaults to 32 bits */
592		maskaddr.s_addr = 0xFFFFFFFF;
593		return &maskaddr;
594	}
595	if ((addrp = dotted_to_addr(mask)) != NULL)
596		/* dotted_to_addr already returns a network byte order addr */
597		return addrp;
598	if (string_to_number(mask, 0, 32, &bits) == -1)
599		exit_error(PARAMETER_PROBLEM,
600			   "invalid mask `%s' specified", mask);
601	if (bits != 0) {
602		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
603		return &maskaddr;
604	}
605
606	maskaddr.s_addr = 0L;
607	return &maskaddr;
608}
609
610void
611parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
612		      struct in_addr *maskp, unsigned int *naddrs)
613{
614	struct in_addr *addrp;
615	char buf[256];
616	char *p;
617	int i, j, k, n;
618
619	strncpy(buf, name, sizeof(buf) - 1);
620	if ((p = strrchr(buf, '/')) != NULL) {
621		*p = '\0';
622		addrp = parse_mask(p + 1);
623	} else
624		addrp = parse_mask(NULL);
625	inaddrcpy(maskp, addrp);
626
627	/* if a null mask is given, the name is ignored, like in "any/0" */
628	if (maskp->s_addr == 0L)
629		strcpy(buf, "0.0.0.0");
630
631	addrp = *addrpp = parse_hostnetwork(buf, naddrs);
632	n = *naddrs;
633	for (i = 0, j = 0; i < n; i++) {
634		addrp[j++].s_addr &= maskp->s_addr;
635		for (k = 0; k < j - 1; k++) {
636			if (addrp[k].s_addr == addrp[j - 1].s_addr) {
637				(*naddrs)--;
638				j--;
639				break;
640			}
641		}
642	}
643}
644
645struct iptables_match *
646find_match(const char *name, enum ipt_tryload tryload)
647{
648	struct iptables_match *ptr;
649
650	for (ptr = iptables_matches; ptr; ptr = ptr->next) {
651		if (strcmp(name, ptr->name) == 0)
652			break;
653	}
654
655#ifndef NO_SHARED_LIBS
656	if (!ptr && tryload != DONT_LOAD) {
657		char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
658			 + strlen(name)];
659		sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
660		if (dlopen(path, RTLD_NOW)) {
661			/* Found library.  If it didn't register itself,
662			   maybe they specified target as match. */
663			ptr = find_match(name, DONT_LOAD);
664
665			if (!ptr)
666				exit_error(PARAMETER_PROBLEM,
667					   "Couldn't load match `%s'\n",
668					   name);
669		} else if (tryload == LOAD_MUST_SUCCEED)
670			exit_error(PARAMETER_PROBLEM,
671				   "Couldn't load match `%s':%s\n",
672				   name, dlerror());
673	}
674#else
675	if (ptr && !ptr->loaded) {
676		if (tryload != DONT_LOAD)
677			ptr->loaded = 1;
678		else
679			ptr = NULL;
680	}
681	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
682		exit_error(PARAMETER_PROBLEM,
683			   "Couldn't find match `%s'\n", name);
684	}
685#endif
686
687	if (ptr)
688		ptr->used = 1;
689
690	return ptr;
691}
692
693/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
694static struct iptables_match *
695find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
696{
697	unsigned int proto;
698
699	if (string_to_number(pname, 0, 255, &proto) != -1) {
700		char *protoname = proto_to_name(proto, nolookup);
701
702		if (protoname)
703			return find_match(protoname, tryload);
704	} else
705		return find_match(pname, tryload);
706
707	return NULL;
708}
709
710u_int16_t
711parse_protocol(const char *s)
712{
713	unsigned int proto;
714
715	if (string_to_number(s, 0, 255, &proto) == -1) {
716		struct protoent *pent;
717
718		if ((pent = getprotobyname(s)))
719			proto = pent->p_proto;
720		else {
721			unsigned int i;
722			for (i = 0;
723			     i < sizeof(chain_protos)/sizeof(struct pprot);
724			     i++) {
725				if (strcmp(s, chain_protos[i].name) == 0) {
726					proto = chain_protos[i].num;
727					break;
728				}
729			}
730			if (i == sizeof(chain_protos)/sizeof(struct pprot))
731				exit_error(PARAMETER_PROBLEM,
732					   "unknown protocol `%s' specified",
733					   s);
734		}
735	}
736
737	return (u_int16_t)proto;
738}
739
740static void
741parse_interface(const char *arg, char *vianame, unsigned char *mask)
742{
743	int vialen = strlen(arg);
744	unsigned int i;
745
746	memset(mask, 0, IFNAMSIZ);
747	memset(vianame, 0, IFNAMSIZ);
748
749	if (vialen + 1 > IFNAMSIZ)
750		exit_error(PARAMETER_PROBLEM,
751			   "interface name `%s' must be shorter than IFNAMSIZ"
752			   " (%i)", arg, IFNAMSIZ-1);
753
754	strcpy(vianame, arg);
755	if (vialen == 0)
756		memset(mask, 0, IFNAMSIZ);
757	else if (vianame[vialen - 1] == '+') {
758		memset(mask, 0xFF, vialen - 1);
759		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
760		/* Don't remove `+' here! -HW */
761	} else {
762		/* Include nul-terminator in match */
763		memset(mask, 0xFF, vialen + 1);
764		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
765		for (i = 0; vianame[i]; i++) {
766			if (!isalnum(vianame[i])
767			    && vianame[i] != '_'
768			    && vianame[i] != '.') {
769				printf("Warning: wierd character in interface"
770				       " `%s' (No aliases, :, ! or *).\n",
771				       vianame);
772				break;
773			}
774		}
775	}
776}
777
778/* Can't be zero. */
779static int
780parse_rulenumber(const char *rule)
781{
782	unsigned int rulenum;
783
784	if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
785		exit_error(PARAMETER_PROBLEM,
786			   "Invalid rule number `%s'", rule);
787
788	return rulenum;
789}
790
791static const char *
792parse_target(const char *targetname)
793{
794	const char *ptr;
795
796	if (strlen(targetname) < 1)
797		exit_error(PARAMETER_PROBLEM,
798			   "Invalid target name (too short)");
799
800	if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
801		exit_error(PARAMETER_PROBLEM,
802			   "Invalid target name `%s' (%i chars max)",
803			   targetname, sizeof(ipt_chainlabel)-1);
804
805	for (ptr = targetname; *ptr; ptr++)
806		if (isspace(*ptr))
807			exit_error(PARAMETER_PROBLEM,
808				   "Invalid target name `%s'", targetname);
809	return targetname;
810}
811
812static char *
813addr_to_network(const struct in_addr *addr)
814{
815	struct netent *net;
816
817	if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
818		return (char *) net->n_name;
819
820	return (char *) NULL;
821}
822
823char *
824addr_to_dotted(const struct in_addr *addrp)
825{
826	static char buf[20];
827	const unsigned char *bytep;
828
829	bytep = (const unsigned char *) &(addrp->s_addr);
830	sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
831	return buf;
832}
833
834char *
835addr_to_anyname(const struct in_addr *addr)
836{
837	char *name;
838
839	if ((name = addr_to_host(addr)) != NULL ||
840	    (name = addr_to_network(addr)) != NULL)
841		return name;
842
843	return addr_to_dotted(addr);
844}
845
846char *
847mask_to_dotted(const struct in_addr *mask)
848{
849	int i;
850	static char buf[20];
851	u_int32_t maskaddr, bits;
852
853	maskaddr = ntohl(mask->s_addr);
854
855	if (maskaddr == 0xFFFFFFFFL)
856		/* we don't want to see "/32" */
857		return "";
858
859	i = 32;
860	bits = 0xFFFFFFFEL;
861	while (--i >= 0 && maskaddr != bits)
862		bits <<= 1;
863	if (i >= 0)
864		sprintf(buf, "/%d", i);
865	else
866		/* mask was not a decent combination of 1's and 0's */
867		sprintf(buf, "/%s", addr_to_dotted(mask));
868
869	return buf;
870}
871
872int
873string_to_number(const char *s, unsigned int min, unsigned int max,
874		 unsigned int *ret)
875{
876	long number;
877	char *end;
878
879	/* Handle hex, octal, etc. */
880	errno = 0;
881	number = strtol(s, &end, 0);
882	if (*end == '\0' && end != s) {
883		/* we parsed a number, let's see if we want this */
884		if (errno != ERANGE && min <= number && number <= max) {
885			*ret = number;
886			return 0;
887		}
888	}
889	return -1;
890}
891
892static void
893set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
894	   int invert)
895{
896	if (*options & option)
897		exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
898			   opt2char(option));
899	*options |= option;
900
901	if (invert) {
902		unsigned int i;
903		for (i = 0; 1 << i != option; i++);
904
905		if (!inverse_for_options[i])
906			exit_error(PARAMETER_PROBLEM,
907				   "cannot have ! before -%c",
908				   opt2char(option));
909		*invflg |= inverse_for_options[i];
910	}
911}
912
913struct iptables_target *
914find_target(const char *name, enum ipt_tryload tryload)
915{
916	struct iptables_target *ptr;
917
918	/* Standard target? */
919	if (strcmp(name, "") == 0
920	    || strcmp(name, IPTC_LABEL_ACCEPT) == 0
921	    || strcmp(name, IPTC_LABEL_DROP) == 0
922	    || strcmp(name, IPTC_LABEL_QUEUE) == 0
923	    || strcmp(name, IPTC_LABEL_RETURN) == 0)
924		name = "standard";
925
926	for (ptr = iptables_targets; ptr; ptr = ptr->next) {
927		if (strcmp(name, ptr->name) == 0)
928			break;
929	}
930
931#ifndef NO_SHARED_LIBS
932	if (!ptr && tryload != DONT_LOAD) {
933		char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
934			 + strlen(name)];
935		sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
936		if (dlopen(path, RTLD_NOW)) {
937			/* Found library.  If it didn't register itself,
938			   maybe they specified match as a target. */
939			ptr = find_target(name, DONT_LOAD);
940			if (!ptr)
941				exit_error(PARAMETER_PROBLEM,
942					   "Couldn't load target `%s'\n",
943					   name);
944		} else if (tryload == LOAD_MUST_SUCCEED)
945			exit_error(PARAMETER_PROBLEM,
946				   "Couldn't load target `%s':%s\n",
947				   name, dlerror());
948	}
949#else
950	if (ptr && !ptr->loaded) {
951		if (tryload != DONT_LOAD)
952			ptr->loaded = 1;
953		else
954			ptr = NULL;
955	}
956	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
957		exit_error(PARAMETER_PROBLEM,
958			   "Couldn't find target `%s'\n", name);
959	}
960#endif
961
962	if (ptr)
963		ptr->used = 1;
964
965	return ptr;
966}
967
968static struct option *
969merge_options(struct option *oldopts, const struct option *newopts,
970	      unsigned int *option_offset)
971{
972	unsigned int num_old, num_new, i;
973	struct option *merge;
974
975	for (num_old = 0; oldopts[num_old].name; num_old++);
976	for (num_new = 0; newopts[num_new].name; num_new++);
977
978	global_option_offset += OPTION_OFFSET;
979	*option_offset = global_option_offset;
980
981	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
982	memcpy(merge, oldopts, num_old * sizeof(struct option));
983	for (i = 0; i < num_new; i++) {
984		merge[num_old + i] = newopts[i];
985		merge[num_old + i].val += *option_offset;
986	}
987	memset(merge + num_old + num_new, 0, sizeof(struct option));
988
989	return merge;
990}
991
992void
993register_match(struct iptables_match *me)
994{
995	struct iptables_match **i;
996
997	if (strcmp(me->version, program_version) != 0) {
998		fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
999			program_name, me->name, me->version, program_version);
1000		exit(1);
1001	}
1002
1003	if (find_match(me->name, DONT_LOAD)) {
1004		fprintf(stderr, "%s: match `%s' already registered.\n",
1005			program_name, me->name);
1006		exit(1);
1007	}
1008
1009	if (me->size != IPT_ALIGN(me->size)) {
1010		fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
1011			program_name, me->name, me->size);
1012		exit(1);
1013	}
1014
1015	/* Append to list. */
1016	for (i = &iptables_matches; *i; i = &(*i)->next);
1017	me->next = NULL;
1018	*i = me;
1019
1020	me->m = NULL;
1021	me->mflags = 0;
1022}
1023
1024void
1025register_target(struct iptables_target *me)
1026{
1027	if (strcmp(me->version, program_version) != 0) {
1028		fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
1029			program_name, me->name, me->version, program_version);
1030		exit(1);
1031	}
1032
1033	if (find_target(me->name, DONT_LOAD)) {
1034		fprintf(stderr, "%s: target `%s' already registered.\n",
1035			program_name, me->name);
1036		exit(1);
1037	}
1038
1039	if (me->size != IPT_ALIGN(me->size)) {
1040		fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
1041			program_name, me->name, me->size);
1042		exit(1);
1043	}
1044
1045	/* Prepend to list. */
1046	me->next = iptables_targets;
1047	iptables_targets = me;
1048	me->t = NULL;
1049	me->tflags = 0;
1050}
1051
1052static void
1053print_num(u_int64_t number, unsigned int format)
1054{
1055	if (format & FMT_KILOMEGAGIGA) {
1056		if (number > 99999) {
1057			number = (number + 500) / 1000;
1058			if (number > 9999) {
1059				number = (number + 500) / 1000;
1060				if (number > 9999) {
1061					number = (number + 500) / 1000;
1062					if (number > 9999) {
1063						number = (number + 500) / 1000;
1064						printf(FMT("%4lluT ","%lluT "), number);
1065					}
1066					else printf(FMT("%4lluG ","%lluG "), number);
1067				}
1068				else printf(FMT("%4lluM ","%lluM "), number);
1069			} else
1070				printf(FMT("%4lluK ","%lluK "), number);
1071		} else
1072			printf(FMT("%5llu ","%llu "), number);
1073	} else
1074		printf(FMT("%8llu ","%llu "), number);
1075}
1076
1077
1078static void
1079print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
1080{
1081	struct ipt_counters counters;
1082	const char *pol = iptc_get_policy(chain, &counters, handle);
1083	printf("Chain %s", chain);
1084	if (pol) {
1085		printf(" (policy %s", pol);
1086		if (!(format & FMT_NOCOUNTS)) {
1087			fputc(' ', stdout);
1088			print_num(counters.pcnt, (format|FMT_NOTABLE));
1089			fputs("packets, ", stdout);
1090			print_num(counters.bcnt, (format|FMT_NOTABLE));
1091			fputs("bytes", stdout);
1092		}
1093		printf(")\n");
1094	} else {
1095		unsigned int refs;
1096		if (!iptc_get_references(&refs, chain, handle))
1097			printf(" (ERROR obtaining refs)\n");
1098		else
1099			printf(" (%u references)\n", refs);
1100	}
1101
1102	if (format & FMT_LINENUMBERS)
1103		printf(FMT("%-4s ", "%s "), "num");
1104	if (!(format & FMT_NOCOUNTS)) {
1105		if (format & FMT_KILOMEGAGIGA) {
1106			printf(FMT("%5s ","%s "), "pkts");
1107			printf(FMT("%5s ","%s "), "bytes");
1108		} else {
1109			printf(FMT("%8s ","%s "), "pkts");
1110			printf(FMT("%10s ","%s "), "bytes");
1111		}
1112	}
1113	if (!(format & FMT_NOTARGET))
1114		printf(FMT("%-9s ","%s "), "target");
1115	fputs(" prot ", stdout);
1116	if (format & FMT_OPTIONS)
1117		fputs("opt", stdout);
1118	if (format & FMT_VIA) {
1119		printf(FMT(" %-6s ","%s "), "in");
1120		printf(FMT("%-6s ","%s "), "out");
1121	}
1122	printf(FMT(" %-19s ","%s "), "source");
1123	printf(FMT(" %-19s "," %s "), "destination");
1124	printf("\n");
1125}
1126
1127
1128static int
1129print_match(const struct ipt_entry_match *m,
1130	    const struct ipt_ip *ip,
1131	    int numeric)
1132{
1133	struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
1134
1135	if (match) {
1136		if (match->print)
1137			match->print(ip, m, numeric);
1138		else
1139			printf("%s ", match->name);
1140	} else {
1141		if (m->u.user.name[0])
1142			printf("UNKNOWN match `%s' ", m->u.user.name);
1143	}
1144	/* Don't stop iterating. */
1145	return 0;
1146}
1147
1148/* e is called `fw' here for hysterical raisins */
1149static void
1150print_firewall(const struct ipt_entry *fw,
1151	       const char *targname,
1152	       unsigned int num,
1153	       unsigned int format,
1154	       const iptc_handle_t handle)
1155{
1156	struct iptables_target *target = NULL;
1157	const struct ipt_entry_target *t;
1158	u_int8_t flags;
1159	char buf[BUFSIZ];
1160
1161	if (!iptc_is_chain(targname, handle))
1162		target = find_target(targname, TRY_LOAD);
1163	else
1164		target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
1165
1166	t = ipt_get_target((struct ipt_entry *)fw);
1167	flags = fw->ip.flags;
1168
1169	if (format & FMT_LINENUMBERS)
1170		printf(FMT("%-4u ", "%u "), num+1);
1171
1172	if (!(format & FMT_NOCOUNTS)) {
1173		print_num(fw->counters.pcnt, format);
1174		print_num(fw->counters.bcnt, format);
1175	}
1176
1177	if (!(format & FMT_NOTARGET))
1178		printf(FMT("%-9s ", "%s "), targname);
1179
1180	fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
1181	{
1182		char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
1183		if (pname)
1184			printf(FMT("%-5s", "%s "), pname);
1185		else
1186			printf(FMT("%-5hu", "%hu "), fw->ip.proto);
1187	}
1188
1189	if (format & FMT_OPTIONS) {
1190		if (format & FMT_NOTABLE)
1191			fputs("opt ", stdout);
1192		fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
1193		fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
1194		fputc(' ', stdout);
1195	}
1196
1197	if (format & FMT_VIA) {
1198		char iface[IFNAMSIZ+2];
1199
1200		if (fw->ip.invflags & IPT_INV_VIA_IN) {
1201			iface[0] = '!';
1202			iface[1] = '\0';
1203		}
1204		else iface[0] = '\0';
1205
1206		if (fw->ip.iniface[0] != '\0') {
1207			strcat(iface, fw->ip.iniface);
1208		}
1209		else if (format & FMT_NUMERIC) strcat(iface, "*");
1210		else strcat(iface, "any");
1211		printf(FMT(" %-6s ","in %s "), iface);
1212
1213		if (fw->ip.invflags & IPT_INV_VIA_OUT) {
1214			iface[0] = '!';
1215			iface[1] = '\0';
1216		}
1217		else iface[0] = '\0';
1218
1219		if (fw->ip.outiface[0] != '\0') {
1220			strcat(iface, fw->ip.outiface);
1221		}
1222		else if (format & FMT_NUMERIC) strcat(iface, "*");
1223		else strcat(iface, "any");
1224		printf(FMT("%-6s ","out %s "), iface);
1225	}
1226
1227	fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
1228	if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
1229		printf(FMT("%-19s ","%s "), "anywhere");
1230	else {
1231		if (format & FMT_NUMERIC)
1232			sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
1233		else
1234			sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
1235		strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
1236		printf(FMT("%-19s ","%s "), buf);
1237	}
1238
1239	fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
1240	if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
1241		printf(FMT("%-19s","-> %s"), "anywhere");
1242	else {
1243		if (format & FMT_NUMERIC)
1244			sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
1245		else
1246			sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
1247		strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
1248		printf(FMT("%-19s","-> %s"), buf);
1249	}
1250
1251	if (format & FMT_NOTABLE)
1252		fputs("  ", stdout);
1253
1254	IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
1255
1256	if (target) {
1257		if (target->print)
1258			/* Print the target information. */
1259			target->print(&fw->ip, t, format & FMT_NUMERIC);
1260	} else if (t->u.target_size != sizeof(*t))
1261		printf("[%u bytes of unknown target data] ",
1262		       t->u.target_size - sizeof(*t));
1263
1264	if (!(format & FMT_NONEWLINE))
1265		fputc('\n', stdout);
1266}
1267
1268static void
1269print_firewall_line(const struct ipt_entry *fw,
1270		    const iptc_handle_t h)
1271{
1272	struct ipt_entry_target *t;
1273
1274	t = ipt_get_target((struct ipt_entry *)fw);
1275	print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
1276}
1277
1278static int
1279append_entry(const ipt_chainlabel chain,
1280	     struct ipt_entry *fw,
1281	     unsigned int nsaddrs,
1282	     const struct in_addr saddrs[],
1283	     unsigned int ndaddrs,
1284	     const struct in_addr daddrs[],
1285	     int verbose,
1286	     iptc_handle_t *handle)
1287{
1288	unsigned int i, j;
1289	int ret = 1;
1290
1291	for (i = 0; i < nsaddrs; i++) {
1292		fw->ip.src.s_addr = saddrs[i].s_addr;
1293		for (j = 0; j < ndaddrs; j++) {
1294			fw->ip.dst.s_addr = daddrs[j].s_addr;
1295			if (verbose)
1296				print_firewall_line(fw, *handle);
1297			ret &= iptc_append_entry(chain, fw, handle);
1298		}
1299	}
1300
1301	return ret;
1302}
1303
1304static int
1305replace_entry(const ipt_chainlabel chain,
1306	      struct ipt_entry *fw,
1307	      unsigned int rulenum,
1308	      const struct in_addr *saddr,
1309	      const struct in_addr *daddr,
1310	      int verbose,
1311	      iptc_handle_t *handle)
1312{
1313	fw->ip.src.s_addr = saddr->s_addr;
1314	fw->ip.dst.s_addr = daddr->s_addr;
1315
1316	if (verbose)
1317		print_firewall_line(fw, *handle);
1318	return iptc_replace_entry(chain, fw, rulenum, handle);
1319}
1320
1321static int
1322insert_entry(const ipt_chainlabel chain,
1323	     struct ipt_entry *fw,
1324	     unsigned int rulenum,
1325	     unsigned int nsaddrs,
1326	     const struct in_addr saddrs[],
1327	     unsigned int ndaddrs,
1328	     const struct in_addr daddrs[],
1329	     int verbose,
1330	     iptc_handle_t *handle)
1331{
1332	unsigned int i, j;
1333	int ret = 1;
1334
1335	for (i = 0; i < nsaddrs; i++) {
1336		fw->ip.src.s_addr = saddrs[i].s_addr;
1337		for (j = 0; j < ndaddrs; j++) {
1338			fw->ip.dst.s_addr = daddrs[j].s_addr;
1339			if (verbose)
1340				print_firewall_line(fw, *handle);
1341			ret &= iptc_insert_entry(chain, fw, rulenum, handle);
1342		}
1343	}
1344
1345	return ret;
1346}
1347
1348static unsigned char *
1349make_delete_mask(struct ipt_entry *fw)
1350{
1351	/* Establish mask for comparison */
1352	unsigned int size;
1353	struct iptables_match *m;
1354	unsigned char *mask, *mptr;
1355
1356	size = sizeof(struct ipt_entry);
1357	for (m = iptables_matches; m; m = m->next) {
1358		if (!m->used)
1359			continue;
1360
1361		size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
1362	}
1363
1364	mask = fw_calloc(1, size
1365			 + IPT_ALIGN(sizeof(struct ipt_entry_target))
1366			 + iptables_targets->size);
1367
1368	memset(mask, 0xFF, sizeof(struct ipt_entry));
1369	mptr = mask + sizeof(struct ipt_entry);
1370
1371	for (m = iptables_matches; m; m = m->next) {
1372		if (!m->used)
1373			continue;
1374
1375		memset(mptr, 0xFF,
1376		       IPT_ALIGN(sizeof(struct ipt_entry_match))
1377		       + m->userspacesize);
1378		mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
1379	}
1380
1381	memset(mptr, 0xFF,
1382	       IPT_ALIGN(sizeof(struct ipt_entry_target))
1383	       + iptables_targets->userspacesize);
1384
1385	return mask;
1386}
1387
1388static int
1389delete_entry(const ipt_chainlabel chain,
1390	     struct ipt_entry *fw,
1391	     unsigned int nsaddrs,
1392	     const struct in_addr saddrs[],
1393	     unsigned int ndaddrs,
1394	     const struct in_addr daddrs[],
1395	     int verbose,
1396	     iptc_handle_t *handle)
1397{
1398	unsigned int i, j;
1399	int ret = 1;
1400	unsigned char *mask;
1401
1402	mask = make_delete_mask(fw);
1403	for (i = 0; i < nsaddrs; i++) {
1404		fw->ip.src.s_addr = saddrs[i].s_addr;
1405		for (j = 0; j < ndaddrs; j++) {
1406			fw->ip.dst.s_addr = daddrs[j].s_addr;
1407			if (verbose)
1408				print_firewall_line(fw, *handle);
1409			ret &= iptc_delete_entry(chain, fw, mask, handle);
1410		}
1411	}
1412	return ret;
1413}
1414
1415int
1416for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
1417	       int verbose, int builtinstoo, iptc_handle_t *handle)
1418{
1419        int ret = 1;
1420	const char *chain;
1421	char *chains;
1422	unsigned int i, chaincount = 0;
1423
1424	chain = iptc_first_chain(handle);
1425	while (chain) {
1426		chaincount++;
1427		chain = iptc_next_chain(handle);
1428        }
1429
1430	chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
1431	i = 0;
1432	chain = iptc_first_chain(handle);
1433	while (chain) {
1434		strcpy(chains + i*sizeof(ipt_chainlabel), chain);
1435		i++;
1436		chain = iptc_next_chain(handle);
1437        }
1438
1439	for (i = 0; i < chaincount; i++) {
1440		if (!builtinstoo
1441		    && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
1442				    *handle))
1443			continue;
1444	        ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
1445	}
1446
1447	free(chains);
1448        return ret;
1449}
1450
1451int
1452flush_entries(const ipt_chainlabel chain, int verbose,
1453	      iptc_handle_t *handle)
1454{
1455	if (!chain)
1456		return for_each_chain(flush_entries, verbose, 1, handle);
1457
1458	if (verbose)
1459		fprintf(stdout, "Flushing chain `%s'\n", chain);
1460	return iptc_flush_entries(chain, handle);
1461}
1462
1463static int
1464zero_entries(const ipt_chainlabel chain, int verbose,
1465	     iptc_handle_t *handle)
1466{
1467	if (!chain)
1468		return for_each_chain(zero_entries, verbose, 1, handle);
1469
1470	if (verbose)
1471		fprintf(stdout, "Zeroing chain `%s'\n", chain);
1472	return iptc_zero_entries(chain, handle);
1473}
1474
1475int
1476delete_chain(const ipt_chainlabel chain, int verbose,
1477	     iptc_handle_t *handle)
1478{
1479	if (!chain)
1480		return for_each_chain(delete_chain, verbose, 0, handle);
1481
1482	if (verbose)
1483	        fprintf(stdout, "Deleting chain `%s'\n", chain);
1484	return iptc_delete_chain(chain, handle);
1485}
1486
1487static int
1488list_entries(const ipt_chainlabel chain, int verbose, int numeric,
1489	     int expanded, int linenumbers, iptc_handle_t *handle)
1490{
1491	int found = 0;
1492	unsigned int format;
1493	const char *this;
1494
1495	format = FMT_OPTIONS;
1496	if (!verbose)
1497		format |= FMT_NOCOUNTS;
1498	else
1499		format |= FMT_VIA;
1500
1501	if (numeric)
1502		format |= FMT_NUMERIC;
1503
1504	if (!expanded)
1505		format |= FMT_KILOMEGAGIGA;
1506
1507	if (linenumbers)
1508		format |= FMT_LINENUMBERS;
1509
1510	for (this = iptc_first_chain(handle);
1511	     this;
1512	     this = iptc_next_chain(handle)) {
1513		const struct ipt_entry *i;
1514		unsigned int num;
1515
1516		if (chain && strcmp(chain, this) != 0)
1517			continue;
1518
1519		if (found) printf("\n");
1520
1521		print_header(format, this, handle);
1522		i = iptc_first_rule(this, handle);
1523
1524		num = 0;
1525		while (i) {
1526			print_firewall(i,
1527				       iptc_get_target(i, handle),
1528				       num++,
1529				       format,
1530				       *handle);
1531			i = iptc_next_rule(i, handle);
1532		}
1533		found = 1;
1534	}
1535
1536	errno = ENOENT;
1537	return found;
1538}
1539
1540static char *get_modprobe(void)
1541{
1542	int procfile;
1543	char *ret;
1544
1545	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
1546	if (procfile < 0)
1547		return NULL;
1548
1549	ret = malloc(1024);
1550	if (ret) {
1551		switch (read(procfile, ret, 1024)) {
1552		case -1: goto fail;
1553		case 1024: goto fail; /* Partial read.  Wierd */
1554		}
1555		if (ret[strlen(ret)-1]=='\n')
1556			ret[strlen(ret)-1]=0;
1557		close(procfile);
1558		return ret;
1559	}
1560 fail:
1561	free(ret);
1562	close(procfile);
1563	return NULL;
1564}
1565
1566int iptables_insmod(const char *modname, const char *modprobe)
1567{
1568	char *buf = NULL;
1569	char *argv[3];
1570
1571	/* If they don't explicitly set it, read out of kernel */
1572	if (!modprobe) {
1573		buf = get_modprobe();
1574		if (!buf)
1575			return -1;
1576		modprobe = buf;
1577	}
1578
1579	switch (fork()) {
1580	case 0:
1581		argv[0] = (char *)modprobe;
1582		argv[1] = (char *)modname;
1583		argv[2] = NULL;
1584		execv(argv[0], argv);
1585
1586		/* not usually reached */
1587		exit(0);
1588	case -1:
1589		return -1;
1590
1591	default: /* parent */
1592		wait(NULL);
1593	}
1594
1595	free(buf);
1596	return 0;
1597}
1598
1599static struct ipt_entry *
1600generate_entry(const struct ipt_entry *fw,
1601	       struct iptables_match *matches,
1602	       struct ipt_entry_target *target)
1603{
1604	unsigned int size;
1605	struct iptables_match *m;
1606	struct ipt_entry *e;
1607
1608	size = sizeof(struct ipt_entry);
1609	for (m = matches; m; m = m->next) {
1610		if (!m->used)
1611			continue;
1612
1613		size += m->m->u.match_size;
1614	}
1615
1616	e = fw_malloc(size + target->u.target_size);
1617	*e = *fw;
1618	e->target_offset = size;
1619	e->next_offset = size + target->u.target_size;
1620
1621	size = 0;
1622	for (m = matches; m; m = m->next) {
1623		if (!m->used)
1624			continue;
1625
1626		memcpy(e->elems + size, m->m, m->m->u.match_size);
1627		size += m->m->u.match_size;
1628	}
1629	memcpy(e->elems + size, target, target->u.target_size);
1630
1631	return e;
1632}
1633
1634int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
1635{
1636	struct ipt_entry fw, *e = NULL;
1637	int invert = 0;
1638	unsigned int nsaddrs = 0, ndaddrs = 0;
1639	struct in_addr *saddrs = NULL, *daddrs = NULL;
1640
1641	int c, verbose = 0;
1642	const char *chain = NULL;
1643	const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
1644	const char *policy = NULL, *newname = NULL;
1645	unsigned int rulenum = 0, options = 0, command = 0;
1646	const char *pcnt = NULL, *bcnt = NULL;
1647	int ret = 1;
1648	struct iptables_match *m;
1649	struct iptables_target *target = NULL;
1650	struct iptables_target *t;
1651	const char *jumpto = "";
1652	char *protocol = NULL;
1653	const char *modprobe = NULL;
1654	int proto_used = 0;
1655
1656	memset(&fw, 0, sizeof(fw));
1657
1658	opts = original_opts;
1659	global_option_offset = 0;
1660
1661	/* re-set optind to 0 in case do_command gets called
1662	 * a second time */
1663	optind = 0;
1664
1665	/* clear mflags in case do_command gets called a second time
1666	 * (we clear the global list of all matches for security)*/
1667	for (m = iptables_matches; m; m = m->next) {
1668		m->mflags = 0;
1669		m->used = 0;
1670	}
1671
1672	for (t = iptables_targets; t; t = t->next) {
1673		t->tflags = 0;
1674		t->used = 0;
1675	}
1676
1677	/* Suppress error messages: we may add new options if we
1678           demand-load a protocol. */
1679	opterr = 0;
1680
1681	while ((c = getopt_long(argc, argv,
1682	   "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
1683					   opts, NULL)) != -1) {
1684		switch (c) {
1685			/*
1686			 * Command selection
1687			 */
1688		case 'A':
1689			add_command(&command, CMD_APPEND, CMD_NONE,
1690				    invert);
1691			chain = optarg;
1692			break;
1693
1694		case 'D':
1695			add_command(&command, CMD_DELETE, CMD_NONE,
1696				    invert);
1697			chain = optarg;
1698			if (optind < argc && argv[optind][0] != '-'
1699			    && argv[optind][0] != '!') {
1700				rulenum = parse_rulenumber(argv[optind++]);
1701				command = CMD_DELETE_NUM;
1702			}
1703			break;
1704
1705		case 'R':
1706			add_command(&command, CMD_REPLACE, CMD_NONE,
1707				    invert);
1708			chain = optarg;
1709			if (optind < argc && argv[optind][0] != '-'
1710			    && argv[optind][0] != '!')
1711				rulenum = parse_rulenumber(argv[optind++]);
1712			else
1713				exit_error(PARAMETER_PROBLEM,
1714					   "-%c requires a rule number",
1715					   cmd2char(CMD_REPLACE));
1716			break;
1717
1718		case 'I':
1719			add_command(&command, CMD_INSERT, CMD_NONE,
1720				    invert);
1721			chain = optarg;
1722			if (optind < argc && argv[optind][0] != '-'
1723			    && argv[optind][0] != '!')
1724				rulenum = parse_rulenumber(argv[optind++]);
1725			else rulenum = 1;
1726			break;
1727
1728		case 'L':
1729			add_command(&command, CMD_LIST, CMD_ZERO,
1730				    invert);
1731			if (optarg) chain = optarg;
1732			else if (optind < argc && argv[optind][0] != '-'
1733				 && argv[optind][0] != '!')
1734				chain = argv[optind++];
1735			break;
1736
1737		case 'F':
1738			add_command(&command, CMD_FLUSH, CMD_NONE,
1739				    invert);
1740			if (optarg) chain = optarg;
1741			else if (optind < argc && argv[optind][0] != '-'
1742				 && argv[optind][0] != '!')
1743				chain = argv[optind++];
1744			break;
1745
1746		case 'Z':
1747			add_command(&command, CMD_ZERO, CMD_LIST,
1748				    invert);
1749			if (optarg) chain = optarg;
1750			else if (optind < argc && argv[optind][0] != '-'
1751				&& argv[optind][0] != '!')
1752				chain = argv[optind++];
1753			break;
1754
1755		case 'N':
1756			if (optarg && *optarg == '-')
1757				exit_error(PARAMETER_PROBLEM,
1758					   "chain name not allowed to start "
1759					   "with `-'\n");
1760			if (find_target(optarg, TRY_LOAD))
1761				exit_error(PARAMETER_PROBLEM,
1762					   "chain name may not clash "
1763					   "with target name\n");
1764			add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
1765				    invert);
1766			chain = optarg;
1767			break;
1768
1769		case 'X':
1770			add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
1771				    invert);
1772			if (optarg) chain = optarg;
1773			else if (optind < argc && argv[optind][0] != '-'
1774				 && argv[optind][0] != '!')
1775				chain = argv[optind++];
1776			break;
1777
1778		case 'E':
1779			add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
1780				    invert);
1781			chain = optarg;
1782			if (optind < argc && argv[optind][0] != '-'
1783			    && argv[optind][0] != '!')
1784				newname = argv[optind++];
1785			else
1786				exit_error(PARAMETER_PROBLEM,
1787				           "-%c requires old-chain-name and "
1788					   "new-chain-name",
1789					    cmd2char(CMD_RENAME_CHAIN));
1790			break;
1791
1792		case 'P':
1793			add_command(&command, CMD_SET_POLICY, CMD_NONE,
1794				    invert);
1795			chain = optarg;
1796			if (optind < argc && argv[optind][0] != '-'
1797			    && argv[optind][0] != '!')
1798				policy = argv[optind++];
1799			else
1800				exit_error(PARAMETER_PROBLEM,
1801					   "-%c requires a chain and a policy",
1802					   cmd2char(CMD_SET_POLICY));
1803			break;
1804
1805		case 'h':
1806			if (!optarg)
1807				optarg = argv[optind];
1808
1809			/* iptables -p icmp -h */
1810			if (!iptables_matches && protocol)
1811				find_match(protocol, TRY_LOAD);
1812
1813			exit_printhelp();
1814
1815			/*
1816			 * Option selection
1817			 */
1818		case 'p':
1819			check_inverse(optarg, &invert, &optind, argc);
1820			set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
1821				   invert);
1822
1823			/* Canonicalize into lower case */
1824			for (protocol = argv[optind-1]; *protocol; protocol++)
1825				*protocol = tolower(*protocol);
1826
1827			protocol = argv[optind-1];
1828			fw.ip.proto = parse_protocol(protocol);
1829
1830			if (fw.ip.proto == 0
1831			    && (fw.ip.invflags & IPT_INV_PROTO))
1832				exit_error(PARAMETER_PROBLEM,
1833					   "rule would never match protocol");
1834			fw.nfcache |= NFC_IP_PROTO;
1835			break;
1836
1837		case 's':
1838			check_inverse(optarg, &invert, &optind, argc);
1839			set_option(&options, OPT_SOURCE, &fw.ip.invflags,
1840				   invert);
1841			shostnetworkmask = argv[optind-1];
1842			fw.nfcache |= NFC_IP_SRC;
1843			break;
1844
1845		case 'd':
1846			check_inverse(optarg, &invert, &optind, argc);
1847			set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
1848				   invert);
1849			dhostnetworkmask = argv[optind-1];
1850			fw.nfcache |= NFC_IP_DST;
1851			break;
1852
1853		case 'j':
1854			set_option(&options, OPT_JUMP, &fw.ip.invflags,
1855				   invert);
1856			jumpto = parse_target(optarg);
1857			/* TRY_LOAD (may be chain name) */
1858			target = find_target(jumpto, TRY_LOAD);
1859
1860			if (target) {
1861				size_t size;
1862
1863				size = IPT_ALIGN(sizeof(struct ipt_entry_target))
1864					+ target->size;
1865
1866				target->t = fw_calloc(1, size);
1867				target->t->u.target_size = size;
1868				strcpy(target->t->u.user.name, jumpto);
1869				target->init(target->t, &fw.nfcache);
1870				opts = merge_options(opts, target->extra_opts, &target->option_offset);
1871			}
1872			break;
1873
1874
1875		case 'i':
1876			check_inverse(optarg, &invert, &optind, argc);
1877			set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
1878				   invert);
1879			parse_interface(argv[optind-1],
1880					fw.ip.iniface,
1881					fw.ip.iniface_mask);
1882			fw.nfcache |= NFC_IP_IF_IN;
1883			break;
1884
1885		case 'o':
1886			check_inverse(optarg, &invert, &optind, argc);
1887			set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
1888				   invert);
1889			parse_interface(argv[optind-1],
1890					fw.ip.outiface,
1891					fw.ip.outiface_mask);
1892			fw.nfcache |= NFC_IP_IF_OUT;
1893			break;
1894
1895		case 'f':
1896			set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
1897				   invert);
1898			fw.ip.flags |= IPT_F_FRAG;
1899			fw.nfcache |= NFC_IP_FRAG;
1900			break;
1901
1902		case 'v':
1903			if (!verbose)
1904				set_option(&options, OPT_VERBOSE,
1905					   &fw.ip.invflags, invert);
1906			verbose++;
1907			break;
1908
1909		case 'm': {
1910			size_t size;
1911
1912			if (invert)
1913				exit_error(PARAMETER_PROBLEM,
1914					   "unexpected ! flag before --match");
1915
1916			m = find_match(optarg, LOAD_MUST_SUCCEED);
1917			size = IPT_ALIGN(sizeof(struct ipt_entry_match))
1918					 + m->size;
1919			m->m = fw_calloc(1, size);
1920			m->m->u.match_size = size;
1921			strcpy(m->m->u.user.name, m->name);
1922			m->init(m->m, &fw.nfcache);
1923			opts = merge_options(opts, m->extra_opts, &m->option_offset);
1924		}
1925		break;
1926
1927		case 'n':
1928			set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
1929				   invert);
1930			break;
1931
1932		case 't':
1933			if (invert)
1934				exit_error(PARAMETER_PROBLEM,
1935					   "unexpected ! flag before --table");
1936			*table = argv[optind-1];
1937			break;
1938
1939		case 'x':
1940			set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
1941				   invert);
1942			break;
1943
1944		case 'V':
1945			if (invert)
1946				printf("Not %s ;-)\n", program_version);
1947			else
1948				printf("%s v%s\n",
1949				       program_name, program_version);
1950			exit(0);
1951
1952		case '0':
1953			set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
1954				   invert);
1955			break;
1956
1957		case 'M':
1958			modprobe = optarg;
1959			break;
1960
1961		case 'c':
1962
1963			set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
1964				   invert);
1965			pcnt = optarg;
1966			if (optind < argc && argv[optind][0] != '-'
1967			    && argv[optind][0] != '!')
1968				bcnt = argv[optind++];
1969			else
1970				exit_error(PARAMETER_PROBLEM,
1971					"-%c requires packet and byte counter",
1972					opt2char(OPT_COUNTERS));
1973
1974			if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
1975				exit_error(PARAMETER_PROBLEM,
1976					"-%c packet counter not numeric",
1977					opt2char(OPT_COUNTERS));
1978
1979			if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
1980				exit_error(PARAMETER_PROBLEM,
1981					"-%c byte counter not numeric",
1982					opt2char(OPT_COUNTERS));
1983
1984			break;
1985
1986
1987		case 1: /* non option */
1988			if (optarg[0] == '!' && optarg[1] == '\0') {
1989				if (invert)
1990					exit_error(PARAMETER_PROBLEM,
1991						   "multiple consecutive ! not"
1992						   " allowed");
1993				invert = TRUE;
1994				optarg[0] = '\0';
1995				continue;
1996			}
1997			printf("Bad argument `%s'\n", optarg);
1998			exit_tryhelp(2);
1999
2000		default:
2001			if (!target
2002			    || !(target->parse(c - target->option_offset,
2003					       argv, invert,
2004					       &target->tflags,
2005					       &fw, &target->t))) {
2006				for (m = iptables_matches; m; m = m->next) {
2007					if (!m->used)
2008						continue;
2009
2010					if (m->parse(c - m->option_offset,
2011						     argv, invert,
2012						     &m->mflags,
2013						     &fw,
2014						     &fw.nfcache,
2015						     &m->m))
2016						break;
2017				}
2018
2019				/* If you listen carefully, you can
2020				   actually hear this code suck. */
2021
2022				/* some explanations (after four different bugs
2023				 * in 3 different releases): If we encountere a
2024				 * parameter, that has not been parsed yet,
2025				 * it's not an option of an explicitly loaded
2026				 * match or a target.  However, we support
2027				 * implicit loading of the protocol match
2028				 * extension.  '-p tcp' means 'l4 proto 6' and
2029				 * at the same time 'load tcp protocol match on
2030				 * demand if we specify --dport'.
2031				 *
2032				 * To make this work, we need to make sure:
2033				 * - the parameter has not been parsed by
2034				 *   a match (m above)
2035				 * - a protocol has been specified
2036				 * - the protocol extension has not been
2037				 *   loaded yet, or is loaded and unused
2038				 *   [think of iptables-restore!]
2039				 * - the protocol extension can be successively
2040				 *   loaded
2041				 */
2042				if (m == NULL
2043				    && protocol
2044				    && (!find_proto(protocol, DONT_LOAD,
2045						   options&OPT_NUMERIC)
2046					|| (find_proto(protocol, DONT_LOAD,
2047							options&OPT_NUMERIC)
2048					    && (proto_used == 0))
2049				       )
2050				    && (m = find_proto(protocol, TRY_LOAD,
2051						       options&OPT_NUMERIC))) {
2052					/* Try loading protocol */
2053					size_t size;
2054
2055					proto_used = 1;
2056
2057					size = IPT_ALIGN(sizeof(struct ipt_entry_match))
2058							 + m->size;
2059
2060					m->m = fw_calloc(1, size);
2061					m->m->u.match_size = size;
2062					strcpy(m->m->u.user.name, m->name);
2063					m->init(m->m, &fw.nfcache);
2064
2065					opts = merge_options(opts,
2066					    m->extra_opts, &m->option_offset);
2067
2068					optind--;
2069					continue;
2070				}
2071				if (!m)
2072					exit_error(PARAMETER_PROBLEM,
2073						   "Unknown arg `%s'",
2074						   argv[optind-1]);
2075			}
2076		}
2077		invert = FALSE;
2078	}
2079
2080	for (m = iptables_matches; m; m = m->next) {
2081		if (!m->used)
2082			continue;
2083
2084		m->final_check(m->mflags);
2085	}
2086
2087	if (target)
2088		target->final_check(target->tflags);
2089
2090	/* Fix me: must put inverse options checking here --MN */
2091
2092	if (optind < argc)
2093		exit_error(PARAMETER_PROBLEM,
2094			   "unknown arguments found on commandline");
2095	if (!command)
2096		exit_error(PARAMETER_PROBLEM, "no command specified");
2097	if (invert)
2098		exit_error(PARAMETER_PROBLEM,
2099			   "nothing appropriate following !");
2100
2101	if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
2102		if (!(options & OPT_DESTINATION))
2103			dhostnetworkmask = "0.0.0.0/0";
2104		if (!(options & OPT_SOURCE))
2105			shostnetworkmask = "0.0.0.0/0";
2106	}
2107
2108	if (shostnetworkmask)
2109		parse_hostnetworkmask(shostnetworkmask, &saddrs,
2110				      &(fw.ip.smsk), &nsaddrs);
2111
2112	if (dhostnetworkmask)
2113		parse_hostnetworkmask(dhostnetworkmask, &daddrs,
2114				      &(fw.ip.dmsk), &ndaddrs);
2115
2116	if ((nsaddrs > 1 || ndaddrs > 1) &&
2117	    (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
2118		exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
2119			   " source or destination IP addresses");
2120
2121	if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
2122		exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
2123			   "specify a unique address");
2124
2125	generic_opt_check(command, options);
2126
2127	if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
2128		exit_error(PARAMETER_PROBLEM,
2129			   "chain name `%s' too long (must be under %i chars)",
2130			   chain, IPT_FUNCTION_MAXNAMELEN);
2131
2132	/* only allocate handle if we weren't called with a handle */
2133	if (!*handle)
2134		*handle = iptc_init(*table);
2135
2136	if (!*handle) {
2137		/* try to insmod the module if iptc_init failed */
2138		iptables_insmod("ip_tables", modprobe);
2139		*handle = iptc_init(*table);
2140	}
2141
2142	if (!*handle)
2143		exit_error(VERSION_PROBLEM,
2144			   "can't initialize iptables table `%s': %s",
2145			   *table, iptc_strerror(errno));
2146
2147	if (command == CMD_APPEND
2148	    || command == CMD_DELETE
2149	    || command == CMD_INSERT
2150	    || command == CMD_REPLACE) {
2151		if (strcmp(chain, "PREROUTING") == 0
2152		    || strcmp(chain, "INPUT") == 0) {
2153			/* -o not valid with incoming packets. */
2154			if (options & OPT_VIANAMEOUT)
2155				exit_error(PARAMETER_PROBLEM,
2156					   "Can't use -%c with %s\n",
2157					   opt2char(OPT_VIANAMEOUT),
2158					   chain);
2159		}
2160
2161		if (strcmp(chain, "POSTROUTING") == 0
2162		    || strcmp(chain, "OUTPUT") == 0) {
2163			/* -i not valid with outgoing packets */
2164			if (options & OPT_VIANAMEIN)
2165				exit_error(PARAMETER_PROBLEM,
2166					   "Can't use -%c with %s\n",
2167					   opt2char(OPT_VIANAMEIN),
2168					   chain);
2169		}
2170
2171		if (target && iptc_is_chain(jumpto, *handle)) {
2172			printf("Warning: using chain %s, not extension\n",
2173			       jumpto);
2174
2175			target = NULL;
2176		}
2177
2178		/* If they didn't specify a target, or it's a chain
2179		   name, use standard. */
2180		if (!target
2181		    && (strlen(jumpto) == 0
2182			|| iptc_is_chain(jumpto, *handle))) {
2183			size_t size;
2184
2185			target = find_target(IPT_STANDARD_TARGET,
2186					     LOAD_MUST_SUCCEED);
2187
2188			size = sizeof(struct ipt_entry_target)
2189				+ target->size;
2190			target->t = fw_calloc(1, size);
2191			target->t->u.target_size = size;
2192			strcpy(target->t->u.user.name, jumpto);
2193			target->init(target->t, &fw.nfcache);
2194		}
2195
2196		if (!target) {
2197			/* it is no chain, and we can't load a plugin.
2198			 * We cannot know if the plugin is corrupt, non
2199			 * existant OR if the user just misspelled a
2200			 * chain. */
2201			find_target(jumpto, LOAD_MUST_SUCCEED);
2202		} else {
2203			e = generate_entry(&fw, iptables_matches, target->t);
2204		}
2205	}
2206
2207	switch (command) {
2208	case CMD_APPEND:
2209		ret = append_entry(chain, e,
2210				   nsaddrs, saddrs, ndaddrs, daddrs,
2211				   options&OPT_VERBOSE,
2212				   handle);
2213		break;
2214	case CMD_DELETE:
2215		ret = delete_entry(chain, e,
2216				   nsaddrs, saddrs, ndaddrs, daddrs,
2217				   options&OPT_VERBOSE,
2218				   handle);
2219		break;
2220	case CMD_DELETE_NUM:
2221		ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
2222		break;
2223	case CMD_REPLACE:
2224		ret = replace_entry(chain, e, rulenum - 1,
2225				    saddrs, daddrs, options&OPT_VERBOSE,
2226				    handle);
2227		break;
2228	case CMD_INSERT:
2229		ret = insert_entry(chain, e, rulenum - 1,
2230				   nsaddrs, saddrs, ndaddrs, daddrs,
2231				   options&OPT_VERBOSE,
2232				   handle);
2233		break;
2234	case CMD_LIST:
2235		ret = list_entries(chain,
2236				   options&OPT_VERBOSE,
2237				   options&OPT_NUMERIC,
2238				   options&OPT_EXPANDED,
2239				   options&OPT_LINENUMBERS,
2240				   handle);
2241		break;
2242	case CMD_FLUSH:
2243		ret = flush_entries(chain, options&OPT_VERBOSE, handle);
2244		break;
2245	case CMD_ZERO:
2246		ret = zero_entries(chain, options&OPT_VERBOSE, handle);
2247		break;
2248	case CMD_LIST|CMD_ZERO:
2249		ret = list_entries(chain,
2250				   options&OPT_VERBOSE,
2251				   options&OPT_NUMERIC,
2252				   options&OPT_EXPANDED,
2253				   options&OPT_LINENUMBERS,
2254				   handle);
2255		if (ret)
2256			ret = zero_entries(chain,
2257					   options&OPT_VERBOSE, handle);
2258		break;
2259	case CMD_NEW_CHAIN:
2260		ret = iptc_create_chain(chain, handle);
2261		break;
2262	case CMD_DELETE_CHAIN:
2263		ret = delete_chain(chain, options&OPT_VERBOSE, handle);
2264		break;
2265	case CMD_RENAME_CHAIN:
2266		ret = iptc_rename_chain(chain, newname,	handle);
2267		break;
2268	case CMD_SET_POLICY:
2269		ret = iptc_set_policy(chain, policy, NULL, handle);
2270		break;
2271	default:
2272		/* We should never reach this... */
2273		exit_tryhelp(2);
2274	}
2275
2276	if (verbose > 1)
2277		dump_entries(*handle);
2278
2279	return ret;
2280}
2281