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