xtoptions.c revision 44517bda3d8130638882f69478a8091316f30cbb
1/*
2 *	Argument parser
3 *	Copyright © Jan Engelhardt, 2011
4 *
5 *	This program is free software; you can redistribute it and/or
6 *	modify it under the terms of the GNU General Public License as
7 *	published by the Free Software Foundation; either version 2 of
8 *	the License, or (at your option) any later version.
9 */
10#include <ctype.h>
11#include <errno.h>
12#include <getopt.h>
13#include <limits.h>
14#include <netdb.h>
15#include <stdbool.h>
16#include <stdint.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <syslog.h>
21#include <arpa/inet.h>
22#include "xtables.h"
23#include "xshared.h"
24
25#define XTOPT_MKPTR(cb) \
26	((void *)((char *)(cb)->data + (cb)->entry->ptroff))
27
28/**
29 * Simple key-value pairs for syslog levels
30 */
31struct syslog_level {
32	char name[8];
33	uint8_t level;
34};
35
36/**
37 * Creates getopt options from the x6-style option map, and assigns each a
38 * getopt id.
39 */
40struct option *
41xtables_options_xfrm(struct option *orig_opts, struct option *oldopts,
42		     const struct xt_option_entry *entry, unsigned int *offset)
43{
44	unsigned int num_orig, num_old = 0, num_new, i;
45	struct option *merge, *mp;
46
47	if (entry == NULL)
48		return oldopts;
49	for (num_orig = 0; orig_opts[num_orig].name != NULL; ++num_orig)
50		;
51	if (oldopts != NULL)
52		for (num_old = 0; oldopts[num_old].name != NULL; ++num_old)
53			;
54	for (num_new = 0; entry[num_new].name != NULL; ++num_new)
55		;
56
57	/*
58	 * Since @oldopts also has @orig_opts already (and does so at the
59	 * start), skip these entries.
60	 */
61	oldopts += num_orig;
62	num_old -= num_orig;
63
64	merge = malloc(sizeof(*mp) * (num_orig + num_old + num_new + 1));
65	if (merge == NULL)
66		return NULL;
67
68	/* Let the base options -[ADI...] have precedence over everything */
69	memcpy(merge, orig_opts, sizeof(*mp) * num_orig);
70	mp = merge + num_orig;
71
72	/* Second, the new options */
73	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
74	*offset = xt_params->option_offset;
75
76	for (i = 0; i < num_new; ++i, ++mp, ++entry) {
77		mp->name         = entry->name;
78		mp->has_arg      = entry->type != XTTYPE_NONE;
79		mp->flag         = NULL;
80		mp->val          = entry->id + *offset;
81	}
82
83	/* Third, the old options */
84	memcpy(mp, oldopts, sizeof(*mp) * num_old);
85	mp += num_old;
86	xtables_free_opts(0);
87
88	/* Clear trailing entry */
89	memset(mp, 0, sizeof(*mp));
90	return merge;
91}
92
93/**
94 * Require a simple integer.
95 */
96static void xtopt_parse_int(struct xt_option_call *cb)
97{
98	const struct xt_option_entry *entry = cb->entry;
99	unsigned long long lmin = 0, lmax = UINT32_MAX;
100	unsigned int value;
101
102	if (entry->type == XTTYPE_UINT8)
103		lmax = UINT8_MAX;
104	else if (entry->type == XTTYPE_UINT16)
105		lmax = UINT16_MAX;
106	else if (entry->type == XTTYPE_UINT64)
107		lmax = UINT64_MAX;
108	if (cb->entry->min != 0)
109		lmin = cb->entry->min;
110	if (cb->entry->max != 0)
111		lmax = cb->entry->max;
112
113	if (!xtables_strtoui(cb->arg, NULL, &value, lmin, lmax))
114		xt_params->exit_err(PARAMETER_PROBLEM,
115			"%s: bad value for option \"--%s\", "
116			"or out of range (%llu-%llu).\n",
117			cb->ext_name, entry->name, lmin, lmax);
118
119	if (entry->type == XTTYPE_UINT8) {
120		cb->val.u8 = value;
121		if (entry->flags & XTOPT_PUT)
122			*(uint8_t *)XTOPT_MKPTR(cb) = cb->val.u8;
123	} else if (entry->type == XTTYPE_UINT16) {
124		cb->val.u16 = value;
125		if (entry->flags & XTOPT_PUT)
126			*(uint16_t *)XTOPT_MKPTR(cb) = cb->val.u16;
127	} else if (entry->type == XTTYPE_UINT32) {
128		cb->val.u32 = value;
129		if (entry->flags & XTOPT_PUT)
130			*(uint32_t *)XTOPT_MKPTR(cb) = cb->val.u32;
131	} else if (entry->type == XTTYPE_UINT64) {
132		cb->val.u64 = value;
133		if (entry->flags & XTOPT_PUT)
134			*(uint64_t *)XTOPT_MKPTR(cb) = cb->val.u64;
135	}
136}
137
138/**
139 * Multiple integer parse routine.
140 *
141 * This function is capable of parsing any number of fields. Only the first
142 * two values from the string will be put into @cb however (and as such,
143 * @cb->val.uXX_range is just that large) to cater for the few extensions that
144 * do not have a range[2] field, but {min, max}, and which cannot use
145 * XTOPT_POINTER.
146 */
147static void xtopt_parse_mint(struct xt_option_call *cb)
148{
149	const struct xt_option_entry *entry = cb->entry;
150	const char *arg = cb->arg;
151	size_t esize = sizeof(uint32_t);
152	char *put = XTOPT_MKPTR(cb);
153	unsigned int maxiter, value;
154	char *end = "";
155	char sep = ':';
156
157	if (entry->type == XTTYPE_UINT8RC)
158		esize = sizeof(uint8_t);
159	else if (entry->type == XTTYPE_UINT16RC)
160		esize = sizeof(uint16_t);
161	else if (entry->type == XTTYPE_UINT64RC)
162		esize = sizeof(uint64_t);
163	maxiter = entry->size / esize;
164	if (maxiter == 0)
165		maxiter = 2; /* ARRAY_SIZE(cb->val.uXX_range) */
166	if (entry->size % esize != 0)
167		xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
168			"not have proper size\n", __func__);
169
170	cb->nvals = 0;
171	for (arg = cb->arg; ; arg = end + 1) {
172		if (cb->nvals == maxiter)
173			xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
174				"components for option \"--%s\" (max: %u)\n",
175				cb->ext_name, entry->name, maxiter);
176		if (!xtables_strtoui(arg, &end, &value, 0, UINT32_MAX))
177			xt_params->exit_err(PARAMETER_PROBLEM,
178				"%s: bad value for option \"--%s\", "
179				"or out of range (0-%u).\n",
180				cb->ext_name, entry->name, UINT32_MAX);
181		if (*end != '\0' && *end != sep)
182			xt_params->exit_err(PARAMETER_PROBLEM,
183				"%s: Argument to \"--%s\" has unexpected "
184				"characters.\n", cb->ext_name, entry->name);
185		++cb->nvals;
186		if (cb->nvals < ARRAY_SIZE(cb->val.u32_range)) {
187			if (entry->type == XTTYPE_UINT8RC)
188				cb->val.u8_range[cb->nvals] = value;
189			else if (entry->type == XTTYPE_UINT16RC)
190				cb->val.u16_range[cb->nvals] = value;
191			else if (entry->type == XTTYPE_UINT32RC)
192				cb->val.u32_range[cb->nvals] = value;
193			else if (entry->type == XTTYPE_UINT64RC)
194				cb->val.u64_range[cb->nvals] = value;
195		}
196		if (entry->flags & XTOPT_PUT) {
197			if (entry->type == XTTYPE_UINT8RC)
198				*(uint8_t *)put = value;
199			else if (entry->type == XTTYPE_UINT16RC)
200				*(uint16_t *)put = value;
201			else if (entry->type == XTTYPE_UINT32RC)
202				*(uint32_t *)put = value;
203			else if (entry->type == XTTYPE_UINT64RC)
204				*(uint64_t *)put = value;
205			put += esize;
206		}
207		if (*end == '\0')
208			break;
209	}
210}
211
212static void xtopt_parse_string(struct xt_option_call *cb)
213{
214	const struct xt_option_entry *entry = cb->entry;
215	size_t z = strlen(cb->arg);
216	char *p;
217
218	if (entry->min != 0 && z < entry->min)
219		xt_params->exit_err(PARAMETER_PROBLEM,
220			"Argument must have a minimum length of "
221			"%u characters\n", entry->min);
222	if (entry->max != 0 && z > entry->max)
223		xt_params->exit_err(PARAMETER_PROBLEM,
224			"Argument must have a maximum length of "
225			"%u characters\n", entry->max);
226	if (!(entry->flags & XTOPT_PUT))
227		return;
228	if (z >= entry->size)
229		z = entry->size - 1;
230	p = XTOPT_MKPTR(cb);
231	strncpy(p, cb->arg, z);
232	p[z] = '\0';
233}
234
235/**
236 * Validate the input for being conformant to "mark[/mask]".
237 */
238static void xtopt_parse_markmask(struct xt_option_call *cb)
239{
240	unsigned int mark = 0, mask = ~0U;
241	char *end;
242
243	if (!xtables_strtoui(cb->arg, &end, &mark, 0, UINT32_MAX))
244		xt_params->exit_err(PARAMETER_PROBLEM,
245			"%s: bad mark value for option \"--%s\", "
246			"or out of range.\n",
247			cb->ext_name, cb->entry->name);
248	if (*end == '/' &&
249	    !xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
250		xt_params->exit_err(PARAMETER_PROBLEM,
251			"%s: bad mask value for option \"--%s\", "
252			"or out of range.\n",
253			cb->ext_name, cb->entry->name);
254	if (*end != '\0')
255		xt_params->exit_err(PARAMETER_PROBLEM,
256			"%s: trailing garbage after value "
257			"for option \"--%s\".\n",
258			cb->ext_name, cb->entry->name);
259	cb->val.mark = mark;
260	cb->val.mask = mask;
261}
262
263static int xtopt_sysloglvl_compare(const void *a, const void *b)
264{
265	const char *name = a;
266	const struct syslog_level *entry = b;
267
268	return strcmp(name, entry->name);
269}
270
271static void xtopt_parse_sysloglevel(struct xt_option_call *cb)
272{
273	static const struct syslog_level log_names[] = { /* must be sorted */
274		{"alert",   LOG_ALERT},
275		{"crit",    LOG_CRIT},
276		{"debug",   LOG_DEBUG},
277		{"emerg",   LOG_EMERG},
278		{"error",   LOG_ERR}, /* deprecated */
279		{"info",    LOG_INFO},
280		{"notice",  LOG_NOTICE},
281		{"panic",   LOG_EMERG}, /* deprecated */
282		{"warning", LOG_WARNING},
283	};
284	const struct syslog_level *e;
285	unsigned int num = 0;
286
287	if (!xtables_strtoui(cb->arg, NULL, &num, 0, 7)) {
288		e = bsearch(cb->arg, log_names, ARRAY_SIZE(log_names),
289			    sizeof(*log_names), xtopt_sysloglvl_compare);
290		if (e == NULL)
291			xt_params->exit_err(PARAMETER_PROBLEM,
292				"log level \"%s\" unknown\n", cb->arg);
293		num = e->level;
294	}
295	cb->val.syslog_level = num;
296	if (cb->entry->flags & XTOPT_PUT)
297		*(uint8_t *)XTOPT_MKPTR(cb) = num;
298}
299
300static void *xtables_sa_host(const void *sa, unsigned int afproto)
301{
302	if (afproto == AF_INET6)
303		return &((struct sockaddr_in6 *)sa)->sin6_addr;
304	else if (afproto == AF_INET)
305		return &((struct sockaddr_in *)sa)->sin_addr;
306	return (void *)sa;
307}
308
309static socklen_t xtables_sa_hostlen(unsigned int afproto)
310{
311	if (afproto == AF_INET6)
312		return sizeof(struct in6_addr);
313	else if (afproto == AF_INET)
314		return sizeof(struct in_addr);
315	return 0;
316}
317
318/**
319 * Accepts: a hostname (DNS), or a single inetaddr.
320 */
321static void xtopt_parse_onehost(struct xt_option_call *cb)
322{
323	struct addrinfo hints = {.ai_family = afinfo->family};
324	unsigned int adcount = 0;
325	struct addrinfo *res, *p;
326	int ret;
327
328	ret = getaddrinfo(cb->arg, NULL, &hints, &res);
329	if (ret < 0)
330		xt_params->exit_err(PARAMETER_PROBLEM,
331			"getaddrinfo: %s\n", gai_strerror(ret));
332
333	for (p = res; p != NULL; p = p->ai_next) {
334		if (adcount == 0) {
335			memset(&cb->val.inetaddr, 0, sizeof(cb->val.inetaddr));
336			memcpy(&cb->val.inetaddr,
337			       xtables_sa_host(p->ai_addr, p->ai_family),
338			       xtables_sa_hostlen(p->ai_family));
339			++adcount;
340			continue;
341		}
342		if (memcmp(&cb->val.inetaddr,
343		    xtables_sa_host(p->ai_addr, p->ai_family),
344		    xtables_sa_hostlen(p->ai_family)) != 0)
345			xt_params->exit_err(PARAMETER_PROBLEM,
346				"%s resolves to more than one address\n",
347				cb->arg);
348	}
349
350	freeaddrinfo(res);
351	if (cb->entry->flags & XTOPT_PUT)
352		/* Validation in xtables_option_metavalidate */
353		memcpy(XTOPT_MKPTR(cb), &cb->val.inetaddr,
354		       sizeof(cb->val.inetaddr));
355}
356
357/**
358 * @name:	port name, or number as a string (e.g. "http" or "80")
359 *
360 * Resolve a port name to a number. Returns the port number in integral
361 * form on success, or <0 on error. (errno will not be set.)
362 */
363static int xtables_getportbyname(const char *name)
364{
365	struct addrinfo *res = NULL, *p;
366	int ret;
367
368	ret = getaddrinfo(NULL, name, NULL, &res);
369	if (ret < 0)
370		return -1;
371	ret = -1;
372	for (p = res; p != NULL; p = p->ai_next) {
373		if (p->ai_family == AF_INET6) {
374			ret = ((struct sockaddr_in6 *)p->ai_addr)->sin6_port;
375			break;
376		} else if (p->ai_family == AF_INET) {
377			ret = ((struct sockaddr_in *)p->ai_addr)->sin_port;
378			break;
379		}
380	}
381	freeaddrinfo(res);
382	if (ret < 0)
383		return ret;
384	return ntohs(ret);
385}
386
387/**
388 * Validate and parse a port specification and put the result into @cb.
389 */
390static void xtopt_parse_port(struct xt_option_call *cb)
391{
392	int ret;
393
394	ret = xtables_getportbyname(cb->arg);
395	if (ret < 0)
396		xt_params->exit_err(PARAMETER_PROBLEM,
397			"Port \"%s\" does not resolve to anything.\n",
398			cb->arg);
399	cb->val.port = ret;
400	if (cb->entry->type == XTTYPE_PORT_NE)
401		cb->val.port = htons(cb->val.port);
402	if (cb->entry->flags & XTOPT_PUT)
403		*(uint16_t *)XTOPT_MKPTR(cb) = cb->val.port;
404}
405
406static void (*const xtopt_subparse[])(struct xt_option_call *) = {
407	[XTTYPE_UINT8]       = xtopt_parse_int,
408	[XTTYPE_UINT16]      = xtopt_parse_int,
409	[XTTYPE_UINT32]      = xtopt_parse_int,
410	[XTTYPE_UINT64]      = xtopt_parse_int,
411	[XTTYPE_UINT8RC]     = xtopt_parse_mint,
412	[XTTYPE_UINT16RC]    = xtopt_parse_mint,
413	[XTTYPE_UINT32RC]    = xtopt_parse_mint,
414	[XTTYPE_UINT64RC]    = xtopt_parse_mint,
415	[XTTYPE_STRING]      = xtopt_parse_string,
416	[XTTYPE_MARKMASK32]  = xtopt_parse_markmask,
417	[XTTYPE_SYSLOGLEVEL] = xtopt_parse_sysloglevel,
418	[XTTYPE_ONEHOST]     = xtopt_parse_onehost,
419	[XTTYPE_PORT]        = xtopt_parse_port,
420	[XTTYPE_PORT_NE]     = xtopt_parse_port,
421};
422
423static const size_t xtopt_psize[] = {
424	[XTTYPE_UINT8]       = sizeof(uint8_t),
425	[XTTYPE_UINT16]      = sizeof(uint16_t),
426	[XTTYPE_UINT32]      = sizeof(uint32_t),
427	[XTTYPE_UINT64]      = sizeof(uint64_t),
428	[XTTYPE_UINT8RC]     = sizeof(uint8_t[2]),
429	[XTTYPE_UINT16RC]    = sizeof(uint16_t[2]),
430	[XTTYPE_UINT32RC]    = sizeof(uint32_t[2]),
431	[XTTYPE_UINT64RC]    = sizeof(uint64_t[2]),
432	[XTTYPE_STRING]      = -1,
433	[XTTYPE_SYSLOGLEVEL] = sizeof(uint8_t),
434	[XTTYPE_ONEHOST]     = sizeof(union nf_inet_addr),
435	[XTTYPE_PORT]        = sizeof(uint16_t),
436	[XTTYPE_PORT_NE]     = sizeof(uint16_t),
437};
438
439/**
440 * The master option parsing routine. May be used for the ".x6_parse"
441 * function pointer in extensions if fully automatic parsing is desired.
442 * It may be also called manually from a custom x6_parse function.
443 */
444void xtables_option_parse(struct xt_option_call *cb)
445{
446	const struct xt_option_entry *entry = cb->entry;
447	unsigned int eflag = 1 << cb->entry->id;
448
449	/*
450	 * With {.id = P_FOO, .excl = P_FOO} we can have simple double-use
451	 * prevention. Though it turned out that this is too much typing (most
452	 * of the options are one-time use only), so now we also have
453	 * %XTOPT_MULTI.
454	 */
455	if ((!(entry->flags & XTOPT_MULTI) || (entry->excl & eflag)) &&
456	    cb->xflags & eflag)
457		xt_params->exit_err(PARAMETER_PROBLEM,
458			"%s: option \"--%s\" can only be used once.\n",
459			cb->ext_name, cb->entry->name);
460	if (cb->invert && !(entry->flags & XTOPT_INVERT))
461		xt_params->exit_err(PARAMETER_PROBLEM,
462			"%s: option \"--%s\" cannot be inverted.\n",
463			cb->ext_name, entry->name);
464	if (entry->type != XTTYPE_NONE && optarg == NULL)
465		xt_params->exit_err(PARAMETER_PROBLEM,
466			"%s: option \"--%s\" requires an argument.\n",
467			cb->ext_name, entry->name);
468	if (entry->type <= ARRAY_SIZE(xtopt_subparse) &&
469	    xtopt_subparse[entry->type] != NULL)
470		xtopt_subparse[entry->type](cb);
471	/* Exclusion with other flags tested later in finalize. */
472	cb->xflags |= 1 << entry->id;
473}
474
475/**
476 * Verifies that an extension's option map descriptor is valid, and ought to
477 * be called right after the extension has been loaded, and before option
478 * merging/xfrm.
479 */
480void xtables_option_metavalidate(const char *name,
481				 const struct xt_option_entry *entry)
482{
483	for (; entry->name != NULL; ++entry) {
484		if (entry->id >= CHAR_BIT * sizeof(unsigned int) ||
485		    entry->id >= XT_OPTION_OFFSET_SCALE)
486			xt_params->exit_err(OTHER_PROBLEM,
487				"Extension %s uses invalid ID %u\n",
488				name, entry->id);
489		if (!(entry->flags & XTOPT_PUT))
490			continue;
491		if (entry->type >= ARRAY_SIZE(xtopt_psize))
492			xt_params->exit_err(OTHER_PROBLEM,
493				"%s: entry type of option \"--%s\" cannot be "
494				"combined with XTOPT_PUT\n",
495				name, entry->name);
496		if (xtopt_psize[entry->type] != -1 &&
497		    xtopt_psize[entry->type] != entry->size)
498			xt_params->exit_err(OTHER_PROBLEM,
499				"%s: option \"--%s\" points to a memory block "
500				"of wrong size (expected %zu, got %zu)\n",
501				name, entry->name,
502				xtopt_psize[entry->type], entry->size);
503	}
504}
505
506/**
507 * Find an option entry by its id.
508 */
509static const struct xt_option_entry *
510xtables_option_lookup(const struct xt_option_entry *entry, unsigned int id)
511{
512	for (; entry->name != NULL; ++entry)
513		if (entry->id == id)
514			return entry;
515	return NULL;
516}
517
518/**
519 * @c:		getopt id (i.e. with offset)
520 * @fw:		struct ipt_entry or ip6t_entry
521 *
522 * Dispatch arguments to the appropriate parse function, based upon the
523 * extension's choice of API.
524 */
525void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
526			   struct xtables_target *t, void *fw)
527{
528	struct xt_option_call cb;
529
530	if (t->x6_parse == NULL) {
531		if (t->parse != NULL)
532			t->parse(c - t->option_offset, argv, invert,
533				 &t->tflags, fw, &t->t);
534		return;
535	}
536
537	c -= t->option_offset;
538	cb.entry = xtables_option_lookup(t->x6_options, c);
539	if (cb.entry == NULL)
540		xtables_error(OTHER_PROBLEM,
541			"Extension does not know id %u\n", c);
542	cb.arg      = optarg;
543	cb.invert   = invert;
544	cb.ext_name = t->name;
545	cb.data     = t->t->data;
546	cb.xflags   = t->tflags;
547	cb.target   = &t->t;
548	t->x6_parse(&cb);
549	t->tflags = cb.xflags;
550}
551
552/**
553 * @c:		getopt id (i.e. with offset)
554 * @fw:		struct ipt_entry or ip6t_entry
555 *
556 * Dispatch arguments to the appropriate parse function, based upon the
557 * extension's choice of API.
558 */
559void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
560			   struct xtables_match *m, void *fw)
561{
562	struct xt_option_call cb;
563
564	if (m->x6_parse == NULL) {
565		if (m->parse != NULL)
566			m->parse(c - m->option_offset, argv, invert,
567				 &m->mflags, fw, &m->m);
568		return;
569	}
570
571	c -= m->option_offset;
572	cb.entry = xtables_option_lookup(m->x6_options, c);
573	if (cb.entry == NULL)
574		xtables_error(OTHER_PROBLEM,
575			"Extension does not know id %u\n", c);
576	cb.arg      = optarg;
577	cb.invert   = invert;
578	cb.ext_name = m->name;
579	cb.data     = m->m->data;
580	cb.xflags   = m->mflags;
581	cb.match    = &m->m;
582	m->x6_parse(&cb);
583	m->mflags = cb.xflags;
584}
585
586/**
587 * @name:	name of extension
588 * @entry:	current option (from all ext's entries) being validated
589 * @xflags:	flags the extension has collected
590 * @i:		conflicting option (id) to test for
591 */
592static void
593xtables_option_fcheck2(const char *name, const struct xt_option_entry *entry,
594		       const struct xt_option_entry *other,
595		       unsigned int xflags)
596{
597	unsigned int ef = 1 << entry->id, of = 1 << other->id;
598
599	if (entry->also & of && !(xflags & of))
600		xt_params->exit_err(PARAMETER_PROBLEM,
601			"%s: option \"--%s\" also requires \"--%s\".\n",
602			name, entry->name, other->name);
603
604	if (!(entry->excl & of))
605		/* Use of entry does not collide with other option, good. */
606		return;
607	if ((xflags & (ef | of)) != (ef | of))
608		/* Conflicting options were not used. */
609		return;
610
611	xt_params->exit_err(PARAMETER_PROBLEM,
612		"%s: option \"--%s\" cannot be used together with \"--%s\".\n",
613		name, entry->name, other->name);
614}
615
616/**
617 * @name:	name of extension
618 * @xflags:	accumulated flags
619 * @entry:	extension's option table
620 *
621 * Check that all option constraints have been met. This effectively replaces
622 * ->final_check of the older API.
623 */
624void xtables_options_fcheck(const char *name, unsigned int xflags,
625			    const struct xt_option_entry *table)
626{
627	const struct xt_option_entry *entry, *other;
628	unsigned int i;
629
630	for (entry = table; entry->name != NULL; ++entry) {
631		if (entry->flags & XTOPT_MAND &&
632		    !(xflags & (1 << entry->id)))
633			xt_params->exit_err(PARAMETER_PROBLEM,
634				"%s: option \"--%s\" must be specified\n",
635				name, entry->name);
636
637		for (i = 0; i < CHAR_BIT * sizeof(entry->id); ++i) {
638			if (entry->id == i)
639				/*
640				 * Avoid conflict with self. Multi-use check
641				 * was done earlier in xtables_option_parse.
642				 */
643				continue;
644			other = xtables_option_lookup(table, i);
645			if (other == NULL)
646				continue;
647			xtables_option_fcheck2(name, entry, other, xflags);
648		}
649	}
650}
651
652/**
653 * Dispatch arguments to the appropriate final_check function, based upon the
654 * extension's choice of API.
655 */
656void xtables_option_tfcall(struct xtables_target *t)
657{
658	if (t->x6_fcheck != NULL) {
659		struct xt_fcheck_call cb;
660
661		cb.ext_name = t->name;
662		cb.data     = t->t->data;
663		cb.xflags   = t->tflags;
664		t->x6_fcheck(&cb);
665	} else if (t->final_check != NULL) {
666		t->final_check(t->tflags);
667	}
668	if (t->x6_options != NULL)
669		xtables_options_fcheck(t->name, t->tflags, t->x6_options);
670}
671
672/**
673 * Dispatch arguments to the appropriate final_check function, based upon the
674 * extension's choice of API.
675 */
676void xtables_option_mfcall(struct xtables_match *m)
677{
678	if (m->x6_fcheck != NULL) {
679		struct xt_fcheck_call cb;
680
681		cb.ext_name = m->name;
682		cb.data     = m->m->data;
683		cb.xflags   = m->mflags;
684		m->x6_fcheck(&cb);
685	} else if (m->final_check != NULL) {
686		m->final_check(m->mflags);
687	}
688	if (m->x6_options != NULL)
689		xtables_options_fcheck(m->name, m->mflags, m->x6_options);
690}
691
692struct xtables_lmap *xtables_lmap_init(const char *file)
693{
694	struct xtables_lmap *lmap_head = NULL, *lmap_prev = NULL, *lmap_this;
695	char buf[512];
696	FILE *fp;
697	char *cur, *nxt;
698	int id;
699
700	fp = fopen(file, "re");
701	if (fp == NULL)
702		return NULL;
703
704	while (fgets(buf, sizeof(buf), fp) != NULL) {
705		cur = buf;
706		while (isspace(*cur))
707			++cur;
708		if (*cur == '#' || *cur == '\n' || *cur == '\0')
709			continue;
710
711		/* iproute2 allows hex and dec format */
712		errno = 0;
713		id = strtoul(cur, &nxt, strncmp(cur, "0x", 2) == 0 ? 16 : 10);
714		if (nxt == cur || errno != 0)
715			continue;
716
717		/* same boundaries as in iproute2 */
718		if (id < 0 || id > 255)
719			continue;
720		cur = nxt;
721
722		if (!isspace(*cur))
723			continue;
724		while (isspace(*cur))
725			++cur;
726		if (*cur == '#' || *cur == '\n' || *cur == '\0')
727			continue;
728		nxt = cur;
729		while (*nxt != '\0' && !isspace(*nxt))
730			++nxt;
731		if (nxt == cur)
732			continue;
733		*nxt = '\0';
734
735		/* found valid data */
736		lmap_this = malloc(sizeof(*lmap_this));
737		if (lmap_this == NULL) {
738			perror("malloc");
739			goto out;
740		}
741		lmap_this->id   = id;
742		lmap_this->name = strdup(cur);
743		if (lmap_this->name == NULL) {
744			free(lmap_this);
745			goto out;
746		}
747		lmap_this->next = NULL;
748
749		if (lmap_prev != NULL)
750			lmap_prev->next = lmap_this;
751		else
752			lmap_head = lmap_this;
753		lmap_prev = lmap_this;
754	}
755
756	fclose(fp);
757	return lmap_head;
758 out:
759	xtables_lmap_free(lmap_head);
760	return NULL;
761}
762
763void xtables_lmap_free(struct xtables_lmap *head)
764{
765	struct xtables_lmap *next;
766
767	for (; head != NULL; head = next) {
768		next = head->next;
769		free(head->name);
770		free(head);
771	}
772}
773
774int xtables_lmap_name2id(const struct xtables_lmap *head, const char *name)
775{
776	for (; head != NULL; head = head->next)
777		if (strcmp(head->name, name) == 0)
778			return head->id;
779	return -1;
780}
781
782const char *xtables_lmap_id2name(const struct xtables_lmap *head, int id)
783{
784	for (; head != NULL; head = head->next)
785		if (head->id == id)
786			return head->name;
787	return NULL;
788}
789