xtables.c revision 4e41854423b529d3107c23b85434d50a75d08057
1/*
2 * (C) 2000-2006 by the netfilter coreteam <coreteam@netfilter.org>:
3 *
4 *	This program is free software; you can redistribute it and/or modify
5 *	it under the terms of the GNU General Public License as published by
6 *	the Free Software Foundation; either version 2 of the License, or
7 *	(at your option) any later version.
8 *
9 *	This program is distributed in the hope that it will be useful,
10 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 *	GNU General Public License for more details.
13 *
14 *	You should have received a copy of the GNU General Public License
15 *	along with this program; if not, write to the Free Software
16 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#include <errno.h>
20#include <fcntl.h>
21#include <netdb.h>
22#include <stdarg.h>
23#include <stdbool.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28#include <sys/socket.h>
29#include <sys/stat.h>
30#include <sys/types.h>
31#include <sys/wait.h>
32#include <arpa/inet.h>
33
34#include <xtables.h>
35#include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
36#include <linux/netfilter_ipv4/ip_tables.h>
37#include <linux/netfilter_ipv6/ip6_tables.h>
38#include <libiptc/libxtc.h>
39
40#ifndef NO_SHARED_LIBS
41#include <dlfcn.h>
42#endif
43#ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
44#	define IPT_SO_GET_REVISION_MATCH	(IPT_BASE_CTL + 2)
45#	define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
46#endif
47#ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
48#	define IP6T_SO_GET_REVISION_MATCH	68
49#	define IP6T_SO_GET_REVISION_TARGET	69
50#endif
51#include <getopt.h>
52
53
54#define NPROTO	255
55
56#ifndef PROC_SYS_MODPROBE
57#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
58#endif
59
60void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
61
62struct xtables_globals *xt_params = NULL;
63
64void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
65{
66	va_list args;
67
68	va_start(args, msg);
69	fprintf(stderr, "%s v%s: ", xt_params->program_name, xt_params->program_version);
70	vfprintf(stderr, msg, args);
71	va_end(args);
72	fprintf(stderr, "\n");
73	exit(status);
74}
75
76
77void xtables_free_opts(int reset_offset)
78{
79	if (xt_params->opts != xt_params->orig_opts) {
80		free(xt_params->opts);
81		xt_params->opts = xt_params->orig_opts;
82		if (reset_offset)
83			xt_params->option_offset = 0;
84	}
85}
86
87struct option *xtables_merge_options(struct option *oldopts,
88				     const struct option *newopts,
89				     unsigned int *option_offset)
90{
91	unsigned int num_old, num_new, i;
92	struct option *merge;
93
94	if (newopts == NULL)
95		return oldopts;
96
97	for (num_old = 0; oldopts[num_old].name; num_old++) ;
98	for (num_new = 0; newopts[num_new].name; num_new++) ;
99
100	xt_params->option_offset += 256;
101	*option_offset = xt_params->option_offset;
102
103	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
104	if (merge == NULL)
105		return NULL;
106	memcpy(merge, oldopts, num_old * sizeof(struct option));
107	xtables_free_opts(0);	/* Release any old options merged  */
108	for (i = 0; i < num_new; i++) {
109		merge[num_old + i] = newopts[i];
110		merge[num_old + i].val += *option_offset;
111	}
112	memset(merge + num_old + num_new, 0, sizeof(struct option));
113
114	return merge;
115}
116
117void xtables_set_revision(char *name, u_int8_t revision)
118{
119	/* Old kernel sources don't have ".revision" field,
120	*            but we stole a byte from name. */
121	name[XT_FUNCTION_MAXNAMELEN - 2] = '\0';
122	name[XT_FUNCTION_MAXNAMELEN - 1] = revision;
123}
124
125/**
126 * xtables_afinfo - protocol family dependent information
127 * @kmod:		kernel module basename (e.g. "ip_tables")
128 * @libprefix:		prefix of .so library name (e.g. "libipt_")
129 * @family:		nfproto family
130 * @ipproto:		used by setsockopt (e.g. IPPROTO_IP)
131 * @so_rev_match:	optname to check revision support of match
132 * @so_rev_target:	optname to check revision support of target
133 */
134struct xtables_afinfo {
135	const char *kmod;
136	const char *libprefix;
137	uint8_t family;
138	uint8_t ipproto;
139	int so_rev_match;
140	int so_rev_target;
141};
142
143static const struct xtables_afinfo afinfo_ipv4 = {
144	.kmod          = "ip_tables",
145	.libprefix     = "libipt_",
146	.family	       = NFPROTO_IPV4,
147	.ipproto       = IPPROTO_IP,
148	.so_rev_match  = IPT_SO_GET_REVISION_MATCH,
149	.so_rev_target = IPT_SO_GET_REVISION_TARGET,
150};
151
152static const struct xtables_afinfo afinfo_ipv6 = {
153	.kmod          = "ip6_tables",
154	.libprefix     = "libip6t_",
155	.family        = NFPROTO_IPV6,
156	.ipproto       = IPPROTO_IPV6,
157	.so_rev_match  = IP6T_SO_GET_REVISION_MATCH,
158	.so_rev_target = IP6T_SO_GET_REVISION_TARGET,
159};
160
161static const struct xtables_afinfo *afinfo;
162
163/* Search path for Xtables .so files */
164static const char *xtables_libdir;
165
166/* the path to command to load kernel module */
167const char *xtables_modprobe_program;
168
169/* Keeping track of external matches and targets: linked lists.  */
170struct xtables_match *xtables_matches;
171struct xtables_target *xtables_targets;
172
173void xtables_init(void)
174{
175	xtables_libdir = getenv("XTABLES_LIBDIR");
176	if (xtables_libdir != NULL)
177		return;
178	xtables_libdir = getenv("IPTABLES_LIB_DIR");
179	if (xtables_libdir != NULL) {
180		fprintf(stderr, "IPTABLES_LIB_DIR is deprecated, "
181		        "use XTABLES_LIBDIR.\n");
182		return;
183	}
184	/*
185	 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
186	 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
187	 * for these env vars are deprecated anyhow, and in light of the
188	 * (shared) libxt_*.so files, makes less sense to have
189	 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
190	 */
191	xtables_libdir = getenv("IP6TABLES_LIB_DIR");
192	if (xtables_libdir != NULL) {
193		fprintf(stderr, "IP6TABLES_LIB_DIR is deprecated, "
194		        "use XTABLES_LIBDIR.\n");
195		return;
196	}
197	xtables_libdir = XTABLES_LIBDIR;
198}
199
200void xtables_set_nfproto(uint8_t nfproto)
201{
202	switch (nfproto) {
203	case NFPROTO_IPV4:
204		afinfo = &afinfo_ipv4;
205		break;
206	case NFPROTO_IPV6:
207		afinfo = &afinfo_ipv6;
208		break;
209	default:
210		fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
211		        __func__);
212	}
213}
214
215/**
216 * xtables_set_params - set the global parameters used by xtables
217 * @xtp:	input xtables_globals structure
218 *
219 * The app is expected to pass a valid xtables_globals data-filled
220 * with proper values
221 * @xtp cannot be NULL
222 *
223 * Returns -1 on failure to set and 0 on success
224 */
225int xtables_set_params(struct xtables_globals *xtp)
226{
227	if (!xtp) {
228		fprintf(stderr, "%s: Illegal global params\n",__func__);
229		return -1;
230	}
231
232	xt_params = xtp;
233
234	if (!xt_params->exit_err)
235		xt_params->exit_err = basic_exit_err;
236
237	return 0;
238}
239
240int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
241{
242	xtables_init();
243	xtables_set_nfproto(nfproto);
244	return xtables_set_params(xtp);
245}
246
247/**
248 * xtables_*alloc - wrappers that exit on failure
249 */
250void *xtables_calloc(size_t count, size_t size)
251{
252	void *p;
253
254	if ((p = calloc(count, size)) == NULL) {
255		perror("ip[6]tables: calloc failed");
256		exit(1);
257	}
258
259	return p;
260}
261
262void *xtables_malloc(size_t size)
263{
264	void *p;
265
266	if ((p = malloc(size)) == NULL) {
267		perror("ip[6]tables: malloc failed");
268		exit(1);
269	}
270
271	return p;
272}
273
274static char *get_modprobe(void)
275{
276	int procfile;
277	char *ret;
278
279#define PROCFILE_BUFSIZ	1024
280	procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
281	if (procfile < 0)
282		return NULL;
283
284	ret = (char *) malloc(PROCFILE_BUFSIZ);
285	if (ret) {
286		memset(ret, 0, PROCFILE_BUFSIZ);
287		switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
288		case -1: goto fail;
289		case PROCFILE_BUFSIZ: goto fail; /* Partial read.  Wierd */
290		}
291		if (ret[strlen(ret)-1]=='\n')
292			ret[strlen(ret)-1]=0;
293		close(procfile);
294		return ret;
295	}
296 fail:
297	free(ret);
298	close(procfile);
299	return NULL;
300}
301
302int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
303{
304	char *buf = NULL;
305	char *argv[4];
306	int status;
307
308	/* If they don't explicitly set it, read out of kernel */
309	if (!modprobe) {
310		buf = get_modprobe();
311		if (!buf)
312			return -1;
313		modprobe = buf;
314	}
315
316	/*
317	 * Need to flush the buffer, or the child may output it again
318	 * when switching the program thru execv.
319	 */
320	fflush(stdout);
321
322	switch (fork()) {
323	case 0:
324		argv[0] = (char *)modprobe;
325		argv[1] = (char *)modname;
326		if (quiet) {
327			argv[2] = "-q";
328			argv[3] = NULL;
329		} else {
330			argv[2] = NULL;
331			argv[3] = NULL;
332		}
333		execv(argv[0], argv);
334
335		/* not usually reached */
336		exit(1);
337	case -1:
338		return -1;
339
340	default: /* parent */
341		wait(&status);
342	}
343
344	free(buf);
345	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
346		return 0;
347	return -1;
348}
349
350int xtables_load_ko(const char *modprobe, bool quiet)
351{
352	static bool loaded = false;
353	static int ret = -1;
354
355	if (!loaded) {
356		ret = xtables_insmod(afinfo->kmod, modprobe, quiet);
357		loaded = (ret == 0);
358	}
359
360	return ret;
361}
362
363/**
364 * xtables_strtou{i,l} - string to number conversion
365 * @s:	input string
366 * @end:	like strtoul's "end" pointer
367 * @value:	pointer for result
368 * @min:	minimum accepted value
369 * @max:	maximum accepted value
370 *
371 * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
372 * "15a" is rejected.
373 * In either case, the value obtained is compared for min-max compliance.
374 * Base is always 0, i.e. autodetect depending on @s.
375 *
376 * Returns true/false whether number was accepted. On failure, *value has
377 * undefined contents.
378 */
379bool xtables_strtoul(const char *s, char **end, unsigned long *value,
380                     unsigned long min, unsigned long max)
381{
382	unsigned long v;
383	char *my_end;
384
385	errno = 0;
386	v = strtoul(s, &my_end, 0);
387
388	if (my_end == s)
389		return false;
390	if (end != NULL)
391		*end = my_end;
392
393	if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
394		if (value != NULL)
395			*value = v;
396		if (end == NULL)
397			return *my_end == '\0';
398		return true;
399	}
400
401	return false;
402}
403
404bool xtables_strtoui(const char *s, char **end, unsigned int *value,
405                     unsigned int min, unsigned int max)
406{
407	unsigned long v;
408	bool ret;
409
410	ret = xtables_strtoul(s, end, &v, min, max);
411	if (value != NULL)
412		*value = v;
413	return ret;
414}
415
416int xtables_service_to_port(const char *name, const char *proto)
417{
418	struct servent *service;
419
420	if ((service = getservbyname(name, proto)) != NULL)
421		return ntohs((unsigned short) service->s_port);
422
423	return -1;
424}
425
426u_int16_t xtables_parse_port(const char *port, const char *proto)
427{
428	unsigned int portnum;
429
430	if (xtables_strtoui(port, NULL, &portnum, 0, UINT16_MAX) ||
431	    (portnum = xtables_service_to_port(port, proto)) != (unsigned)-1)
432		return portnum;
433
434	xt_params->exit_err(PARAMETER_PROBLEM,
435		   "invalid port/service `%s' specified", port);
436}
437
438void xtables_parse_interface(const char *arg, char *vianame,
439			     unsigned char *mask)
440{
441	int vialen = strlen(arg);
442	unsigned int i;
443
444	memset(mask, 0, IFNAMSIZ);
445	memset(vianame, 0, IFNAMSIZ);
446
447	if (vialen + 1 > IFNAMSIZ)
448		xt_params->exit_err(PARAMETER_PROBLEM,
449			   "interface name `%s' must be shorter than IFNAMSIZ"
450			   " (%i)", arg, IFNAMSIZ-1);
451
452	strcpy(vianame, arg);
453	if ((vialen == 0) || (vialen == 1 && vianame[0] == '+'))
454		memset(mask, 0, IFNAMSIZ);
455	else if (vianame[vialen - 1] == '+') {
456		memset(mask, 0xFF, vialen - 1);
457		memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
458		/* Don't remove `+' here! -HW */
459	} else {
460		/* Include nul-terminator in match */
461		memset(mask, 0xFF, vialen + 1);
462		memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
463		for (i = 0; vianame[i]; i++) {
464			if (vianame[i] == ':' ||
465			    vianame[i] == '!' ||
466			    vianame[i] == '*') {
467				fprintf(stderr,
468					"Warning: weird character in interface"
469					" `%s' (No aliases, :, ! or *).\n",
470					vianame);
471				break;
472			}
473		}
474	}
475}
476
477#ifndef NO_SHARED_LIBS
478static void *load_extension(const char *search_path, const char *prefix,
479    const char *name, bool is_target)
480{
481	const char *dir = search_path, *next;
482	void *ptr = NULL;
483	struct stat sb;
484	char path[256];
485
486	do {
487		next = strchr(dir, ':');
488		if (next == NULL)
489			next = dir + strlen(dir);
490		snprintf(path, sizeof(path), "%.*s/libxt_%s.so",
491		         (unsigned int)(next - dir), dir, name);
492
493		if (dlopen(path, RTLD_NOW) != NULL) {
494			/* Found library.  If it didn't register itself,
495			   maybe they specified target as match. */
496			if (is_target)
497				ptr = xtables_find_target(name, XTF_DONT_LOAD);
498			else
499				ptr = xtables_find_match(name,
500				      XTF_DONT_LOAD, NULL);
501		} else if (stat(path, &sb) == 0) {
502			fprintf(stderr, "%s: %s\n", path, dlerror());
503		}
504
505		if (ptr != NULL)
506			return ptr;
507
508		snprintf(path, sizeof(path), "%.*s/%s%s.so",
509		         (unsigned int)(next - dir), dir, prefix, name);
510		if (dlopen(path, RTLD_NOW) != NULL) {
511			if (is_target)
512				ptr = xtables_find_target(name, XTF_DONT_LOAD);
513			else
514				ptr = xtables_find_match(name,
515				      XTF_DONT_LOAD, NULL);
516		} else if (stat(path, &sb) == 0) {
517			fprintf(stderr, "%s: %s\n", path, dlerror());
518		}
519
520		if (ptr != NULL)
521			return ptr;
522
523		dir = next + 1;
524	} while (*next != '\0');
525
526	return NULL;
527}
528#endif
529
530struct xtables_match *
531xtables_find_match(const char *name, enum xtables_tryload tryload,
532		   struct xtables_rule_match **matches)
533{
534	struct xtables_match *ptr;
535	const char *icmp6 = "icmp6";
536
537	/* This is ugly as hell. Nonetheless, there is no way of changing
538	 * this without hurting backwards compatibility */
539	if ( (strcmp(name,"icmpv6") == 0) ||
540	     (strcmp(name,"ipv6-icmp") == 0) ||
541	     (strcmp(name,"icmp6") == 0) )
542		name = icmp6;
543
544	for (ptr = xtables_matches; ptr; ptr = ptr->next) {
545		if (strcmp(name, ptr->name) == 0) {
546			struct xtables_match *clone;
547
548			/* First match of this type: */
549			if (ptr->m == NULL)
550				break;
551
552			/* Second and subsequent clones */
553			clone = xtables_malloc(sizeof(struct xtables_match));
554			memcpy(clone, ptr, sizeof(struct xtables_match));
555			clone->mflags = 0;
556			/* This is a clone: */
557			clone->next = clone;
558
559			ptr = clone;
560			break;
561		}
562	}
563
564#ifndef NO_SHARED_LIBS
565	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
566		ptr = load_extension(xtables_libdir, afinfo->libprefix,
567		      name, false);
568
569		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
570			xt_params->exit_err(PARAMETER_PROBLEM,
571				   "Couldn't load match `%s':%s\n",
572				   name, dlerror());
573	}
574#else
575	if (ptr && !ptr->loaded) {
576		if (tryload != XTF_DONT_LOAD)
577			ptr->loaded = 1;
578		else
579			ptr = NULL;
580	}
581	if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
582		xt_params->exit_err(PARAMETER_PROBLEM,
583			   "Couldn't find match `%s'\n", name);
584	}
585#endif
586
587	if (ptr && matches) {
588		struct xtables_rule_match **i;
589		struct xtables_rule_match *newentry;
590
591		newentry = xtables_malloc(sizeof(struct xtables_rule_match));
592
593		for (i = matches; *i; i = &(*i)->next) {
594			if (strcmp(name, (*i)->match->name) == 0)
595				(*i)->completed = true;
596		}
597		newentry->match = ptr;
598		newentry->completed = false;
599		newentry->next = NULL;
600		*i = newentry;
601	}
602
603	return ptr;
604}
605
606struct xtables_target *
607xtables_find_target(const char *name, enum xtables_tryload tryload)
608{
609	struct xtables_target *ptr;
610
611	/* Standard target? */
612	if (strcmp(name, "") == 0
613	    || strcmp(name, XTC_LABEL_ACCEPT) == 0
614	    || strcmp(name, XTC_LABEL_DROP) == 0
615	    || strcmp(name, XTC_LABEL_QUEUE) == 0
616	    || strcmp(name, XTC_LABEL_RETURN) == 0)
617		name = "standard";
618
619	for (ptr = xtables_targets; ptr; ptr = ptr->next) {
620		if (strcmp(name, ptr->name) == 0)
621			break;
622	}
623
624#ifndef NO_SHARED_LIBS
625	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
626		ptr = load_extension(xtables_libdir, afinfo->libprefix,
627		      name, true);
628
629		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
630			xt_params->exit_err(PARAMETER_PROBLEM,
631				   "Couldn't load target `%s':%s\n",
632				   name, dlerror());
633	}
634#else
635	if (ptr && !ptr->loaded) {
636		if (tryload != XTF_DONT_LOAD)
637			ptr->loaded = 1;
638		else
639			ptr = NULL;
640	}
641	if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
642		xt_params->exit_err(PARAMETER_PROBLEM,
643			   "Couldn't find target `%s'\n", name);
644	}
645#endif
646
647	if (ptr)
648		ptr->used = 1;
649
650	return ptr;
651}
652
653static int compatible_revision(const char *name, u_int8_t revision, int opt)
654{
655	struct xt_get_revision rev;
656	socklen_t s = sizeof(rev);
657	int max_rev, sockfd;
658
659	sockfd = socket(afinfo->family, SOCK_RAW, IPPROTO_RAW);
660	if (sockfd < 0) {
661		if (errno == EPERM) {
662			/* revision 0 is always supported. */
663			if (revision != 0)
664				fprintf(stderr, "Could not determine whether "
665						"revision %u is supported, "
666						"assuming it is.\n",
667					revision);
668			return 1;
669		}
670		fprintf(stderr, "Could not open socket to kernel: %s\n",
671			strerror(errno));
672		exit(1);
673	}
674
675	xtables_load_ko(xtables_modprobe_program, true);
676
677	strcpy(rev.name, name);
678	rev.revision = revision;
679
680	max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
681	if (max_rev < 0) {
682		/* Definitely don't support this? */
683		if (errno == ENOENT || errno == EPROTONOSUPPORT) {
684			close(sockfd);
685			return 0;
686		} else if (errno == ENOPROTOOPT) {
687			close(sockfd);
688			/* Assume only revision 0 support (old kernel) */
689			return (revision == 0);
690		} else {
691			fprintf(stderr, "getsockopt failed strangely: %s\n",
692				strerror(errno));
693			exit(1);
694		}
695	}
696	close(sockfd);
697	return 1;
698}
699
700
701static int compatible_match_revision(const char *name, u_int8_t revision)
702{
703	return compatible_revision(name, revision, afinfo->so_rev_match);
704}
705
706static int compatible_target_revision(const char *name, u_int8_t revision)
707{
708	return compatible_revision(name, revision, afinfo->so_rev_target);
709}
710
711void xtables_register_match(struct xtables_match *me)
712{
713	struct xtables_match **i, *old;
714
715	if (strcmp(me->version, XTABLES_VERSION) != 0) {
716		fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
717		        "but \"%s\" is required.\n",
718			xt_params->program_name, me->name,
719			me->version, XTABLES_VERSION);
720		exit(1);
721	}
722
723	/* Revision field stole a char from name. */
724	if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
725		fprintf(stderr, "%s: target `%s' has invalid name\n",
726			xt_params->program_name, me->name);
727		exit(1);
728	}
729
730	if (me->family >= NPROTO) {
731		fprintf(stderr,
732			"%s: BUG: match %s has invalid protocol family\n",
733			xt_params->program_name, me->name);
734		exit(1);
735	}
736
737	/* ignore not interested match */
738	if (me->family != afinfo->family && me->family != AF_UNSPEC)
739		return;
740
741	old = xtables_find_match(me->name, XTF_DURING_LOAD, NULL);
742	if (old) {
743		if (old->revision == me->revision &&
744		    old->family == me->family) {
745			fprintf(stderr,
746				"%s: match `%s' already registered.\n",
747				xt_params->program_name, me->name);
748			exit(1);
749		}
750
751		/* Now we have two (or more) options, check compatibility. */
752		if (compatible_match_revision(old->name, old->revision)
753		    && old->revision > me->revision)
754			return;
755
756		/* See if new match can be used. */
757		if (!compatible_match_revision(me->name, me->revision))
758			return;
759
760		/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
761		if (old->revision == me->revision && me->family == AF_UNSPEC)
762			return;
763
764		/* Delete old one. */
765		for (i = &xtables_matches; *i!=old; i = &(*i)->next);
766		*i = old->next;
767	}
768
769	if (me->size != XT_ALIGN(me->size)) {
770		fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
771		        xt_params->program_name, me->name,
772		        (unsigned int)me->size);
773		exit(1);
774	}
775
776	/* Append to list. */
777	for (i = &xtables_matches; *i; i = &(*i)->next);
778	me->next = NULL;
779	*i = me;
780
781	me->m = NULL;
782	me->mflags = 0;
783}
784
785void xtables_register_target(struct xtables_target *me)
786{
787	struct xtables_target *old;
788
789	if (strcmp(me->version, XTABLES_VERSION) != 0) {
790		fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
791		        "but \"%s\" is required.\n",
792			xt_params->program_name, me->name,
793			me->version, XTABLES_VERSION);
794		exit(1);
795	}
796
797	/* Revision field stole a char from name. */
798	if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
799		fprintf(stderr, "%s: target `%s' has invalid name\n",
800			xt_params->program_name, me->name);
801		exit(1);
802	}
803
804	if (me->family >= NPROTO) {
805		fprintf(stderr,
806			"%s: BUG: target %s has invalid protocol family\n",
807			xt_params->program_name, me->name);
808		exit(1);
809	}
810
811	/* ignore not interested target */
812	if (me->family != afinfo->family && me->family != AF_UNSPEC)
813		return;
814
815	old = xtables_find_target(me->name, XTF_DURING_LOAD);
816	if (old) {
817		struct xtables_target **i;
818
819		if (old->revision == me->revision &&
820		    old->family == me->family) {
821			fprintf(stderr,
822				"%s: target `%s' already registered.\n",
823				xt_params->program_name, me->name);
824			exit(1);
825		}
826
827		/* Now we have two (or more) options, check compatibility. */
828		if (compatible_target_revision(old->name, old->revision)
829		    && old->revision > me->revision)
830			return;
831
832		/* See if new target can be used. */
833		if (!compatible_target_revision(me->name, me->revision))
834			return;
835
836		/* Prefer !AF_UNSPEC over AF_UNSPEC for same revision. */
837		if (old->revision == me->revision && me->family == AF_UNSPEC)
838			return;
839
840		/* Delete old one. */
841		for (i = &xtables_targets; *i!=old; i = &(*i)->next);
842		*i = old->next;
843	}
844
845	if (me->size != XT_ALIGN(me->size)) {
846		fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
847		        xt_params->program_name, me->name,
848		        (unsigned int)me->size);
849		exit(1);
850	}
851
852	/* Prepend to list. */
853	me->next = xtables_targets;
854	xtables_targets = me;
855	me->t = NULL;
856	me->tflags = 0;
857}
858
859/**
860 * xtables_param_act - act on condition
861 * @status:	a constant from enum xtables_exittype
862 *
863 * %XTF_ONLY_ONCE: print error message that option may only be used once.
864 * @p1:		module name (e.g. "mark")
865 * @p2(...):	option in conflict (e.g. "--mark")
866 * @p3(...):	condition to match on (see extensions/ for examples)
867 *
868 * %XTF_NO_INVERT: option does not support inversion
869 * @p1:		module name
870 * @p2:		option in conflict
871 * @p3:		condition to match on
872 *
873 * %XTF_BAD_VALUE: bad value for option
874 * @p1:		module name
875 * @p2:		option with which the problem occured (e.g. "--mark")
876 * @p3:		string the user passed in (e.g. "99999999999999")
877 *
878 * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
879 * @p1:		module name
880 *
881 * Displays an error message and exits the program.
882 */
883void xtables_param_act(unsigned int status, const char *p1, ...)
884{
885	const char *p2, *p3;
886	va_list args;
887	bool b;
888
889	va_start(args, p1);
890
891	switch (status) {
892	case XTF_ONLY_ONCE:
893		p2 = va_arg(args, const char *);
894		b  = va_arg(args, unsigned int);
895		if (!b)
896			return;
897		xt_params->exit_err(PARAMETER_PROBLEM,
898		           "%s: \"%s\" option may only be specified once",
899		           p1, p2);
900		break;
901	case XTF_NO_INVERT:
902		p2 = va_arg(args, const char *);
903		b  = va_arg(args, unsigned int);
904		if (!b)
905			return;
906		xt_params->exit_err(PARAMETER_PROBLEM,
907		           "%s: \"%s\" option cannot be inverted", p1, p2);
908		break;
909	case XTF_BAD_VALUE:
910		p2 = va_arg(args, const char *);
911		p3 = va_arg(args, const char *);
912		xt_params->exit_err(PARAMETER_PROBLEM,
913		           "%s: Bad value for \"%s\" option: \"%s\"",
914		           p1, p2, p3);
915		break;
916	case XTF_ONE_ACTION:
917		b = va_arg(args, unsigned int);
918		if (!b)
919			return;
920		xt_params->exit_err(PARAMETER_PROBLEM,
921		           "%s: At most one action is possible", p1);
922		break;
923	default:
924		xt_params->exit_err(status, p1, args);
925		break;
926	}
927
928	va_end(args);
929}
930
931const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
932{
933	static char buf[20];
934	const unsigned char *bytep = (const void *)&addrp->s_addr;
935
936	sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
937	return buf;
938}
939
940static const char *ipaddr_to_host(const struct in_addr *addr)
941{
942	struct hostent *host;
943
944	host = gethostbyaddr(addr, sizeof(struct in_addr), AF_INET);
945	if (host == NULL)
946		return NULL;
947
948	return host->h_name;
949}
950
951static const char *ipaddr_to_network(const struct in_addr *addr)
952{
953	struct netent *net;
954
955	if ((net = getnetbyaddr(ntohl(addr->s_addr), AF_INET)) != NULL)
956		return net->n_name;
957
958	return NULL;
959}
960
961const char *xtables_ipaddr_to_anyname(const struct in_addr *addr)
962{
963	const char *name;
964
965	if ((name = ipaddr_to_host(addr)) != NULL ||
966	    (name = ipaddr_to_network(addr)) != NULL)
967		return name;
968
969	return xtables_ipaddr_to_numeric(addr);
970}
971
972const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
973{
974	static char buf[20];
975	uint32_t maskaddr, bits;
976	int i;
977
978	maskaddr = ntohl(mask->s_addr);
979
980	if (maskaddr == 0xFFFFFFFFL)
981		/* we don't want to see "/32" */
982		return "";
983
984	i = 32;
985	bits = 0xFFFFFFFEL;
986	while (--i >= 0 && maskaddr != bits)
987		bits <<= 1;
988	if (i >= 0)
989		sprintf(buf, "/%d", i);
990	else
991		/* mask was not a decent combination of 1's and 0's */
992		sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
993
994	return buf;
995}
996
997static struct in_addr *__numeric_to_ipaddr(const char *dotted, bool is_mask)
998{
999	static struct in_addr addr;
1000	unsigned char *addrp;
1001	unsigned int onebyte;
1002	char buf[20], *p, *q;
1003	int i;
1004
1005	/* copy dotted string, because we need to modify it */
1006	strncpy(buf, dotted, sizeof(buf) - 1);
1007	buf[sizeof(buf) - 1] = '\0';
1008	addrp = (void *)&addr.s_addr;
1009
1010	p = buf;
1011	for (i = 0; i < 3; ++i) {
1012		if ((q = strchr(p, '.')) == NULL) {
1013			if (is_mask)
1014				return NULL;
1015
1016			/* autocomplete, this is a network address */
1017			if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1018				return NULL;
1019
1020			addrp[i] = onebyte;
1021			while (i < 3)
1022				addrp[++i] = 0;
1023
1024			return &addr;
1025		}
1026
1027		*q = '\0';
1028		if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1029			return NULL;
1030
1031		addrp[i] = onebyte;
1032		p = q + 1;
1033	}
1034
1035	/* we have checked 3 bytes, now we check the last one */
1036	if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1037		return NULL;
1038
1039	addrp[3] = onebyte;
1040	return &addr;
1041}
1042
1043struct in_addr *xtables_numeric_to_ipaddr(const char *dotted)
1044{
1045	return __numeric_to_ipaddr(dotted, false);
1046}
1047
1048struct in_addr *xtables_numeric_to_ipmask(const char *dotted)
1049{
1050	return __numeric_to_ipaddr(dotted, true);
1051}
1052
1053static struct in_addr *network_to_ipaddr(const char *name)
1054{
1055	static struct in_addr addr;
1056	struct netent *net;
1057
1058	if ((net = getnetbyname(name)) != NULL) {
1059		if (net->n_addrtype != AF_INET)
1060			return NULL;
1061		addr.s_addr = htonl(net->n_net);
1062		return &addr;
1063	}
1064
1065	return NULL;
1066}
1067
1068static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
1069{
1070	struct hostent *host;
1071	struct in_addr *addr;
1072	unsigned int i;
1073
1074	*naddr = 0;
1075	if ((host = gethostbyname(name)) != NULL) {
1076		if (host->h_addrtype != AF_INET ||
1077		    host->h_length != sizeof(struct in_addr))
1078			return NULL;
1079
1080		while (host->h_addr_list[*naddr] != NULL)
1081			++*naddr;
1082		addr = xtables_calloc(*naddr, sizeof(struct in_addr) * *naddr);
1083		for (i = 0; i < *naddr; i++)
1084			memcpy(&addr[i], host->h_addr_list[i],
1085			       sizeof(struct in_addr));
1086		return addr;
1087	}
1088
1089	return NULL;
1090}
1091
1092static struct in_addr *
1093ipparse_hostnetwork(const char *name, unsigned int *naddrs)
1094{
1095	struct in_addr *addrptmp, *addrp;
1096
1097	if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
1098	    (addrptmp = network_to_ipaddr(name)) != NULL) {
1099		addrp = xtables_malloc(sizeof(struct in_addr));
1100		memcpy(addrp, addrptmp, sizeof(*addrp));
1101		*naddrs = 1;
1102		return addrp;
1103	}
1104	if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
1105		return addrptmp;
1106
1107	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
1108}
1109
1110static struct in_addr *parse_ipmask(const char *mask)
1111{
1112	static struct in_addr maskaddr;
1113	struct in_addr *addrp;
1114	unsigned int bits;
1115
1116	if (mask == NULL) {
1117		/* no mask at all defaults to 32 bits */
1118		maskaddr.s_addr = 0xFFFFFFFF;
1119		return &maskaddr;
1120	}
1121	if ((addrp = xtables_numeric_to_ipmask(mask)) != NULL)
1122		/* dotted_to_addr already returns a network byte order addr */
1123		return addrp;
1124	if (!xtables_strtoui(mask, NULL, &bits, 0, 32))
1125		xt_params->exit_err(PARAMETER_PROBLEM,
1126			   "invalid mask `%s' specified", mask);
1127	if (bits != 0) {
1128		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
1129		return &maskaddr;
1130	}
1131
1132	maskaddr.s_addr = 0U;
1133	return &maskaddr;
1134}
1135
1136/**
1137 * xtables_ipparse_any - transform arbitrary name to in_addr
1138 *
1139 * Possible inputs (pseudo regex):
1140 * 	m{^($hostname|$networkname|$ipaddr)(/$mask)?}
1141 * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
1142 */
1143void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
1144                         struct in_addr *maskp, unsigned int *naddrs)
1145{
1146	unsigned int i, j, k, n;
1147	struct in_addr *addrp;
1148	char buf[256], *p;
1149
1150	strncpy(buf, name, sizeof(buf) - 1);
1151	buf[sizeof(buf) - 1] = '\0';
1152	if ((p = strrchr(buf, '/')) != NULL) {
1153		*p = '\0';
1154		addrp = parse_ipmask(p + 1);
1155	} else {
1156		addrp = parse_ipmask(NULL);
1157	}
1158	memcpy(maskp, addrp, sizeof(*maskp));
1159
1160	/* if a null mask is given, the name is ignored, like in "any/0" */
1161	if (maskp->s_addr == 0U)
1162		strcpy(buf, "0.0.0.0");
1163
1164	addrp = *addrpp = ipparse_hostnetwork(buf, naddrs);
1165	n = *naddrs;
1166	for (i = 0, j = 0; i < n; ++i) {
1167		addrp[j++].s_addr &= maskp->s_addr;
1168		for (k = 0; k < j - 1; ++k)
1169			if (addrp[k].s_addr == addrp[j-1].s_addr) {
1170				--*naddrs;
1171				--j;
1172				break;
1173			}
1174	}
1175}
1176
1177const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
1178{
1179	/* 0000:0000:0000:0000:0000:000.000.000.000
1180	 * 0000:0000:0000:0000:0000:0000:0000:0000 */
1181	static char buf[50+1];
1182	return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
1183}
1184
1185static const char *ip6addr_to_host(const struct in6_addr *addr)
1186{
1187	static char hostname[NI_MAXHOST];
1188	struct sockaddr_in6 saddr;
1189	int err;
1190
1191	memset(&saddr, 0, sizeof(struct sockaddr_in6));
1192	memcpy(&saddr.sin6_addr, addr, sizeof(*addr));
1193	saddr.sin6_family = AF_INET6;
1194
1195	err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
1196	      hostname, sizeof(hostname) - 1, NULL, 0, 0);
1197	if (err != 0) {
1198#ifdef DEBUG
1199		fprintf(stderr,"IP2Name: %s\n",gai_strerror(err));
1200#endif
1201		return NULL;
1202	}
1203
1204#ifdef DEBUG
1205	fprintf (stderr, "\naddr2host: %s\n", hostname);
1206#endif
1207	return hostname;
1208}
1209
1210const char *xtables_ip6addr_to_anyname(const struct in6_addr *addr)
1211{
1212	const char *name;
1213
1214	if ((name = ip6addr_to_host(addr)) != NULL)
1215		return name;
1216
1217	return xtables_ip6addr_to_numeric(addr);
1218}
1219
1220static int ip6addr_prefix_length(const struct in6_addr *k)
1221{
1222	unsigned int bits = 0;
1223	uint32_t a, b, c, d;
1224
1225	a = ntohl(k->s6_addr32[0]);
1226	b = ntohl(k->s6_addr32[1]);
1227	c = ntohl(k->s6_addr32[2]);
1228	d = ntohl(k->s6_addr32[3]);
1229	while (a & 0x80000000U) {
1230		++bits;
1231		a <<= 1;
1232		a  |= (b >> 31) & 1;
1233		b <<= 1;
1234		b  |= (c >> 31) & 1;
1235		c <<= 1;
1236		c  |= (d >> 31) & 1;
1237		d <<= 1;
1238	}
1239	if (a != 0 || b != 0 || c != 0 || d != 0)
1240		return -1;
1241	return bits;
1242}
1243
1244const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
1245{
1246	static char buf[50+2];
1247	int l = ip6addr_prefix_length(addrp);
1248
1249	if (l == -1) {
1250		strcpy(buf, "/");
1251		strcat(buf, xtables_ip6addr_to_numeric(addrp));
1252		return buf;
1253	}
1254	sprintf(buf, "/%d", l);
1255	return buf;
1256}
1257
1258struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
1259{
1260	static struct in6_addr ap;
1261	int err;
1262
1263	if ((err = inet_pton(AF_INET6, num, &ap)) == 1)
1264		return &ap;
1265#ifdef DEBUG
1266	fprintf(stderr, "\nnumeric2addr: %d\n", err);
1267#endif
1268	return NULL;
1269}
1270
1271static struct in6_addr *
1272host_to_ip6addr(const char *name, unsigned int *naddr)
1273{
1274	static struct in6_addr *addr;
1275	struct addrinfo hints;
1276	struct addrinfo *res;
1277	int err;
1278
1279	memset(&hints, 0, sizeof(hints));
1280	hints.ai_flags    = AI_CANONNAME;
1281	hints.ai_family   = AF_INET6;
1282	hints.ai_socktype = SOCK_RAW;
1283	hints.ai_protocol = IPPROTO_IPV6;
1284	hints.ai_next     = NULL;
1285
1286	*naddr = 0;
1287	if ((err = getaddrinfo(name, NULL, &hints, &res)) != 0) {
1288#ifdef DEBUG
1289		fprintf(stderr,"Name2IP: %s\n",gai_strerror(err));
1290#endif
1291		return NULL;
1292	} else {
1293		if (res->ai_family != AF_INET6 ||
1294		    res->ai_addrlen != sizeof(struct sockaddr_in6))
1295			return NULL;
1296
1297#ifdef DEBUG
1298		fprintf(stderr, "resolved: len=%d  %s ", res->ai_addrlen,
1299		        ip6addr_to_numeric(&((struct sockaddr_in6 *)res->ai_addr)->sin6_addr));
1300#endif
1301		/* Get the first element of the address-chain */
1302		addr = xtables_malloc(sizeof(struct in6_addr));
1303		memcpy(addr, &((const struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
1304		       sizeof(struct in6_addr));
1305		freeaddrinfo(res);
1306		*naddr = 1;
1307		return addr;
1308	}
1309
1310	return NULL;
1311}
1312
1313static struct in6_addr *network_to_ip6addr(const char *name)
1314{
1315	/*	abort();*/
1316	/* TODO: not implemented yet, but the exception breaks the
1317	 *       name resolvation */
1318	return NULL;
1319}
1320
1321static struct in6_addr *
1322ip6parse_hostnetwork(const char *name, unsigned int *naddrs)
1323{
1324	struct in6_addr *addrp, *addrptmp;
1325
1326	if ((addrptmp = xtables_numeric_to_ip6addr(name)) != NULL ||
1327	    (addrptmp = network_to_ip6addr(name)) != NULL) {
1328		addrp = xtables_malloc(sizeof(struct in6_addr));
1329		memcpy(addrp, addrptmp, sizeof(*addrp));
1330		*naddrs = 1;
1331		return addrp;
1332	}
1333	if ((addrp = host_to_ip6addr(name, naddrs)) != NULL)
1334		return addrp;
1335
1336	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
1337}
1338
1339static struct in6_addr *parse_ip6mask(char *mask)
1340{
1341	static struct in6_addr maskaddr;
1342	struct in6_addr *addrp;
1343	unsigned int bits;
1344
1345	if (mask == NULL) {
1346		/* no mask at all defaults to 128 bits */
1347		memset(&maskaddr, 0xff, sizeof maskaddr);
1348		return &maskaddr;
1349	}
1350	if ((addrp = xtables_numeric_to_ip6addr(mask)) != NULL)
1351		return addrp;
1352	if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
1353		xt_params->exit_err(PARAMETER_PROBLEM,
1354			   "invalid mask `%s' specified", mask);
1355	if (bits != 0) {
1356		char *p = (void *)&maskaddr;
1357		memset(p, 0xff, bits / 8);
1358		memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
1359		p[bits/8] = 0xff << (8 - (bits & 7));
1360		return &maskaddr;
1361	}
1362
1363	memset(&maskaddr, 0, sizeof(maskaddr));
1364	return &maskaddr;
1365}
1366
1367void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp,
1368                          struct in6_addr *maskp, unsigned int *naddrs)
1369{
1370	struct in6_addr *addrp;
1371	unsigned int i, j, k, n;
1372	char buf[256], *p;
1373
1374	strncpy(buf, name, sizeof(buf) - 1);
1375	buf[sizeof(buf)-1] = '\0';
1376	if ((p = strrchr(buf, '/')) != NULL) {
1377		*p = '\0';
1378		addrp = parse_ip6mask(p + 1);
1379	} else {
1380		addrp = parse_ip6mask(NULL);
1381	}
1382	memcpy(maskp, addrp, sizeof(*maskp));
1383
1384	/* if a null mask is given, the name is ignored, like in "any/0" */
1385	if (memcmp(maskp, &in6addr_any, sizeof(in6addr_any)) == 0)
1386		strcpy(buf, "::");
1387
1388	addrp = *addrpp = ip6parse_hostnetwork(buf, naddrs);
1389	n = *naddrs;
1390	for (i = 0, j = 0; i < n; ++i) {
1391		for (k = 0; k < 4; ++k)
1392			addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
1393		++j;
1394		for (k = 0; k < j - 1; ++k)
1395			if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
1396				--*naddrs;
1397				--j;
1398				break;
1399			}
1400	}
1401}
1402
1403void xtables_save_string(const char *value)
1404{
1405	static const char no_quote_chars[] = "_-0123456789"
1406		"abcdefghijklmnopqrstuvwxyz"
1407		"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1408	static const char escape_chars[] = "\"\\'";
1409	size_t length;
1410	const char *p;
1411
1412	length = strcspn(value, no_quote_chars);
1413	if (length > 0 && value[length] == 0) {
1414		/* no quoting required */
1415		fputs(value, stdout);
1416		putchar(' ');
1417	} else {
1418		/* there is at least one dangerous character in the
1419		   value, which we have to quote.  Write double quotes
1420		   around the value and escape special characters with
1421		   a backslash */
1422		putchar('"');
1423
1424		for (p = strpbrk(value, escape_chars); p != NULL;
1425		     p = strpbrk(value, escape_chars)) {
1426			if (p > value)
1427				fwrite(value, 1, p - value, stdout);
1428			putchar('\\');
1429			putchar(*p);
1430			value = p + 1;
1431		}
1432
1433		/* print the rest and finish the double quoted
1434		   string */
1435		fputs(value, stdout);
1436		printf("\" ");
1437	}
1438}
1439
1440/**
1441 * Check for option-intrapositional negation.
1442 * Do not use in new code.
1443 */
1444int xtables_check_inverse(const char option[], int *invert,
1445			  int *my_optind, int argc)
1446{
1447	if (option && strcmp(option, "!") == 0) {
1448		fprintf(stderr, "Using intrapositioned negation "
1449		        "(`--option ! this`) is deprecated in favor of "
1450		        "extrapositioned (`! --option this`).\n");
1451
1452		if (*invert)
1453			xt_params->exit_err(PARAMETER_PROBLEM,
1454				   "Multiple `!' flags not allowed");
1455		*invert = true;
1456		if (my_optind != NULL) {
1457			++*my_optind;
1458			if (argc && *my_optind > argc)
1459				xt_params->exit_err(PARAMETER_PROBLEM,
1460					   "no argument following `!'");
1461		}
1462
1463		return true;
1464	}
1465	return false;
1466}
1467
1468const struct xtables_pprot xtables_chain_protos[] = {
1469	{"tcp",       IPPROTO_TCP},
1470	{"sctp",      IPPROTO_SCTP},
1471	{"udp",       IPPROTO_UDP},
1472	{"udplite",   IPPROTO_UDPLITE},
1473	{"icmp",      IPPROTO_ICMP},
1474	{"icmpv6",    IPPROTO_ICMPV6},
1475	{"ipv6-icmp", IPPROTO_ICMPV6},
1476	{"esp",       IPPROTO_ESP},
1477	{"ah",        IPPROTO_AH},
1478	{"ipv6-mh",   IPPROTO_MH},
1479	{"mh",        IPPROTO_MH},
1480	{"all",       0},
1481	{NULL},
1482};
1483
1484u_int16_t
1485xtables_parse_protocol(const char *s)
1486{
1487	unsigned int proto;
1488
1489	if (!xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX)) {
1490		struct protoent *pent;
1491
1492		/* first deal with the special case of 'all' to prevent
1493		 * people from being able to redefine 'all' in nsswitch
1494		 * and/or provoke expensive [not working] ldap/nis/...
1495		 * lookups */
1496		if (!strcmp(s, "all"))
1497			return 0;
1498
1499		if ((pent = getprotobyname(s)))
1500			proto = pent->p_proto;
1501		else {
1502			unsigned int i;
1503			for (i = 0; i < ARRAY_SIZE(xtables_chain_protos); ++i) {
1504				if (strcmp(s, xtables_chain_protos[i].name) == 0) {
1505					proto = xtables_chain_protos[i].num;
1506					break;
1507				}
1508			}
1509			if (i == ARRAY_SIZE(xtables_chain_protos))
1510				xt_params->exit_err(PARAMETER_PROBLEM,
1511					   "unknown protocol `%s' specified",
1512					   s);
1513		}
1514	}
1515
1516	return proto;
1517}
1518