xref: /aosp_15_r20/external/iptables/libiptc/libiptc.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1*a71a9546SAutomerger Merge Worker /* Library which manipulates firewall rules.  Version $Revision$ */
2*a71a9546SAutomerger Merge Worker 
3*a71a9546SAutomerger Merge Worker /* Architecture of firewall rules is as follows:
4*a71a9546SAutomerger Merge Worker  *
5*a71a9546SAutomerger Merge Worker  * Chains go INPUT, FORWARD, OUTPUT then user chains.
6*a71a9546SAutomerger Merge Worker  * Each user chain starts with an ERROR node.
7*a71a9546SAutomerger Merge Worker  * Every chain ends with an unconditional jump: a RETURN for user chains,
8*a71a9546SAutomerger Merge Worker  * and a POLICY for built-ins.
9*a71a9546SAutomerger Merge Worker  */
10*a71a9546SAutomerger Merge Worker 
11*a71a9546SAutomerger Merge Worker /* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12*a71a9546SAutomerger Merge Worker  * COPYING for details).
13*a71a9546SAutomerger Merge Worker  * (C) 2000-2004 by the Netfilter Core Team <[email protected]>
14*a71a9546SAutomerger Merge Worker  *
15*a71a9546SAutomerger Merge Worker  * 2003-Jun-20: Harald Welte <[email protected]>:
16*a71a9546SAutomerger Merge Worker  *	- Reimplementation of chain cache to use offsets instead of entries
17*a71a9546SAutomerger Merge Worker  * 2003-Jun-23: Harald Welte <[email protected]>:
18*a71a9546SAutomerger Merge Worker  * 	- performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
19*a71a9546SAutomerger Merge Worker  * 	  don't rebuild the chain cache after every operation, instead fix it
20*a71a9546SAutomerger Merge Worker  * 	  up after a ruleset change.
21*a71a9546SAutomerger Merge Worker  * 2004-Aug-18: Harald Welte <[email protected]>:
22*a71a9546SAutomerger Merge Worker  * 	- further performance work: total reimplementation of libiptc.
23*a71a9546SAutomerger Merge Worker  * 	- libiptc now has a real internal (linked-list) represntation of the
24*a71a9546SAutomerger Merge Worker  * 	  ruleset and a parser/compiler from/to this internal representation
25*a71a9546SAutomerger Merge Worker  * 	- again sponsored by Astaro AG (http://www.astaro.com/)
26*a71a9546SAutomerger Merge Worker  *
27*a71a9546SAutomerger Merge Worker  * 2008-Jan+Jul: Jesper Dangaard Brouer <[email protected]>
28*a71a9546SAutomerger Merge Worker  * 	- performance work: speedup chain list "name" searching.
29*a71a9546SAutomerger Merge Worker  * 	- performance work: speedup initial ruleset parsing.
30*a71a9546SAutomerger Merge Worker  * 	- sponsored by ComX Networks A/S (http://www.comx.dk/)
31*a71a9546SAutomerger Merge Worker  */
32*a71a9546SAutomerger Merge Worker #include <unistd.h>
33*a71a9546SAutomerger Merge Worker #include <fcntl.h>
34*a71a9546SAutomerger Merge Worker #include <sys/types.h>
35*a71a9546SAutomerger Merge Worker #include <sys/socket.h>
36*a71a9546SAutomerger Merge Worker #include <stdbool.h>
37*a71a9546SAutomerger Merge Worker #include <xtables.h>
38*a71a9546SAutomerger Merge Worker #include <libiptc/xtcshared.h>
39*a71a9546SAutomerger Merge Worker 
40*a71a9546SAutomerger Merge Worker #include "linux_list.h"
41*a71a9546SAutomerger Merge Worker 
42*a71a9546SAutomerger Merge Worker //#define IPTC_DEBUG2 1
43*a71a9546SAutomerger Merge Worker 
44*a71a9546SAutomerger Merge Worker #ifdef IPTC_DEBUG2
45*a71a9546SAutomerger Merge Worker #include <fcntl.h>
46*a71a9546SAutomerger Merge Worker #define DEBUGP(x, args...)	fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
47*a71a9546SAutomerger Merge Worker #define DEBUGP_C(x, args...)	fprintf(stderr, x, ## args)
48*a71a9546SAutomerger Merge Worker #else
49*a71a9546SAutomerger Merge Worker #define DEBUGP(x, args...)
50*a71a9546SAutomerger Merge Worker #define DEBUGP_C(x, args...)
51*a71a9546SAutomerger Merge Worker #endif
52*a71a9546SAutomerger Merge Worker 
53*a71a9546SAutomerger Merge Worker #ifdef DEBUG
54*a71a9546SAutomerger Merge Worker #define debug(x, args...)	fprintf(stderr, x, ## args)
55*a71a9546SAutomerger Merge Worker #else
56*a71a9546SAutomerger Merge Worker #define debug(x, args...)
57*a71a9546SAutomerger Merge Worker #endif
58*a71a9546SAutomerger Merge Worker 
59*a71a9546SAutomerger Merge Worker static void *iptc_fn = NULL;
60*a71a9546SAutomerger Merge Worker 
61*a71a9546SAutomerger Merge Worker static const char *hooknames[] = {
62*a71a9546SAutomerger Merge Worker 	[HOOK_PRE_ROUTING]	= "PREROUTING",
63*a71a9546SAutomerger Merge Worker 	[HOOK_LOCAL_IN]		= "INPUT",
64*a71a9546SAutomerger Merge Worker 	[HOOK_FORWARD]		= "FORWARD",
65*a71a9546SAutomerger Merge Worker 	[HOOK_LOCAL_OUT]	= "OUTPUT",
66*a71a9546SAutomerger Merge Worker 	[HOOK_POST_ROUTING]	= "POSTROUTING",
67*a71a9546SAutomerger Merge Worker };
68*a71a9546SAutomerger Merge Worker 
69*a71a9546SAutomerger Merge Worker /* Convenience structures */
70*a71a9546SAutomerger Merge Worker struct chain_head;
71*a71a9546SAutomerger Merge Worker struct rule_head;
72*a71a9546SAutomerger Merge Worker 
73*a71a9546SAutomerger Merge Worker struct counter_map
74*a71a9546SAutomerger Merge Worker {
75*a71a9546SAutomerger Merge Worker 	enum {
76*a71a9546SAutomerger Merge Worker 		COUNTER_MAP_NOMAP,
77*a71a9546SAutomerger Merge Worker 		COUNTER_MAP_NORMAL_MAP,
78*a71a9546SAutomerger Merge Worker 		COUNTER_MAP_ZEROED,
79*a71a9546SAutomerger Merge Worker 		COUNTER_MAP_SET
80*a71a9546SAutomerger Merge Worker 	} maptype;
81*a71a9546SAutomerger Merge Worker 	unsigned int mappos;
82*a71a9546SAutomerger Merge Worker };
83*a71a9546SAutomerger Merge Worker 
84*a71a9546SAutomerger Merge Worker enum iptcc_rule_type {
85*a71a9546SAutomerger Merge Worker 	IPTCC_R_STANDARD,		/* standard target (ACCEPT, ...) */
86*a71a9546SAutomerger Merge Worker 	IPTCC_R_MODULE,			/* extension module (SNAT, ...) */
87*a71a9546SAutomerger Merge Worker 	IPTCC_R_FALLTHROUGH,		/* fallthrough rule */
88*a71a9546SAutomerger Merge Worker 	IPTCC_R_JUMP,			/* jump to other chain */
89*a71a9546SAutomerger Merge Worker };
90*a71a9546SAutomerger Merge Worker 
91*a71a9546SAutomerger Merge Worker struct rule_head
92*a71a9546SAutomerger Merge Worker {
93*a71a9546SAutomerger Merge Worker 	struct list_head list;
94*a71a9546SAutomerger Merge Worker 	struct chain_head *chain;
95*a71a9546SAutomerger Merge Worker 	struct counter_map counter_map;
96*a71a9546SAutomerger Merge Worker 
97*a71a9546SAutomerger Merge Worker 	unsigned int index;		/* index (needed for counter_map) */
98*a71a9546SAutomerger Merge Worker 	unsigned int offset;		/* offset in rule blob */
99*a71a9546SAutomerger Merge Worker 
100*a71a9546SAutomerger Merge Worker 	enum iptcc_rule_type type;
101*a71a9546SAutomerger Merge Worker 	struct chain_head *jump;	/* jump target, if IPTCC_R_JUMP */
102*a71a9546SAutomerger Merge Worker 
103*a71a9546SAutomerger Merge Worker 	unsigned int size;		/* size of entry data */
104*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY entry[0];
105*a71a9546SAutomerger Merge Worker };
106*a71a9546SAutomerger Merge Worker 
107*a71a9546SAutomerger Merge Worker struct chain_head
108*a71a9546SAutomerger Merge Worker {
109*a71a9546SAutomerger Merge Worker 	struct list_head list;
110*a71a9546SAutomerger Merge Worker 	char name[TABLE_MAXNAMELEN];
111*a71a9546SAutomerger Merge Worker 	unsigned int hooknum;		/* hook number+1 if builtin */
112*a71a9546SAutomerger Merge Worker 	unsigned int references;	/* how many jumps reference us */
113*a71a9546SAutomerger Merge Worker 	int verdict;			/* verdict if builtin */
114*a71a9546SAutomerger Merge Worker 
115*a71a9546SAutomerger Merge Worker 	STRUCT_COUNTERS counters;	/* per-chain counters */
116*a71a9546SAutomerger Merge Worker 	struct counter_map counter_map;
117*a71a9546SAutomerger Merge Worker 
118*a71a9546SAutomerger Merge Worker 	unsigned int num_rules;		/* number of rules in list */
119*a71a9546SAutomerger Merge Worker 	struct list_head rules;		/* list of rules */
120*a71a9546SAutomerger Merge Worker 
121*a71a9546SAutomerger Merge Worker 	unsigned int index;		/* index (needed for jump resolval) */
122*a71a9546SAutomerger Merge Worker 	unsigned int head_offset;	/* offset in rule blob */
123*a71a9546SAutomerger Merge Worker 	unsigned int foot_index;	/* index (needed for counter_map) */
124*a71a9546SAutomerger Merge Worker 	unsigned int foot_offset;	/* offset in rule blob */
125*a71a9546SAutomerger Merge Worker };
126*a71a9546SAutomerger Merge Worker 
127*a71a9546SAutomerger Merge Worker struct xtc_handle {
128*a71a9546SAutomerger Merge Worker 	int sockfd;
129*a71a9546SAutomerger Merge Worker 	int changed;			 /* Have changes been made? */
130*a71a9546SAutomerger Merge Worker 
131*a71a9546SAutomerger Merge Worker 	struct list_head chains;
132*a71a9546SAutomerger Merge Worker 
133*a71a9546SAutomerger Merge Worker 	struct chain_head *chain_iterator_cur;
134*a71a9546SAutomerger Merge Worker 	struct rule_head *rule_iterator_cur;
135*a71a9546SAutomerger Merge Worker 
136*a71a9546SAutomerger Merge Worker 	unsigned int num_chains;         /* number of user defined chains */
137*a71a9546SAutomerger Merge Worker 
138*a71a9546SAutomerger Merge Worker 	struct chain_head **chain_index;   /* array for fast chain list access*/
139*a71a9546SAutomerger Merge Worker 	unsigned int        chain_index_sz;/* size of chain index array */
140*a71a9546SAutomerger Merge Worker 
141*a71a9546SAutomerger Merge Worker 	int sorted_offsets; /* if chains are received sorted from kernel,
142*a71a9546SAutomerger Merge Worker 			     * then the offsets are also sorted. Says if its
143*a71a9546SAutomerger Merge Worker 			     * possible to bsearch offsets using chain_index.
144*a71a9546SAutomerger Merge Worker 			     */
145*a71a9546SAutomerger Merge Worker 
146*a71a9546SAutomerger Merge Worker 	STRUCT_GETINFO info;
147*a71a9546SAutomerger Merge Worker 	STRUCT_GET_ENTRIES *entries;
148*a71a9546SAutomerger Merge Worker };
149*a71a9546SAutomerger Merge Worker 
150*a71a9546SAutomerger Merge Worker enum bsearch_type {
151*a71a9546SAutomerger Merge Worker 	BSEARCH_NAME,	/* Binary search after chain name */
152*a71a9546SAutomerger Merge Worker 	BSEARCH_OFFSET,	/* Binary search based on offset */
153*a71a9546SAutomerger Merge Worker };
154*a71a9546SAutomerger Merge Worker 
155*a71a9546SAutomerger Merge Worker /* allocate a new chain head for the cache */
iptcc_alloc_chain_head(const char * name,int hooknum)156*a71a9546SAutomerger Merge Worker static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
157*a71a9546SAutomerger Merge Worker {
158*a71a9546SAutomerger Merge Worker 	struct chain_head *c = malloc(sizeof(*c));
159*a71a9546SAutomerger Merge Worker 	if (!c)
160*a71a9546SAutomerger Merge Worker 		return NULL;
161*a71a9546SAutomerger Merge Worker 	memset(c, 0, sizeof(*c));
162*a71a9546SAutomerger Merge Worker 
163*a71a9546SAutomerger Merge Worker 	strncpy(c->name, name, TABLE_MAXNAMELEN - 1);
164*a71a9546SAutomerger Merge Worker 	c->hooknum = hooknum;
165*a71a9546SAutomerger Merge Worker 	INIT_LIST_HEAD(&c->rules);
166*a71a9546SAutomerger Merge Worker 
167*a71a9546SAutomerger Merge Worker 	return c;
168*a71a9546SAutomerger Merge Worker }
169*a71a9546SAutomerger Merge Worker 
170*a71a9546SAutomerger Merge Worker /* allocate and initialize a new rule for the cache */
iptcc_alloc_rule(struct chain_head * c,unsigned int size)171*a71a9546SAutomerger Merge Worker static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
172*a71a9546SAutomerger Merge Worker {
173*a71a9546SAutomerger Merge Worker 	struct rule_head *r = malloc(sizeof(*r)+size);
174*a71a9546SAutomerger Merge Worker 	if (!r)
175*a71a9546SAutomerger Merge Worker 		return NULL;
176*a71a9546SAutomerger Merge Worker 	memset(r, 0, sizeof(*r));
177*a71a9546SAutomerger Merge Worker 
178*a71a9546SAutomerger Merge Worker 	r->chain = c;
179*a71a9546SAutomerger Merge Worker 	r->size = size;
180*a71a9546SAutomerger Merge Worker 
181*a71a9546SAutomerger Merge Worker 	return r;
182*a71a9546SAutomerger Merge Worker }
183*a71a9546SAutomerger Merge Worker 
184*a71a9546SAutomerger Merge Worker /* notify us that the ruleset has been modified by the user */
185*a71a9546SAutomerger Merge Worker static inline void
set_changed(struct xtc_handle * h)186*a71a9546SAutomerger Merge Worker set_changed(struct xtc_handle *h)
187*a71a9546SAutomerger Merge Worker {
188*a71a9546SAutomerger Merge Worker 	h->changed = 1;
189*a71a9546SAutomerger Merge Worker }
190*a71a9546SAutomerger Merge Worker 
191*a71a9546SAutomerger Merge Worker /**********************************************************************
192*a71a9546SAutomerger Merge Worker  * iptc blob utility functions (iptcb_*)
193*a71a9546SAutomerger Merge Worker  **********************************************************************/
194*a71a9546SAutomerger Merge Worker 
195*a71a9546SAutomerger Merge Worker static inline int
iptcb_get_number(const STRUCT_ENTRY * i,const STRUCT_ENTRY * seek,unsigned int * pos)196*a71a9546SAutomerger Merge Worker iptcb_get_number(const STRUCT_ENTRY *i,
197*a71a9546SAutomerger Merge Worker 	   const STRUCT_ENTRY *seek,
198*a71a9546SAutomerger Merge Worker 	   unsigned int *pos)
199*a71a9546SAutomerger Merge Worker {
200*a71a9546SAutomerger Merge Worker 	if (i == seek)
201*a71a9546SAutomerger Merge Worker 		return 1;
202*a71a9546SAutomerger Merge Worker 	(*pos)++;
203*a71a9546SAutomerger Merge Worker 	return 0;
204*a71a9546SAutomerger Merge Worker }
205*a71a9546SAutomerger Merge Worker 
206*a71a9546SAutomerger Merge Worker static inline int
iptcb_get_entry_n(STRUCT_ENTRY * i,unsigned int number,unsigned int * pos,STRUCT_ENTRY ** pe)207*a71a9546SAutomerger Merge Worker iptcb_get_entry_n(STRUCT_ENTRY *i,
208*a71a9546SAutomerger Merge Worker 	    unsigned int number,
209*a71a9546SAutomerger Merge Worker 	    unsigned int *pos,
210*a71a9546SAutomerger Merge Worker 	    STRUCT_ENTRY **pe)
211*a71a9546SAutomerger Merge Worker {
212*a71a9546SAutomerger Merge Worker 	if (*pos == number) {
213*a71a9546SAutomerger Merge Worker 		*pe = i;
214*a71a9546SAutomerger Merge Worker 		return 1;
215*a71a9546SAutomerger Merge Worker 	}
216*a71a9546SAutomerger Merge Worker 	(*pos)++;
217*a71a9546SAutomerger Merge Worker 	return 0;
218*a71a9546SAutomerger Merge Worker }
219*a71a9546SAutomerger Merge Worker 
220*a71a9546SAutomerger Merge Worker static inline STRUCT_ENTRY *
iptcb_get_entry(struct xtc_handle * h,unsigned int offset)221*a71a9546SAutomerger Merge Worker iptcb_get_entry(struct xtc_handle *h, unsigned int offset)
222*a71a9546SAutomerger Merge Worker {
223*a71a9546SAutomerger Merge Worker 	return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
224*a71a9546SAutomerger Merge Worker }
225*a71a9546SAutomerger Merge Worker 
226*a71a9546SAutomerger Merge Worker static unsigned int
iptcb_entry2index(struct xtc_handle * const h,const STRUCT_ENTRY * seek)227*a71a9546SAutomerger Merge Worker iptcb_entry2index(struct xtc_handle *const h, const STRUCT_ENTRY *seek)
228*a71a9546SAutomerger Merge Worker {
229*a71a9546SAutomerger Merge Worker 	unsigned int pos = 0;
230*a71a9546SAutomerger Merge Worker 
231*a71a9546SAutomerger Merge Worker 	if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
232*a71a9546SAutomerger Merge Worker 			  iptcb_get_number, seek, &pos) == 0) {
233*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "ERROR: offset %u not an entry!\n",
234*a71a9546SAutomerger Merge Worker 			(unsigned int)((char *)seek - (char *)h->entries->entrytable));
235*a71a9546SAutomerger Merge Worker 		abort();
236*a71a9546SAutomerger Merge Worker 	}
237*a71a9546SAutomerger Merge Worker 	return pos;
238*a71a9546SAutomerger Merge Worker }
239*a71a9546SAutomerger Merge Worker 
240*a71a9546SAutomerger Merge Worker static inline STRUCT_ENTRY *
iptcb_offset2entry(struct xtc_handle * h,unsigned int offset)241*a71a9546SAutomerger Merge Worker iptcb_offset2entry(struct xtc_handle *h, unsigned int offset)
242*a71a9546SAutomerger Merge Worker {
243*a71a9546SAutomerger Merge Worker 	return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
244*a71a9546SAutomerger Merge Worker }
245*a71a9546SAutomerger Merge Worker 
246*a71a9546SAutomerger Merge Worker 
247*a71a9546SAutomerger Merge Worker static inline unsigned long
iptcb_entry2offset(struct xtc_handle * const h,const STRUCT_ENTRY * e)248*a71a9546SAutomerger Merge Worker iptcb_entry2offset(struct xtc_handle *const h, const STRUCT_ENTRY *e)
249*a71a9546SAutomerger Merge Worker {
250*a71a9546SAutomerger Merge Worker 	return (void *)e - (void *)h->entries->entrytable;
251*a71a9546SAutomerger Merge Worker }
252*a71a9546SAutomerger Merge Worker 
253*a71a9546SAutomerger Merge Worker static inline unsigned int
iptcb_offset2index(struct xtc_handle * const h,unsigned int offset)254*a71a9546SAutomerger Merge Worker iptcb_offset2index(struct xtc_handle *const h, unsigned int offset)
255*a71a9546SAutomerger Merge Worker {
256*a71a9546SAutomerger Merge Worker 	return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
257*a71a9546SAutomerger Merge Worker }
258*a71a9546SAutomerger Merge Worker 
259*a71a9546SAutomerger Merge Worker /* Returns 0 if not hook entry, else hooknumber + 1 */
260*a71a9546SAutomerger Merge Worker static inline unsigned int
iptcb_ent_is_hook_entry(STRUCT_ENTRY * e,struct xtc_handle * h)261*a71a9546SAutomerger Merge Worker iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, struct xtc_handle *h)
262*a71a9546SAutomerger Merge Worker {
263*a71a9546SAutomerger Merge Worker 	unsigned int i;
264*a71a9546SAutomerger Merge Worker 
265*a71a9546SAutomerger Merge Worker 	for (i = 0; i < NUMHOOKS; i++) {
266*a71a9546SAutomerger Merge Worker 		if ((h->info.valid_hooks & (1 << i))
267*a71a9546SAutomerger Merge Worker 		    && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
268*a71a9546SAutomerger Merge Worker 			return i+1;
269*a71a9546SAutomerger Merge Worker 	}
270*a71a9546SAutomerger Merge Worker 	return 0;
271*a71a9546SAutomerger Merge Worker }
272*a71a9546SAutomerger Merge Worker 
273*a71a9546SAutomerger Merge Worker 
274*a71a9546SAutomerger Merge Worker /**********************************************************************
275*a71a9546SAutomerger Merge Worker  * Chain index (cache utility) functions
276*a71a9546SAutomerger Merge Worker  **********************************************************************
277*a71a9546SAutomerger Merge Worker  * The chain index is an array with pointers into the chain list, with
278*a71a9546SAutomerger Merge Worker  * CHAIN_INDEX_BUCKET_LEN spacing.  This facilitates the ability to
279*a71a9546SAutomerger Merge Worker  * speedup chain list searching, by find a more optimal starting
280*a71a9546SAutomerger Merge Worker  * points when searching the linked list.
281*a71a9546SAutomerger Merge Worker  *
282*a71a9546SAutomerger Merge Worker  * The starting point can be found fast by using a binary search of
283*a71a9546SAutomerger Merge Worker  * the chain index. Thus, reducing the previous search complexity of
284*a71a9546SAutomerger Merge Worker  * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN.
285*a71a9546SAutomerger Merge Worker  *
286*a71a9546SAutomerger Merge Worker  * A nice property of the chain index, is that the "bucket" list
287*a71a9546SAutomerger Merge Worker  * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will
288*a71a9546SAutomerger Merge Worker  * change this). Oppose to hashing, where the "bucket" list length can
289*a71a9546SAutomerger Merge Worker  * vary a lot.
290*a71a9546SAutomerger Merge Worker  */
291*a71a9546SAutomerger Merge Worker #ifndef CHAIN_INDEX_BUCKET_LEN
292*a71a9546SAutomerger Merge Worker #define CHAIN_INDEX_BUCKET_LEN 40
293*a71a9546SAutomerger Merge Worker #endif
294*a71a9546SAutomerger Merge Worker 
295*a71a9546SAutomerger Merge Worker /* Another nice property of the chain index is that inserting/creating
296*a71a9546SAutomerger Merge Worker  * chains in chain list don't change the correctness of the chain
297*a71a9546SAutomerger Merge Worker  * index, it only causes longer lists in the buckets.
298*a71a9546SAutomerger Merge Worker  *
299*a71a9546SAutomerger Merge Worker  * To mitigate the performance penalty of longer bucket lists and the
300*a71a9546SAutomerger Merge Worker  * penalty of rebuilding, the chain index is rebuild only when
301*a71a9546SAutomerger Merge Worker  * CHAIN_INDEX_INSERT_MAX chains has been added.
302*a71a9546SAutomerger Merge Worker  */
303*a71a9546SAutomerger Merge Worker #ifndef CHAIN_INDEX_INSERT_MAX
304*a71a9546SAutomerger Merge Worker #define CHAIN_INDEX_INSERT_MAX 355
305*a71a9546SAutomerger Merge Worker #endif
306*a71a9546SAutomerger Merge Worker 
307*a71a9546SAutomerger Merge Worker static inline unsigned int iptcc_is_builtin(struct chain_head *c);
308*a71a9546SAutomerger Merge Worker 
309*a71a9546SAutomerger Merge Worker /* Use binary search in the chain index array, to find a chain_head
310*a71a9546SAutomerger Merge Worker  * pointer closest to the place of the searched name element.
311*a71a9546SAutomerger Merge Worker  *
312*a71a9546SAutomerger Merge Worker  * Notes that, binary search (obviously) requires that the chain list
313*a71a9546SAutomerger Merge Worker  * is sorted by name.
314*a71a9546SAutomerger Merge Worker  *
315*a71a9546SAutomerger Merge Worker  * The not so obvious: The chain index array, is actually both sorted
316*a71a9546SAutomerger Merge Worker  * by name and offset, at the same time!.  This is only true because,
317*a71a9546SAutomerger Merge Worker  * chain are stored sorted in the kernel (as we pushed it in sorted).
318*a71a9546SAutomerger Merge Worker  *
319*a71a9546SAutomerger Merge Worker  */
320*a71a9546SAutomerger Merge Worker static struct list_head *
__iptcc_bsearch_chain_index(const char * name,unsigned int offset,unsigned int * idx,struct xtc_handle * handle,enum bsearch_type type)321*a71a9546SAutomerger Merge Worker __iptcc_bsearch_chain_index(const char *name, unsigned int offset,
322*a71a9546SAutomerger Merge Worker 			    unsigned int *idx, struct xtc_handle *handle,
323*a71a9546SAutomerger Merge Worker 			    enum bsearch_type type)
324*a71a9546SAutomerger Merge Worker {
325*a71a9546SAutomerger Merge Worker 	unsigned int pos, end;
326*a71a9546SAutomerger Merge Worker 	int res;
327*a71a9546SAutomerger Merge Worker 
328*a71a9546SAutomerger Merge Worker 	struct list_head *list_pos;
329*a71a9546SAutomerger Merge Worker 	list_pos=&handle->chains;
330*a71a9546SAutomerger Merge Worker 
331*a71a9546SAutomerger Merge Worker 	/* Check for empty array, e.g. no user defined chains */
332*a71a9546SAutomerger Merge Worker 	if (handle->chain_index_sz == 0) {
333*a71a9546SAutomerger Merge Worker 		debug("WARNING: handle->chain_index_sz == 0\n");
334*a71a9546SAutomerger Merge Worker 		return list_pos;
335*a71a9546SAutomerger Merge Worker 	}
336*a71a9546SAutomerger Merge Worker 
337*a71a9546SAutomerger Merge Worker 	/* Init */
338*a71a9546SAutomerger Merge Worker 	end = handle->chain_index_sz;
339*a71a9546SAutomerger Merge Worker 	pos = end / 2;
340*a71a9546SAutomerger Merge Worker 
341*a71a9546SAutomerger Merge Worker 	debug("bsearch Find chain:%s (pos:%d end:%d) (offset:%d)\n",
342*a71a9546SAutomerger Merge Worker 	      name, pos, end, offset);
343*a71a9546SAutomerger Merge Worker 
344*a71a9546SAutomerger Merge Worker 	/* Loop */
345*a71a9546SAutomerger Merge Worker  loop:
346*a71a9546SAutomerger Merge Worker 	if (!handle->chain_index[pos]) {
347*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos);
348*a71a9546SAutomerger Merge Worker 		return &handle->chains; /* Be safe, return orig start pos */
349*a71a9546SAutomerger Merge Worker 	}
350*a71a9546SAutomerger Merge Worker 
351*a71a9546SAutomerger Merge Worker 	debug("bsearch Index[%d] name:%s ",
352*a71a9546SAutomerger Merge Worker 	      pos, handle->chain_index[pos]->name);
353*a71a9546SAutomerger Merge Worker 
354*a71a9546SAutomerger Merge Worker 	/* Support for different compare functions */
355*a71a9546SAutomerger Merge Worker 	switch (type) {
356*a71a9546SAutomerger Merge Worker 	case BSEARCH_NAME:
357*a71a9546SAutomerger Merge Worker 		res = strcmp(name, handle->chain_index[pos]->name);
358*a71a9546SAutomerger Merge Worker 		break;
359*a71a9546SAutomerger Merge Worker 	case BSEARCH_OFFSET:
360*a71a9546SAutomerger Merge Worker 		debug("head_offset:[%d] foot_offset:[%d] ",
361*a71a9546SAutomerger Merge Worker 		      handle->chain_index[pos]->head_offset,
362*a71a9546SAutomerger Merge Worker 		      handle->chain_index[pos]->foot_offset);
363*a71a9546SAutomerger Merge Worker 		res = offset - handle->chain_index[pos]->head_offset;
364*a71a9546SAutomerger Merge Worker 		break;
365*a71a9546SAutomerger Merge Worker 	default:
366*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "ERROR: %d not a valid bsearch type\n",
367*a71a9546SAutomerger Merge Worker 			type);
368*a71a9546SAutomerger Merge Worker 		abort();
369*a71a9546SAutomerger Merge Worker 		break;
370*a71a9546SAutomerger Merge Worker 	}
371*a71a9546SAutomerger Merge Worker 	debug("res:%d ", res);
372*a71a9546SAutomerger Merge Worker 
373*a71a9546SAutomerger Merge Worker 
374*a71a9546SAutomerger Merge Worker 	list_pos = &handle->chain_index[pos]->list;
375*a71a9546SAutomerger Merge Worker 	*idx = pos;
376*a71a9546SAutomerger Merge Worker 
377*a71a9546SAutomerger Merge Worker 	if (res == 0) { /* Found element, by direct hit */
378*a71a9546SAutomerger Merge Worker 		debug("[found] Direct hit pos:%d end:%d\n", pos, end);
379*a71a9546SAutomerger Merge Worker 		return list_pos;
380*a71a9546SAutomerger Merge Worker 	} else if (res < 0) { /* Too far, jump back */
381*a71a9546SAutomerger Merge Worker 		end = pos;
382*a71a9546SAutomerger Merge Worker 		pos = pos / 2;
383*a71a9546SAutomerger Merge Worker 
384*a71a9546SAutomerger Merge Worker 		/* Exit case: First element of array */
385*a71a9546SAutomerger Merge Worker 		if (end == 0) {
386*a71a9546SAutomerger Merge Worker 			debug("[found] Reached first array elem (end%d)\n",end);
387*a71a9546SAutomerger Merge Worker 			return list_pos;
388*a71a9546SAutomerger Merge Worker 		}
389*a71a9546SAutomerger Merge Worker 		debug("jump back to pos:%d (end:%d)\n", pos, end);
390*a71a9546SAutomerger Merge Worker 		goto loop;
391*a71a9546SAutomerger Merge Worker 	} else { /* res > 0; Not far enough, jump forward */
392*a71a9546SAutomerger Merge Worker 
393*a71a9546SAutomerger Merge Worker 		/* Exit case: Last element of array */
394*a71a9546SAutomerger Merge Worker 		if (pos == handle->chain_index_sz-1) {
395*a71a9546SAutomerger Merge Worker 			debug("[found] Last array elem (end:%d)\n", end);
396*a71a9546SAutomerger Merge Worker 			return list_pos;
397*a71a9546SAutomerger Merge Worker 		}
398*a71a9546SAutomerger Merge Worker 
399*a71a9546SAutomerger Merge Worker 		/* Exit case: Next index less, thus elem in this list section */
400*a71a9546SAutomerger Merge Worker 		switch (type) {
401*a71a9546SAutomerger Merge Worker 		case BSEARCH_NAME:
402*a71a9546SAutomerger Merge Worker 			res = strcmp(name, handle->chain_index[pos+1]->name);
403*a71a9546SAutomerger Merge Worker 			break;
404*a71a9546SAutomerger Merge Worker 		case BSEARCH_OFFSET:
405*a71a9546SAutomerger Merge Worker 			res = offset - handle->chain_index[pos+1]->head_offset;
406*a71a9546SAutomerger Merge Worker 			break;
407*a71a9546SAutomerger Merge Worker 		}
408*a71a9546SAutomerger Merge Worker 
409*a71a9546SAutomerger Merge Worker 		if (res < 0) {
410*a71a9546SAutomerger Merge Worker 			debug("[found] closest list (end:%d)\n", end);
411*a71a9546SAutomerger Merge Worker 			return list_pos;
412*a71a9546SAutomerger Merge Worker 		}
413*a71a9546SAutomerger Merge Worker 
414*a71a9546SAutomerger Merge Worker 		pos = (pos+end)/2;
415*a71a9546SAutomerger Merge Worker 		debug("jump forward to pos:%d (end:%d)\n", pos, end);
416*a71a9546SAutomerger Merge Worker 		goto loop;
417*a71a9546SAutomerger Merge Worker 	}
418*a71a9546SAutomerger Merge Worker }
419*a71a9546SAutomerger Merge Worker 
420*a71a9546SAutomerger Merge Worker /* Wrapper for string chain name based bsearch */
421*a71a9546SAutomerger Merge Worker static struct list_head *
iptcc_bsearch_chain_index(const char * name,unsigned int * idx,struct xtc_handle * handle)422*a71a9546SAutomerger Merge Worker iptcc_bsearch_chain_index(const char *name, unsigned int *idx,
423*a71a9546SAutomerger Merge Worker 			  struct xtc_handle *handle)
424*a71a9546SAutomerger Merge Worker {
425*a71a9546SAutomerger Merge Worker 	return __iptcc_bsearch_chain_index(name, 0, idx, handle, BSEARCH_NAME);
426*a71a9546SAutomerger Merge Worker }
427*a71a9546SAutomerger Merge Worker 
428*a71a9546SAutomerger Merge Worker 
429*a71a9546SAutomerger Merge Worker /* Wrapper for offset chain based bsearch */
430*a71a9546SAutomerger Merge Worker static struct list_head *
iptcc_bsearch_chain_offset(unsigned int offset,unsigned int * idx,struct xtc_handle * handle)431*a71a9546SAutomerger Merge Worker iptcc_bsearch_chain_offset(unsigned int offset, unsigned int *idx,
432*a71a9546SAutomerger Merge Worker 			  struct xtc_handle *handle)
433*a71a9546SAutomerger Merge Worker {
434*a71a9546SAutomerger Merge Worker 	struct list_head *pos;
435*a71a9546SAutomerger Merge Worker 
436*a71a9546SAutomerger Merge Worker 	/* If chains were not received sorted from kernel, then the
437*a71a9546SAutomerger Merge Worker 	 * offset bsearch is not possible.
438*a71a9546SAutomerger Merge Worker 	 */
439*a71a9546SAutomerger Merge Worker 	if (!handle->sorted_offsets)
440*a71a9546SAutomerger Merge Worker 		pos = handle->chains.next;
441*a71a9546SAutomerger Merge Worker 	else
442*a71a9546SAutomerger Merge Worker 		pos = __iptcc_bsearch_chain_index(NULL, offset, idx, handle,
443*a71a9546SAutomerger Merge Worker 						  BSEARCH_OFFSET);
444*a71a9546SAutomerger Merge Worker 	return pos;
445*a71a9546SAutomerger Merge Worker }
446*a71a9546SAutomerger Merge Worker 
447*a71a9546SAutomerger Merge Worker 
448*a71a9546SAutomerger Merge Worker #ifdef DEBUG
449*a71a9546SAutomerger Merge Worker /* Trivial linear search of chain index. Function used for verifying
450*a71a9546SAutomerger Merge Worker    the output of bsearch function */
451*a71a9546SAutomerger Merge Worker static struct list_head *
iptcc_linearly_search_chain_index(const char * name,struct xtc_handle * handle)452*a71a9546SAutomerger Merge Worker iptcc_linearly_search_chain_index(const char *name, struct xtc_handle *handle)
453*a71a9546SAutomerger Merge Worker {
454*a71a9546SAutomerger Merge Worker 	unsigned int i=0;
455*a71a9546SAutomerger Merge Worker 	int res=0;
456*a71a9546SAutomerger Merge Worker 
457*a71a9546SAutomerger Merge Worker 	struct list_head *list_pos;
458*a71a9546SAutomerger Merge Worker 	list_pos = &handle->chains;
459*a71a9546SAutomerger Merge Worker 
460*a71a9546SAutomerger Merge Worker 	if (handle->chain_index_sz)
461*a71a9546SAutomerger Merge Worker 		list_pos = &handle->chain_index[0]->list;
462*a71a9546SAutomerger Merge Worker 
463*a71a9546SAutomerger Merge Worker 	/* Linearly walk of chain index array */
464*a71a9546SAutomerger Merge Worker 
465*a71a9546SAutomerger Merge Worker 	for (i=0; i < handle->chain_index_sz; i++) {
466*a71a9546SAutomerger Merge Worker 		if (handle->chain_index[i]) {
467*a71a9546SAutomerger Merge Worker 			res = strcmp(handle->chain_index[i]->name, name);
468*a71a9546SAutomerger Merge Worker 			if (res > 0)
469*a71a9546SAutomerger Merge Worker 				break; // One step too far
470*a71a9546SAutomerger Merge Worker 			list_pos = &handle->chain_index[i]->list;
471*a71a9546SAutomerger Merge Worker 			if (res == 0)
472*a71a9546SAutomerger Merge Worker 				break; // Direct hit
473*a71a9546SAutomerger Merge Worker 		}
474*a71a9546SAutomerger Merge Worker 	}
475*a71a9546SAutomerger Merge Worker 
476*a71a9546SAutomerger Merge Worker 	return list_pos;
477*a71a9546SAutomerger Merge Worker }
478*a71a9546SAutomerger Merge Worker #endif
479*a71a9546SAutomerger Merge Worker 
iptcc_chain_index_alloc(struct xtc_handle * h)480*a71a9546SAutomerger Merge Worker static int iptcc_chain_index_alloc(struct xtc_handle *h)
481*a71a9546SAutomerger Merge Worker {
482*a71a9546SAutomerger Merge Worker 	unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
483*a71a9546SAutomerger Merge Worker 	unsigned int array_elems;
484*a71a9546SAutomerger Merge Worker 	unsigned int array_mem;
485*a71a9546SAutomerger Merge Worker 
486*a71a9546SAutomerger Merge Worker 	/* Allocate memory for the chain index array */
487*a71a9546SAutomerger Merge Worker 	array_elems = (h->num_chains / list_length) +
488*a71a9546SAutomerger Merge Worker                       (h->num_chains % list_length ? 1 : 0);
489*a71a9546SAutomerger Merge Worker 	array_mem   = sizeof(h->chain_index) * array_elems;
490*a71a9546SAutomerger Merge Worker 
491*a71a9546SAutomerger Merge Worker 	debug("Alloc Chain index, elems:%d mem:%d bytes\n",
492*a71a9546SAutomerger Merge Worker 	      array_elems, array_mem);
493*a71a9546SAutomerger Merge Worker 
494*a71a9546SAutomerger Merge Worker 	h->chain_index = malloc(array_mem);
495*a71a9546SAutomerger Merge Worker 	if (h->chain_index == NULL && array_mem > 0) {
496*a71a9546SAutomerger Merge Worker 		h->chain_index_sz = 0;
497*a71a9546SAutomerger Merge Worker 		return -ENOMEM;
498*a71a9546SAutomerger Merge Worker 	}
499*a71a9546SAutomerger Merge Worker 	memset(h->chain_index, 0, array_mem);
500*a71a9546SAutomerger Merge Worker 	h->chain_index_sz = array_elems;
501*a71a9546SAutomerger Merge Worker 
502*a71a9546SAutomerger Merge Worker 	return 1;
503*a71a9546SAutomerger Merge Worker }
504*a71a9546SAutomerger Merge Worker 
iptcc_chain_index_free(struct xtc_handle * h)505*a71a9546SAutomerger Merge Worker static void iptcc_chain_index_free(struct xtc_handle *h)
506*a71a9546SAutomerger Merge Worker {
507*a71a9546SAutomerger Merge Worker 	h->chain_index_sz = 0;
508*a71a9546SAutomerger Merge Worker 	free(h->chain_index);
509*a71a9546SAutomerger Merge Worker }
510*a71a9546SAutomerger Merge Worker 
511*a71a9546SAutomerger Merge Worker 
512*a71a9546SAutomerger Merge Worker #ifdef DEBUG
iptcc_chain_index_dump(struct xtc_handle * h)513*a71a9546SAutomerger Merge Worker static void iptcc_chain_index_dump(struct xtc_handle *h)
514*a71a9546SAutomerger Merge Worker {
515*a71a9546SAutomerger Merge Worker 	unsigned int i = 0;
516*a71a9546SAutomerger Merge Worker 
517*a71a9546SAutomerger Merge Worker 	/* Dump: contents of chain index array */
518*a71a9546SAutomerger Merge Worker 	for (i=0; i < h->chain_index_sz; i++) {
519*a71a9546SAutomerger Merge Worker 		if (h->chain_index[i]) {
520*a71a9546SAutomerger Merge Worker 			fprintf(stderr, "Chain index[%d].name: %s\n",
521*a71a9546SAutomerger Merge Worker 				i, h->chain_index[i]->name);
522*a71a9546SAutomerger Merge Worker 		}
523*a71a9546SAutomerger Merge Worker 	}
524*a71a9546SAutomerger Merge Worker }
525*a71a9546SAutomerger Merge Worker #endif
526*a71a9546SAutomerger Merge Worker 
527*a71a9546SAutomerger Merge Worker /* Build the chain index */
iptcc_chain_index_build(struct xtc_handle * h)528*a71a9546SAutomerger Merge Worker static int iptcc_chain_index_build(struct xtc_handle *h)
529*a71a9546SAutomerger Merge Worker {
530*a71a9546SAutomerger Merge Worker 	unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
531*a71a9546SAutomerger Merge Worker 	unsigned int chains = 0;
532*a71a9546SAutomerger Merge Worker 	unsigned int cindex = 0;
533*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
534*a71a9546SAutomerger Merge Worker 
535*a71a9546SAutomerger Merge Worker 	/* Build up the chain index array here */
536*a71a9546SAutomerger Merge Worker 	debug("Building chain index\n");
537*a71a9546SAutomerger Merge Worker 
538*a71a9546SAutomerger Merge Worker 	debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n",
539*a71a9546SAutomerger Merge Worker 		h->num_chains, list_length, h->chain_index_sz);
540*a71a9546SAutomerger Merge Worker 
541*a71a9546SAutomerger Merge Worker 	if (h->chain_index_sz == 0)
542*a71a9546SAutomerger Merge Worker 		return 0;
543*a71a9546SAutomerger Merge Worker 
544*a71a9546SAutomerger Merge Worker 	list_for_each_entry(c, &h->chains, list) {
545*a71a9546SAutomerger Merge Worker 
546*a71a9546SAutomerger Merge Worker 		/* Issue: The index array needs to start after the
547*a71a9546SAutomerger Merge Worker 		 * builtin chains, as they are not sorted */
548*a71a9546SAutomerger Merge Worker 		if (!iptcc_is_builtin(c)) {
549*a71a9546SAutomerger Merge Worker 			cindex=chains / list_length;
550*a71a9546SAutomerger Merge Worker 
551*a71a9546SAutomerger Merge Worker 			/* Safe guard, break out on array limit, this
552*a71a9546SAutomerger Merge Worker 			 * is useful if chains are added and array is
553*a71a9546SAutomerger Merge Worker 			 * rebuild, without realloc of memory. */
554*a71a9546SAutomerger Merge Worker 			if (cindex >= h->chain_index_sz)
555*a71a9546SAutomerger Merge Worker 				break;
556*a71a9546SAutomerger Merge Worker 
557*a71a9546SAutomerger Merge Worker 			if ((chains % list_length)== 0) {
558*a71a9546SAutomerger Merge Worker 				debug("\nIndex[%d] Chains:", cindex);
559*a71a9546SAutomerger Merge Worker 				h->chain_index[cindex] = c;
560*a71a9546SAutomerger Merge Worker 			}
561*a71a9546SAutomerger Merge Worker 			chains++;
562*a71a9546SAutomerger Merge Worker 		}
563*a71a9546SAutomerger Merge Worker 		debug("%s, ", c->name);
564*a71a9546SAutomerger Merge Worker 	}
565*a71a9546SAutomerger Merge Worker 	debug("\n");
566*a71a9546SAutomerger Merge Worker 
567*a71a9546SAutomerger Merge Worker 	return 1;
568*a71a9546SAutomerger Merge Worker }
569*a71a9546SAutomerger Merge Worker 
iptcc_chain_index_rebuild(struct xtc_handle * h)570*a71a9546SAutomerger Merge Worker static int iptcc_chain_index_rebuild(struct xtc_handle *h)
571*a71a9546SAutomerger Merge Worker {
572*a71a9546SAutomerger Merge Worker 	debug("REBUILD chain index array\n");
573*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_free(h);
574*a71a9546SAutomerger Merge Worker 	if ((iptcc_chain_index_alloc(h)) < 0)
575*a71a9546SAutomerger Merge Worker 		return -ENOMEM;
576*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_build(h);
577*a71a9546SAutomerger Merge Worker 	return 1;
578*a71a9546SAutomerger Merge Worker }
579*a71a9546SAutomerger Merge Worker 
580*a71a9546SAutomerger Merge Worker /* Delete chain (pointer) from index array.  Removing an element from
581*a71a9546SAutomerger Merge Worker  * the chain list only affects the chain index array, if the chain
582*a71a9546SAutomerger Merge Worker  * index points-to/uses that list pointer.
583*a71a9546SAutomerger Merge Worker  *
584*a71a9546SAutomerger Merge Worker  * There are different strategies, the simple and safe is to rebuild
585*a71a9546SAutomerger Merge Worker  * the chain index every time.  The more advanced is to update the
586*a71a9546SAutomerger Merge Worker  * array index to point to the next element, but that requires some
587*a71a9546SAutomerger Merge Worker  * house keeping and boundary checks.  The advanced is implemented, as
588*a71a9546SAutomerger Merge Worker  * the simple approach behaves badly when all chains are deleted
589*a71a9546SAutomerger Merge Worker  * because list_for_each processing will always hit the first chain
590*a71a9546SAutomerger Merge Worker  * index, thus causing a rebuild for every chain.
591*a71a9546SAutomerger Merge Worker  */
iptcc_chain_index_delete_chain(struct chain_head * c,struct xtc_handle * h)592*a71a9546SAutomerger Merge Worker static int iptcc_chain_index_delete_chain(struct chain_head *c, struct xtc_handle *h)
593*a71a9546SAutomerger Merge Worker {
594*a71a9546SAutomerger Merge Worker 	struct list_head *index_ptr, *next;
595*a71a9546SAutomerger Merge Worker 	struct chain_head *c2;
596*a71a9546SAutomerger Merge Worker 	unsigned int idx, idx2;
597*a71a9546SAutomerger Merge Worker 
598*a71a9546SAutomerger Merge Worker 	index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h);
599*a71a9546SAutomerger Merge Worker 
600*a71a9546SAutomerger Merge Worker 	debug("Del chain[%s] c->list:%p index_ptr:%p\n",
601*a71a9546SAutomerger Merge Worker 	      c->name, &c->list, index_ptr);
602*a71a9546SAutomerger Merge Worker 
603*a71a9546SAutomerger Merge Worker 	/* Save the next pointer */
604*a71a9546SAutomerger Merge Worker 	next = c->list.next;
605*a71a9546SAutomerger Merge Worker 	list_del(&c->list);
606*a71a9546SAutomerger Merge Worker 
607*a71a9546SAutomerger Merge Worker 	if (index_ptr == &c->list) { /* Chain used as index ptr */
608*a71a9546SAutomerger Merge Worker 
609*a71a9546SAutomerger Merge Worker 		/* If this is the last chain in the list, its index bucket just
610*a71a9546SAutomerger Merge Worker 		 * became empty. Adjust the size to avoid a NULL-pointer deref
611*a71a9546SAutomerger Merge Worker 		 * later.
612*a71a9546SAutomerger Merge Worker 		 */
613*a71a9546SAutomerger Merge Worker 		if (next == &h->chains) {
614*a71a9546SAutomerger Merge Worker 			h->chain_index_sz--;
615*a71a9546SAutomerger Merge Worker 			return 0;
616*a71a9546SAutomerger Merge Worker 		}
617*a71a9546SAutomerger Merge Worker 
618*a71a9546SAutomerger Merge Worker 		/* See if its possible to avoid a rebuild, by shifting
619*a71a9546SAutomerger Merge Worker 		 * to next pointer.  Its possible if the next pointer
620*a71a9546SAutomerger Merge Worker 		 * is located in the same index bucket.
621*a71a9546SAutomerger Merge Worker 		 */
622*a71a9546SAutomerger Merge Worker 		c2         = list_entry(next, struct chain_head, list);
623*a71a9546SAutomerger Merge Worker 		iptcc_bsearch_chain_index(c2->name, &idx2, h);
624*a71a9546SAutomerger Merge Worker 		if (idx != idx2) {
625*a71a9546SAutomerger Merge Worker 			/* Rebuild needed */
626*a71a9546SAutomerger Merge Worker 			return iptcc_chain_index_rebuild(h);
627*a71a9546SAutomerger Merge Worker 		} else {
628*a71a9546SAutomerger Merge Worker 			/* Avoiding rebuild */
629*a71a9546SAutomerger Merge Worker 			debug("Update cindex[%d] with next ptr name:[%s]\n",
630*a71a9546SAutomerger Merge Worker 			      idx, c2->name);
631*a71a9546SAutomerger Merge Worker 			h->chain_index[idx]=c2;
632*a71a9546SAutomerger Merge Worker 			return 0;
633*a71a9546SAutomerger Merge Worker 		}
634*a71a9546SAutomerger Merge Worker 	}
635*a71a9546SAutomerger Merge Worker 	return 0;
636*a71a9546SAutomerger Merge Worker }
637*a71a9546SAutomerger Merge Worker 
638*a71a9546SAutomerger Merge Worker 
639*a71a9546SAutomerger Merge Worker /**********************************************************************
640*a71a9546SAutomerger Merge Worker  * iptc cache utility functions (iptcc_*)
641*a71a9546SAutomerger Merge Worker  **********************************************************************/
642*a71a9546SAutomerger Merge Worker 
643*a71a9546SAutomerger Merge Worker /* Is the given chain builtin (1) or user-defined (0) */
iptcc_is_builtin(struct chain_head * c)644*a71a9546SAutomerger Merge Worker static inline unsigned int iptcc_is_builtin(struct chain_head *c)
645*a71a9546SAutomerger Merge Worker {
646*a71a9546SAutomerger Merge Worker 	return (c->hooknum ? 1 : 0);
647*a71a9546SAutomerger Merge Worker }
648*a71a9546SAutomerger Merge Worker 
649*a71a9546SAutomerger Merge Worker /* Get a specific rule within a chain */
iptcc_get_rule_num(struct chain_head * c,unsigned int rulenum)650*a71a9546SAutomerger Merge Worker static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
651*a71a9546SAutomerger Merge Worker 					    unsigned int rulenum)
652*a71a9546SAutomerger Merge Worker {
653*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
654*a71a9546SAutomerger Merge Worker 	unsigned int num = 0;
655*a71a9546SAutomerger Merge Worker 
656*a71a9546SAutomerger Merge Worker 	list_for_each_entry(r, &c->rules, list) {
657*a71a9546SAutomerger Merge Worker 		num++;
658*a71a9546SAutomerger Merge Worker 		if (num == rulenum)
659*a71a9546SAutomerger Merge Worker 			return r;
660*a71a9546SAutomerger Merge Worker 	}
661*a71a9546SAutomerger Merge Worker 	return NULL;
662*a71a9546SAutomerger Merge Worker }
663*a71a9546SAutomerger Merge Worker 
664*a71a9546SAutomerger Merge Worker /* Get a specific rule within a chain backwards */
iptcc_get_rule_num_reverse(struct chain_head * c,unsigned int rulenum)665*a71a9546SAutomerger Merge Worker static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
666*a71a9546SAutomerger Merge Worker 					    unsigned int rulenum)
667*a71a9546SAutomerger Merge Worker {
668*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
669*a71a9546SAutomerger Merge Worker 	unsigned int num = 0;
670*a71a9546SAutomerger Merge Worker 
671*a71a9546SAutomerger Merge Worker 	list_for_each_entry_reverse(r, &c->rules, list) {
672*a71a9546SAutomerger Merge Worker 		num++;
673*a71a9546SAutomerger Merge Worker 		if (num == rulenum)
674*a71a9546SAutomerger Merge Worker 			return r;
675*a71a9546SAutomerger Merge Worker 	}
676*a71a9546SAutomerger Merge Worker 	return NULL;
677*a71a9546SAutomerger Merge Worker }
678*a71a9546SAutomerger Merge Worker 
679*a71a9546SAutomerger Merge Worker /* Returns chain head if found, otherwise NULL. */
680*a71a9546SAutomerger Merge Worker static struct chain_head *
iptcc_find_chain_by_offset(struct xtc_handle * handle,unsigned int offset)681*a71a9546SAutomerger Merge Worker iptcc_find_chain_by_offset(struct xtc_handle *handle, unsigned int offset)
682*a71a9546SAutomerger Merge Worker {
683*a71a9546SAutomerger Merge Worker 	struct list_head *pos;
684*a71a9546SAutomerger Merge Worker 	struct list_head *list_start_pos;
685*a71a9546SAutomerger Merge Worker 	unsigned int i;
686*a71a9546SAutomerger Merge Worker 
687*a71a9546SAutomerger Merge Worker 	if (list_empty(&handle->chains))
688*a71a9546SAutomerger Merge Worker 		return NULL;
689*a71a9546SAutomerger Merge Worker 
690*a71a9546SAutomerger Merge Worker 	/* Find a smart place to start the search */
691*a71a9546SAutomerger Merge Worker   	list_start_pos = iptcc_bsearch_chain_offset(offset, &i, handle);
692*a71a9546SAutomerger Merge Worker 
693*a71a9546SAutomerger Merge Worker 	/* Note that iptcc_bsearch_chain_offset() skips builtin
694*a71a9546SAutomerger Merge Worker 	 * chains, but this function is only used for finding jump
695*a71a9546SAutomerger Merge Worker 	 * targets, and a buildin chain is not a valid jump target */
696*a71a9546SAutomerger Merge Worker 
697*a71a9546SAutomerger Merge Worker 	debug("Offset:[%u] starting search at index:[%u]\n", offset, i);
698*a71a9546SAutomerger Merge Worker //	list_for_each(pos, &handle->chains) {
699*a71a9546SAutomerger Merge Worker 	list_for_each(pos, list_start_pos->prev) {
700*a71a9546SAutomerger Merge Worker 		struct chain_head *c = list_entry(pos, struct chain_head, list);
701*a71a9546SAutomerger Merge Worker 		debug(".");
702*a71a9546SAutomerger Merge Worker 		if (offset >= c->head_offset && offset <= c->foot_offset) {
703*a71a9546SAutomerger Merge Worker 			debug("Offset search found chain:[%s]\n", c->name);
704*a71a9546SAutomerger Merge Worker 			return c;
705*a71a9546SAutomerger Merge Worker 		}
706*a71a9546SAutomerger Merge Worker 	}
707*a71a9546SAutomerger Merge Worker 
708*a71a9546SAutomerger Merge Worker 	return NULL;
709*a71a9546SAutomerger Merge Worker }
710*a71a9546SAutomerger Merge Worker 
711*a71a9546SAutomerger Merge Worker /* Returns chain head if found, otherwise NULL. */
712*a71a9546SAutomerger Merge Worker static struct chain_head *
iptcc_find_label(const char * name,struct xtc_handle * handle)713*a71a9546SAutomerger Merge Worker iptcc_find_label(const char *name, struct xtc_handle *handle)
714*a71a9546SAutomerger Merge Worker {
715*a71a9546SAutomerger Merge Worker 	struct list_head *pos;
716*a71a9546SAutomerger Merge Worker 	struct list_head *list_start_pos;
717*a71a9546SAutomerger Merge Worker 	unsigned int i=0;
718*a71a9546SAutomerger Merge Worker 	int res;
719*a71a9546SAutomerger Merge Worker 
720*a71a9546SAutomerger Merge Worker 	if (list_empty(&handle->chains))
721*a71a9546SAutomerger Merge Worker 		return NULL;
722*a71a9546SAutomerger Merge Worker 
723*a71a9546SAutomerger Merge Worker 	/* First look at builtin chains */
724*a71a9546SAutomerger Merge Worker 	list_for_each(pos, &handle->chains) {
725*a71a9546SAutomerger Merge Worker 		struct chain_head *c = list_entry(pos, struct chain_head, list);
726*a71a9546SAutomerger Merge Worker 		if (!iptcc_is_builtin(c))
727*a71a9546SAutomerger Merge Worker 			break;
728*a71a9546SAutomerger Merge Worker 		if (!strcmp(c->name, name))
729*a71a9546SAutomerger Merge Worker 			return c;
730*a71a9546SAutomerger Merge Worker 	}
731*a71a9546SAutomerger Merge Worker 
732*a71a9546SAutomerger Merge Worker 	/* Find a smart place to start the search via chain index */
733*a71a9546SAutomerger Merge Worker   	//list_start_pos = iptcc_linearly_search_chain_index(name, handle);
734*a71a9546SAutomerger Merge Worker   	list_start_pos = iptcc_bsearch_chain_index(name, &i, handle);
735*a71a9546SAutomerger Merge Worker 
736*a71a9546SAutomerger Merge Worker 	/* Handel if bsearch bails out early */
737*a71a9546SAutomerger Merge Worker 	if (list_start_pos == &handle->chains) {
738*a71a9546SAutomerger Merge Worker 		list_start_pos = pos;
739*a71a9546SAutomerger Merge Worker 	}
740*a71a9546SAutomerger Merge Worker #ifdef DEBUG
741*a71a9546SAutomerger Merge Worker 	else {
742*a71a9546SAutomerger Merge Worker 		/* Verify result of bsearch against linearly index search */
743*a71a9546SAutomerger Merge Worker 		struct list_head *test_pos;
744*a71a9546SAutomerger Merge Worker 		struct chain_head *test_c, *tmp_c;
745*a71a9546SAutomerger Merge Worker 		test_pos = iptcc_linearly_search_chain_index(name, handle);
746*a71a9546SAutomerger Merge Worker 		if (list_start_pos != test_pos) {
747*a71a9546SAutomerger Merge Worker 			debug("BUG in chain_index search\n");
748*a71a9546SAutomerger Merge Worker 			test_c=list_entry(test_pos,      struct chain_head,list);
749*a71a9546SAutomerger Merge Worker 			tmp_c =list_entry(list_start_pos,struct chain_head,list);
750*a71a9546SAutomerger Merge Worker 			debug("Verify search found:\n");
751*a71a9546SAutomerger Merge Worker 			debug(" Chain:%s\n", test_c->name);
752*a71a9546SAutomerger Merge Worker 			debug("BSearch found:\n");
753*a71a9546SAutomerger Merge Worker 			debug(" Chain:%s\n", tmp_c->name);
754*a71a9546SAutomerger Merge Worker 			exit(42);
755*a71a9546SAutomerger Merge Worker 		}
756*a71a9546SAutomerger Merge Worker 	}
757*a71a9546SAutomerger Merge Worker #endif
758*a71a9546SAutomerger Merge Worker 
759*a71a9546SAutomerger Merge Worker 	/* Initial/special case, no user defined chains */
760*a71a9546SAutomerger Merge Worker 	if (handle->num_chains == 0)
761*a71a9546SAutomerger Merge Worker 		return NULL;
762*a71a9546SAutomerger Merge Worker 
763*a71a9546SAutomerger Merge Worker 	/* Start searching through the chain list */
764*a71a9546SAutomerger Merge Worker 	list_for_each(pos, list_start_pos->prev) {
765*a71a9546SAutomerger Merge Worker 		struct chain_head *c = list_entry(pos, struct chain_head, list);
766*a71a9546SAutomerger Merge Worker 		res = strcmp(c->name, name);
767*a71a9546SAutomerger Merge Worker 		debug("List search name:%s == %s res:%d\n", name, c->name, res);
768*a71a9546SAutomerger Merge Worker 		if (res==0)
769*a71a9546SAutomerger Merge Worker 			return c;
770*a71a9546SAutomerger Merge Worker 
771*a71a9546SAutomerger Merge Worker 		/* We can stop earlier as we know list is sorted */
772*a71a9546SAutomerger Merge Worker 		if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/
773*a71a9546SAutomerger Merge Worker 			debug(" Not in list, walked too far, sorted list\n");
774*a71a9546SAutomerger Merge Worker 			return NULL;
775*a71a9546SAutomerger Merge Worker 		}
776*a71a9546SAutomerger Merge Worker 
777*a71a9546SAutomerger Merge Worker 		/* Stop on wrap around, if list head is reached */
778*a71a9546SAutomerger Merge Worker 		if (pos == &handle->chains) {
779*a71a9546SAutomerger Merge Worker 			debug("Stop, list head reached\n");
780*a71a9546SAutomerger Merge Worker 			return NULL;
781*a71a9546SAutomerger Merge Worker 		}
782*a71a9546SAutomerger Merge Worker 	}
783*a71a9546SAutomerger Merge Worker 
784*a71a9546SAutomerger Merge Worker 	debug("List search NOT found name:%s\n", name);
785*a71a9546SAutomerger Merge Worker 	return NULL;
786*a71a9546SAutomerger Merge Worker }
787*a71a9546SAutomerger Merge Worker 
788*a71a9546SAutomerger Merge Worker /* called when rule is to be removed from cache */
iptcc_delete_rule(struct rule_head * r)789*a71a9546SAutomerger Merge Worker static void iptcc_delete_rule(struct rule_head *r)
790*a71a9546SAutomerger Merge Worker {
791*a71a9546SAutomerger Merge Worker 	DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
792*a71a9546SAutomerger Merge Worker 	/* clean up reference count of called chain */
793*a71a9546SAutomerger Merge Worker 	if (r->type == IPTCC_R_JUMP
794*a71a9546SAutomerger Merge Worker 	    && r->jump)
795*a71a9546SAutomerger Merge Worker 		r->jump->references--;
796*a71a9546SAutomerger Merge Worker 
797*a71a9546SAutomerger Merge Worker 	list_del(&r->list);
798*a71a9546SAutomerger Merge Worker 	free(r);
799*a71a9546SAutomerger Merge Worker }
800*a71a9546SAutomerger Merge Worker 
801*a71a9546SAutomerger Merge Worker 
802*a71a9546SAutomerger Merge Worker /**********************************************************************
803*a71a9546SAutomerger Merge Worker  * RULESET PARSER (blob -> cache)
804*a71a9546SAutomerger Merge Worker  **********************************************************************/
805*a71a9546SAutomerger Merge Worker 
806*a71a9546SAutomerger Merge Worker /* Delete policy rule of previous chain, since cache doesn't contain
807*a71a9546SAutomerger Merge Worker  * chain policy rules.
808*a71a9546SAutomerger Merge Worker  * WARNING: This function has ugly design and relies on a lot of context, only
809*a71a9546SAutomerger Merge Worker  * to be called from specific places within the parser */
__iptcc_p_del_policy(struct xtc_handle * h,unsigned int num)810*a71a9546SAutomerger Merge Worker static int __iptcc_p_del_policy(struct xtc_handle *h, unsigned int num)
811*a71a9546SAutomerger Merge Worker {
812*a71a9546SAutomerger Merge Worker 	const unsigned char *data;
813*a71a9546SAutomerger Merge Worker 
814*a71a9546SAutomerger Merge Worker 	if (h->chain_iterator_cur) {
815*a71a9546SAutomerger Merge Worker 		/* policy rule is last rule */
816*a71a9546SAutomerger Merge Worker 		struct rule_head *pr = (struct rule_head *)
817*a71a9546SAutomerger Merge Worker 			h->chain_iterator_cur->rules.prev;
818*a71a9546SAutomerger Merge Worker 
819*a71a9546SAutomerger Merge Worker 		/* save verdict */
820*a71a9546SAutomerger Merge Worker 		data = GET_TARGET(pr->entry)->data;
821*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->verdict = *(const int *)data;
822*a71a9546SAutomerger Merge Worker 
823*a71a9546SAutomerger Merge Worker 		/* save counter and counter_map information */
824*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->counter_map.maptype =
825*a71a9546SAutomerger Merge Worker 						COUNTER_MAP_NORMAL_MAP;
826*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->counter_map.mappos = num-1;
827*a71a9546SAutomerger Merge Worker 		memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
828*a71a9546SAutomerger Merge Worker 			sizeof(h->chain_iterator_cur->counters));
829*a71a9546SAutomerger Merge Worker 
830*a71a9546SAutomerger Merge Worker 		/* foot_offset points to verdict rule */
831*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->foot_index = num;
832*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->foot_offset = pr->offset;
833*a71a9546SAutomerger Merge Worker 
834*a71a9546SAutomerger Merge Worker 		/* delete rule from cache */
835*a71a9546SAutomerger Merge Worker 		iptcc_delete_rule(pr);
836*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->num_rules--;
837*a71a9546SAutomerger Merge Worker 
838*a71a9546SAutomerger Merge Worker 		return 1;
839*a71a9546SAutomerger Merge Worker 	}
840*a71a9546SAutomerger Merge Worker 	return 0;
841*a71a9546SAutomerger Merge Worker }
842*a71a9546SAutomerger Merge Worker 
843*a71a9546SAutomerger Merge Worker /* alphabetically insert a chain into the list */
iptc_insert_chain(struct xtc_handle * h,struct chain_head * c)844*a71a9546SAutomerger Merge Worker static void iptc_insert_chain(struct xtc_handle *h, struct chain_head *c)
845*a71a9546SAutomerger Merge Worker {
846*a71a9546SAutomerger Merge Worker 	struct chain_head *tmp;
847*a71a9546SAutomerger Merge Worker 	struct list_head  *list_start_pos;
848*a71a9546SAutomerger Merge Worker 	unsigned int i=1;
849*a71a9546SAutomerger Merge Worker 
850*a71a9546SAutomerger Merge Worker 	/* Find a smart place to start the insert search */
851*a71a9546SAutomerger Merge Worker   	list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h);
852*a71a9546SAutomerger Merge Worker 
853*a71a9546SAutomerger Merge Worker 	/* Handle the case, where chain.name is smaller than index[0] */
854*a71a9546SAutomerger Merge Worker 	if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) {
855*a71a9546SAutomerger Merge Worker 		h->chain_index[0] = c; /* Update chain index head */
856*a71a9546SAutomerger Merge Worker 		list_start_pos = h->chains.next;
857*a71a9546SAutomerger Merge Worker 		debug("Update chain_index[0] with %s\n", c->name);
858*a71a9546SAutomerger Merge Worker 	}
859*a71a9546SAutomerger Merge Worker 
860*a71a9546SAutomerger Merge Worker 	/* Handel if bsearch bails out early */
861*a71a9546SAutomerger Merge Worker 	if (list_start_pos == &h->chains) {
862*a71a9546SAutomerger Merge Worker 		list_start_pos = h->chains.next;
863*a71a9546SAutomerger Merge Worker 	}
864*a71a9546SAutomerger Merge Worker 
865*a71a9546SAutomerger Merge Worker 	/* sort only user defined chains */
866*a71a9546SAutomerger Merge Worker 	if (!c->hooknum) {
867*a71a9546SAutomerger Merge Worker 		list_for_each_entry(tmp, list_start_pos->prev, list) {
868*a71a9546SAutomerger Merge Worker 			if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
869*a71a9546SAutomerger Merge Worker 				list_add(&c->list, tmp->list.prev);
870*a71a9546SAutomerger Merge Worker 				return;
871*a71a9546SAutomerger Merge Worker 			}
872*a71a9546SAutomerger Merge Worker 
873*a71a9546SAutomerger Merge Worker 			/* Stop if list head is reached */
874*a71a9546SAutomerger Merge Worker 			if (&tmp->list == &h->chains) {
875*a71a9546SAutomerger Merge Worker 				debug("Insert, list head reached add to tail\n");
876*a71a9546SAutomerger Merge Worker 				break;
877*a71a9546SAutomerger Merge Worker 			}
878*a71a9546SAutomerger Merge Worker 		}
879*a71a9546SAutomerger Merge Worker 	}
880*a71a9546SAutomerger Merge Worker 
881*a71a9546SAutomerger Merge Worker 	/* survived till end of list: add at tail */
882*a71a9546SAutomerger Merge Worker 	list_add_tail(&c->list, &h->chains);
883*a71a9546SAutomerger Merge Worker }
884*a71a9546SAutomerger Merge Worker 
885*a71a9546SAutomerger Merge Worker /* Another ugly helper function split out of cache_add_entry to make it less
886*a71a9546SAutomerger Merge Worker  * spaghetti code */
__iptcc_p_add_chain(struct xtc_handle * h,struct chain_head * c,unsigned int offset,unsigned int * num)887*a71a9546SAutomerger Merge Worker static void __iptcc_p_add_chain(struct xtc_handle *h, struct chain_head *c,
888*a71a9546SAutomerger Merge Worker 				unsigned int offset, unsigned int *num)
889*a71a9546SAutomerger Merge Worker {
890*a71a9546SAutomerger Merge Worker 	struct list_head  *tail = h->chains.prev;
891*a71a9546SAutomerger Merge Worker 	struct chain_head *ctail;
892*a71a9546SAutomerger Merge Worker 
893*a71a9546SAutomerger Merge Worker 	__iptcc_p_del_policy(h, *num);
894*a71a9546SAutomerger Merge Worker 
895*a71a9546SAutomerger Merge Worker 	c->head_offset = offset;
896*a71a9546SAutomerger Merge Worker 	c->index = *num;
897*a71a9546SAutomerger Merge Worker 
898*a71a9546SAutomerger Merge Worker 	/* Chains from kernel are already sorted, as they are inserted
899*a71a9546SAutomerger Merge Worker 	 * sorted. But there exists an issue when shifting to 1.4.0
900*a71a9546SAutomerger Merge Worker 	 * from an older version, as old versions allow last created
901*a71a9546SAutomerger Merge Worker 	 * chain to be unsorted.
902*a71a9546SAutomerger Merge Worker 	 */
903*a71a9546SAutomerger Merge Worker 	if (iptcc_is_builtin(c)) /* Only user defined chains are sorted*/
904*a71a9546SAutomerger Merge Worker 		list_add_tail(&c->list, &h->chains);
905*a71a9546SAutomerger Merge Worker 	else {
906*a71a9546SAutomerger Merge Worker 		ctail = list_entry(tail, struct chain_head, list);
907*a71a9546SAutomerger Merge Worker 
908*a71a9546SAutomerger Merge Worker 		if (strcmp(c->name, ctail->name) > 0 ||
909*a71a9546SAutomerger Merge Worker 		    iptcc_is_builtin(ctail))
910*a71a9546SAutomerger Merge Worker 			list_add_tail(&c->list, &h->chains);/* Already sorted*/
911*a71a9546SAutomerger Merge Worker 		else {
912*a71a9546SAutomerger Merge Worker 			iptc_insert_chain(h, c);/* Was not sorted */
913*a71a9546SAutomerger Merge Worker 
914*a71a9546SAutomerger Merge Worker 			/* Notice, if chains were not received sorted
915*a71a9546SAutomerger Merge Worker 			 * from kernel, then an offset bsearch is no
916*a71a9546SAutomerger Merge Worker 			 * longer valid.
917*a71a9546SAutomerger Merge Worker 			 */
918*a71a9546SAutomerger Merge Worker 			h->sorted_offsets = 0;
919*a71a9546SAutomerger Merge Worker 
920*a71a9546SAutomerger Merge Worker 			debug("NOTICE: chain:[%s] was NOT sorted(ctail:%s)\n",
921*a71a9546SAutomerger Merge Worker 			      c->name, ctail->name);
922*a71a9546SAutomerger Merge Worker 		}
923*a71a9546SAutomerger Merge Worker 	}
924*a71a9546SAutomerger Merge Worker 
925*a71a9546SAutomerger Merge Worker 	h->chain_iterator_cur = c;
926*a71a9546SAutomerger Merge Worker }
927*a71a9546SAutomerger Merge Worker 
928*a71a9546SAutomerger Merge Worker /* main parser function: add an entry from the blob to the cache */
cache_add_entry(STRUCT_ENTRY * e,struct xtc_handle * h,STRUCT_ENTRY ** prev,unsigned int * num)929*a71a9546SAutomerger Merge Worker static int cache_add_entry(STRUCT_ENTRY *e,
930*a71a9546SAutomerger Merge Worker 			   struct xtc_handle *h,
931*a71a9546SAutomerger Merge Worker 			   STRUCT_ENTRY **prev,
932*a71a9546SAutomerger Merge Worker 			   unsigned int *num)
933*a71a9546SAutomerger Merge Worker {
934*a71a9546SAutomerger Merge Worker 	unsigned int builtin;
935*a71a9546SAutomerger Merge Worker 	unsigned int offset = (char *)e - (char *)h->entries->entrytable;
936*a71a9546SAutomerger Merge Worker 
937*a71a9546SAutomerger Merge Worker 	DEBUGP("entering...");
938*a71a9546SAutomerger Merge Worker 
939*a71a9546SAutomerger Merge Worker 	/* Last entry ("policy rule"). End it.*/
940*a71a9546SAutomerger Merge Worker 	if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
941*a71a9546SAutomerger Merge Worker 		/* This is the ERROR node at the end of the chain */
942*a71a9546SAutomerger Merge Worker 		DEBUGP_C("%u:%u: end of table:\n", *num, offset);
943*a71a9546SAutomerger Merge Worker 
944*a71a9546SAutomerger Merge Worker 		__iptcc_p_del_policy(h, *num);
945*a71a9546SAutomerger Merge Worker 
946*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur = NULL;
947*a71a9546SAutomerger Merge Worker 		goto out_inc;
948*a71a9546SAutomerger Merge Worker 	}
949*a71a9546SAutomerger Merge Worker 
950*a71a9546SAutomerger Merge Worker 	/* We know this is the start of a new chain if it's an ERROR
951*a71a9546SAutomerger Merge Worker 	 * target, or a hook entry point */
952*a71a9546SAutomerger Merge Worker 
953*a71a9546SAutomerger Merge Worker 	if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
954*a71a9546SAutomerger Merge Worker 		struct chain_head *c =
955*a71a9546SAutomerger Merge Worker 			iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
956*a71a9546SAutomerger Merge Worker 		DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
957*a71a9546SAutomerger Merge Worker 			(char *)c->name, c);
958*a71a9546SAutomerger Merge Worker 		if (!c) {
959*a71a9546SAutomerger Merge Worker 			errno = -ENOMEM;
960*a71a9546SAutomerger Merge Worker 			return -1;
961*a71a9546SAutomerger Merge Worker 		}
962*a71a9546SAutomerger Merge Worker 		h->num_chains++; /* New user defined chain */
963*a71a9546SAutomerger Merge Worker 
964*a71a9546SAutomerger Merge Worker 		__iptcc_p_add_chain(h, c, offset, num);
965*a71a9546SAutomerger Merge Worker 
966*a71a9546SAutomerger Merge Worker 	} else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
967*a71a9546SAutomerger Merge Worker 		struct chain_head *c =
968*a71a9546SAutomerger Merge Worker 			iptcc_alloc_chain_head((char *)hooknames[builtin-1],
969*a71a9546SAutomerger Merge Worker 						builtin);
970*a71a9546SAutomerger Merge Worker 		DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
971*a71a9546SAutomerger Merge Worker 			*num, offset, c, &c->rules);
972*a71a9546SAutomerger Merge Worker 		if (!c) {
973*a71a9546SAutomerger Merge Worker 			errno = -ENOMEM;
974*a71a9546SAutomerger Merge Worker 			return -1;
975*a71a9546SAutomerger Merge Worker 		}
976*a71a9546SAutomerger Merge Worker 
977*a71a9546SAutomerger Merge Worker 		c->hooknum = builtin;
978*a71a9546SAutomerger Merge Worker 
979*a71a9546SAutomerger Merge Worker 		__iptcc_p_add_chain(h, c, offset, num);
980*a71a9546SAutomerger Merge Worker 
981*a71a9546SAutomerger Merge Worker 		/* FIXME: this is ugly. */
982*a71a9546SAutomerger Merge Worker 		goto new_rule;
983*a71a9546SAutomerger Merge Worker 	} else {
984*a71a9546SAutomerger Merge Worker 		/* has to be normal rule */
985*a71a9546SAutomerger Merge Worker 		struct rule_head *r;
986*a71a9546SAutomerger Merge Worker new_rule:
987*a71a9546SAutomerger Merge Worker 
988*a71a9546SAutomerger Merge Worker 		if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
989*a71a9546SAutomerger Merge Worker 					   e->next_offset))) {
990*a71a9546SAutomerger Merge Worker 			errno = ENOMEM;
991*a71a9546SAutomerger Merge Worker 			return -1;
992*a71a9546SAutomerger Merge Worker 		}
993*a71a9546SAutomerger Merge Worker 		DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
994*a71a9546SAutomerger Merge Worker 
995*a71a9546SAutomerger Merge Worker 		r->index = *num;
996*a71a9546SAutomerger Merge Worker 		r->offset = offset;
997*a71a9546SAutomerger Merge Worker 		memcpy(r->entry, e, e->next_offset);
998*a71a9546SAutomerger Merge Worker 		r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
999*a71a9546SAutomerger Merge Worker 		r->counter_map.mappos = r->index;
1000*a71a9546SAutomerger Merge Worker 
1001*a71a9546SAutomerger Merge Worker 		/* handling of jumps, etc. */
1002*a71a9546SAutomerger Merge Worker 		if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
1003*a71a9546SAutomerger Merge Worker 			STRUCT_STANDARD_TARGET *t;
1004*a71a9546SAutomerger Merge Worker 
1005*a71a9546SAutomerger Merge Worker 			t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1006*a71a9546SAutomerger Merge Worker 			if (t->target.u.target_size
1007*a71a9546SAutomerger Merge Worker 			    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
1008*a71a9546SAutomerger Merge Worker 				errno = EINVAL;
1009*a71a9546SAutomerger Merge Worker 				free(r);
1010*a71a9546SAutomerger Merge Worker 				return -1;
1011*a71a9546SAutomerger Merge Worker 			}
1012*a71a9546SAutomerger Merge Worker 
1013*a71a9546SAutomerger Merge Worker 			if (t->verdict < 0) {
1014*a71a9546SAutomerger Merge Worker 				DEBUGP_C("standard, verdict=%d\n", t->verdict);
1015*a71a9546SAutomerger Merge Worker 				r->type = IPTCC_R_STANDARD;
1016*a71a9546SAutomerger Merge Worker 			} else if (t->verdict == r->offset+e->next_offset) {
1017*a71a9546SAutomerger Merge Worker 				DEBUGP_C("fallthrough\n");
1018*a71a9546SAutomerger Merge Worker 				r->type = IPTCC_R_FALLTHROUGH;
1019*a71a9546SAutomerger Merge Worker 			} else {
1020*a71a9546SAutomerger Merge Worker 				DEBUGP_C("jump, target=%u\n", t->verdict);
1021*a71a9546SAutomerger Merge Worker 				r->type = IPTCC_R_JUMP;
1022*a71a9546SAutomerger Merge Worker 				/* Jump target fixup has to be deferred
1023*a71a9546SAutomerger Merge Worker 				 * until second pass, since we migh not
1024*a71a9546SAutomerger Merge Worker 				 * yet have parsed the target */
1025*a71a9546SAutomerger Merge Worker 			}
1026*a71a9546SAutomerger Merge Worker 		} else {
1027*a71a9546SAutomerger Merge Worker 			DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
1028*a71a9546SAutomerger Merge Worker 			r->type = IPTCC_R_MODULE;
1029*a71a9546SAutomerger Merge Worker 		}
1030*a71a9546SAutomerger Merge Worker 
1031*a71a9546SAutomerger Merge Worker 		list_add_tail(&r->list, &h->chain_iterator_cur->rules);
1032*a71a9546SAutomerger Merge Worker 		h->chain_iterator_cur->num_rules++;
1033*a71a9546SAutomerger Merge Worker 	}
1034*a71a9546SAutomerger Merge Worker out_inc:
1035*a71a9546SAutomerger Merge Worker 	(*num)++;
1036*a71a9546SAutomerger Merge Worker 	return 0;
1037*a71a9546SAutomerger Merge Worker }
1038*a71a9546SAutomerger Merge Worker 
1039*a71a9546SAutomerger Merge Worker 
1040*a71a9546SAutomerger Merge Worker /* parse an iptables blob into it's pieces */
parse_table(struct xtc_handle * h)1041*a71a9546SAutomerger Merge Worker static int parse_table(struct xtc_handle *h)
1042*a71a9546SAutomerger Merge Worker {
1043*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY *prev;
1044*a71a9546SAutomerger Merge Worker 	unsigned int num = 0;
1045*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1046*a71a9546SAutomerger Merge Worker 
1047*a71a9546SAutomerger Merge Worker 	/* Assume that chains offsets are sorted, this verified during
1048*a71a9546SAutomerger Merge Worker 	   parsing of ruleset (in __iptcc_p_add_chain())*/
1049*a71a9546SAutomerger Merge Worker 	h->sorted_offsets = 1;
1050*a71a9546SAutomerger Merge Worker 
1051*a71a9546SAutomerger Merge Worker 	/* First pass: over ruleset blob */
1052*a71a9546SAutomerger Merge Worker 	ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
1053*a71a9546SAutomerger Merge Worker 			cache_add_entry, h, &prev, &num);
1054*a71a9546SAutomerger Merge Worker 
1055*a71a9546SAutomerger Merge Worker 	/* Build the chain index, used for chain list search speedup */
1056*a71a9546SAutomerger Merge Worker 	if ((iptcc_chain_index_alloc(h)) < 0)
1057*a71a9546SAutomerger Merge Worker 		return -ENOMEM;
1058*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_build(h);
1059*a71a9546SAutomerger Merge Worker 
1060*a71a9546SAutomerger Merge Worker 	/* Second pass: fixup parsed data from first pass */
1061*a71a9546SAutomerger Merge Worker 	list_for_each_entry(c, &h->chains, list) {
1062*a71a9546SAutomerger Merge Worker 		struct rule_head *r;
1063*a71a9546SAutomerger Merge Worker 		list_for_each_entry(r, &c->rules, list) {
1064*a71a9546SAutomerger Merge Worker 			struct chain_head *lc;
1065*a71a9546SAutomerger Merge Worker 			STRUCT_STANDARD_TARGET *t;
1066*a71a9546SAutomerger Merge Worker 
1067*a71a9546SAutomerger Merge Worker 			if (r->type != IPTCC_R_JUMP)
1068*a71a9546SAutomerger Merge Worker 				continue;
1069*a71a9546SAutomerger Merge Worker 
1070*a71a9546SAutomerger Merge Worker 			t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
1071*a71a9546SAutomerger Merge Worker 			lc = iptcc_find_chain_by_offset(h, t->verdict);
1072*a71a9546SAutomerger Merge Worker 			if (!lc)
1073*a71a9546SAutomerger Merge Worker 				return -1;
1074*a71a9546SAutomerger Merge Worker 			r->jump = lc;
1075*a71a9546SAutomerger Merge Worker 			lc->references++;
1076*a71a9546SAutomerger Merge Worker 		}
1077*a71a9546SAutomerger Merge Worker 	}
1078*a71a9546SAutomerger Merge Worker 
1079*a71a9546SAutomerger Merge Worker 	return 1;
1080*a71a9546SAutomerger Merge Worker }
1081*a71a9546SAutomerger Merge Worker 
1082*a71a9546SAutomerger Merge Worker 
1083*a71a9546SAutomerger Merge Worker /**********************************************************************
1084*a71a9546SAutomerger Merge Worker  * RULESET COMPILATION (cache -> blob)
1085*a71a9546SAutomerger Merge Worker  **********************************************************************/
1086*a71a9546SAutomerger Merge Worker 
1087*a71a9546SAutomerger Merge Worker /* Convenience structures */
1088*a71a9546SAutomerger Merge Worker struct iptcb_chain_start{
1089*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY e;
1090*a71a9546SAutomerger Merge Worker 	struct xt_error_target name;
1091*a71a9546SAutomerger Merge Worker };
1092*a71a9546SAutomerger Merge Worker #define IPTCB_CHAIN_START_SIZE	(sizeof(STRUCT_ENTRY) +			\
1093*a71a9546SAutomerger Merge Worker 				 ALIGN(sizeof(struct xt_error_target)))
1094*a71a9546SAutomerger Merge Worker 
1095*a71a9546SAutomerger Merge Worker struct iptcb_chain_foot {
1096*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY e;
1097*a71a9546SAutomerger Merge Worker 	STRUCT_STANDARD_TARGET target;
1098*a71a9546SAutomerger Merge Worker };
1099*a71a9546SAutomerger Merge Worker #define IPTCB_CHAIN_FOOT_SIZE	(sizeof(STRUCT_ENTRY) +			\
1100*a71a9546SAutomerger Merge Worker 				 ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
1101*a71a9546SAutomerger Merge Worker 
1102*a71a9546SAutomerger Merge Worker struct iptcb_chain_error {
1103*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY entry;
1104*a71a9546SAutomerger Merge Worker 	struct xt_error_target target;
1105*a71a9546SAutomerger Merge Worker };
1106*a71a9546SAutomerger Merge Worker #define IPTCB_CHAIN_ERROR_SIZE	(sizeof(STRUCT_ENTRY) +			\
1107*a71a9546SAutomerger Merge Worker 				 ALIGN(sizeof(struct xt_error_target)))
1108*a71a9546SAutomerger Merge Worker 
1109*a71a9546SAutomerger Merge Worker 
1110*a71a9546SAutomerger Merge Worker 
1111*a71a9546SAutomerger Merge Worker /* compile rule from cache into blob */
iptcc_compile_rule(struct xtc_handle * h,STRUCT_REPLACE * repl,struct rule_head * r)1112*a71a9546SAutomerger Merge Worker static inline int iptcc_compile_rule (struct xtc_handle *h, STRUCT_REPLACE *repl, struct rule_head *r)
1113*a71a9546SAutomerger Merge Worker {
1114*a71a9546SAutomerger Merge Worker 	/* handle jumps */
1115*a71a9546SAutomerger Merge Worker 	if (r->type == IPTCC_R_JUMP) {
1116*a71a9546SAutomerger Merge Worker 		STRUCT_STANDARD_TARGET *t;
1117*a71a9546SAutomerger Merge Worker 		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
1118*a71a9546SAutomerger Merge Worker 		/* memset for memcmp convenience on delete/replace */
1119*a71a9546SAutomerger Merge Worker 		memset(t->target.u.user.name, 0, XT_EXTENSION_MAXNAMELEN);
1120*a71a9546SAutomerger Merge Worker 		strcpy(t->target.u.user.name, STANDARD_TARGET);
1121*a71a9546SAutomerger Merge Worker 		t->target.u.user.revision = 0;
1122*a71a9546SAutomerger Merge Worker 		/* Jumps can only happen to builtin chains, so we
1123*a71a9546SAutomerger Merge Worker 		 * can safely assume that they always have a header */
1124*a71a9546SAutomerger Merge Worker 		t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
1125*a71a9546SAutomerger Merge Worker 	} else if (r->type == IPTCC_R_FALLTHROUGH) {
1126*a71a9546SAutomerger Merge Worker 		STRUCT_STANDARD_TARGET *t;
1127*a71a9546SAutomerger Merge Worker 		t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
1128*a71a9546SAutomerger Merge Worker 		t->verdict = r->offset + r->size;
1129*a71a9546SAutomerger Merge Worker 	}
1130*a71a9546SAutomerger Merge Worker 
1131*a71a9546SAutomerger Merge Worker 	/* copy entry from cache to blob */
1132*a71a9546SAutomerger Merge Worker 	memcpy((char *)repl->entries+r->offset, r->entry, r->size);
1133*a71a9546SAutomerger Merge Worker 
1134*a71a9546SAutomerger Merge Worker 	return 1;
1135*a71a9546SAutomerger Merge Worker }
1136*a71a9546SAutomerger Merge Worker 
1137*a71a9546SAutomerger Merge Worker /* compile chain from cache into blob */
iptcc_compile_chain(struct xtc_handle * h,STRUCT_REPLACE * repl,struct chain_head * c)1138*a71a9546SAutomerger Merge Worker static int iptcc_compile_chain(struct xtc_handle *h, STRUCT_REPLACE *repl, struct chain_head *c)
1139*a71a9546SAutomerger Merge Worker {
1140*a71a9546SAutomerger Merge Worker 	int ret;
1141*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1142*a71a9546SAutomerger Merge Worker 	struct iptcb_chain_start *head;
1143*a71a9546SAutomerger Merge Worker 	struct iptcb_chain_foot *foot;
1144*a71a9546SAutomerger Merge Worker 
1145*a71a9546SAutomerger Merge Worker 	/* only user-defined chains have heaer */
1146*a71a9546SAutomerger Merge Worker 	if (!iptcc_is_builtin(c)) {
1147*a71a9546SAutomerger Merge Worker 		/* put chain header in place */
1148*a71a9546SAutomerger Merge Worker 		head = (void *)repl->entries + c->head_offset;
1149*a71a9546SAutomerger Merge Worker 		head->e.target_offset = sizeof(STRUCT_ENTRY);
1150*a71a9546SAutomerger Merge Worker 		head->e.next_offset = IPTCB_CHAIN_START_SIZE;
1151*a71a9546SAutomerger Merge Worker 		strcpy(head->name.target.u.user.name, ERROR_TARGET);
1152*a71a9546SAutomerger Merge Worker 		head->name.target.u.target_size =
1153*a71a9546SAutomerger Merge Worker 				ALIGN(sizeof(struct xt_error_target));
1154*a71a9546SAutomerger Merge Worker 		strncpy(head->name.errorname, c->name, XT_FUNCTION_MAXNAMELEN);
1155*a71a9546SAutomerger Merge Worker 		head->name.errorname[XT_FUNCTION_MAXNAMELEN - 1] = '\0';
1156*a71a9546SAutomerger Merge Worker 	} else {
1157*a71a9546SAutomerger Merge Worker 		repl->hook_entry[c->hooknum-1] = c->head_offset;
1158*a71a9546SAutomerger Merge Worker 		repl->underflow[c->hooknum-1] = c->foot_offset;
1159*a71a9546SAutomerger Merge Worker 	}
1160*a71a9546SAutomerger Merge Worker 
1161*a71a9546SAutomerger Merge Worker 	/* iterate over rules */
1162*a71a9546SAutomerger Merge Worker 	list_for_each_entry(r, &c->rules, list) {
1163*a71a9546SAutomerger Merge Worker 		ret = iptcc_compile_rule(h, repl, r);
1164*a71a9546SAutomerger Merge Worker 		if (ret < 0)
1165*a71a9546SAutomerger Merge Worker 			return ret;
1166*a71a9546SAutomerger Merge Worker 	}
1167*a71a9546SAutomerger Merge Worker 
1168*a71a9546SAutomerger Merge Worker 	/* put chain footer in place */
1169*a71a9546SAutomerger Merge Worker 	foot = (void *)repl->entries + c->foot_offset;
1170*a71a9546SAutomerger Merge Worker 	foot->e.target_offset = sizeof(STRUCT_ENTRY);
1171*a71a9546SAutomerger Merge Worker 	foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
1172*a71a9546SAutomerger Merge Worker 	strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
1173*a71a9546SAutomerger Merge Worker 	foot->target.target.u.target_size =
1174*a71a9546SAutomerger Merge Worker 				ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1175*a71a9546SAutomerger Merge Worker 	/* builtin targets have verdict, others return */
1176*a71a9546SAutomerger Merge Worker 	if (iptcc_is_builtin(c))
1177*a71a9546SAutomerger Merge Worker 		foot->target.verdict = c->verdict;
1178*a71a9546SAutomerger Merge Worker 	else
1179*a71a9546SAutomerger Merge Worker 		foot->target.verdict = RETURN;
1180*a71a9546SAutomerger Merge Worker 	/* set policy-counters */
1181*a71a9546SAutomerger Merge Worker 	foot->e.counters = c->counters;
1182*a71a9546SAutomerger Merge Worker 
1183*a71a9546SAutomerger Merge Worker 	return 0;
1184*a71a9546SAutomerger Merge Worker }
1185*a71a9546SAutomerger Merge Worker 
1186*a71a9546SAutomerger Merge Worker /* calculate offset and number for every rule in the cache */
iptcc_compile_chain_offsets(struct xtc_handle * h,struct chain_head * c,unsigned int * offset,unsigned int * num)1187*a71a9546SAutomerger Merge Worker static int iptcc_compile_chain_offsets(struct xtc_handle *h, struct chain_head *c,
1188*a71a9546SAutomerger Merge Worker 				       unsigned int *offset, unsigned int *num)
1189*a71a9546SAutomerger Merge Worker {
1190*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1191*a71a9546SAutomerger Merge Worker 
1192*a71a9546SAutomerger Merge Worker 	c->head_offset = *offset;
1193*a71a9546SAutomerger Merge Worker 	DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
1194*a71a9546SAutomerger Merge Worker 
1195*a71a9546SAutomerger Merge Worker 	if (!iptcc_is_builtin(c))  {
1196*a71a9546SAutomerger Merge Worker 		/* Chain has header */
1197*a71a9546SAutomerger Merge Worker 		*offset += sizeof(STRUCT_ENTRY)
1198*a71a9546SAutomerger Merge Worker 			     + ALIGN(sizeof(struct xt_error_target));
1199*a71a9546SAutomerger Merge Worker 		(*num)++;
1200*a71a9546SAutomerger Merge Worker 	}
1201*a71a9546SAutomerger Merge Worker 
1202*a71a9546SAutomerger Merge Worker 	list_for_each_entry(r, &c->rules, list) {
1203*a71a9546SAutomerger Merge Worker 		DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
1204*a71a9546SAutomerger Merge Worker 		r->offset = *offset;
1205*a71a9546SAutomerger Merge Worker 		r->index = *num;
1206*a71a9546SAutomerger Merge Worker 		*offset += r->size;
1207*a71a9546SAutomerger Merge Worker 		(*num)++;
1208*a71a9546SAutomerger Merge Worker 	}
1209*a71a9546SAutomerger Merge Worker 
1210*a71a9546SAutomerger Merge Worker 	DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
1211*a71a9546SAutomerger Merge Worker 		*offset, *num);
1212*a71a9546SAutomerger Merge Worker 	c->foot_offset = *offset;
1213*a71a9546SAutomerger Merge Worker 	c->foot_index = *num;
1214*a71a9546SAutomerger Merge Worker 	*offset += sizeof(STRUCT_ENTRY)
1215*a71a9546SAutomerger Merge Worker 		   + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1216*a71a9546SAutomerger Merge Worker 	(*num)++;
1217*a71a9546SAutomerger Merge Worker 
1218*a71a9546SAutomerger Merge Worker 	return 1;
1219*a71a9546SAutomerger Merge Worker }
1220*a71a9546SAutomerger Merge Worker 
1221*a71a9546SAutomerger Merge Worker /* put the pieces back together again */
iptcc_compile_table_prep(struct xtc_handle * h,unsigned int * size)1222*a71a9546SAutomerger Merge Worker static int iptcc_compile_table_prep(struct xtc_handle *h, unsigned int *size)
1223*a71a9546SAutomerger Merge Worker {
1224*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1225*a71a9546SAutomerger Merge Worker 	unsigned int offset = 0, num = 0;
1226*a71a9546SAutomerger Merge Worker 	int ret = 0;
1227*a71a9546SAutomerger Merge Worker 
1228*a71a9546SAutomerger Merge Worker 	/* First pass: calculate offset for every rule */
1229*a71a9546SAutomerger Merge Worker 	list_for_each_entry(c, &h->chains, list) {
1230*a71a9546SAutomerger Merge Worker 		ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
1231*a71a9546SAutomerger Merge Worker 		if (ret < 0)
1232*a71a9546SAutomerger Merge Worker 			return ret;
1233*a71a9546SAutomerger Merge Worker 	}
1234*a71a9546SAutomerger Merge Worker 
1235*a71a9546SAutomerger Merge Worker 	/* Append one error rule at end of chain */
1236*a71a9546SAutomerger Merge Worker 	num++;
1237*a71a9546SAutomerger Merge Worker 	offset += sizeof(STRUCT_ENTRY)
1238*a71a9546SAutomerger Merge Worker 		  + ALIGN(sizeof(struct xt_error_target));
1239*a71a9546SAutomerger Merge Worker 
1240*a71a9546SAutomerger Merge Worker 	/* ruleset size is now in offset */
1241*a71a9546SAutomerger Merge Worker 	*size = offset;
1242*a71a9546SAutomerger Merge Worker 	return num;
1243*a71a9546SAutomerger Merge Worker }
1244*a71a9546SAutomerger Merge Worker 
iptcc_compile_table(struct xtc_handle * h,STRUCT_REPLACE * repl)1245*a71a9546SAutomerger Merge Worker static int iptcc_compile_table(struct xtc_handle *h, STRUCT_REPLACE *repl)
1246*a71a9546SAutomerger Merge Worker {
1247*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1248*a71a9546SAutomerger Merge Worker 	struct iptcb_chain_error *error;
1249*a71a9546SAutomerger Merge Worker 
1250*a71a9546SAutomerger Merge Worker 	/* Second pass: copy from cache to offsets, fill in jumps */
1251*a71a9546SAutomerger Merge Worker 	list_for_each_entry(c, &h->chains, list) {
1252*a71a9546SAutomerger Merge Worker 		int ret = iptcc_compile_chain(h, repl, c);
1253*a71a9546SAutomerger Merge Worker 		if (ret < 0)
1254*a71a9546SAutomerger Merge Worker 			return ret;
1255*a71a9546SAutomerger Merge Worker 	}
1256*a71a9546SAutomerger Merge Worker 
1257*a71a9546SAutomerger Merge Worker 	/* Append error rule at end of chain */
1258*a71a9546SAutomerger Merge Worker 	error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
1259*a71a9546SAutomerger Merge Worker 	error->entry.target_offset = sizeof(STRUCT_ENTRY);
1260*a71a9546SAutomerger Merge Worker 	error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
1261*a71a9546SAutomerger Merge Worker 	error->target.target.u.user.target_size =
1262*a71a9546SAutomerger Merge Worker 		ALIGN(sizeof(struct xt_error_target));
1263*a71a9546SAutomerger Merge Worker 	strcpy((char *)&error->target.target.u.user.name, ERROR_TARGET);
1264*a71a9546SAutomerger Merge Worker 	strcpy((char *)&error->target.errorname, "ERROR");
1265*a71a9546SAutomerger Merge Worker 
1266*a71a9546SAutomerger Merge Worker 	return 1;
1267*a71a9546SAutomerger Merge Worker }
1268*a71a9546SAutomerger Merge Worker 
1269*a71a9546SAutomerger Merge Worker /**********************************************************************
1270*a71a9546SAutomerger Merge Worker  * EXTERNAL API (operates on cache only)
1271*a71a9546SAutomerger Merge Worker  **********************************************************************/
1272*a71a9546SAutomerger Merge Worker 
1273*a71a9546SAutomerger Merge Worker /* Allocate handle of given size */
1274*a71a9546SAutomerger Merge Worker static struct xtc_handle *
alloc_handle(STRUCT_GETINFO * infop)1275*a71a9546SAutomerger Merge Worker alloc_handle(STRUCT_GETINFO *infop)
1276*a71a9546SAutomerger Merge Worker {
1277*a71a9546SAutomerger Merge Worker 	struct xtc_handle *h;
1278*a71a9546SAutomerger Merge Worker 
1279*a71a9546SAutomerger Merge Worker 	h = malloc(sizeof(*h));
1280*a71a9546SAutomerger Merge Worker 	if (!h) {
1281*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
1282*a71a9546SAutomerger Merge Worker 		return NULL;
1283*a71a9546SAutomerger Merge Worker 	}
1284*a71a9546SAutomerger Merge Worker 	memset(h, 0, sizeof(*h));
1285*a71a9546SAutomerger Merge Worker 	INIT_LIST_HEAD(&h->chains);
1286*a71a9546SAutomerger Merge Worker 	strcpy(h->info.name, infop->name);
1287*a71a9546SAutomerger Merge Worker 
1288*a71a9546SAutomerger Merge Worker 	h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + infop->size);
1289*a71a9546SAutomerger Merge Worker 	if (!h->entries)
1290*a71a9546SAutomerger Merge Worker 		goto out_free_handle;
1291*a71a9546SAutomerger Merge Worker 
1292*a71a9546SAutomerger Merge Worker 	strcpy(h->entries->name, infop->name);
1293*a71a9546SAutomerger Merge Worker 	h->entries->size = infop->size;
1294*a71a9546SAutomerger Merge Worker 
1295*a71a9546SAutomerger Merge Worker 	return h;
1296*a71a9546SAutomerger Merge Worker 
1297*a71a9546SAutomerger Merge Worker out_free_handle:
1298*a71a9546SAutomerger Merge Worker 	free(h);
1299*a71a9546SAutomerger Merge Worker 
1300*a71a9546SAutomerger Merge Worker 	return NULL;
1301*a71a9546SAutomerger Merge Worker }
1302*a71a9546SAutomerger Merge Worker 
1303*a71a9546SAutomerger Merge Worker 
1304*a71a9546SAutomerger Merge Worker struct xtc_handle *
TC_INIT(const char * tablename)1305*a71a9546SAutomerger Merge Worker TC_INIT(const char *tablename)
1306*a71a9546SAutomerger Merge Worker {
1307*a71a9546SAutomerger Merge Worker 	struct xtc_handle *h;
1308*a71a9546SAutomerger Merge Worker 	STRUCT_GETINFO info;
1309*a71a9546SAutomerger Merge Worker 	unsigned int tmp;
1310*a71a9546SAutomerger Merge Worker 	socklen_t s;
1311*a71a9546SAutomerger Merge Worker 	int sockfd;
1312*a71a9546SAutomerger Merge Worker 
1313*a71a9546SAutomerger Merge Worker retry:
1314*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_INIT;
1315*a71a9546SAutomerger Merge Worker 
1316*a71a9546SAutomerger Merge Worker 	if (strlen(tablename) >= TABLE_MAXNAMELEN) {
1317*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
1318*a71a9546SAutomerger Merge Worker 		return NULL;
1319*a71a9546SAutomerger Merge Worker 	}
1320*a71a9546SAutomerger Merge Worker 
1321*a71a9546SAutomerger Merge Worker 	sockfd = socket(TC_AF, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
1322*a71a9546SAutomerger Merge Worker 	if (sockfd < 0)
1323*a71a9546SAutomerger Merge Worker 		return NULL;
1324*a71a9546SAutomerger Merge Worker 
1325*a71a9546SAutomerger Merge Worker 	s = sizeof(info);
1326*a71a9546SAutomerger Merge Worker 
1327*a71a9546SAutomerger Merge Worker 	strcpy(info.name, tablename);
1328*a71a9546SAutomerger Merge Worker 	if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
1329*a71a9546SAutomerger Merge Worker 		close(sockfd);
1330*a71a9546SAutomerger Merge Worker 		return NULL;
1331*a71a9546SAutomerger Merge Worker 	}
1332*a71a9546SAutomerger Merge Worker 
1333*a71a9546SAutomerger Merge Worker 	DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
1334*a71a9546SAutomerger Merge Worker 		info.valid_hooks, info.num_entries, info.size);
1335*a71a9546SAutomerger Merge Worker 
1336*a71a9546SAutomerger Merge Worker 	h = alloc_handle(&info);
1337*a71a9546SAutomerger Merge Worker 	if (h == NULL) {
1338*a71a9546SAutomerger Merge Worker 		close(sockfd);
1339*a71a9546SAutomerger Merge Worker 		return NULL;
1340*a71a9546SAutomerger Merge Worker 	}
1341*a71a9546SAutomerger Merge Worker 
1342*a71a9546SAutomerger Merge Worker 	/* Initialize current state */
1343*a71a9546SAutomerger Merge Worker 	h->sockfd = sockfd;
1344*a71a9546SAutomerger Merge Worker 	h->info = info;
1345*a71a9546SAutomerger Merge Worker 
1346*a71a9546SAutomerger Merge Worker 	h->entries->size = h->info.size;
1347*a71a9546SAutomerger Merge Worker 
1348*a71a9546SAutomerger Merge Worker 	tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
1349*a71a9546SAutomerger Merge Worker 
1350*a71a9546SAutomerger Merge Worker 	if (getsockopt(h->sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
1351*a71a9546SAutomerger Merge Worker 		       &tmp) < 0)
1352*a71a9546SAutomerger Merge Worker 		goto error;
1353*a71a9546SAutomerger Merge Worker 
1354*a71a9546SAutomerger Merge Worker #ifdef IPTC_DEBUG2
1355*a71a9546SAutomerger Merge Worker 	{
1356*a71a9546SAutomerger Merge Worker 		int fd = open("/tmp/libiptc-so_get_entries.blob",
1357*a71a9546SAutomerger Merge Worker 				O_CREAT|O_WRONLY, 0644);
1358*a71a9546SAutomerger Merge Worker 		if (fd >= 0) {
1359*a71a9546SAutomerger Merge Worker 			write(fd, h->entries, tmp);
1360*a71a9546SAutomerger Merge Worker 			close(fd);
1361*a71a9546SAutomerger Merge Worker 		}
1362*a71a9546SAutomerger Merge Worker 	}
1363*a71a9546SAutomerger Merge Worker #endif
1364*a71a9546SAutomerger Merge Worker 
1365*a71a9546SAutomerger Merge Worker 	if (parse_table(h) < 0)
1366*a71a9546SAutomerger Merge Worker 		goto error;
1367*a71a9546SAutomerger Merge Worker 
1368*a71a9546SAutomerger Merge Worker 	return h;
1369*a71a9546SAutomerger Merge Worker error:
1370*a71a9546SAutomerger Merge Worker 	TC_FREE(h);
1371*a71a9546SAutomerger Merge Worker 	/* A different process changed the ruleset size, retry */
1372*a71a9546SAutomerger Merge Worker 	if (errno == EAGAIN)
1373*a71a9546SAutomerger Merge Worker 		goto retry;
1374*a71a9546SAutomerger Merge Worker 	return NULL;
1375*a71a9546SAutomerger Merge Worker }
1376*a71a9546SAutomerger Merge Worker 
1377*a71a9546SAutomerger Merge Worker void
TC_FREE(struct xtc_handle * h)1378*a71a9546SAutomerger Merge Worker TC_FREE(struct xtc_handle *h)
1379*a71a9546SAutomerger Merge Worker {
1380*a71a9546SAutomerger Merge Worker 	struct chain_head *c, *tmp;
1381*a71a9546SAutomerger Merge Worker 
1382*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_FREE;
1383*a71a9546SAutomerger Merge Worker 	close(h->sockfd);
1384*a71a9546SAutomerger Merge Worker 
1385*a71a9546SAutomerger Merge Worker 	list_for_each_entry_safe(c, tmp, &h->chains, list) {
1386*a71a9546SAutomerger Merge Worker 		struct rule_head *r, *rtmp;
1387*a71a9546SAutomerger Merge Worker 
1388*a71a9546SAutomerger Merge Worker 		list_for_each_entry_safe(r, rtmp, &c->rules, list) {
1389*a71a9546SAutomerger Merge Worker 			free(r);
1390*a71a9546SAutomerger Merge Worker 		}
1391*a71a9546SAutomerger Merge Worker 
1392*a71a9546SAutomerger Merge Worker 		free(c);
1393*a71a9546SAutomerger Merge Worker 	}
1394*a71a9546SAutomerger Merge Worker 
1395*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_free(h);
1396*a71a9546SAutomerger Merge Worker 
1397*a71a9546SAutomerger Merge Worker 	free(h->entries);
1398*a71a9546SAutomerger Merge Worker 	free(h);
1399*a71a9546SAutomerger Merge Worker }
1400*a71a9546SAutomerger Merge Worker 
1401*a71a9546SAutomerger Merge Worker static inline int
print_match(const STRUCT_ENTRY_MATCH * m)1402*a71a9546SAutomerger Merge Worker print_match(const STRUCT_ENTRY_MATCH *m)
1403*a71a9546SAutomerger Merge Worker {
1404*a71a9546SAutomerger Merge Worker 	printf("Match name: `%s'\n", m->u.user.name);
1405*a71a9546SAutomerger Merge Worker 	return 0;
1406*a71a9546SAutomerger Merge Worker }
1407*a71a9546SAutomerger Merge Worker 
1408*a71a9546SAutomerger Merge Worker static int dump_entry(STRUCT_ENTRY *e, struct xtc_handle *const handle);
1409*a71a9546SAutomerger Merge Worker 
1410*a71a9546SAutomerger Merge Worker void
TC_DUMP_ENTRIES(struct xtc_handle * const handle)1411*a71a9546SAutomerger Merge Worker TC_DUMP_ENTRIES(struct xtc_handle *const handle)
1412*a71a9546SAutomerger Merge Worker {
1413*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_DUMP_ENTRIES;
1414*a71a9546SAutomerger Merge Worker 
1415*a71a9546SAutomerger Merge Worker 	printf("libiptc v%s. %u bytes.\n",
1416*a71a9546SAutomerger Merge Worker 	       XTABLES_VERSION, handle->entries->size);
1417*a71a9546SAutomerger Merge Worker 	printf("Table `%s'\n", handle->info.name);
1418*a71a9546SAutomerger Merge Worker 	printf("Hooks: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
1419*a71a9546SAutomerger Merge Worker 	       handle->info.hook_entry[HOOK_PRE_ROUTING],
1420*a71a9546SAutomerger Merge Worker 	       handle->info.hook_entry[HOOK_LOCAL_IN],
1421*a71a9546SAutomerger Merge Worker 	       handle->info.hook_entry[HOOK_FORWARD],
1422*a71a9546SAutomerger Merge Worker 	       handle->info.hook_entry[HOOK_LOCAL_OUT],
1423*a71a9546SAutomerger Merge Worker 	       handle->info.hook_entry[HOOK_POST_ROUTING]);
1424*a71a9546SAutomerger Merge Worker 	printf("Underflows: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
1425*a71a9546SAutomerger Merge Worker 	       handle->info.underflow[HOOK_PRE_ROUTING],
1426*a71a9546SAutomerger Merge Worker 	       handle->info.underflow[HOOK_LOCAL_IN],
1427*a71a9546SAutomerger Merge Worker 	       handle->info.underflow[HOOK_FORWARD],
1428*a71a9546SAutomerger Merge Worker 	       handle->info.underflow[HOOK_LOCAL_OUT],
1429*a71a9546SAutomerger Merge Worker 	       handle->info.underflow[HOOK_POST_ROUTING]);
1430*a71a9546SAutomerger Merge Worker 
1431*a71a9546SAutomerger Merge Worker 	ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
1432*a71a9546SAutomerger Merge Worker 		      dump_entry, handle);
1433*a71a9546SAutomerger Merge Worker }
1434*a71a9546SAutomerger Merge Worker 
1435*a71a9546SAutomerger Merge Worker /* Does this chain exist? */
TC_IS_CHAIN(const char * chain,struct xtc_handle * const handle)1436*a71a9546SAutomerger Merge Worker int TC_IS_CHAIN(const char *chain, struct xtc_handle *const handle)
1437*a71a9546SAutomerger Merge Worker {
1438*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_IS_CHAIN;
1439*a71a9546SAutomerger Merge Worker 	return iptcc_find_label(chain, handle) != NULL;
1440*a71a9546SAutomerger Merge Worker }
1441*a71a9546SAutomerger Merge Worker 
iptcc_chain_iterator_advance(struct xtc_handle * handle)1442*a71a9546SAutomerger Merge Worker static void iptcc_chain_iterator_advance(struct xtc_handle *handle)
1443*a71a9546SAutomerger Merge Worker {
1444*a71a9546SAutomerger Merge Worker 	struct chain_head *c = handle->chain_iterator_cur;
1445*a71a9546SAutomerger Merge Worker 
1446*a71a9546SAutomerger Merge Worker 	if (c->list.next == &handle->chains)
1447*a71a9546SAutomerger Merge Worker 		handle->chain_iterator_cur = NULL;
1448*a71a9546SAutomerger Merge Worker 	else
1449*a71a9546SAutomerger Merge Worker 		handle->chain_iterator_cur =
1450*a71a9546SAutomerger Merge Worker 			list_entry(c->list.next, struct chain_head, list);
1451*a71a9546SAutomerger Merge Worker }
1452*a71a9546SAutomerger Merge Worker 
1453*a71a9546SAutomerger Merge Worker /* Iterator functions to run through the chains. */
1454*a71a9546SAutomerger Merge Worker const char *
TC_FIRST_CHAIN(struct xtc_handle * handle)1455*a71a9546SAutomerger Merge Worker TC_FIRST_CHAIN(struct xtc_handle *handle)
1456*a71a9546SAutomerger Merge Worker {
1457*a71a9546SAutomerger Merge Worker 	struct chain_head *c = list_entry(handle->chains.next,
1458*a71a9546SAutomerger Merge Worker 					  struct chain_head, list);
1459*a71a9546SAutomerger Merge Worker 
1460*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_FIRST_CHAIN;
1461*a71a9546SAutomerger Merge Worker 
1462*a71a9546SAutomerger Merge Worker 
1463*a71a9546SAutomerger Merge Worker 	if (list_empty(&handle->chains)) {
1464*a71a9546SAutomerger Merge Worker 		DEBUGP(": no chains\n");
1465*a71a9546SAutomerger Merge Worker 		return NULL;
1466*a71a9546SAutomerger Merge Worker 	}
1467*a71a9546SAutomerger Merge Worker 
1468*a71a9546SAutomerger Merge Worker 	handle->chain_iterator_cur = c;
1469*a71a9546SAutomerger Merge Worker 	iptcc_chain_iterator_advance(handle);
1470*a71a9546SAutomerger Merge Worker 
1471*a71a9546SAutomerger Merge Worker 	DEBUGP(": returning `%s'\n", c->name);
1472*a71a9546SAutomerger Merge Worker 	return c->name;
1473*a71a9546SAutomerger Merge Worker }
1474*a71a9546SAutomerger Merge Worker 
1475*a71a9546SAutomerger Merge Worker /* Iterator functions to run through the chains.  Returns NULL at end. */
1476*a71a9546SAutomerger Merge Worker const char *
TC_NEXT_CHAIN(struct xtc_handle * handle)1477*a71a9546SAutomerger Merge Worker TC_NEXT_CHAIN(struct xtc_handle *handle)
1478*a71a9546SAutomerger Merge Worker {
1479*a71a9546SAutomerger Merge Worker 	struct chain_head *c = handle->chain_iterator_cur;
1480*a71a9546SAutomerger Merge Worker 
1481*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_NEXT_CHAIN;
1482*a71a9546SAutomerger Merge Worker 
1483*a71a9546SAutomerger Merge Worker 	if (!c) {
1484*a71a9546SAutomerger Merge Worker 		DEBUGP(": no more chains\n");
1485*a71a9546SAutomerger Merge Worker 		return NULL;
1486*a71a9546SAutomerger Merge Worker 	}
1487*a71a9546SAutomerger Merge Worker 
1488*a71a9546SAutomerger Merge Worker 	iptcc_chain_iterator_advance(handle);
1489*a71a9546SAutomerger Merge Worker 
1490*a71a9546SAutomerger Merge Worker 	DEBUGP(": returning `%s'\n", c->name);
1491*a71a9546SAutomerger Merge Worker 	return c->name;
1492*a71a9546SAutomerger Merge Worker }
1493*a71a9546SAutomerger Merge Worker 
1494*a71a9546SAutomerger Merge Worker /* Get first rule in the given chain: NULL for empty chain. */
1495*a71a9546SAutomerger Merge Worker const STRUCT_ENTRY *
TC_FIRST_RULE(const char * chain,struct xtc_handle * handle)1496*a71a9546SAutomerger Merge Worker TC_FIRST_RULE(const char *chain, struct xtc_handle *handle)
1497*a71a9546SAutomerger Merge Worker {
1498*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1499*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1500*a71a9546SAutomerger Merge Worker 
1501*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_FIRST_RULE;
1502*a71a9546SAutomerger Merge Worker 
1503*a71a9546SAutomerger Merge Worker 	DEBUGP("first rule(%s): ", chain);
1504*a71a9546SAutomerger Merge Worker 
1505*a71a9546SAutomerger Merge Worker 	c = iptcc_find_label(chain, handle);
1506*a71a9546SAutomerger Merge Worker 	if (!c) {
1507*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1508*a71a9546SAutomerger Merge Worker 		return NULL;
1509*a71a9546SAutomerger Merge Worker 	}
1510*a71a9546SAutomerger Merge Worker 
1511*a71a9546SAutomerger Merge Worker 	/* Empty chain: single return/policy rule */
1512*a71a9546SAutomerger Merge Worker 	if (list_empty(&c->rules)) {
1513*a71a9546SAutomerger Merge Worker 		DEBUGP_C("no rules, returning NULL\n");
1514*a71a9546SAutomerger Merge Worker 		return NULL;
1515*a71a9546SAutomerger Merge Worker 	}
1516*a71a9546SAutomerger Merge Worker 
1517*a71a9546SAutomerger Merge Worker 	r = list_entry(c->rules.next, struct rule_head, list);
1518*a71a9546SAutomerger Merge Worker 	handle->rule_iterator_cur = r;
1519*a71a9546SAutomerger Merge Worker 	DEBUGP_C("%p\n", r);
1520*a71a9546SAutomerger Merge Worker 
1521*a71a9546SAutomerger Merge Worker 	return r->entry;
1522*a71a9546SAutomerger Merge Worker }
1523*a71a9546SAutomerger Merge Worker 
1524*a71a9546SAutomerger Merge Worker /* Returns NULL when rules run out. */
1525*a71a9546SAutomerger Merge Worker const STRUCT_ENTRY *
TC_NEXT_RULE(const STRUCT_ENTRY * prev,struct xtc_handle * handle)1526*a71a9546SAutomerger Merge Worker TC_NEXT_RULE(const STRUCT_ENTRY *prev, struct xtc_handle *handle)
1527*a71a9546SAutomerger Merge Worker {
1528*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1529*a71a9546SAutomerger Merge Worker 
1530*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_NEXT_RULE;
1531*a71a9546SAutomerger Merge Worker 	DEBUGP("rule_iterator_cur=%p...", handle->rule_iterator_cur);
1532*a71a9546SAutomerger Merge Worker 
1533*a71a9546SAutomerger Merge Worker 	if (handle->rule_iterator_cur == NULL) {
1534*a71a9546SAutomerger Merge Worker 		DEBUGP_C("returning NULL\n");
1535*a71a9546SAutomerger Merge Worker 		return NULL;
1536*a71a9546SAutomerger Merge Worker 	}
1537*a71a9546SAutomerger Merge Worker 
1538*a71a9546SAutomerger Merge Worker 	r = list_entry(handle->rule_iterator_cur->list.next,
1539*a71a9546SAutomerger Merge Worker 			struct rule_head, list);
1540*a71a9546SAutomerger Merge Worker 
1541*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_NEXT_RULE;
1542*a71a9546SAutomerger Merge Worker 
1543*a71a9546SAutomerger Merge Worker 	DEBUGP_C("next=%p, head=%p...", &r->list,
1544*a71a9546SAutomerger Merge Worker 		&handle->rule_iterator_cur->chain->rules);
1545*a71a9546SAutomerger Merge Worker 
1546*a71a9546SAutomerger Merge Worker 	if (&r->list == &handle->rule_iterator_cur->chain->rules) {
1547*a71a9546SAutomerger Merge Worker 		handle->rule_iterator_cur = NULL;
1548*a71a9546SAutomerger Merge Worker 		DEBUGP_C("finished, returning NULL\n");
1549*a71a9546SAutomerger Merge Worker 		return NULL;
1550*a71a9546SAutomerger Merge Worker 	}
1551*a71a9546SAutomerger Merge Worker 
1552*a71a9546SAutomerger Merge Worker 	handle->rule_iterator_cur = r;
1553*a71a9546SAutomerger Merge Worker 
1554*a71a9546SAutomerger Merge Worker 	/* NOTE: prev is without any influence ! */
1555*a71a9546SAutomerger Merge Worker 	DEBUGP_C("returning rule %p\n", r);
1556*a71a9546SAutomerger Merge Worker 	return r->entry;
1557*a71a9546SAutomerger Merge Worker }
1558*a71a9546SAutomerger Merge Worker 
1559*a71a9546SAutomerger Merge Worker /* Returns a pointer to the target name of this position. */
standard_target_map(int verdict)1560*a71a9546SAutomerger Merge Worker static const char *standard_target_map(int verdict)
1561*a71a9546SAutomerger Merge Worker {
1562*a71a9546SAutomerger Merge Worker 	switch (verdict) {
1563*a71a9546SAutomerger Merge Worker 		case RETURN:
1564*a71a9546SAutomerger Merge Worker 			return LABEL_RETURN;
1565*a71a9546SAutomerger Merge Worker 			break;
1566*a71a9546SAutomerger Merge Worker 		case -NF_ACCEPT-1:
1567*a71a9546SAutomerger Merge Worker 			return LABEL_ACCEPT;
1568*a71a9546SAutomerger Merge Worker 			break;
1569*a71a9546SAutomerger Merge Worker 		case -NF_DROP-1:
1570*a71a9546SAutomerger Merge Worker 			return LABEL_DROP;
1571*a71a9546SAutomerger Merge Worker 			break;
1572*a71a9546SAutomerger Merge Worker 		case -NF_QUEUE-1:
1573*a71a9546SAutomerger Merge Worker 			return LABEL_QUEUE;
1574*a71a9546SAutomerger Merge Worker 			break;
1575*a71a9546SAutomerger Merge Worker 		default:
1576*a71a9546SAutomerger Merge Worker 			fprintf(stderr, "ERROR: %d not a valid target)\n",
1577*a71a9546SAutomerger Merge Worker 				verdict);
1578*a71a9546SAutomerger Merge Worker 			abort();
1579*a71a9546SAutomerger Merge Worker 			break;
1580*a71a9546SAutomerger Merge Worker 	}
1581*a71a9546SAutomerger Merge Worker 	/* not reached */
1582*a71a9546SAutomerger Merge Worker 	return NULL;
1583*a71a9546SAutomerger Merge Worker }
1584*a71a9546SAutomerger Merge Worker 
1585*a71a9546SAutomerger Merge Worker /* Returns a pointer to the target name of this position. */
TC_GET_TARGET(const STRUCT_ENTRY * ce,struct xtc_handle * handle)1586*a71a9546SAutomerger Merge Worker const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
1587*a71a9546SAutomerger Merge Worker 			  struct xtc_handle *handle)
1588*a71a9546SAutomerger Merge Worker {
1589*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
1590*a71a9546SAutomerger Merge Worker 	struct rule_head *r = container_of(e, struct rule_head, entry[0]);
1591*a71a9546SAutomerger Merge Worker 	const unsigned char *data;
1592*a71a9546SAutomerger Merge Worker 
1593*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_GET_TARGET;
1594*a71a9546SAutomerger Merge Worker 
1595*a71a9546SAutomerger Merge Worker 	switch(r->type) {
1596*a71a9546SAutomerger Merge Worker 		int spos;
1597*a71a9546SAutomerger Merge Worker 		case IPTCC_R_FALLTHROUGH:
1598*a71a9546SAutomerger Merge Worker 			return "";
1599*a71a9546SAutomerger Merge Worker 			break;
1600*a71a9546SAutomerger Merge Worker 		case IPTCC_R_JUMP:
1601*a71a9546SAutomerger Merge Worker 			DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
1602*a71a9546SAutomerger Merge Worker 			return r->jump->name;
1603*a71a9546SAutomerger Merge Worker 			break;
1604*a71a9546SAutomerger Merge Worker 		case IPTCC_R_STANDARD:
1605*a71a9546SAutomerger Merge Worker 			data = GET_TARGET(e)->data;
1606*a71a9546SAutomerger Merge Worker 			spos = *(const int *)data;
1607*a71a9546SAutomerger Merge Worker 			DEBUGP("r=%p, spos=%d'\n", r, spos);
1608*a71a9546SAutomerger Merge Worker 			return standard_target_map(spos);
1609*a71a9546SAutomerger Merge Worker 			break;
1610*a71a9546SAutomerger Merge Worker 		case IPTCC_R_MODULE:
1611*a71a9546SAutomerger Merge Worker 			return GET_TARGET(e)->u.user.name;
1612*a71a9546SAutomerger Merge Worker 			break;
1613*a71a9546SAutomerger Merge Worker 	}
1614*a71a9546SAutomerger Merge Worker 	return NULL;
1615*a71a9546SAutomerger Merge Worker }
1616*a71a9546SAutomerger Merge Worker /* Is this a built-in chain?  Actually returns hook + 1. */
1617*a71a9546SAutomerger Merge Worker int
TC_BUILTIN(const char * chain,struct xtc_handle * const handle)1618*a71a9546SAutomerger Merge Worker TC_BUILTIN(const char *chain, struct xtc_handle *const handle)
1619*a71a9546SAutomerger Merge Worker {
1620*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1621*a71a9546SAutomerger Merge Worker 
1622*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_BUILTIN;
1623*a71a9546SAutomerger Merge Worker 
1624*a71a9546SAutomerger Merge Worker 	c = iptcc_find_label(chain, handle);
1625*a71a9546SAutomerger Merge Worker 	if (!c) {
1626*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1627*a71a9546SAutomerger Merge Worker 		return 0;
1628*a71a9546SAutomerger Merge Worker 	}
1629*a71a9546SAutomerger Merge Worker 
1630*a71a9546SAutomerger Merge Worker 	return iptcc_is_builtin(c);
1631*a71a9546SAutomerger Merge Worker }
1632*a71a9546SAutomerger Merge Worker 
1633*a71a9546SAutomerger Merge Worker /* Get the policy of a given built-in chain */
1634*a71a9546SAutomerger Merge Worker const char *
TC_GET_POLICY(const char * chain,STRUCT_COUNTERS * counters,struct xtc_handle * handle)1635*a71a9546SAutomerger Merge Worker TC_GET_POLICY(const char *chain,
1636*a71a9546SAutomerger Merge Worker 	      STRUCT_COUNTERS *counters,
1637*a71a9546SAutomerger Merge Worker 	      struct xtc_handle *handle)
1638*a71a9546SAutomerger Merge Worker {
1639*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1640*a71a9546SAutomerger Merge Worker 
1641*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_GET_POLICY;
1642*a71a9546SAutomerger Merge Worker 
1643*a71a9546SAutomerger Merge Worker 	DEBUGP("called for chain %s\n", chain);
1644*a71a9546SAutomerger Merge Worker 
1645*a71a9546SAutomerger Merge Worker 	c = iptcc_find_label(chain, handle);
1646*a71a9546SAutomerger Merge Worker 	if (!c) {
1647*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1648*a71a9546SAutomerger Merge Worker 		return NULL;
1649*a71a9546SAutomerger Merge Worker 	}
1650*a71a9546SAutomerger Merge Worker 
1651*a71a9546SAutomerger Merge Worker 	if (!iptcc_is_builtin(c))
1652*a71a9546SAutomerger Merge Worker 		return NULL;
1653*a71a9546SAutomerger Merge Worker 
1654*a71a9546SAutomerger Merge Worker 	*counters = c->counters;
1655*a71a9546SAutomerger Merge Worker 
1656*a71a9546SAutomerger Merge Worker 	return standard_target_map(c->verdict);
1657*a71a9546SAutomerger Merge Worker }
1658*a71a9546SAutomerger Merge Worker 
1659*a71a9546SAutomerger Merge Worker static int
iptcc_standard_map(struct rule_head * r,int verdict)1660*a71a9546SAutomerger Merge Worker iptcc_standard_map(struct rule_head *r, int verdict)
1661*a71a9546SAutomerger Merge Worker {
1662*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY *e = r->entry;
1663*a71a9546SAutomerger Merge Worker 	STRUCT_STANDARD_TARGET *t;
1664*a71a9546SAutomerger Merge Worker 
1665*a71a9546SAutomerger Merge Worker 	t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1666*a71a9546SAutomerger Merge Worker 
1667*a71a9546SAutomerger Merge Worker 	if (t->target.u.target_size
1668*a71a9546SAutomerger Merge Worker 	    != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
1669*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
1670*a71a9546SAutomerger Merge Worker 		return 0;
1671*a71a9546SAutomerger Merge Worker 	}
1672*a71a9546SAutomerger Merge Worker 	/* memset for memcmp convenience on delete/replace */
1673*a71a9546SAutomerger Merge Worker 	memset(t->target.u.user.name, 0, XT_EXTENSION_MAXNAMELEN);
1674*a71a9546SAutomerger Merge Worker 	strcpy(t->target.u.user.name, STANDARD_TARGET);
1675*a71a9546SAutomerger Merge Worker 	t->target.u.user.revision = 0;
1676*a71a9546SAutomerger Merge Worker 	t->verdict = verdict;
1677*a71a9546SAutomerger Merge Worker 
1678*a71a9546SAutomerger Merge Worker 	r->type = IPTCC_R_STANDARD;
1679*a71a9546SAutomerger Merge Worker 
1680*a71a9546SAutomerger Merge Worker 	return 1;
1681*a71a9546SAutomerger Merge Worker }
1682*a71a9546SAutomerger Merge Worker 
1683*a71a9546SAutomerger Merge Worker static int
iptcc_map_target(struct xtc_handle * const handle,struct rule_head * r,bool dry_run)1684*a71a9546SAutomerger Merge Worker iptcc_map_target(struct xtc_handle *const handle,
1685*a71a9546SAutomerger Merge Worker 	   struct rule_head *r,
1686*a71a9546SAutomerger Merge Worker 	   bool dry_run)
1687*a71a9546SAutomerger Merge Worker {
1688*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY *e = r->entry;
1689*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
1690*a71a9546SAutomerger Merge Worker 
1691*a71a9546SAutomerger Merge Worker 	/* Maybe it's empty (=> fall through) */
1692*a71a9546SAutomerger Merge Worker 	if (strcmp(t->u.user.name, "") == 0) {
1693*a71a9546SAutomerger Merge Worker 		r->type = IPTCC_R_FALLTHROUGH;
1694*a71a9546SAutomerger Merge Worker 		return 1;
1695*a71a9546SAutomerger Merge Worker 	}
1696*a71a9546SAutomerger Merge Worker 	/* Maybe it's a standard target name... */
1697*a71a9546SAutomerger Merge Worker 	else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
1698*a71a9546SAutomerger Merge Worker 		return iptcc_standard_map(r, -NF_ACCEPT - 1);
1699*a71a9546SAutomerger Merge Worker 	else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
1700*a71a9546SAutomerger Merge Worker 		return iptcc_standard_map(r, -NF_DROP - 1);
1701*a71a9546SAutomerger Merge Worker 	else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
1702*a71a9546SAutomerger Merge Worker 		return iptcc_standard_map(r, -NF_QUEUE - 1);
1703*a71a9546SAutomerger Merge Worker 	else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
1704*a71a9546SAutomerger Merge Worker 		return iptcc_standard_map(r, RETURN);
1705*a71a9546SAutomerger Merge Worker 	else if (TC_BUILTIN(t->u.user.name, handle)) {
1706*a71a9546SAutomerger Merge Worker 		/* Can't jump to builtins. */
1707*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
1708*a71a9546SAutomerger Merge Worker 		return 0;
1709*a71a9546SAutomerger Merge Worker 	} else {
1710*a71a9546SAutomerger Merge Worker 		/* Maybe it's an existing chain name. */
1711*a71a9546SAutomerger Merge Worker 		struct chain_head *c;
1712*a71a9546SAutomerger Merge Worker 		DEBUGP("trying to find chain `%s': ", t->u.user.name);
1713*a71a9546SAutomerger Merge Worker 
1714*a71a9546SAutomerger Merge Worker 		c = iptcc_find_label(t->u.user.name, handle);
1715*a71a9546SAutomerger Merge Worker 		if (c) {
1716*a71a9546SAutomerger Merge Worker 			DEBUGP_C("found!\n");
1717*a71a9546SAutomerger Merge Worker 			r->type = IPTCC_R_JUMP;
1718*a71a9546SAutomerger Merge Worker 			r->jump = c;
1719*a71a9546SAutomerger Merge Worker 			c->references++;
1720*a71a9546SAutomerger Merge Worker 			return 1;
1721*a71a9546SAutomerger Merge Worker 		}
1722*a71a9546SAutomerger Merge Worker 		DEBUGP_C("not found :(\n");
1723*a71a9546SAutomerger Merge Worker 	}
1724*a71a9546SAutomerger Merge Worker 
1725*a71a9546SAutomerger Merge Worker 	/* Must be a module?  If not, kernel will reject... */
1726*a71a9546SAutomerger Merge Worker 	/* memset to all 0 for your memcmp convenience: don't clear version */
1727*a71a9546SAutomerger Merge Worker 	memset(t->u.user.name + strlen(t->u.user.name),
1728*a71a9546SAutomerger Merge Worker 	       0,
1729*a71a9546SAutomerger Merge Worker 	       FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
1730*a71a9546SAutomerger Merge Worker 	r->type = IPTCC_R_MODULE;
1731*a71a9546SAutomerger Merge Worker 	if (!dry_run)
1732*a71a9546SAutomerger Merge Worker 		set_changed(handle);
1733*a71a9546SAutomerger Merge Worker 	return 1;
1734*a71a9546SAutomerger Merge Worker }
1735*a71a9546SAutomerger Merge Worker 
1736*a71a9546SAutomerger Merge Worker /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
1737*a71a9546SAutomerger Merge Worker int
TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * e,unsigned int rulenum,struct xtc_handle * handle)1738*a71a9546SAutomerger Merge Worker TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1739*a71a9546SAutomerger Merge Worker 		const STRUCT_ENTRY *e,
1740*a71a9546SAutomerger Merge Worker 		unsigned int rulenum,
1741*a71a9546SAutomerger Merge Worker 		struct xtc_handle *handle)
1742*a71a9546SAutomerger Merge Worker {
1743*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1744*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1745*a71a9546SAutomerger Merge Worker 	struct list_head *prev;
1746*a71a9546SAutomerger Merge Worker 
1747*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_INSERT_ENTRY;
1748*a71a9546SAutomerger Merge Worker 
1749*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
1750*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1751*a71a9546SAutomerger Merge Worker 		return 0;
1752*a71a9546SAutomerger Merge Worker 	}
1753*a71a9546SAutomerger Merge Worker 
1754*a71a9546SAutomerger Merge Worker 	/* first rulenum index = 0
1755*a71a9546SAutomerger Merge Worker 	   first c->num_rules index = 1 */
1756*a71a9546SAutomerger Merge Worker 	if (rulenum > c->num_rules) {
1757*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
1758*a71a9546SAutomerger Merge Worker 		return 0;
1759*a71a9546SAutomerger Merge Worker 	}
1760*a71a9546SAutomerger Merge Worker 
1761*a71a9546SAutomerger Merge Worker 	/* If we are inserting at the end just take advantage of the
1762*a71a9546SAutomerger Merge Worker 	   double linked list, insert will happen before the entry
1763*a71a9546SAutomerger Merge Worker 	   prev points to. */
1764*a71a9546SAutomerger Merge Worker 	if (rulenum == c->num_rules) {
1765*a71a9546SAutomerger Merge Worker 		prev = &c->rules;
1766*a71a9546SAutomerger Merge Worker 	} else if (rulenum + 1 <= c->num_rules/2) {
1767*a71a9546SAutomerger Merge Worker 		r = iptcc_get_rule_num(c, rulenum + 1);
1768*a71a9546SAutomerger Merge Worker 		prev = &r->list;
1769*a71a9546SAutomerger Merge Worker 	} else {
1770*a71a9546SAutomerger Merge Worker 		r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
1771*a71a9546SAutomerger Merge Worker 		prev = &r->list;
1772*a71a9546SAutomerger Merge Worker 	}
1773*a71a9546SAutomerger Merge Worker 
1774*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1775*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
1776*a71a9546SAutomerger Merge Worker 		return 0;
1777*a71a9546SAutomerger Merge Worker 	}
1778*a71a9546SAutomerger Merge Worker 
1779*a71a9546SAutomerger Merge Worker 	memcpy(r->entry, e, e->next_offset);
1780*a71a9546SAutomerger Merge Worker 	r->counter_map.maptype = COUNTER_MAP_SET;
1781*a71a9546SAutomerger Merge Worker 
1782*a71a9546SAutomerger Merge Worker 	if (!iptcc_map_target(handle, r, false)) {
1783*a71a9546SAutomerger Merge Worker 		free(r);
1784*a71a9546SAutomerger Merge Worker 		return 0;
1785*a71a9546SAutomerger Merge Worker 	}
1786*a71a9546SAutomerger Merge Worker 
1787*a71a9546SAutomerger Merge Worker 	list_add_tail(&r->list, prev);
1788*a71a9546SAutomerger Merge Worker 	c->num_rules++;
1789*a71a9546SAutomerger Merge Worker 
1790*a71a9546SAutomerger Merge Worker 	set_changed(handle);
1791*a71a9546SAutomerger Merge Worker 
1792*a71a9546SAutomerger Merge Worker 	return 1;
1793*a71a9546SAutomerger Merge Worker }
1794*a71a9546SAutomerger Merge Worker 
1795*a71a9546SAutomerger Merge Worker /* Atomically replace rule `rulenum' in `chain' with `fw'. */
1796*a71a9546SAutomerger Merge Worker int
TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * e,unsigned int rulenum,struct xtc_handle * handle)1797*a71a9546SAutomerger Merge Worker TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1798*a71a9546SAutomerger Merge Worker 		 const STRUCT_ENTRY *e,
1799*a71a9546SAutomerger Merge Worker 		 unsigned int rulenum,
1800*a71a9546SAutomerger Merge Worker 		 struct xtc_handle *handle)
1801*a71a9546SAutomerger Merge Worker {
1802*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1803*a71a9546SAutomerger Merge Worker 	struct rule_head *r, *old;
1804*a71a9546SAutomerger Merge Worker 
1805*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_REPLACE_ENTRY;
1806*a71a9546SAutomerger Merge Worker 
1807*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
1808*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1809*a71a9546SAutomerger Merge Worker 		return 0;
1810*a71a9546SAutomerger Merge Worker 	}
1811*a71a9546SAutomerger Merge Worker 
1812*a71a9546SAutomerger Merge Worker 	if (rulenum >= c->num_rules) {
1813*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
1814*a71a9546SAutomerger Merge Worker 		return 0;
1815*a71a9546SAutomerger Merge Worker 	}
1816*a71a9546SAutomerger Merge Worker 
1817*a71a9546SAutomerger Merge Worker 	/* Take advantage of the double linked list if possible. */
1818*a71a9546SAutomerger Merge Worker 	if (rulenum + 1 <= c->num_rules/2) {
1819*a71a9546SAutomerger Merge Worker 		old = iptcc_get_rule_num(c, rulenum + 1);
1820*a71a9546SAutomerger Merge Worker 	} else {
1821*a71a9546SAutomerger Merge Worker 		old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
1822*a71a9546SAutomerger Merge Worker 	}
1823*a71a9546SAutomerger Merge Worker 
1824*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1825*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
1826*a71a9546SAutomerger Merge Worker 		return 0;
1827*a71a9546SAutomerger Merge Worker 	}
1828*a71a9546SAutomerger Merge Worker 
1829*a71a9546SAutomerger Merge Worker 	memcpy(r->entry, e, e->next_offset);
1830*a71a9546SAutomerger Merge Worker 	r->counter_map.maptype = COUNTER_MAP_SET;
1831*a71a9546SAutomerger Merge Worker 
1832*a71a9546SAutomerger Merge Worker 	if (!iptcc_map_target(handle, r, false)) {
1833*a71a9546SAutomerger Merge Worker 		free(r);
1834*a71a9546SAutomerger Merge Worker 		return 0;
1835*a71a9546SAutomerger Merge Worker 	}
1836*a71a9546SAutomerger Merge Worker 
1837*a71a9546SAutomerger Merge Worker 	list_add(&r->list, &old->list);
1838*a71a9546SAutomerger Merge Worker 	iptcc_delete_rule(old);
1839*a71a9546SAutomerger Merge Worker 
1840*a71a9546SAutomerger Merge Worker 	set_changed(handle);
1841*a71a9546SAutomerger Merge Worker 
1842*a71a9546SAutomerger Merge Worker 	return 1;
1843*a71a9546SAutomerger Merge Worker }
1844*a71a9546SAutomerger Merge Worker 
1845*a71a9546SAutomerger Merge Worker /* Append entry `fw' to chain `chain'.  Equivalent to insert with
1846*a71a9546SAutomerger Merge Worker    rulenum = length of chain. */
1847*a71a9546SAutomerger Merge Worker int
TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * e,struct xtc_handle * handle)1848*a71a9546SAutomerger Merge Worker TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1849*a71a9546SAutomerger Merge Worker 		const STRUCT_ENTRY *e,
1850*a71a9546SAutomerger Merge Worker 		struct xtc_handle *handle)
1851*a71a9546SAutomerger Merge Worker {
1852*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1853*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
1854*a71a9546SAutomerger Merge Worker 
1855*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_APPEND_ENTRY;
1856*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
1857*a71a9546SAutomerger Merge Worker 		DEBUGP("unable to find chain `%s'\n", chain);
1858*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1859*a71a9546SAutomerger Merge Worker 		return 0;
1860*a71a9546SAutomerger Merge Worker 	}
1861*a71a9546SAutomerger Merge Worker 
1862*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1863*a71a9546SAutomerger Merge Worker 		DEBUGP("unable to allocate rule for chain `%s'\n", chain);
1864*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
1865*a71a9546SAutomerger Merge Worker 		return 0;
1866*a71a9546SAutomerger Merge Worker 	}
1867*a71a9546SAutomerger Merge Worker 
1868*a71a9546SAutomerger Merge Worker 	memcpy(r->entry, e, e->next_offset);
1869*a71a9546SAutomerger Merge Worker 	r->counter_map.maptype = COUNTER_MAP_SET;
1870*a71a9546SAutomerger Merge Worker 
1871*a71a9546SAutomerger Merge Worker 	if (!iptcc_map_target(handle, r, false)) {
1872*a71a9546SAutomerger Merge Worker 		DEBUGP("unable to map target of rule for chain `%s'\n", chain);
1873*a71a9546SAutomerger Merge Worker 		free(r);
1874*a71a9546SAutomerger Merge Worker 		return 0;
1875*a71a9546SAutomerger Merge Worker 	}
1876*a71a9546SAutomerger Merge Worker 
1877*a71a9546SAutomerger Merge Worker 	list_add_tail(&r->list, &c->rules);
1878*a71a9546SAutomerger Merge Worker 	c->num_rules++;
1879*a71a9546SAutomerger Merge Worker 
1880*a71a9546SAutomerger Merge Worker 	set_changed(handle);
1881*a71a9546SAutomerger Merge Worker 
1882*a71a9546SAutomerger Merge Worker 	return 1;
1883*a71a9546SAutomerger Merge Worker }
1884*a71a9546SAutomerger Merge Worker 
1885*a71a9546SAutomerger Merge Worker static inline int
match_different(const STRUCT_ENTRY_MATCH * a,const unsigned char * a_elems,const unsigned char * b_elems,unsigned char ** maskptr)1886*a71a9546SAutomerger Merge Worker match_different(const STRUCT_ENTRY_MATCH *a,
1887*a71a9546SAutomerger Merge Worker 		const unsigned char *a_elems,
1888*a71a9546SAutomerger Merge Worker 		const unsigned char *b_elems,
1889*a71a9546SAutomerger Merge Worker 		unsigned char **maskptr)
1890*a71a9546SAutomerger Merge Worker {
1891*a71a9546SAutomerger Merge Worker 	const STRUCT_ENTRY_MATCH *b;
1892*a71a9546SAutomerger Merge Worker 	unsigned int i;
1893*a71a9546SAutomerger Merge Worker 
1894*a71a9546SAutomerger Merge Worker 	/* Offset of b is the same as a. */
1895*a71a9546SAutomerger Merge Worker 	b = (void *)b_elems + ((unsigned char *)a - a_elems);
1896*a71a9546SAutomerger Merge Worker 
1897*a71a9546SAutomerger Merge Worker 	if (a->u.match_size != b->u.match_size)
1898*a71a9546SAutomerger Merge Worker 		return 1;
1899*a71a9546SAutomerger Merge Worker 
1900*a71a9546SAutomerger Merge Worker 	if (strcmp(a->u.user.name, b->u.user.name) != 0)
1901*a71a9546SAutomerger Merge Worker 		return 1;
1902*a71a9546SAutomerger Merge Worker 
1903*a71a9546SAutomerger Merge Worker 	*maskptr += ALIGN(sizeof(*a));
1904*a71a9546SAutomerger Merge Worker 
1905*a71a9546SAutomerger Merge Worker 	for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
1906*a71a9546SAutomerger Merge Worker 		if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
1907*a71a9546SAutomerger Merge Worker 			return 1;
1908*a71a9546SAutomerger Merge Worker 	*maskptr += i;
1909*a71a9546SAutomerger Merge Worker 	return 0;
1910*a71a9546SAutomerger Merge Worker }
1911*a71a9546SAutomerger Merge Worker 
1912*a71a9546SAutomerger Merge Worker static inline int
target_same(struct rule_head * a,struct rule_head * b,const unsigned char * mask)1913*a71a9546SAutomerger Merge Worker target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
1914*a71a9546SAutomerger Merge Worker {
1915*a71a9546SAutomerger Merge Worker 	unsigned int i;
1916*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY_TARGET *ta, *tb;
1917*a71a9546SAutomerger Merge Worker 
1918*a71a9546SAutomerger Merge Worker 	if (a->type != b->type)
1919*a71a9546SAutomerger Merge Worker 		return 0;
1920*a71a9546SAutomerger Merge Worker 
1921*a71a9546SAutomerger Merge Worker 	ta = GET_TARGET(a->entry);
1922*a71a9546SAutomerger Merge Worker 	tb = GET_TARGET(b->entry);
1923*a71a9546SAutomerger Merge Worker 
1924*a71a9546SAutomerger Merge Worker 	switch (a->type) {
1925*a71a9546SAutomerger Merge Worker 	case IPTCC_R_FALLTHROUGH:
1926*a71a9546SAutomerger Merge Worker 		return 1;
1927*a71a9546SAutomerger Merge Worker 	case IPTCC_R_JUMP:
1928*a71a9546SAutomerger Merge Worker 		return a->jump == b->jump;
1929*a71a9546SAutomerger Merge Worker 	case IPTCC_R_STANDARD:
1930*a71a9546SAutomerger Merge Worker 		return ((STRUCT_STANDARD_TARGET *)ta)->verdict
1931*a71a9546SAutomerger Merge Worker 			== ((STRUCT_STANDARD_TARGET *)tb)->verdict;
1932*a71a9546SAutomerger Merge Worker 	case IPTCC_R_MODULE:
1933*a71a9546SAutomerger Merge Worker 		if (ta->u.target_size != tb->u.target_size)
1934*a71a9546SAutomerger Merge Worker 			return 0;
1935*a71a9546SAutomerger Merge Worker 		if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
1936*a71a9546SAutomerger Merge Worker 			return 0;
1937*a71a9546SAutomerger Merge Worker 
1938*a71a9546SAutomerger Merge Worker 		for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
1939*a71a9546SAutomerger Merge Worker 			if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
1940*a71a9546SAutomerger Merge Worker 				return 0;
1941*a71a9546SAutomerger Merge Worker 		return 1;
1942*a71a9546SAutomerger Merge Worker 	default:
1943*a71a9546SAutomerger Merge Worker 		fprintf(stderr, "ERROR: bad type %i\n", a->type);
1944*a71a9546SAutomerger Merge Worker 		abort();
1945*a71a9546SAutomerger Merge Worker 	}
1946*a71a9546SAutomerger Merge Worker }
1947*a71a9546SAutomerger Merge Worker 
1948*a71a9546SAutomerger Merge Worker static unsigned char *
1949*a71a9546SAutomerger Merge Worker is_same(const STRUCT_ENTRY *a,
1950*a71a9546SAutomerger Merge Worker 	const STRUCT_ENTRY *b,
1951*a71a9546SAutomerger Merge Worker 	unsigned char *matchmask);
1952*a71a9546SAutomerger Merge Worker 
1953*a71a9546SAutomerger Merge Worker 
1954*a71a9546SAutomerger Merge Worker /* find the first rule in `chain' which matches `fw' and remove it unless dry_run is set */
delete_entry(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * origfw,unsigned char * matchmask,struct xtc_handle * handle,bool dry_run)1955*a71a9546SAutomerger Merge Worker static int delete_entry(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
1956*a71a9546SAutomerger Merge Worker 			unsigned char *matchmask, struct xtc_handle *handle,
1957*a71a9546SAutomerger Merge Worker 			bool dry_run)
1958*a71a9546SAutomerger Merge Worker {
1959*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
1960*a71a9546SAutomerger Merge Worker 	struct rule_head *r, *i;
1961*a71a9546SAutomerger Merge Worker 
1962*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_DELETE_ENTRY;
1963*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
1964*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
1965*a71a9546SAutomerger Merge Worker 		return 0;
1966*a71a9546SAutomerger Merge Worker 	}
1967*a71a9546SAutomerger Merge Worker 
1968*a71a9546SAutomerger Merge Worker 	/* Create a rule_head from origfw. */
1969*a71a9546SAutomerger Merge Worker 	r = iptcc_alloc_rule(c, origfw->next_offset);
1970*a71a9546SAutomerger Merge Worker 	if (!r) {
1971*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
1972*a71a9546SAutomerger Merge Worker 		return 0;
1973*a71a9546SAutomerger Merge Worker 	}
1974*a71a9546SAutomerger Merge Worker 
1975*a71a9546SAutomerger Merge Worker 	memcpy(r->entry, origfw, origfw->next_offset);
1976*a71a9546SAutomerger Merge Worker 	r->counter_map.maptype = COUNTER_MAP_NOMAP;
1977*a71a9546SAutomerger Merge Worker 	if (!iptcc_map_target(handle, r, dry_run)) {
1978*a71a9546SAutomerger Merge Worker 		DEBUGP("unable to map target of rule for chain `%s'\n", chain);
1979*a71a9546SAutomerger Merge Worker 		free(r);
1980*a71a9546SAutomerger Merge Worker 		return 0;
1981*a71a9546SAutomerger Merge Worker 	} else {
1982*a71a9546SAutomerger Merge Worker 		/* iptcc_map_target increment target chain references
1983*a71a9546SAutomerger Merge Worker 		 * since this is a fake rule only used for matching
1984*a71a9546SAutomerger Merge Worker 		 * the chain references count is decremented again.
1985*a71a9546SAutomerger Merge Worker 		 */
1986*a71a9546SAutomerger Merge Worker 		if (r->type == IPTCC_R_JUMP
1987*a71a9546SAutomerger Merge Worker 		    && r->jump)
1988*a71a9546SAutomerger Merge Worker 			r->jump->references--;
1989*a71a9546SAutomerger Merge Worker 	}
1990*a71a9546SAutomerger Merge Worker 
1991*a71a9546SAutomerger Merge Worker 	list_for_each_entry(i, &c->rules, list) {
1992*a71a9546SAutomerger Merge Worker 		unsigned char *mask;
1993*a71a9546SAutomerger Merge Worker 
1994*a71a9546SAutomerger Merge Worker 		mask = is_same(r->entry, i->entry, matchmask);
1995*a71a9546SAutomerger Merge Worker 		if (!mask)
1996*a71a9546SAutomerger Merge Worker 			continue;
1997*a71a9546SAutomerger Merge Worker 
1998*a71a9546SAutomerger Merge Worker 		if (!target_same(r, i, mask))
1999*a71a9546SAutomerger Merge Worker 			continue;
2000*a71a9546SAutomerger Merge Worker 
2001*a71a9546SAutomerger Merge Worker 		/* if we are just doing a dry run, we simply skip the rest */
2002*a71a9546SAutomerger Merge Worker 		if (dry_run){
2003*a71a9546SAutomerger Merge Worker 			free(r);
2004*a71a9546SAutomerger Merge Worker 			return 1;
2005*a71a9546SAutomerger Merge Worker 		}
2006*a71a9546SAutomerger Merge Worker 
2007*a71a9546SAutomerger Merge Worker 		/* If we are about to delete the rule that is the
2008*a71a9546SAutomerger Merge Worker 		 * current iterator, move rule iterator back.  next
2009*a71a9546SAutomerger Merge Worker 		 * pointer will then point to real next node */
2010*a71a9546SAutomerger Merge Worker 		if (i == handle->rule_iterator_cur) {
2011*a71a9546SAutomerger Merge Worker 			handle->rule_iterator_cur =
2012*a71a9546SAutomerger Merge Worker 				list_entry(handle->rule_iterator_cur->list.prev,
2013*a71a9546SAutomerger Merge Worker 					   struct rule_head, list);
2014*a71a9546SAutomerger Merge Worker 		}
2015*a71a9546SAutomerger Merge Worker 
2016*a71a9546SAutomerger Merge Worker 		c->num_rules--;
2017*a71a9546SAutomerger Merge Worker 		iptcc_delete_rule(i);
2018*a71a9546SAutomerger Merge Worker 
2019*a71a9546SAutomerger Merge Worker 		set_changed(handle);
2020*a71a9546SAutomerger Merge Worker 		free(r);
2021*a71a9546SAutomerger Merge Worker 		return 1;
2022*a71a9546SAutomerger Merge Worker 	}
2023*a71a9546SAutomerger Merge Worker 
2024*a71a9546SAutomerger Merge Worker 	free(r);
2025*a71a9546SAutomerger Merge Worker 	errno = ENOENT;
2026*a71a9546SAutomerger Merge Worker 	return 0;
2027*a71a9546SAutomerger Merge Worker }
2028*a71a9546SAutomerger Merge Worker 
2029*a71a9546SAutomerger Merge Worker /* check whether a specified rule is present */
TC_CHECK_ENTRY(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * origfw,unsigned char * matchmask,struct xtc_handle * handle)2030*a71a9546SAutomerger Merge Worker int TC_CHECK_ENTRY(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
2031*a71a9546SAutomerger Merge Worker 		   unsigned char *matchmask, struct xtc_handle *handle)
2032*a71a9546SAutomerger Merge Worker {
2033*a71a9546SAutomerger Merge Worker 	/* do a dry-run delete to find out whether a matching rule exists */
2034*a71a9546SAutomerger Merge Worker 	return delete_entry(chain, origfw, matchmask, handle, true);
2035*a71a9546SAutomerger Merge Worker }
2036*a71a9546SAutomerger Merge Worker 
2037*a71a9546SAutomerger Merge Worker /* Delete the first rule in `chain' which matches `fw'. */
TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,const STRUCT_ENTRY * origfw,unsigned char * matchmask,struct xtc_handle * handle)2038*a71a9546SAutomerger Merge Worker int TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,	const STRUCT_ENTRY *origfw,
2039*a71a9546SAutomerger Merge Worker 		    unsigned char *matchmask, struct xtc_handle *handle)
2040*a71a9546SAutomerger Merge Worker {
2041*a71a9546SAutomerger Merge Worker 	return delete_entry(chain, origfw, matchmask, handle, false);
2042*a71a9546SAutomerger Merge Worker }
2043*a71a9546SAutomerger Merge Worker 
2044*a71a9546SAutomerger Merge Worker /* Delete the rule in position `rulenum' in `chain'. */
2045*a71a9546SAutomerger Merge Worker int
TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,unsigned int rulenum,struct xtc_handle * handle)2046*a71a9546SAutomerger Merge Worker TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
2047*a71a9546SAutomerger Merge Worker 		    unsigned int rulenum,
2048*a71a9546SAutomerger Merge Worker 		    struct xtc_handle *handle)
2049*a71a9546SAutomerger Merge Worker {
2050*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2051*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
2052*a71a9546SAutomerger Merge Worker 
2053*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_DELETE_NUM_ENTRY;
2054*a71a9546SAutomerger Merge Worker 
2055*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2056*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2057*a71a9546SAutomerger Merge Worker 		return 0;
2058*a71a9546SAutomerger Merge Worker 	}
2059*a71a9546SAutomerger Merge Worker 
2060*a71a9546SAutomerger Merge Worker 	if (rulenum >= c->num_rules) {
2061*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
2062*a71a9546SAutomerger Merge Worker 		return 0;
2063*a71a9546SAutomerger Merge Worker 	}
2064*a71a9546SAutomerger Merge Worker 
2065*a71a9546SAutomerger Merge Worker 	/* Take advantage of the double linked list if possible. */
2066*a71a9546SAutomerger Merge Worker 	if (rulenum + 1 <= c->num_rules/2) {
2067*a71a9546SAutomerger Merge Worker 		r = iptcc_get_rule_num(c, rulenum + 1);
2068*a71a9546SAutomerger Merge Worker 	} else {
2069*a71a9546SAutomerger Merge Worker 		r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
2070*a71a9546SAutomerger Merge Worker 	}
2071*a71a9546SAutomerger Merge Worker 
2072*a71a9546SAutomerger Merge Worker 	/* If we are about to delete the rule that is the current
2073*a71a9546SAutomerger Merge Worker 	 * iterator, move rule iterator back.  next pointer will then
2074*a71a9546SAutomerger Merge Worker 	 * point to real next node */
2075*a71a9546SAutomerger Merge Worker 	if (r == handle->rule_iterator_cur) {
2076*a71a9546SAutomerger Merge Worker 		handle->rule_iterator_cur =
2077*a71a9546SAutomerger Merge Worker 			list_entry(handle->rule_iterator_cur->list.prev,
2078*a71a9546SAutomerger Merge Worker 				   struct rule_head, list);
2079*a71a9546SAutomerger Merge Worker 	}
2080*a71a9546SAutomerger Merge Worker 
2081*a71a9546SAutomerger Merge Worker 	c->num_rules--;
2082*a71a9546SAutomerger Merge Worker 	iptcc_delete_rule(r);
2083*a71a9546SAutomerger Merge Worker 
2084*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2085*a71a9546SAutomerger Merge Worker 
2086*a71a9546SAutomerger Merge Worker 	return 1;
2087*a71a9546SAutomerger Merge Worker }
2088*a71a9546SAutomerger Merge Worker 
2089*a71a9546SAutomerger Merge Worker /* Flushes the entries in the given chain (ie. empties chain). */
2090*a71a9546SAutomerger Merge Worker int
TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain,struct xtc_handle * handle)2091*a71a9546SAutomerger Merge Worker TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
2092*a71a9546SAutomerger Merge Worker {
2093*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2094*a71a9546SAutomerger Merge Worker 	struct rule_head *r, *tmp;
2095*a71a9546SAutomerger Merge Worker 
2096*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_FLUSH_ENTRIES;
2097*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2098*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2099*a71a9546SAutomerger Merge Worker 		return 0;
2100*a71a9546SAutomerger Merge Worker 	}
2101*a71a9546SAutomerger Merge Worker 
2102*a71a9546SAutomerger Merge Worker 	list_for_each_entry_safe(r, tmp, &c->rules, list) {
2103*a71a9546SAutomerger Merge Worker 		iptcc_delete_rule(r);
2104*a71a9546SAutomerger Merge Worker 	}
2105*a71a9546SAutomerger Merge Worker 
2106*a71a9546SAutomerger Merge Worker 	c->num_rules = 0;
2107*a71a9546SAutomerger Merge Worker 
2108*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2109*a71a9546SAutomerger Merge Worker 
2110*a71a9546SAutomerger Merge Worker 	return 1;
2111*a71a9546SAutomerger Merge Worker }
2112*a71a9546SAutomerger Merge Worker 
2113*a71a9546SAutomerger Merge Worker /* Zeroes the counters in a chain. */
2114*a71a9546SAutomerger Merge Worker int
TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain,struct xtc_handle * handle)2115*a71a9546SAutomerger Merge Worker TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
2116*a71a9546SAutomerger Merge Worker {
2117*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2118*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
2119*a71a9546SAutomerger Merge Worker 
2120*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_ZERO_ENTRIES;
2121*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2122*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2123*a71a9546SAutomerger Merge Worker 		return 0;
2124*a71a9546SAutomerger Merge Worker 	}
2125*a71a9546SAutomerger Merge Worker 
2126*a71a9546SAutomerger Merge Worker 	if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2127*a71a9546SAutomerger Merge Worker 		c->counter_map.maptype = COUNTER_MAP_ZEROED;
2128*a71a9546SAutomerger Merge Worker 
2129*a71a9546SAutomerger Merge Worker 	list_for_each_entry(r, &c->rules, list) {
2130*a71a9546SAutomerger Merge Worker 		if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2131*a71a9546SAutomerger Merge Worker 			r->counter_map.maptype = COUNTER_MAP_ZEROED;
2132*a71a9546SAutomerger Merge Worker 	}
2133*a71a9546SAutomerger Merge Worker 
2134*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2135*a71a9546SAutomerger Merge Worker 
2136*a71a9546SAutomerger Merge Worker 	return 1;
2137*a71a9546SAutomerger Merge Worker }
2138*a71a9546SAutomerger Merge Worker 
2139*a71a9546SAutomerger Merge Worker STRUCT_COUNTERS *
TC_READ_COUNTER(const IPT_CHAINLABEL chain,unsigned int rulenum,struct xtc_handle * handle)2140*a71a9546SAutomerger Merge Worker TC_READ_COUNTER(const IPT_CHAINLABEL chain,
2141*a71a9546SAutomerger Merge Worker 		unsigned int rulenum,
2142*a71a9546SAutomerger Merge Worker 		struct xtc_handle *handle)
2143*a71a9546SAutomerger Merge Worker {
2144*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2145*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
2146*a71a9546SAutomerger Merge Worker 
2147*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_READ_COUNTER;
2148*a71a9546SAutomerger Merge Worker 
2149*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2150*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2151*a71a9546SAutomerger Merge Worker 		return NULL;
2152*a71a9546SAutomerger Merge Worker 	}
2153*a71a9546SAutomerger Merge Worker 
2154*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
2155*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
2156*a71a9546SAutomerger Merge Worker 		return NULL;
2157*a71a9546SAutomerger Merge Worker 	}
2158*a71a9546SAutomerger Merge Worker 
2159*a71a9546SAutomerger Merge Worker 	return &r->entry[0].counters;
2160*a71a9546SAutomerger Merge Worker }
2161*a71a9546SAutomerger Merge Worker 
2162*a71a9546SAutomerger Merge Worker int
TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,unsigned int rulenum,struct xtc_handle * handle)2163*a71a9546SAutomerger Merge Worker TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
2164*a71a9546SAutomerger Merge Worker 		unsigned int rulenum,
2165*a71a9546SAutomerger Merge Worker 		struct xtc_handle *handle)
2166*a71a9546SAutomerger Merge Worker {
2167*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2168*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
2169*a71a9546SAutomerger Merge Worker 
2170*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_ZERO_COUNTER;
2171*a71a9546SAutomerger Merge Worker 
2172*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2173*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2174*a71a9546SAutomerger Merge Worker 		return 0;
2175*a71a9546SAutomerger Merge Worker 	}
2176*a71a9546SAutomerger Merge Worker 
2177*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
2178*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
2179*a71a9546SAutomerger Merge Worker 		return 0;
2180*a71a9546SAutomerger Merge Worker 	}
2181*a71a9546SAutomerger Merge Worker 
2182*a71a9546SAutomerger Merge Worker 	if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2183*a71a9546SAutomerger Merge Worker 		r->counter_map.maptype = COUNTER_MAP_ZEROED;
2184*a71a9546SAutomerger Merge Worker 
2185*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2186*a71a9546SAutomerger Merge Worker 
2187*a71a9546SAutomerger Merge Worker 	return 1;
2188*a71a9546SAutomerger Merge Worker }
2189*a71a9546SAutomerger Merge Worker 
2190*a71a9546SAutomerger Merge Worker int
TC_SET_COUNTER(const IPT_CHAINLABEL chain,unsigned int rulenum,STRUCT_COUNTERS * counters,struct xtc_handle * handle)2191*a71a9546SAutomerger Merge Worker TC_SET_COUNTER(const IPT_CHAINLABEL chain,
2192*a71a9546SAutomerger Merge Worker 	       unsigned int rulenum,
2193*a71a9546SAutomerger Merge Worker 	       STRUCT_COUNTERS *counters,
2194*a71a9546SAutomerger Merge Worker 	       struct xtc_handle *handle)
2195*a71a9546SAutomerger Merge Worker {
2196*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2197*a71a9546SAutomerger Merge Worker 	struct rule_head *r;
2198*a71a9546SAutomerger Merge Worker 	STRUCT_ENTRY *e;
2199*a71a9546SAutomerger Merge Worker 
2200*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_SET_COUNTER;
2201*a71a9546SAutomerger Merge Worker 
2202*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2203*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2204*a71a9546SAutomerger Merge Worker 		return 0;
2205*a71a9546SAutomerger Merge Worker 	}
2206*a71a9546SAutomerger Merge Worker 
2207*a71a9546SAutomerger Merge Worker 	if (!(r = iptcc_get_rule_num(c, rulenum))) {
2208*a71a9546SAutomerger Merge Worker 		errno = E2BIG;
2209*a71a9546SAutomerger Merge Worker 		return 0;
2210*a71a9546SAutomerger Merge Worker 	}
2211*a71a9546SAutomerger Merge Worker 
2212*a71a9546SAutomerger Merge Worker 	e = r->entry;
2213*a71a9546SAutomerger Merge Worker 	r->counter_map.maptype = COUNTER_MAP_SET;
2214*a71a9546SAutomerger Merge Worker 
2215*a71a9546SAutomerger Merge Worker 	memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
2216*a71a9546SAutomerger Merge Worker 
2217*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2218*a71a9546SAutomerger Merge Worker 
2219*a71a9546SAutomerger Merge Worker 	return 1;
2220*a71a9546SAutomerger Merge Worker }
2221*a71a9546SAutomerger Merge Worker 
2222*a71a9546SAutomerger Merge Worker /* Creates a new chain. */
2223*a71a9546SAutomerger Merge Worker /* To create a chain, create two rules: error node and unconditional
2224*a71a9546SAutomerger Merge Worker  * return. */
2225*a71a9546SAutomerger Merge Worker int
TC_CREATE_CHAIN(const IPT_CHAINLABEL chain,struct xtc_handle * handle)2226*a71a9546SAutomerger Merge Worker TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
2227*a71a9546SAutomerger Merge Worker {
2228*a71a9546SAutomerger Merge Worker 	static struct chain_head *c;
2229*a71a9546SAutomerger Merge Worker 	int capacity;
2230*a71a9546SAutomerger Merge Worker 	int exceeded;
2231*a71a9546SAutomerger Merge Worker 
2232*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_CREATE_CHAIN;
2233*a71a9546SAutomerger Merge Worker 
2234*a71a9546SAutomerger Merge Worker 	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
2235*a71a9546SAutomerger Merge Worker            QUEUE, RETURN. */
2236*a71a9546SAutomerger Merge Worker 	if (iptcc_find_label(chain, handle)
2237*a71a9546SAutomerger Merge Worker 	    || strcmp(chain, LABEL_DROP) == 0
2238*a71a9546SAutomerger Merge Worker 	    || strcmp(chain, LABEL_ACCEPT) == 0
2239*a71a9546SAutomerger Merge Worker 	    || strcmp(chain, LABEL_QUEUE) == 0
2240*a71a9546SAutomerger Merge Worker 	    || strcmp(chain, LABEL_RETURN) == 0) {
2241*a71a9546SAutomerger Merge Worker 		DEBUGP("Chain `%s' already exists\n", chain);
2242*a71a9546SAutomerger Merge Worker 		errno = EEXIST;
2243*a71a9546SAutomerger Merge Worker 		return 0;
2244*a71a9546SAutomerger Merge Worker 	}
2245*a71a9546SAutomerger Merge Worker 
2246*a71a9546SAutomerger Merge Worker 	if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
2247*a71a9546SAutomerger Merge Worker 		DEBUGP("Chain name `%s' too long\n", chain);
2248*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
2249*a71a9546SAutomerger Merge Worker 		return 0;
2250*a71a9546SAutomerger Merge Worker 	}
2251*a71a9546SAutomerger Merge Worker 
2252*a71a9546SAutomerger Merge Worker 	c = iptcc_alloc_chain_head(chain, 0);
2253*a71a9546SAutomerger Merge Worker 	if (!c) {
2254*a71a9546SAutomerger Merge Worker 		DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
2255*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
2256*a71a9546SAutomerger Merge Worker 		return 0;
2257*a71a9546SAutomerger Merge Worker 
2258*a71a9546SAutomerger Merge Worker 	}
2259*a71a9546SAutomerger Merge Worker 	handle->num_chains++; /* New user defined chain */
2260*a71a9546SAutomerger Merge Worker 
2261*a71a9546SAutomerger Merge Worker 	DEBUGP("Creating chain `%s'\n", chain);
2262*a71a9546SAutomerger Merge Worker 	iptc_insert_chain(handle, c); /* Insert sorted */
2263*a71a9546SAutomerger Merge Worker 
2264*a71a9546SAutomerger Merge Worker 	/* Inserting chains don't change the correctness of the chain
2265*a71a9546SAutomerger Merge Worker 	 * index (except if its smaller than index[0], but that
2266*a71a9546SAutomerger Merge Worker 	 * handled by iptc_insert_chain).  It only causes longer lists
2267*a71a9546SAutomerger Merge Worker 	 * in the buckets. Thus, only rebuild chain index when the
2268*a71a9546SAutomerger Merge Worker 	 * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains.
2269*a71a9546SAutomerger Merge Worker 	 */
2270*a71a9546SAutomerger Merge Worker 	capacity = handle->chain_index_sz * CHAIN_INDEX_BUCKET_LEN;
2271*a71a9546SAutomerger Merge Worker 	exceeded = handle->num_chains - capacity;
2272*a71a9546SAutomerger Merge Worker 	if (exceeded > CHAIN_INDEX_INSERT_MAX) {
2273*a71a9546SAutomerger Merge Worker 		debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n",
2274*a71a9546SAutomerger Merge Worker 		      capacity, exceeded, handle->num_chains);
2275*a71a9546SAutomerger Merge Worker 		iptcc_chain_index_rebuild(handle);
2276*a71a9546SAutomerger Merge Worker 	}
2277*a71a9546SAutomerger Merge Worker 
2278*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2279*a71a9546SAutomerger Merge Worker 
2280*a71a9546SAutomerger Merge Worker 	return 1;
2281*a71a9546SAutomerger Merge Worker }
2282*a71a9546SAutomerger Merge Worker 
2283*a71a9546SAutomerger Merge Worker /* Get the number of references to this chain. */
2284*a71a9546SAutomerger Merge Worker int
TC_GET_REFERENCES(unsigned int * ref,const IPT_CHAINLABEL chain,struct xtc_handle * handle)2285*a71a9546SAutomerger Merge Worker TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
2286*a71a9546SAutomerger Merge Worker 		  struct xtc_handle *handle)
2287*a71a9546SAutomerger Merge Worker {
2288*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2289*a71a9546SAutomerger Merge Worker 
2290*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_GET_REFERENCES;
2291*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2292*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2293*a71a9546SAutomerger Merge Worker 		return 0;
2294*a71a9546SAutomerger Merge Worker 	}
2295*a71a9546SAutomerger Merge Worker 
2296*a71a9546SAutomerger Merge Worker 	*ref = c->references;
2297*a71a9546SAutomerger Merge Worker 
2298*a71a9546SAutomerger Merge Worker 	return 1;
2299*a71a9546SAutomerger Merge Worker }
2300*a71a9546SAutomerger Merge Worker 
2301*a71a9546SAutomerger Merge Worker /* Deletes a chain. */
2302*a71a9546SAutomerger Merge Worker int
TC_DELETE_CHAIN(const IPT_CHAINLABEL chain,struct xtc_handle * handle)2303*a71a9546SAutomerger Merge Worker TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
2304*a71a9546SAutomerger Merge Worker {
2305*a71a9546SAutomerger Merge Worker 	unsigned int references;
2306*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2307*a71a9546SAutomerger Merge Worker 
2308*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_DELETE_CHAIN;
2309*a71a9546SAutomerger Merge Worker 
2310*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2311*a71a9546SAutomerger Merge Worker 		DEBUGP("cannot find chain `%s'\n", chain);
2312*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2313*a71a9546SAutomerger Merge Worker 		return 0;
2314*a71a9546SAutomerger Merge Worker 	}
2315*a71a9546SAutomerger Merge Worker 
2316*a71a9546SAutomerger Merge Worker 	if (TC_BUILTIN(chain, handle)) {
2317*a71a9546SAutomerger Merge Worker 		DEBUGP("cannot remove builtin chain `%s'\n", chain);
2318*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
2319*a71a9546SAutomerger Merge Worker 		return 0;
2320*a71a9546SAutomerger Merge Worker 	}
2321*a71a9546SAutomerger Merge Worker 
2322*a71a9546SAutomerger Merge Worker 	if (!TC_GET_REFERENCES(&references, chain, handle)) {
2323*a71a9546SAutomerger Merge Worker 		DEBUGP("cannot get references on chain `%s'\n", chain);
2324*a71a9546SAutomerger Merge Worker 		return 0;
2325*a71a9546SAutomerger Merge Worker 	}
2326*a71a9546SAutomerger Merge Worker 
2327*a71a9546SAutomerger Merge Worker 	if (references > 0) {
2328*a71a9546SAutomerger Merge Worker 		DEBUGP("chain `%s' still has references\n", chain);
2329*a71a9546SAutomerger Merge Worker 		errno = EMLINK;
2330*a71a9546SAutomerger Merge Worker 		return 0;
2331*a71a9546SAutomerger Merge Worker 	}
2332*a71a9546SAutomerger Merge Worker 
2333*a71a9546SAutomerger Merge Worker 	if (c->num_rules) {
2334*a71a9546SAutomerger Merge Worker 		DEBUGP("chain `%s' is not empty\n", chain);
2335*a71a9546SAutomerger Merge Worker 		errno = ENOTEMPTY;
2336*a71a9546SAutomerger Merge Worker 		return 0;
2337*a71a9546SAutomerger Merge Worker 	}
2338*a71a9546SAutomerger Merge Worker 
2339*a71a9546SAutomerger Merge Worker 	/* If we are about to delete the chain that is the current
2340*a71a9546SAutomerger Merge Worker 	 * iterator, move chain iterator forward. */
2341*a71a9546SAutomerger Merge Worker 	if (c == handle->chain_iterator_cur)
2342*a71a9546SAutomerger Merge Worker 		iptcc_chain_iterator_advance(handle);
2343*a71a9546SAutomerger Merge Worker 
2344*a71a9546SAutomerger Merge Worker 	handle->num_chains--; /* One user defined chain deleted */
2345*a71a9546SAutomerger Merge Worker 
2346*a71a9546SAutomerger Merge Worker 	//list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */
2347*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_delete_chain(c, handle);
2348*a71a9546SAutomerger Merge Worker 	free(c);
2349*a71a9546SAutomerger Merge Worker 
2350*a71a9546SAutomerger Merge Worker 	DEBUGP("chain `%s' deleted\n", chain);
2351*a71a9546SAutomerger Merge Worker 
2352*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2353*a71a9546SAutomerger Merge Worker 
2354*a71a9546SAutomerger Merge Worker 	return 1;
2355*a71a9546SAutomerger Merge Worker }
2356*a71a9546SAutomerger Merge Worker 
2357*a71a9546SAutomerger Merge Worker /* Renames a chain. */
TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,const IPT_CHAINLABEL newname,struct xtc_handle * handle)2358*a71a9546SAutomerger Merge Worker int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
2359*a71a9546SAutomerger Merge Worker 		    const IPT_CHAINLABEL newname,
2360*a71a9546SAutomerger Merge Worker 		    struct xtc_handle *handle)
2361*a71a9546SAutomerger Merge Worker {
2362*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2363*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_RENAME_CHAIN;
2364*a71a9546SAutomerger Merge Worker 
2365*a71a9546SAutomerger Merge Worker 	/* find_label doesn't cover built-in targets: DROP, ACCEPT,
2366*a71a9546SAutomerger Merge Worker            QUEUE, RETURN. */
2367*a71a9546SAutomerger Merge Worker 	if (iptcc_find_label(newname, handle)
2368*a71a9546SAutomerger Merge Worker 	    || strcmp(newname, LABEL_DROP) == 0
2369*a71a9546SAutomerger Merge Worker 	    || strcmp(newname, LABEL_ACCEPT) == 0
2370*a71a9546SAutomerger Merge Worker 	    || strcmp(newname, LABEL_QUEUE) == 0
2371*a71a9546SAutomerger Merge Worker 	    || strcmp(newname, LABEL_RETURN) == 0) {
2372*a71a9546SAutomerger Merge Worker 		errno = EEXIST;
2373*a71a9546SAutomerger Merge Worker 		return 0;
2374*a71a9546SAutomerger Merge Worker 	}
2375*a71a9546SAutomerger Merge Worker 
2376*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(oldname, handle))
2377*a71a9546SAutomerger Merge Worker 	    || TC_BUILTIN(oldname, handle)) {
2378*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2379*a71a9546SAutomerger Merge Worker 		return 0;
2380*a71a9546SAutomerger Merge Worker 	}
2381*a71a9546SAutomerger Merge Worker 
2382*a71a9546SAutomerger Merge Worker 	if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
2383*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
2384*a71a9546SAutomerger Merge Worker 		return 0;
2385*a71a9546SAutomerger Merge Worker 	}
2386*a71a9546SAutomerger Merge Worker 
2387*a71a9546SAutomerger Merge Worker 	handle->num_chains--;
2388*a71a9546SAutomerger Merge Worker 
2389*a71a9546SAutomerger Merge Worker 	/* This only unlinks "c" from the list, thus no free(c) */
2390*a71a9546SAutomerger Merge Worker 	iptcc_chain_index_delete_chain(c, handle);
2391*a71a9546SAutomerger Merge Worker 
2392*a71a9546SAutomerger Merge Worker 	/* Change the name of the chain */
2393*a71a9546SAutomerger Merge Worker 	strncpy(c->name, newname, sizeof(IPT_CHAINLABEL) - 1);
2394*a71a9546SAutomerger Merge Worker 
2395*a71a9546SAutomerger Merge Worker 	handle->num_chains++;
2396*a71a9546SAutomerger Merge Worker 
2397*a71a9546SAutomerger Merge Worker 	/* Insert sorted into to list again */
2398*a71a9546SAutomerger Merge Worker 	iptc_insert_chain(handle, c);
2399*a71a9546SAutomerger Merge Worker 
2400*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2401*a71a9546SAutomerger Merge Worker 
2402*a71a9546SAutomerger Merge Worker 	return 1;
2403*a71a9546SAutomerger Merge Worker }
2404*a71a9546SAutomerger Merge Worker 
2405*a71a9546SAutomerger Merge Worker /* Sets the policy on a built-in chain. */
2406*a71a9546SAutomerger Merge Worker int
TC_SET_POLICY(const IPT_CHAINLABEL chain,const IPT_CHAINLABEL policy,STRUCT_COUNTERS * counters,struct xtc_handle * handle)2407*a71a9546SAutomerger Merge Worker TC_SET_POLICY(const IPT_CHAINLABEL chain,
2408*a71a9546SAutomerger Merge Worker 	      const IPT_CHAINLABEL policy,
2409*a71a9546SAutomerger Merge Worker 	      STRUCT_COUNTERS *counters,
2410*a71a9546SAutomerger Merge Worker 	      struct xtc_handle *handle)
2411*a71a9546SAutomerger Merge Worker {
2412*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2413*a71a9546SAutomerger Merge Worker 
2414*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_SET_POLICY;
2415*a71a9546SAutomerger Merge Worker 
2416*a71a9546SAutomerger Merge Worker 	if (!(c = iptcc_find_label(chain, handle))) {
2417*a71a9546SAutomerger Merge Worker 		DEBUGP("cannot find chain `%s'\n", chain);
2418*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2419*a71a9546SAutomerger Merge Worker 		return 0;
2420*a71a9546SAutomerger Merge Worker 	}
2421*a71a9546SAutomerger Merge Worker 
2422*a71a9546SAutomerger Merge Worker 	if (!iptcc_is_builtin(c)) {
2423*a71a9546SAutomerger Merge Worker 		DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
2424*a71a9546SAutomerger Merge Worker 		errno = ENOENT;
2425*a71a9546SAutomerger Merge Worker 		return 0;
2426*a71a9546SAutomerger Merge Worker 	}
2427*a71a9546SAutomerger Merge Worker 
2428*a71a9546SAutomerger Merge Worker 	if (strcmp(policy, LABEL_ACCEPT) == 0)
2429*a71a9546SAutomerger Merge Worker 		c->verdict = -NF_ACCEPT - 1;
2430*a71a9546SAutomerger Merge Worker 	else if (strcmp(policy, LABEL_DROP) == 0)
2431*a71a9546SAutomerger Merge Worker 		c->verdict = -NF_DROP - 1;
2432*a71a9546SAutomerger Merge Worker 	else {
2433*a71a9546SAutomerger Merge Worker 		errno = EINVAL;
2434*a71a9546SAutomerger Merge Worker 		return 0;
2435*a71a9546SAutomerger Merge Worker 	}
2436*a71a9546SAutomerger Merge Worker 
2437*a71a9546SAutomerger Merge Worker 	if (counters) {
2438*a71a9546SAutomerger Merge Worker 		/* set byte and packet counters */
2439*a71a9546SAutomerger Merge Worker 		memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
2440*a71a9546SAutomerger Merge Worker 		c->counter_map.maptype = COUNTER_MAP_SET;
2441*a71a9546SAutomerger Merge Worker 	} else {
2442*a71a9546SAutomerger Merge Worker 		c->counter_map.maptype = COUNTER_MAP_NOMAP;
2443*a71a9546SAutomerger Merge Worker 	}
2444*a71a9546SAutomerger Merge Worker 
2445*a71a9546SAutomerger Merge Worker 	set_changed(handle);
2446*a71a9546SAutomerger Merge Worker 
2447*a71a9546SAutomerger Merge Worker 	return 1;
2448*a71a9546SAutomerger Merge Worker }
2449*a71a9546SAutomerger Merge Worker 
2450*a71a9546SAutomerger Merge Worker /* Without this, on gcc 2.7.2.3, we get:
2451*a71a9546SAutomerger Merge Worker    libiptc.c: In function `TC_COMMIT':
2452*a71a9546SAutomerger Merge Worker    libiptc.c:833: fixed or forbidden register was spilled.
2453*a71a9546SAutomerger Merge Worker    This may be due to a compiler bug or to impossible asm
2454*a71a9546SAutomerger Merge Worker    statements or clauses.
2455*a71a9546SAutomerger Merge Worker */
2456*a71a9546SAutomerger Merge Worker static void
subtract_counters(STRUCT_COUNTERS * answer,const STRUCT_COUNTERS * a,const STRUCT_COUNTERS * b)2457*a71a9546SAutomerger Merge Worker subtract_counters(STRUCT_COUNTERS *answer,
2458*a71a9546SAutomerger Merge Worker 		  const STRUCT_COUNTERS *a,
2459*a71a9546SAutomerger Merge Worker 		  const STRUCT_COUNTERS *b)
2460*a71a9546SAutomerger Merge Worker {
2461*a71a9546SAutomerger Merge Worker 	answer->pcnt = a->pcnt - b->pcnt;
2462*a71a9546SAutomerger Merge Worker 	answer->bcnt = a->bcnt - b->bcnt;
2463*a71a9546SAutomerger Merge Worker }
2464*a71a9546SAutomerger Merge Worker 
2465*a71a9546SAutomerger Merge Worker 
counters_nomap(STRUCT_COUNTERS_INFO * newcounters,unsigned int idx)2466*a71a9546SAutomerger Merge Worker static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx)
2467*a71a9546SAutomerger Merge Worker {
2468*a71a9546SAutomerger Merge Worker 	newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0});
2469*a71a9546SAutomerger Merge Worker 	DEBUGP_C("NOMAP => zero\n");
2470*a71a9546SAutomerger Merge Worker }
2471*a71a9546SAutomerger Merge Worker 
counters_normal_map(STRUCT_COUNTERS_INFO * newcounters,STRUCT_REPLACE * repl,unsigned int idx,unsigned int mappos)2472*a71a9546SAutomerger Merge Worker static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
2473*a71a9546SAutomerger Merge Worker 				STRUCT_REPLACE *repl, unsigned int idx,
2474*a71a9546SAutomerger Merge Worker 				unsigned int mappos)
2475*a71a9546SAutomerger Merge Worker {
2476*a71a9546SAutomerger Merge Worker 	/* Original read: X.
2477*a71a9546SAutomerger Merge Worker 	 * Atomic read on replacement: X + Y.
2478*a71a9546SAutomerger Merge Worker 	 * Currently in kernel: Z.
2479*a71a9546SAutomerger Merge Worker 	 * Want in kernel: X + Y + Z.
2480*a71a9546SAutomerger Merge Worker 	 * => Add in X + Y
2481*a71a9546SAutomerger Merge Worker 	 * => Add in replacement read.
2482*a71a9546SAutomerger Merge Worker 	 */
2483*a71a9546SAutomerger Merge Worker 	newcounters->counters[idx] = repl->counters[mappos];
2484*a71a9546SAutomerger Merge Worker 	DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
2485*a71a9546SAutomerger Merge Worker }
2486*a71a9546SAutomerger Merge Worker 
counters_map_zeroed(STRUCT_COUNTERS_INFO * newcounters,STRUCT_REPLACE * repl,unsigned int idx,unsigned int mappos,STRUCT_COUNTERS * counters)2487*a71a9546SAutomerger Merge Worker static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
2488*a71a9546SAutomerger Merge Worker 				STRUCT_REPLACE *repl, unsigned int idx,
2489*a71a9546SAutomerger Merge Worker 				unsigned int mappos, STRUCT_COUNTERS *counters)
2490*a71a9546SAutomerger Merge Worker {
2491*a71a9546SAutomerger Merge Worker 	/* Original read: X.
2492*a71a9546SAutomerger Merge Worker 	 * Atomic read on replacement: X + Y.
2493*a71a9546SAutomerger Merge Worker 	 * Currently in kernel: Z.
2494*a71a9546SAutomerger Merge Worker 	 * Want in kernel: Y + Z.
2495*a71a9546SAutomerger Merge Worker 	 * => Add in Y.
2496*a71a9546SAutomerger Merge Worker 	 * => Add in (replacement read - original read).
2497*a71a9546SAutomerger Merge Worker 	 */
2498*a71a9546SAutomerger Merge Worker 	subtract_counters(&newcounters->counters[idx],
2499*a71a9546SAutomerger Merge Worker 			  &repl->counters[mappos],
2500*a71a9546SAutomerger Merge Worker 			  counters);
2501*a71a9546SAutomerger Merge Worker 	DEBUGP_C("ZEROED => mappos %u\n", mappos);
2502*a71a9546SAutomerger Merge Worker }
2503*a71a9546SAutomerger Merge Worker 
counters_map_set(STRUCT_COUNTERS_INFO * newcounters,unsigned int idx,STRUCT_COUNTERS * counters)2504*a71a9546SAutomerger Merge Worker static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
2505*a71a9546SAutomerger Merge Worker                              unsigned int idx, STRUCT_COUNTERS *counters)
2506*a71a9546SAutomerger Merge Worker {
2507*a71a9546SAutomerger Merge Worker 	/* Want to set counter (iptables-restore) */
2508*a71a9546SAutomerger Merge Worker 
2509*a71a9546SAutomerger Merge Worker 	memcpy(&newcounters->counters[idx], counters,
2510*a71a9546SAutomerger Merge Worker 		sizeof(STRUCT_COUNTERS));
2511*a71a9546SAutomerger Merge Worker 
2512*a71a9546SAutomerger Merge Worker 	DEBUGP_C("SET\n");
2513*a71a9546SAutomerger Merge Worker }
2514*a71a9546SAutomerger Merge Worker 
2515*a71a9546SAutomerger Merge Worker 
2516*a71a9546SAutomerger Merge Worker int
TC_COMMIT(struct xtc_handle * handle)2517*a71a9546SAutomerger Merge Worker TC_COMMIT(struct xtc_handle *handle)
2518*a71a9546SAutomerger Merge Worker {
2519*a71a9546SAutomerger Merge Worker 	/* Replace, then map back the counters. */
2520*a71a9546SAutomerger Merge Worker 	STRUCT_REPLACE *repl;
2521*a71a9546SAutomerger Merge Worker 	STRUCT_COUNTERS_INFO *newcounters;
2522*a71a9546SAutomerger Merge Worker 	struct chain_head *c;
2523*a71a9546SAutomerger Merge Worker 	int ret;
2524*a71a9546SAutomerger Merge Worker 	size_t counterlen;
2525*a71a9546SAutomerger Merge Worker 	int new_number;
2526*a71a9546SAutomerger Merge Worker 	unsigned int new_size;
2527*a71a9546SAutomerger Merge Worker 
2528*a71a9546SAutomerger Merge Worker 	iptc_fn = TC_COMMIT;
2529*a71a9546SAutomerger Merge Worker 
2530*a71a9546SAutomerger Merge Worker 	/* Don't commit if nothing changed. */
2531*a71a9546SAutomerger Merge Worker 	if (!handle->changed)
2532*a71a9546SAutomerger Merge Worker 		goto finished;
2533*a71a9546SAutomerger Merge Worker 
2534*a71a9546SAutomerger Merge Worker 	new_number = iptcc_compile_table_prep(handle, &new_size);
2535*a71a9546SAutomerger Merge Worker 	if (new_number < 0) {
2536*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
2537*a71a9546SAutomerger Merge Worker 		goto out_zero;
2538*a71a9546SAutomerger Merge Worker 	}
2539*a71a9546SAutomerger Merge Worker 
2540*a71a9546SAutomerger Merge Worker 	repl = malloc(sizeof(*repl) + new_size);
2541*a71a9546SAutomerger Merge Worker 	if (!repl) {
2542*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
2543*a71a9546SAutomerger Merge Worker 		goto out_zero;
2544*a71a9546SAutomerger Merge Worker 	}
2545*a71a9546SAutomerger Merge Worker 	memset(repl, 0, sizeof(*repl) + new_size);
2546*a71a9546SAutomerger Merge Worker 
2547*a71a9546SAutomerger Merge Worker #if 0
2548*a71a9546SAutomerger Merge Worker 	TC_DUMP_ENTRIES(*handle);
2549*a71a9546SAutomerger Merge Worker #endif
2550*a71a9546SAutomerger Merge Worker 
2551*a71a9546SAutomerger Merge Worker 	counterlen = sizeof(STRUCT_COUNTERS_INFO)
2552*a71a9546SAutomerger Merge Worker 			+ sizeof(STRUCT_COUNTERS) * new_number;
2553*a71a9546SAutomerger Merge Worker 
2554*a71a9546SAutomerger Merge Worker 	/* These are the old counters we will get from kernel */
2555*a71a9546SAutomerger Merge Worker 	repl->counters = calloc(handle->info.num_entries,
2556*a71a9546SAutomerger Merge Worker 				sizeof(STRUCT_COUNTERS));
2557*a71a9546SAutomerger Merge Worker 	if (!repl->counters) {
2558*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
2559*a71a9546SAutomerger Merge Worker 		goto out_free_repl;
2560*a71a9546SAutomerger Merge Worker 	}
2561*a71a9546SAutomerger Merge Worker 	/* These are the counters we're going to put back, later. */
2562*a71a9546SAutomerger Merge Worker 	newcounters = malloc(counterlen);
2563*a71a9546SAutomerger Merge Worker 	if (!newcounters) {
2564*a71a9546SAutomerger Merge Worker 		errno = ENOMEM;
2565*a71a9546SAutomerger Merge Worker 		goto out_free_repl_counters;
2566*a71a9546SAutomerger Merge Worker 	}
2567*a71a9546SAutomerger Merge Worker 	memset(newcounters, 0, counterlen);
2568*a71a9546SAutomerger Merge Worker 
2569*a71a9546SAutomerger Merge Worker 	strcpy(repl->name, handle->info.name);
2570*a71a9546SAutomerger Merge Worker 	repl->num_entries = new_number;
2571*a71a9546SAutomerger Merge Worker 	repl->size = new_size;
2572*a71a9546SAutomerger Merge Worker 
2573*a71a9546SAutomerger Merge Worker 	repl->num_counters = handle->info.num_entries;
2574*a71a9546SAutomerger Merge Worker 	repl->valid_hooks  = handle->info.valid_hooks;
2575*a71a9546SAutomerger Merge Worker 
2576*a71a9546SAutomerger Merge Worker 	DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
2577*a71a9546SAutomerger Merge Worker 		repl->num_entries, repl->size, repl->num_counters);
2578*a71a9546SAutomerger Merge Worker 
2579*a71a9546SAutomerger Merge Worker 	ret = iptcc_compile_table(handle, repl);
2580*a71a9546SAutomerger Merge Worker 	if (ret < 0) {
2581*a71a9546SAutomerger Merge Worker 		errno = ret;
2582*a71a9546SAutomerger Merge Worker 		goto out_free_newcounters;
2583*a71a9546SAutomerger Merge Worker 	}
2584*a71a9546SAutomerger Merge Worker 
2585*a71a9546SAutomerger Merge Worker 
2586*a71a9546SAutomerger Merge Worker #ifdef IPTC_DEBUG2
2587*a71a9546SAutomerger Merge Worker 	{
2588*a71a9546SAutomerger Merge Worker 		int fd = open("/tmp/libiptc-so_set_replace.blob",
2589*a71a9546SAutomerger Merge Worker 				O_CREAT|O_WRONLY, 0644);
2590*a71a9546SAutomerger Merge Worker 		if (fd >= 0) {
2591*a71a9546SAutomerger Merge Worker 			write(fd, repl, sizeof(*repl) + repl->size);
2592*a71a9546SAutomerger Merge Worker 			close(fd);
2593*a71a9546SAutomerger Merge Worker 		}
2594*a71a9546SAutomerger Merge Worker 	}
2595*a71a9546SAutomerger Merge Worker #endif
2596*a71a9546SAutomerger Merge Worker 
2597*a71a9546SAutomerger Merge Worker 	ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
2598*a71a9546SAutomerger Merge Worker 			 sizeof(*repl) + repl->size);
2599*a71a9546SAutomerger Merge Worker 	if (ret < 0)
2600*a71a9546SAutomerger Merge Worker 		goto out_free_newcounters;
2601*a71a9546SAutomerger Merge Worker 
2602*a71a9546SAutomerger Merge Worker 	/* Put counters back. */
2603*a71a9546SAutomerger Merge Worker 	strcpy(newcounters->name, handle->info.name);
2604*a71a9546SAutomerger Merge Worker 	newcounters->num_counters = new_number;
2605*a71a9546SAutomerger Merge Worker 
2606*a71a9546SAutomerger Merge Worker 	list_for_each_entry(c, &handle->chains, list) {
2607*a71a9546SAutomerger Merge Worker 		struct rule_head *r;
2608*a71a9546SAutomerger Merge Worker 
2609*a71a9546SAutomerger Merge Worker 		/* Builtin chains have their own counters */
2610*a71a9546SAutomerger Merge Worker 		if (iptcc_is_builtin(c)) {
2611*a71a9546SAutomerger Merge Worker 			DEBUGP("counter for chain-index %u: ", c->foot_index);
2612*a71a9546SAutomerger Merge Worker 			switch(c->counter_map.maptype) {
2613*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_NOMAP:
2614*a71a9546SAutomerger Merge Worker 				counters_nomap(newcounters, c->foot_index);
2615*a71a9546SAutomerger Merge Worker 				break;
2616*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_NORMAL_MAP:
2617*a71a9546SAutomerger Merge Worker 				counters_normal_map(newcounters, repl,
2618*a71a9546SAutomerger Merge Worker 						    c->foot_index,
2619*a71a9546SAutomerger Merge Worker 						    c->counter_map.mappos);
2620*a71a9546SAutomerger Merge Worker 				break;
2621*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_ZEROED:
2622*a71a9546SAutomerger Merge Worker 				counters_map_zeroed(newcounters, repl,
2623*a71a9546SAutomerger Merge Worker 						    c->foot_index,
2624*a71a9546SAutomerger Merge Worker 						    c->counter_map.mappos,
2625*a71a9546SAutomerger Merge Worker 						    &c->counters);
2626*a71a9546SAutomerger Merge Worker 				break;
2627*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_SET:
2628*a71a9546SAutomerger Merge Worker 				counters_map_set(newcounters, c->foot_index,
2629*a71a9546SAutomerger Merge Worker 						 &c->counters);
2630*a71a9546SAutomerger Merge Worker 				break;
2631*a71a9546SAutomerger Merge Worker 			}
2632*a71a9546SAutomerger Merge Worker 		}
2633*a71a9546SAutomerger Merge Worker 
2634*a71a9546SAutomerger Merge Worker 		list_for_each_entry(r, &c->rules, list) {
2635*a71a9546SAutomerger Merge Worker 			DEBUGP("counter for index %u: ", r->index);
2636*a71a9546SAutomerger Merge Worker 			switch (r->counter_map.maptype) {
2637*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_NOMAP:
2638*a71a9546SAutomerger Merge Worker 				counters_nomap(newcounters, r->index);
2639*a71a9546SAutomerger Merge Worker 				break;
2640*a71a9546SAutomerger Merge Worker 
2641*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_NORMAL_MAP:
2642*a71a9546SAutomerger Merge Worker 				counters_normal_map(newcounters, repl,
2643*a71a9546SAutomerger Merge Worker 						    r->index,
2644*a71a9546SAutomerger Merge Worker 						    r->counter_map.mappos);
2645*a71a9546SAutomerger Merge Worker 				break;
2646*a71a9546SAutomerger Merge Worker 
2647*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_ZEROED:
2648*a71a9546SAutomerger Merge Worker 				counters_map_zeroed(newcounters, repl,
2649*a71a9546SAutomerger Merge Worker 						    r->index,
2650*a71a9546SAutomerger Merge Worker 						    r->counter_map.mappos,
2651*a71a9546SAutomerger Merge Worker 						    &r->entry->counters);
2652*a71a9546SAutomerger Merge Worker 				break;
2653*a71a9546SAutomerger Merge Worker 
2654*a71a9546SAutomerger Merge Worker 			case COUNTER_MAP_SET:
2655*a71a9546SAutomerger Merge Worker 				counters_map_set(newcounters, r->index,
2656*a71a9546SAutomerger Merge Worker 						 &r->entry->counters);
2657*a71a9546SAutomerger Merge Worker 				break;
2658*a71a9546SAutomerger Merge Worker 			}
2659*a71a9546SAutomerger Merge Worker 		}
2660*a71a9546SAutomerger Merge Worker 	}
2661*a71a9546SAutomerger Merge Worker 
2662*a71a9546SAutomerger Merge Worker #ifdef IPTC_DEBUG2
2663*a71a9546SAutomerger Merge Worker 	{
2664*a71a9546SAutomerger Merge Worker 		int fd = open("/tmp/libiptc-so_set_add_counters.blob",
2665*a71a9546SAutomerger Merge Worker 				O_CREAT|O_WRONLY, 0644);
2666*a71a9546SAutomerger Merge Worker 		if (fd >= 0) {
2667*a71a9546SAutomerger Merge Worker 			write(fd, newcounters, counterlen);
2668*a71a9546SAutomerger Merge Worker 			close(fd);
2669*a71a9546SAutomerger Merge Worker 		}
2670*a71a9546SAutomerger Merge Worker 	}
2671*a71a9546SAutomerger Merge Worker #endif
2672*a71a9546SAutomerger Merge Worker 
2673*a71a9546SAutomerger Merge Worker 	ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
2674*a71a9546SAutomerger Merge Worker 			 newcounters, counterlen);
2675*a71a9546SAutomerger Merge Worker 	if (ret < 0)
2676*a71a9546SAutomerger Merge Worker 		goto out_free_newcounters;
2677*a71a9546SAutomerger Merge Worker 
2678*a71a9546SAutomerger Merge Worker 	free(repl->counters);
2679*a71a9546SAutomerger Merge Worker 	free(repl);
2680*a71a9546SAutomerger Merge Worker 	free(newcounters);
2681*a71a9546SAutomerger Merge Worker 
2682*a71a9546SAutomerger Merge Worker finished:
2683*a71a9546SAutomerger Merge Worker 	return 1;
2684*a71a9546SAutomerger Merge Worker 
2685*a71a9546SAutomerger Merge Worker out_free_newcounters:
2686*a71a9546SAutomerger Merge Worker 	free(newcounters);
2687*a71a9546SAutomerger Merge Worker out_free_repl_counters:
2688*a71a9546SAutomerger Merge Worker 	free(repl->counters);
2689*a71a9546SAutomerger Merge Worker out_free_repl:
2690*a71a9546SAutomerger Merge Worker 	free(repl);
2691*a71a9546SAutomerger Merge Worker out_zero:
2692*a71a9546SAutomerger Merge Worker 	return 0;
2693*a71a9546SAutomerger Merge Worker }
2694*a71a9546SAutomerger Merge Worker 
2695*a71a9546SAutomerger Merge Worker /* Translates errno numbers into more human-readable form than strerror. */
2696*a71a9546SAutomerger Merge Worker const char *
TC_STRERROR(int err)2697*a71a9546SAutomerger Merge Worker TC_STRERROR(int err)
2698*a71a9546SAutomerger Merge Worker {
2699*a71a9546SAutomerger Merge Worker 	unsigned int i;
2700*a71a9546SAutomerger Merge Worker 	struct table_struct {
2701*a71a9546SAutomerger Merge Worker 		void *fn;
2702*a71a9546SAutomerger Merge Worker 		int err;
2703*a71a9546SAutomerger Merge Worker 		const char *message;
2704*a71a9546SAutomerger Merge Worker 	} table [] =
2705*a71a9546SAutomerger Merge Worker 	  { { TC_INIT, EPERM, "Permission denied (you must be root)" },
2706*a71a9546SAutomerger Merge Worker 	    { TC_INIT, EINVAL, "Module is wrong version" },
2707*a71a9546SAutomerger Merge Worker 	    { TC_INIT, ENOENT,
2708*a71a9546SAutomerger Merge Worker 		    "Table does not exist (do you need to insmod?)" },
2709*a71a9546SAutomerger Merge Worker 	    { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
2710*a71a9546SAutomerger Merge Worker 	    { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
2711*a71a9546SAutomerger Merge Worker 	    { TC_DELETE_CHAIN, EMLINK,
2712*a71a9546SAutomerger Merge Worker 	      "Can't delete chain with references left" },
2713*a71a9546SAutomerger Merge Worker 	    { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
2714*a71a9546SAutomerger Merge Worker 	    { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
2715*a71a9546SAutomerger Merge Worker 	    { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
2716*a71a9546SAutomerger Merge Worker 	    { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
2717*a71a9546SAutomerger Merge Worker 	    { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
2718*a71a9546SAutomerger Merge Worker 	    { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
2719*a71a9546SAutomerger Merge Worker 	    { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
2720*a71a9546SAutomerger Merge Worker 	    { TC_INSERT_ENTRY, EINVAL, "Target problem" },
2721*a71a9546SAutomerger Merge Worker 	    /* ENOENT for DELETE probably means no matching rule */
2722*a71a9546SAutomerger Merge Worker 	    { TC_DELETE_ENTRY, ENOENT,
2723*a71a9546SAutomerger Merge Worker 	      "Bad rule (does a matching rule exist in that chain?)" },
2724*a71a9546SAutomerger Merge Worker 	    { TC_SET_POLICY, ENOENT,
2725*a71a9546SAutomerger Merge Worker 	      "Bad built-in chain name" },
2726*a71a9546SAutomerger Merge Worker 	    { TC_SET_POLICY, EINVAL,
2727*a71a9546SAutomerger Merge Worker 	      "Bad policy name" },
2728*a71a9546SAutomerger Merge Worker 
2729*a71a9546SAutomerger Merge Worker 	    { NULL, 0, "Incompatible with this kernel" },
2730*a71a9546SAutomerger Merge Worker 	    { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
2731*a71a9546SAutomerger Merge Worker 	    { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
2732*a71a9546SAutomerger Merge Worker 	    { NULL, ENOMEM, "Memory allocation problem" },
2733*a71a9546SAutomerger Merge Worker 	    { NULL, ENOENT, "No chain/target/match by that name" },
2734*a71a9546SAutomerger Merge Worker 	  };
2735*a71a9546SAutomerger Merge Worker 
2736*a71a9546SAutomerger Merge Worker 	for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
2737*a71a9546SAutomerger Merge Worker 		if ((!table[i].fn || table[i].fn == iptc_fn)
2738*a71a9546SAutomerger Merge Worker 		    && table[i].err == err)
2739*a71a9546SAutomerger Merge Worker 			return table[i].message;
2740*a71a9546SAutomerger Merge Worker 	}
2741*a71a9546SAutomerger Merge Worker 
2742*a71a9546SAutomerger Merge Worker 	return strerror(err);
2743*a71a9546SAutomerger Merge Worker }
2744*a71a9546SAutomerger Merge Worker 
2745*a71a9546SAutomerger Merge Worker const struct xtc_ops TC_OPS = {
2746*a71a9546SAutomerger Merge Worker 	.commit        = TC_COMMIT,
2747*a71a9546SAutomerger Merge Worker 	.init          = TC_INIT,
2748*a71a9546SAutomerger Merge Worker 	.free          = TC_FREE,
2749*a71a9546SAutomerger Merge Worker 	.builtin       = TC_BUILTIN,
2750*a71a9546SAutomerger Merge Worker 	.is_chain      = TC_IS_CHAIN,
2751*a71a9546SAutomerger Merge Worker 	.flush_entries = TC_FLUSH_ENTRIES,
2752*a71a9546SAutomerger Merge Worker 	.create_chain  = TC_CREATE_CHAIN,
2753*a71a9546SAutomerger Merge Worker 	.first_chain   = TC_FIRST_CHAIN,
2754*a71a9546SAutomerger Merge Worker 	.next_chain    = TC_NEXT_CHAIN,
2755*a71a9546SAutomerger Merge Worker 	.get_policy    = TC_GET_POLICY,
2756*a71a9546SAutomerger Merge Worker 	.set_policy    = TC_SET_POLICY,
2757*a71a9546SAutomerger Merge Worker 	.strerror      = TC_STRERROR,
2758*a71a9546SAutomerger Merge Worker };
2759