blob: 593c5de8ca039897094824c31f5a9fee426fb5e5 [file] [log] [blame]
Martin Josefsson0f9b8b12004-12-18 17:18:49 +00001/* Library which manipulates firewall rules. Version $Revision$ */
Marc Bouchere6869a82000-03-20 06:03:29 +00002
3/* Architecture of firewall rules is as follows:
4 *
5 * Chains go INPUT, FORWARD, OUTPUT then user chains.
6 * Each user chain starts with an ERROR node.
7 * Every chain ends with an unconditional jump: a RETURN for user chains,
8 * and a POLICY for built-ins.
9 */
10
Harald Welte3ea8f402003-06-23 18:25:59 +000011/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +010012 * COPYING for details).
Harald Welteaae69be2004-08-29 23:32:14 +000013 * (C) 2000-2004 by the Netfilter Core Team <coreteam@netfilter.org>
Harald Welte3ea8f402003-06-23 18:25:59 +000014 *
Harald Weltefbc85232003-06-24 17:37:21 +000015 * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
Harald Welte3ea8f402003-06-23 18:25:59 +000016 * - Reimplementation of chain cache to use offsets instead of entries
Harald Weltefbc85232003-06-24 17:37:21 +000017 * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
Harald Welte0113fe72004-01-06 19:04:02 +000018 * - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
Harald Weltefbc85232003-06-24 17:37:21 +000019 * don't rebuild the chain cache after every operation, instead fix it
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +010020 * up after a ruleset change.
Harald Welteaae69be2004-08-29 23:32:14 +000021 * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +010022 * - further performance work: total reimplementation of libiptc.
Harald Welteaae69be2004-08-29 23:32:14 +000023 * - libiptc now has a real internal (linked-list) represntation of the
24 * ruleset and a parser/compiler from/to this internal representation
25 * - again sponsored by Astaro AG (http://www.astaro.com/)
Jesper Dangaard Brouerc9477d02009-03-23 14:27:44 +010026 *
27 * 2008-Jan+Jul: Jesper Dangaard Brouer <hawk@comx.dk>
28 * - performance work: speedup chain list "name" searching.
29 * - performance work: speedup initial ruleset parsing.
30 * - sponsored by ComX Networks A/S (http://www.comx.dk/)
Harald Welte3ea8f402003-06-23 18:25:59 +000031 */
Harald Welte15920d12004-05-16 09:05:07 +000032#include <sys/types.h>
33#include <sys/socket.h>
Stefan Tomanekd59b9db2011-03-08 22:42:51 +010034#include <stdbool.h>
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +020035#include <xtables.h>
Stephane Ouellette7cd00282004-05-14 08:21:06 +000036
Harald Welteaae69be2004-08-29 23:32:14 +000037#include "linux_list.h"
38
39//#define IPTC_DEBUG2 1
40
41#ifdef IPTC_DEBUG2
42#include <fcntl.h>
43#define DEBUGP(x, args...) fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
44#define DEBUGP_C(x, args...) fprintf(stderr, x, ## args)
45#else
46#define DEBUGP(x, args...)
47#define DEBUGP_C(x, args...)
48#endif
49
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +000050#ifdef DEBUG
51#define debug(x, args...) fprintf(stderr, x, ## args)
52#else
53#define debug(x, args...)
54#endif
55
Marc Bouchere6869a82000-03-20 06:03:29 +000056static void *iptc_fn = NULL;
57
Patrick McHardy0b639362007-09-08 16:00:01 +000058static const char *hooknames[] = {
59 [HOOK_PRE_ROUTING] = "PREROUTING",
60 [HOOK_LOCAL_IN] = "INPUT",
61 [HOOK_FORWARD] = "FORWARD",
62 [HOOK_LOCAL_OUT] = "OUTPUT",
63 [HOOK_POST_ROUTING] = "POSTROUTING",
Marc Bouchere6869a82000-03-20 06:03:29 +000064};
65
Harald Welteaae69be2004-08-29 23:32:14 +000066/* Convenience structures */
Harald Welteaae69be2004-08-29 23:32:14 +000067struct chain_head;
68struct rule_head;
69
Marc Bouchere6869a82000-03-20 06:03:29 +000070struct counter_map
71{
72 enum {
73 COUNTER_MAP_NOMAP,
74 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000075 COUNTER_MAP_ZEROED,
76 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000077 } maptype;
78 unsigned int mappos;
79};
80
Harald Welteaae69be2004-08-29 23:32:14 +000081enum iptcc_rule_type {
82 IPTCC_R_STANDARD, /* standard target (ACCEPT, ...) */
83 IPTCC_R_MODULE, /* extension module (SNAT, ...) */
84 IPTCC_R_FALLTHROUGH, /* fallthrough rule */
85 IPTCC_R_JUMP, /* jump to other chain */
Marc Bouchere6869a82000-03-20 06:03:29 +000086};
87
Harald Welteaae69be2004-08-29 23:32:14 +000088struct rule_head
Rusty Russell30fd6e52000-04-23 09:16:06 +000089{
Harald Welteaae69be2004-08-29 23:32:14 +000090 struct list_head list;
91 struct chain_head *chain;
92 struct counter_map counter_map;
93
94 unsigned int index; /* index (needed for counter_map) */
95 unsigned int offset; /* offset in rule blob */
96
97 enum iptcc_rule_type type;
98 struct chain_head *jump; /* jump target, if IPTCC_R_JUMP */
99
100 unsigned int size; /* size of entry data */
101 STRUCT_ENTRY entry[0];
102};
103
104struct chain_head
105{
106 struct list_head list;
Rusty Russell79dee072000-05-02 16:45:16 +0000107 char name[TABLE_MAXNAMELEN];
Harald Welteaae69be2004-08-29 23:32:14 +0000108 unsigned int hooknum; /* hook number+1 if builtin */
109 unsigned int references; /* how many jumps reference us */
110 int verdict; /* verdict if builtin */
111
112 STRUCT_COUNTERS counters; /* per-chain counters */
113 struct counter_map counter_map;
114
115 unsigned int num_rules; /* number of rules in list */
116 struct list_head rules; /* list of rules */
117
118 unsigned int index; /* index (needed for jump resolval) */
119 unsigned int head_offset; /* offset in rule blob */
120 unsigned int foot_index; /* index (needed for counter_map) */
121 unsigned int foot_offset; /* offset in rule blob */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000122};
123
Jan Engelhardt1639fe82011-08-27 11:39:52 +0200124struct xtc_handle {
Jan Engelhardt175f4512008-11-10 17:25:55 +0100125 int sockfd;
Harald Welteaae69be2004-08-29 23:32:14 +0000126 int changed; /* Have changes been made? */
127
128 struct list_head chains;
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100129
Harald Welteaae69be2004-08-29 23:32:14 +0000130 struct chain_head *chain_iterator_cur;
131 struct rule_head *rule_iterator_cur;
132
Jesper Dangaard Brouer48bde402008-01-15 17:06:48 +0000133 unsigned int num_chains; /* number of user defined chains */
134
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000135 struct chain_head **chain_index; /* array for fast chain list access*/
136 unsigned int chain_index_sz;/* size of chain index array */
137
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200138 int sorted_offsets; /* if chains are received sorted from kernel,
139 * then the offsets are also sorted. Says if its
140 * possible to bsearch offsets using chain_index.
141 */
142
Rusty Russell79dee072000-05-02 16:45:16 +0000143 STRUCT_GETINFO info;
Harald Welteaae69be2004-08-29 23:32:14 +0000144 STRUCT_GET_ENTRIES *entries;
Marc Bouchere6869a82000-03-20 06:03:29 +0000145};
146
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200147enum bsearch_type {
148 BSEARCH_NAME, /* Binary search after chain name */
149 BSEARCH_OFFSET, /* Binary search based on offset */
150};
151
Harald Welteaae69be2004-08-29 23:32:14 +0000152/* allocate a new chain head for the cache */
153static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
154{
155 struct chain_head *c = malloc(sizeof(*c));
156 if (!c)
157 return NULL;
158 memset(c, 0, sizeof(*c));
159
160 strncpy(c->name, name, TABLE_MAXNAMELEN);
161 c->hooknum = hooknum;
162 INIT_LIST_HEAD(&c->rules);
163
164 return c;
165}
166
167/* allocate and initialize a new rule for the cache */
168static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
169{
170 struct rule_head *r = malloc(sizeof(*r)+size);
171 if (!r)
172 return NULL;
173 memset(r, 0, sizeof(*r));
174
175 r->chain = c;
176 r->size = size;
177
178 return r;
179}
180
181/* notify us that the ruleset has been modified by the user */
Jesper Dangaard Brouer91093982008-01-15 17:01:58 +0000182static inline void
Jan Engelhardtfd187312008-11-10 16:59:27 +0100183set_changed(struct xtc_handle *h)
Rusty Russell175f6412000-03-24 09:32:20 +0000184{
Rusty Russell175f6412000-03-24 09:32:20 +0000185 h->changed = 1;
186}
187
Harald Welte380ba5f2002-02-13 16:19:55 +0000188#ifdef IPTC_DEBUG
Jan Engelhardtfd187312008-11-10 16:59:27 +0100189static void do_check(struct xtc_handle *h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000190#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000191#else
192#define CHECK(h)
193#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000194
Harald Welteaae69be2004-08-29 23:32:14 +0000195
196/**********************************************************************
197 * iptc blob utility functions (iptcb_*)
198 **********************************************************************/
199
Marc Bouchere6869a82000-03-20 06:03:29 +0000200static inline int
Harald Welteaae69be2004-08-29 23:32:14 +0000201iptcb_get_number(const STRUCT_ENTRY *i,
Rusty Russell79dee072000-05-02 16:45:16 +0000202 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000203 unsigned int *pos)
204{
205 if (i == seek)
206 return 1;
207 (*pos)++;
208 return 0;
209}
210
Marc Bouchere6869a82000-03-20 06:03:29 +0000211static inline int
Harald Welteaae69be2004-08-29 23:32:14 +0000212iptcb_get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000213 unsigned int number,
214 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000215 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000216{
217 if (*pos == number) {
218 *pe = i;
219 return 1;
220 }
221 (*pos)++;
222 return 0;
223}
224
Harald Welteaae69be2004-08-29 23:32:14 +0000225static inline STRUCT_ENTRY *
Jan Engelhardtfd187312008-11-10 16:59:27 +0100226iptcb_get_entry(struct xtc_handle *h, unsigned int offset)
Harald Welteaae69be2004-08-29 23:32:14 +0000227{
228 return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
229}
230
231static unsigned int
Jan Engelhardtfd187312008-11-10 16:59:27 +0100232iptcb_entry2index(struct xtc_handle *const h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000233{
234 unsigned int pos = 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000235
Harald Welteaae69be2004-08-29 23:32:14 +0000236 if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
237 iptcb_get_number, seek, &pos) == 0) {
238 fprintf(stderr, "ERROR: offset %u not an entry!\n",
239 (unsigned int)((char *)seek - (char *)h->entries->entrytable));
240 abort();
241 }
242 return pos;
Marc Bouchere6869a82000-03-20 06:03:29 +0000243}
244
Harald Welte0113fe72004-01-06 19:04:02 +0000245static inline STRUCT_ENTRY *
Jan Engelhardtfd187312008-11-10 16:59:27 +0100246iptcb_offset2entry(struct xtc_handle *h, unsigned int offset)
Harald Welte0113fe72004-01-06 19:04:02 +0000247{
Harald Welteaae69be2004-08-29 23:32:14 +0000248 return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
Harald Welte0113fe72004-01-06 19:04:02 +0000249}
250
Harald Welteaae69be2004-08-29 23:32:14 +0000251
Harald Welte0113fe72004-01-06 19:04:02 +0000252static inline unsigned long
Jan Engelhardtfd187312008-11-10 16:59:27 +0100253iptcb_entry2offset(struct xtc_handle *const h, const STRUCT_ENTRY *e)
Harald Welte0113fe72004-01-06 19:04:02 +0000254{
Harald Welteaae69be2004-08-29 23:32:14 +0000255 return (void *)e - (void *)h->entries->entrytable;
Harald Welte3ea8f402003-06-23 18:25:59 +0000256}
257
258static inline unsigned int
Jan Engelhardtfd187312008-11-10 16:59:27 +0100259iptcb_offset2index(struct xtc_handle *const h, unsigned int offset)
Harald Welte3ea8f402003-06-23 18:25:59 +0000260{
Harald Welteaae69be2004-08-29 23:32:14 +0000261 return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
262}
263
264/* Returns 0 if not hook entry, else hooknumber + 1 */
265static inline unsigned int
Jan Engelhardtfd187312008-11-10 16:59:27 +0100266iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, struct xtc_handle *h)
Harald Welteaae69be2004-08-29 23:32:14 +0000267{
268 unsigned int i;
269
270 for (i = 0; i < NUMHOOKS; i++) {
271 if ((h->info.valid_hooks & (1 << i))
272 && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
273 return i+1;
274 }
275 return 0;
Harald Welte3ea8f402003-06-23 18:25:59 +0000276}
277
278
Harald Welteaae69be2004-08-29 23:32:14 +0000279/**********************************************************************
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000280 * Chain index (cache utility) functions
281 **********************************************************************
282 * The chain index is an array with pointers into the chain list, with
283 * CHAIN_INDEX_BUCKET_LEN spacing. This facilitates the ability to
284 * speedup chain list searching, by find a more optimal starting
285 * points when searching the linked list.
286 *
287 * The starting point can be found fast by using a binary search of
288 * the chain index. Thus, reducing the previous search complexity of
289 * O(n) to O(log(n/k) + k) where k is CHAIN_INDEX_BUCKET_LEN.
290 *
291 * A nice property of the chain index, is that the "bucket" list
292 * length is max CHAIN_INDEX_BUCKET_LEN (when just build, inserts will
293 * change this). Oppose to hashing, where the "bucket" list length can
294 * vary a lot.
295 */
296#ifndef CHAIN_INDEX_BUCKET_LEN
297#define CHAIN_INDEX_BUCKET_LEN 40
298#endif
299
300/* Another nice property of the chain index is that inserting/creating
301 * chains in chain list don't change the correctness of the chain
302 * index, it only causes longer lists in the buckets.
303 *
304 * To mitigate the performance penalty of longer bucket lists and the
305 * penalty of rebuilding, the chain index is rebuild only when
306 * CHAIN_INDEX_INSERT_MAX chains has been added.
307 */
308#ifndef CHAIN_INDEX_INSERT_MAX
309#define CHAIN_INDEX_INSERT_MAX 355
310#endif
311
312static inline unsigned int iptcc_is_builtin(struct chain_head *c);
313
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000314/* Use binary search in the chain index array, to find a chain_head
315 * pointer closest to the place of the searched name element.
316 *
317 * Notes that, binary search (obviously) requires that the chain list
318 * is sorted by name.
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200319 *
320 * The not so obvious: The chain index array, is actually both sorted
321 * by name and offset, at the same time!. This is only true because,
322 * chain are stored sorted in the kernel (as we pushed it in sorted).
323 *
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000324 */
325static struct list_head *
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200326__iptcc_bsearch_chain_index(const char *name, unsigned int offset,
Jan Engelhardtfd187312008-11-10 16:59:27 +0100327 unsigned int *idx, struct xtc_handle *handle,
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200328 enum bsearch_type type)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000329{
330 unsigned int pos, end;
331 int res;
332
333 struct list_head *list_pos;
334 list_pos=&handle->chains;
335
336 /* Check for empty array, e.g. no user defined chains */
337 if (handle->chain_index_sz == 0) {
338 debug("WARNING: handle->chain_index_sz == 0\n");
339 return list_pos;
340 }
341
342 /* Init */
343 end = handle->chain_index_sz;
344 pos = end / 2;
345
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200346 debug("bsearch Find chain:%s (pos:%d end:%d) (offset:%d)\n",
347 name, pos, end, offset);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000348
349 /* Loop */
350 loop:
351 if (!handle->chain_index[pos]) {
352 fprintf(stderr, "ERROR: NULL pointer chain_index[%d]\n", pos);
353 return &handle->chains; /* Be safe, return orig start pos */
354 }
355
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200356 debug("bsearch Index[%d] name:%s ",
357 pos, handle->chain_index[pos]->name);
358
359 /* Support for different compare functions */
360 switch (type) {
361 case BSEARCH_NAME:
362 res = strcmp(name, handle->chain_index[pos]->name);
363 break;
364 case BSEARCH_OFFSET:
365 debug("head_offset:[%d] foot_offset:[%d] ",
366 handle->chain_index[pos]->head_offset,
367 handle->chain_index[pos]->foot_offset);
368 res = offset - handle->chain_index[pos]->head_offset;
369 break;
370 default:
371 fprintf(stderr, "ERROR: %d not a valid bsearch type\n",
372 type);
373 abort();
374 break;
375 }
376 debug("res:%d ", res);
377
378
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000379 list_pos = &handle->chain_index[pos]->list;
Jan Engelhardtdbb77542008-02-11 00:33:30 +0100380 *idx = pos;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000381
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000382 if (res == 0) { /* Found element, by direct hit */
383 debug("[found] Direct hit pos:%d end:%d\n", pos, end);
384 return list_pos;
385 } else if (res < 0) { /* Too far, jump back */
386 end = pos;
387 pos = pos / 2;
388
389 /* Exit case: First element of array */
390 if (end == 0) {
391 debug("[found] Reached first array elem (end%d)\n",end);
392 return list_pos;
393 }
394 debug("jump back to pos:%d (end:%d)\n", pos, end);
395 goto loop;
Jiri Popelka96d0d012011-06-10 15:25:55 +0200396 } else { /* res > 0; Not far enough, jump forward */
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000397
398 /* Exit case: Last element of array */
399 if (pos == handle->chain_index_sz-1) {
400 debug("[found] Last array elem (end:%d)\n", end);
401 return list_pos;
402 }
403
404 /* Exit case: Next index less, thus elem in this list section */
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200405 switch (type) {
406 case BSEARCH_NAME:
407 res = strcmp(name, handle->chain_index[pos+1]->name);
408 break;
409 case BSEARCH_OFFSET:
410 res = offset - handle->chain_index[pos+1]->head_offset;
411 break;
412 }
413
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000414 if (res < 0) {
415 debug("[found] closest list (end:%d)\n", end);
416 return list_pos;
417 }
418
419 pos = (pos+end)/2;
420 debug("jump forward to pos:%d (end:%d)\n", pos, end);
421 goto loop;
422 }
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000423}
424
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200425/* Wrapper for string chain name based bsearch */
426static struct list_head *
427iptcc_bsearch_chain_index(const char *name, unsigned int *idx,
Jan Engelhardtfd187312008-11-10 16:59:27 +0100428 struct xtc_handle *handle)
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200429{
430 return __iptcc_bsearch_chain_index(name, 0, idx, handle, BSEARCH_NAME);
431}
432
433
434/* Wrapper for offset chain based bsearch */
435static struct list_head *
436iptcc_bsearch_chain_offset(unsigned int offset, unsigned int *idx,
Jan Engelhardtfd187312008-11-10 16:59:27 +0100437 struct xtc_handle *handle)
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200438{
439 struct list_head *pos;
440
441 /* If chains were not received sorted from kernel, then the
442 * offset bsearch is not possible.
443 */
444 if (!handle->sorted_offsets)
445 pos = handle->chains.next;
446 else
447 pos = __iptcc_bsearch_chain_index(NULL, offset, idx, handle,
448 BSEARCH_OFFSET);
449 return pos;
450}
451
452
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000453#ifdef DEBUG
454/* Trivial linear search of chain index. Function used for verifying
455 the output of bsearch function */
456static struct list_head *
Jan Engelhardtfd187312008-11-10 16:59:27 +0100457iptcc_linearly_search_chain_index(const char *name, struct xtc_handle *handle)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000458{
459 unsigned int i=0;
460 int res=0;
461
462 struct list_head *list_pos;
463 list_pos = &handle->chains;
464
465 if (handle->chain_index_sz)
466 list_pos = &handle->chain_index[0]->list;
467
468 /* Linearly walk of chain index array */
469
470 for (i=0; i < handle->chain_index_sz; i++) {
471 if (handle->chain_index[i]) {
472 res = strcmp(handle->chain_index[i]->name, name);
473 if (res > 0)
474 break; // One step too far
475 list_pos = &handle->chain_index[i]->list;
476 if (res == 0)
477 break; // Direct hit
478 }
479 }
480
481 return list_pos;
482}
483#endif
484
Jan Engelhardtfd187312008-11-10 16:59:27 +0100485static int iptcc_chain_index_alloc(struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000486{
487 unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
488 unsigned int array_elems;
489 unsigned int array_mem;
490
491 /* Allocate memory for the chain index array */
492 array_elems = (h->num_chains / list_length) +
493 (h->num_chains % list_length ? 1 : 0);
494 array_mem = sizeof(h->chain_index) * array_elems;
495
496 debug("Alloc Chain index, elems:%d mem:%d bytes\n",
497 array_elems, array_mem);
498
499 h->chain_index = malloc(array_mem);
Jan Engelhardt0eee3002008-11-26 17:18:08 +0100500 if (h->chain_index == NULL && array_mem > 0) {
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000501 h->chain_index_sz = 0;
502 return -ENOMEM;
503 }
504 memset(h->chain_index, 0, array_mem);
505 h->chain_index_sz = array_elems;
506
507 return 1;
508}
509
Jan Engelhardtfd187312008-11-10 16:59:27 +0100510static void iptcc_chain_index_free(struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000511{
512 h->chain_index_sz = 0;
513 free(h->chain_index);
514}
515
516
517#ifdef DEBUG
Jan Engelhardtfd187312008-11-10 16:59:27 +0100518static void iptcc_chain_index_dump(struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000519{
520 unsigned int i = 0;
521
522 /* Dump: contents of chain index array */
523 for (i=0; i < h->chain_index_sz; i++) {
524 if (h->chain_index[i]) {
525 fprintf(stderr, "Chain index[%d].name: %s\n",
526 i, h->chain_index[i]->name);
527 }
528 }
529}
530#endif
531
532/* Build the chain index */
Jan Engelhardtfd187312008-11-10 16:59:27 +0100533static int iptcc_chain_index_build(struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000534{
535 unsigned int list_length = CHAIN_INDEX_BUCKET_LEN;
536 unsigned int chains = 0;
537 unsigned int cindex = 0;
538 struct chain_head *c;
539
540 /* Build up the chain index array here */
541 debug("Building chain index\n");
542
543 debug("Number of user defined chains:%d bucket_sz:%d array_sz:%d\n",
544 h->num_chains, list_length, h->chain_index_sz);
545
546 if (h->chain_index_sz == 0)
547 return 0;
548
549 list_for_each_entry(c, &h->chains, list) {
550
551 /* Issue: The index array needs to start after the
552 * builtin chains, as they are not sorted */
553 if (!iptcc_is_builtin(c)) {
554 cindex=chains / list_length;
555
556 /* Safe guard, break out on array limit, this
557 * is useful if chains are added and array is
558 * rebuild, without realloc of memory. */
559 if (cindex >= h->chain_index_sz)
560 break;
561
562 if ((chains % list_length)== 0) {
563 debug("\nIndex[%d] Chains:", cindex);
564 h->chain_index[cindex] = c;
565 }
566 chains++;
567 }
568 debug("%s, ", c->name);
569 }
570 debug("\n");
571
572 return 1;
573}
574
Jan Engelhardtfd187312008-11-10 16:59:27 +0100575static int iptcc_chain_index_rebuild(struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000576{
577 debug("REBUILD chain index array\n");
578 iptcc_chain_index_free(h);
579 if ((iptcc_chain_index_alloc(h)) < 0)
580 return -ENOMEM;
581 iptcc_chain_index_build(h);
582 return 1;
583}
584
585/* Delete chain (pointer) from index array. Removing an element from
586 * the chain list only affects the chain index array, if the chain
587 * index points-to/uses that list pointer.
588 *
589 * There are different strategies, the simple and safe is to rebuild
590 * the chain index every time. The more advanced is to update the
591 * array index to point to the next element, but that requires some
592 * house keeping and boundry checks. The advanced is implemented, as
593 * the simple approach behaves badly when all chains are deleted
594 * because list_for_each processing will always hit the first chain
595 * index, thus causing a rebuild for every chain.
596 */
Jan Engelhardtfd187312008-11-10 16:59:27 +0100597static int iptcc_chain_index_delete_chain(struct chain_head *c, struct xtc_handle *h)
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000598{
Jan Engelhardt7d91a2a2011-05-30 01:39:54 +0200599 struct list_head *index_ptr, *next;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000600 struct chain_head *c2;
Jan Engelhardtdbb77542008-02-11 00:33:30 +0100601 unsigned int idx, idx2;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000602
Jan Engelhardtdbb77542008-02-11 00:33:30 +0100603 index_ptr = iptcc_bsearch_chain_index(c->name, &idx, h);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000604
605 debug("Del chain[%s] c->list:%p index_ptr:%p\n",
606 c->name, &c->list, index_ptr);
607
608 /* Save the next pointer */
609 next = c->list.next;
610 list_del(&c->list);
611
612 if (index_ptr == &c->list) { /* Chain used as index ptr */
613
614 /* See if its possible to avoid a rebuild, by shifting
615 * to next pointer. Its possible if the next pointer
616 * is located in the same index bucket.
617 */
618 c2 = list_entry(next, struct chain_head, list);
Jan Engelhardt7d91a2a2011-05-30 01:39:54 +0200619 iptcc_bsearch_chain_index(c2->name, &idx2, h);
Jan Engelhardtdbb77542008-02-11 00:33:30 +0100620 if (idx != idx2) {
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000621 /* Rebuild needed */
622 return iptcc_chain_index_rebuild(h);
623 } else {
624 /* Avoiding rebuild */
625 debug("Update cindex[%d] with next ptr name:[%s]\n",
Jan Engelhardtdbb77542008-02-11 00:33:30 +0100626 idx, c2->name);
627 h->chain_index[idx]=c2;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000628 return 0;
629 }
630 }
631 return 0;
632}
633
634
635/**********************************************************************
Harald Welteaae69be2004-08-29 23:32:14 +0000636 * iptc cache utility functions (iptcc_*)
637 **********************************************************************/
Harald Welte0113fe72004-01-06 19:04:02 +0000638
Harald Welteaae69be2004-08-29 23:32:14 +0000639/* Is the given chain builtin (1) or user-defined (0) */
Jesper Dangaard Brouer91093982008-01-15 17:01:58 +0000640static inline unsigned int iptcc_is_builtin(struct chain_head *c)
Harald Welteaae69be2004-08-29 23:32:14 +0000641{
642 return (c->hooknum ? 1 : 0);
643}
644
645/* Get a specific rule within a chain */
646static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
647 unsigned int rulenum)
648{
649 struct rule_head *r;
650 unsigned int num = 0;
651
652 list_for_each_entry(r, &c->rules, list) {
653 num++;
654 if (num == rulenum)
655 return r;
656 }
657 return NULL;
658}
659
Martin Josefssona5616dc2004-10-24 22:27:31 +0000660/* Get a specific rule within a chain backwards */
661static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
662 unsigned int rulenum)
663{
664 struct rule_head *r;
665 unsigned int num = 0;
666
667 list_for_each_entry_reverse(r, &c->rules, list) {
668 num++;
669 if (num == rulenum)
670 return r;
671 }
672 return NULL;
673}
674
Harald Welteaae69be2004-08-29 23:32:14 +0000675/* Returns chain head if found, otherwise NULL. */
676static struct chain_head *
Jan Engelhardtfd187312008-11-10 16:59:27 +0100677iptcc_find_chain_by_offset(struct xtc_handle *handle, unsigned int offset)
Harald Welteaae69be2004-08-29 23:32:14 +0000678{
679 struct list_head *pos;
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200680 struct list_head *list_start_pos;
681 unsigned int i;
Harald Welteaae69be2004-08-29 23:32:14 +0000682
683 if (list_empty(&handle->chains))
684 return NULL;
685
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200686 /* Find a smart place to start the search */
687 list_start_pos = iptcc_bsearch_chain_offset(offset, &i, handle);
688
689 /* Note that iptcc_bsearch_chain_offset() skips builtin
690 * chains, but this function is only used for finding jump
691 * targets, and a buildin chain is not a valid jump target */
692
693 debug("Offset:[%u] starting search at index:[%u]\n", offset, i);
694// list_for_each(pos, &handle->chains) {
695 list_for_each(pos, list_start_pos->prev) {
Harald Welteaae69be2004-08-29 23:32:14 +0000696 struct chain_head *c = list_entry(pos, struct chain_head, list);
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200697 debug(".");
698 if (offset >= c->head_offset && offset <= c->foot_offset) {
699 debug("Offset search found chain:[%s]\n", c->name);
Harald Welteaae69be2004-08-29 23:32:14 +0000700 return c;
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200701 }
Harald Welte0113fe72004-01-06 19:04:02 +0000702 }
703
Harald Welteaae69be2004-08-29 23:32:14 +0000704 return NULL;
Harald Welte0113fe72004-01-06 19:04:02 +0000705}
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000706
Harald Welteaae69be2004-08-29 23:32:14 +0000707/* Returns chain head if found, otherwise NULL. */
708static struct chain_head *
Jan Engelhardtfd187312008-11-10 16:59:27 +0100709iptcc_find_label(const char *name, struct xtc_handle *handle)
Harald Welteaae69be2004-08-29 23:32:14 +0000710{
711 struct list_head *pos;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000712 struct list_head *list_start_pos;
713 unsigned int i=0;
714 int res;
Harald Welteaae69be2004-08-29 23:32:14 +0000715
716 if (list_empty(&handle->chains))
717 return NULL;
718
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000719 /* First look at builtin chains */
Harald Welteaae69be2004-08-29 23:32:14 +0000720 list_for_each(pos, &handle->chains) {
721 struct chain_head *c = list_entry(pos, struct chain_head, list);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000722 if (!iptcc_is_builtin(c))
723 break;
Harald Welteaae69be2004-08-29 23:32:14 +0000724 if (!strcmp(c->name, name))
725 return c;
726 }
727
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000728 /* Find a smart place to start the search via chain index */
729 //list_start_pos = iptcc_linearly_search_chain_index(name, handle);
730 list_start_pos = iptcc_bsearch_chain_index(name, &i, handle);
731
732 /* Handel if bsearch bails out early */
733 if (list_start_pos == &handle->chains) {
734 list_start_pos = pos;
735 }
736#ifdef DEBUG
737 else {
738 /* Verify result of bsearch against linearly index search */
739 struct list_head *test_pos;
740 struct chain_head *test_c, *tmp_c;
741 test_pos = iptcc_linearly_search_chain_index(name, handle);
742 if (list_start_pos != test_pos) {
743 debug("BUG in chain_index search\n");
744 test_c=list_entry(test_pos, struct chain_head,list);
745 tmp_c =list_entry(list_start_pos,struct chain_head,list);
746 debug("Verify search found:\n");
747 debug(" Chain:%s\n", test_c->name);
748 debug("BSearch found:\n");
749 debug(" Chain:%s\n", tmp_c->name);
750 exit(42);
751 }
752 }
753#endif
754
755 /* Initial/special case, no user defined chains */
756 if (handle->num_chains == 0)
757 return NULL;
758
759 /* Start searching through the chain list */
760 list_for_each(pos, list_start_pos->prev) {
761 struct chain_head *c = list_entry(pos, struct chain_head, list);
762 res = strcmp(c->name, name);
763 debug("List search name:%s == %s res:%d\n", name, c->name, res);
764 if (res==0)
765 return c;
766
767 /* We can stop earlier as we know list is sorted */
768 if (res>0 && !iptcc_is_builtin(c)) { /* Walked too far*/
769 debug(" Not in list, walked too far, sorted list\n");
770 return NULL;
771 }
772
773 /* Stop on wrap around, if list head is reached */
774 if (pos == &handle->chains) {
775 debug("Stop, list head reached\n");
776 return NULL;
777 }
778 }
779
780 debug("List search NOT found name:%s\n", name);
Harald Welteaae69be2004-08-29 23:32:14 +0000781 return NULL;
782}
783
784/* called when rule is to be removed from cache */
785static void iptcc_delete_rule(struct rule_head *r)
786{
787 DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
788 /* clean up reference count of called chain */
789 if (r->type == IPTCC_R_JUMP
790 && r->jump)
791 r->jump->references--;
792
793 list_del(&r->list);
794 free(r);
795}
796
797
798/**********************************************************************
799 * RULESET PARSER (blob -> cache)
800 **********************************************************************/
801
Harald Welteaae69be2004-08-29 23:32:14 +0000802/* Delete policy rule of previous chain, since cache doesn't contain
803 * chain policy rules.
804 * WARNING: This function has ugly design and relies on a lot of context, only
805 * to be called from specific places within the parser */
Jan Engelhardtfd187312008-11-10 16:59:27 +0100806static int __iptcc_p_del_policy(struct xtc_handle *h, unsigned int num)
Harald Welteaae69be2004-08-29 23:32:14 +0000807{
Jan Engelhardt51651b62009-10-23 23:35:49 +0200808 const unsigned char *data;
809
Harald Welteaae69be2004-08-29 23:32:14 +0000810 if (h->chain_iterator_cur) {
811 /* policy rule is last rule */
812 struct rule_head *pr = (struct rule_head *)
813 h->chain_iterator_cur->rules.prev;
814
815 /* save verdict */
Jan Engelhardt51651b62009-10-23 23:35:49 +0200816 data = GET_TARGET(pr->entry)->data;
817 h->chain_iterator_cur->verdict = *(const int *)data;
Harald Welteaae69be2004-08-29 23:32:14 +0000818
819 /* save counter and counter_map information */
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100820 h->chain_iterator_cur->counter_map.maptype =
Jan Engelhardt7c4d6682009-10-26 18:43:54 +0100821 COUNTER_MAP_ZEROED;
Harald Welteaae69be2004-08-29 23:32:14 +0000822 h->chain_iterator_cur->counter_map.mappos = num-1;
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100823 memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
Harald Welteaae69be2004-08-29 23:32:14 +0000824 sizeof(h->chain_iterator_cur->counters));
825
826 /* foot_offset points to verdict rule */
827 h->chain_iterator_cur->foot_index = num;
828 h->chain_iterator_cur->foot_offset = pr->offset;
829
830 /* delete rule from cache */
831 iptcc_delete_rule(pr);
Martin Josefsson8d1b38a2004-09-22 21:00:19 +0000832 h->chain_iterator_cur->num_rules--;
Harald Welteaae69be2004-08-29 23:32:14 +0000833
834 return 1;
835 }
836 return 0;
837}
838
Harald Welteec30b6c2005-02-01 16:45:56 +0000839/* alphabetically insert a chain into the list */
Christoph Paasch7cd15e32009-03-23 13:50:11 +0100840static void iptc_insert_chain(struct xtc_handle *h, struct chain_head *c)
Harald Welteec30b6c2005-02-01 16:45:56 +0000841{
842 struct chain_head *tmp;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000843 struct list_head *list_start_pos;
844 unsigned int i=1;
845
846 /* Find a smart place to start the insert search */
847 list_start_pos = iptcc_bsearch_chain_index(c->name, &i, h);
848
849 /* Handle the case, where chain.name is smaller than index[0] */
850 if (i==0 && strcmp(c->name, h->chain_index[0]->name) <= 0) {
851 h->chain_index[0] = c; /* Update chain index head */
852 list_start_pos = h->chains.next;
853 debug("Update chain_index[0] with %s\n", c->name);
854 }
855
856 /* Handel if bsearch bails out early */
857 if (list_start_pos == &h->chains) {
858 list_start_pos = h->chains.next;
859 }
Harald Welteec30b6c2005-02-01 16:45:56 +0000860
Olaf Rempel9d3ed772005-03-04 23:08:30 +0000861 /* sort only user defined chains */
862 if (!c->hooknum) {
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000863 list_for_each_entry(tmp, list_start_pos->prev, list) {
Robert de Barthfeca0572005-07-31 07:04:59 +0000864 if (!tmp->hooknum && strcmp(c->name, tmp->name) <= 0) {
Olaf Rempel9d3ed772005-03-04 23:08:30 +0000865 list_add(&c->list, tmp->list.prev);
866 return;
867 }
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +0000868
869 /* Stop if list head is reached */
870 if (&tmp->list == &h->chains) {
871 debug("Insert, list head reached add to tail\n");
872 break;
873 }
Harald Welteec30b6c2005-02-01 16:45:56 +0000874 }
875 }
876
877 /* survived till end of list: add at tail */
878 list_add_tail(&c->list, &h->chains);
879}
880
Harald Welteaae69be2004-08-29 23:32:14 +0000881/* Another ugly helper function split out of cache_add_entry to make it less
882 * spaghetti code */
Jan Engelhardtfd187312008-11-10 16:59:27 +0100883static void __iptcc_p_add_chain(struct xtc_handle *h, struct chain_head *c,
Harald Welteaae69be2004-08-29 23:32:14 +0000884 unsigned int offset, unsigned int *num)
885{
Jesper Dangaard Brouer13364512007-12-12 15:20:42 +0000886 struct list_head *tail = h->chains.prev;
887 struct chain_head *ctail;
888
Harald Welteaae69be2004-08-29 23:32:14 +0000889 __iptcc_p_del_policy(h, *num);
890
891 c->head_offset = offset;
892 c->index = *num;
893
Jesper Dangaard Brouer13364512007-12-12 15:20:42 +0000894 /* Chains from kernel are already sorted, as they are inserted
895 * sorted. But there exists an issue when shifting to 1.4.0
896 * from an older version, as old versions allow last created
897 * chain to be unsorted.
898 */
899 if (iptcc_is_builtin(c)) /* Only user defined chains are sorted*/
900 list_add_tail(&c->list, &h->chains);
901 else {
902 ctail = list_entry(tail, struct chain_head, list);
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200903
Jesper Dangaard Brouer526d3e12008-07-03 20:29:34 +0200904 if (strcmp(c->name, ctail->name) > 0 ||
905 iptcc_is_builtin(ctail))
Jesper Dangaard Brouer13364512007-12-12 15:20:42 +0000906 list_add_tail(&c->list, &h->chains);/* Already sorted*/
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200907 else {
Jesper Dangaard Brouer13364512007-12-12 15:20:42 +0000908 iptc_insert_chain(h, c);/* Was not sorted */
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +0200909
910 /* Notice, if chains were not received sorted
911 * from kernel, then an offset bsearch is no
912 * longer valid.
913 */
914 h->sorted_offsets = 0;
915
916 debug("NOTICE: chain:[%s] was NOT sorted(ctail:%s)\n",
917 c->name, ctail->name);
918 }
Jesper Dangaard Brouer13364512007-12-12 15:20:42 +0000919 }
Jesper Dangaard Brouerd8cb7872007-11-28 08:40:26 +0000920
Harald Welteaae69be2004-08-29 23:32:14 +0000921 h->chain_iterator_cur = c;
922}
923
924/* main parser function: add an entry from the blob to the cache */
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100925static int cache_add_entry(STRUCT_ENTRY *e,
926 struct xtc_handle *h,
Harald Welteaae69be2004-08-29 23:32:14 +0000927 STRUCT_ENTRY **prev,
928 unsigned int *num)
929{
930 unsigned int builtin;
931 unsigned int offset = (char *)e - (char *)h->entries->entrytable;
932
933 DEBUGP("entering...");
934
935 /* Last entry ("policy rule"). End it.*/
936 if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
937 /* This is the ERROR node at the end of the chain */
938 DEBUGP_C("%u:%u: end of table:\n", *num, offset);
939
940 __iptcc_p_del_policy(h, *num);
941
942 h->chain_iterator_cur = NULL;
943 goto out_inc;
944 }
945
946 /* We know this is the start of a new chain if it's an ERROR
947 * target, or a hook entry point */
948
949 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100950 struct chain_head *c =
Harald Welteaae69be2004-08-29 23:32:14 +0000951 iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100952 DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
Harald Welteaae69be2004-08-29 23:32:14 +0000953 (char *)c->name, c);
954 if (!c) {
955 errno = -ENOMEM;
956 return -1;
957 }
Jesper Dangaard Brouer48bde402008-01-15 17:06:48 +0000958 h->num_chains++; /* New user defined chain */
Harald Welteaae69be2004-08-29 23:32:14 +0000959
960 __iptcc_p_add_chain(h, c, offset, num);
961
962 } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
963 struct chain_head *c =
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100964 iptcc_alloc_chain_head((char *)hooknames[builtin-1],
Harald Welteaae69be2004-08-29 23:32:14 +0000965 builtin);
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100966 DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
Harald Welteaae69be2004-08-29 23:32:14 +0000967 *num, offset, c, &c->rules);
968 if (!c) {
969 errno = -ENOMEM;
970 return -1;
971 }
972
973 c->hooknum = builtin;
974
975 __iptcc_p_add_chain(h, c, offset, num);
976
977 /* FIXME: this is ugly. */
978 goto new_rule;
979 } else {
980 /* has to be normal rule */
981 struct rule_head *r;
982new_rule:
983
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +0100984 if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
Harald Welteaae69be2004-08-29 23:32:14 +0000985 e->next_offset))) {
986 errno = ENOMEM;
987 return -1;
988 }
989 DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
990
991 r->index = *num;
992 r->offset = offset;
993 memcpy(r->entry, e, e->next_offset);
994 r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
995 r->counter_map.mappos = r->index;
996
997 /* handling of jumps, etc. */
998 if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
999 STRUCT_STANDARD_TARGET *t;
1000
1001 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1002 if (t->target.u.target_size
1003 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
1004 errno = EINVAL;
1005 return -1;
1006 }
1007
1008 if (t->verdict < 0) {
1009 DEBUGP_C("standard, verdict=%d\n", t->verdict);
1010 r->type = IPTCC_R_STANDARD;
1011 } else if (t->verdict == r->offset+e->next_offset) {
1012 DEBUGP_C("fallthrough\n");
1013 r->type = IPTCC_R_FALLTHROUGH;
1014 } else {
1015 DEBUGP_C("jump, target=%u\n", t->verdict);
1016 r->type = IPTCC_R_JUMP;
1017 /* Jump target fixup has to be deferred
1018 * until second pass, since we migh not
1019 * yet have parsed the target */
1020 }
Martin Josefsson52c38022004-09-22 19:39:40 +00001021 } else {
1022 DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
1023 r->type = IPTCC_R_MODULE;
Harald Welteaae69be2004-08-29 23:32:14 +00001024 }
1025
1026 list_add_tail(&r->list, &h->chain_iterator_cur->rules);
Martin Josefsson8d1b38a2004-09-22 21:00:19 +00001027 h->chain_iterator_cur->num_rules++;
Harald Welteaae69be2004-08-29 23:32:14 +00001028 }
1029out_inc:
1030 (*num)++;
1031 return 0;
1032}
1033
1034
1035/* parse an iptables blob into it's pieces */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001036static int parse_table(struct xtc_handle *h)
Harald Welteaae69be2004-08-29 23:32:14 +00001037{
1038 STRUCT_ENTRY *prev;
1039 unsigned int num = 0;
1040 struct chain_head *c;
1041
Jesper Dangaard Brouer4bae3f12008-07-03 20:31:42 +02001042 /* Assume that chains offsets are sorted, this verified during
1043 parsing of ruleset (in __iptcc_p_add_chain())*/
1044 h->sorted_offsets = 1;
1045
Harald Welteaae69be2004-08-29 23:32:14 +00001046 /* First pass: over ruleset blob */
1047 ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
1048 cache_add_entry, h, &prev, &num);
1049
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00001050 /* Build the chain index, used for chain list search speedup */
1051 if ((iptcc_chain_index_alloc(h)) < 0)
1052 return -ENOMEM;
1053 iptcc_chain_index_build(h);
1054
Harald Welteaae69be2004-08-29 23:32:14 +00001055 /* Second pass: fixup parsed data from first pass */
1056 list_for_each_entry(c, &h->chains, list) {
1057 struct rule_head *r;
1058 list_for_each_entry(r, &c->rules, list) {
Jan Engelhardtdbb77542008-02-11 00:33:30 +01001059 struct chain_head *lc;
Harald Welteaae69be2004-08-29 23:32:14 +00001060 STRUCT_STANDARD_TARGET *t;
1061
1062 if (r->type != IPTCC_R_JUMP)
1063 continue;
1064
1065 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
Jan Engelhardtdbb77542008-02-11 00:33:30 +01001066 lc = iptcc_find_chain_by_offset(h, t->verdict);
1067 if (!lc)
Harald Welteaae69be2004-08-29 23:32:14 +00001068 return -1;
Jan Engelhardtdbb77542008-02-11 00:33:30 +01001069 r->jump = lc;
1070 lc->references++;
Harald Welteaae69be2004-08-29 23:32:14 +00001071 }
1072 }
1073
Harald Welteaae69be2004-08-29 23:32:14 +00001074 return 1;
1075}
1076
1077
1078/**********************************************************************
1079 * RULESET COMPILATION (cache -> blob)
1080 **********************************************************************/
1081
1082/* Convenience structures */
1083struct iptcb_chain_start{
1084 STRUCT_ENTRY e;
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001085 struct xt_error_target name;
Harald Welteaae69be2004-08-29 23:32:14 +00001086};
1087#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001088 ALIGN(sizeof(struct xt_error_target)))
Harald Welteaae69be2004-08-29 23:32:14 +00001089
1090struct iptcb_chain_foot {
1091 STRUCT_ENTRY e;
1092 STRUCT_STANDARD_TARGET target;
1093};
1094#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
1095 ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
1096
1097struct iptcb_chain_error {
1098 STRUCT_ENTRY entry;
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001099 struct xt_error_target target;
Harald Welteaae69be2004-08-29 23:32:14 +00001100};
1101#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001102 ALIGN(sizeof(struct xt_error_target)))
Harald Welteaae69be2004-08-29 23:32:14 +00001103
1104
1105
1106/* compile rule from cache into blob */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001107static inline int iptcc_compile_rule (struct xtc_handle *h, STRUCT_REPLACE *repl, struct rule_head *r)
Harald Welteaae69be2004-08-29 23:32:14 +00001108{
1109 /* handle jumps */
1110 if (r->type == IPTCC_R_JUMP) {
1111 STRUCT_STANDARD_TARGET *t;
1112 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
1113 /* memset for memcmp convenience on delete/replace */
1114 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
1115 strcpy(t->target.u.user.name, STANDARD_TARGET);
1116 /* Jumps can only happen to builtin chains, so we
1117 * can safely assume that they always have a header */
1118 t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
1119 } else if (r->type == IPTCC_R_FALLTHROUGH) {
1120 STRUCT_STANDARD_TARGET *t;
1121 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
1122 t->verdict = r->offset + r->size;
1123 }
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001124
Harald Welteaae69be2004-08-29 23:32:14 +00001125 /* copy entry from cache to blob */
1126 memcpy((char *)repl->entries+r->offset, r->entry, r->size);
1127
1128 return 1;
1129}
1130
1131/* compile chain from cache into blob */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001132static int iptcc_compile_chain(struct xtc_handle *h, STRUCT_REPLACE *repl, struct chain_head *c)
Harald Welteaae69be2004-08-29 23:32:14 +00001133{
1134 int ret;
1135 struct rule_head *r;
1136 struct iptcb_chain_start *head;
1137 struct iptcb_chain_foot *foot;
1138
1139 /* only user-defined chains have heaer */
1140 if (!iptcc_is_builtin(c)) {
1141 /* put chain header in place */
1142 head = (void *)repl->entries + c->head_offset;
1143 head->e.target_offset = sizeof(STRUCT_ENTRY);
1144 head->e.next_offset = IPTCB_CHAIN_START_SIZE;
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001145 strcpy(head->name.target.u.user.name, ERROR_TARGET);
1146 head->name.target.u.target_size =
1147 ALIGN(sizeof(struct xt_error_target));
1148 strcpy(head->name.errorname, c->name);
Harald Welteaae69be2004-08-29 23:32:14 +00001149 } else {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001150 repl->hook_entry[c->hooknum-1] = c->head_offset;
Harald Welteaae69be2004-08-29 23:32:14 +00001151 repl->underflow[c->hooknum-1] = c->foot_offset;
1152 }
1153
1154 /* iterate over rules */
1155 list_for_each_entry(r, &c->rules, list) {
1156 ret = iptcc_compile_rule(h, repl, r);
1157 if (ret < 0)
1158 return ret;
1159 }
1160
1161 /* put chain footer in place */
1162 foot = (void *)repl->entries + c->foot_offset;
1163 foot->e.target_offset = sizeof(STRUCT_ENTRY);
1164 foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
1165 strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
1166 foot->target.target.u.target_size =
1167 ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1168 /* builtin targets have verdict, others return */
1169 if (iptcc_is_builtin(c))
1170 foot->target.verdict = c->verdict;
1171 else
1172 foot->target.verdict = RETURN;
1173 /* set policy-counters */
1174 memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
1175
1176 return 0;
1177}
1178
1179/* calculate offset and number for every rule in the cache */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001180static int iptcc_compile_chain_offsets(struct xtc_handle *h, struct chain_head *c,
Harald Welteefa8fc22005-07-19 22:03:49 +00001181 unsigned int *offset, unsigned int *num)
Harald Welteaae69be2004-08-29 23:32:14 +00001182{
1183 struct rule_head *r;
1184
1185 c->head_offset = *offset;
1186 DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
1187
1188 if (!iptcc_is_builtin(c)) {
1189 /* Chain has header */
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001190 *offset += sizeof(STRUCT_ENTRY)
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001191 + ALIGN(sizeof(struct xt_error_target));
Harald Welteaae69be2004-08-29 23:32:14 +00001192 (*num)++;
1193 }
1194
1195 list_for_each_entry(r, &c->rules, list) {
1196 DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
1197 r->offset = *offset;
1198 r->index = *num;
1199 *offset += r->size;
1200 (*num)++;
1201 }
1202
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001203 DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
Harald Welteaae69be2004-08-29 23:32:14 +00001204 *offset, *num);
1205 c->foot_offset = *offset;
1206 c->foot_index = *num;
1207 *offset += sizeof(STRUCT_ENTRY)
1208 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1209 (*num)++;
1210
1211 return 1;
1212}
1213
1214/* put the pieces back together again */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001215static int iptcc_compile_table_prep(struct xtc_handle *h, unsigned int *size)
Harald Welteaae69be2004-08-29 23:32:14 +00001216{
1217 struct chain_head *c;
1218 unsigned int offset = 0, num = 0;
1219 int ret = 0;
1220
1221 /* First pass: calculate offset for every rule */
1222 list_for_each_entry(c, &h->chains, list) {
1223 ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
1224 if (ret < 0)
1225 return ret;
1226 }
1227
1228 /* Append one error rule at end of chain */
1229 num++;
1230 offset += sizeof(STRUCT_ENTRY)
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001231 + ALIGN(sizeof(struct xt_error_target));
Harald Welteaae69be2004-08-29 23:32:14 +00001232
1233 /* ruleset size is now in offset */
1234 *size = offset;
1235 return num;
1236}
1237
Jan Engelhardtfd187312008-11-10 16:59:27 +01001238static int iptcc_compile_table(struct xtc_handle *h, STRUCT_REPLACE *repl)
Harald Welteaae69be2004-08-29 23:32:14 +00001239{
1240 struct chain_head *c;
1241 struct iptcb_chain_error *error;
1242
1243 /* Second pass: copy from cache to offsets, fill in jumps */
1244 list_for_each_entry(c, &h->chains, list) {
1245 int ret = iptcc_compile_chain(h, repl, c);
1246 if (ret < 0)
1247 return ret;
1248 }
1249
1250 /* Append error rule at end of chain */
1251 error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
1252 error->entry.target_offset = sizeof(STRUCT_ENTRY);
1253 error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
Jan Engelhardt9cf67de2011-09-11 17:24:26 +02001254 error->target.target.u.user.target_size =
1255 ALIGN(sizeof(struct xt_error_target));
1256 strcpy((char *)&error->target.target.u.user.name, ERROR_TARGET);
1257 strcpy((char *)&error->target.errorname, "ERROR");
Harald Welteaae69be2004-08-29 23:32:14 +00001258
1259 return 1;
1260}
1261
1262/**********************************************************************
1263 * EXTERNAL API (operates on cache only)
1264 **********************************************************************/
Marc Bouchere6869a82000-03-20 06:03:29 +00001265
1266/* Allocate handle of given size */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001267static struct xtc_handle *
Harald Welte0113fe72004-01-06 19:04:02 +00001268alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +00001269{
Jan Engelhardtfd187312008-11-10 16:59:27 +01001270 struct xtc_handle *h;
Marc Bouchere6869a82000-03-20 06:03:29 +00001271
Jan Engelhardt1639fe82011-08-27 11:39:52 +02001272 h = malloc(sizeof(*h));
Harald Welteaae69be2004-08-29 23:32:14 +00001273 if (!h) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001274 errno = ENOMEM;
1275 return NULL;
1276 }
Harald Welteaae69be2004-08-29 23:32:14 +00001277 memset(h, 0, sizeof(*h));
1278 INIT_LIST_HEAD(&h->chains);
Marc Bouchere6869a82000-03-20 06:03:29 +00001279 strcpy(h->info.name, tablename);
Harald Welteaae69be2004-08-29 23:32:14 +00001280
Harald Welte0371c0c2004-09-19 21:00:12 +00001281 h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
Harald Welteaae69be2004-08-29 23:32:14 +00001282 if (!h->entries)
1283 goto out_free_handle;
1284
1285 strcpy(h->entries->name, tablename);
Harald Welte0371c0c2004-09-19 21:00:12 +00001286 h->entries->size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +00001287
1288 return h;
Harald Welteaae69be2004-08-29 23:32:14 +00001289
1290out_free_handle:
1291 free(h);
1292
1293 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001294}
1295
Harald Welteaae69be2004-08-29 23:32:14 +00001296
Jan Engelhardtfd187312008-11-10 16:59:27 +01001297struct xtc_handle *
Rusty Russell79dee072000-05-02 16:45:16 +00001298TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +00001299{
Jan Engelhardtfd187312008-11-10 16:59:27 +01001300 struct xtc_handle *h;
Rusty Russell79dee072000-05-02 16:45:16 +00001301 STRUCT_GETINFO info;
Harald Welteefa8fc22005-07-19 22:03:49 +00001302 unsigned int tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001303 socklen_t s;
Jan Engelhardt175f4512008-11-10 17:25:55 +01001304 int sockfd;
Marc Bouchere6869a82000-03-20 06:03:29 +00001305
Rusty Russell79dee072000-05-02 16:45:16 +00001306 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001307
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001308 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
1309 errno = EINVAL;
1310 return NULL;
1311 }
Jan Engelhardt175f4512008-11-10 17:25:55 +01001312
1313 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
1314 if (sockfd < 0)
1315 return NULL;
1316
Patrick McHardy2f932052008-04-02 14:01:53 +02001317retry:
Marc Bouchere6869a82000-03-20 06:03:29 +00001318 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001319
Marc Bouchere6869a82000-03-20 06:03:29 +00001320 strcpy(info.name, tablename);
Derrik Pates664c0a32005-02-01 13:28:14 +00001321 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) {
Jan Engelhardt175f4512008-11-10 17:25:55 +01001322 close(sockfd);
Marc Bouchere6869a82000-03-20 06:03:29 +00001323 return NULL;
Derrik Pates664c0a32005-02-01 13:28:14 +00001324 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001325
Harald Welteaae69be2004-08-29 23:32:14 +00001326 DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
1327 info.valid_hooks, info.num_entries, info.size);
1328
Harald Welte0113fe72004-01-06 19:04:02 +00001329 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001330 == NULL) {
Jan Engelhardt175f4512008-11-10 17:25:55 +01001331 close(sockfd);
Marc Bouchere6869a82000-03-20 06:03:29 +00001332 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001333 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001334
Marc Bouchere6869a82000-03-20 06:03:29 +00001335 /* Initialize current state */
Jan Engelhardt175f4512008-11-10 17:25:55 +01001336 h->sockfd = sockfd;
Marc Bouchere6869a82000-03-20 06:03:29 +00001337 h->info = info;
Harald Welte0113fe72004-01-06 19:04:02 +00001338
Harald Welteaae69be2004-08-29 23:32:14 +00001339 h->entries->size = h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +00001340
Rusty Russell79dee072000-05-02 16:45:16 +00001341 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +00001342
Jan Engelhardt175f4512008-11-10 17:25:55 +01001343 if (getsockopt(h->sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
Harald Welteaae69be2004-08-29 23:32:14 +00001344 &tmp) < 0)
1345 goto error;
1346
1347#ifdef IPTC_DEBUG2
1348 {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001349 int fd = open("/tmp/libiptc-so_get_entries.blob",
Harald Welteaae69be2004-08-29 23:32:14 +00001350 O_CREAT|O_WRONLY);
1351 if (fd >= 0) {
1352 write(fd, h->entries, tmp);
1353 close(fd);
1354 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001355 }
Harald Welteaae69be2004-08-29 23:32:14 +00001356#endif
1357
1358 if (parse_table(h) < 0)
1359 goto error;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001360
Marc Bouchere6869a82000-03-20 06:03:29 +00001361 CHECK(h);
1362 return h;
Harald Welteaae69be2004-08-29 23:32:14 +00001363error:
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001364 TC_FREE(h);
Patrick McHardy2f932052008-04-02 14:01:53 +02001365 /* A different process changed the ruleset size, retry */
1366 if (errno == EAGAIN)
1367 goto retry;
Harald Welteaae69be2004-08-29 23:32:14 +00001368 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001369}
1370
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001371void
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001372TC_FREE(struct xtc_handle *h)
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001373{
Harald Welteaae69be2004-08-29 23:32:14 +00001374 struct chain_head *c, *tmp;
1375
Derrik Pates664c0a32005-02-01 13:28:14 +00001376 iptc_fn = TC_FREE;
Jan Engelhardt175f4512008-11-10 17:25:55 +01001377 close(h->sockfd);
Harald Welteaae69be2004-08-29 23:32:14 +00001378
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001379 list_for_each_entry_safe(c, tmp, &h->chains, list) {
Harald Welteaae69be2004-08-29 23:32:14 +00001380 struct rule_head *r, *rtmp;
1381
1382 list_for_each_entry_safe(r, rtmp, &c->rules, list) {
1383 free(r);
1384 }
1385
1386 free(c);
1387 }
1388
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001389 iptcc_chain_index_free(h);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00001390
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001391 free(h->entries);
1392 free(h);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001393}
1394
Marc Bouchere6869a82000-03-20 06:03:29 +00001395static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001396print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +00001397{
Rusty Russell228e98d2000-04-27 10:28:06 +00001398 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +00001399 return 0;
1400}
1401
Jan Engelhardtfd187312008-11-10 16:59:27 +01001402static int dump_entry(STRUCT_ENTRY *e, struct xtc_handle *const handle);
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001403
Marc Bouchere6869a82000-03-20 06:03:29 +00001404void
Jan Engelhardtfd187312008-11-10 16:59:27 +01001405TC_DUMP_ENTRIES(struct xtc_handle *const handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001406{
Derrik Pates664c0a32005-02-01 13:28:14 +00001407 iptc_fn = TC_DUMP_ENTRIES;
Marc Bouchere6869a82000-03-20 06:03:29 +00001408 CHECK(handle);
Patrick McHardy97fb2f12007-09-08 16:52:25 +00001409
Rusty Russelle45c7132004-12-16 13:21:44 +00001410 printf("libiptc v%s. %u bytes.\n",
Jan Engelhardt8b7c64d2008-04-15 11:48:25 +02001411 XTABLES_VERSION, handle->entries->size);
Marc Bouchere6869a82000-03-20 06:03:29 +00001412 printf("Table `%s'\n", handle->info.name);
Jan Engelhardtd73af642008-11-10 17:07:31 +01001413 printf("Hooks: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
Rusty Russell67088e72000-05-10 01:18:57 +00001414 handle->info.hook_entry[HOOK_PRE_ROUTING],
1415 handle->info.hook_entry[HOOK_LOCAL_IN],
1416 handle->info.hook_entry[HOOK_FORWARD],
1417 handle->info.hook_entry[HOOK_LOCAL_OUT],
1418 handle->info.hook_entry[HOOK_POST_ROUTING]);
Jan Engelhardtd73af642008-11-10 17:07:31 +01001419 printf("Underflows: pre/in/fwd/out/post = %x/%x/%x/%x/%x\n",
Rusty Russell67088e72000-05-10 01:18:57 +00001420 handle->info.underflow[HOOK_PRE_ROUTING],
1421 handle->info.underflow[HOOK_LOCAL_IN],
1422 handle->info.underflow[HOOK_FORWARD],
1423 handle->info.underflow[HOOK_LOCAL_OUT],
1424 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +00001425
Harald Welteaae69be2004-08-29 23:32:14 +00001426 ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
Rusty Russell79dee072000-05-02 16:45:16 +00001427 dump_entry, handle);
Harald Welte0113fe72004-01-06 19:04:02 +00001428}
Rusty Russell30fd6e52000-04-23 09:16:06 +00001429
Marc Bouchere6869a82000-03-20 06:03:29 +00001430/* Does this chain exist? */
Jan Engelhardtfd187312008-11-10 16:59:27 +01001431int TC_IS_CHAIN(const char *chain, struct xtc_handle *const handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001432{
Derrik Pates664c0a32005-02-01 13:28:14 +00001433 iptc_fn = TC_IS_CHAIN;
Harald Welteaae69be2004-08-29 23:32:14 +00001434 return iptcc_find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001435}
1436
Jan Engelhardtfd187312008-11-10 16:59:27 +01001437static void iptcc_chain_iterator_advance(struct xtc_handle *handle)
Harald Welteaae69be2004-08-29 23:32:14 +00001438{
1439 struct chain_head *c = handle->chain_iterator_cur;
1440
1441 if (c->list.next == &handle->chains)
1442 handle->chain_iterator_cur = NULL;
1443 else
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001444 handle->chain_iterator_cur =
Harald Welteaae69be2004-08-29 23:32:14 +00001445 list_entry(c->list.next, struct chain_head, list);
1446}
Marc Bouchere6869a82000-03-20 06:03:29 +00001447
Rusty Russell30fd6e52000-04-23 09:16:06 +00001448/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001449const char *
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001450TC_FIRST_CHAIN(struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001451{
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001452 struct chain_head *c = list_entry(handle->chains.next,
Harald Welteaae69be2004-08-29 23:32:14 +00001453 struct chain_head, list);
1454
1455 iptc_fn = TC_FIRST_CHAIN;
1456
1457
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001458 if (list_empty(&handle->chains)) {
Harald Welteaae69be2004-08-29 23:32:14 +00001459 DEBUGP(": no chains\n");
Harald Welte0113fe72004-01-06 19:04:02 +00001460 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +00001461 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001462
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001463 handle->chain_iterator_cur = c;
1464 iptcc_chain_iterator_advance(handle);
Harald Welte0113fe72004-01-06 19:04:02 +00001465
Harald Welteaae69be2004-08-29 23:32:14 +00001466 DEBUGP(": returning `%s'\n", c->name);
1467 return c->name;
Marc Bouchere6869a82000-03-20 06:03:29 +00001468}
1469
Rusty Russell30fd6e52000-04-23 09:16:06 +00001470/* Iterator functions to run through the chains. Returns NULL at end. */
1471const char *
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001472TC_NEXT_CHAIN(struct xtc_handle *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +00001473{
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001474 struct chain_head *c = handle->chain_iterator_cur;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001475
Harald Welteaae69be2004-08-29 23:32:14 +00001476 iptc_fn = TC_NEXT_CHAIN;
1477
1478 if (!c) {
1479 DEBUGP(": no more chains\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +00001480 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +00001481 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001482
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001483 iptcc_chain_iterator_advance(handle);
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001484
Harald Welteaae69be2004-08-29 23:32:14 +00001485 DEBUGP(": returning `%s'\n", c->name);
1486 return c->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001487}
1488
1489/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001490const STRUCT_ENTRY *
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001491TC_FIRST_RULE(const char *chain, struct xtc_handle *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +00001492{
Harald Welteaae69be2004-08-29 23:32:14 +00001493 struct chain_head *c;
1494 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001495
Harald Welteaae69be2004-08-29 23:32:14 +00001496 iptc_fn = TC_FIRST_RULE;
1497
1498 DEBUGP("first rule(%s): ", chain);
1499
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001500 c = iptcc_find_label(chain, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +00001501 if (!c) {
1502 errno = ENOENT;
1503 return NULL;
1504 }
1505
1506 /* Empty chain: single return/policy rule */
Harald Welteaae69be2004-08-29 23:32:14 +00001507 if (list_empty(&c->rules)) {
1508 DEBUGP_C("no rules, returning NULL\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +00001509 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +00001510 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001511
Harald Welteaae69be2004-08-29 23:32:14 +00001512 r = list_entry(c->rules.next, struct rule_head, list);
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001513 handle->rule_iterator_cur = r;
Harald Welteaae69be2004-08-29 23:32:14 +00001514 DEBUGP_C("%p\n", r);
1515
1516 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001517}
1518
1519/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +00001520const STRUCT_ENTRY *
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001521TC_NEXT_RULE(const STRUCT_ENTRY *prev, struct xtc_handle *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +00001522{
Harald Welteaae69be2004-08-29 23:32:14 +00001523 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001524
Derrik Pates664c0a32005-02-01 13:28:14 +00001525 iptc_fn = TC_NEXT_RULE;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001526 DEBUGP("rule_iterator_cur=%p...", handle->rule_iterator_cur);
Harald Welteaae69be2004-08-29 23:32:14 +00001527
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001528 if (handle->rule_iterator_cur == NULL) {
Harald Welteaae69be2004-08-29 23:32:14 +00001529 DEBUGP_C("returning NULL\n");
1530 return NULL;
1531 }
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001532
1533 r = list_entry(handle->rule_iterator_cur->list.next,
Harald Welteaae69be2004-08-29 23:32:14 +00001534 struct rule_head, list);
1535
1536 iptc_fn = TC_NEXT_RULE;
1537
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001538 DEBUGP_C("next=%p, head=%p...", &r->list,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001539 &handle->rule_iterator_cur->chain->rules);
Harald Welteaae69be2004-08-29 23:32:14 +00001540
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001541 if (&r->list == &handle->rule_iterator_cur->chain->rules) {
1542 handle->rule_iterator_cur = NULL;
Harald Welteaae69be2004-08-29 23:32:14 +00001543 DEBUGP_C("finished, returning NULL\n");
1544 return NULL;
1545 }
1546
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001547 handle->rule_iterator_cur = r;
Harald Welteaae69be2004-08-29 23:32:14 +00001548
1549 /* NOTE: prev is without any influence ! */
1550 DEBUGP_C("returning rule %p\n", r);
1551 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001552}
1553
Marc Bouchere6869a82000-03-20 06:03:29 +00001554/* Returns a pointer to the target name of this position. */
Jan Engelhardt33690a12008-02-11 00:54:00 +01001555static const char *standard_target_map(int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +00001556{
Harald Welteaae69be2004-08-29 23:32:14 +00001557 switch (verdict) {
1558 case RETURN:
1559 return LABEL_RETURN;
1560 break;
1561 case -NF_ACCEPT-1:
1562 return LABEL_ACCEPT;
1563 break;
1564 case -NF_DROP-1:
1565 return LABEL_DROP;
1566 break;
1567 case -NF_QUEUE-1:
1568 return LABEL_QUEUE;
1569 break;
1570 default:
1571 fprintf(stderr, "ERROR: %d not a valid target)\n",
1572 verdict);
1573 abort();
1574 break;
1575 }
1576 /* not reached */
1577 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001578}
1579
Harald Welteaae69be2004-08-29 23:32:14 +00001580/* Returns a pointer to the target name of this position. */
1581const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001582 struct xtc_handle *handle)
Harald Welteaae69be2004-08-29 23:32:14 +00001583{
1584 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russelle45c7132004-12-16 13:21:44 +00001585 struct rule_head *r = container_of(e, struct rule_head, entry[0]);
Jan Engelhardt51651b62009-10-23 23:35:49 +02001586 const unsigned char *data;
Harald Welteaae69be2004-08-29 23:32:14 +00001587
1588 iptc_fn = TC_GET_TARGET;
1589
1590 switch(r->type) {
1591 int spos;
1592 case IPTCC_R_FALLTHROUGH:
1593 return "";
1594 break;
1595 case IPTCC_R_JUMP:
1596 DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
1597 return r->jump->name;
1598 break;
1599 case IPTCC_R_STANDARD:
Jan Engelhardt51651b62009-10-23 23:35:49 +02001600 data = GET_TARGET(e)->data;
1601 spos = *(const int *)data;
Harald Welteaae69be2004-08-29 23:32:14 +00001602 DEBUGP("r=%p, spos=%d'\n", r, spos);
1603 return standard_target_map(spos);
1604 break;
1605 case IPTCC_R_MODULE:
1606 return GET_TARGET(e)->u.user.name;
1607 break;
1608 }
1609 return NULL;
1610}
Marc Bouchere6869a82000-03-20 06:03:29 +00001611/* Is this a built-in chain? Actually returns hook + 1. */
1612int
Jan Engelhardtfd187312008-11-10 16:59:27 +01001613TC_BUILTIN(const char *chain, struct xtc_handle *const handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001614{
Harald Welteaae69be2004-08-29 23:32:14 +00001615 struct chain_head *c;
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001616
Harald Welteaae69be2004-08-29 23:32:14 +00001617 iptc_fn = TC_BUILTIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001618
Harald Welteaae69be2004-08-29 23:32:14 +00001619 c = iptcc_find_label(chain, handle);
1620 if (!c) {
1621 errno = ENOENT;
Martin Josefssonb0f3d2d2004-09-23 18:23:20 +00001622 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001623 }
Harald Welteaae69be2004-08-29 23:32:14 +00001624
1625 return iptcc_is_builtin(c);
Marc Bouchere6869a82000-03-20 06:03:29 +00001626}
1627
1628/* Get the policy of a given built-in chain */
1629const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001630TC_GET_POLICY(const char *chain,
1631 STRUCT_COUNTERS *counters,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001632 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001633{
Harald Welteaae69be2004-08-29 23:32:14 +00001634 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001635
Harald Welteaae69be2004-08-29 23:32:14 +00001636 iptc_fn = TC_GET_POLICY;
1637
1638 DEBUGP("called for chain %s\n", chain);
1639
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001640 c = iptcc_find_label(chain, handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001641 if (!c) {
1642 errno = ENOENT;
1643 return NULL;
1644 }
1645
1646 if (!iptcc_is_builtin(c))
Marc Bouchere6869a82000-03-20 06:03:29 +00001647 return NULL;
1648
Harald Welteaae69be2004-08-29 23:32:14 +00001649 *counters = c->counters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001650
Harald Welteaae69be2004-08-29 23:32:14 +00001651 return standard_target_map(c->verdict);
Harald Welte0113fe72004-01-06 19:04:02 +00001652}
1653
1654static int
Harald Welteaae69be2004-08-29 23:32:14 +00001655iptcc_standard_map(struct rule_head *r, int verdict)
Harald Welte0113fe72004-01-06 19:04:02 +00001656{
Harald Welteaae69be2004-08-29 23:32:14 +00001657 STRUCT_ENTRY *e = r->entry;
Rusty Russell79dee072000-05-02 16:45:16 +00001658 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001659
Rusty Russell79dee072000-05-02 16:45:16 +00001660 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001661
Rusty Russell67088e72000-05-10 01:18:57 +00001662 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001663 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001664 errno = EINVAL;
1665 return 0;
1666 }
1667 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +00001668 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
1669 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +00001670 t->verdict = verdict;
1671
Harald Welteaae69be2004-08-29 23:32:14 +00001672 r->type = IPTCC_R_STANDARD;
1673
Marc Bouchere6869a82000-03-20 06:03:29 +00001674 return 1;
1675}
Rusty Russell7e53bf92000-03-20 07:03:28 +00001676
Marc Bouchere6869a82000-03-20 06:03:29 +00001677static int
Jan Engelhardtfd187312008-11-10 16:59:27 +01001678iptcc_map_target(struct xtc_handle *const handle,
Harald Welteaae69be2004-08-29 23:32:14 +00001679 struct rule_head *r)
Marc Bouchere6869a82000-03-20 06:03:29 +00001680{
Harald Welteaae69be2004-08-29 23:32:14 +00001681 STRUCT_ENTRY *e = r->entry;
Harald Welte0113fe72004-01-06 19:04:02 +00001682 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001683
Marc Bouchere6869a82000-03-20 06:03:29 +00001684 /* Maybe it's empty (=> fall through) */
Harald Welteaae69be2004-08-29 23:32:14 +00001685 if (strcmp(t->u.user.name, "") == 0) {
1686 r->type = IPTCC_R_FALLTHROUGH;
1687 return 1;
1688 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001689 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +00001690 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001691 return iptcc_standard_map(r, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001692 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001693 return iptcc_standard_map(r, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +00001694 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001695 return iptcc_standard_map(r, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001696 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001697 return iptcc_standard_map(r, RETURN);
Rusty Russell79dee072000-05-02 16:45:16 +00001698 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001699 /* Can't jump to builtins. */
1700 errno = EINVAL;
1701 return 0;
1702 } else {
1703 /* Maybe it's an existing chain name. */
Harald Welteaae69be2004-08-29 23:32:14 +00001704 struct chain_head *c;
1705 DEBUGP("trying to find chain `%s': ", t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +00001706
Harald Welteaae69be2004-08-29 23:32:14 +00001707 c = iptcc_find_label(t->u.user.name, handle);
1708 if (c) {
1709 DEBUGP_C("found!\n");
1710 r->type = IPTCC_R_JUMP;
1711 r->jump = c;
1712 c->references++;
1713 return 1;
1714 }
1715 DEBUGP_C("not found :(\n");
Marc Bouchere6869a82000-03-20 06:03:29 +00001716 }
1717
1718 /* Must be a module? If not, kernel will reject... */
Rusty Russell3aef54d2005-01-03 03:48:40 +00001719 /* memset to all 0 for your memcmp convenience: don't clear version */
Rusty Russell228e98d2000-04-27 10:28:06 +00001720 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001721 0,
Rusty Russell3aef54d2005-01-03 03:48:40 +00001722 FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name));
Rusty Russell733e54b2004-12-16 14:22:23 +00001723 r->type = IPTCC_R_MODULE;
Harald Welteaae69be2004-08-29 23:32:14 +00001724 set_changed(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001725 return 1;
1726}
1727
Harald Welte0113fe72004-01-06 19:04:02 +00001728/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001729int
Rusty Russell79dee072000-05-02 16:45:16 +00001730TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1731 const STRUCT_ENTRY *e,
1732 unsigned int rulenum,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001733 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001734{
Harald Welteaae69be2004-08-29 23:32:14 +00001735 struct chain_head *c;
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001736 struct rule_head *r;
1737 struct list_head *prev;
Marc Bouchere6869a82000-03-20 06:03:29 +00001738
Rusty Russell79dee072000-05-02 16:45:16 +00001739 iptc_fn = TC_INSERT_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001740
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001741 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001742 errno = ENOENT;
1743 return 0;
1744 }
1745
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001746 /* first rulenum index = 0
1747 first c->num_rules index = 1 */
1748 if (rulenum > c->num_rules) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001749 errno = E2BIG;
1750 return 0;
1751 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001752
Martin Josefsson631f3612004-09-23 19:25:06 +00001753 /* If we are inserting at the end just take advantage of the
1754 double linked list, insert will happen before the entry
1755 prev points to. */
1756 if (rulenum == c->num_rules) {
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001757 prev = &c->rules;
Martin Josefssona5616dc2004-10-24 22:27:31 +00001758 } else if (rulenum + 1 <= c->num_rules/2) {
Martin Josefsson631f3612004-09-23 19:25:06 +00001759 r = iptcc_get_rule_num(c, rulenum + 1);
Martin Josefssona5616dc2004-10-24 22:27:31 +00001760 prev = &r->list;
1761 } else {
1762 r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
Martin Josefsson631f3612004-09-23 19:25:06 +00001763 prev = &r->list;
1764 }
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001765
Harald Welteaae69be2004-08-29 23:32:14 +00001766 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1767 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001768 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001769 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001770
Harald Welteaae69be2004-08-29 23:32:14 +00001771 memcpy(r->entry, e, e->next_offset);
1772 r->counter_map.maptype = COUNTER_MAP_SET;
1773
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001774 if (!iptcc_map_target(handle, r)) {
Harald Welteaae69be2004-08-29 23:32:14 +00001775 free(r);
1776 return 0;
1777 }
1778
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001779 list_add_tail(&r->list, prev);
Harald Welteaae69be2004-08-29 23:32:14 +00001780 c->num_rules++;
1781
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001782 set_changed(handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001783
1784 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001785}
1786
1787/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1788int
Rusty Russell79dee072000-05-02 16:45:16 +00001789TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1790 const STRUCT_ENTRY *e,
1791 unsigned int rulenum,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001792 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001793{
Harald Welteaae69be2004-08-29 23:32:14 +00001794 struct chain_head *c;
1795 struct rule_head *r, *old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001796
Rusty Russell79dee072000-05-02 16:45:16 +00001797 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001798
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001799 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001800 errno = ENOENT;
1801 return 0;
1802 }
1803
Martin Josefsson0f9b8b12004-12-18 17:18:49 +00001804 if (rulenum >= c->num_rules) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001805 errno = E2BIG;
1806 return 0;
1807 }
1808
Martin Josefsson0f9b8b12004-12-18 17:18:49 +00001809 /* Take advantage of the double linked list if possible. */
1810 if (rulenum + 1 <= c->num_rules/2) {
1811 old = iptcc_get_rule_num(c, rulenum + 1);
1812 } else {
1813 old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
1814 }
1815
Harald Welteaae69be2004-08-29 23:32:14 +00001816 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1817 errno = ENOMEM;
Marc Bouchere6869a82000-03-20 06:03:29 +00001818 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001819 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001820
Harald Welteaae69be2004-08-29 23:32:14 +00001821 memcpy(r->entry, e, e->next_offset);
1822 r->counter_map.maptype = COUNTER_MAP_SET;
1823
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001824 if (!iptcc_map_target(handle, r)) {
Harald Welteaae69be2004-08-29 23:32:14 +00001825 free(r);
Harald Welte0113fe72004-01-06 19:04:02 +00001826 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001827 }
Harald Welte0113fe72004-01-06 19:04:02 +00001828
Harald Welteaae69be2004-08-29 23:32:14 +00001829 list_add(&r->list, &old->list);
1830 iptcc_delete_rule(old);
1831
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001832 set_changed(handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001833
1834 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001835}
1836
Harald Welte0113fe72004-01-06 19:04:02 +00001837/* Append entry `fw' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001838 rulenum = length of chain. */
1839int
Rusty Russell79dee072000-05-02 16:45:16 +00001840TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1841 const STRUCT_ENTRY *e,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001842 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001843{
Harald Welteaae69be2004-08-29 23:32:14 +00001844 struct chain_head *c;
1845 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00001846
Rusty Russell79dee072000-05-02 16:45:16 +00001847 iptc_fn = TC_APPEND_ENTRY;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001848 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welteaae69be2004-08-29 23:32:14 +00001849 DEBUGP("unable to find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001850 errno = ENOENT;
1851 return 0;
1852 }
1853
Harald Welteaae69be2004-08-29 23:32:14 +00001854 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1855 DEBUGP("unable to allocate rule for chain `%s'\n", chain);
1856 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001857 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001858 }
Harald Welte0113fe72004-01-06 19:04:02 +00001859
Harald Welteaae69be2004-08-29 23:32:14 +00001860 memcpy(r->entry, e, e->next_offset);
1861 r->counter_map.maptype = COUNTER_MAP_SET;
1862
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001863 if (!iptcc_map_target(handle, r)) {
Martin Josefsson12009532004-09-23 18:24:29 +00001864 DEBUGP("unable to map target of rule for chain `%s'\n", chain);
Harald Welteaae69be2004-08-29 23:32:14 +00001865 free(r);
1866 return 0;
1867 }
1868
1869 list_add_tail(&r->list, &c->rules);
1870 c->num_rules++;
1871
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001872 set_changed(handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001873
1874 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001875}
1876
1877static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001878match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001879 const unsigned char *a_elems,
1880 const unsigned char *b_elems,
1881 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001882{
Rusty Russell79dee072000-05-02 16:45:16 +00001883 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001884 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001885
1886 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001887 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001888
Rusty Russell228e98d2000-04-27 10:28:06 +00001889 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001890 return 1;
1891
Rusty Russell228e98d2000-04-27 10:28:06 +00001892 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001893 return 1;
1894
Rusty Russell73ef09b2000-07-03 10:24:04 +00001895 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001896
Rusty Russell73ef09b2000-07-03 10:24:04 +00001897 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001898 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001899 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001900 *maskptr += i;
1901 return 0;
1902}
1903
1904static inline int
Rusty Russell733e54b2004-12-16 14:22:23 +00001905target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001906{
1907 unsigned int i;
Rusty Russell733e54b2004-12-16 14:22:23 +00001908 STRUCT_ENTRY_TARGET *ta, *tb;
Marc Bouchere6869a82000-03-20 06:03:29 +00001909
Rusty Russell733e54b2004-12-16 14:22:23 +00001910 if (a->type != b->type)
1911 return 0;
1912
1913 ta = GET_TARGET(a->entry);
1914 tb = GET_TARGET(b->entry);
1915
1916 switch (a->type) {
1917 case IPTCC_R_FALLTHROUGH:
1918 return 1;
1919 case IPTCC_R_JUMP:
1920 return a->jump == b->jump;
1921 case IPTCC_R_STANDARD:
1922 return ((STRUCT_STANDARD_TARGET *)ta)->verdict
1923 == ((STRUCT_STANDARD_TARGET *)tb)->verdict;
1924 case IPTCC_R_MODULE:
1925 if (ta->u.target_size != tb->u.target_size)
1926 return 0;
1927 if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
1928 return 0;
1929
1930 for (i = 0; i < ta->u.target_size - sizeof(*ta); i++)
Rusty Russelldaade442004-12-29 11:14:52 +00001931 if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0)
Rusty Russell733e54b2004-12-16 14:22:23 +00001932 return 0;
1933 return 1;
1934 default:
1935 fprintf(stderr, "ERROR: bad type %i\n", a->type);
1936 abort();
1937 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001938}
1939
Rusty Russell733e54b2004-12-16 14:22:23 +00001940static unsigned char *
Rusty Russell79dee072000-05-02 16:45:16 +00001941is_same(const STRUCT_ENTRY *a,
1942 const STRUCT_ENTRY *b,
1943 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001944
Stefan Tomanekd59b9db2011-03-08 22:42:51 +01001945
1946/* find the first rule in `chain' which matches `fw' and remove it unless dry_run is set */
1947static int delete_entry(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
1948 unsigned char *matchmask, struct xtc_handle *handle,
1949 bool dry_run)
Marc Bouchere6869a82000-03-20 06:03:29 +00001950{
Harald Welteaae69be2004-08-29 23:32:14 +00001951 struct chain_head *c;
Rusty Russelle45c7132004-12-16 13:21:44 +00001952 struct rule_head *r, *i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001953
Rusty Russell79dee072000-05-02 16:45:16 +00001954 iptc_fn = TC_DELETE_ENTRY;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001955 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001956 errno = ENOENT;
1957 return 0;
1958 }
1959
Rusty Russelle45c7132004-12-16 13:21:44 +00001960 /* Create a rule_head from origfw. */
1961 r = iptcc_alloc_rule(c, origfw->next_offset);
1962 if (!r) {
Harald Welte0113fe72004-01-06 19:04:02 +00001963 errno = ENOMEM;
1964 return 0;
1965 }
1966
Rusty Russelle45c7132004-12-16 13:21:44 +00001967 memcpy(r->entry, origfw, origfw->next_offset);
1968 r->counter_map.maptype = COUNTER_MAP_NOMAP;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01001969 if (!iptcc_map_target(handle, r)) {
Rusty Russelle45c7132004-12-16 13:21:44 +00001970 DEBUGP("unable to map target of rule for chain `%s'\n", chain);
1971 free(r);
1972 return 0;
Patrick McHardyJesper Brouer04a1e4c2006-07-25 01:50:48 +00001973 } else {
1974 /* iptcc_map_target increment target chain references
1975 * since this is a fake rule only used for matching
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01001976 * the chain references count is decremented again.
Patrick McHardyJesper Brouer04a1e4c2006-07-25 01:50:48 +00001977 */
1978 if (r->type == IPTCC_R_JUMP
1979 && r->jump)
1980 r->jump->references--;
Rusty Russelle45c7132004-12-16 13:21:44 +00001981 }
Harald Welte0113fe72004-01-06 19:04:02 +00001982
Rusty Russelle45c7132004-12-16 13:21:44 +00001983 list_for_each_entry(i, &c->rules, list) {
Rusty Russell733e54b2004-12-16 14:22:23 +00001984 unsigned char *mask;
Harald Weltefe537072004-08-30 20:28:53 +00001985
Rusty Russell733e54b2004-12-16 14:22:23 +00001986 mask = is_same(r->entry, i->entry, matchmask);
1987 if (!mask)
1988 continue;
Martin Josefsson2a5dbbb2004-09-22 21:37:41 +00001989
Rusty Russell733e54b2004-12-16 14:22:23 +00001990 if (!target_same(r, i, mask))
1991 continue;
1992
Stefan Tomanekd59b9db2011-03-08 22:42:51 +01001993 /* if we are just doing a dry run, we simply skip the rest */
1994 if (dry_run)
1995 return 1;
1996
Rusty Russell733e54b2004-12-16 14:22:23 +00001997 /* If we are about to delete the rule that is the
1998 * current iterator, move rule iterator back. next
1999 * pointer will then point to real next node */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002000 if (i == handle->rule_iterator_cur) {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002001 handle->rule_iterator_cur =
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002002 list_entry(handle->rule_iterator_cur->list.prev,
Rusty Russell733e54b2004-12-16 14:22:23 +00002003 struct rule_head, list);
Marc Bouchere6869a82000-03-20 06:03:29 +00002004 }
Rusty Russell733e54b2004-12-16 14:22:23 +00002005
2006 c->num_rules--;
2007 iptcc_delete_rule(i);
2008
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002009 set_changed(handle);
Rusty Russell733e54b2004-12-16 14:22:23 +00002010 free(r);
2011 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002012 }
2013
Rusty Russelle45c7132004-12-16 13:21:44 +00002014 free(r);
Marc Bouchere6869a82000-03-20 06:03:29 +00002015 errno = ENOENT;
2016 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00002017}
Harald Welteaae69be2004-08-29 23:32:14 +00002018
Stefan Tomanekd59b9db2011-03-08 22:42:51 +01002019/* check whether a specified rule is present */
2020int TC_CHECK_ENTRY(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
2021 unsigned char *matchmask, struct xtc_handle *handle)
2022{
2023 /* do a dry-run delete to find out whether a matching rule exists */
2024 return delete_entry(chain, origfw, matchmask, handle, true);
2025}
2026
2027/* Delete the first rule in `chain' which matches `fw'. */
2028int TC_DELETE_ENTRY(const IPT_CHAINLABEL chain, const STRUCT_ENTRY *origfw,
2029 unsigned char *matchmask, struct xtc_handle *handle)
2030{
2031 return delete_entry(chain, origfw, matchmask, handle, false);
2032}
Marc Bouchere6869a82000-03-20 06:03:29 +00002033
2034/* Delete the rule in position `rulenum' in `chain'. */
2035int
Rusty Russell79dee072000-05-02 16:45:16 +00002036TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
2037 unsigned int rulenum,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002038 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002039{
Harald Welteaae69be2004-08-29 23:32:14 +00002040 struct chain_head *c;
2041 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00002042
Rusty Russell79dee072000-05-02 16:45:16 +00002043 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00002044
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002045 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002046 errno = ENOENT;
2047 return 0;
2048 }
2049
Martin Josefssona5616dc2004-10-24 22:27:31 +00002050 if (rulenum >= c->num_rules) {
Martin Josefsson631f3612004-09-23 19:25:06 +00002051 errno = E2BIG;
2052 return 0;
2053 }
2054
2055 /* Take advantage of the double linked list if possible. */
Martin Josefssona5616dc2004-10-24 22:27:31 +00002056 if (rulenum + 1 <= c->num_rules/2) {
2057 r = iptcc_get_rule_num(c, rulenum + 1);
2058 } else {
2059 r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
Marc Bouchere6869a82000-03-20 06:03:29 +00002060 }
2061
Harald Welteaae69be2004-08-29 23:32:14 +00002062 /* If we are about to delete the rule that is the current
2063 * iterator, move rule iterator back. next pointer will then
2064 * point to real next node */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002065 if (r == handle->rule_iterator_cur) {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002066 handle->rule_iterator_cur =
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002067 list_entry(handle->rule_iterator_cur->list.prev,
Harald Welteaae69be2004-08-29 23:32:14 +00002068 struct rule_head, list);
Harald Welte0113fe72004-01-06 19:04:02 +00002069 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002070
Harald Welteaae69be2004-08-29 23:32:14 +00002071 c->num_rules--;
2072 iptcc_delete_rule(r);
2073
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002074 set_changed(handle);
Martin Josefsson2a5dbbb2004-09-22 21:37:41 +00002075
Harald Welteaae69be2004-08-29 23:32:14 +00002076 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002077}
2078
Marc Bouchere6869a82000-03-20 06:03:29 +00002079/* Flushes the entries in the given chain (ie. empties chain). */
2080int
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002081TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002082{
Harald Welteaae69be2004-08-29 23:32:14 +00002083 struct chain_head *c;
2084 struct rule_head *r, *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00002085
Harald Welte0113fe72004-01-06 19:04:02 +00002086 iptc_fn = TC_FLUSH_ENTRIES;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002087 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002088 errno = ENOENT;
2089 return 0;
2090 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002091
Harald Welteaae69be2004-08-29 23:32:14 +00002092 list_for_each_entry_safe(r, tmp, &c->rules, list) {
2093 iptcc_delete_rule(r);
2094 }
2095
2096 c->num_rules = 0;
2097
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002098 set_changed(handle);
Harald Welteaae69be2004-08-29 23:32:14 +00002099
2100 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002101}
2102
2103/* Zeroes the counters in a chain. */
2104int
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002105TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002106{
Harald Welteaae69be2004-08-29 23:32:14 +00002107 struct chain_head *c;
2108 struct rule_head *r;
Rusty Russell7e53bf92000-03-20 07:03:28 +00002109
Derrik Pates664c0a32005-02-01 13:28:14 +00002110 iptc_fn = TC_ZERO_ENTRIES;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002111 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002112 errno = ENOENT;
2113 return 0;
2114 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002115
Andy Gaye5bd1d72006-08-22 02:56:41 +00002116 if (c->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2117 c->counter_map.maptype = COUNTER_MAP_ZEROED;
2118
Harald Welteaae69be2004-08-29 23:32:14 +00002119 list_for_each_entry(r, &c->rules, list) {
2120 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2121 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00002122 }
Harald Welteaae69be2004-08-29 23:32:14 +00002123
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002124 set_changed(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00002125
Marc Bouchere6869a82000-03-20 06:03:29 +00002126 return 1;
2127}
2128
Harald Welte1cef74d2001-01-05 15:22:59 +00002129STRUCT_COUNTERS *
2130TC_READ_COUNTER(const IPT_CHAINLABEL chain,
2131 unsigned int rulenum,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002132 struct xtc_handle *handle)
Harald Welte1cef74d2001-01-05 15:22:59 +00002133{
Harald Welteaae69be2004-08-29 23:32:14 +00002134 struct chain_head *c;
2135 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00002136
2137 iptc_fn = TC_READ_COUNTER;
2138 CHECK(*handle);
2139
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002140 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00002141 errno = ENOENT;
2142 return NULL;
2143 }
2144
Harald Welteaae69be2004-08-29 23:32:14 +00002145 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00002146 errno = E2BIG;
2147 return NULL;
2148 }
2149
Harald Welteaae69be2004-08-29 23:32:14 +00002150 return &r->entry[0].counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00002151}
2152
2153int
2154TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
2155 unsigned int rulenum,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002156 struct xtc_handle *handle)
Harald Welte1cef74d2001-01-05 15:22:59 +00002157{
Harald Welteaae69be2004-08-29 23:32:14 +00002158 struct chain_head *c;
2159 struct rule_head *r;
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002160
Harald Welte1cef74d2001-01-05 15:22:59 +00002161 iptc_fn = TC_ZERO_COUNTER;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002162 CHECK(handle);
Harald Welte1cef74d2001-01-05 15:22:59 +00002163
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002164 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00002165 errno = ENOENT;
2166 return 0;
2167 }
2168
Harald Welteaae69be2004-08-29 23:32:14 +00002169 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00002170 errno = E2BIG;
2171 return 0;
2172 }
2173
Harald Welteaae69be2004-08-29 23:32:14 +00002174 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
2175 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Harald Welte1cef74d2001-01-05 15:22:59 +00002176
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002177 set_changed(handle);
Harald Welte1cef74d2001-01-05 15:22:59 +00002178
2179 return 1;
2180}
2181
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002182int
Harald Welte1cef74d2001-01-05 15:22:59 +00002183TC_SET_COUNTER(const IPT_CHAINLABEL chain,
2184 unsigned int rulenum,
2185 STRUCT_COUNTERS *counters,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002186 struct xtc_handle *handle)
Harald Welte1cef74d2001-01-05 15:22:59 +00002187{
Harald Welteaae69be2004-08-29 23:32:14 +00002188 struct chain_head *c;
2189 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00002190 STRUCT_ENTRY *e;
Harald Welte1cef74d2001-01-05 15:22:59 +00002191
2192 iptc_fn = TC_SET_COUNTER;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002193 CHECK(handle);
Harald Welte1cef74d2001-01-05 15:22:59 +00002194
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002195 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00002196 errno = ENOENT;
2197 return 0;
2198 }
Harald Welte0113fe72004-01-06 19:04:02 +00002199
Harald Welteaae69be2004-08-29 23:32:14 +00002200 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00002201 errno = E2BIG;
2202 return 0;
2203 }
2204
Harald Welteaae69be2004-08-29 23:32:14 +00002205 e = r->entry;
2206 r->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte0113fe72004-01-06 19:04:02 +00002207
2208 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00002209
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002210 set_changed(handle);
Harald Welte1cef74d2001-01-05 15:22:59 +00002211
2212 return 1;
2213}
2214
Marc Bouchere6869a82000-03-20 06:03:29 +00002215/* Creates a new chain. */
2216/* To create a chain, create two rules: error node and unconditional
2217 * return. */
2218int
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002219TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002220{
Harald Welteaae69be2004-08-29 23:32:14 +00002221 static struct chain_head *c;
Patrick McHardy1f23d3c2008-06-07 15:04:34 +02002222 int capacity;
2223 int exceeded;
Marc Bouchere6869a82000-03-20 06:03:29 +00002224
Rusty Russell79dee072000-05-02 16:45:16 +00002225 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00002226
2227 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
2228 QUEUE, RETURN. */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002229 if (iptcc_find_label(chain, handle)
Rusty Russell79dee072000-05-02 16:45:16 +00002230 || strcmp(chain, LABEL_DROP) == 0
2231 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00002232 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00002233 || strcmp(chain, LABEL_RETURN) == 0) {
Harald Welteaae69be2004-08-29 23:32:14 +00002234 DEBUGP("Chain `%s' already exists\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00002235 errno = EEXIST;
2236 return 0;
2237 }
2238
Rusty Russell79dee072000-05-02 16:45:16 +00002239 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Harald Welteaae69be2004-08-29 23:32:14 +00002240 DEBUGP("Chain name `%s' too long\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00002241 errno = EINVAL;
2242 return 0;
2243 }
2244
Harald Welteaae69be2004-08-29 23:32:14 +00002245 c = iptcc_alloc_chain_head(chain, 0);
2246 if (!c) {
2247 DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
2248 errno = ENOMEM;
2249 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00002250
Harald Welteaae69be2004-08-29 23:32:14 +00002251 }
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002252 handle->num_chains++; /* New user defined chain */
Marc Bouchere6869a82000-03-20 06:03:29 +00002253
Harald Welteaae69be2004-08-29 23:32:14 +00002254 DEBUGP("Creating chain `%s'\n", chain);
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002255 iptc_insert_chain(handle, c); /* Insert sorted */
Harald Welte0113fe72004-01-06 19:04:02 +00002256
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00002257 /* Inserting chains don't change the correctness of the chain
2258 * index (except if its smaller than index[0], but that
2259 * handled by iptc_insert_chain). It only causes longer lists
2260 * in the buckets. Thus, only rebuild chain index when the
2261 * capacity is exceed with CHAIN_INDEX_INSERT_MAX chains.
2262 */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002263 capacity = handle->chain_index_sz * CHAIN_INDEX_BUCKET_LEN;
2264 exceeded = handle->num_chains - capacity;
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00002265 if (exceeded > CHAIN_INDEX_INSERT_MAX) {
2266 debug("Capacity(%d) exceeded(%d) rebuild (chains:%d)\n",
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002267 capacity, exceeded, handle->num_chains);
2268 iptcc_chain_index_rebuild(handle);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00002269 }
2270
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002271 set_changed(handle);
Harald Welte0113fe72004-01-06 19:04:02 +00002272
Harald Welteaae69be2004-08-29 23:32:14 +00002273 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002274}
2275
2276/* Get the number of references to this chain. */
2277int
Rusty Russell79dee072000-05-02 16:45:16 +00002278TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002279 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002280{
Harald Welteaae69be2004-08-29 23:32:14 +00002281 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00002282
Derrik Pates664c0a32005-02-01 13:28:14 +00002283 iptc_fn = TC_GET_REFERENCES;
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002284 if (!(c = iptcc_find_label(chain, handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002285 errno = ENOENT;
2286 return 0;
2287 }
2288
Harald Welteaae69be2004-08-29 23:32:14 +00002289 *ref = c->references;
2290
Marc Bouchere6869a82000-03-20 06:03:29 +00002291 return 1;
2292}
2293
2294/* Deletes a chain. */
2295int
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002296TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002297{
Marc Bouchere6869a82000-03-20 06:03:29 +00002298 unsigned int references;
Harald Welteaae69be2004-08-29 23:32:14 +00002299 struct chain_head *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00002300
Rusty Russell79dee072000-05-02 16:45:16 +00002301 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00002302
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002303 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welteaae69be2004-08-29 23:32:14 +00002304 DEBUGP("cannot find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00002305 errno = ENOENT;
2306 return 0;
2307 }
2308
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002309 if (TC_BUILTIN(chain, handle)) {
Harald Welteaae69be2004-08-29 23:32:14 +00002310 DEBUGP("cannot remove builtin chain `%s'\n", chain);
2311 errno = EINVAL;
2312 return 0;
2313 }
2314
2315 if (!TC_GET_REFERENCES(&references, chain, handle)) {
2316 DEBUGP("cannot get references on chain `%s'\n", chain);
2317 return 0;
2318 }
2319
2320 if (references > 0) {
2321 DEBUGP("chain `%s' still has references\n", chain);
2322 errno = EMLINK;
2323 return 0;
2324 }
2325
2326 if (c->num_rules) {
2327 DEBUGP("chain `%s' is not empty\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00002328 errno = ENOTEMPTY;
2329 return 0;
2330 }
2331
Harald Welteaae69be2004-08-29 23:32:14 +00002332 /* If we are about to delete the chain that is the current
Jesper Dangaard Brouer48bde402008-01-15 17:06:48 +00002333 * iterator, move chain iterator forward. */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002334 if (c == handle->chain_iterator_cur)
2335 iptcc_chain_iterator_advance(handle);
Harald Welte0113fe72004-01-06 19:04:02 +00002336
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002337 handle->num_chains--; /* One user defined chain deleted */
Jesper Dangaard Brouer48bde402008-01-15 17:06:48 +00002338
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00002339 //list_del(&c->list); /* Done in iptcc_chain_index_delete_chain() */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002340 iptcc_chain_index_delete_chain(c, handle);
Jesper Dangaard Brouer01444da2008-01-15 17:18:15 +00002341 free(c);
2342
Harald Welteaae69be2004-08-29 23:32:14 +00002343 DEBUGP("chain `%s' deleted\n", chain);
2344
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002345 set_changed(handle);
Harald Welteaae69be2004-08-29 23:32:14 +00002346
2347 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002348}
2349
2350/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00002351int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
2352 const IPT_CHAINLABEL newname,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002353 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002354{
Harald Welteaae69be2004-08-29 23:32:14 +00002355 struct chain_head *c;
Rusty Russell79dee072000-05-02 16:45:16 +00002356 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00002357
Harald Welte1de80462000-10-30 12:00:27 +00002358 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
2359 QUEUE, RETURN. */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002360 if (iptcc_find_label(newname, handle)
Rusty Russell79dee072000-05-02 16:45:16 +00002361 || strcmp(newname, LABEL_DROP) == 0
2362 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00002363 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00002364 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002365 errno = EEXIST;
2366 return 0;
2367 }
2368
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002369 if (!(c = iptcc_find_label(oldname, handle))
2370 || TC_BUILTIN(oldname, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002371 errno = ENOENT;
2372 return 0;
2373 }
2374
Rusty Russell79dee072000-05-02 16:45:16 +00002375 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002376 errno = EINVAL;
2377 return 0;
2378 }
2379
Jesper Dangaard Brouer64ff47c2009-03-23 14:25:49 +01002380 /* This only unlinks "c" from the list, thus no free(c) */
2381 iptcc_chain_index_delete_chain(c, handle);
2382
2383 /* Change the name of the chain */
Harald Welteaae69be2004-08-29 23:32:14 +00002384 strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
Jesper Dangaard Brouer64ff47c2009-03-23 14:25:49 +01002385
2386 /* Insert sorted into to list again */
2387 iptc_insert_chain(handle, c);
2388
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002389 set_changed(handle);
Harald Welte0113fe72004-01-06 19:04:02 +00002390
Marc Bouchere6869a82000-03-20 06:03:29 +00002391 return 1;
2392}
2393
2394/* Sets the policy on a built-in chain. */
2395int
Rusty Russell79dee072000-05-02 16:45:16 +00002396TC_SET_POLICY(const IPT_CHAINLABEL chain,
2397 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00002398 STRUCT_COUNTERS *counters,
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002399 struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002400{
Harald Welteaae69be2004-08-29 23:32:14 +00002401 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00002402
Rusty Russell79dee072000-05-02 16:45:16 +00002403 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00002404
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002405 if (!(c = iptcc_find_label(chain, handle))) {
Harald Welteaae69be2004-08-29 23:32:14 +00002406 DEBUGP("cannot find chain `%s'\n", chain);
2407 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00002408 return 0;
2409 }
2410
Harald Welteaae69be2004-08-29 23:32:14 +00002411 if (!iptcc_is_builtin(c)) {
2412 DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
2413 errno = ENOENT;
2414 return 0;
2415 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002416
Rusty Russell79dee072000-05-02 16:45:16 +00002417 if (strcmp(policy, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00002418 c->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00002419 else if (strcmp(policy, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00002420 c->verdict = -NF_DROP - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00002421 else {
2422 errno = EINVAL;
2423 return 0;
2424 }
Harald Welte1cef74d2001-01-05 15:22:59 +00002425
Harald Welte1cef74d2001-01-05 15:22:59 +00002426 if (counters) {
2427 /* set byte and packet counters */
Harald Welteaae69be2004-08-29 23:32:14 +00002428 memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
2429 c->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00002430 } else {
Harald Welteaae69be2004-08-29 23:32:14 +00002431 c->counter_map.maptype = COUNTER_MAP_NOMAP;
Harald Welte1cef74d2001-01-05 15:22:59 +00002432 }
2433
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002434 set_changed(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00002435
Marc Bouchere6869a82000-03-20 06:03:29 +00002436 return 1;
2437}
2438
2439/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00002440 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00002441 libiptc.c:833: fixed or forbidden register was spilled.
2442 This may be due to a compiler bug or to impossible asm
2443 statements or clauses.
2444*/
2445static void
Rusty Russell79dee072000-05-02 16:45:16 +00002446subtract_counters(STRUCT_COUNTERS *answer,
2447 const STRUCT_COUNTERS *a,
2448 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00002449{
2450 answer->pcnt = a->pcnt - b->pcnt;
2451 answer->bcnt = a->bcnt - b->bcnt;
2452}
2453
Harald Welteaae69be2004-08-29 23:32:14 +00002454
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002455static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, unsigned int idx)
Harald Welteaae69be2004-08-29 23:32:14 +00002456{
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002457 newcounters->counters[idx] = ((STRUCT_COUNTERS) { 0, 0});
Harald Welteaae69be2004-08-29 23:32:14 +00002458 DEBUGP_C("NOMAP => zero\n");
2459}
2460
2461static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002462 STRUCT_REPLACE *repl, unsigned int idx,
Harald Welteaae69be2004-08-29 23:32:14 +00002463 unsigned int mappos)
2464{
2465 /* Original read: X.
2466 * Atomic read on replacement: X + Y.
2467 * Currently in kernel: Z.
2468 * Want in kernel: X + Y + Z.
2469 * => Add in X + Y
2470 * => Add in replacement read.
2471 */
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002472 newcounters->counters[idx] = repl->counters[mappos];
Harald Welteaae69be2004-08-29 23:32:14 +00002473 DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
2474}
2475
2476static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002477 STRUCT_REPLACE *repl, unsigned int idx,
2478 unsigned int mappos, STRUCT_COUNTERS *counters)
Harald Welteaae69be2004-08-29 23:32:14 +00002479{
2480 /* Original read: X.
2481 * Atomic read on replacement: X + Y.
2482 * Currently in kernel: Z.
2483 * Want in kernel: Y + Z.
2484 * => Add in Y.
2485 * => Add in (replacement read - original read).
2486 */
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002487 subtract_counters(&newcounters->counters[idx],
Harald Welteaae69be2004-08-29 23:32:14 +00002488 &repl->counters[mappos],
2489 counters);
2490 DEBUGP_C("ZEROED => mappos %u\n", mappos);
2491}
2492
2493static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002494 unsigned int idx, STRUCT_COUNTERS *counters)
Harald Welteaae69be2004-08-29 23:32:14 +00002495{
2496 /* Want to set counter (iptables-restore) */
2497
Jan Engelhardtdbb77542008-02-11 00:33:30 +01002498 memcpy(&newcounters->counters[idx], counters,
Harald Welteaae69be2004-08-29 23:32:14 +00002499 sizeof(STRUCT_COUNTERS));
2500
2501 DEBUGP_C("SET\n");
2502}
2503
2504
Marc Bouchere6869a82000-03-20 06:03:29 +00002505int
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002506TC_COMMIT(struct xtc_handle *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00002507{
2508 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00002509 STRUCT_REPLACE *repl;
2510 STRUCT_COUNTERS_INFO *newcounters;
Harald Welteaae69be2004-08-29 23:32:14 +00002511 struct chain_head *c;
2512 int ret;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00002513 size_t counterlen;
Harald Welteaae69be2004-08-29 23:32:14 +00002514 int new_number;
2515 unsigned int new_size;
Marc Bouchere6869a82000-03-20 06:03:29 +00002516
Derrik Pates664c0a32005-02-01 13:28:14 +00002517 iptc_fn = TC_COMMIT;
Marc Bouchere6869a82000-03-20 06:03:29 +00002518 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00002519
Marc Bouchere6869a82000-03-20 06:03:29 +00002520 /* Don't commit if nothing changed. */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002521 if (!handle->changed)
Marc Bouchere6869a82000-03-20 06:03:29 +00002522 goto finished;
2523
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002524 new_number = iptcc_compile_table_prep(handle, &new_size);
Harald Welteaae69be2004-08-29 23:32:14 +00002525 if (new_number < 0) {
2526 errno = ENOMEM;
Harald Welted6ba6f52005-11-12 10:39:40 +00002527 goto out_zero;
Harald Welteaae69be2004-08-29 23:32:14 +00002528 }
2529
2530 repl = malloc(sizeof(*repl) + new_size);
Marc Bouchere6869a82000-03-20 06:03:29 +00002531 if (!repl) {
2532 errno = ENOMEM;
Harald Welted6ba6f52005-11-12 10:39:40 +00002533 goto out_zero;
Marc Bouchere6869a82000-03-20 06:03:29 +00002534 }
Martin Josefssonad3b4f92004-09-22 22:04:07 +00002535 memset(repl, 0, sizeof(*repl) + new_size);
Harald Welteaae69be2004-08-29 23:32:14 +00002536
Rusty Russelle45c7132004-12-16 13:21:44 +00002537#if 0
2538 TC_DUMP_ENTRIES(*handle);
2539#endif
2540
Harald Welteaae69be2004-08-29 23:32:14 +00002541 counterlen = sizeof(STRUCT_COUNTERS_INFO)
2542 + sizeof(STRUCT_COUNTERS) * new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00002543
2544 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00002545 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002546 * handle->info.num_entries);
Marc Bouchere6869a82000-03-20 06:03:29 +00002547 if (!repl->counters) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002548 errno = ENOMEM;
Harald Welted6ba6f52005-11-12 10:39:40 +00002549 goto out_free_repl;
Marc Bouchere6869a82000-03-20 06:03:29 +00002550 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002551 /* These are the counters we're going to put back, later. */
2552 newcounters = malloc(counterlen);
2553 if (!newcounters) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002554 errno = ENOMEM;
Harald Welted6ba6f52005-11-12 10:39:40 +00002555 goto out_free_repl_counters;
Marc Bouchere6869a82000-03-20 06:03:29 +00002556 }
Harald Welteaae69be2004-08-29 23:32:14 +00002557 memset(newcounters, 0, counterlen);
Marc Bouchere6869a82000-03-20 06:03:29 +00002558
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002559 strcpy(repl->name, handle->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00002560 repl->num_entries = new_number;
2561 repl->size = new_size;
2562
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002563 repl->num_counters = handle->info.num_entries;
2564 repl->valid_hooks = handle->info.valid_hooks;
Harald Welteaae69be2004-08-29 23:32:14 +00002565
2566 DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
2567 repl->num_entries, repl->size, repl->num_counters);
2568
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002569 ret = iptcc_compile_table(handle, repl);
Harald Welteaae69be2004-08-29 23:32:14 +00002570 if (ret < 0) {
2571 errno = ret;
Harald Welted6ba6f52005-11-12 10:39:40 +00002572 goto out_free_newcounters;
Harald Welteaae69be2004-08-29 23:32:14 +00002573 }
2574
2575
2576#ifdef IPTC_DEBUG2
2577 {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002578 int fd = open("/tmp/libiptc-so_set_replace.blob",
Harald Welteaae69be2004-08-29 23:32:14 +00002579 O_CREAT|O_WRONLY);
2580 if (fd >= 0) {
2581 write(fd, repl, sizeof(*repl) + repl->size);
2582 close(fd);
2583 }
2584 }
2585#endif
Marc Bouchere6869a82000-03-20 06:03:29 +00002586
Jan Engelhardt175f4512008-11-10 17:25:55 +01002587 ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Harald Welted6ba6f52005-11-12 10:39:40 +00002588 sizeof(*repl) + repl->size);
Patrick McHardye0865ad2006-04-22 02:08:56 +00002589 if (ret < 0)
Harald Welted6ba6f52005-11-12 10:39:40 +00002590 goto out_free_newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00002591
2592 /* Put counters back. */
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002593 strcpy(newcounters->name, handle->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00002594 newcounters->num_counters = new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00002595
Jan Engelhardt1c9015b2008-11-10 17:00:41 +01002596 list_for_each_entry(c, &handle->chains, list) {
Harald Welteaae69be2004-08-29 23:32:14 +00002597 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00002598
Harald Welteaae69be2004-08-29 23:32:14 +00002599 /* Builtin chains have their own counters */
2600 if (iptcc_is_builtin(c)) {
2601 DEBUGP("counter for chain-index %u: ", c->foot_index);
2602 switch(c->counter_map.maptype) {
2603 case COUNTER_MAP_NOMAP:
2604 counters_nomap(newcounters, c->foot_index);
2605 break;
2606 case COUNTER_MAP_NORMAL_MAP:
2607 counters_normal_map(newcounters, repl,
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002608 c->foot_index,
Harald Welteaae69be2004-08-29 23:32:14 +00002609 c->counter_map.mappos);
2610 break;
2611 case COUNTER_MAP_ZEROED:
2612 counters_map_zeroed(newcounters, repl,
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002613 c->foot_index,
Harald Welteaae69be2004-08-29 23:32:14 +00002614 c->counter_map.mappos,
2615 &c->counters);
2616 break;
2617 case COUNTER_MAP_SET:
2618 counters_map_set(newcounters, c->foot_index,
2619 &c->counters);
2620 break;
2621 }
2622 }
Harald Welte1cef74d2001-01-05 15:22:59 +00002623
Harald Welteaae69be2004-08-29 23:32:14 +00002624 list_for_each_entry(r, &c->rules, list) {
2625 DEBUGP("counter for index %u: ", r->index);
2626 switch (r->counter_map.maptype) {
2627 case COUNTER_MAP_NOMAP:
2628 counters_nomap(newcounters, r->index);
2629 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002630
Harald Welteaae69be2004-08-29 23:32:14 +00002631 case COUNTER_MAP_NORMAL_MAP:
2632 counters_normal_map(newcounters, repl,
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002633 r->index,
Harald Welteaae69be2004-08-29 23:32:14 +00002634 r->counter_map.mappos);
2635 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002636
Harald Welteaae69be2004-08-29 23:32:14 +00002637 case COUNTER_MAP_ZEROED:
2638 counters_map_zeroed(newcounters, repl,
2639 r->index,
2640 r->counter_map.mappos,
2641 &r->entry->counters);
2642 break;
2643
2644 case COUNTER_MAP_SET:
2645 counters_map_set(newcounters, r->index,
2646 &r->entry->counters);
2647 break;
2648 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002649 }
2650 }
Rusty Russell62527ce2000-09-04 09:45:54 +00002651
Harald Welteaae69be2004-08-29 23:32:14 +00002652#ifdef IPTC_DEBUG2
2653 {
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002654 int fd = open("/tmp/libiptc-so_set_add_counters.blob",
Harald Welteaae69be2004-08-29 23:32:14 +00002655 O_CREAT|O_WRONLY);
2656 if (fd >= 0) {
2657 write(fd, newcounters, counterlen);
2658 close(fd);
2659 }
2660 }
2661#endif
2662
Jan Engelhardt175f4512008-11-10 17:25:55 +01002663 ret = setsockopt(handle->sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
Harald Welted6ba6f52005-11-12 10:39:40 +00002664 newcounters, counterlen);
Patrick McHardye0865ad2006-04-22 02:08:56 +00002665 if (ret < 0)
Harald Welted6ba6f52005-11-12 10:39:40 +00002666 goto out_free_newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00002667
2668 free(repl->counters);
2669 free(repl);
2670 free(newcounters);
2671
Harald Welted6ba6f52005-11-12 10:39:40 +00002672finished:
Marc Bouchere6869a82000-03-20 06:03:29 +00002673 return 1;
Harald Welted6ba6f52005-11-12 10:39:40 +00002674
2675out_free_newcounters:
2676 free(newcounters);
2677out_free_repl_counters:
2678 free(repl->counters);
2679out_free_repl:
2680 free(repl);
2681out_zero:
2682 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00002683}
2684
Marc Bouchere6869a82000-03-20 06:03:29 +00002685/* Translates errno numbers into more human-readable form than strerror. */
2686const char *
Rusty Russell79dee072000-05-02 16:45:16 +00002687TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00002688{
2689 unsigned int i;
2690 struct table_struct {
2691 void *fn;
2692 int err;
2693 const char *message;
2694 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00002695 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002696 { TC_INIT, EINVAL, "Module is wrong version" },
Jesper Dangaard Brouera9fe5b32009-03-23 14:26:56 +01002697 { TC_INIT, ENOENT,
Harald Welte4ccfa632001-07-30 15:12:43 +00002698 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002699 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
2700 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
2701 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00002702 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00002703 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
2704 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
2705 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
2706 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00002707 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
2708 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00002709 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
2710 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002711 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00002712 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002713 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002714 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002715 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00002716 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00002717 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002718
2719 { NULL, 0, "Incompatible with this kernel" },
2720 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
2721 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
2722 { NULL, ENOMEM, "Memory allocation problem" },
2723 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002724 };
2725
2726 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
2727 if ((!table[i].fn || table[i].fn == iptc_fn)
2728 && table[i].err == err)
2729 return table[i].message;
2730 }
2731
2732 return strerror(err);
2733}