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