xref: /aosp_15_r20/external/iptables/libxtables/xtables.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1*a71a9546SAutomerger Merge Worker /*
2*a71a9546SAutomerger Merge Worker  * (C) 2000-2006 by the netfilter coreteam <[email protected]>:
3*a71a9546SAutomerger Merge Worker  *
4*a71a9546SAutomerger Merge Worker  *	This program is free software; you can redistribute it and/or modify
5*a71a9546SAutomerger Merge Worker  *	it under the terms of the GNU General Public License as published by
6*a71a9546SAutomerger Merge Worker  *	the Free Software Foundation; either version 2 of the License, or
7*a71a9546SAutomerger Merge Worker  *	(at your option) any later version.
8*a71a9546SAutomerger Merge Worker  *
9*a71a9546SAutomerger Merge Worker  *	This program is distributed in the hope that it will be useful,
10*a71a9546SAutomerger Merge Worker  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
11*a71a9546SAutomerger Merge Worker  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*a71a9546SAutomerger Merge Worker  *	GNU General Public License for more details.
13*a71a9546SAutomerger Merge Worker  *
14*a71a9546SAutomerger Merge Worker  *	You should have received a copy of the GNU General Public License
15*a71a9546SAutomerger Merge Worker  *	along with this program; if not, write to the Free Software
16*a71a9546SAutomerger Merge Worker  *	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17*a71a9546SAutomerger Merge Worker  */
18*a71a9546SAutomerger Merge Worker #include "config.h"
19*a71a9546SAutomerger Merge Worker #include <ctype.h>
20*a71a9546SAutomerger Merge Worker #include <errno.h>
21*a71a9546SAutomerger Merge Worker #include <fcntl.h>
22*a71a9546SAutomerger Merge Worker #include <inttypes.h>
23*a71a9546SAutomerger Merge Worker #include <netdb.h>
24*a71a9546SAutomerger Merge Worker #include <spawn.h>
25*a71a9546SAutomerger Merge Worker #include <stdarg.h>
26*a71a9546SAutomerger Merge Worker #include <stdbool.h>
27*a71a9546SAutomerger Merge Worker #include <stdio.h>
28*a71a9546SAutomerger Merge Worker #include <stdlib.h>
29*a71a9546SAutomerger Merge Worker #include <string.h>
30*a71a9546SAutomerger Merge Worker #include <unistd.h>
31*a71a9546SAutomerger Merge Worker #include <netinet/ether.h>
32*a71a9546SAutomerger Merge Worker #include <sys/socket.h>
33*a71a9546SAutomerger Merge Worker #include <sys/stat.h>
34*a71a9546SAutomerger Merge Worker #include <sys/statfs.h>
35*a71a9546SAutomerger Merge Worker #include <sys/types.h>
36*a71a9546SAutomerger Merge Worker #include <sys/utsname.h>
37*a71a9546SAutomerger Merge Worker #include <sys/wait.h>
38*a71a9546SAutomerger Merge Worker #include <arpa/inet.h>
39*a71a9546SAutomerger Merge Worker #if defined(HAVE_LINUX_MAGIC_H)
40*a71a9546SAutomerger Merge Worker #	include <linux/magic.h> /* for PROC_SUPER_MAGIC */
41*a71a9546SAutomerger Merge Worker #elif defined(HAVE_LINUX_PROC_FS_H)
42*a71a9546SAutomerger Merge Worker #	include <linux/proc_fs.h>	/* Linux 2.4 */
43*a71a9546SAutomerger Merge Worker #else
44*a71a9546SAutomerger Merge Worker #	define PROC_SUPER_MAGIC	0x9fa0
45*a71a9546SAutomerger Merge Worker #endif
46*a71a9546SAutomerger Merge Worker 
47*a71a9546SAutomerger Merge Worker #include <xtables.h>
48*a71a9546SAutomerger Merge Worker #include <limits.h> /* INT_MAX in ip_tables.h/ip6_tables.h */
49*a71a9546SAutomerger Merge Worker #include <linux/netfilter_ipv4/ip_tables.h>
50*a71a9546SAutomerger Merge Worker #include <linux/netfilter_ipv6/ip6_tables.h>
51*a71a9546SAutomerger Merge Worker #include <libiptc/libxtc.h>
52*a71a9546SAutomerger Merge Worker #include <libiptc/linux_list.h>
53*a71a9546SAutomerger Merge Worker 
54*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
55*a71a9546SAutomerger Merge Worker #include <dlfcn.h>
56*a71a9546SAutomerger Merge Worker #endif
57*a71a9546SAutomerger Merge Worker #ifndef IPT_SO_GET_REVISION_MATCH /* Old kernel source. */
58*a71a9546SAutomerger Merge Worker #	define IPT_SO_GET_REVISION_MATCH	(IPT_BASE_CTL + 2)
59*a71a9546SAutomerger Merge Worker #	define IPT_SO_GET_REVISION_TARGET	(IPT_BASE_CTL + 3)
60*a71a9546SAutomerger Merge Worker #endif
61*a71a9546SAutomerger Merge Worker #ifndef IP6T_SO_GET_REVISION_MATCH /* Old kernel source. */
62*a71a9546SAutomerger Merge Worker #	define IP6T_SO_GET_REVISION_MATCH	68
63*a71a9546SAutomerger Merge Worker #	define IP6T_SO_GET_REVISION_TARGET	69
64*a71a9546SAutomerger Merge Worker #endif
65*a71a9546SAutomerger Merge Worker #include <getopt.h>
66*a71a9546SAutomerger Merge Worker #include "iptables/internal.h"
67*a71a9546SAutomerger Merge Worker 
68*a71a9546SAutomerger Merge Worker #define NPROTO	255
69*a71a9546SAutomerger Merge Worker 
70*a71a9546SAutomerger Merge Worker #ifndef PROC_SYS_MODPROBE
71*a71a9546SAutomerger Merge Worker #define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
72*a71a9546SAutomerger Merge Worker #endif
73*a71a9546SAutomerger Merge Worker 
74*a71a9546SAutomerger Merge Worker #ifndef ETH_ALEN
75*a71a9546SAutomerger Merge Worker #define ETH_ALEN 6
76*a71a9546SAutomerger Merge Worker #endif
77*a71a9546SAutomerger Merge Worker 
78*a71a9546SAutomerger Merge Worker /* we need this for ip6?tables-restore.  ip6?tables-restore.c sets line to the
79*a71a9546SAutomerger Merge Worker  * current line of the input file, in order  to give a more precise error
80*a71a9546SAutomerger Merge Worker  * message.  ip6?tables itself doesn't need this, so it is initialized to the
81*a71a9546SAutomerger Merge Worker  * magic number of -1 */
82*a71a9546SAutomerger Merge Worker int line = -1;
83*a71a9546SAutomerger Merge Worker 
84*a71a9546SAutomerger Merge Worker void basic_exit_err(enum xtables_exittype status, const char *msg, ...) __attribute__((noreturn, format(printf,2,3)));
85*a71a9546SAutomerger Merge Worker 
86*a71a9546SAutomerger Merge Worker struct xtables_globals *xt_params = NULL;
87*a71a9546SAutomerger Merge Worker 
basic_exit_err(enum xtables_exittype status,const char * msg,...)88*a71a9546SAutomerger Merge Worker void basic_exit_err(enum xtables_exittype status, const char *msg, ...)
89*a71a9546SAutomerger Merge Worker {
90*a71a9546SAutomerger Merge Worker 	va_list args;
91*a71a9546SAutomerger Merge Worker 
92*a71a9546SAutomerger Merge Worker 	va_start(args, msg);
93*a71a9546SAutomerger Merge Worker 	fprintf(stderr, "%s v%s: ", xt_params->program_name, xt_params->program_version);
94*a71a9546SAutomerger Merge Worker 	vfprintf(stderr, msg, args);
95*a71a9546SAutomerger Merge Worker 	va_end(args);
96*a71a9546SAutomerger Merge Worker 	fprintf(stderr, "\n");
97*a71a9546SAutomerger Merge Worker 	if (status == PARAMETER_PROBLEM) {
98*a71a9546SAutomerger Merge Worker 		if (line != -1)
99*a71a9546SAutomerger Merge Worker 			fprintf(stderr, "Error occurred at line: %d\n", line);
100*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
101*a71a9546SAutomerger Merge Worker 				xt_params->program_name, xt_params->program_name);
102*a71a9546SAutomerger Merge Worker 	} else if (status == VERSION_PROBLEM) {
103*a71a9546SAutomerger Merge Worker 		fprintf(stderr,
104*a71a9546SAutomerger Merge Worker 			"Perhaps %s or your kernel needs to be upgraded.\n",
105*a71a9546SAutomerger Merge Worker 			xt_params->program_name);
106*a71a9546SAutomerger Merge Worker 	}
107*a71a9546SAutomerger Merge Worker 	/* On error paths, make sure that we don't leak memory */
108*a71a9546SAutomerger Merge Worker 	xtables_free_opts(1);
109*a71a9546SAutomerger Merge Worker 	exit(status);
110*a71a9546SAutomerger Merge Worker }
111*a71a9546SAutomerger Merge Worker 
xtables_free_opts(int unused)112*a71a9546SAutomerger Merge Worker void xtables_free_opts(int unused)
113*a71a9546SAutomerger Merge Worker {
114*a71a9546SAutomerger Merge Worker 	if (xt_params->opts != xt_params->orig_opts) {
115*a71a9546SAutomerger Merge Worker 		free(xt_params->opts);
116*a71a9546SAutomerger Merge Worker 		xt_params->opts = NULL;
117*a71a9546SAutomerger Merge Worker 	}
118*a71a9546SAutomerger Merge Worker }
119*a71a9546SAutomerger Merge Worker 
xtables_merge_options(struct option * orig_opts,struct option * oldopts,const struct option * newopts,unsigned int * option_offset)120*a71a9546SAutomerger Merge Worker struct option *xtables_merge_options(struct option *orig_opts,
121*a71a9546SAutomerger Merge Worker 				     struct option *oldopts,
122*a71a9546SAutomerger Merge Worker 				     const struct option *newopts,
123*a71a9546SAutomerger Merge Worker 				     unsigned int *option_offset)
124*a71a9546SAutomerger Merge Worker {
125*a71a9546SAutomerger Merge Worker 	unsigned int num_oold = 0, num_old = 0, num_new = 0, i;
126*a71a9546SAutomerger Merge Worker 	struct option *merge, *mp;
127*a71a9546SAutomerger Merge Worker 
128*a71a9546SAutomerger Merge Worker 	if (newopts == NULL)
129*a71a9546SAutomerger Merge Worker 		return oldopts;
130*a71a9546SAutomerger Merge Worker 
131*a71a9546SAutomerger Merge Worker 	for (num_oold = 0; orig_opts[num_oold].name; num_oold++) ;
132*a71a9546SAutomerger Merge Worker 	if (oldopts != NULL)
133*a71a9546SAutomerger Merge Worker 		for (num_old = 0; oldopts[num_old].name; num_old++) ;
134*a71a9546SAutomerger Merge Worker 	for (num_new = 0; newopts[num_new].name; num_new++) ;
135*a71a9546SAutomerger Merge Worker 
136*a71a9546SAutomerger Merge Worker 	/*
137*a71a9546SAutomerger Merge Worker 	 * Since @oldopts also has @orig_opts already (and does so at the
138*a71a9546SAutomerger Merge Worker 	 * start), skip these entries.
139*a71a9546SAutomerger Merge Worker 	 */
140*a71a9546SAutomerger Merge Worker 	if (oldopts != NULL) {
141*a71a9546SAutomerger Merge Worker 		oldopts += num_oold;
142*a71a9546SAutomerger Merge Worker 		num_old -= num_oold;
143*a71a9546SAutomerger Merge Worker 	}
144*a71a9546SAutomerger Merge Worker 
145*a71a9546SAutomerger Merge Worker 	merge = malloc(sizeof(*mp) * (num_oold + num_old + num_new + 1));
146*a71a9546SAutomerger Merge Worker 	if (merge == NULL)
147*a71a9546SAutomerger Merge Worker 		return NULL;
148*a71a9546SAutomerger Merge Worker 
149*a71a9546SAutomerger Merge Worker 	/* Let the base options -[ADI...] have precedence over everything */
150*a71a9546SAutomerger Merge Worker 	memcpy(merge, orig_opts, sizeof(*mp) * num_oold);
151*a71a9546SAutomerger Merge Worker 	mp = merge + num_oold;
152*a71a9546SAutomerger Merge Worker 
153*a71a9546SAutomerger Merge Worker 	/* Second, the new options */
154*a71a9546SAutomerger Merge Worker 	xt_params->option_offset += XT_OPTION_OFFSET_SCALE;
155*a71a9546SAutomerger Merge Worker 	*option_offset = xt_params->option_offset;
156*a71a9546SAutomerger Merge Worker 	memcpy(mp, newopts, sizeof(*mp) * num_new);
157*a71a9546SAutomerger Merge Worker 
158*a71a9546SAutomerger Merge Worker 	for (i = 0; i < num_new; ++i, ++mp)
159*a71a9546SAutomerger Merge Worker 		mp->val += *option_offset;
160*a71a9546SAutomerger Merge Worker 
161*a71a9546SAutomerger Merge Worker 	/* Third, the old options */
162*a71a9546SAutomerger Merge Worker 	if (oldopts != NULL) {
163*a71a9546SAutomerger Merge Worker 		memcpy(mp, oldopts, sizeof(*mp) * num_old);
164*a71a9546SAutomerger Merge Worker 		mp += num_old;
165*a71a9546SAutomerger Merge Worker 	}
166*a71a9546SAutomerger Merge Worker 	xtables_free_opts(0);
167*a71a9546SAutomerger Merge Worker 
168*a71a9546SAutomerger Merge Worker 	/* Clear trailing entry */
169*a71a9546SAutomerger Merge Worker 	memset(mp, 0, sizeof(*mp));
170*a71a9546SAutomerger Merge Worker 	return merge;
171*a71a9546SAutomerger Merge Worker }
172*a71a9546SAutomerger Merge Worker 
173*a71a9546SAutomerger Merge Worker static const struct xtables_afinfo afinfo_ipv4 = {
174*a71a9546SAutomerger Merge Worker 	.kmod          = "ip_tables",
175*a71a9546SAutomerger Merge Worker 	.proc_exists   = "/proc/net/ip_tables_names",
176*a71a9546SAutomerger Merge Worker 	.libprefix     = "libipt_",
177*a71a9546SAutomerger Merge Worker 	.family	       = NFPROTO_IPV4,
178*a71a9546SAutomerger Merge Worker 	.ipproto       = IPPROTO_IP,
179*a71a9546SAutomerger Merge Worker 	.so_rev_match  = IPT_SO_GET_REVISION_MATCH,
180*a71a9546SAutomerger Merge Worker 	.so_rev_target = IPT_SO_GET_REVISION_TARGET,
181*a71a9546SAutomerger Merge Worker };
182*a71a9546SAutomerger Merge Worker 
183*a71a9546SAutomerger Merge Worker static const struct xtables_afinfo afinfo_ipv6 = {
184*a71a9546SAutomerger Merge Worker 	.kmod          = "ip6_tables",
185*a71a9546SAutomerger Merge Worker 	.proc_exists   = "/proc/net/ip6_tables_names",
186*a71a9546SAutomerger Merge Worker 	.libprefix     = "libip6t_",
187*a71a9546SAutomerger Merge Worker 	.family        = NFPROTO_IPV6,
188*a71a9546SAutomerger Merge Worker 	.ipproto       = IPPROTO_IPV6,
189*a71a9546SAutomerger Merge Worker 	.so_rev_match  = IP6T_SO_GET_REVISION_MATCH,
190*a71a9546SAutomerger Merge Worker 	.so_rev_target = IP6T_SO_GET_REVISION_TARGET,
191*a71a9546SAutomerger Merge Worker };
192*a71a9546SAutomerger Merge Worker 
193*a71a9546SAutomerger Merge Worker /* Dummy families for arptables-compat and ebtables-compat. Leave structure
194*a71a9546SAutomerger Merge Worker  * fields that we don't use unset.
195*a71a9546SAutomerger Merge Worker  */
196*a71a9546SAutomerger Merge Worker static const struct xtables_afinfo afinfo_bridge = {
197*a71a9546SAutomerger Merge Worker 	.libprefix     = "libebt_",
198*a71a9546SAutomerger Merge Worker 	.family        = NFPROTO_BRIDGE,
199*a71a9546SAutomerger Merge Worker };
200*a71a9546SAutomerger Merge Worker 
201*a71a9546SAutomerger Merge Worker static const struct xtables_afinfo afinfo_arp = {
202*a71a9546SAutomerger Merge Worker 	.libprefix     = "libarpt_",
203*a71a9546SAutomerger Merge Worker 	.family        = NFPROTO_ARP,
204*a71a9546SAutomerger Merge Worker };
205*a71a9546SAutomerger Merge Worker 
206*a71a9546SAutomerger Merge Worker const struct xtables_afinfo *afinfo;
207*a71a9546SAutomerger Merge Worker 
208*a71a9546SAutomerger Merge Worker /* Search path for Xtables .so files */
209*a71a9546SAutomerger Merge Worker static const char *xtables_libdir;
210*a71a9546SAutomerger Merge Worker 
211*a71a9546SAutomerger Merge Worker /* the path to command to load kernel module */
212*a71a9546SAutomerger Merge Worker const char *xtables_modprobe_program;
213*a71a9546SAutomerger Merge Worker 
214*a71a9546SAutomerger Merge Worker /* Keep track of matches/targets pending full registration: linked lists. */
215*a71a9546SAutomerger Merge Worker struct xtables_match *xtables_pending_matches;
216*a71a9546SAutomerger Merge Worker struct xtables_target *xtables_pending_targets;
217*a71a9546SAutomerger Merge Worker 
218*a71a9546SAutomerger Merge Worker /* Keep track of fully registered external matches/targets: linked lists. */
219*a71a9546SAutomerger Merge Worker struct xtables_match *xtables_matches;
220*a71a9546SAutomerger Merge Worker struct xtables_target *xtables_targets;
221*a71a9546SAutomerger Merge Worker 
222*a71a9546SAutomerger Merge Worker /* Fully register a match/target which was previously partially registered. */
223*a71a9546SAutomerger Merge Worker static bool xtables_fully_register_pending_match(struct xtables_match *me,
224*a71a9546SAutomerger Merge Worker 						 struct xtables_match *prev);
225*a71a9546SAutomerger Merge Worker static bool xtables_fully_register_pending_target(struct xtables_target *me,
226*a71a9546SAutomerger Merge Worker 						  struct xtables_target *prev);
227*a71a9546SAutomerger Merge Worker 
228*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
229*a71a9546SAutomerger Merge Worker /* registry for loaded shared objects to close later */
230*a71a9546SAutomerger Merge Worker struct dlreg {
231*a71a9546SAutomerger Merge Worker 	struct dlreg *next;
232*a71a9546SAutomerger Merge Worker 	void *handle;
233*a71a9546SAutomerger Merge Worker };
234*a71a9546SAutomerger Merge Worker static struct dlreg *dlreg = NULL;
235*a71a9546SAutomerger Merge Worker 
dlreg_add(void * handle)236*a71a9546SAutomerger Merge Worker static int dlreg_add(void *handle)
237*a71a9546SAutomerger Merge Worker {
238*a71a9546SAutomerger Merge Worker 	struct dlreg *new = malloc(sizeof(*new));
239*a71a9546SAutomerger Merge Worker 
240*a71a9546SAutomerger Merge Worker 	if (!new)
241*a71a9546SAutomerger Merge Worker 		return -1;
242*a71a9546SAutomerger Merge Worker 
243*a71a9546SAutomerger Merge Worker 	new->handle = handle;
244*a71a9546SAutomerger Merge Worker 	new->next = dlreg;
245*a71a9546SAutomerger Merge Worker 	dlreg = new;
246*a71a9546SAutomerger Merge Worker 	return 0;
247*a71a9546SAutomerger Merge Worker }
248*a71a9546SAutomerger Merge Worker 
dlreg_free(void)249*a71a9546SAutomerger Merge Worker static void dlreg_free(void)
250*a71a9546SAutomerger Merge Worker {
251*a71a9546SAutomerger Merge Worker 	struct dlreg *next;
252*a71a9546SAutomerger Merge Worker 
253*a71a9546SAutomerger Merge Worker 	while (dlreg) {
254*a71a9546SAutomerger Merge Worker 		next = dlreg->next;
255*a71a9546SAutomerger Merge Worker 		dlclose(dlreg->handle);
256*a71a9546SAutomerger Merge Worker 		free(dlreg);
257*a71a9546SAutomerger Merge Worker 		dlreg = next;
258*a71a9546SAutomerger Merge Worker 	}
259*a71a9546SAutomerger Merge Worker }
260*a71a9546SAutomerger Merge Worker #endif
261*a71a9546SAutomerger Merge Worker 
262*a71a9546SAutomerger Merge Worker struct notarget {
263*a71a9546SAutomerger Merge Worker 	struct hlist_node node;
264*a71a9546SAutomerger Merge Worker 	char name[];
265*a71a9546SAutomerger Merge Worker };
266*a71a9546SAutomerger Merge Worker 
267*a71a9546SAutomerger Merge Worker #define NOTARGET_HSIZE	512
268*a71a9546SAutomerger Merge Worker static struct hlist_head notargets[NOTARGET_HSIZE];
269*a71a9546SAutomerger Merge Worker 
notargets_hlist_init(void)270*a71a9546SAutomerger Merge Worker static void notargets_hlist_init(void)
271*a71a9546SAutomerger Merge Worker {
272*a71a9546SAutomerger Merge Worker 	int i;
273*a71a9546SAutomerger Merge Worker 
274*a71a9546SAutomerger Merge Worker 	for (i = 0; i < NOTARGET_HSIZE; i++)
275*a71a9546SAutomerger Merge Worker 		INIT_HLIST_HEAD(&notargets[i]);
276*a71a9546SAutomerger Merge Worker }
277*a71a9546SAutomerger Merge Worker 
notargets_hlist_free(void)278*a71a9546SAutomerger Merge Worker static void notargets_hlist_free(void)
279*a71a9546SAutomerger Merge Worker {
280*a71a9546SAutomerger Merge Worker 	struct hlist_node *pos, *n;
281*a71a9546SAutomerger Merge Worker 	struct notarget *cur;
282*a71a9546SAutomerger Merge Worker 	int i;
283*a71a9546SAutomerger Merge Worker 
284*a71a9546SAutomerger Merge Worker 	for (i = 0; i < NOTARGET_HSIZE; i++) {
285*a71a9546SAutomerger Merge Worker 		hlist_for_each_entry_safe(cur, pos, n, &notargets[i], node) {
286*a71a9546SAutomerger Merge Worker 			hlist_del(&cur->node);
287*a71a9546SAutomerger Merge Worker 			free(cur);
288*a71a9546SAutomerger Merge Worker 		}
289*a71a9546SAutomerger Merge Worker 	}
290*a71a9546SAutomerger Merge Worker }
291*a71a9546SAutomerger Merge Worker 
djb_hash(const char * key)292*a71a9546SAutomerger Merge Worker static uint32_t djb_hash(const char *key)
293*a71a9546SAutomerger Merge Worker {
294*a71a9546SAutomerger Merge Worker 	uint32_t i, hash = 5381;
295*a71a9546SAutomerger Merge Worker 
296*a71a9546SAutomerger Merge Worker 	for (i = 0; i < strlen(key); i++)
297*a71a9546SAutomerger Merge Worker 		hash = ((hash << 5) + hash) + key[i];
298*a71a9546SAutomerger Merge Worker 
299*a71a9546SAutomerger Merge Worker 	return hash;
300*a71a9546SAutomerger Merge Worker }
301*a71a9546SAutomerger Merge Worker 
notargets_hlist_lookup(const char * name)302*a71a9546SAutomerger Merge Worker static struct notarget *notargets_hlist_lookup(const char *name)
303*a71a9546SAutomerger Merge Worker {
304*a71a9546SAutomerger Merge Worker 	uint32_t key = djb_hash(name) % NOTARGET_HSIZE;
305*a71a9546SAutomerger Merge Worker 	struct hlist_node *node;
306*a71a9546SAutomerger Merge Worker 	struct notarget *cur;
307*a71a9546SAutomerger Merge Worker 
308*a71a9546SAutomerger Merge Worker 	hlist_for_each_entry(cur, node, &notargets[key], node) {
309*a71a9546SAutomerger Merge Worker 		if (!strcmp(name, cur->name))
310*a71a9546SAutomerger Merge Worker 			return cur;
311*a71a9546SAutomerger Merge Worker 	}
312*a71a9546SAutomerger Merge Worker 	return NULL;
313*a71a9546SAutomerger Merge Worker }
314*a71a9546SAutomerger Merge Worker 
notargets_hlist_insert(const char * name)315*a71a9546SAutomerger Merge Worker static void notargets_hlist_insert(const char *name)
316*a71a9546SAutomerger Merge Worker {
317*a71a9546SAutomerger Merge Worker 	struct notarget *cur;
318*a71a9546SAutomerger Merge Worker 
319*a71a9546SAutomerger Merge Worker 	if (!name)
320*a71a9546SAutomerger Merge Worker 		return;
321*a71a9546SAutomerger Merge Worker 
322*a71a9546SAutomerger Merge Worker 	cur = xtables_malloc(sizeof(*cur) + strlen(name) + 1);
323*a71a9546SAutomerger Merge Worker 	strcpy(cur->name, name);
324*a71a9546SAutomerger Merge Worker 	hlist_add_head(&cur->node, &notargets[djb_hash(name) % NOTARGET_HSIZE]);
325*a71a9546SAutomerger Merge Worker }
326*a71a9546SAutomerger Merge Worker 
xtables_announce_chain(const char * name)327*a71a9546SAutomerger Merge Worker void xtables_announce_chain(const char *name)
328*a71a9546SAutomerger Merge Worker {
329*a71a9546SAutomerger Merge Worker 	if (!notargets_hlist_lookup(name))
330*a71a9546SAutomerger Merge Worker 		notargets_hlist_insert(name);
331*a71a9546SAutomerger Merge Worker }
332*a71a9546SAutomerger Merge Worker 
xtables_init(void)333*a71a9546SAutomerger Merge Worker void xtables_init(void)
334*a71a9546SAutomerger Merge Worker {
335*a71a9546SAutomerger Merge Worker 	/* xtables cannot be used with setuid in a safe way. */
336*a71a9546SAutomerger Merge Worker 	if (getuid() != geteuid())
337*a71a9546SAutomerger Merge Worker 		_exit(111);
338*a71a9546SAutomerger Merge Worker 
339*a71a9546SAutomerger Merge Worker 	xtables_libdir = getenv("XTABLES_LIBDIR");
340*a71a9546SAutomerger Merge Worker 	if (xtables_libdir != NULL)
341*a71a9546SAutomerger Merge Worker 		return;
342*a71a9546SAutomerger Merge Worker 	xtables_libdir = getenv("IPTABLES_LIB_DIR");
343*a71a9546SAutomerger Merge Worker 	if (xtables_libdir != NULL) {
344*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "IPTABLES_LIB_DIR is deprecated, "
345*a71a9546SAutomerger Merge Worker 		        "use XTABLES_LIBDIR.\n");
346*a71a9546SAutomerger Merge Worker 		return;
347*a71a9546SAutomerger Merge Worker 	}
348*a71a9546SAutomerger Merge Worker 	/*
349*a71a9546SAutomerger Merge Worker 	 * Well yes, IP6TABLES_LIB_DIR is of lower priority over
350*a71a9546SAutomerger Merge Worker 	 * IPTABLES_LIB_DIR since this moved to libxtables; I think that is ok
351*a71a9546SAutomerger Merge Worker 	 * for these env vars are deprecated anyhow, and in light of the
352*a71a9546SAutomerger Merge Worker 	 * (shared) libxt_*.so files, makes less sense to have
353*a71a9546SAutomerger Merge Worker 	 * IPTABLES_LIB_DIR != IP6TABLES_LIB_DIR.
354*a71a9546SAutomerger Merge Worker 	 */
355*a71a9546SAutomerger Merge Worker 	xtables_libdir = getenv("IP6TABLES_LIB_DIR");
356*a71a9546SAutomerger Merge Worker 	if (xtables_libdir != NULL) {
357*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "IP6TABLES_LIB_DIR is deprecated, "
358*a71a9546SAutomerger Merge Worker 		        "use XTABLES_LIBDIR.\n");
359*a71a9546SAutomerger Merge Worker 		return;
360*a71a9546SAutomerger Merge Worker 	}
361*a71a9546SAutomerger Merge Worker 	xtables_libdir = XTABLES_LIBDIR;
362*a71a9546SAutomerger Merge Worker 
363*a71a9546SAutomerger Merge Worker 	notargets_hlist_init();
364*a71a9546SAutomerger Merge Worker }
365*a71a9546SAutomerger Merge Worker 
xtables_fini(void)366*a71a9546SAutomerger Merge Worker void xtables_fini(void)
367*a71a9546SAutomerger Merge Worker {
368*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
369*a71a9546SAutomerger Merge Worker 	dlreg_free();
370*a71a9546SAutomerger Merge Worker #endif
371*a71a9546SAutomerger Merge Worker 	notargets_hlist_free();
372*a71a9546SAutomerger Merge Worker }
373*a71a9546SAutomerger Merge Worker 
xtables_set_nfproto(uint8_t nfproto)374*a71a9546SAutomerger Merge Worker void xtables_set_nfproto(uint8_t nfproto)
375*a71a9546SAutomerger Merge Worker {
376*a71a9546SAutomerger Merge Worker 	switch (nfproto) {
377*a71a9546SAutomerger Merge Worker 	case NFPROTO_IPV4:
378*a71a9546SAutomerger Merge Worker 		afinfo = &afinfo_ipv4;
379*a71a9546SAutomerger Merge Worker 		break;
380*a71a9546SAutomerger Merge Worker 	case NFPROTO_IPV6:
381*a71a9546SAutomerger Merge Worker 		afinfo = &afinfo_ipv6;
382*a71a9546SAutomerger Merge Worker 		break;
383*a71a9546SAutomerger Merge Worker 	case NFPROTO_BRIDGE:
384*a71a9546SAutomerger Merge Worker 		afinfo = &afinfo_bridge;
385*a71a9546SAutomerger Merge Worker 		break;
386*a71a9546SAutomerger Merge Worker 	case NFPROTO_ARP:
387*a71a9546SAutomerger Merge Worker 		afinfo = &afinfo_arp;
388*a71a9546SAutomerger Merge Worker 		break;
389*a71a9546SAutomerger Merge Worker 	default:
390*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "libxtables: unhandled NFPROTO in %s\n",
391*a71a9546SAutomerger Merge Worker 		        __func__);
392*a71a9546SAutomerger Merge Worker 	}
393*a71a9546SAutomerger Merge Worker }
394*a71a9546SAutomerger Merge Worker 
395*a71a9546SAutomerger Merge Worker /**
396*a71a9546SAutomerger Merge Worker  * xtables_set_params - set the global parameters used by xtables
397*a71a9546SAutomerger Merge Worker  * @xtp:	input xtables_globals structure
398*a71a9546SAutomerger Merge Worker  *
399*a71a9546SAutomerger Merge Worker  * The app is expected to pass a valid xtables_globals data-filled
400*a71a9546SAutomerger Merge Worker  * with proper values
401*a71a9546SAutomerger Merge Worker  * @xtp cannot be NULL
402*a71a9546SAutomerger Merge Worker  *
403*a71a9546SAutomerger Merge Worker  * Returns -1 on failure to set and 0 on success
404*a71a9546SAutomerger Merge Worker  */
xtables_set_params(struct xtables_globals * xtp)405*a71a9546SAutomerger Merge Worker int xtables_set_params(struct xtables_globals *xtp)
406*a71a9546SAutomerger Merge Worker {
407*a71a9546SAutomerger Merge Worker 	if (!xtp) {
408*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: Illegal global params\n",__func__);
409*a71a9546SAutomerger Merge Worker 		return -1;
410*a71a9546SAutomerger Merge Worker 	}
411*a71a9546SAutomerger Merge Worker 
412*a71a9546SAutomerger Merge Worker 	xt_params = xtp;
413*a71a9546SAutomerger Merge Worker 
414*a71a9546SAutomerger Merge Worker 	if (!xt_params->exit_err)
415*a71a9546SAutomerger Merge Worker 		xt_params->exit_err = basic_exit_err;
416*a71a9546SAutomerger Merge Worker 
417*a71a9546SAutomerger Merge Worker 	return 0;
418*a71a9546SAutomerger Merge Worker }
419*a71a9546SAutomerger Merge Worker 
xtables_init_all(struct xtables_globals * xtp,uint8_t nfproto)420*a71a9546SAutomerger Merge Worker int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
421*a71a9546SAutomerger Merge Worker {
422*a71a9546SAutomerger Merge Worker 	xtables_init();
423*a71a9546SAutomerger Merge Worker 	xtables_set_nfproto(nfproto);
424*a71a9546SAutomerger Merge Worker 	return xtables_set_params(xtp);
425*a71a9546SAutomerger Merge Worker }
426*a71a9546SAutomerger Merge Worker 
427*a71a9546SAutomerger Merge Worker /**
428*a71a9546SAutomerger Merge Worker  * xtables_*alloc - wrappers that exit on failure
429*a71a9546SAutomerger Merge Worker  */
xtables_calloc(size_t count,size_t size)430*a71a9546SAutomerger Merge Worker void *xtables_calloc(size_t count, size_t size)
431*a71a9546SAutomerger Merge Worker {
432*a71a9546SAutomerger Merge Worker 	void *p;
433*a71a9546SAutomerger Merge Worker 
434*a71a9546SAutomerger Merge Worker 	if ((p = calloc(count, size)) == NULL) {
435*a71a9546SAutomerger Merge Worker 		perror("ip[6]tables: calloc failed");
436*a71a9546SAutomerger Merge Worker 		exit(1);
437*a71a9546SAutomerger Merge Worker 	}
438*a71a9546SAutomerger Merge Worker 
439*a71a9546SAutomerger Merge Worker 	return p;
440*a71a9546SAutomerger Merge Worker }
441*a71a9546SAutomerger Merge Worker 
xtables_malloc(size_t size)442*a71a9546SAutomerger Merge Worker void *xtables_malloc(size_t size)
443*a71a9546SAutomerger Merge Worker {
444*a71a9546SAutomerger Merge Worker 	void *p;
445*a71a9546SAutomerger Merge Worker 
446*a71a9546SAutomerger Merge Worker 	if ((p = malloc(size)) == NULL) {
447*a71a9546SAutomerger Merge Worker 		perror("ip[6]tables: malloc failed");
448*a71a9546SAutomerger Merge Worker 		exit(1);
449*a71a9546SAutomerger Merge Worker 	}
450*a71a9546SAutomerger Merge Worker 
451*a71a9546SAutomerger Merge Worker 	return p;
452*a71a9546SAutomerger Merge Worker }
453*a71a9546SAutomerger Merge Worker 
xtables_realloc(void * ptr,size_t size)454*a71a9546SAutomerger Merge Worker void *xtables_realloc(void *ptr, size_t size)
455*a71a9546SAutomerger Merge Worker {
456*a71a9546SAutomerger Merge Worker 	void *p;
457*a71a9546SAutomerger Merge Worker 
458*a71a9546SAutomerger Merge Worker 	if ((p = realloc(ptr, size)) == NULL) {
459*a71a9546SAutomerger Merge Worker 		perror("ip[6]tables: realloc failed");
460*a71a9546SAutomerger Merge Worker 		exit(1);
461*a71a9546SAutomerger Merge Worker 	}
462*a71a9546SAutomerger Merge Worker 
463*a71a9546SAutomerger Merge Worker 	return p;
464*a71a9546SAutomerger Merge Worker }
465*a71a9546SAutomerger Merge Worker 
xtables_strdup(const char * s)466*a71a9546SAutomerger Merge Worker char *xtables_strdup(const char *s)
467*a71a9546SAutomerger Merge Worker {
468*a71a9546SAutomerger Merge Worker 	char *dup = strdup(s);
469*a71a9546SAutomerger Merge Worker 
470*a71a9546SAutomerger Merge Worker 	if (!dup) {
471*a71a9546SAutomerger Merge Worker 		perror("ip[6]tables: strdup failed");
472*a71a9546SAutomerger Merge Worker 		exit(1);
473*a71a9546SAutomerger Merge Worker 	}
474*a71a9546SAutomerger Merge Worker 
475*a71a9546SAutomerger Merge Worker 	return dup;
476*a71a9546SAutomerger Merge Worker }
477*a71a9546SAutomerger Merge Worker 
get_modprobe(void)478*a71a9546SAutomerger Merge Worker static char *get_modprobe(void)
479*a71a9546SAutomerger Merge Worker {
480*a71a9546SAutomerger Merge Worker 	int procfile;
481*a71a9546SAutomerger Merge Worker 	char *ret;
482*a71a9546SAutomerger Merge Worker 	int count;
483*a71a9546SAutomerger Merge Worker 
484*a71a9546SAutomerger Merge Worker 	procfile = open(PROC_SYS_MODPROBE, O_RDONLY | O_CLOEXEC);
485*a71a9546SAutomerger Merge Worker 	if (procfile < 0)
486*a71a9546SAutomerger Merge Worker 		return NULL;
487*a71a9546SAutomerger Merge Worker 
488*a71a9546SAutomerger Merge Worker 	ret = malloc(PATH_MAX);
489*a71a9546SAutomerger Merge Worker 	if (ret) {
490*a71a9546SAutomerger Merge Worker 		count = read(procfile, ret, PATH_MAX);
491*a71a9546SAutomerger Merge Worker 		if (count > 0 && count < PATH_MAX)
492*a71a9546SAutomerger Merge Worker 		{
493*a71a9546SAutomerger Merge Worker 			if (ret[count - 1] == '\n')
494*a71a9546SAutomerger Merge Worker 				ret[count - 1] = '\0';
495*a71a9546SAutomerger Merge Worker 			else
496*a71a9546SAutomerger Merge Worker 				ret[count] = '\0';
497*a71a9546SAutomerger Merge Worker 			close(procfile);
498*a71a9546SAutomerger Merge Worker 			return ret;
499*a71a9546SAutomerger Merge Worker 		}
500*a71a9546SAutomerger Merge Worker 	}
501*a71a9546SAutomerger Merge Worker 	free(ret);
502*a71a9546SAutomerger Merge Worker 	close(procfile);
503*a71a9546SAutomerger Merge Worker 	return NULL;
504*a71a9546SAutomerger Merge Worker }
505*a71a9546SAutomerger Merge Worker 
xtables_insmod(const char * modname,const char * modprobe,bool quiet)506*a71a9546SAutomerger Merge Worker int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
507*a71a9546SAutomerger Merge Worker {
508*a71a9546SAutomerger Merge Worker 	char *buf = NULL;
509*a71a9546SAutomerger Merge Worker 	char *argv[4];
510*a71a9546SAutomerger Merge Worker 	int status;
511*a71a9546SAutomerger Merge Worker 	pid_t pid;
512*a71a9546SAutomerger Merge Worker 
513*a71a9546SAutomerger Merge Worker 	/* If they don't explicitly set it, read out of kernel */
514*a71a9546SAutomerger Merge Worker 	if (!modprobe) {
515*a71a9546SAutomerger Merge Worker 		buf = get_modprobe();
516*a71a9546SAutomerger Merge Worker 		if (!buf)
517*a71a9546SAutomerger Merge Worker 			return -1;
518*a71a9546SAutomerger Merge Worker 		modprobe = buf;
519*a71a9546SAutomerger Merge Worker 	}
520*a71a9546SAutomerger Merge Worker 
521*a71a9546SAutomerger Merge Worker 	argv[0] = (char *)modprobe;
522*a71a9546SAutomerger Merge Worker 	argv[1] = (char *)modname;
523*a71a9546SAutomerger Merge Worker 	argv[2] = quiet ? "-q" : NULL;
524*a71a9546SAutomerger Merge Worker 	argv[3] = NULL;
525*a71a9546SAutomerger Merge Worker 
526*a71a9546SAutomerger Merge Worker 	/*
527*a71a9546SAutomerger Merge Worker 	 * Need to flush the buffer, or the child may output it again
528*a71a9546SAutomerger Merge Worker 	 * when switching the program thru execv.
529*a71a9546SAutomerger Merge Worker 	 */
530*a71a9546SAutomerger Merge Worker 	fflush(stdout);
531*a71a9546SAutomerger Merge Worker 
532*a71a9546SAutomerger Merge Worker 	if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL)) {
533*a71a9546SAutomerger Merge Worker 		free(buf);
534*a71a9546SAutomerger Merge Worker 		return -1;
535*a71a9546SAutomerger Merge Worker 	} else {
536*a71a9546SAutomerger Merge Worker 		waitpid(pid, &status, 0);
537*a71a9546SAutomerger Merge Worker 	}
538*a71a9546SAutomerger Merge Worker 
539*a71a9546SAutomerger Merge Worker 	free(buf);
540*a71a9546SAutomerger Merge Worker 	if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
541*a71a9546SAutomerger Merge Worker 		return 0;
542*a71a9546SAutomerger Merge Worker 	return -1;
543*a71a9546SAutomerger Merge Worker }
544*a71a9546SAutomerger Merge Worker 
545*a71a9546SAutomerger Merge Worker /* return true if a given file exists within procfs */
proc_file_exists(const char * filename)546*a71a9546SAutomerger Merge Worker static bool proc_file_exists(const char *filename)
547*a71a9546SAutomerger Merge Worker {
548*a71a9546SAutomerger Merge Worker 	struct stat s;
549*a71a9546SAutomerger Merge Worker 	struct statfs f;
550*a71a9546SAutomerger Merge Worker 
551*a71a9546SAutomerger Merge Worker 	if (lstat(filename, &s))
552*a71a9546SAutomerger Merge Worker 		return false;
553*a71a9546SAutomerger Merge Worker 	if (!S_ISREG(s.st_mode))
554*a71a9546SAutomerger Merge Worker 		return false;
555*a71a9546SAutomerger Merge Worker 	if (statfs(filename, &f))
556*a71a9546SAutomerger Merge Worker 		return false;
557*a71a9546SAutomerger Merge Worker 	if (f.f_type != PROC_SUPER_MAGIC)
558*a71a9546SAutomerger Merge Worker 		return false;
559*a71a9546SAutomerger Merge Worker 	return true;
560*a71a9546SAutomerger Merge Worker }
561*a71a9546SAutomerger Merge Worker 
xtables_load_ko(const char * modprobe,bool quiet)562*a71a9546SAutomerger Merge Worker int xtables_load_ko(const char *modprobe, bool quiet)
563*a71a9546SAutomerger Merge Worker {
564*a71a9546SAutomerger Merge Worker 	static bool loaded = false;
565*a71a9546SAutomerger Merge Worker 	int ret;
566*a71a9546SAutomerger Merge Worker 
567*a71a9546SAutomerger Merge Worker 	if (loaded)
568*a71a9546SAutomerger Merge Worker 		return 0;
569*a71a9546SAutomerger Merge Worker 
570*a71a9546SAutomerger Merge Worker 	if (proc_file_exists(afinfo->proc_exists)) {
571*a71a9546SAutomerger Merge Worker 		loaded = true;
572*a71a9546SAutomerger Merge Worker 		return 0;
573*a71a9546SAutomerger Merge Worker 	};
574*a71a9546SAutomerger Merge Worker 
575*a71a9546SAutomerger Merge Worker 	ret = xtables_insmod(afinfo->kmod, modprobe, quiet);
576*a71a9546SAutomerger Merge Worker 	if (ret == 0)
577*a71a9546SAutomerger Merge Worker 		loaded = true;
578*a71a9546SAutomerger Merge Worker 
579*a71a9546SAutomerger Merge Worker 	return ret;
580*a71a9546SAutomerger Merge Worker }
581*a71a9546SAutomerger Merge Worker 
582*a71a9546SAutomerger Merge Worker /**
583*a71a9546SAutomerger Merge Worker  * xtables_strtou{i,l} - string to number conversion
584*a71a9546SAutomerger Merge Worker  * @s:	input string
585*a71a9546SAutomerger Merge Worker  * @end:	like strtoul's "end" pointer
586*a71a9546SAutomerger Merge Worker  * @value:	pointer for result
587*a71a9546SAutomerger Merge Worker  * @min:	minimum accepted value
588*a71a9546SAutomerger Merge Worker  * @max:	maximum accepted value
589*a71a9546SAutomerger Merge Worker  *
590*a71a9546SAutomerger Merge Worker  * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
591*a71a9546SAutomerger Merge Worker  * "15a" is rejected.
592*a71a9546SAutomerger Merge Worker  * In either case, the value obtained is compared for min-max compliance.
593*a71a9546SAutomerger Merge Worker  * Base is always 0, i.e. autodetect depending on @s.
594*a71a9546SAutomerger Merge Worker  *
595*a71a9546SAutomerger Merge Worker  * Returns true/false whether number was accepted. On failure, *value has
596*a71a9546SAutomerger Merge Worker  * undefined contents.
597*a71a9546SAutomerger Merge Worker  */
xtables_strtoul(const char * s,char ** end,uintmax_t * value,uintmax_t min,uintmax_t max)598*a71a9546SAutomerger Merge Worker bool xtables_strtoul(const char *s, char **end, uintmax_t *value,
599*a71a9546SAutomerger Merge Worker                      uintmax_t min, uintmax_t max)
600*a71a9546SAutomerger Merge Worker {
601*a71a9546SAutomerger Merge Worker 	uintmax_t v;
602*a71a9546SAutomerger Merge Worker 	const char *p;
603*a71a9546SAutomerger Merge Worker 	char *my_end;
604*a71a9546SAutomerger Merge Worker 
605*a71a9546SAutomerger Merge Worker 	errno = 0;
606*a71a9546SAutomerger Merge Worker 	/* Since strtoul allows leading minus, we have to check for ourself. */
607*a71a9546SAutomerger Merge Worker 	for (p = s; isspace(*p); ++p)
608*a71a9546SAutomerger Merge Worker 		;
609*a71a9546SAutomerger Merge Worker 	if (*p == '-')
610*a71a9546SAutomerger Merge Worker 		return false;
611*a71a9546SAutomerger Merge Worker 	v = strtoumax(s, &my_end, 0);
612*a71a9546SAutomerger Merge Worker 	if (my_end == s)
613*a71a9546SAutomerger Merge Worker 		return false;
614*a71a9546SAutomerger Merge Worker 	if (end != NULL)
615*a71a9546SAutomerger Merge Worker 		*end = my_end;
616*a71a9546SAutomerger Merge Worker 
617*a71a9546SAutomerger Merge Worker 	if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
618*a71a9546SAutomerger Merge Worker 		if (value != NULL)
619*a71a9546SAutomerger Merge Worker 			*value = v;
620*a71a9546SAutomerger Merge Worker 		if (end == NULL)
621*a71a9546SAutomerger Merge Worker 			return *my_end == '\0';
622*a71a9546SAutomerger Merge Worker 		return true;
623*a71a9546SAutomerger Merge Worker 	}
624*a71a9546SAutomerger Merge Worker 
625*a71a9546SAutomerger Merge Worker 	return false;
626*a71a9546SAutomerger Merge Worker }
627*a71a9546SAutomerger Merge Worker 
xtables_strtoui(const char * s,char ** end,unsigned int * value,unsigned int min,unsigned int max)628*a71a9546SAutomerger Merge Worker bool xtables_strtoui(const char *s, char **end, unsigned int *value,
629*a71a9546SAutomerger Merge Worker                      unsigned int min, unsigned int max)
630*a71a9546SAutomerger Merge Worker {
631*a71a9546SAutomerger Merge Worker 	uintmax_t v;
632*a71a9546SAutomerger Merge Worker 	bool ret;
633*a71a9546SAutomerger Merge Worker 
634*a71a9546SAutomerger Merge Worker 	ret = xtables_strtoul(s, end, &v, min, max);
635*a71a9546SAutomerger Merge Worker 	if (ret && value != NULL)
636*a71a9546SAutomerger Merge Worker 		*value = v;
637*a71a9546SAutomerger Merge Worker 	return ret;
638*a71a9546SAutomerger Merge Worker }
639*a71a9546SAutomerger Merge Worker 
xtables_service_to_port(const char * name,const char * proto)640*a71a9546SAutomerger Merge Worker int xtables_service_to_port(const char *name, const char *proto)
641*a71a9546SAutomerger Merge Worker {
642*a71a9546SAutomerger Merge Worker 	struct servent *service;
643*a71a9546SAutomerger Merge Worker 
644*a71a9546SAutomerger Merge Worker 	if ((service = getservbyname(name, proto)) != NULL)
645*a71a9546SAutomerger Merge Worker 		return ntohs((unsigned short) service->s_port);
646*a71a9546SAutomerger Merge Worker 
647*a71a9546SAutomerger Merge Worker 	return -1;
648*a71a9546SAutomerger Merge Worker }
649*a71a9546SAutomerger Merge Worker 
xtables_parse_port(const char * port,const char * proto)650*a71a9546SAutomerger Merge Worker uint16_t xtables_parse_port(const char *port, const char *proto)
651*a71a9546SAutomerger Merge Worker {
652*a71a9546SAutomerger Merge Worker 	unsigned int portnum;
653*a71a9546SAutomerger Merge Worker 
654*a71a9546SAutomerger Merge Worker 	if (xtables_strtoui(port, NULL, &portnum, 0, UINT16_MAX) ||
655*a71a9546SAutomerger Merge Worker 	    (portnum = xtables_service_to_port(port, proto)) != (unsigned)-1)
656*a71a9546SAutomerger Merge Worker 		return portnum;
657*a71a9546SAutomerger Merge Worker 
658*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM,
659*a71a9546SAutomerger Merge Worker 		   "invalid port/service `%s' specified", port);
660*a71a9546SAutomerger Merge Worker }
661*a71a9546SAutomerger Merge Worker 
xtables_parse_interface(const char * arg,char * vianame,unsigned char * mask)662*a71a9546SAutomerger Merge Worker void xtables_parse_interface(const char *arg, char *vianame,
663*a71a9546SAutomerger Merge Worker 			     unsigned char *mask)
664*a71a9546SAutomerger Merge Worker {
665*a71a9546SAutomerger Merge Worker 	unsigned int vialen = strlen(arg);
666*a71a9546SAutomerger Merge Worker 	unsigned int i;
667*a71a9546SAutomerger Merge Worker 
668*a71a9546SAutomerger Merge Worker 	memset(mask, 0, IFNAMSIZ);
669*a71a9546SAutomerger Merge Worker 	memset(vianame, 0, IFNAMSIZ);
670*a71a9546SAutomerger Merge Worker 
671*a71a9546SAutomerger Merge Worker 	if (vialen + 1 > IFNAMSIZ)
672*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
673*a71a9546SAutomerger Merge Worker 			   "interface name `%s' must be shorter than IFNAMSIZ"
674*a71a9546SAutomerger Merge Worker 			   " (%i)", arg, IFNAMSIZ-1);
675*a71a9546SAutomerger Merge Worker 
676*a71a9546SAutomerger Merge Worker 	strcpy(vianame, arg);
677*a71a9546SAutomerger Merge Worker 	if (vialen == 0)
678*a71a9546SAutomerger Merge Worker 		return;
679*a71a9546SAutomerger Merge Worker 	else if (vianame[vialen - 1] == '+') {
680*a71a9546SAutomerger Merge Worker 		memset(mask, 0xFF, vialen - 1);
681*a71a9546SAutomerger Merge Worker 		/* Don't remove `+' here! -HW */
682*a71a9546SAutomerger Merge Worker 	} else {
683*a71a9546SAutomerger Merge Worker 		/* Include nul-terminator in match */
684*a71a9546SAutomerger Merge Worker 		memset(mask, 0xFF, vialen + 1);
685*a71a9546SAutomerger Merge Worker 	}
686*a71a9546SAutomerger Merge Worker 
687*a71a9546SAutomerger Merge Worker 	/* Display warning on invalid characters */
688*a71a9546SAutomerger Merge Worker 	for (i = 0; vianame[i]; i++) {
689*a71a9546SAutomerger Merge Worker 		if (vianame[i] == '/' || vianame[i] == ' ') {
690*a71a9546SAutomerger Merge Worker 			fprintf(stderr,	"Warning: weird character in interface"
691*a71a9546SAutomerger Merge Worker 				" `%s' ('/' and ' ' are not allowed by the kernel).\n",
692*a71a9546SAutomerger Merge Worker 				vianame);
693*a71a9546SAutomerger Merge Worker 			break;
694*a71a9546SAutomerger Merge Worker 		}
695*a71a9546SAutomerger Merge Worker 	}
696*a71a9546SAutomerger Merge Worker }
697*a71a9546SAutomerger Merge Worker 
698*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
load_extension(const char * search_path,const char * af_prefix,const char * name,bool is_target)699*a71a9546SAutomerger Merge Worker static void *load_extension(const char *search_path, const char *af_prefix,
700*a71a9546SAutomerger Merge Worker     const char *name, bool is_target)
701*a71a9546SAutomerger Merge Worker {
702*a71a9546SAutomerger Merge Worker 	const char *all_prefixes[] = {af_prefix, "libxt_", NULL};
703*a71a9546SAutomerger Merge Worker 	const char **prefix;
704*a71a9546SAutomerger Merge Worker 	const char *dir = search_path, *next;
705*a71a9546SAutomerger Merge Worker 	void *ptr = NULL;
706*a71a9546SAutomerger Merge Worker 	struct stat sb;
707*a71a9546SAutomerger Merge Worker 	char path[256];
708*a71a9546SAutomerger Merge Worker 
709*a71a9546SAutomerger Merge Worker 	do {
710*a71a9546SAutomerger Merge Worker 		next = strchr(dir, ':');
711*a71a9546SAutomerger Merge Worker 		if (next == NULL)
712*a71a9546SAutomerger Merge Worker 			next = dir + strlen(dir);
713*a71a9546SAutomerger Merge Worker 
714*a71a9546SAutomerger Merge Worker 		for (prefix = all_prefixes; *prefix != NULL; ++prefix) {
715*a71a9546SAutomerger Merge Worker 			void *handle;
716*a71a9546SAutomerger Merge Worker 
717*a71a9546SAutomerger Merge Worker 			snprintf(path, sizeof(path), "%.*s/%s%s.so",
718*a71a9546SAutomerger Merge Worker 			         (unsigned int)(next - dir), dir,
719*a71a9546SAutomerger Merge Worker 			         *prefix, name);
720*a71a9546SAutomerger Merge Worker 
721*a71a9546SAutomerger Merge Worker 			if (stat(path, &sb) != 0) {
722*a71a9546SAutomerger Merge Worker 				if (errno == ENOENT)
723*a71a9546SAutomerger Merge Worker 					continue;
724*a71a9546SAutomerger Merge Worker 				fprintf(stderr, "%s: %s\n", path,
725*a71a9546SAutomerger Merge Worker 					strerror(errno));
726*a71a9546SAutomerger Merge Worker 				return NULL;
727*a71a9546SAutomerger Merge Worker 			}
728*a71a9546SAutomerger Merge Worker 			handle = dlopen(path, RTLD_NOW);
729*a71a9546SAutomerger Merge Worker 			if (handle == NULL) {
730*a71a9546SAutomerger Merge Worker 				fprintf(stderr, "%s: %s\n", path, dlerror());
731*a71a9546SAutomerger Merge Worker 				break;
732*a71a9546SAutomerger Merge Worker 			}
733*a71a9546SAutomerger Merge Worker 
734*a71a9546SAutomerger Merge Worker 			dlreg_add(handle);
735*a71a9546SAutomerger Merge Worker 
736*a71a9546SAutomerger Merge Worker 			if (is_target)
737*a71a9546SAutomerger Merge Worker 				ptr = xtables_find_target(name, XTF_DONT_LOAD);
738*a71a9546SAutomerger Merge Worker 			else
739*a71a9546SAutomerger Merge Worker 				ptr = xtables_find_match(name,
740*a71a9546SAutomerger Merge Worker 				      XTF_DONT_LOAD, NULL);
741*a71a9546SAutomerger Merge Worker 
742*a71a9546SAutomerger Merge Worker 			if (ptr != NULL)
743*a71a9546SAutomerger Merge Worker 				return ptr;
744*a71a9546SAutomerger Merge Worker 
745*a71a9546SAutomerger Merge Worker 			errno = ENOENT;
746*a71a9546SAutomerger Merge Worker 			return NULL;
747*a71a9546SAutomerger Merge Worker 		}
748*a71a9546SAutomerger Merge Worker 		dir = next + 1;
749*a71a9546SAutomerger Merge Worker 	} while (*next != '\0');
750*a71a9546SAutomerger Merge Worker 
751*a71a9546SAutomerger Merge Worker 	return NULL;
752*a71a9546SAutomerger Merge Worker }
753*a71a9546SAutomerger Merge Worker #endif
754*a71a9546SAutomerger Merge Worker 
extension_cmp(const char * name1,const char * name2,uint32_t family)755*a71a9546SAutomerger Merge Worker static bool extension_cmp(const char *name1, const char *name2, uint32_t family)
756*a71a9546SAutomerger Merge Worker {
757*a71a9546SAutomerger Merge Worker 	if (strcmp(name1, name2) == 0 &&
758*a71a9546SAutomerger Merge Worker 	    (family == afinfo->family ||
759*a71a9546SAutomerger Merge Worker 	     family == NFPROTO_UNSPEC))
760*a71a9546SAutomerger Merge Worker 		return true;
761*a71a9546SAutomerger Merge Worker 
762*a71a9546SAutomerger Merge Worker 	return false;
763*a71a9546SAutomerger Merge Worker }
764*a71a9546SAutomerger Merge Worker 
765*a71a9546SAutomerger Merge Worker struct xtables_match *
xtables_find_match(const char * name,enum xtables_tryload tryload,struct xtables_rule_match ** matches)766*a71a9546SAutomerger Merge Worker xtables_find_match(const char *name, enum xtables_tryload tryload,
767*a71a9546SAutomerger Merge Worker 		   struct xtables_rule_match **matches)
768*a71a9546SAutomerger Merge Worker {
769*a71a9546SAutomerger Merge Worker 	struct xtables_match *prev = NULL;
770*a71a9546SAutomerger Merge Worker 	struct xtables_match **dptr;
771*a71a9546SAutomerger Merge Worker 	struct xtables_match *ptr;
772*a71a9546SAutomerger Merge Worker 	const char *icmp6 = "icmp6";
773*a71a9546SAutomerger Merge Worker 	bool found = false;
774*a71a9546SAutomerger Merge Worker 	bool seen = false;
775*a71a9546SAutomerger Merge Worker 
776*a71a9546SAutomerger Merge Worker 	if (strlen(name) >= XT_EXTENSION_MAXNAMELEN)
777*a71a9546SAutomerger Merge Worker 		xtables_error(PARAMETER_PROBLEM,
778*a71a9546SAutomerger Merge Worker 			   "Invalid match name \"%s\" (%u chars max)",
779*a71a9546SAutomerger Merge Worker 			   name, XT_EXTENSION_MAXNAMELEN - 1);
780*a71a9546SAutomerger Merge Worker 
781*a71a9546SAutomerger Merge Worker 	/* This is ugly as hell. Nonetheless, there is no way of changing
782*a71a9546SAutomerger Merge Worker 	 * this without hurting backwards compatibility */
783*a71a9546SAutomerger Merge Worker 	if ( (strcmp(name,"icmpv6") == 0) ||
784*a71a9546SAutomerger Merge Worker 	     (strcmp(name,"ipv6-icmp") == 0) ||
785*a71a9546SAutomerger Merge Worker 	     (strcmp(name,"icmp6") == 0) )
786*a71a9546SAutomerger Merge Worker 		name = icmp6;
787*a71a9546SAutomerger Merge Worker 
788*a71a9546SAutomerger Merge Worker 	/* Trigger delayed initialization */
789*a71a9546SAutomerger Merge Worker 	for (dptr = &xtables_pending_matches; *dptr; ) {
790*a71a9546SAutomerger Merge Worker 		if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
791*a71a9546SAutomerger Merge Worker 			ptr = *dptr;
792*a71a9546SAutomerger Merge Worker 			*dptr = (*dptr)->next;
793*a71a9546SAutomerger Merge Worker 			seen = true;
794*a71a9546SAutomerger Merge Worker 			if (!found &&
795*a71a9546SAutomerger Merge Worker 			    xtables_fully_register_pending_match(ptr, prev)) {
796*a71a9546SAutomerger Merge Worker 				found = true;
797*a71a9546SAutomerger Merge Worker 				prev = ptr;
798*a71a9546SAutomerger Merge Worker 				continue;
799*a71a9546SAutomerger Merge Worker 			} else if (prev) {
800*a71a9546SAutomerger Merge Worker 				continue;
801*a71a9546SAutomerger Merge Worker 			}
802*a71a9546SAutomerger Merge Worker 			*dptr = ptr;
803*a71a9546SAutomerger Merge Worker 		}
804*a71a9546SAutomerger Merge Worker 		dptr = &((*dptr)->next);
805*a71a9546SAutomerger Merge Worker 	}
806*a71a9546SAutomerger Merge Worker 
807*a71a9546SAutomerger Merge Worker 	if (seen && !found)
808*a71a9546SAutomerger Merge Worker 		fprintf(stderr,
809*a71a9546SAutomerger Merge Worker 			"Warning: Extension %s is not supported, missing kernel module?\n",
810*a71a9546SAutomerger Merge Worker 			name);
811*a71a9546SAutomerger Merge Worker 
812*a71a9546SAutomerger Merge Worker 	for (ptr = xtables_matches; ptr; ptr = ptr->next) {
813*a71a9546SAutomerger Merge Worker 		if (extension_cmp(name, ptr->name, ptr->family)) {
814*a71a9546SAutomerger Merge Worker 			struct xtables_match *clone;
815*a71a9546SAutomerger Merge Worker 
816*a71a9546SAutomerger Merge Worker 			/* First match of this type: */
817*a71a9546SAutomerger Merge Worker 			if (ptr->m == NULL)
818*a71a9546SAutomerger Merge Worker 				break;
819*a71a9546SAutomerger Merge Worker 
820*a71a9546SAutomerger Merge Worker 			/* Second and subsequent clones */
821*a71a9546SAutomerger Merge Worker 			clone = xtables_malloc(sizeof(struct xtables_match));
822*a71a9546SAutomerger Merge Worker 			memcpy(clone, ptr, sizeof(struct xtables_match));
823*a71a9546SAutomerger Merge Worker 			clone->udata = NULL;
824*a71a9546SAutomerger Merge Worker 			clone->mflags = 0;
825*a71a9546SAutomerger Merge Worker 			/* This is a clone: */
826*a71a9546SAutomerger Merge Worker 			clone->next = clone;
827*a71a9546SAutomerger Merge Worker 
828*a71a9546SAutomerger Merge Worker 			ptr = clone;
829*a71a9546SAutomerger Merge Worker 			break;
830*a71a9546SAutomerger Merge Worker 		}
831*a71a9546SAutomerger Merge Worker 	}
832*a71a9546SAutomerger Merge Worker 
833*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
834*a71a9546SAutomerger Merge Worker 	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
835*a71a9546SAutomerger Merge Worker 		ptr = load_extension(xtables_libdir, afinfo->libprefix,
836*a71a9546SAutomerger Merge Worker 		      name, false);
837*a71a9546SAutomerger Merge Worker 
838*a71a9546SAutomerger Merge Worker 		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
839*a71a9546SAutomerger Merge Worker 			xt_params->exit_err(PARAMETER_PROBLEM,
840*a71a9546SAutomerger Merge Worker 				   "Couldn't load match `%s':%s\n",
841*a71a9546SAutomerger Merge Worker 				   name, strerror(errno));
842*a71a9546SAutomerger Merge Worker 	}
843*a71a9546SAutomerger Merge Worker #else
844*a71a9546SAutomerger Merge Worker 	if (ptr && !ptr->loaded) {
845*a71a9546SAutomerger Merge Worker 		if (tryload != XTF_DONT_LOAD)
846*a71a9546SAutomerger Merge Worker 			ptr->loaded = 1;
847*a71a9546SAutomerger Merge Worker 		else
848*a71a9546SAutomerger Merge Worker 			ptr = NULL;
849*a71a9546SAutomerger Merge Worker 	}
850*a71a9546SAutomerger Merge Worker 	if(!ptr && (tryload == XTF_LOAD_MUST_SUCCEED)) {
851*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
852*a71a9546SAutomerger Merge Worker 			   "Couldn't find match `%s'\n", name);
853*a71a9546SAutomerger Merge Worker 	}
854*a71a9546SAutomerger Merge Worker #endif
855*a71a9546SAutomerger Merge Worker 
856*a71a9546SAutomerger Merge Worker 	if (ptr && matches) {
857*a71a9546SAutomerger Merge Worker 		struct xtables_rule_match **i;
858*a71a9546SAutomerger Merge Worker 		struct xtables_rule_match *newentry;
859*a71a9546SAutomerger Merge Worker 
860*a71a9546SAutomerger Merge Worker 		newentry = xtables_malloc(sizeof(struct xtables_rule_match));
861*a71a9546SAutomerger Merge Worker 
862*a71a9546SAutomerger Merge Worker 		for (i = matches; *i; i = &(*i)->next) {
863*a71a9546SAutomerger Merge Worker 			if (extension_cmp(name, (*i)->match->name,
864*a71a9546SAutomerger Merge Worker 					  (*i)->match->family))
865*a71a9546SAutomerger Merge Worker 				(*i)->completed = true;
866*a71a9546SAutomerger Merge Worker 		}
867*a71a9546SAutomerger Merge Worker 		newentry->match = ptr;
868*a71a9546SAutomerger Merge Worker 		newentry->completed = false;
869*a71a9546SAutomerger Merge Worker 		newentry->next = NULL;
870*a71a9546SAutomerger Merge Worker 		*i = newentry;
871*a71a9546SAutomerger Merge Worker 	}
872*a71a9546SAutomerger Merge Worker 
873*a71a9546SAutomerger Merge Worker 	return ptr;
874*a71a9546SAutomerger Merge Worker }
875*a71a9546SAutomerger Merge Worker 
876*a71a9546SAutomerger Merge Worker struct xtables_match *
xtables_find_match_revision(const char * name,enum xtables_tryload tryload,struct xtables_match * match,int revision)877*a71a9546SAutomerger Merge Worker xtables_find_match_revision(const char *name, enum xtables_tryload tryload,
878*a71a9546SAutomerger Merge Worker 			    struct xtables_match *match, int revision)
879*a71a9546SAutomerger Merge Worker {
880*a71a9546SAutomerger Merge Worker 	if (!match) {
881*a71a9546SAutomerger Merge Worker 		match = xtables_find_match(name, tryload, NULL);
882*a71a9546SAutomerger Merge Worker 		if (!match)
883*a71a9546SAutomerger Merge Worker 			return NULL;
884*a71a9546SAutomerger Merge Worker 	}
885*a71a9546SAutomerger Merge Worker 
886*a71a9546SAutomerger Merge Worker 	while (1) {
887*a71a9546SAutomerger Merge Worker 		if (match->revision == revision)
888*a71a9546SAutomerger Merge Worker 			return match;
889*a71a9546SAutomerger Merge Worker 		match = match->next;
890*a71a9546SAutomerger Merge Worker 		if (!match)
891*a71a9546SAutomerger Merge Worker 			return NULL;
892*a71a9546SAutomerger Merge Worker 		if (!extension_cmp(name, match->name, match->family))
893*a71a9546SAutomerger Merge Worker 			return NULL;
894*a71a9546SAutomerger Merge Worker 	}
895*a71a9546SAutomerger Merge Worker }
896*a71a9546SAutomerger Merge Worker 
897*a71a9546SAutomerger Merge Worker struct xtables_target *
xtables_find_target(const char * name,enum xtables_tryload tryload)898*a71a9546SAutomerger Merge Worker xtables_find_target(const char *name, enum xtables_tryload tryload)
899*a71a9546SAutomerger Merge Worker {
900*a71a9546SAutomerger Merge Worker 	struct xtables_target *prev = NULL;
901*a71a9546SAutomerger Merge Worker 	struct xtables_target **dptr;
902*a71a9546SAutomerger Merge Worker 	struct xtables_target *ptr;
903*a71a9546SAutomerger Merge Worker 	bool found = false;
904*a71a9546SAutomerger Merge Worker 	bool seen = false;
905*a71a9546SAutomerger Merge Worker 
906*a71a9546SAutomerger Merge Worker 	/* Standard target? */
907*a71a9546SAutomerger Merge Worker 	if (strcmp(name, "") == 0
908*a71a9546SAutomerger Merge Worker 	    || strcmp(name, XTC_LABEL_ACCEPT) == 0
909*a71a9546SAutomerger Merge Worker 	    || strcmp(name, XTC_LABEL_DROP) == 0
910*a71a9546SAutomerger Merge Worker 	    || strcmp(name, XTC_LABEL_QUEUE) == 0
911*a71a9546SAutomerger Merge Worker 	    || strcmp(name, XTC_LABEL_RETURN) == 0)
912*a71a9546SAutomerger Merge Worker 		name = "standard";
913*a71a9546SAutomerger Merge Worker 	/* known non-target? */
914*a71a9546SAutomerger Merge Worker 	else if (notargets_hlist_lookup(name) &&
915*a71a9546SAutomerger Merge Worker 		 tryload != XTF_LOAD_MUST_SUCCEED)
916*a71a9546SAutomerger Merge Worker 		return NULL;
917*a71a9546SAutomerger Merge Worker 
918*a71a9546SAutomerger Merge Worker 	/* Trigger delayed initialization */
919*a71a9546SAutomerger Merge Worker 	for (dptr = &xtables_pending_targets; *dptr; ) {
920*a71a9546SAutomerger Merge Worker 		if (extension_cmp(name, (*dptr)->name, (*dptr)->family)) {
921*a71a9546SAutomerger Merge Worker 			ptr = *dptr;
922*a71a9546SAutomerger Merge Worker 			*dptr = (*dptr)->next;
923*a71a9546SAutomerger Merge Worker 			seen = true;
924*a71a9546SAutomerger Merge Worker 			if (!found &&
925*a71a9546SAutomerger Merge Worker 			    xtables_fully_register_pending_target(ptr, prev)) {
926*a71a9546SAutomerger Merge Worker 				found = true;
927*a71a9546SAutomerger Merge Worker 				prev = ptr;
928*a71a9546SAutomerger Merge Worker 				continue;
929*a71a9546SAutomerger Merge Worker 			} else if (prev) {
930*a71a9546SAutomerger Merge Worker 				continue;
931*a71a9546SAutomerger Merge Worker 			}
932*a71a9546SAutomerger Merge Worker 			*dptr = ptr;
933*a71a9546SAutomerger Merge Worker 		}
934*a71a9546SAutomerger Merge Worker 		dptr = &((*dptr)->next);
935*a71a9546SAutomerger Merge Worker 	}
936*a71a9546SAutomerger Merge Worker 
937*a71a9546SAutomerger Merge Worker 	if (seen && !found)
938*a71a9546SAutomerger Merge Worker 		fprintf(stderr,
939*a71a9546SAutomerger Merge Worker 			"Warning: Extension %s is not supported, missing kernel module?\n",
940*a71a9546SAutomerger Merge Worker 			name);
941*a71a9546SAutomerger Merge Worker 
942*a71a9546SAutomerger Merge Worker 	for (ptr = xtables_targets; ptr; ptr = ptr->next) {
943*a71a9546SAutomerger Merge Worker 		if (extension_cmp(name, ptr->name, ptr->family)) {
944*a71a9546SAutomerger Merge Worker #if 0			/* Code block below causes memory leak.  (Bugs 162925719 and 168688680) */
945*a71a9546SAutomerger Merge Worker 			struct xtables_target *clone;
946*a71a9546SAutomerger Merge Worker 
947*a71a9546SAutomerger Merge Worker 			/* First target of this type: */
948*a71a9546SAutomerger Merge Worker 			if (ptr->t == NULL)
949*a71a9546SAutomerger Merge Worker 				break;
950*a71a9546SAutomerger Merge Worker 
951*a71a9546SAutomerger Merge Worker 			/* Second and subsequent clones */
952*a71a9546SAutomerger Merge Worker 			clone = xtables_malloc(sizeof(struct xtables_target));
953*a71a9546SAutomerger Merge Worker 			memcpy(clone, ptr, sizeof(struct xtables_target));
954*a71a9546SAutomerger Merge Worker 			clone->udata = NULL;
955*a71a9546SAutomerger Merge Worker 			clone->tflags = 0;
956*a71a9546SAutomerger Merge Worker 			/* This is a clone: */
957*a71a9546SAutomerger Merge Worker 			clone->next = clone;
958*a71a9546SAutomerger Merge Worker 
959*a71a9546SAutomerger Merge Worker 			ptr = clone;
960*a71a9546SAutomerger Merge Worker #endif
961*a71a9546SAutomerger Merge Worker 			break;
962*a71a9546SAutomerger Merge Worker 		}
963*a71a9546SAutomerger Merge Worker 	}
964*a71a9546SAutomerger Merge Worker 
965*a71a9546SAutomerger Merge Worker #ifndef NO_SHARED_LIBS
966*a71a9546SAutomerger Merge Worker 	if (!ptr && tryload != XTF_DONT_LOAD && tryload != XTF_DURING_LOAD) {
967*a71a9546SAutomerger Merge Worker 		ptr = load_extension(xtables_libdir, afinfo->libprefix,
968*a71a9546SAutomerger Merge Worker 		      name, true);
969*a71a9546SAutomerger Merge Worker 
970*a71a9546SAutomerger Merge Worker 		if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED)
971*a71a9546SAutomerger Merge Worker 			xt_params->exit_err(PARAMETER_PROBLEM,
972*a71a9546SAutomerger Merge Worker 				   "Couldn't load target `%s':%s\n",
973*a71a9546SAutomerger Merge Worker 				   name, strerror(errno));
974*a71a9546SAutomerger Merge Worker 	}
975*a71a9546SAutomerger Merge Worker #else
976*a71a9546SAutomerger Merge Worker 	if (ptr && !ptr->loaded) {
977*a71a9546SAutomerger Merge Worker 		if (tryload != XTF_DONT_LOAD)
978*a71a9546SAutomerger Merge Worker 			ptr->loaded = 1;
979*a71a9546SAutomerger Merge Worker 		else
980*a71a9546SAutomerger Merge Worker 			ptr = NULL;
981*a71a9546SAutomerger Merge Worker 	}
982*a71a9546SAutomerger Merge Worker 	if (ptr == NULL && tryload == XTF_LOAD_MUST_SUCCEED) {
983*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
984*a71a9546SAutomerger Merge Worker 			   "Couldn't find target `%s'\n", name);
985*a71a9546SAutomerger Merge Worker 	}
986*a71a9546SAutomerger Merge Worker #endif
987*a71a9546SAutomerger Merge Worker 
988*a71a9546SAutomerger Merge Worker 	if (ptr)
989*a71a9546SAutomerger Merge Worker 		ptr->used = 1;
990*a71a9546SAutomerger Merge Worker 	else
991*a71a9546SAutomerger Merge Worker 		notargets_hlist_insert(name);
992*a71a9546SAutomerger Merge Worker 
993*a71a9546SAutomerger Merge Worker 	return ptr;
994*a71a9546SAutomerger Merge Worker }
995*a71a9546SAutomerger Merge Worker 
996*a71a9546SAutomerger Merge Worker struct xtables_target *
xtables_find_target_revision(const char * name,enum xtables_tryload tryload,struct xtables_target * target,int revision)997*a71a9546SAutomerger Merge Worker xtables_find_target_revision(const char *name, enum xtables_tryload tryload,
998*a71a9546SAutomerger Merge Worker 			     struct xtables_target *target, int revision)
999*a71a9546SAutomerger Merge Worker {
1000*a71a9546SAutomerger Merge Worker 	if (!target) {
1001*a71a9546SAutomerger Merge Worker 		target = xtables_find_target(name, tryload);
1002*a71a9546SAutomerger Merge Worker 		if (!target)
1003*a71a9546SAutomerger Merge Worker 			return NULL;
1004*a71a9546SAutomerger Merge Worker 	}
1005*a71a9546SAutomerger Merge Worker 
1006*a71a9546SAutomerger Merge Worker 	while (1) {
1007*a71a9546SAutomerger Merge Worker 		if (target->revision == revision)
1008*a71a9546SAutomerger Merge Worker 			return target;
1009*a71a9546SAutomerger Merge Worker 		target = target->next;
1010*a71a9546SAutomerger Merge Worker 		if (!target)
1011*a71a9546SAutomerger Merge Worker 			return NULL;
1012*a71a9546SAutomerger Merge Worker 		if (!extension_cmp(name, target->name, target->family))
1013*a71a9546SAutomerger Merge Worker 			return NULL;
1014*a71a9546SAutomerger Merge Worker 	}
1015*a71a9546SAutomerger Merge Worker }
1016*a71a9546SAutomerger Merge Worker 
xtables_compatible_revision(const char * name,uint8_t revision,int opt)1017*a71a9546SAutomerger Merge Worker int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
1018*a71a9546SAutomerger Merge Worker {
1019*a71a9546SAutomerger Merge Worker 	struct xt_get_revision rev;
1020*a71a9546SAutomerger Merge Worker 	socklen_t s = sizeof(rev);
1021*a71a9546SAutomerger Merge Worker 	int max_rev, sockfd;
1022*a71a9546SAutomerger Merge Worker 
1023*a71a9546SAutomerger Merge Worker 	sockfd = socket(afinfo->family, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
1024*a71a9546SAutomerger Merge Worker 	if (sockfd < 0) {
1025*a71a9546SAutomerger Merge Worker 		if (errno == EPERM) {
1026*a71a9546SAutomerger Merge Worker 			/* revision 0 is always supported. */
1027*a71a9546SAutomerger Merge Worker 			if (revision != 0)
1028*a71a9546SAutomerger Merge Worker 				fprintf(stderr, "%s: Could not determine whether "
1029*a71a9546SAutomerger Merge Worker 						"revision %u is supported, "
1030*a71a9546SAutomerger Merge Worker 						"assuming it is.\n",
1031*a71a9546SAutomerger Merge Worker 					name, revision);
1032*a71a9546SAutomerger Merge Worker 			return 1;
1033*a71a9546SAutomerger Merge Worker 		}
1034*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "Could not open socket to kernel: %s\n",
1035*a71a9546SAutomerger Merge Worker 			strerror(errno));
1036*a71a9546SAutomerger Merge Worker 		exit(1);
1037*a71a9546SAutomerger Merge Worker 	}
1038*a71a9546SAutomerger Merge Worker 
1039*a71a9546SAutomerger Merge Worker 	xtables_load_ko(xtables_modprobe_program, true);
1040*a71a9546SAutomerger Merge Worker 
1041*a71a9546SAutomerger Merge Worker 	strncpy(rev.name, name, XT_EXTENSION_MAXNAMELEN - 1);
1042*a71a9546SAutomerger Merge Worker 	rev.name[XT_EXTENSION_MAXNAMELEN - 1] = '\0';
1043*a71a9546SAutomerger Merge Worker 	rev.revision = revision;
1044*a71a9546SAutomerger Merge Worker 
1045*a71a9546SAutomerger Merge Worker 	max_rev = getsockopt(sockfd, afinfo->ipproto, opt, &rev, &s);
1046*a71a9546SAutomerger Merge Worker 	if (max_rev < 0) {
1047*a71a9546SAutomerger Merge Worker 		/* Definitely don't support this? */
1048*a71a9546SAutomerger Merge Worker 		if (errno == ENOENT || errno == EPROTONOSUPPORT) {
1049*a71a9546SAutomerger Merge Worker 			close(sockfd);
1050*a71a9546SAutomerger Merge Worker 			/* Pretend revision 0 support for better error messaging */
1051*a71a9546SAutomerger Merge Worker 			if (revision == 0)
1052*a71a9546SAutomerger Merge Worker 				fprintf(stderr,
1053*a71a9546SAutomerger Merge Worker 					"Warning: Extension %s revision 0 not supported, missing kernel module?\n",
1054*a71a9546SAutomerger Merge Worker 					name);
1055*a71a9546SAutomerger Merge Worker 			return (revision == 0);
1056*a71a9546SAutomerger Merge Worker 		} else if (errno == ENOPROTOOPT) {
1057*a71a9546SAutomerger Merge Worker 			close(sockfd);
1058*a71a9546SAutomerger Merge Worker 			/* Assume only revision 0 support (old kernel) */
1059*a71a9546SAutomerger Merge Worker 			return (revision == 0);
1060*a71a9546SAutomerger Merge Worker 		} else {
1061*a71a9546SAutomerger Merge Worker 			fprintf(stderr, "getsockopt failed strangely: %s\n",
1062*a71a9546SAutomerger Merge Worker 				strerror(errno));
1063*a71a9546SAutomerger Merge Worker 			exit(1);
1064*a71a9546SAutomerger Merge Worker 		}
1065*a71a9546SAutomerger Merge Worker 	}
1066*a71a9546SAutomerger Merge Worker 	close(sockfd);
1067*a71a9546SAutomerger Merge Worker 	return 1;
1068*a71a9546SAutomerger Merge Worker }
1069*a71a9546SAutomerger Merge Worker 
1070*a71a9546SAutomerger Merge Worker 
compatible_match_revision(const char * name,uint8_t revision)1071*a71a9546SAutomerger Merge Worker static int compatible_match_revision(const char *name, uint8_t revision)
1072*a71a9546SAutomerger Merge Worker {
1073*a71a9546SAutomerger Merge Worker 	return xt_params->compat_rev(name, revision, afinfo->so_rev_match);
1074*a71a9546SAutomerger Merge Worker }
1075*a71a9546SAutomerger Merge Worker 
compatible_target_revision(const char * name,uint8_t revision)1076*a71a9546SAutomerger Merge Worker static int compatible_target_revision(const char *name, uint8_t revision)
1077*a71a9546SAutomerger Merge Worker {
1078*a71a9546SAutomerger Merge Worker 	return xt_params->compat_rev(name, revision, afinfo->so_rev_target);
1079*a71a9546SAutomerger Merge Worker }
1080*a71a9546SAutomerger Merge Worker 
xtables_check_options(const char * name,const struct option * opt)1081*a71a9546SAutomerger Merge Worker static void xtables_check_options(const char *name, const struct option *opt)
1082*a71a9546SAutomerger Merge Worker {
1083*a71a9546SAutomerger Merge Worker 	for (; opt->name != NULL; ++opt)
1084*a71a9546SAutomerger Merge Worker 		if (opt->val < 0 || opt->val >= XT_OPTION_OFFSET_SCALE) {
1085*a71a9546SAutomerger Merge Worker 			fprintf(stderr, "%s: Extension %s uses invalid "
1086*a71a9546SAutomerger Merge Worker 			        "option value %d\n",xt_params->program_name,
1087*a71a9546SAutomerger Merge Worker 			        name, opt->val);
1088*a71a9546SAutomerger Merge Worker 			exit(1);
1089*a71a9546SAutomerger Merge Worker 		}
1090*a71a9546SAutomerger Merge Worker }
1091*a71a9546SAutomerger Merge Worker 
1092*a71a9546SAutomerger Merge Worker static int xtables_match_prefer(const struct xtables_match *a,
1093*a71a9546SAutomerger Merge Worker 				const struct xtables_match *b);
1094*a71a9546SAutomerger Merge Worker 
xtables_register_match(struct xtables_match * me)1095*a71a9546SAutomerger Merge Worker void xtables_register_match(struct xtables_match *me)
1096*a71a9546SAutomerger Merge Worker {
1097*a71a9546SAutomerger Merge Worker 	struct xtables_match **pos;
1098*a71a9546SAutomerger Merge Worker 	bool seen_myself = false;
1099*a71a9546SAutomerger Merge Worker 
1100*a71a9546SAutomerger Merge Worker 	if (me->next) {
1101*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match \"%s\" already registered\n",
1102*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1103*a71a9546SAutomerger Merge Worker 		exit(1);
1104*a71a9546SAutomerger Merge Worker 	}
1105*a71a9546SAutomerger Merge Worker 
1106*a71a9546SAutomerger Merge Worker 	if (me->version == NULL) {
1107*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match %s<%u> is missing a version\n",
1108*a71a9546SAutomerger Merge Worker 		        xt_params->program_name, me->name, me->revision);
1109*a71a9546SAutomerger Merge Worker 		exit(1);
1110*a71a9546SAutomerger Merge Worker 	}
1111*a71a9546SAutomerger Merge Worker 
1112*a71a9546SAutomerger Merge Worker 	if (me->size != XT_ALIGN(me->size)) {
1113*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match \"%s\" has invalid size %u.\n",
1114*a71a9546SAutomerger Merge Worker 		        xt_params->program_name, me->name,
1115*a71a9546SAutomerger Merge Worker 		        (unsigned int)me->size);
1116*a71a9546SAutomerger Merge Worker 		exit(1);
1117*a71a9546SAutomerger Merge Worker 	}
1118*a71a9546SAutomerger Merge Worker 
1119*a71a9546SAutomerger Merge Worker 	if (strcmp(me->version, XTABLES_VERSION) != 0) {
1120*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match \"%s\" has version \"%s\", "
1121*a71a9546SAutomerger Merge Worker 		        "but \"%s\" is required.\n",
1122*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name,
1123*a71a9546SAutomerger Merge Worker 			me->version, XTABLES_VERSION);
1124*a71a9546SAutomerger Merge Worker 		exit(1);
1125*a71a9546SAutomerger Merge Worker 	}
1126*a71a9546SAutomerger Merge Worker 
1127*a71a9546SAutomerger Merge Worker 	if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
1128*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match `%s' has invalid name\n",
1129*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1130*a71a9546SAutomerger Merge Worker 		exit(1);
1131*a71a9546SAutomerger Merge Worker 	}
1132*a71a9546SAutomerger Merge Worker 
1133*a71a9546SAutomerger Merge Worker 	if (me->real_name && strlen(me->real_name) >= XT_EXTENSION_MAXNAMELEN) {
1134*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: match `%s' has invalid real name\n",
1135*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->real_name);
1136*a71a9546SAutomerger Merge Worker 		exit(1);
1137*a71a9546SAutomerger Merge Worker 	}
1138*a71a9546SAutomerger Merge Worker 
1139*a71a9546SAutomerger Merge Worker 	if (me->family >= NPROTO) {
1140*a71a9546SAutomerger Merge Worker 		fprintf(stderr,
1141*a71a9546SAutomerger Merge Worker 			"%s: BUG: match %s has invalid protocol family\n",
1142*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1143*a71a9546SAutomerger Merge Worker 		exit(1);
1144*a71a9546SAutomerger Merge Worker 	}
1145*a71a9546SAutomerger Merge Worker 
1146*a71a9546SAutomerger Merge Worker 	if (me->x6_options != NULL)
1147*a71a9546SAutomerger Merge Worker 		xtables_option_metavalidate(me->name, me->x6_options);
1148*a71a9546SAutomerger Merge Worker 	if (me->extra_opts != NULL)
1149*a71a9546SAutomerger Merge Worker 		xtables_check_options(me->name, me->extra_opts);
1150*a71a9546SAutomerger Merge Worker 
1151*a71a9546SAutomerger Merge Worker 	/* order into linked list of matches pending full registration */
1152*a71a9546SAutomerger Merge Worker 	for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) {
1153*a71a9546SAutomerger Merge Worker 		/* group by name and family */
1154*a71a9546SAutomerger Merge Worker 		if (strcmp(me->name, (*pos)->name) ||
1155*a71a9546SAutomerger Merge Worker 		    me->family != (*pos)->family) {
1156*a71a9546SAutomerger Merge Worker 			if (seen_myself)
1157*a71a9546SAutomerger Merge Worker 				break; /* end of own group, append to it */
1158*a71a9546SAutomerger Merge Worker 			continue;
1159*a71a9546SAutomerger Merge Worker 		}
1160*a71a9546SAutomerger Merge Worker 		/* found own group */
1161*a71a9546SAutomerger Merge Worker 		seen_myself = true;
1162*a71a9546SAutomerger Merge Worker 		if (xtables_match_prefer(me, *pos) >= 0)
1163*a71a9546SAutomerger Merge Worker 			break; /* put preferred items first in group */
1164*a71a9546SAutomerger Merge Worker 	}
1165*a71a9546SAutomerger Merge Worker 	/* if own group was not found, prepend item */
1166*a71a9546SAutomerger Merge Worker 	if (!*pos && !seen_myself)
1167*a71a9546SAutomerger Merge Worker 		pos = &xtables_pending_matches;
1168*a71a9546SAutomerger Merge Worker 
1169*a71a9546SAutomerger Merge Worker 	me->next = *pos;
1170*a71a9546SAutomerger Merge Worker 	*pos = me;
1171*a71a9546SAutomerger Merge Worker #ifdef DEBUG
1172*a71a9546SAutomerger Merge Worker 	printf("%s: inserted match %s (family %d, revision %d):\n",
1173*a71a9546SAutomerger Merge Worker 			__func__, me->name, me->family, me->revision);
1174*a71a9546SAutomerger Merge Worker 	for (pos = &xtables_pending_matches; *pos; pos = &(*pos)->next) {
1175*a71a9546SAutomerger Merge Worker 		printf("%s:\tmatch %s (family %d, revision %d)\n", __func__,
1176*a71a9546SAutomerger Merge Worker 		       (*pos)->name, (*pos)->family, (*pos)->revision);
1177*a71a9546SAutomerger Merge Worker 	}
1178*a71a9546SAutomerger Merge Worker #endif
1179*a71a9546SAutomerger Merge Worker }
1180*a71a9546SAutomerger Merge Worker 
1181*a71a9546SAutomerger Merge Worker /**
1182*a71a9546SAutomerger Merge Worker  * Compare two actions for their preference
1183*a71a9546SAutomerger Merge Worker  * @a:	one action
1184*a71a9546SAutomerger Merge Worker  * @b: 	another
1185*a71a9546SAutomerger Merge Worker  *
1186*a71a9546SAutomerger Merge Worker  * Like strcmp, returns a negative number if @a is less preferred than @b,
1187*a71a9546SAutomerger Merge Worker  * positive number if @a is more preferred than @b, or zero if equally
1188*a71a9546SAutomerger Merge Worker  * preferred.
1189*a71a9546SAutomerger Merge Worker  */
1190*a71a9546SAutomerger Merge Worker static int
xtables_mt_prefer(bool a_alias,unsigned int a_rev,unsigned int a_fam,bool b_alias,unsigned int b_rev,unsigned int b_fam)1191*a71a9546SAutomerger Merge Worker xtables_mt_prefer(bool a_alias, unsigned int a_rev, unsigned int a_fam,
1192*a71a9546SAutomerger Merge Worker 		  bool b_alias, unsigned int b_rev, unsigned int b_fam)
1193*a71a9546SAutomerger Merge Worker {
1194*a71a9546SAutomerger Merge Worker 	/*
1195*a71a9546SAutomerger Merge Worker 	 * Alias ranks higher than no alias.
1196*a71a9546SAutomerger Merge Worker 	 * (We want the new action to be used whenever possible.)
1197*a71a9546SAutomerger Merge Worker 	 */
1198*a71a9546SAutomerger Merge Worker 	if (!a_alias && b_alias)
1199*a71a9546SAutomerger Merge Worker 		return -1;
1200*a71a9546SAutomerger Merge Worker 	if (a_alias && !b_alias)
1201*a71a9546SAutomerger Merge Worker 		return 1;
1202*a71a9546SAutomerger Merge Worker 
1203*a71a9546SAutomerger Merge Worker 	/* Higher revision ranks higher. */
1204*a71a9546SAutomerger Merge Worker 	if (a_rev < b_rev)
1205*a71a9546SAutomerger Merge Worker 		return -1;
1206*a71a9546SAutomerger Merge Worker 	if (a_rev > b_rev)
1207*a71a9546SAutomerger Merge Worker 		return 1;
1208*a71a9546SAutomerger Merge Worker 
1209*a71a9546SAutomerger Merge Worker 	/* NFPROTO_<specific> ranks higher than NFPROTO_UNSPEC. */
1210*a71a9546SAutomerger Merge Worker 	if (a_fam == NFPROTO_UNSPEC && b_fam != NFPROTO_UNSPEC)
1211*a71a9546SAutomerger Merge Worker 		return -1;
1212*a71a9546SAutomerger Merge Worker 	if (a_fam != NFPROTO_UNSPEC && b_fam == NFPROTO_UNSPEC)
1213*a71a9546SAutomerger Merge Worker 		return 1;
1214*a71a9546SAutomerger Merge Worker 
1215*a71a9546SAutomerger Merge Worker 	/* Must be the same thing. */
1216*a71a9546SAutomerger Merge Worker 	return 0;
1217*a71a9546SAutomerger Merge Worker }
1218*a71a9546SAutomerger Merge Worker 
xtables_match_prefer(const struct xtables_match * a,const struct xtables_match * b)1219*a71a9546SAutomerger Merge Worker static int xtables_match_prefer(const struct xtables_match *a,
1220*a71a9546SAutomerger Merge Worker 				const struct xtables_match *b)
1221*a71a9546SAutomerger Merge Worker {
1222*a71a9546SAutomerger Merge Worker 	return xtables_mt_prefer(a->real_name != NULL,
1223*a71a9546SAutomerger Merge Worker 				 a->revision, a->family,
1224*a71a9546SAutomerger Merge Worker 				 b->real_name != NULL,
1225*a71a9546SAutomerger Merge Worker 				 b->revision, b->family);
1226*a71a9546SAutomerger Merge Worker }
1227*a71a9546SAutomerger Merge Worker 
xtables_target_prefer(const struct xtables_target * a,const struct xtables_target * b)1228*a71a9546SAutomerger Merge Worker static int xtables_target_prefer(const struct xtables_target *a,
1229*a71a9546SAutomerger Merge Worker 				 const struct xtables_target *b)
1230*a71a9546SAutomerger Merge Worker {
1231*a71a9546SAutomerger Merge Worker 	/*
1232*a71a9546SAutomerger Merge Worker 	 * Note that if x->real_name==NULL, it will be set to x->name in
1233*a71a9546SAutomerger Merge Worker 	 * xtables_register_*; the direct pointer comparison here is therefore
1234*a71a9546SAutomerger Merge Worker 	 * legitimate to detect an alias.
1235*a71a9546SAutomerger Merge Worker 	 */
1236*a71a9546SAutomerger Merge Worker 	return xtables_mt_prefer(a->real_name != NULL,
1237*a71a9546SAutomerger Merge Worker 				 a->revision, a->family,
1238*a71a9546SAutomerger Merge Worker 				 b->real_name != NULL,
1239*a71a9546SAutomerger Merge Worker 				 b->revision, b->family);
1240*a71a9546SAutomerger Merge Worker }
1241*a71a9546SAutomerger Merge Worker 
xtables_fully_register_pending_match(struct xtables_match * me,struct xtables_match * prev)1242*a71a9546SAutomerger Merge Worker static bool xtables_fully_register_pending_match(struct xtables_match *me,
1243*a71a9546SAutomerger Merge Worker 						 struct xtables_match *prev)
1244*a71a9546SAutomerger Merge Worker {
1245*a71a9546SAutomerger Merge Worker 	struct xtables_match **i;
1246*a71a9546SAutomerger Merge Worker 	const char *rn;
1247*a71a9546SAutomerger Merge Worker 
1248*a71a9546SAutomerger Merge Worker 	/* See if new match can be used. */
1249*a71a9546SAutomerger Merge Worker 	rn = (me->real_name != NULL) ? me->real_name : me->name;
1250*a71a9546SAutomerger Merge Worker 	if (!compatible_match_revision(rn, me->revision))
1251*a71a9546SAutomerger Merge Worker 		return false;
1252*a71a9546SAutomerger Merge Worker 
1253*a71a9546SAutomerger Merge Worker 	if (!prev) {
1254*a71a9546SAutomerger Merge Worker 		/* Append to list. */
1255*a71a9546SAutomerger Merge Worker 		for (i = &xtables_matches; *i; i = &(*i)->next);
1256*a71a9546SAutomerger Merge Worker 	} else {
1257*a71a9546SAutomerger Merge Worker 		/* Append it */
1258*a71a9546SAutomerger Merge Worker 		i = &prev->next;
1259*a71a9546SAutomerger Merge Worker 		prev = prev->next;
1260*a71a9546SAutomerger Merge Worker 	}
1261*a71a9546SAutomerger Merge Worker 
1262*a71a9546SAutomerger Merge Worker 	me->next = prev;
1263*a71a9546SAutomerger Merge Worker 	*i = me;
1264*a71a9546SAutomerger Merge Worker 
1265*a71a9546SAutomerger Merge Worker 	me->m = NULL;
1266*a71a9546SAutomerger Merge Worker 	me->mflags = 0;
1267*a71a9546SAutomerger Merge Worker 
1268*a71a9546SAutomerger Merge Worker 	return true;
1269*a71a9546SAutomerger Merge Worker }
1270*a71a9546SAutomerger Merge Worker 
xtables_register_matches(struct xtables_match * match,unsigned int n)1271*a71a9546SAutomerger Merge Worker void xtables_register_matches(struct xtables_match *match, unsigned int n)
1272*a71a9546SAutomerger Merge Worker {
1273*a71a9546SAutomerger Merge Worker 	int i;
1274*a71a9546SAutomerger Merge Worker 
1275*a71a9546SAutomerger Merge Worker 	for (i = 0; i < n; i++)
1276*a71a9546SAutomerger Merge Worker 		xtables_register_match(&match[i]);
1277*a71a9546SAutomerger Merge Worker }
1278*a71a9546SAutomerger Merge Worker 
xtables_register_target(struct xtables_target * me)1279*a71a9546SAutomerger Merge Worker void xtables_register_target(struct xtables_target *me)
1280*a71a9546SAutomerger Merge Worker {
1281*a71a9546SAutomerger Merge Worker 	struct xtables_target **pos;
1282*a71a9546SAutomerger Merge Worker 	bool seen_myself = false;
1283*a71a9546SAutomerger Merge Worker 
1284*a71a9546SAutomerger Merge Worker 	if (me->next) {
1285*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target \"%s\" already registered\n",
1286*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1287*a71a9546SAutomerger Merge Worker 		exit(1);
1288*a71a9546SAutomerger Merge Worker 	}
1289*a71a9546SAutomerger Merge Worker 
1290*a71a9546SAutomerger Merge Worker 	if (me->version == NULL) {
1291*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target %s<%u> is missing a version\n",
1292*a71a9546SAutomerger Merge Worker 		        xt_params->program_name, me->name, me->revision);
1293*a71a9546SAutomerger Merge Worker 		exit(1);
1294*a71a9546SAutomerger Merge Worker 	}
1295*a71a9546SAutomerger Merge Worker 
1296*a71a9546SAutomerger Merge Worker 	if (me->size != XT_ALIGN(me->size)) {
1297*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target \"%s\" has invalid size %u.\n",
1298*a71a9546SAutomerger Merge Worker 		        xt_params->program_name, me->name,
1299*a71a9546SAutomerger Merge Worker 		        (unsigned int)me->size);
1300*a71a9546SAutomerger Merge Worker 		exit(1);
1301*a71a9546SAutomerger Merge Worker 	}
1302*a71a9546SAutomerger Merge Worker 
1303*a71a9546SAutomerger Merge Worker 	if (strcmp(me->version, XTABLES_VERSION) != 0) {
1304*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target \"%s\" has version \"%s\", "
1305*a71a9546SAutomerger Merge Worker 		        "but \"%s\" is required.\n",
1306*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name,
1307*a71a9546SAutomerger Merge Worker 			me->version, XTABLES_VERSION);
1308*a71a9546SAutomerger Merge Worker 		exit(1);
1309*a71a9546SAutomerger Merge Worker 	}
1310*a71a9546SAutomerger Merge Worker 
1311*a71a9546SAutomerger Merge Worker 	if (strlen(me->name) >= XT_EXTENSION_MAXNAMELEN) {
1312*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target `%s' has invalid name\n",
1313*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1314*a71a9546SAutomerger Merge Worker 		exit(1);
1315*a71a9546SAutomerger Merge Worker 	}
1316*a71a9546SAutomerger Merge Worker 
1317*a71a9546SAutomerger Merge Worker 	if (me->real_name && strlen(me->real_name) >= XT_EXTENSION_MAXNAMELEN) {
1318*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "%s: target `%s' has invalid real name\n",
1319*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->real_name);
1320*a71a9546SAutomerger Merge Worker 		exit(1);
1321*a71a9546SAutomerger Merge Worker 	}
1322*a71a9546SAutomerger Merge Worker 
1323*a71a9546SAutomerger Merge Worker 	if (me->family >= NPROTO) {
1324*a71a9546SAutomerger Merge Worker 		fprintf(stderr,
1325*a71a9546SAutomerger Merge Worker 			"%s: BUG: target %s has invalid protocol family\n",
1326*a71a9546SAutomerger Merge Worker 			xt_params->program_name, me->name);
1327*a71a9546SAutomerger Merge Worker 		exit(1);
1328*a71a9546SAutomerger Merge Worker 	}
1329*a71a9546SAutomerger Merge Worker 
1330*a71a9546SAutomerger Merge Worker 	if (me->x6_options != NULL)
1331*a71a9546SAutomerger Merge Worker 		xtables_option_metavalidate(me->name, me->x6_options);
1332*a71a9546SAutomerger Merge Worker 	if (me->extra_opts != NULL)
1333*a71a9546SAutomerger Merge Worker 		xtables_check_options(me->name, me->extra_opts);
1334*a71a9546SAutomerger Merge Worker 
1335*a71a9546SAutomerger Merge Worker 	/* ignore not interested target */
1336*a71a9546SAutomerger Merge Worker 	if (me->family != afinfo->family && me->family != AF_UNSPEC)
1337*a71a9546SAutomerger Merge Worker 		return;
1338*a71a9546SAutomerger Merge Worker 
1339*a71a9546SAutomerger Merge Worker 	/* order into linked list of targets pending full registration */
1340*a71a9546SAutomerger Merge Worker 	for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) {
1341*a71a9546SAutomerger Merge Worker 		/* group by name */
1342*a71a9546SAutomerger Merge Worker 		if (!extension_cmp(me->name, (*pos)->name, (*pos)->family)) {
1343*a71a9546SAutomerger Merge Worker 			if (seen_myself)
1344*a71a9546SAutomerger Merge Worker 				break; /* end of own group, append to it */
1345*a71a9546SAutomerger Merge Worker 			continue;
1346*a71a9546SAutomerger Merge Worker 		}
1347*a71a9546SAutomerger Merge Worker 		/* found own group */
1348*a71a9546SAutomerger Merge Worker 		seen_myself = true;
1349*a71a9546SAutomerger Merge Worker 		if (xtables_target_prefer(me, *pos) >= 0)
1350*a71a9546SAutomerger Merge Worker 			break; /* put preferred items first in group */
1351*a71a9546SAutomerger Merge Worker 	}
1352*a71a9546SAutomerger Merge Worker 	/* if own group was not found, prepend item */
1353*a71a9546SAutomerger Merge Worker 	if (!*pos && !seen_myself)
1354*a71a9546SAutomerger Merge Worker 		pos = &xtables_pending_targets;
1355*a71a9546SAutomerger Merge Worker 
1356*a71a9546SAutomerger Merge Worker 	me->next = *pos;
1357*a71a9546SAutomerger Merge Worker 	*pos = me;
1358*a71a9546SAutomerger Merge Worker #ifdef DEBUG
1359*a71a9546SAutomerger Merge Worker 	printf("%s: inserted target %s (family %d, revision %d):\n",
1360*a71a9546SAutomerger Merge Worker 			__func__, me->name, me->family, me->revision);
1361*a71a9546SAutomerger Merge Worker 	for (pos = &xtables_pending_targets; *pos; pos = &(*pos)->next) {
1362*a71a9546SAutomerger Merge Worker 		printf("%s:\ttarget %s (family %d, revision %d)\n", __func__,
1363*a71a9546SAutomerger Merge Worker 		       (*pos)->name, (*pos)->family, (*pos)->revision);
1364*a71a9546SAutomerger Merge Worker 	}
1365*a71a9546SAutomerger Merge Worker #endif
1366*a71a9546SAutomerger Merge Worker }
1367*a71a9546SAutomerger Merge Worker 
xtables_fully_register_pending_target(struct xtables_target * me,struct xtables_target * prev)1368*a71a9546SAutomerger Merge Worker static bool xtables_fully_register_pending_target(struct xtables_target *me,
1369*a71a9546SAutomerger Merge Worker 						  struct xtables_target *prev)
1370*a71a9546SAutomerger Merge Worker {
1371*a71a9546SAutomerger Merge Worker 	struct xtables_target **i;
1372*a71a9546SAutomerger Merge Worker 	const char *rn;
1373*a71a9546SAutomerger Merge Worker 
1374*a71a9546SAutomerger Merge Worker 	if (strcmp(me->name, "standard") != 0) {
1375*a71a9546SAutomerger Merge Worker 		/* See if new target can be used. */
1376*a71a9546SAutomerger Merge Worker 		rn = (me->real_name != NULL) ? me->real_name : me->name;
1377*a71a9546SAutomerger Merge Worker 		if (!compatible_target_revision(rn, me->revision))
1378*a71a9546SAutomerger Merge Worker 			return false;
1379*a71a9546SAutomerger Merge Worker 	}
1380*a71a9546SAutomerger Merge Worker 
1381*a71a9546SAutomerger Merge Worker 	if (!prev) {
1382*a71a9546SAutomerger Merge Worker 		/* Prepend to list. */
1383*a71a9546SAutomerger Merge Worker 		i = &xtables_targets;
1384*a71a9546SAutomerger Merge Worker 		prev = xtables_targets;
1385*a71a9546SAutomerger Merge Worker 	} else {
1386*a71a9546SAutomerger Merge Worker 		/* Append it */
1387*a71a9546SAutomerger Merge Worker 		i = &prev->next;
1388*a71a9546SAutomerger Merge Worker 		prev = prev->next;
1389*a71a9546SAutomerger Merge Worker 	}
1390*a71a9546SAutomerger Merge Worker 
1391*a71a9546SAutomerger Merge Worker 	me->next = prev;
1392*a71a9546SAutomerger Merge Worker 	*i = me;
1393*a71a9546SAutomerger Merge Worker 
1394*a71a9546SAutomerger Merge Worker 	me->t = NULL;
1395*a71a9546SAutomerger Merge Worker 	me->tflags = 0;
1396*a71a9546SAutomerger Merge Worker 
1397*a71a9546SAutomerger Merge Worker 	return true;
1398*a71a9546SAutomerger Merge Worker }
1399*a71a9546SAutomerger Merge Worker 
xtables_register_targets(struct xtables_target * target,unsigned int n)1400*a71a9546SAutomerger Merge Worker void xtables_register_targets(struct xtables_target *target, unsigned int n)
1401*a71a9546SAutomerger Merge Worker {
1402*a71a9546SAutomerger Merge Worker 	int i;
1403*a71a9546SAutomerger Merge Worker 
1404*a71a9546SAutomerger Merge Worker 	for (i = 0; i < n; i++)
1405*a71a9546SAutomerger Merge Worker 		xtables_register_target(&target[i]);
1406*a71a9546SAutomerger Merge Worker }
1407*a71a9546SAutomerger Merge Worker 
1408*a71a9546SAutomerger Merge Worker /* receives a list of xtables_rule_match, release them */
xtables_rule_matches_free(struct xtables_rule_match ** matches)1409*a71a9546SAutomerger Merge Worker void xtables_rule_matches_free(struct xtables_rule_match **matches)
1410*a71a9546SAutomerger Merge Worker {
1411*a71a9546SAutomerger Merge Worker 	struct xtables_rule_match *matchp, *tmp;
1412*a71a9546SAutomerger Merge Worker 
1413*a71a9546SAutomerger Merge Worker 	for (matchp = *matches; matchp;) {
1414*a71a9546SAutomerger Merge Worker 		tmp = matchp->next;
1415*a71a9546SAutomerger Merge Worker 		if (matchp->match->m) {
1416*a71a9546SAutomerger Merge Worker 			free(matchp->match->m);
1417*a71a9546SAutomerger Merge Worker 			matchp->match->m = NULL;
1418*a71a9546SAutomerger Merge Worker 		}
1419*a71a9546SAutomerger Merge Worker 		if (matchp->match == matchp->match->next) {
1420*a71a9546SAutomerger Merge Worker 			free(matchp->match);
1421*a71a9546SAutomerger Merge Worker 			matchp->match = NULL;
1422*a71a9546SAutomerger Merge Worker 		}
1423*a71a9546SAutomerger Merge Worker 		free(matchp);
1424*a71a9546SAutomerger Merge Worker 		matchp = tmp;
1425*a71a9546SAutomerger Merge Worker 	}
1426*a71a9546SAutomerger Merge Worker 
1427*a71a9546SAutomerger Merge Worker 	*matches = NULL;
1428*a71a9546SAutomerger Merge Worker }
1429*a71a9546SAutomerger Merge Worker 
1430*a71a9546SAutomerger Merge Worker /**
1431*a71a9546SAutomerger Merge Worker  * xtables_param_act - act on condition
1432*a71a9546SAutomerger Merge Worker  * @status:	a constant from enum xtables_exittype
1433*a71a9546SAutomerger Merge Worker  *
1434*a71a9546SAutomerger Merge Worker  * %XTF_ONLY_ONCE: print error message that option may only be used once.
1435*a71a9546SAutomerger Merge Worker  * @p1:		module name (e.g. "mark")
1436*a71a9546SAutomerger Merge Worker  * @p2(...):	option in conflict (e.g. "--mark")
1437*a71a9546SAutomerger Merge Worker  * @p3(...):	condition to match on (see extensions/ for examples)
1438*a71a9546SAutomerger Merge Worker  *
1439*a71a9546SAutomerger Merge Worker  * %XTF_NO_INVERT: option does not support inversion
1440*a71a9546SAutomerger Merge Worker  * @p1:		module name
1441*a71a9546SAutomerger Merge Worker  * @p2:		option in conflict
1442*a71a9546SAutomerger Merge Worker  * @p3:		condition to match on
1443*a71a9546SAutomerger Merge Worker  *
1444*a71a9546SAutomerger Merge Worker  * %XTF_BAD_VALUE: bad value for option
1445*a71a9546SAutomerger Merge Worker  * @p1:		module name
1446*a71a9546SAutomerger Merge Worker  * @p2:		option with which the problem occurred (e.g. "--mark")
1447*a71a9546SAutomerger Merge Worker  * @p3:		string the user passed in (e.g. "99999999999999")
1448*a71a9546SAutomerger Merge Worker  *
1449*a71a9546SAutomerger Merge Worker  * %XTF_ONE_ACTION: two mutually exclusive actions have been specified
1450*a71a9546SAutomerger Merge Worker  * @p1:		module name
1451*a71a9546SAutomerger Merge Worker  *
1452*a71a9546SAutomerger Merge Worker  * Displays an error message and exits the program.
1453*a71a9546SAutomerger Merge Worker  */
xtables_param_act(unsigned int status,const char * p1,...)1454*a71a9546SAutomerger Merge Worker void xtables_param_act(unsigned int status, const char *p1, ...)
1455*a71a9546SAutomerger Merge Worker {
1456*a71a9546SAutomerger Merge Worker 	const char *p2, *p3;
1457*a71a9546SAutomerger Merge Worker 	va_list args;
1458*a71a9546SAutomerger Merge Worker 	bool b;
1459*a71a9546SAutomerger Merge Worker 
1460*a71a9546SAutomerger Merge Worker 	va_start(args, p1);
1461*a71a9546SAutomerger Merge Worker 
1462*a71a9546SAutomerger Merge Worker 	switch (status) {
1463*a71a9546SAutomerger Merge Worker 	case XTF_ONLY_ONCE:
1464*a71a9546SAutomerger Merge Worker 		p2 = va_arg(args, const char *);
1465*a71a9546SAutomerger Merge Worker 		b  = va_arg(args, unsigned int);
1466*a71a9546SAutomerger Merge Worker 		if (!b) {
1467*a71a9546SAutomerger Merge Worker 			va_end(args);
1468*a71a9546SAutomerger Merge Worker 			return;
1469*a71a9546SAutomerger Merge Worker 		}
1470*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
1471*a71a9546SAutomerger Merge Worker 		           "%s: \"%s\" option may only be specified once",
1472*a71a9546SAutomerger Merge Worker 		           p1, p2);
1473*a71a9546SAutomerger Merge Worker 		break;
1474*a71a9546SAutomerger Merge Worker 	case XTF_NO_INVERT:
1475*a71a9546SAutomerger Merge Worker 		p2 = va_arg(args, const char *);
1476*a71a9546SAutomerger Merge Worker 		b  = va_arg(args, unsigned int);
1477*a71a9546SAutomerger Merge Worker 		if (!b) {
1478*a71a9546SAutomerger Merge Worker 			va_end(args);
1479*a71a9546SAutomerger Merge Worker 			return;
1480*a71a9546SAutomerger Merge Worker 		}
1481*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
1482*a71a9546SAutomerger Merge Worker 		           "%s: \"%s\" option cannot be inverted", p1, p2);
1483*a71a9546SAutomerger Merge Worker 		break;
1484*a71a9546SAutomerger Merge Worker 	case XTF_BAD_VALUE:
1485*a71a9546SAutomerger Merge Worker 		p2 = va_arg(args, const char *);
1486*a71a9546SAutomerger Merge Worker 		p3 = va_arg(args, const char *);
1487*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
1488*a71a9546SAutomerger Merge Worker 		           "%s: Bad value for \"%s\" option: \"%s\"",
1489*a71a9546SAutomerger Merge Worker 		           p1, p2, p3);
1490*a71a9546SAutomerger Merge Worker 		break;
1491*a71a9546SAutomerger Merge Worker 	case XTF_ONE_ACTION:
1492*a71a9546SAutomerger Merge Worker 		b = va_arg(args, unsigned int);
1493*a71a9546SAutomerger Merge Worker 		if (!b) {
1494*a71a9546SAutomerger Merge Worker 			va_end(args);
1495*a71a9546SAutomerger Merge Worker 			return;
1496*a71a9546SAutomerger Merge Worker 		}
1497*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
1498*a71a9546SAutomerger Merge Worker 		           "%s: At most one action is possible", p1);
1499*a71a9546SAutomerger Merge Worker 		break;
1500*a71a9546SAutomerger Merge Worker 	default:
1501*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(status, p1, args);
1502*a71a9546SAutomerger Merge Worker 		break;
1503*a71a9546SAutomerger Merge Worker 	}
1504*a71a9546SAutomerger Merge Worker 
1505*a71a9546SAutomerger Merge Worker 	va_end(args);
1506*a71a9546SAutomerger Merge Worker }
1507*a71a9546SAutomerger Merge Worker 
xtables_ipaddr_to_numeric(const struct in_addr * addrp)1508*a71a9546SAutomerger Merge Worker const char *xtables_ipaddr_to_numeric(const struct in_addr *addrp)
1509*a71a9546SAutomerger Merge Worker {
1510*a71a9546SAutomerger Merge Worker 	static char buf[16];
1511*a71a9546SAutomerger Merge Worker 	const unsigned char *bytep = (const void *)&addrp->s_addr;
1512*a71a9546SAutomerger Merge Worker 
1513*a71a9546SAutomerger Merge Worker 	sprintf(buf, "%u.%u.%u.%u", bytep[0], bytep[1], bytep[2], bytep[3]);
1514*a71a9546SAutomerger Merge Worker 	return buf;
1515*a71a9546SAutomerger Merge Worker }
1516*a71a9546SAutomerger Merge Worker 
ipaddr_to_host(const struct in_addr * addr)1517*a71a9546SAutomerger Merge Worker static const char *ipaddr_to_host(const struct in_addr *addr)
1518*a71a9546SAutomerger Merge Worker {
1519*a71a9546SAutomerger Merge Worker 	static char hostname[NI_MAXHOST];
1520*a71a9546SAutomerger Merge Worker 	struct sockaddr_in saddr = {
1521*a71a9546SAutomerger Merge Worker 		.sin_family = AF_INET,
1522*a71a9546SAutomerger Merge Worker 		.sin_addr = *addr,
1523*a71a9546SAutomerger Merge Worker 	};
1524*a71a9546SAutomerger Merge Worker 	int err;
1525*a71a9546SAutomerger Merge Worker 
1526*a71a9546SAutomerger Merge Worker 
1527*a71a9546SAutomerger Merge Worker 	err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in),
1528*a71a9546SAutomerger Merge Worker 		       hostname, sizeof(hostname) - 1, NULL, 0, 0);
1529*a71a9546SAutomerger Merge Worker 	if (err != 0)
1530*a71a9546SAutomerger Merge Worker 		return NULL;
1531*a71a9546SAutomerger Merge Worker 
1532*a71a9546SAutomerger Merge Worker 	return hostname;
1533*a71a9546SAutomerger Merge Worker }
1534*a71a9546SAutomerger Merge Worker 
ipaddr_to_network(const struct in_addr * addr)1535*a71a9546SAutomerger Merge Worker static const char *ipaddr_to_network(const struct in_addr *addr)
1536*a71a9546SAutomerger Merge Worker {
1537*a71a9546SAutomerger Merge Worker 	struct netent *net;
1538*a71a9546SAutomerger Merge Worker 
1539*a71a9546SAutomerger Merge Worker 	if ((net = getnetbyaddr(ntohl(addr->s_addr), AF_INET)) != NULL)
1540*a71a9546SAutomerger Merge Worker 		return net->n_name;
1541*a71a9546SAutomerger Merge Worker 
1542*a71a9546SAutomerger Merge Worker 	return NULL;
1543*a71a9546SAutomerger Merge Worker }
1544*a71a9546SAutomerger Merge Worker 
xtables_ipaddr_to_anyname(const struct in_addr * addr)1545*a71a9546SAutomerger Merge Worker const char *xtables_ipaddr_to_anyname(const struct in_addr *addr)
1546*a71a9546SAutomerger Merge Worker {
1547*a71a9546SAutomerger Merge Worker 	const char *name;
1548*a71a9546SAutomerger Merge Worker 
1549*a71a9546SAutomerger Merge Worker 	if ((name = ipaddr_to_host(addr)) != NULL ||
1550*a71a9546SAutomerger Merge Worker 	    (name = ipaddr_to_network(addr)) != NULL)
1551*a71a9546SAutomerger Merge Worker 		return name;
1552*a71a9546SAutomerger Merge Worker 
1553*a71a9546SAutomerger Merge Worker 	return xtables_ipaddr_to_numeric(addr);
1554*a71a9546SAutomerger Merge Worker }
1555*a71a9546SAutomerger Merge Worker 
xtables_ipmask_to_cidr(const struct in_addr * mask)1556*a71a9546SAutomerger Merge Worker int xtables_ipmask_to_cidr(const struct in_addr *mask)
1557*a71a9546SAutomerger Merge Worker {
1558*a71a9546SAutomerger Merge Worker 	uint32_t maskaddr, bits;
1559*a71a9546SAutomerger Merge Worker 	int i;
1560*a71a9546SAutomerger Merge Worker 
1561*a71a9546SAutomerger Merge Worker 	maskaddr = ntohl(mask->s_addr);
1562*a71a9546SAutomerger Merge Worker 
1563*a71a9546SAutomerger Merge Worker 	for (i = 32, bits = (uint32_t)-1; i >= 0; i--, bits <<= 1) {
1564*a71a9546SAutomerger Merge Worker 		if (bits == maskaddr)
1565*a71a9546SAutomerger Merge Worker 			return i;
1566*a71a9546SAutomerger Merge Worker 	}
1567*a71a9546SAutomerger Merge Worker 
1568*a71a9546SAutomerger Merge Worker 	/* this mask cannot be converted to CIDR notation */
1569*a71a9546SAutomerger Merge Worker 	return -1;
1570*a71a9546SAutomerger Merge Worker }
1571*a71a9546SAutomerger Merge Worker 
xtables_ipmask_to_numeric(const struct in_addr * mask)1572*a71a9546SAutomerger Merge Worker const char *xtables_ipmask_to_numeric(const struct in_addr *mask)
1573*a71a9546SAutomerger Merge Worker {
1574*a71a9546SAutomerger Merge Worker 	static char buf[20];
1575*a71a9546SAutomerger Merge Worker 	uint32_t cidr;
1576*a71a9546SAutomerger Merge Worker 
1577*a71a9546SAutomerger Merge Worker 	cidr = xtables_ipmask_to_cidr(mask);
1578*a71a9546SAutomerger Merge Worker 	if (cidr == (unsigned int)-1) {
1579*a71a9546SAutomerger Merge Worker 		/* mask was not a decent combination of 1's and 0's */
1580*a71a9546SAutomerger Merge Worker 		sprintf(buf, "/%s", xtables_ipaddr_to_numeric(mask));
1581*a71a9546SAutomerger Merge Worker 		return buf;
1582*a71a9546SAutomerger Merge Worker 	} else if (cidr == 32) {
1583*a71a9546SAutomerger Merge Worker 		/* we don't want to see "/32" */
1584*a71a9546SAutomerger Merge Worker 		return "";
1585*a71a9546SAutomerger Merge Worker 	}
1586*a71a9546SAutomerger Merge Worker 
1587*a71a9546SAutomerger Merge Worker 	sprintf(buf, "/%d", cidr);
1588*a71a9546SAutomerger Merge Worker 	return buf;
1589*a71a9546SAutomerger Merge Worker }
1590*a71a9546SAutomerger Merge Worker 
__numeric_to_ipaddr(const char * dotted,bool is_mask)1591*a71a9546SAutomerger Merge Worker static struct in_addr *__numeric_to_ipaddr(const char *dotted, bool is_mask)
1592*a71a9546SAutomerger Merge Worker {
1593*a71a9546SAutomerger Merge Worker 	static struct in_addr addr;
1594*a71a9546SAutomerger Merge Worker 	unsigned char *addrp;
1595*a71a9546SAutomerger Merge Worker 	unsigned int onebyte;
1596*a71a9546SAutomerger Merge Worker 	char buf[20], *p, *q;
1597*a71a9546SAutomerger Merge Worker 	int i;
1598*a71a9546SAutomerger Merge Worker 
1599*a71a9546SAutomerger Merge Worker 	/* copy dotted string, because we need to modify it */
1600*a71a9546SAutomerger Merge Worker 	strncpy(buf, dotted, sizeof(buf) - 1);
1601*a71a9546SAutomerger Merge Worker 	buf[sizeof(buf) - 1] = '\0';
1602*a71a9546SAutomerger Merge Worker 	addrp = (void *)&addr.s_addr;
1603*a71a9546SAutomerger Merge Worker 
1604*a71a9546SAutomerger Merge Worker 	p = buf;
1605*a71a9546SAutomerger Merge Worker 	for (i = 0; i < 3; ++i) {
1606*a71a9546SAutomerger Merge Worker 		if ((q = strchr(p, '.')) == NULL) {
1607*a71a9546SAutomerger Merge Worker 			if (is_mask)
1608*a71a9546SAutomerger Merge Worker 				return NULL;
1609*a71a9546SAutomerger Merge Worker 
1610*a71a9546SAutomerger Merge Worker 			/* autocomplete, this is a network address */
1611*a71a9546SAutomerger Merge Worker 			if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1612*a71a9546SAutomerger Merge Worker 				return NULL;
1613*a71a9546SAutomerger Merge Worker 
1614*a71a9546SAutomerger Merge Worker 			addrp[i] = onebyte;
1615*a71a9546SAutomerger Merge Worker 			while (i < 3)
1616*a71a9546SAutomerger Merge Worker 				addrp[++i] = 0;
1617*a71a9546SAutomerger Merge Worker 
1618*a71a9546SAutomerger Merge Worker 			return &addr;
1619*a71a9546SAutomerger Merge Worker 		}
1620*a71a9546SAutomerger Merge Worker 
1621*a71a9546SAutomerger Merge Worker 		*q = '\0';
1622*a71a9546SAutomerger Merge Worker 		if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1623*a71a9546SAutomerger Merge Worker 			return NULL;
1624*a71a9546SAutomerger Merge Worker 
1625*a71a9546SAutomerger Merge Worker 		addrp[i] = onebyte;
1626*a71a9546SAutomerger Merge Worker 		p = q + 1;
1627*a71a9546SAutomerger Merge Worker 	}
1628*a71a9546SAutomerger Merge Worker 
1629*a71a9546SAutomerger Merge Worker 	/* we have checked 3 bytes, now we check the last one */
1630*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(p, NULL, &onebyte, 0, UINT8_MAX))
1631*a71a9546SAutomerger Merge Worker 		return NULL;
1632*a71a9546SAutomerger Merge Worker 
1633*a71a9546SAutomerger Merge Worker 	addrp[3] = onebyte;
1634*a71a9546SAutomerger Merge Worker 	return &addr;
1635*a71a9546SAutomerger Merge Worker }
1636*a71a9546SAutomerger Merge Worker 
xtables_numeric_to_ipaddr(const char * dotted)1637*a71a9546SAutomerger Merge Worker struct in_addr *xtables_numeric_to_ipaddr(const char *dotted)
1638*a71a9546SAutomerger Merge Worker {
1639*a71a9546SAutomerger Merge Worker 	return __numeric_to_ipaddr(dotted, false);
1640*a71a9546SAutomerger Merge Worker }
1641*a71a9546SAutomerger Merge Worker 
xtables_numeric_to_ipmask(const char * dotted)1642*a71a9546SAutomerger Merge Worker struct in_addr *xtables_numeric_to_ipmask(const char *dotted)
1643*a71a9546SAutomerger Merge Worker {
1644*a71a9546SAutomerger Merge Worker 	return __numeric_to_ipaddr(dotted, true);
1645*a71a9546SAutomerger Merge Worker }
1646*a71a9546SAutomerger Merge Worker 
network_to_ipaddr(const char * name)1647*a71a9546SAutomerger Merge Worker static struct in_addr *network_to_ipaddr(const char *name)
1648*a71a9546SAutomerger Merge Worker {
1649*a71a9546SAutomerger Merge Worker 	static struct in_addr addr;
1650*a71a9546SAutomerger Merge Worker 	struct netent *net;
1651*a71a9546SAutomerger Merge Worker 
1652*a71a9546SAutomerger Merge Worker 	if ((net = getnetbyname(name)) != NULL) {
1653*a71a9546SAutomerger Merge Worker 		if (net->n_addrtype != AF_INET)
1654*a71a9546SAutomerger Merge Worker 			return NULL;
1655*a71a9546SAutomerger Merge Worker 		addr.s_addr = htonl(net->n_net);
1656*a71a9546SAutomerger Merge Worker 		return &addr;
1657*a71a9546SAutomerger Merge Worker 	}
1658*a71a9546SAutomerger Merge Worker 
1659*a71a9546SAutomerger Merge Worker 	return NULL;
1660*a71a9546SAutomerger Merge Worker }
1661*a71a9546SAutomerger Merge Worker 
host_to_ipaddr(const char * name,unsigned int * naddr)1662*a71a9546SAutomerger Merge Worker static struct in_addr *host_to_ipaddr(const char *name, unsigned int *naddr)
1663*a71a9546SAutomerger Merge Worker {
1664*a71a9546SAutomerger Merge Worker 	struct in_addr *addr;
1665*a71a9546SAutomerger Merge Worker 	struct addrinfo hints;
1666*a71a9546SAutomerger Merge Worker 	struct addrinfo *res, *p;
1667*a71a9546SAutomerger Merge Worker 	int err;
1668*a71a9546SAutomerger Merge Worker 	unsigned int i;
1669*a71a9546SAutomerger Merge Worker 
1670*a71a9546SAutomerger Merge Worker 	memset(&hints, 0, sizeof(hints));
1671*a71a9546SAutomerger Merge Worker 	hints.ai_family   = AF_INET;
1672*a71a9546SAutomerger Merge Worker 	hints.ai_socktype = SOCK_RAW;
1673*a71a9546SAutomerger Merge Worker 
1674*a71a9546SAutomerger Merge Worker 	*naddr = 0;
1675*a71a9546SAutomerger Merge Worker 	err = getaddrinfo(name, NULL, &hints, &res);
1676*a71a9546SAutomerger Merge Worker 	if (err != 0)
1677*a71a9546SAutomerger Merge Worker 		return NULL;
1678*a71a9546SAutomerger Merge Worker 	for (p = res; p != NULL; p = p->ai_next)
1679*a71a9546SAutomerger Merge Worker 		++*naddr;
1680*a71a9546SAutomerger Merge Worker 	addr = xtables_calloc(*naddr, sizeof(struct in_addr));
1681*a71a9546SAutomerger Merge Worker 	for (i = 0, p = res; p != NULL; p = p->ai_next)
1682*a71a9546SAutomerger Merge Worker 		memcpy(&addr[i++],
1683*a71a9546SAutomerger Merge Worker 		       &((const struct sockaddr_in *)p->ai_addr)->sin_addr,
1684*a71a9546SAutomerger Merge Worker 		       sizeof(struct in_addr));
1685*a71a9546SAutomerger Merge Worker 	freeaddrinfo(res);
1686*a71a9546SAutomerger Merge Worker 	return addr;
1687*a71a9546SAutomerger Merge Worker }
1688*a71a9546SAutomerger Merge Worker 
1689*a71a9546SAutomerger Merge Worker static struct in_addr *
ipparse_hostnetwork(const char * name,unsigned int * naddrs)1690*a71a9546SAutomerger Merge Worker ipparse_hostnetwork(const char *name, unsigned int *naddrs)
1691*a71a9546SAutomerger Merge Worker {
1692*a71a9546SAutomerger Merge Worker 	struct in_addr *addrptmp, *addrp;
1693*a71a9546SAutomerger Merge Worker 
1694*a71a9546SAutomerger Merge Worker 	if ((addrptmp = xtables_numeric_to_ipaddr(name)) != NULL ||
1695*a71a9546SAutomerger Merge Worker 	    (addrptmp = network_to_ipaddr(name)) != NULL) {
1696*a71a9546SAutomerger Merge Worker 		addrp = xtables_malloc(sizeof(struct in_addr));
1697*a71a9546SAutomerger Merge Worker 		memcpy(addrp, addrptmp, sizeof(*addrp));
1698*a71a9546SAutomerger Merge Worker 		*naddrs = 1;
1699*a71a9546SAutomerger Merge Worker 		return addrp;
1700*a71a9546SAutomerger Merge Worker 	}
1701*a71a9546SAutomerger Merge Worker 	if ((addrptmp = host_to_ipaddr(name, naddrs)) != NULL)
1702*a71a9546SAutomerger Merge Worker 		return addrptmp;
1703*a71a9546SAutomerger Merge Worker 
1704*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
1705*a71a9546SAutomerger Merge Worker }
1706*a71a9546SAutomerger Merge Worker 
parse_ipmask(const char * mask)1707*a71a9546SAutomerger Merge Worker static struct in_addr *parse_ipmask(const char *mask)
1708*a71a9546SAutomerger Merge Worker {
1709*a71a9546SAutomerger Merge Worker 	static struct in_addr maskaddr;
1710*a71a9546SAutomerger Merge Worker 	struct in_addr *addrp;
1711*a71a9546SAutomerger Merge Worker 	unsigned int bits;
1712*a71a9546SAutomerger Merge Worker 
1713*a71a9546SAutomerger Merge Worker 	if (mask == NULL) {
1714*a71a9546SAutomerger Merge Worker 		/* no mask at all defaults to 32 bits */
1715*a71a9546SAutomerger Merge Worker 		maskaddr.s_addr = 0xFFFFFFFF;
1716*a71a9546SAutomerger Merge Worker 		return &maskaddr;
1717*a71a9546SAutomerger Merge Worker 	}
1718*a71a9546SAutomerger Merge Worker 	if ((addrp = xtables_numeric_to_ipmask(mask)) != NULL)
1719*a71a9546SAutomerger Merge Worker 		/* dotted_to_addr already returns a network byte order addr */
1720*a71a9546SAutomerger Merge Worker 		return addrp;
1721*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(mask, NULL, &bits, 0, 32))
1722*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
1723*a71a9546SAutomerger Merge Worker 			   "invalid mask `%s' specified", mask);
1724*a71a9546SAutomerger Merge Worker 	if (bits != 0) {
1725*a71a9546SAutomerger Merge Worker 		maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
1726*a71a9546SAutomerger Merge Worker 		return &maskaddr;
1727*a71a9546SAutomerger Merge Worker 	}
1728*a71a9546SAutomerger Merge Worker 
1729*a71a9546SAutomerger Merge Worker 	maskaddr.s_addr = 0U;
1730*a71a9546SAutomerger Merge Worker 	return &maskaddr;
1731*a71a9546SAutomerger Merge Worker }
1732*a71a9546SAutomerger Merge Worker 
xtables_ipparse_multiple(const char * name,struct in_addr ** addrpp,struct in_addr ** maskpp,unsigned int * naddrs)1733*a71a9546SAutomerger Merge Worker void xtables_ipparse_multiple(const char *name, struct in_addr **addrpp,
1734*a71a9546SAutomerger Merge Worker                               struct in_addr **maskpp, unsigned int *naddrs)
1735*a71a9546SAutomerger Merge Worker {
1736*a71a9546SAutomerger Merge Worker 	struct in_addr *addrp;
1737*a71a9546SAutomerger Merge Worker 	char buf[256], *p, *next;
1738*a71a9546SAutomerger Merge Worker 	unsigned int len, i, j, n, count = 1;
1739*a71a9546SAutomerger Merge Worker 	const char *loop = name;
1740*a71a9546SAutomerger Merge Worker 
1741*a71a9546SAutomerger Merge Worker 	while ((loop = strchr(loop, ',')) != NULL) {
1742*a71a9546SAutomerger Merge Worker 		++count;
1743*a71a9546SAutomerger Merge Worker 		++loop; /* skip ',' */
1744*a71a9546SAutomerger Merge Worker 	}
1745*a71a9546SAutomerger Merge Worker 
1746*a71a9546SAutomerger Merge Worker 	*addrpp = xtables_malloc(sizeof(struct in_addr) * count);
1747*a71a9546SAutomerger Merge Worker 	*maskpp = xtables_malloc(sizeof(struct in_addr) * count);
1748*a71a9546SAutomerger Merge Worker 
1749*a71a9546SAutomerger Merge Worker 	loop = name;
1750*a71a9546SAutomerger Merge Worker 
1751*a71a9546SAutomerger Merge Worker 	for (i = 0; i < count; ++i) {
1752*a71a9546SAutomerger Merge Worker 		while (isspace(*loop))
1753*a71a9546SAutomerger Merge Worker 			++loop;
1754*a71a9546SAutomerger Merge Worker 		next = strchr(loop, ',');
1755*a71a9546SAutomerger Merge Worker 		if (next != NULL)
1756*a71a9546SAutomerger Merge Worker 			len = next - loop;
1757*a71a9546SAutomerger Merge Worker 		else
1758*a71a9546SAutomerger Merge Worker 			len = strlen(loop);
1759*a71a9546SAutomerger Merge Worker 		if (len > sizeof(buf) - 1)
1760*a71a9546SAutomerger Merge Worker 			xt_params->exit_err(PARAMETER_PROBLEM,
1761*a71a9546SAutomerger Merge Worker 				"Hostname too long");
1762*a71a9546SAutomerger Merge Worker 
1763*a71a9546SAutomerger Merge Worker 		strncpy(buf, loop, len);
1764*a71a9546SAutomerger Merge Worker 		buf[len] = '\0';
1765*a71a9546SAutomerger Merge Worker 		if ((p = strrchr(buf, '/')) != NULL) {
1766*a71a9546SAutomerger Merge Worker 			*p = '\0';
1767*a71a9546SAutomerger Merge Worker 			addrp = parse_ipmask(p + 1);
1768*a71a9546SAutomerger Merge Worker 		} else {
1769*a71a9546SAutomerger Merge Worker 			addrp = parse_ipmask(NULL);
1770*a71a9546SAutomerger Merge Worker 		}
1771*a71a9546SAutomerger Merge Worker 		memcpy(*maskpp + i, addrp, sizeof(*addrp));
1772*a71a9546SAutomerger Merge Worker 
1773*a71a9546SAutomerger Merge Worker 		/* if a null mask is given, the name is ignored, like in "any/0" */
1774*a71a9546SAutomerger Merge Worker 		if ((*maskpp + i)->s_addr == 0)
1775*a71a9546SAutomerger Merge Worker 			/*
1776*a71a9546SAutomerger Merge Worker 			 * A bit pointless to process multiple addresses
1777*a71a9546SAutomerger Merge Worker 			 * in this case...
1778*a71a9546SAutomerger Merge Worker 			 */
1779*a71a9546SAutomerger Merge Worker 			strcpy(buf, "0.0.0.0");
1780*a71a9546SAutomerger Merge Worker 
1781*a71a9546SAutomerger Merge Worker 		addrp = ipparse_hostnetwork(buf, &n);
1782*a71a9546SAutomerger Merge Worker 		if (n > 1) {
1783*a71a9546SAutomerger Merge Worker 			count += n - 1;
1784*a71a9546SAutomerger Merge Worker 			*addrpp = xtables_realloc(*addrpp,
1785*a71a9546SAutomerger Merge Worker 			          sizeof(struct in_addr) * count);
1786*a71a9546SAutomerger Merge Worker 			*maskpp = xtables_realloc(*maskpp,
1787*a71a9546SAutomerger Merge Worker 			          sizeof(struct in_addr) * count);
1788*a71a9546SAutomerger Merge Worker 			for (j = 0; j < n; ++j)
1789*a71a9546SAutomerger Merge Worker 				/* for each new addr */
1790*a71a9546SAutomerger Merge Worker 				memcpy(*addrpp + i + j, addrp + j,
1791*a71a9546SAutomerger Merge Worker 				       sizeof(*addrp));
1792*a71a9546SAutomerger Merge Worker 			for (j = 1; j < n; ++j)
1793*a71a9546SAutomerger Merge Worker 				/* for each new mask */
1794*a71a9546SAutomerger Merge Worker 				memcpy(*maskpp + i + j, *maskpp + i,
1795*a71a9546SAutomerger Merge Worker 				       sizeof(*addrp));
1796*a71a9546SAutomerger Merge Worker 			i += n - 1;
1797*a71a9546SAutomerger Merge Worker 		} else {
1798*a71a9546SAutomerger Merge Worker 			memcpy(*addrpp + i, addrp, sizeof(*addrp));
1799*a71a9546SAutomerger Merge Worker 		}
1800*a71a9546SAutomerger Merge Worker 		/* free what ipparse_hostnetwork had allocated: */
1801*a71a9546SAutomerger Merge Worker 		free(addrp);
1802*a71a9546SAutomerger Merge Worker 		if (next == NULL)
1803*a71a9546SAutomerger Merge Worker 			break;
1804*a71a9546SAutomerger Merge Worker 		loop = next + 1;
1805*a71a9546SAutomerger Merge Worker 	}
1806*a71a9546SAutomerger Merge Worker 	*naddrs = count;
1807*a71a9546SAutomerger Merge Worker 	for (i = 0; i < count; ++i)
1808*a71a9546SAutomerger Merge Worker 		(*addrpp+i)->s_addr &= (*maskpp+i)->s_addr;
1809*a71a9546SAutomerger Merge Worker }
1810*a71a9546SAutomerger Merge Worker 
1811*a71a9546SAutomerger Merge Worker 
1812*a71a9546SAutomerger Merge Worker /**
1813*a71a9546SAutomerger Merge Worker  * xtables_ipparse_any - transform arbitrary name to in_addr
1814*a71a9546SAutomerger Merge Worker  *
1815*a71a9546SAutomerger Merge Worker  * Possible inputs (pseudo regex):
1816*a71a9546SAutomerger Merge Worker  * 	m{^($hostname|$networkname|$ipaddr)(/$mask)?}
1817*a71a9546SAutomerger Merge Worker  * "1.2.3.4/5", "1.2.3.4", "hostname", "networkname"
1818*a71a9546SAutomerger Merge Worker  */
xtables_ipparse_any(const char * name,struct in_addr ** addrpp,struct in_addr * maskp,unsigned int * naddrs)1819*a71a9546SAutomerger Merge Worker void xtables_ipparse_any(const char *name, struct in_addr **addrpp,
1820*a71a9546SAutomerger Merge Worker                          struct in_addr *maskp, unsigned int *naddrs)
1821*a71a9546SAutomerger Merge Worker {
1822*a71a9546SAutomerger Merge Worker 	unsigned int i, j, k, n;
1823*a71a9546SAutomerger Merge Worker 	struct in_addr *addrp;
1824*a71a9546SAutomerger Merge Worker 	char buf[256], *p;
1825*a71a9546SAutomerger Merge Worker 
1826*a71a9546SAutomerger Merge Worker 	strncpy(buf, name, sizeof(buf) - 1);
1827*a71a9546SAutomerger Merge Worker 	buf[sizeof(buf) - 1] = '\0';
1828*a71a9546SAutomerger Merge Worker 	if ((p = strrchr(buf, '/')) != NULL) {
1829*a71a9546SAutomerger Merge Worker 		*p = '\0';
1830*a71a9546SAutomerger Merge Worker 		addrp = parse_ipmask(p + 1);
1831*a71a9546SAutomerger Merge Worker 	} else {
1832*a71a9546SAutomerger Merge Worker 		addrp = parse_ipmask(NULL);
1833*a71a9546SAutomerger Merge Worker 	}
1834*a71a9546SAutomerger Merge Worker 	memcpy(maskp, addrp, sizeof(*maskp));
1835*a71a9546SAutomerger Merge Worker 
1836*a71a9546SAutomerger Merge Worker 	/* if a null mask is given, the name is ignored, like in "any/0" */
1837*a71a9546SAutomerger Merge Worker 	if (maskp->s_addr == 0U)
1838*a71a9546SAutomerger Merge Worker 		strcpy(buf, "0.0.0.0");
1839*a71a9546SAutomerger Merge Worker 
1840*a71a9546SAutomerger Merge Worker 	addrp = *addrpp = ipparse_hostnetwork(buf, naddrs);
1841*a71a9546SAutomerger Merge Worker 	n = *naddrs;
1842*a71a9546SAutomerger Merge Worker 	for (i = 0, j = 0; i < n; ++i) {
1843*a71a9546SAutomerger Merge Worker 		addrp[j++].s_addr &= maskp->s_addr;
1844*a71a9546SAutomerger Merge Worker 		for (k = 0; k < j - 1; ++k)
1845*a71a9546SAutomerger Merge Worker 			if (addrp[k].s_addr == addrp[j-1].s_addr) {
1846*a71a9546SAutomerger Merge Worker 				/*
1847*a71a9546SAutomerger Merge Worker 				 * Nuke the dup by copying an address from the
1848*a71a9546SAutomerger Merge Worker 				 * tail here, and check the current position
1849*a71a9546SAutomerger Merge Worker 				 * again (--j).
1850*a71a9546SAutomerger Merge Worker 				 */
1851*a71a9546SAutomerger Merge Worker 				memcpy(&addrp[--j], &addrp[--*naddrs],
1852*a71a9546SAutomerger Merge Worker 				       sizeof(struct in_addr));
1853*a71a9546SAutomerger Merge Worker 				break;
1854*a71a9546SAutomerger Merge Worker 			}
1855*a71a9546SAutomerger Merge Worker 	}
1856*a71a9546SAutomerger Merge Worker }
1857*a71a9546SAutomerger Merge Worker 
xtables_ip6addr_to_numeric(const struct in6_addr * addrp)1858*a71a9546SAutomerger Merge Worker const char *xtables_ip6addr_to_numeric(const struct in6_addr *addrp)
1859*a71a9546SAutomerger Merge Worker {
1860*a71a9546SAutomerger Merge Worker 	/* 0000:0000:0000:0000:0000:0000:000.000.000.000
1861*a71a9546SAutomerger Merge Worker 	 * 0000:0000:0000:0000:0000:0000:0000:0000 */
1862*a71a9546SAutomerger Merge Worker 	static char buf[50+1];
1863*a71a9546SAutomerger Merge Worker 	return inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
1864*a71a9546SAutomerger Merge Worker }
1865*a71a9546SAutomerger Merge Worker 
ip6addr_to_host(const struct in6_addr * addr)1866*a71a9546SAutomerger Merge Worker static const char *ip6addr_to_host(const struct in6_addr *addr)
1867*a71a9546SAutomerger Merge Worker {
1868*a71a9546SAutomerger Merge Worker 	static char hostname[NI_MAXHOST];
1869*a71a9546SAutomerger Merge Worker 	struct sockaddr_in6 saddr;
1870*a71a9546SAutomerger Merge Worker 	int err;
1871*a71a9546SAutomerger Merge Worker 
1872*a71a9546SAutomerger Merge Worker 	memset(&saddr, 0, sizeof(struct sockaddr_in6));
1873*a71a9546SAutomerger Merge Worker 	memcpy(&saddr.sin6_addr, addr, sizeof(*addr));
1874*a71a9546SAutomerger Merge Worker 	saddr.sin6_family = AF_INET6;
1875*a71a9546SAutomerger Merge Worker 
1876*a71a9546SAutomerger Merge Worker 	err = getnameinfo((const void *)&saddr, sizeof(struct sockaddr_in6),
1877*a71a9546SAutomerger Merge Worker 			hostname, sizeof(hostname) - 1, NULL, 0, 0);
1878*a71a9546SAutomerger Merge Worker 	if (err != 0)
1879*a71a9546SAutomerger Merge Worker 		return NULL;
1880*a71a9546SAutomerger Merge Worker 
1881*a71a9546SAutomerger Merge Worker 	return hostname;
1882*a71a9546SAutomerger Merge Worker }
1883*a71a9546SAutomerger Merge Worker 
xtables_ip6addr_to_anyname(const struct in6_addr * addr)1884*a71a9546SAutomerger Merge Worker const char *xtables_ip6addr_to_anyname(const struct in6_addr *addr)
1885*a71a9546SAutomerger Merge Worker {
1886*a71a9546SAutomerger Merge Worker 	const char *name;
1887*a71a9546SAutomerger Merge Worker 
1888*a71a9546SAutomerger Merge Worker 	if ((name = ip6addr_to_host(addr)) != NULL)
1889*a71a9546SAutomerger Merge Worker 		return name;
1890*a71a9546SAutomerger Merge Worker 
1891*a71a9546SAutomerger Merge Worker 	return xtables_ip6addr_to_numeric(addr);
1892*a71a9546SAutomerger Merge Worker }
1893*a71a9546SAutomerger Merge Worker 
xtables_ip6mask_to_cidr(const struct in6_addr * k)1894*a71a9546SAutomerger Merge Worker int xtables_ip6mask_to_cidr(const struct in6_addr *k)
1895*a71a9546SAutomerger Merge Worker {
1896*a71a9546SAutomerger Merge Worker 	unsigned int bits = 0;
1897*a71a9546SAutomerger Merge Worker 	uint32_t a, b, c, d;
1898*a71a9546SAutomerger Merge Worker 
1899*a71a9546SAutomerger Merge Worker 	a = ntohl(k->s6_addr32[0]);
1900*a71a9546SAutomerger Merge Worker 	b = ntohl(k->s6_addr32[1]);
1901*a71a9546SAutomerger Merge Worker 	c = ntohl(k->s6_addr32[2]);
1902*a71a9546SAutomerger Merge Worker 	d = ntohl(k->s6_addr32[3]);
1903*a71a9546SAutomerger Merge Worker 	while (a & 0x80000000U) {
1904*a71a9546SAutomerger Merge Worker 		++bits;
1905*a71a9546SAutomerger Merge Worker 		a <<= 1;
1906*a71a9546SAutomerger Merge Worker 		a  |= (b >> 31) & 1;
1907*a71a9546SAutomerger Merge Worker 		b <<= 1;
1908*a71a9546SAutomerger Merge Worker 		b  |= (c >> 31) & 1;
1909*a71a9546SAutomerger Merge Worker 		c <<= 1;
1910*a71a9546SAutomerger Merge Worker 		c  |= (d >> 31) & 1;
1911*a71a9546SAutomerger Merge Worker 		d <<= 1;
1912*a71a9546SAutomerger Merge Worker 	}
1913*a71a9546SAutomerger Merge Worker 	if (a != 0 || b != 0 || c != 0 || d != 0)
1914*a71a9546SAutomerger Merge Worker 		return -1;
1915*a71a9546SAutomerger Merge Worker 	return bits;
1916*a71a9546SAutomerger Merge Worker }
1917*a71a9546SAutomerger Merge Worker 
xtables_ip6mask_to_numeric(const struct in6_addr * addrp)1918*a71a9546SAutomerger Merge Worker const char *xtables_ip6mask_to_numeric(const struct in6_addr *addrp)
1919*a71a9546SAutomerger Merge Worker {
1920*a71a9546SAutomerger Merge Worker 	static char buf[50+2];
1921*a71a9546SAutomerger Merge Worker 	int l = xtables_ip6mask_to_cidr(addrp);
1922*a71a9546SAutomerger Merge Worker 
1923*a71a9546SAutomerger Merge Worker 	if (l == -1) {
1924*a71a9546SAutomerger Merge Worker 		strcpy(buf, "/");
1925*a71a9546SAutomerger Merge Worker 		strcat(buf, xtables_ip6addr_to_numeric(addrp));
1926*a71a9546SAutomerger Merge Worker 		return buf;
1927*a71a9546SAutomerger Merge Worker 	}
1928*a71a9546SAutomerger Merge Worker 	/* we don't want to see "/128" */
1929*a71a9546SAutomerger Merge Worker 	if (l == 128)
1930*a71a9546SAutomerger Merge Worker 		return "";
1931*a71a9546SAutomerger Merge Worker 	else
1932*a71a9546SAutomerger Merge Worker 		sprintf(buf, "/%d", l);
1933*a71a9546SAutomerger Merge Worker 	return buf;
1934*a71a9546SAutomerger Merge Worker }
1935*a71a9546SAutomerger Merge Worker 
xtables_numeric_to_ip6addr(const char * num)1936*a71a9546SAutomerger Merge Worker struct in6_addr *xtables_numeric_to_ip6addr(const char *num)
1937*a71a9546SAutomerger Merge Worker {
1938*a71a9546SAutomerger Merge Worker 	static struct in6_addr ap;
1939*a71a9546SAutomerger Merge Worker 
1940*a71a9546SAutomerger Merge Worker 	if (inet_pton(AF_INET6, num, &ap) == 1)
1941*a71a9546SAutomerger Merge Worker 		return &ap;
1942*a71a9546SAutomerger Merge Worker 
1943*a71a9546SAutomerger Merge Worker 	return NULL;
1944*a71a9546SAutomerger Merge Worker }
1945*a71a9546SAutomerger Merge Worker 
1946*a71a9546SAutomerger Merge Worker static struct in6_addr *
host_to_ip6addr(const char * name,unsigned int * naddr)1947*a71a9546SAutomerger Merge Worker host_to_ip6addr(const char *name, unsigned int *naddr)
1948*a71a9546SAutomerger Merge Worker {
1949*a71a9546SAutomerger Merge Worker 	struct in6_addr *addr;
1950*a71a9546SAutomerger Merge Worker 	struct addrinfo hints;
1951*a71a9546SAutomerger Merge Worker 	struct addrinfo *res, *p;
1952*a71a9546SAutomerger Merge Worker 	int err;
1953*a71a9546SAutomerger Merge Worker 	unsigned int i;
1954*a71a9546SAutomerger Merge Worker 
1955*a71a9546SAutomerger Merge Worker 	memset(&hints, 0, sizeof(hints));
1956*a71a9546SAutomerger Merge Worker 	hints.ai_family   = AF_INET6;
1957*a71a9546SAutomerger Merge Worker 	hints.ai_socktype = SOCK_RAW;
1958*a71a9546SAutomerger Merge Worker 
1959*a71a9546SAutomerger Merge Worker 	*naddr = 0;
1960*a71a9546SAutomerger Merge Worker 	err = getaddrinfo(name, NULL, &hints, &res);
1961*a71a9546SAutomerger Merge Worker 	if (err != 0)
1962*a71a9546SAutomerger Merge Worker 		return NULL;
1963*a71a9546SAutomerger Merge Worker 	/* Find length of address chain */
1964*a71a9546SAutomerger Merge Worker 	for (p = res; p != NULL; p = p->ai_next)
1965*a71a9546SAutomerger Merge Worker 		++*naddr;
1966*a71a9546SAutomerger Merge Worker 	/* Copy each element of the address chain */
1967*a71a9546SAutomerger Merge Worker 	addr = xtables_calloc(*naddr, sizeof(struct in6_addr));
1968*a71a9546SAutomerger Merge Worker 	for (i = 0, p = res; p != NULL; p = p->ai_next)
1969*a71a9546SAutomerger Merge Worker 		memcpy(&addr[i++],
1970*a71a9546SAutomerger Merge Worker 		       &((const struct sockaddr_in6 *)p->ai_addr)->sin6_addr,
1971*a71a9546SAutomerger Merge Worker 		       sizeof(struct in6_addr));
1972*a71a9546SAutomerger Merge Worker 	freeaddrinfo(res);
1973*a71a9546SAutomerger Merge Worker 	return addr;
1974*a71a9546SAutomerger Merge Worker }
1975*a71a9546SAutomerger Merge Worker 
network_to_ip6addr(const char * name)1976*a71a9546SAutomerger Merge Worker static struct in6_addr *network_to_ip6addr(const char *name)
1977*a71a9546SAutomerger Merge Worker {
1978*a71a9546SAutomerger Merge Worker 	/*	abort();*/
1979*a71a9546SAutomerger Merge Worker 	/* TODO: not implemented yet, but the exception breaks the
1980*a71a9546SAutomerger Merge Worker 	 *       name resolvation */
1981*a71a9546SAutomerger Merge Worker 	return NULL;
1982*a71a9546SAutomerger Merge Worker }
1983*a71a9546SAutomerger Merge Worker 
1984*a71a9546SAutomerger Merge Worker static struct in6_addr *
ip6parse_hostnetwork(const char * name,unsigned int * naddrs)1985*a71a9546SAutomerger Merge Worker ip6parse_hostnetwork(const char *name, unsigned int *naddrs)
1986*a71a9546SAutomerger Merge Worker {
1987*a71a9546SAutomerger Merge Worker 	struct in6_addr *addrp, *addrptmp;
1988*a71a9546SAutomerger Merge Worker 
1989*a71a9546SAutomerger Merge Worker 	if ((addrptmp = xtables_numeric_to_ip6addr(name)) != NULL ||
1990*a71a9546SAutomerger Merge Worker 	    (addrptmp = network_to_ip6addr(name)) != NULL) {
1991*a71a9546SAutomerger Merge Worker 		addrp = xtables_malloc(sizeof(struct in6_addr));
1992*a71a9546SAutomerger Merge Worker 		memcpy(addrp, addrptmp, sizeof(*addrp));
1993*a71a9546SAutomerger Merge Worker 		*naddrs = 1;
1994*a71a9546SAutomerger Merge Worker 		return addrp;
1995*a71a9546SAutomerger Merge Worker 	}
1996*a71a9546SAutomerger Merge Worker 	if ((addrp = host_to_ip6addr(name, naddrs)) != NULL)
1997*a71a9546SAutomerger Merge Worker 		return addrp;
1998*a71a9546SAutomerger Merge Worker 
1999*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM, "host/network `%s' not found", name);
2000*a71a9546SAutomerger Merge Worker }
2001*a71a9546SAutomerger Merge Worker 
parse_ip6mask(char * mask)2002*a71a9546SAutomerger Merge Worker static struct in6_addr *parse_ip6mask(char *mask)
2003*a71a9546SAutomerger Merge Worker {
2004*a71a9546SAutomerger Merge Worker 	static struct in6_addr maskaddr;
2005*a71a9546SAutomerger Merge Worker 	struct in6_addr *addrp;
2006*a71a9546SAutomerger Merge Worker 	unsigned int bits;
2007*a71a9546SAutomerger Merge Worker 
2008*a71a9546SAutomerger Merge Worker 	if (mask == NULL) {
2009*a71a9546SAutomerger Merge Worker 		/* no mask at all defaults to 128 bits */
2010*a71a9546SAutomerger Merge Worker 		memset(&maskaddr, 0xff, sizeof maskaddr);
2011*a71a9546SAutomerger Merge Worker 		return &maskaddr;
2012*a71a9546SAutomerger Merge Worker 	}
2013*a71a9546SAutomerger Merge Worker 	if ((addrp = xtables_numeric_to_ip6addr(mask)) != NULL)
2014*a71a9546SAutomerger Merge Worker 		return addrp;
2015*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(mask, NULL, &bits, 0, 128))
2016*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
2017*a71a9546SAutomerger Merge Worker 			   "invalid mask `%s' specified", mask);
2018*a71a9546SAutomerger Merge Worker 	if (bits != 0) {
2019*a71a9546SAutomerger Merge Worker 		char *p = (void *)&maskaddr;
2020*a71a9546SAutomerger Merge Worker 		memset(p, 0xff, bits / 8);
2021*a71a9546SAutomerger Merge Worker 		memset(p + ((bits + 7) / 8), 0, (128 - bits) / 8);
2022*a71a9546SAutomerger Merge Worker 		if (bits < 128)
2023*a71a9546SAutomerger Merge Worker 			p[bits/8] = 0xff << (8 - (bits & 7));
2024*a71a9546SAutomerger Merge Worker 		return &maskaddr;
2025*a71a9546SAutomerger Merge Worker 	}
2026*a71a9546SAutomerger Merge Worker 
2027*a71a9546SAutomerger Merge Worker 	memset(&maskaddr, 0, sizeof(maskaddr));
2028*a71a9546SAutomerger Merge Worker 	return &maskaddr;
2029*a71a9546SAutomerger Merge Worker }
2030*a71a9546SAutomerger Merge Worker 
2031*a71a9546SAutomerger Merge Worker void
xtables_ip6parse_multiple(const char * name,struct in6_addr ** addrpp,struct in6_addr ** maskpp,unsigned int * naddrs)2032*a71a9546SAutomerger Merge Worker xtables_ip6parse_multiple(const char *name, struct in6_addr **addrpp,
2033*a71a9546SAutomerger Merge Worker 		      struct in6_addr **maskpp, unsigned int *naddrs)
2034*a71a9546SAutomerger Merge Worker {
2035*a71a9546SAutomerger Merge Worker 	static const struct in6_addr zero_addr;
2036*a71a9546SAutomerger Merge Worker 	struct in6_addr *addrp;
2037*a71a9546SAutomerger Merge Worker 	char buf[256], *p, *next;
2038*a71a9546SAutomerger Merge Worker 	unsigned int len, i, j, n, count = 1;
2039*a71a9546SAutomerger Merge Worker 	const char *loop = name;
2040*a71a9546SAutomerger Merge Worker 
2041*a71a9546SAutomerger Merge Worker 	while ((loop = strchr(loop, ',')) != NULL) {
2042*a71a9546SAutomerger Merge Worker 		++count;
2043*a71a9546SAutomerger Merge Worker 		++loop; /* skip ',' */
2044*a71a9546SAutomerger Merge Worker 	}
2045*a71a9546SAutomerger Merge Worker 
2046*a71a9546SAutomerger Merge Worker 	*addrpp = xtables_malloc(sizeof(struct in6_addr) * count);
2047*a71a9546SAutomerger Merge Worker 	*maskpp = xtables_malloc(sizeof(struct in6_addr) * count);
2048*a71a9546SAutomerger Merge Worker 
2049*a71a9546SAutomerger Merge Worker 	loop = name;
2050*a71a9546SAutomerger Merge Worker 
2051*a71a9546SAutomerger Merge Worker 	for (i = 0; i < count /*NB: count can grow*/; ++i) {
2052*a71a9546SAutomerger Merge Worker 		while (isspace(*loop))
2053*a71a9546SAutomerger Merge Worker 			++loop;
2054*a71a9546SAutomerger Merge Worker 		next = strchr(loop, ',');
2055*a71a9546SAutomerger Merge Worker 		if (next != NULL)
2056*a71a9546SAutomerger Merge Worker 			len = next - loop;
2057*a71a9546SAutomerger Merge Worker 		else
2058*a71a9546SAutomerger Merge Worker 			len = strlen(loop);
2059*a71a9546SAutomerger Merge Worker 		if (len > sizeof(buf) - 1)
2060*a71a9546SAutomerger Merge Worker 			xt_params->exit_err(PARAMETER_PROBLEM,
2061*a71a9546SAutomerger Merge Worker 				"Hostname too long");
2062*a71a9546SAutomerger Merge Worker 
2063*a71a9546SAutomerger Merge Worker 		strncpy(buf, loop, len);
2064*a71a9546SAutomerger Merge Worker 		buf[len] = '\0';
2065*a71a9546SAutomerger Merge Worker 		if ((p = strrchr(buf, '/')) != NULL) {
2066*a71a9546SAutomerger Merge Worker 			*p = '\0';
2067*a71a9546SAutomerger Merge Worker 			addrp = parse_ip6mask(p + 1);
2068*a71a9546SAutomerger Merge Worker 		} else {
2069*a71a9546SAutomerger Merge Worker 			addrp = parse_ip6mask(NULL);
2070*a71a9546SAutomerger Merge Worker 		}
2071*a71a9546SAutomerger Merge Worker 		memcpy(*maskpp + i, addrp, sizeof(*addrp));
2072*a71a9546SAutomerger Merge Worker 
2073*a71a9546SAutomerger Merge Worker 		/* if a null mask is given, the name is ignored, like in "any/0" */
2074*a71a9546SAutomerger Merge Worker 		if (memcmp(*maskpp + i, &zero_addr, sizeof(zero_addr)) == 0)
2075*a71a9546SAutomerger Merge Worker 			strcpy(buf, "::");
2076*a71a9546SAutomerger Merge Worker 
2077*a71a9546SAutomerger Merge Worker 		addrp = ip6parse_hostnetwork(buf, &n);
2078*a71a9546SAutomerger Merge Worker 		if (n > 1) {
2079*a71a9546SAutomerger Merge Worker 			count += n - 1;
2080*a71a9546SAutomerger Merge Worker 			*addrpp = xtables_realloc(*addrpp,
2081*a71a9546SAutomerger Merge Worker 			          sizeof(struct in6_addr) * count);
2082*a71a9546SAutomerger Merge Worker 			*maskpp = xtables_realloc(*maskpp,
2083*a71a9546SAutomerger Merge Worker 			          sizeof(struct in6_addr) * count);
2084*a71a9546SAutomerger Merge Worker 			for (j = 0; j < n; ++j)
2085*a71a9546SAutomerger Merge Worker 				/* for each new addr */
2086*a71a9546SAutomerger Merge Worker 				memcpy(*addrpp + i + j, addrp + j,
2087*a71a9546SAutomerger Merge Worker 				       sizeof(*addrp));
2088*a71a9546SAutomerger Merge Worker 			for (j = 1; j < n; ++j)
2089*a71a9546SAutomerger Merge Worker 				/* for each new mask */
2090*a71a9546SAutomerger Merge Worker 				memcpy(*maskpp + i + j, *maskpp + i,
2091*a71a9546SAutomerger Merge Worker 				       sizeof(*addrp));
2092*a71a9546SAutomerger Merge Worker 			i += n - 1;
2093*a71a9546SAutomerger Merge Worker 		} else {
2094*a71a9546SAutomerger Merge Worker 			memcpy(*addrpp + i, addrp, sizeof(*addrp));
2095*a71a9546SAutomerger Merge Worker 		}
2096*a71a9546SAutomerger Merge Worker 		/* free what ip6parse_hostnetwork had allocated: */
2097*a71a9546SAutomerger Merge Worker 		free(addrp);
2098*a71a9546SAutomerger Merge Worker 		if (next == NULL)
2099*a71a9546SAutomerger Merge Worker 			break;
2100*a71a9546SAutomerger Merge Worker 		loop = next + 1;
2101*a71a9546SAutomerger Merge Worker 	}
2102*a71a9546SAutomerger Merge Worker 	*naddrs = count;
2103*a71a9546SAutomerger Merge Worker 	for (i = 0; i < count; ++i)
2104*a71a9546SAutomerger Merge Worker 		for (j = 0; j < 4; ++j)
2105*a71a9546SAutomerger Merge Worker 			(*addrpp+i)->s6_addr32[j] &= (*maskpp+i)->s6_addr32[j];
2106*a71a9546SAutomerger Merge Worker }
2107*a71a9546SAutomerger Merge Worker 
xtables_ip6parse_any(const char * name,struct in6_addr ** addrpp,struct in6_addr * maskp,unsigned int * naddrs)2108*a71a9546SAutomerger Merge Worker void xtables_ip6parse_any(const char *name, struct in6_addr **addrpp,
2109*a71a9546SAutomerger Merge Worker                           struct in6_addr *maskp, unsigned int *naddrs)
2110*a71a9546SAutomerger Merge Worker {
2111*a71a9546SAutomerger Merge Worker 	static const struct in6_addr zero_addr;
2112*a71a9546SAutomerger Merge Worker 	struct in6_addr *addrp;
2113*a71a9546SAutomerger Merge Worker 	unsigned int i, j, k, n;
2114*a71a9546SAutomerger Merge Worker 	char buf[256], *p;
2115*a71a9546SAutomerger Merge Worker 
2116*a71a9546SAutomerger Merge Worker 	strncpy(buf, name, sizeof(buf) - 1);
2117*a71a9546SAutomerger Merge Worker 	buf[sizeof(buf)-1] = '\0';
2118*a71a9546SAutomerger Merge Worker 	if ((p = strrchr(buf, '/')) != NULL) {
2119*a71a9546SAutomerger Merge Worker 		*p = '\0';
2120*a71a9546SAutomerger Merge Worker 		addrp = parse_ip6mask(p + 1);
2121*a71a9546SAutomerger Merge Worker 	} else {
2122*a71a9546SAutomerger Merge Worker 		addrp = parse_ip6mask(NULL);
2123*a71a9546SAutomerger Merge Worker 	}
2124*a71a9546SAutomerger Merge Worker 	memcpy(maskp, addrp, sizeof(*maskp));
2125*a71a9546SAutomerger Merge Worker 
2126*a71a9546SAutomerger Merge Worker 	/* if a null mask is given, the name is ignored, like in "any/0" */
2127*a71a9546SAutomerger Merge Worker 	if (memcmp(maskp, &zero_addr, sizeof(zero_addr)) == 0)
2128*a71a9546SAutomerger Merge Worker 		strcpy(buf, "::");
2129*a71a9546SAutomerger Merge Worker 
2130*a71a9546SAutomerger Merge Worker 	addrp = *addrpp = ip6parse_hostnetwork(buf, naddrs);
2131*a71a9546SAutomerger Merge Worker 	n = *naddrs;
2132*a71a9546SAutomerger Merge Worker 	for (i = 0, j = 0; i < n; ++i) {
2133*a71a9546SAutomerger Merge Worker 		for (k = 0; k < 4; ++k)
2134*a71a9546SAutomerger Merge Worker 			addrp[j].s6_addr32[k] &= maskp->s6_addr32[k];
2135*a71a9546SAutomerger Merge Worker 		++j;
2136*a71a9546SAutomerger Merge Worker 		for (k = 0; k < j - 1; ++k)
2137*a71a9546SAutomerger Merge Worker 			if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
2138*a71a9546SAutomerger Merge Worker 				/*
2139*a71a9546SAutomerger Merge Worker 				 * Nuke the dup by copying an address from the
2140*a71a9546SAutomerger Merge Worker 				 * tail here, and check the current position
2141*a71a9546SAutomerger Merge Worker 				 * again (--j).
2142*a71a9546SAutomerger Merge Worker 				 */
2143*a71a9546SAutomerger Merge Worker 				memcpy(&addrp[--j], &addrp[--*naddrs],
2144*a71a9546SAutomerger Merge Worker 				       sizeof(struct in_addr));
2145*a71a9546SAutomerger Merge Worker 				break;
2146*a71a9546SAutomerger Merge Worker 			}
2147*a71a9546SAutomerger Merge Worker 	}
2148*a71a9546SAutomerger Merge Worker }
2149*a71a9546SAutomerger Merge Worker 
xtables_save_string(const char * value)2150*a71a9546SAutomerger Merge Worker void xtables_save_string(const char *value)
2151*a71a9546SAutomerger Merge Worker {
2152*a71a9546SAutomerger Merge Worker 	static const char no_quote_chars[] = "_-0123456789"
2153*a71a9546SAutomerger Merge Worker 		"abcdefghijklmnopqrstuvwxyz"
2154*a71a9546SAutomerger Merge Worker 		"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
2155*a71a9546SAutomerger Merge Worker 	static const char escape_chars[] = "\"\\'";
2156*a71a9546SAutomerger Merge Worker 	size_t length;
2157*a71a9546SAutomerger Merge Worker 	const char *p;
2158*a71a9546SAutomerger Merge Worker 
2159*a71a9546SAutomerger Merge Worker 	length = strspn(value, no_quote_chars);
2160*a71a9546SAutomerger Merge Worker 	if (length > 0 && value[length] == 0) {
2161*a71a9546SAutomerger Merge Worker 		/* no quoting required */
2162*a71a9546SAutomerger Merge Worker 		putchar(' ');
2163*a71a9546SAutomerger Merge Worker 		fputs(value, stdout);
2164*a71a9546SAutomerger Merge Worker 	} else {
2165*a71a9546SAutomerger Merge Worker 		/* there is at least one dangerous character in the
2166*a71a9546SAutomerger Merge Worker 		   value, which we have to quote.  Write double quotes
2167*a71a9546SAutomerger Merge Worker 		   around the value and escape special characters with
2168*a71a9546SAutomerger Merge Worker 		   a backslash */
2169*a71a9546SAutomerger Merge Worker 		printf(" \"");
2170*a71a9546SAutomerger Merge Worker 
2171*a71a9546SAutomerger Merge Worker 		for (p = strpbrk(value, escape_chars); p != NULL;
2172*a71a9546SAutomerger Merge Worker 		     p = strpbrk(value, escape_chars)) {
2173*a71a9546SAutomerger Merge Worker 			if (p > value)
2174*a71a9546SAutomerger Merge Worker 				fwrite(value, 1, p - value, stdout);
2175*a71a9546SAutomerger Merge Worker 			putchar('\\');
2176*a71a9546SAutomerger Merge Worker 			putchar(*p);
2177*a71a9546SAutomerger Merge Worker 			value = p + 1;
2178*a71a9546SAutomerger Merge Worker 		}
2179*a71a9546SAutomerger Merge Worker 
2180*a71a9546SAutomerger Merge Worker 		/* print the rest and finish the double quoted
2181*a71a9546SAutomerger Merge Worker 		   string */
2182*a71a9546SAutomerger Merge Worker 		fputs(value, stdout);
2183*a71a9546SAutomerger Merge Worker 		putchar('\"');
2184*a71a9546SAutomerger Merge Worker 	}
2185*a71a9546SAutomerger Merge Worker }
2186*a71a9546SAutomerger Merge Worker 
2187*a71a9546SAutomerger Merge Worker const struct xtables_pprot xtables_chain_protos[] = {
2188*a71a9546SAutomerger Merge Worker 	{"tcp",       IPPROTO_TCP},
2189*a71a9546SAutomerger Merge Worker 	{"sctp",      IPPROTO_SCTP},
2190*a71a9546SAutomerger Merge Worker 	{"udp",       IPPROTO_UDP},
2191*a71a9546SAutomerger Merge Worker 	{"udplite",   IPPROTO_UDPLITE},
2192*a71a9546SAutomerger Merge Worker 	{"icmp",      IPPROTO_ICMP},
2193*a71a9546SAutomerger Merge Worker 	{"ipv6-icmp", IPPROTO_ICMPV6},
2194*a71a9546SAutomerger Merge Worker 	{"icmpv6",    IPPROTO_ICMPV6},
2195*a71a9546SAutomerger Merge Worker 	{"esp",       IPPROTO_ESP},
2196*a71a9546SAutomerger Merge Worker 	{"ah",        IPPROTO_AH},
2197*a71a9546SAutomerger Merge Worker 	{"mobility-header", IPPROTO_MH},
2198*a71a9546SAutomerger Merge Worker 	{"ipv6-mh",   IPPROTO_MH},
2199*a71a9546SAutomerger Merge Worker 	{"mh",        IPPROTO_MH},
2200*a71a9546SAutomerger Merge Worker 	{"all",       0},
2201*a71a9546SAutomerger Merge Worker 	{NULL},
2202*a71a9546SAutomerger Merge Worker };
2203*a71a9546SAutomerger Merge Worker 
2204*a71a9546SAutomerger Merge Worker uint16_t
xtables_parse_protocol(const char * s)2205*a71a9546SAutomerger Merge Worker xtables_parse_protocol(const char *s)
2206*a71a9546SAutomerger Merge Worker {
2207*a71a9546SAutomerger Merge Worker 	const struct protoent *pent;
2208*a71a9546SAutomerger Merge Worker 	unsigned int proto, i;
2209*a71a9546SAutomerger Merge Worker 
2210*a71a9546SAutomerger Merge Worker 	if (xtables_strtoui(s, NULL, &proto, 0, UINT8_MAX))
2211*a71a9546SAutomerger Merge Worker 		return proto;
2212*a71a9546SAutomerger Merge Worker 
2213*a71a9546SAutomerger Merge Worker 	for (i = 0; xtables_chain_protos[i].name != NULL; ++i) {
2214*a71a9546SAutomerger Merge Worker 		if (strcmp(s, xtables_chain_protos[i].name) == 0)
2215*a71a9546SAutomerger Merge Worker 			return xtables_chain_protos[i].num;
2216*a71a9546SAutomerger Merge Worker 	}
2217*a71a9546SAutomerger Merge Worker 
2218*a71a9546SAutomerger Merge Worker 	pent = getprotobyname(s);
2219*a71a9546SAutomerger Merge Worker 	if (pent != NULL)
2220*a71a9546SAutomerger Merge Worker 		return pent->p_proto;
2221*a71a9546SAutomerger Merge Worker 
2222*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM,
2223*a71a9546SAutomerger Merge Worker 		"unknown protocol \"%s\" specified", s);
2224*a71a9546SAutomerger Merge Worker 	return -1;
2225*a71a9546SAutomerger Merge Worker }
2226*a71a9546SAutomerger Merge Worker 
xtables_print_num(uint64_t number,unsigned int format)2227*a71a9546SAutomerger Merge Worker void xtables_print_num(uint64_t number, unsigned int format)
2228*a71a9546SAutomerger Merge Worker {
2229*a71a9546SAutomerger Merge Worker 	if (!(format & FMT_KILOMEGAGIGA)) {
2230*a71a9546SAutomerger Merge Worker 		printf(FMT("%8llu ","%llu "), (unsigned long long)number);
2231*a71a9546SAutomerger Merge Worker 		return;
2232*a71a9546SAutomerger Merge Worker 	}
2233*a71a9546SAutomerger Merge Worker 	if (number <= 99999) {
2234*a71a9546SAutomerger Merge Worker 		printf(FMT("%5llu ","%llu "), (unsigned long long)number);
2235*a71a9546SAutomerger Merge Worker 		return;
2236*a71a9546SAutomerger Merge Worker 	}
2237*a71a9546SAutomerger Merge Worker 	number = (number + 500) / 1000;
2238*a71a9546SAutomerger Merge Worker 	if (number <= 9999) {
2239*a71a9546SAutomerger Merge Worker 		printf(FMT("%4lluK ","%lluK "), (unsigned long long)number);
2240*a71a9546SAutomerger Merge Worker 		return;
2241*a71a9546SAutomerger Merge Worker 	}
2242*a71a9546SAutomerger Merge Worker 	number = (number + 500) / 1000;
2243*a71a9546SAutomerger Merge Worker 	if (number <= 9999) {
2244*a71a9546SAutomerger Merge Worker 		printf(FMT("%4lluM ","%lluM "), (unsigned long long)number);
2245*a71a9546SAutomerger Merge Worker 		return;
2246*a71a9546SAutomerger Merge Worker 	}
2247*a71a9546SAutomerger Merge Worker 	number = (number + 500) / 1000;
2248*a71a9546SAutomerger Merge Worker 	if (number <= 9999) {
2249*a71a9546SAutomerger Merge Worker 		printf(FMT("%4lluG ","%lluG "), (unsigned long long)number);
2250*a71a9546SAutomerger Merge Worker 		return;
2251*a71a9546SAutomerger Merge Worker 	}
2252*a71a9546SAutomerger Merge Worker 	number = (number + 500) / 1000;
2253*a71a9546SAutomerger Merge Worker 	printf(FMT("%4lluT ","%lluT "), (unsigned long long)number);
2254*a71a9546SAutomerger Merge Worker }
2255*a71a9546SAutomerger Merge Worker 
2256*a71a9546SAutomerger Merge Worker static const unsigned char mac_type_unicast[ETH_ALEN] =   {};
2257*a71a9546SAutomerger Merge Worker static const unsigned char msk_type_unicast[ETH_ALEN] =   {1};
2258*a71a9546SAutomerger Merge Worker static const unsigned char mac_type_multicast[ETH_ALEN] = {1};
2259*a71a9546SAutomerger Merge Worker static const unsigned char msk_type_multicast[ETH_ALEN] = {1};
2260*a71a9546SAutomerger Merge Worker #define ALL_ONE_MAC {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
2261*a71a9546SAutomerger Merge Worker static const unsigned char mac_type_broadcast[ETH_ALEN] = ALL_ONE_MAC;
2262*a71a9546SAutomerger Merge Worker static const unsigned char msk_type_broadcast[ETH_ALEN] = ALL_ONE_MAC;
2263*a71a9546SAutomerger Merge Worker static const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01, 0x80, 0xc2};
2264*a71a9546SAutomerger Merge Worker static const unsigned char msk_type_bridge_group[ETH_ALEN] = ALL_ONE_MAC;
2265*a71a9546SAutomerger Merge Worker #undef ALL_ONE_MAC
2266*a71a9546SAutomerger Merge Worker 
xtables_parse_mac_and_mask(const char * from,void * to,void * mask)2267*a71a9546SAutomerger Merge Worker int xtables_parse_mac_and_mask(const char *from, void *to, void *mask)
2268*a71a9546SAutomerger Merge Worker {
2269*a71a9546SAutomerger Merge Worker 	char *p;
2270*a71a9546SAutomerger Merge Worker 	int i;
2271*a71a9546SAutomerger Merge Worker 	struct ether_addr *addr = NULL;
2272*a71a9546SAutomerger Merge Worker 
2273*a71a9546SAutomerger Merge Worker 	if (strcasecmp(from, "Unicast") == 0) {
2274*a71a9546SAutomerger Merge Worker 		memcpy(to, mac_type_unicast, ETH_ALEN);
2275*a71a9546SAutomerger Merge Worker 		memcpy(mask, msk_type_unicast, ETH_ALEN);
2276*a71a9546SAutomerger Merge Worker 		return 0;
2277*a71a9546SAutomerger Merge Worker 	}
2278*a71a9546SAutomerger Merge Worker 	if (strcasecmp(from, "Multicast") == 0) {
2279*a71a9546SAutomerger Merge Worker 		memcpy(to, mac_type_multicast, ETH_ALEN);
2280*a71a9546SAutomerger Merge Worker 		memcpy(mask, msk_type_multicast, ETH_ALEN);
2281*a71a9546SAutomerger Merge Worker 		return 0;
2282*a71a9546SAutomerger Merge Worker 	}
2283*a71a9546SAutomerger Merge Worker 	if (strcasecmp(from, "Broadcast") == 0) {
2284*a71a9546SAutomerger Merge Worker 		memcpy(to, mac_type_broadcast, ETH_ALEN);
2285*a71a9546SAutomerger Merge Worker 		memcpy(mask, msk_type_broadcast, ETH_ALEN);
2286*a71a9546SAutomerger Merge Worker 		return 0;
2287*a71a9546SAutomerger Merge Worker 	}
2288*a71a9546SAutomerger Merge Worker 	if (strcasecmp(from, "BGA") == 0) {
2289*a71a9546SAutomerger Merge Worker 		memcpy(to, mac_type_bridge_group, ETH_ALEN);
2290*a71a9546SAutomerger Merge Worker 		memcpy(mask, msk_type_bridge_group, ETH_ALEN);
2291*a71a9546SAutomerger Merge Worker 		return 0;
2292*a71a9546SAutomerger Merge Worker 	}
2293*a71a9546SAutomerger Merge Worker 	if ( (p = strrchr(from, '/')) != NULL) {
2294*a71a9546SAutomerger Merge Worker 		*p = '\0';
2295*a71a9546SAutomerger Merge Worker 		if (!(addr = ether_aton(p + 1)))
2296*a71a9546SAutomerger Merge Worker 			return -1;
2297*a71a9546SAutomerger Merge Worker 		memcpy(mask, addr, ETH_ALEN);
2298*a71a9546SAutomerger Merge Worker 	} else
2299*a71a9546SAutomerger Merge Worker 		memset(mask, 0xff, ETH_ALEN);
2300*a71a9546SAutomerger Merge Worker 	if (!(addr = ether_aton(from)))
2301*a71a9546SAutomerger Merge Worker 		return -1;
2302*a71a9546SAutomerger Merge Worker 	memcpy(to, addr, ETH_ALEN);
2303*a71a9546SAutomerger Merge Worker 	for (i = 0; i < ETH_ALEN; i++)
2304*a71a9546SAutomerger Merge Worker 		((char *)to)[i] &= ((char *)mask)[i];
2305*a71a9546SAutomerger Merge Worker 	return 0;
2306*a71a9546SAutomerger Merge Worker }
2307*a71a9546SAutomerger Merge Worker 
xtables_print_well_known_mac_and_mask(const void * mac,const void * mask)2308*a71a9546SAutomerger Merge Worker int xtables_print_well_known_mac_and_mask(const void *mac, const void *mask)
2309*a71a9546SAutomerger Merge Worker {
2310*a71a9546SAutomerger Merge Worker 	if (!memcmp(mac, mac_type_unicast, ETH_ALEN) &&
2311*a71a9546SAutomerger Merge Worker 	    !memcmp(mask, msk_type_unicast, ETH_ALEN))
2312*a71a9546SAutomerger Merge Worker 		printf("Unicast");
2313*a71a9546SAutomerger Merge Worker 	else if (!memcmp(mac, mac_type_multicast, ETH_ALEN) &&
2314*a71a9546SAutomerger Merge Worker 	         !memcmp(mask, msk_type_multicast, ETH_ALEN))
2315*a71a9546SAutomerger Merge Worker 		printf("Multicast");
2316*a71a9546SAutomerger Merge Worker 	else if (!memcmp(mac, mac_type_broadcast, ETH_ALEN) &&
2317*a71a9546SAutomerger Merge Worker 	         !memcmp(mask, msk_type_broadcast, ETH_ALEN))
2318*a71a9546SAutomerger Merge Worker 		printf("Broadcast");
2319*a71a9546SAutomerger Merge Worker 	else if (!memcmp(mac, mac_type_bridge_group, ETH_ALEN) &&
2320*a71a9546SAutomerger Merge Worker 	         !memcmp(mask, msk_type_bridge_group, ETH_ALEN))
2321*a71a9546SAutomerger Merge Worker 		printf("BGA");
2322*a71a9546SAutomerger Merge Worker 	else
2323*a71a9546SAutomerger Merge Worker 		return -1;
2324*a71a9546SAutomerger Merge Worker 	return 0;
2325*a71a9546SAutomerger Merge Worker }
2326*a71a9546SAutomerger Merge Worker 
xtables_print_mac(const unsigned char * macaddress)2327*a71a9546SAutomerger Merge Worker void xtables_print_mac(const unsigned char *macaddress)
2328*a71a9546SAutomerger Merge Worker {
2329*a71a9546SAutomerger Merge Worker 	unsigned int i;
2330*a71a9546SAutomerger Merge Worker 
2331*a71a9546SAutomerger Merge Worker 	printf("%02x", macaddress[0]);
2332*a71a9546SAutomerger Merge Worker 	for (i = 1; i < 6; ++i)
2333*a71a9546SAutomerger Merge Worker 		printf(":%02x", macaddress[i]);
2334*a71a9546SAutomerger Merge Worker }
2335*a71a9546SAutomerger Merge Worker 
xtables_print_mac_and_mask(const unsigned char * mac,const unsigned char * mask)2336*a71a9546SAutomerger Merge Worker void xtables_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
2337*a71a9546SAutomerger Merge Worker {
2338*a71a9546SAutomerger Merge Worker 	static const char hlpmsk[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
2339*a71a9546SAutomerger Merge Worker 
2340*a71a9546SAutomerger Merge Worker 	xtables_print_mac(mac);
2341*a71a9546SAutomerger Merge Worker 
2342*a71a9546SAutomerger Merge Worker 	if (memcmp(mask, hlpmsk, 6) == 0)
2343*a71a9546SAutomerger Merge Worker 		return;
2344*a71a9546SAutomerger Merge Worker 
2345*a71a9546SAutomerger Merge Worker 	printf("/");
2346*a71a9546SAutomerger Merge Worker 	xtables_print_mac(mask);
2347*a71a9546SAutomerger Merge Worker }
2348*a71a9546SAutomerger Merge Worker 
xtables_parse_val_mask(struct xt_option_call * cb,unsigned int * val,unsigned int * mask,const struct xtables_lmap * lmap)2349*a71a9546SAutomerger Merge Worker void xtables_parse_val_mask(struct xt_option_call *cb,
2350*a71a9546SAutomerger Merge Worker 			    unsigned int *val, unsigned int *mask,
2351*a71a9546SAutomerger Merge Worker 			    const struct xtables_lmap *lmap)
2352*a71a9546SAutomerger Merge Worker {
2353*a71a9546SAutomerger Merge Worker 	char *end;
2354*a71a9546SAutomerger Merge Worker 
2355*a71a9546SAutomerger Merge Worker 	*mask = ~0U;
2356*a71a9546SAutomerger Merge Worker 
2357*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(cb->arg, &end, val, 0, UINT32_MAX)) {
2358*a71a9546SAutomerger Merge Worker 		if (lmap)
2359*a71a9546SAutomerger Merge Worker 			goto name2val;
2360*a71a9546SAutomerger Merge Worker 		else
2361*a71a9546SAutomerger Merge Worker 			goto bad_val;
2362*a71a9546SAutomerger Merge Worker 	}
2363*a71a9546SAutomerger Merge Worker 
2364*a71a9546SAutomerger Merge Worker 	if (*end == '\0')
2365*a71a9546SAutomerger Merge Worker 		return;
2366*a71a9546SAutomerger Merge Worker 
2367*a71a9546SAutomerger Merge Worker 	if (*end != '/') {
2368*a71a9546SAutomerger Merge Worker 		if (lmap)
2369*a71a9546SAutomerger Merge Worker 			goto name2val;
2370*a71a9546SAutomerger Merge Worker 		else
2371*a71a9546SAutomerger Merge Worker 			goto garbage;
2372*a71a9546SAutomerger Merge Worker 	}
2373*a71a9546SAutomerger Merge Worker 
2374*a71a9546SAutomerger Merge Worker 	if (!xtables_strtoui(end + 1, &end, mask, 0, UINT32_MAX))
2375*a71a9546SAutomerger Merge Worker 		goto bad_val;
2376*a71a9546SAutomerger Merge Worker 
2377*a71a9546SAutomerger Merge Worker 	if (*end == '\0')
2378*a71a9546SAutomerger Merge Worker 		return;
2379*a71a9546SAutomerger Merge Worker 
2380*a71a9546SAutomerger Merge Worker garbage:
2381*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM,
2382*a71a9546SAutomerger Merge Worker 			"%s: trailing garbage after value "
2383*a71a9546SAutomerger Merge Worker 			"for option \"--%s\".\n",
2384*a71a9546SAutomerger Merge Worker 			cb->ext_name, cb->entry->name);
2385*a71a9546SAutomerger Merge Worker 
2386*a71a9546SAutomerger Merge Worker bad_val:
2387*a71a9546SAutomerger Merge Worker 	xt_params->exit_err(PARAMETER_PROBLEM,
2388*a71a9546SAutomerger Merge Worker 			"%s: bad integer value for option \"--%s\", "
2389*a71a9546SAutomerger Merge Worker 			"or out of range.\n",
2390*a71a9546SAutomerger Merge Worker 			cb->ext_name, cb->entry->name);
2391*a71a9546SAutomerger Merge Worker 
2392*a71a9546SAutomerger Merge Worker name2val:
2393*a71a9546SAutomerger Merge Worker 	*val = xtables_lmap_name2id(lmap, cb->arg);
2394*a71a9546SAutomerger Merge Worker 	if ((int)*val == -1)
2395*a71a9546SAutomerger Merge Worker 		xt_params->exit_err(PARAMETER_PROBLEM,
2396*a71a9546SAutomerger Merge Worker 			"%s: could not map name %s to an integer value "
2397*a71a9546SAutomerger Merge Worker 			"for option \"--%s\".\n",
2398*a71a9546SAutomerger Merge Worker 			cb->ext_name, cb->arg, cb->entry->name);
2399*a71a9546SAutomerger Merge Worker }
2400*a71a9546SAutomerger Merge Worker 
xtables_print_val_mask(unsigned int val,unsigned int mask,const struct xtables_lmap * lmap)2401*a71a9546SAutomerger Merge Worker void xtables_print_val_mask(unsigned int val, unsigned int mask,
2402*a71a9546SAutomerger Merge Worker 			    const struct xtables_lmap *lmap)
2403*a71a9546SAutomerger Merge Worker {
2404*a71a9546SAutomerger Merge Worker 	if (mask != ~0U) {
2405*a71a9546SAutomerger Merge Worker 		printf(" 0x%x/0x%x", val, mask);
2406*a71a9546SAutomerger Merge Worker 		return;
2407*a71a9546SAutomerger Merge Worker 	}
2408*a71a9546SAutomerger Merge Worker 
2409*a71a9546SAutomerger Merge Worker 	if (lmap) {
2410*a71a9546SAutomerger Merge Worker 		const char *name = xtables_lmap_id2name(lmap, val);
2411*a71a9546SAutomerger Merge Worker 
2412*a71a9546SAutomerger Merge Worker 		if (name) {
2413*a71a9546SAutomerger Merge Worker 			printf(" %s", name);
2414*a71a9546SAutomerger Merge Worker 			return;
2415*a71a9546SAutomerger Merge Worker 		}
2416*a71a9546SAutomerger Merge Worker 	}
2417*a71a9546SAutomerger Merge Worker 
2418*a71a9546SAutomerger Merge Worker 	printf(" 0x%x", val);
2419*a71a9546SAutomerger Merge Worker }
2420*a71a9546SAutomerger Merge Worker 
2421*a71a9546SAutomerger Merge Worker int kernel_version;
2422*a71a9546SAutomerger Merge Worker 
get_kernel_version(void)2423*a71a9546SAutomerger Merge Worker void get_kernel_version(void)
2424*a71a9546SAutomerger Merge Worker {
2425*a71a9546SAutomerger Merge Worker 	static struct utsname uts;
2426*a71a9546SAutomerger Merge Worker 	int x = 0, y = 0, z = 0;
2427*a71a9546SAutomerger Merge Worker 
2428*a71a9546SAutomerger Merge Worker 	if (uname(&uts) == -1) {
2429*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "Unable to retrieve kernel version.\n");
2430*a71a9546SAutomerger Merge Worker 		xtables_free_opts(1);
2431*a71a9546SAutomerger Merge Worker 		exit(1);
2432*a71a9546SAutomerger Merge Worker 	}
2433*a71a9546SAutomerger Merge Worker 
2434*a71a9546SAutomerger Merge Worker 	sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
2435*a71a9546SAutomerger Merge Worker 	kernel_version = LINUX_VERSION(x, y, z);
2436*a71a9546SAutomerger Merge Worker }
2437*a71a9546SAutomerger Merge Worker 
2438*a71a9546SAutomerger Merge Worker #include <linux/netfilter/nf_tables.h>
2439*a71a9546SAutomerger Merge Worker 
2440*a71a9546SAutomerger Merge Worker enum xt_xlate_type {
2441*a71a9546SAutomerger Merge Worker 	XT_XLATE_RULE = 0,
2442*a71a9546SAutomerger Merge Worker 	XT_XLATE_SET,
2443*a71a9546SAutomerger Merge Worker 	__XT_XLATE_MAX
2444*a71a9546SAutomerger Merge Worker };
2445*a71a9546SAutomerger Merge Worker 
2446*a71a9546SAutomerger Merge Worker struct xt_xlate {
2447*a71a9546SAutomerger Merge Worker 	struct xt_xlate_buf {
2448*a71a9546SAutomerger Merge Worker 		char	*data;
2449*a71a9546SAutomerger Merge Worker 		int	size;
2450*a71a9546SAutomerger Merge Worker 		int	rem;
2451*a71a9546SAutomerger Merge Worker 		int	off;
2452*a71a9546SAutomerger Merge Worker 	} buf[__XT_XLATE_MAX];
2453*a71a9546SAutomerger Merge Worker 	char comment[NFT_USERDATA_MAXLEN];
2454*a71a9546SAutomerger Merge Worker 	int family;
2455*a71a9546SAutomerger Merge Worker };
2456*a71a9546SAutomerger Merge Worker 
xt_xlate_alloc(int size)2457*a71a9546SAutomerger Merge Worker struct xt_xlate *xt_xlate_alloc(int size)
2458*a71a9546SAutomerger Merge Worker {
2459*a71a9546SAutomerger Merge Worker 	struct xt_xlate *xl = xtables_malloc(sizeof(struct xt_xlate));
2460*a71a9546SAutomerger Merge Worker 	int i;
2461*a71a9546SAutomerger Merge Worker 
2462*a71a9546SAutomerger Merge Worker 	for (i = 0; i < __XT_XLATE_MAX; i++) {
2463*a71a9546SAutomerger Merge Worker 		xl->buf[i].data = xtables_malloc(size);
2464*a71a9546SAutomerger Merge Worker 		xl->buf[i].data[0] = '\0';
2465*a71a9546SAutomerger Merge Worker 		xl->buf[i].size = size;
2466*a71a9546SAutomerger Merge Worker 		xl->buf[i].rem = size;
2467*a71a9546SAutomerger Merge Worker 		xl->buf[i].off = 0;
2468*a71a9546SAutomerger Merge Worker 	}
2469*a71a9546SAutomerger Merge Worker 	xl->comment[0] = '\0';
2470*a71a9546SAutomerger Merge Worker 
2471*a71a9546SAutomerger Merge Worker 	return xl;
2472*a71a9546SAutomerger Merge Worker }
2473*a71a9546SAutomerger Merge Worker 
xt_xlate_free(struct xt_xlate * xl)2474*a71a9546SAutomerger Merge Worker void xt_xlate_free(struct xt_xlate *xl)
2475*a71a9546SAutomerger Merge Worker {
2476*a71a9546SAutomerger Merge Worker 	int i;
2477*a71a9546SAutomerger Merge Worker 
2478*a71a9546SAutomerger Merge Worker 	for (i = 0; i < __XT_XLATE_MAX; i++)
2479*a71a9546SAutomerger Merge Worker 		free(xl->buf[i].data);
2480*a71a9546SAutomerger Merge Worker 
2481*a71a9546SAutomerger Merge Worker 	free(xl);
2482*a71a9546SAutomerger Merge Worker }
2483*a71a9546SAutomerger Merge Worker 
isbrace(char c)2484*a71a9546SAutomerger Merge Worker static bool isbrace(char c)
2485*a71a9546SAutomerger Merge Worker {
2486*a71a9546SAutomerger Merge Worker 	switch (c) {
2487*a71a9546SAutomerger Merge Worker 	case '(':
2488*a71a9546SAutomerger Merge Worker 	case ')':
2489*a71a9546SAutomerger Merge Worker 	case '{':
2490*a71a9546SAutomerger Merge Worker 	case '}':
2491*a71a9546SAutomerger Merge Worker 	case '[':
2492*a71a9546SAutomerger Merge Worker 	case ']':
2493*a71a9546SAutomerger Merge Worker 		return true;
2494*a71a9546SAutomerger Merge Worker 	}
2495*a71a9546SAutomerger Merge Worker 	return false;
2496*a71a9546SAutomerger Merge Worker }
2497*a71a9546SAutomerger Merge Worker 
__xt_xlate_add(struct xt_xlate * xl,enum xt_xlate_type type,bool space,const char * fmt,va_list ap)2498*a71a9546SAutomerger Merge Worker static void __xt_xlate_add(struct xt_xlate *xl, enum xt_xlate_type type,
2499*a71a9546SAutomerger Merge Worker 			   bool space, const char *fmt, va_list ap)
2500*a71a9546SAutomerger Merge Worker {
2501*a71a9546SAutomerger Merge Worker 	struct xt_xlate_buf *buf = &xl->buf[type];
2502*a71a9546SAutomerger Merge Worker 	char tmpbuf[1024] = "";
2503*a71a9546SAutomerger Merge Worker 	int len;
2504*a71a9546SAutomerger Merge Worker 
2505*a71a9546SAutomerger Merge Worker 	len = vsnprintf(tmpbuf, 1024, fmt, ap);
2506*a71a9546SAutomerger Merge Worker 	if (len < 0 || len >= buf->rem - 1)
2507*a71a9546SAutomerger Merge Worker 		xtables_error(RESOURCE_PROBLEM, "OOM");
2508*a71a9546SAutomerger Merge Worker 
2509*a71a9546SAutomerger Merge Worker 	if (space && buf->off &&
2510*a71a9546SAutomerger Merge Worker 	    !isspace(buf->data[buf->off - 1]) &&
2511*a71a9546SAutomerger Merge Worker 	    (isalnum(tmpbuf[0]) || isbrace(tmpbuf[0]))) {
2512*a71a9546SAutomerger Merge Worker 		buf->data[buf->off] = ' ';
2513*a71a9546SAutomerger Merge Worker 		buf->off++;
2514*a71a9546SAutomerger Merge Worker 		buf->rem--;
2515*a71a9546SAutomerger Merge Worker 	}
2516*a71a9546SAutomerger Merge Worker 	sprintf(buf->data + buf->off, "%s", tmpbuf);
2517*a71a9546SAutomerger Merge Worker 	buf->rem -= len;
2518*a71a9546SAutomerger Merge Worker 	buf->off += len;
2519*a71a9546SAutomerger Merge Worker }
2520*a71a9546SAutomerger Merge Worker 
xt_xlate_rule_add(struct xt_xlate * xl,const char * fmt,...)2521*a71a9546SAutomerger Merge Worker void xt_xlate_rule_add(struct xt_xlate *xl, const char *fmt, ...)
2522*a71a9546SAutomerger Merge Worker {
2523*a71a9546SAutomerger Merge Worker 	va_list ap;
2524*a71a9546SAutomerger Merge Worker 
2525*a71a9546SAutomerger Merge Worker 	va_start(ap, fmt);
2526*a71a9546SAutomerger Merge Worker 	__xt_xlate_add(xl, XT_XLATE_RULE, true, fmt, ap);
2527*a71a9546SAutomerger Merge Worker 	va_end(ap);
2528*a71a9546SAutomerger Merge Worker }
2529*a71a9546SAutomerger Merge Worker 
xt_xlate_rule_add_nospc(struct xt_xlate * xl,const char * fmt,...)2530*a71a9546SAutomerger Merge Worker void xt_xlate_rule_add_nospc(struct xt_xlate *xl, const char *fmt, ...)
2531*a71a9546SAutomerger Merge Worker {
2532*a71a9546SAutomerger Merge Worker 	va_list ap;
2533*a71a9546SAutomerger Merge Worker 
2534*a71a9546SAutomerger Merge Worker 	va_start(ap, fmt);
2535*a71a9546SAutomerger Merge Worker 	__xt_xlate_add(xl, XT_XLATE_RULE, false, fmt, ap);
2536*a71a9546SAutomerger Merge Worker 	va_end(ap);
2537*a71a9546SAutomerger Merge Worker }
2538*a71a9546SAutomerger Merge Worker 
xt_xlate_set_add(struct xt_xlate * xl,const char * fmt,...)2539*a71a9546SAutomerger Merge Worker void xt_xlate_set_add(struct xt_xlate *xl, const char *fmt, ...)
2540*a71a9546SAutomerger Merge Worker {
2541*a71a9546SAutomerger Merge Worker 	va_list ap;
2542*a71a9546SAutomerger Merge Worker 
2543*a71a9546SAutomerger Merge Worker 	va_start(ap, fmt);
2544*a71a9546SAutomerger Merge Worker 	__xt_xlate_add(xl, XT_XLATE_SET, true, fmt, ap);
2545*a71a9546SAutomerger Merge Worker 	va_end(ap);
2546*a71a9546SAutomerger Merge Worker }
2547*a71a9546SAutomerger Merge Worker 
xt_xlate_set_add_nospc(struct xt_xlate * xl,const char * fmt,...)2548*a71a9546SAutomerger Merge Worker void xt_xlate_set_add_nospc(struct xt_xlate *xl, const char *fmt, ...)
2549*a71a9546SAutomerger Merge Worker {
2550*a71a9546SAutomerger Merge Worker 	va_list ap;
2551*a71a9546SAutomerger Merge Worker 
2552*a71a9546SAutomerger Merge Worker 	va_start(ap, fmt);
2553*a71a9546SAutomerger Merge Worker 	__xt_xlate_add(xl, XT_XLATE_SET, false, fmt, ap);
2554*a71a9546SAutomerger Merge Worker 	va_end(ap);
2555*a71a9546SAutomerger Merge Worker }
2556*a71a9546SAutomerger Merge Worker 
xt_xlate_add_comment(struct xt_xlate * xl,const char * comment)2557*a71a9546SAutomerger Merge Worker void xt_xlate_add_comment(struct xt_xlate *xl, const char *comment)
2558*a71a9546SAutomerger Merge Worker {
2559*a71a9546SAutomerger Merge Worker 	strncpy(xl->comment, comment, NFT_USERDATA_MAXLEN - 1);
2560*a71a9546SAutomerger Merge Worker 	xl->comment[NFT_USERDATA_MAXLEN - 1] = '\0';
2561*a71a9546SAutomerger Merge Worker }
2562*a71a9546SAutomerger Merge Worker 
xt_xlate_get_comment(struct xt_xlate * xl)2563*a71a9546SAutomerger Merge Worker const char *xt_xlate_get_comment(struct xt_xlate *xl)
2564*a71a9546SAutomerger Merge Worker {
2565*a71a9546SAutomerger Merge Worker 	return xl->comment[0] ? xl->comment : NULL;
2566*a71a9546SAutomerger Merge Worker }
2567*a71a9546SAutomerger Merge Worker 
xl_xlate_set_family(struct xt_xlate * xl,uint8_t family)2568*a71a9546SAutomerger Merge Worker void xl_xlate_set_family(struct xt_xlate *xl, uint8_t family)
2569*a71a9546SAutomerger Merge Worker {
2570*a71a9546SAutomerger Merge Worker 	xl->family = family;
2571*a71a9546SAutomerger Merge Worker }
2572*a71a9546SAutomerger Merge Worker 
xt_xlate_get_family(struct xt_xlate * xl)2573*a71a9546SAutomerger Merge Worker uint8_t xt_xlate_get_family(struct xt_xlate *xl)
2574*a71a9546SAutomerger Merge Worker {
2575*a71a9546SAutomerger Merge Worker 	return xl->family;
2576*a71a9546SAutomerger Merge Worker }
2577*a71a9546SAutomerger Merge Worker 
xt_xlate_get(struct xt_xlate * xl)2578*a71a9546SAutomerger Merge Worker const char *xt_xlate_get(struct xt_xlate *xl)
2579*a71a9546SAutomerger Merge Worker {
2580*a71a9546SAutomerger Merge Worker 	struct xt_xlate_buf *buf = &xl->buf[XT_XLATE_RULE];
2581*a71a9546SAutomerger Merge Worker 
2582*a71a9546SAutomerger Merge Worker 	while (buf->off && isspace(buf->data[buf->off - 1]))
2583*a71a9546SAutomerger Merge Worker 		buf->data[--buf->off] = '\0';
2584*a71a9546SAutomerger Merge Worker 
2585*a71a9546SAutomerger Merge Worker 	return buf->data;
2586*a71a9546SAutomerger Merge Worker }
2587*a71a9546SAutomerger Merge Worker 
xt_xlate_set_get(struct xt_xlate * xl)2588*a71a9546SAutomerger Merge Worker const char *xt_xlate_set_get(struct xt_xlate *xl)
2589*a71a9546SAutomerger Merge Worker {
2590*a71a9546SAutomerger Merge Worker 	return xl->buf[XT_XLATE_SET].data;
2591*a71a9546SAutomerger Merge Worker }
2592