blob: 0085a065a5156f774225622564fb2951488ff514 [file] [log] [blame]
Martin Josefsson631f3612004-09-23 19:25:06 +00001/* Library which manipulates firewall rules. Version $Revision: 1.56 $ */
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
12 * 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
20 * up after a ruleset change.
Harald Welteaae69be2004-08-29 23:32:14 +000021 * 2004-Aug-18: Harald Welte <laforge@netfilter.org>:
22 * - futher performance work: total reimplementation of libiptc.
23 * - 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/)
Harald Welte3ea8f402003-06-23 18:25:59 +000026 */
Harald Welte15920d12004-05-16 09:05:07 +000027#include <sys/types.h>
28#include <sys/socket.h>
Stephane Ouellette7cd00282004-05-14 08:21:06 +000029
Harald Welteaae69be2004-08-29 23:32:14 +000030#include "linux_list.h"
31
32//#define IPTC_DEBUG2 1
33
34#ifdef IPTC_DEBUG2
35#include <fcntl.h>
36#define DEBUGP(x, args...) fprintf(stderr, "%s: " x, __FUNCTION__, ## args)
37#define DEBUGP_C(x, args...) fprintf(stderr, x, ## args)
38#else
39#define DEBUGP(x, args...)
40#define DEBUGP_C(x, args...)
41#endif
42
Marc Bouchere6869a82000-03-20 06:03:29 +000043#ifndef IPT_LIB_DIR
44#define IPT_LIB_DIR "/usr/local/lib/iptables"
45#endif
46
Harald Welte0113fe72004-01-06 19:04:02 +000047#ifndef __OPTIMIZE__
48STRUCT_ENTRY_TARGET *
49GET_TARGET(STRUCT_ENTRY *e)
50{
51 return (void *)e + e->target_offset;
52}
53#endif
54
Marc Bouchere6869a82000-03-20 06:03:29 +000055static int sockfd = -1;
56static void *iptc_fn = NULL;
57
58static const char *hooknames[]
Rusty Russell79dee072000-05-02 16:45:16 +000059= { [HOOK_PRE_ROUTING] "PREROUTING",
60 [HOOK_LOCAL_IN] "INPUT",
61 [HOOK_FORWARD] "FORWARD",
62 [HOOK_LOCAL_OUT] "OUTPUT",
Rusty Russell10758b72000-09-14 07:37:33 +000063 [HOOK_POST_ROUTING] "POSTROUTING",
64#ifdef HOOK_DROPPING
65 [HOOK_DROPPING] "DROPPING"
66#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000067};
68
Harald Welteaae69be2004-08-29 23:32:14 +000069/* Convenience structures */
70struct ipt_error_target
71{
72 STRUCT_ENTRY_TARGET t;
73 char error[TABLE_MAXNAMELEN];
74};
75
76struct chain_head;
77struct rule_head;
78
Marc Bouchere6869a82000-03-20 06:03:29 +000079struct counter_map
80{
81 enum {
82 COUNTER_MAP_NOMAP,
83 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000084 COUNTER_MAP_ZEROED,
85 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000086 } maptype;
87 unsigned int mappos;
88};
89
Harald Welteaae69be2004-08-29 23:32:14 +000090enum iptcc_rule_type {
91 IPTCC_R_STANDARD, /* standard target (ACCEPT, ...) */
92 IPTCC_R_MODULE, /* extension module (SNAT, ...) */
93 IPTCC_R_FALLTHROUGH, /* fallthrough rule */
94 IPTCC_R_JUMP, /* jump to other chain */
Marc Bouchere6869a82000-03-20 06:03:29 +000095};
96
Harald Welteaae69be2004-08-29 23:32:14 +000097struct rule_head
Rusty Russell30fd6e52000-04-23 09:16:06 +000098{
Harald Welteaae69be2004-08-29 23:32:14 +000099 struct list_head list;
100 struct chain_head *chain;
101 struct counter_map counter_map;
102
103 unsigned int index; /* index (needed for counter_map) */
104 unsigned int offset; /* offset in rule blob */
105
106 enum iptcc_rule_type type;
107 struct chain_head *jump; /* jump target, if IPTCC_R_JUMP */
108
109 unsigned int size; /* size of entry data */
110 STRUCT_ENTRY entry[0];
111};
112
113struct chain_head
114{
115 struct list_head list;
Rusty Russell79dee072000-05-02 16:45:16 +0000116 char name[TABLE_MAXNAMELEN];
Harald Welteaae69be2004-08-29 23:32:14 +0000117 unsigned int hooknum; /* hook number+1 if builtin */
118 unsigned int references; /* how many jumps reference us */
119 int verdict; /* verdict if builtin */
120
121 STRUCT_COUNTERS counters; /* per-chain counters */
122 struct counter_map counter_map;
123
124 unsigned int num_rules; /* number of rules in list */
125 struct list_head rules; /* list of rules */
126
127 unsigned int index; /* index (needed for jump resolval) */
128 unsigned int head_offset; /* offset in rule blob */
129 unsigned int foot_index; /* index (needed for counter_map) */
130 unsigned int foot_offset; /* offset in rule blob */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000131};
132
Rusty Russell79dee072000-05-02 16:45:16 +0000133STRUCT_TC_HANDLE
Marc Bouchere6869a82000-03-20 06:03:29 +0000134{
Harald Welteaae69be2004-08-29 23:32:14 +0000135 int changed; /* Have changes been made? */
136
137 struct list_head chains;
138
139 struct chain_head *chain_iterator_cur;
140 struct rule_head *rule_iterator_cur;
141
Rusty Russell79dee072000-05-02 16:45:16 +0000142 STRUCT_GETINFO info;
Harald Welteaae69be2004-08-29 23:32:14 +0000143 STRUCT_GET_ENTRIES *entries;
Marc Bouchere6869a82000-03-20 06:03:29 +0000144};
145
Harald Welteaae69be2004-08-29 23:32:14 +0000146/* allocate a new chain head for the cache */
147static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum)
148{
149 struct chain_head *c = malloc(sizeof(*c));
150 if (!c)
151 return NULL;
152 memset(c, 0, sizeof(*c));
153
154 strncpy(c->name, name, TABLE_MAXNAMELEN);
155 c->hooknum = hooknum;
156 INIT_LIST_HEAD(&c->rules);
157
158 return c;
159}
160
161/* allocate and initialize a new rule for the cache */
162static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size)
163{
164 struct rule_head *r = malloc(sizeof(*r)+size);
165 if (!r)
166 return NULL;
167 memset(r, 0, sizeof(*r));
168
169 r->chain = c;
170 r->size = size;
171
172 return r;
173}
174
175/* notify us that the ruleset has been modified by the user */
Rusty Russell175f6412000-03-24 09:32:20 +0000176static void
Rusty Russell79dee072000-05-02 16:45:16 +0000177set_changed(TC_HANDLE_T h)
Rusty Russell175f6412000-03-24 09:32:20 +0000178{
Rusty Russell175f6412000-03-24 09:32:20 +0000179 h->changed = 1;
180}
181
Harald Welte380ba5f2002-02-13 16:19:55 +0000182#ifdef IPTC_DEBUG
Rusty Russell79dee072000-05-02 16:45:16 +0000183static void do_check(TC_HANDLE_T h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000184#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000185#else
186#define CHECK(h)
187#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000188
Harald Welteaae69be2004-08-29 23:32:14 +0000189
190/**********************************************************************
191 * iptc blob utility functions (iptcb_*)
192 **********************************************************************/
193
Marc Bouchere6869a82000-03-20 06:03:29 +0000194static inline int
Harald Welteaae69be2004-08-29 23:32:14 +0000195iptcb_get_number(const STRUCT_ENTRY *i,
Rusty Russell79dee072000-05-02 16:45:16 +0000196 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000197 unsigned int *pos)
198{
199 if (i == seek)
200 return 1;
201 (*pos)++;
202 return 0;
203}
204
Marc Bouchere6869a82000-03-20 06:03:29 +0000205static inline int
Harald Welteaae69be2004-08-29 23:32:14 +0000206iptcb_get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000207 unsigned int number,
208 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000209 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000210{
211 if (*pos == number) {
212 *pe = i;
213 return 1;
214 }
215 (*pos)++;
216 return 0;
217}
218
Harald Welteaae69be2004-08-29 23:32:14 +0000219static inline STRUCT_ENTRY *
220iptcb_get_entry(TC_HANDLE_T h, unsigned int offset)
221{
222 return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset);
223}
224
225static unsigned int
226iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000227{
228 unsigned int pos = 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000229
Harald Welteaae69be2004-08-29 23:32:14 +0000230 if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
231 iptcb_get_number, seek, &pos) == 0) {
232 fprintf(stderr, "ERROR: offset %u not an entry!\n",
233 (unsigned int)((char *)seek - (char *)h->entries->entrytable));
234 abort();
235 }
236 return pos;
Marc Bouchere6869a82000-03-20 06:03:29 +0000237}
238
Harald Welte0113fe72004-01-06 19:04:02 +0000239static inline STRUCT_ENTRY *
Harald Welteaae69be2004-08-29 23:32:14 +0000240iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset)
Harald Welte0113fe72004-01-06 19:04:02 +0000241{
Harald Welteaae69be2004-08-29 23:32:14 +0000242 return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset);
Harald Welte0113fe72004-01-06 19:04:02 +0000243}
244
Harald Welteaae69be2004-08-29 23:32:14 +0000245
Harald Welte0113fe72004-01-06 19:04:02 +0000246static inline unsigned long
Harald Welteaae69be2004-08-29 23:32:14 +0000247iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
Harald Welte0113fe72004-01-06 19:04:02 +0000248{
Harald Welteaae69be2004-08-29 23:32:14 +0000249 return (void *)e - (void *)h->entries->entrytable;
Harald Welte3ea8f402003-06-23 18:25:59 +0000250}
251
252static inline unsigned int
Harald Welteaae69be2004-08-29 23:32:14 +0000253iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset)
Harald Welte3ea8f402003-06-23 18:25:59 +0000254{
Harald Welteaae69be2004-08-29 23:32:14 +0000255 return iptcb_entry2index(h, iptcb_offset2entry(h, offset));
256}
257
258/* Returns 0 if not hook entry, else hooknumber + 1 */
259static inline unsigned int
260iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
261{
262 unsigned int i;
263
264 for (i = 0; i < NUMHOOKS; i++) {
265 if ((h->info.valid_hooks & (1 << i))
266 && iptcb_get_entry(h, h->info.hook_entry[i]) == e)
267 return i+1;
268 }
269 return 0;
Harald Welte3ea8f402003-06-23 18:25:59 +0000270}
271
272
Harald Welteaae69be2004-08-29 23:32:14 +0000273/**********************************************************************
274 * iptc cache utility functions (iptcc_*)
275 **********************************************************************/
Harald Welte0113fe72004-01-06 19:04:02 +0000276
Harald Welteaae69be2004-08-29 23:32:14 +0000277/* Is the given chain builtin (1) or user-defined (0) */
278static unsigned int iptcc_is_builtin(struct chain_head *c)
279{
280 return (c->hooknum ? 1 : 0);
281}
282
283/* Get a specific rule within a chain */
284static struct rule_head *iptcc_get_rule_num(struct chain_head *c,
285 unsigned int rulenum)
286{
287 struct rule_head *r;
288 unsigned int num = 0;
289
290 list_for_each_entry(r, &c->rules, list) {
291 num++;
292 if (num == rulenum)
293 return r;
294 }
295 return NULL;
296}
297
Martin Josefssona5616dc2004-10-24 22:27:31 +0000298/* Get a specific rule within a chain backwards */
299static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c,
300 unsigned int rulenum)
301{
302 struct rule_head *r;
303 unsigned int num = 0;
304
305 list_for_each_entry_reverse(r, &c->rules, list) {
306 num++;
307 if (num == rulenum)
308 return r;
309 }
310 return NULL;
311}
312
Harald Welteaae69be2004-08-29 23:32:14 +0000313/* Returns chain head if found, otherwise NULL. */
314static struct chain_head *
315iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
316{
317 struct list_head *pos;
318
319 if (list_empty(&handle->chains))
320 return NULL;
321
322 list_for_each(pos, &handle->chains) {
323 struct chain_head *c = list_entry(pos, struct chain_head, list);
324 if (offset >= c->head_offset && offset <= c->foot_offset)
325 return c;
Harald Welte0113fe72004-01-06 19:04:02 +0000326 }
327
Harald Welteaae69be2004-08-29 23:32:14 +0000328 return NULL;
Harald Welte0113fe72004-01-06 19:04:02 +0000329}
Harald Welteaae69be2004-08-29 23:32:14 +0000330/* Returns chain head if found, otherwise NULL. */
331static struct chain_head *
332iptcc_find_label(const char *name, TC_HANDLE_T handle)
333{
334 struct list_head *pos;
335
336 if (list_empty(&handle->chains))
337 return NULL;
338
339 list_for_each(pos, &handle->chains) {
340 struct chain_head *c = list_entry(pos, struct chain_head, list);
341 if (!strcmp(c->name, name))
342 return c;
343 }
344
345 return NULL;
346}
347
348/* called when rule is to be removed from cache */
349static void iptcc_delete_rule(struct rule_head *r)
350{
351 DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
352 /* clean up reference count of called chain */
353 if (r->type == IPTCC_R_JUMP
354 && r->jump)
355 r->jump->references--;
356
357 list_del(&r->list);
358 free(r);
359}
360
361
362/**********************************************************************
363 * RULESET PARSER (blob -> cache)
364 **********************************************************************/
365
366static int alphasort(const void *a, const void *b)
367{
368 return strcmp(((struct chain_head *)a)->name,
369 ((struct chain_head *)b)->name);
370}
371
372/* Delete policy rule of previous chain, since cache doesn't contain
373 * chain policy rules.
374 * WARNING: This function has ugly design and relies on a lot of context, only
375 * to be called from specific places within the parser */
376static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
377{
378 if (h->chain_iterator_cur) {
379 /* policy rule is last rule */
380 struct rule_head *pr = (struct rule_head *)
381 h->chain_iterator_cur->rules.prev;
382
383 /* save verdict */
384 h->chain_iterator_cur->verdict =
385 *(int *)GET_TARGET(pr->entry)->data;
386
387 /* save counter and counter_map information */
388 h->chain_iterator_cur->counter_map.maptype =
389 COUNTER_MAP_NORMAL_MAP;
390 h->chain_iterator_cur->counter_map.mappos = num-1;
391 memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
392 sizeof(h->chain_iterator_cur->counters));
393
394 /* foot_offset points to verdict rule */
395 h->chain_iterator_cur->foot_index = num;
396 h->chain_iterator_cur->foot_offset = pr->offset;
397
398 /* delete rule from cache */
399 iptcc_delete_rule(pr);
Martin Josefsson8d1b38a2004-09-22 21:00:19 +0000400 h->chain_iterator_cur->num_rules--;
Harald Welteaae69be2004-08-29 23:32:14 +0000401
402 return 1;
403 }
404 return 0;
405}
406
407/* Another ugly helper function split out of cache_add_entry to make it less
408 * spaghetti code */
409static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
410 unsigned int offset, unsigned int *num)
411{
412 __iptcc_p_del_policy(h, *num);
413
414 c->head_offset = offset;
415 c->index = *num;
416
417 list_add_tail(&c->list, &h->chains);
418 h->chain_iterator_cur = c;
419}
420
421/* main parser function: add an entry from the blob to the cache */
422static int cache_add_entry(STRUCT_ENTRY *e,
423 TC_HANDLE_T h,
424 STRUCT_ENTRY **prev,
425 unsigned int *num)
426{
427 unsigned int builtin;
428 unsigned int offset = (char *)e - (char *)h->entries->entrytable;
429
430 DEBUGP("entering...");
431
432 /* Last entry ("policy rule"). End it.*/
433 if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
434 /* This is the ERROR node at the end of the chain */
435 DEBUGP_C("%u:%u: end of table:\n", *num, offset);
436
437 __iptcc_p_del_policy(h, *num);
438
439 h->chain_iterator_cur = NULL;
440 goto out_inc;
441 }
442
443 /* We know this is the start of a new chain if it's an ERROR
444 * target, or a hook entry point */
445
446 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
447 struct chain_head *c =
448 iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
449 DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
450 (char *)c->name, c);
451 if (!c) {
452 errno = -ENOMEM;
453 return -1;
454 }
455
456 __iptcc_p_add_chain(h, c, offset, num);
457
458 } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
459 struct chain_head *c =
460 iptcc_alloc_chain_head((char *)hooknames[builtin-1],
461 builtin);
462 DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
463 *num, offset, c, &c->rules);
464 if (!c) {
465 errno = -ENOMEM;
466 return -1;
467 }
468
469 c->hooknum = builtin;
470
471 __iptcc_p_add_chain(h, c, offset, num);
472
473 /* FIXME: this is ugly. */
474 goto new_rule;
475 } else {
476 /* has to be normal rule */
477 struct rule_head *r;
478new_rule:
479
480 if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
481 e->next_offset))) {
482 errno = ENOMEM;
483 return -1;
484 }
485 DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
486
487 r->index = *num;
488 r->offset = offset;
489 memcpy(r->entry, e, e->next_offset);
490 r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
491 r->counter_map.mappos = r->index;
492
493 /* handling of jumps, etc. */
494 if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
495 STRUCT_STANDARD_TARGET *t;
496
497 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
498 if (t->target.u.target_size
499 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
500 errno = EINVAL;
501 return -1;
502 }
503
504 if (t->verdict < 0) {
505 DEBUGP_C("standard, verdict=%d\n", t->verdict);
506 r->type = IPTCC_R_STANDARD;
507 } else if (t->verdict == r->offset+e->next_offset) {
508 DEBUGP_C("fallthrough\n");
509 r->type = IPTCC_R_FALLTHROUGH;
510 } else {
511 DEBUGP_C("jump, target=%u\n", t->verdict);
512 r->type = IPTCC_R_JUMP;
513 /* Jump target fixup has to be deferred
514 * until second pass, since we migh not
515 * yet have parsed the target */
516 }
Martin Josefsson52c38022004-09-22 19:39:40 +0000517 } else {
518 DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name);
519 r->type = IPTCC_R_MODULE;
Harald Welteaae69be2004-08-29 23:32:14 +0000520 }
521
522 list_add_tail(&r->list, &h->chain_iterator_cur->rules);
Martin Josefsson8d1b38a2004-09-22 21:00:19 +0000523 h->chain_iterator_cur->num_rules++;
Harald Welteaae69be2004-08-29 23:32:14 +0000524 }
525out_inc:
526 (*num)++;
527 return 0;
528}
529
530
531/* parse an iptables blob into it's pieces */
532static int parse_table(TC_HANDLE_T h)
533{
534 STRUCT_ENTRY *prev;
535 unsigned int num = 0;
536 struct chain_head *c;
537
538 /* First pass: over ruleset blob */
539 ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
540 cache_add_entry, h, &prev, &num);
541
542 /* Second pass: fixup parsed data from first pass */
543 list_for_each_entry(c, &h->chains, list) {
544 struct rule_head *r;
545 list_for_each_entry(r, &c->rules, list) {
546 struct chain_head *c;
547 STRUCT_STANDARD_TARGET *t;
548
549 if (r->type != IPTCC_R_JUMP)
550 continue;
551
552 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
553 c = iptcc_find_chain_by_offset(h, t->verdict);
554 if (!c)
555 return -1;
556 r->jump = c;
557 c->references++;
558 }
559 }
560
561 /* FIXME: sort chains */
562
563 return 1;
564}
565
566
567/**********************************************************************
568 * RULESET COMPILATION (cache -> blob)
569 **********************************************************************/
570
571/* Convenience structures */
572struct iptcb_chain_start{
573 STRUCT_ENTRY e;
574 struct ipt_error_target name;
575};
576#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
577 ALIGN(sizeof(struct ipt_error_target)))
578
579struct iptcb_chain_foot {
580 STRUCT_ENTRY e;
581 STRUCT_STANDARD_TARGET target;
582};
583#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
584 ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
585
586struct iptcb_chain_error {
587 STRUCT_ENTRY entry;
588 struct ipt_error_target target;
589};
590#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
591 ALIGN(sizeof(struct ipt_error_target)))
592
593
594
595/* compile rule from cache into blob */
596static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
597{
598 /* handle jumps */
599 if (r->type == IPTCC_R_JUMP) {
600 STRUCT_STANDARD_TARGET *t;
601 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
602 /* memset for memcmp convenience on delete/replace */
603 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
604 strcpy(t->target.u.user.name, STANDARD_TARGET);
605 /* Jumps can only happen to builtin chains, so we
606 * can safely assume that they always have a header */
607 t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
608 } else if (r->type == IPTCC_R_FALLTHROUGH) {
609 STRUCT_STANDARD_TARGET *t;
610 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
611 t->verdict = r->offset + r->size;
612 }
613
614 /* copy entry from cache to blob */
615 memcpy((char *)repl->entries+r->offset, r->entry, r->size);
616
617 return 1;
618}
619
620/* compile chain from cache into blob */
621static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
622{
623 int ret;
624 struct rule_head *r;
625 struct iptcb_chain_start *head;
626 struct iptcb_chain_foot *foot;
627
628 /* only user-defined chains have heaer */
629 if (!iptcc_is_builtin(c)) {
630 /* put chain header in place */
631 head = (void *)repl->entries + c->head_offset;
632 head->e.target_offset = sizeof(STRUCT_ENTRY);
633 head->e.next_offset = IPTCB_CHAIN_START_SIZE;
634 strcpy(head->name.t.u.user.name, ERROR_TARGET);
635 head->name.t.u.target_size =
636 ALIGN(sizeof(struct ipt_error_target));
637 strcpy(head->name.error, c->name);
638 } else {
639 repl->hook_entry[c->hooknum-1] = c->head_offset;
640 repl->underflow[c->hooknum-1] = c->foot_offset;
641 }
642
643 /* iterate over rules */
644 list_for_each_entry(r, &c->rules, list) {
645 ret = iptcc_compile_rule(h, repl, r);
646 if (ret < 0)
647 return ret;
648 }
649
650 /* put chain footer in place */
651 foot = (void *)repl->entries + c->foot_offset;
652 foot->e.target_offset = sizeof(STRUCT_ENTRY);
653 foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
654 strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
655 foot->target.target.u.target_size =
656 ALIGN(sizeof(STRUCT_STANDARD_TARGET));
657 /* builtin targets have verdict, others return */
658 if (iptcc_is_builtin(c))
659 foot->target.verdict = c->verdict;
660 else
661 foot->target.verdict = RETURN;
662 /* set policy-counters */
663 memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
664
665 return 0;
666}
667
668/* calculate offset and number for every rule in the cache */
669static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
670 int *offset, int *num)
671{
672 struct rule_head *r;
673
674 c->head_offset = *offset;
675 DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
676
677 if (!iptcc_is_builtin(c)) {
678 /* Chain has header */
679 *offset += sizeof(STRUCT_ENTRY)
680 + ALIGN(sizeof(struct ipt_error_target));
681 (*num)++;
682 }
683
684 list_for_each_entry(r, &c->rules, list) {
685 DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
686 r->offset = *offset;
687 r->index = *num;
688 *offset += r->size;
689 (*num)++;
690 }
691
692 DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
693 *offset, *num);
694 c->foot_offset = *offset;
695 c->foot_index = *num;
696 *offset += sizeof(STRUCT_ENTRY)
697 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
698 (*num)++;
699
700 return 1;
701}
702
703/* put the pieces back together again */
704static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
705{
706 struct chain_head *c;
707 unsigned int offset = 0, num = 0;
708 int ret = 0;
709
710 /* First pass: calculate offset for every rule */
711 list_for_each_entry(c, &h->chains, list) {
712 ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
713 if (ret < 0)
714 return ret;
715 }
716
717 /* Append one error rule at end of chain */
718 num++;
719 offset += sizeof(STRUCT_ENTRY)
720 + ALIGN(sizeof(struct ipt_error_target));
721
722 /* ruleset size is now in offset */
723 *size = offset;
724 return num;
725}
726
727static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
728{
729 struct chain_head *c;
730 struct iptcb_chain_error *error;
731
732 /* Second pass: copy from cache to offsets, fill in jumps */
733 list_for_each_entry(c, &h->chains, list) {
734 int ret = iptcc_compile_chain(h, repl, c);
735 if (ret < 0)
736 return ret;
737 }
738
739 /* Append error rule at end of chain */
740 error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
741 error->entry.target_offset = sizeof(STRUCT_ENTRY);
742 error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
743 error->target.t.u.user.target_size =
744 ALIGN(sizeof(struct ipt_error_target));
745 strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
746 strcpy((char *)&error->target.error, "ERROR");
747
748 return 1;
749}
750
751/**********************************************************************
752 * EXTERNAL API (operates on cache only)
753 **********************************************************************/
Marc Bouchere6869a82000-03-20 06:03:29 +0000754
755/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000756static TC_HANDLE_T
Harald Welte0113fe72004-01-06 19:04:02 +0000757alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +0000758{
759 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000760 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000761
Harald Welteaae69be2004-08-29 23:32:14 +0000762 len = sizeof(STRUCT_TC_HANDLE) + size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000763
Harald Welteaae69be2004-08-29 23:32:14 +0000764 h = malloc(sizeof(STRUCT_TC_HANDLE));
765 if (!h) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000766 errno = ENOMEM;
767 return NULL;
768 }
Harald Welteaae69be2004-08-29 23:32:14 +0000769 memset(h, 0, sizeof(*h));
770 INIT_LIST_HEAD(&h->chains);
Marc Bouchere6869a82000-03-20 06:03:29 +0000771 strcpy(h->info.name, tablename);
Harald Welteaae69be2004-08-29 23:32:14 +0000772
Harald Welte0371c0c2004-09-19 21:00:12 +0000773 h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size);
Harald Welteaae69be2004-08-29 23:32:14 +0000774 if (!h->entries)
775 goto out_free_handle;
776
777 strcpy(h->entries->name, tablename);
Harald Welte0371c0c2004-09-19 21:00:12 +0000778 h->entries->size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000779
780 return h;
Harald Welteaae69be2004-08-29 23:32:14 +0000781
782out_free_handle:
783 free(h);
784
785 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000786}
787
Harald Welteaae69be2004-08-29 23:32:14 +0000788
Rusty Russell79dee072000-05-02 16:45:16 +0000789TC_HANDLE_T
790TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000791{
Rusty Russell79dee072000-05-02 16:45:16 +0000792 TC_HANDLE_T h;
793 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +0000794 int tmp;
795 socklen_t s;
796
Rusty Russell79dee072000-05-02 16:45:16 +0000797 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000798
Martin Josefssone560fd62003-06-13 16:56:51 +0000799 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000800 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000801 sockfd = -1;
802 }
Harald Welte366454b2002-01-07 13:46:50 +0000803
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000804 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
805 errno = EINVAL;
806 return NULL;
807 }
808
Rusty Russell79dee072000-05-02 16:45:16 +0000809 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000810 if (sockfd < 0)
811 return NULL;
812
813 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000814
Marc Bouchere6869a82000-03-20 06:03:29 +0000815 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000816 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000817 return NULL;
818
Harald Welteaae69be2004-08-29 23:32:14 +0000819 DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
820 info.valid_hooks, info.num_entries, info.size);
821
Harald Welte0113fe72004-01-06 19:04:02 +0000822 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000823 == NULL) {
824 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000825 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000826 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000827 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000828
Marc Bouchere6869a82000-03-20 06:03:29 +0000829 /* Initialize current state */
830 h->info = info;
Harald Welte0113fe72004-01-06 19:04:02 +0000831
Harald Welteaae69be2004-08-29 23:32:14 +0000832 h->entries->size = h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000833
Rusty Russell79dee072000-05-02 16:45:16 +0000834 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000835
Harald Welteaae69be2004-08-29 23:32:14 +0000836 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
837 &tmp) < 0)
838 goto error;
839
840#ifdef IPTC_DEBUG2
841 {
842 int fd = open("/tmp/libiptc-so_get_entries.blob",
843 O_CREAT|O_WRONLY);
844 if (fd >= 0) {
845 write(fd, h->entries, tmp);
846 close(fd);
847 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000848 }
Harald Welteaae69be2004-08-29 23:32:14 +0000849#endif
850
851 if (parse_table(h) < 0)
852 goto error;
Rusty Russell7e53bf92000-03-20 07:03:28 +0000853
Marc Bouchere6869a82000-03-20 06:03:29 +0000854 CHECK(h);
855 return h;
Harald Welteaae69be2004-08-29 23:32:14 +0000856error:
857 TC_FREE(&h);
858 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000859}
860
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000861void
862TC_FREE(TC_HANDLE_T *h)
863{
Harald Welteaae69be2004-08-29 23:32:14 +0000864 struct chain_head *c, *tmp;
865
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000866 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000867 sockfd = -1;
Harald Welteaae69be2004-08-29 23:32:14 +0000868
869 list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
870 struct rule_head *r, *rtmp;
871
872 list_for_each_entry_safe(r, rtmp, &c->rules, list) {
873 free(r);
874 }
875
876 free(c);
877 }
878
879 free((*h)->entries);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000880 free(*h);
Harald Welteaae69be2004-08-29 23:32:14 +0000881
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000882 *h = NULL;
883}
884
Marc Bouchere6869a82000-03-20 06:03:29 +0000885static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000886print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000887{
Rusty Russell228e98d2000-04-27 10:28:06 +0000888 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000889 return 0;
890}
891
Rusty Russell79dee072000-05-02 16:45:16 +0000892static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
893
Marc Bouchere6869a82000-03-20 06:03:29 +0000894void
Rusty Russell79dee072000-05-02 16:45:16 +0000895TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000896{
897 CHECK(handle);
Harald Welteaae69be2004-08-29 23:32:14 +0000898#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000899 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000900 IPTABLES_VERSION,
Harald Welteaae69be2004-08-29 23:32:14 +0000901 handle->new_number, handle->entries->size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000902 printf("Table `%s'\n", handle->info.name);
903 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000904 handle->info.hook_entry[HOOK_PRE_ROUTING],
905 handle->info.hook_entry[HOOK_LOCAL_IN],
906 handle->info.hook_entry[HOOK_FORWARD],
907 handle->info.hook_entry[HOOK_LOCAL_OUT],
908 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000909 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000910 handle->info.underflow[HOOK_PRE_ROUTING],
911 handle->info.underflow[HOOK_LOCAL_IN],
912 handle->info.underflow[HOOK_FORWARD],
913 handle->info.underflow[HOOK_LOCAL_OUT],
914 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000915
Harald Welteaae69be2004-08-29 23:32:14 +0000916 ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
Rusty Russell79dee072000-05-02 16:45:16 +0000917 dump_entry, handle);
Harald Welteaae69be2004-08-29 23:32:14 +0000918#endif
Harald Welte0113fe72004-01-06 19:04:02 +0000919}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000920
Marc Bouchere6869a82000-03-20 06:03:29 +0000921/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000922int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000923{
Harald Welteaae69be2004-08-29 23:32:14 +0000924 return iptcc_find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000925}
926
Harald Welteaae69be2004-08-29 23:32:14 +0000927static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
928{
929 struct chain_head *c = handle->chain_iterator_cur;
930
931 if (c->list.next == &handle->chains)
932 handle->chain_iterator_cur = NULL;
933 else
934 handle->chain_iterator_cur =
935 list_entry(c->list.next, struct chain_head, list);
936}
Marc Bouchere6869a82000-03-20 06:03:29 +0000937
Rusty Russell30fd6e52000-04-23 09:16:06 +0000938/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000939const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000940TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000941{
Harald Welteaae69be2004-08-29 23:32:14 +0000942 struct chain_head *c = list_entry((*handle)->chains.next,
943 struct chain_head, list);
944
945 iptc_fn = TC_FIRST_CHAIN;
946
947
948 if (list_empty(&(*handle)->chains)) {
949 DEBUGP(": no chains\n");
Harald Welte0113fe72004-01-06 19:04:02 +0000950 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +0000951 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000952
Harald Welteaae69be2004-08-29 23:32:14 +0000953 (*handle)->chain_iterator_cur = c;
954 iptcc_chain_iterator_advance(*handle);
Harald Welte0113fe72004-01-06 19:04:02 +0000955
Harald Welteaae69be2004-08-29 23:32:14 +0000956 DEBUGP(": returning `%s'\n", c->name);
957 return c->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000958}
959
Rusty Russell30fd6e52000-04-23 09:16:06 +0000960/* Iterator functions to run through the chains. Returns NULL at end. */
961const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000962TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000963{
Harald Welteaae69be2004-08-29 23:32:14 +0000964 struct chain_head *c = (*handle)->chain_iterator_cur;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000965
Harald Welteaae69be2004-08-29 23:32:14 +0000966 iptc_fn = TC_NEXT_CHAIN;
967
968 if (!c) {
969 DEBUGP(": no more chains\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +0000970 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +0000971 }
Rusty Russell30fd6e52000-04-23 09:16:06 +0000972
Harald Welteaae69be2004-08-29 23:32:14 +0000973 iptcc_chain_iterator_advance(*handle);
974
975 DEBUGP(": returning `%s'\n", c->name);
976 return c->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000977}
978
979/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000980const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000981TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000982{
Harald Welteaae69be2004-08-29 23:32:14 +0000983 struct chain_head *c;
984 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000985
Harald Welteaae69be2004-08-29 23:32:14 +0000986 iptc_fn = TC_FIRST_RULE;
987
988 DEBUGP("first rule(%s): ", chain);
989
990 c = iptcc_find_label(chain, *handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000991 if (!c) {
992 errno = ENOENT;
993 return NULL;
994 }
995
996 /* Empty chain: single return/policy rule */
Harald Welteaae69be2004-08-29 23:32:14 +0000997 if (list_empty(&c->rules)) {
998 DEBUGP_C("no rules, returning NULL\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +0000999 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +00001000 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001001
Harald Welteaae69be2004-08-29 23:32:14 +00001002 r = list_entry(c->rules.next, struct rule_head, list);
1003 (*handle)->rule_iterator_cur = r;
1004 DEBUGP_C("%p\n", r);
1005
1006 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001007}
1008
1009/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +00001010const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +00001011TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +00001012{
Harald Welteaae69be2004-08-29 23:32:14 +00001013 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001014
Harald Welteaae69be2004-08-29 23:32:14 +00001015 DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
1016
1017 if (!(*handle)->rule_iterator_cur) {
1018 DEBUGP_C("returning NULL\n");
1019 return NULL;
1020 }
1021
1022 r = list_entry((*handle)->rule_iterator_cur->list.next,
1023 struct rule_head, list);
1024
1025 iptc_fn = TC_NEXT_RULE;
1026
1027 DEBUGP_C("next=%p, head=%p...", &r->list,
1028 &(*handle)->rule_iterator_cur->chain->rules);
1029
1030 if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
1031 (*handle)->rule_iterator_cur = NULL;
1032 DEBUGP_C("finished, returning NULL\n");
1033 return NULL;
1034 }
1035
1036 (*handle)->rule_iterator_cur = r;
1037
1038 /* NOTE: prev is without any influence ! */
1039 DEBUGP_C("returning rule %p\n", r);
1040 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001041}
1042
Marc Bouchere6869a82000-03-20 06:03:29 +00001043/* How many rules in this chain? */
1044unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +00001045TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001046{
Harald Welteaae69be2004-08-29 23:32:14 +00001047 struct chain_head *c;
1048 iptc_fn = TC_NUM_RULES;
Marc Bouchere6869a82000-03-20 06:03:29 +00001049 CHECK(*handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001050
1051 c = iptcc_find_label(chain, *handle);
1052 if (!c) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001053 errno = ENOENT;
1054 return (unsigned int)-1;
1055 }
Harald Welteaae69be2004-08-29 23:32:14 +00001056
1057 return c->num_rules;
Marc Bouchere6869a82000-03-20 06:03:29 +00001058}
1059
Rusty Russell79dee072000-05-02 16:45:16 +00001060const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
1061 unsigned int n,
1062 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001063{
Harald Welteaae69be2004-08-29 23:32:14 +00001064 struct chain_head *c;
1065 struct rule_head *r;
1066
1067 iptc_fn = TC_GET_RULE;
Marc Bouchere6869a82000-03-20 06:03:29 +00001068
1069 CHECK(*handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001070
1071 c = iptcc_find_label(chain, *handle);
1072 if (!c) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001073 errno = ENOENT;
1074 return NULL;
1075 }
1076
Harald Welteaae69be2004-08-29 23:32:14 +00001077 r = iptcc_get_rule_num(c, n);
1078 if (!r)
1079 return NULL;
1080 return r->entry;
Marc Bouchere6869a82000-03-20 06:03:29 +00001081}
1082
1083/* Returns a pointer to the target name of this position. */
Harald Welteaae69be2004-08-29 23:32:14 +00001084const char *standard_target_map(int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +00001085{
Harald Welteaae69be2004-08-29 23:32:14 +00001086 switch (verdict) {
1087 case RETURN:
1088 return LABEL_RETURN;
1089 break;
1090 case -NF_ACCEPT-1:
1091 return LABEL_ACCEPT;
1092 break;
1093 case -NF_DROP-1:
1094 return LABEL_DROP;
1095 break;
1096 case -NF_QUEUE-1:
1097 return LABEL_QUEUE;
1098 break;
1099 default:
1100 fprintf(stderr, "ERROR: %d not a valid target)\n",
1101 verdict);
1102 abort();
1103 break;
1104 }
1105 /* not reached */
1106 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001107}
1108
Harald Welteaae69be2004-08-29 23:32:14 +00001109/* Returns a pointer to the target name of this position. */
1110const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
1111 TC_HANDLE_T *handle)
1112{
1113 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
1114 struct rule_head *r = container_of(e, struct rule_head, entry);
1115
1116 iptc_fn = TC_GET_TARGET;
1117
1118 switch(r->type) {
1119 int spos;
1120 case IPTCC_R_FALLTHROUGH:
1121 return "";
1122 break;
1123 case IPTCC_R_JUMP:
1124 DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
1125 return r->jump->name;
1126 break;
1127 case IPTCC_R_STANDARD:
1128 spos = *(int *)GET_TARGET(e)->data;
1129 DEBUGP("r=%p, spos=%d'\n", r, spos);
1130 return standard_target_map(spos);
1131 break;
1132 case IPTCC_R_MODULE:
1133 return GET_TARGET(e)->u.user.name;
1134 break;
1135 }
1136 return NULL;
1137}
Marc Bouchere6869a82000-03-20 06:03:29 +00001138/* Is this a built-in chain? Actually returns hook + 1. */
1139int
Rusty Russell79dee072000-05-02 16:45:16 +00001140TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001141{
Harald Welteaae69be2004-08-29 23:32:14 +00001142 struct chain_head *c;
1143
1144 iptc_fn = TC_BUILTIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001145
Harald Welteaae69be2004-08-29 23:32:14 +00001146 c = iptcc_find_label(chain, handle);
1147 if (!c) {
1148 errno = ENOENT;
Martin Josefssonb0f3d2d2004-09-23 18:23:20 +00001149 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001150 }
Harald Welteaae69be2004-08-29 23:32:14 +00001151
1152 return iptcc_is_builtin(c);
Marc Bouchere6869a82000-03-20 06:03:29 +00001153}
1154
1155/* Get the policy of a given built-in chain */
1156const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001157TC_GET_POLICY(const char *chain,
1158 STRUCT_COUNTERS *counters,
1159 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001160{
Harald Welteaae69be2004-08-29 23:32:14 +00001161 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001162
Harald Welteaae69be2004-08-29 23:32:14 +00001163 iptc_fn = TC_GET_POLICY;
1164
1165 DEBUGP("called for chain %s\n", chain);
1166
1167 c = iptcc_find_label(chain, *handle);
1168 if (!c) {
1169 errno = ENOENT;
1170 return NULL;
1171 }
1172
1173 if (!iptcc_is_builtin(c))
Marc Bouchere6869a82000-03-20 06:03:29 +00001174 return NULL;
1175
Harald Welteaae69be2004-08-29 23:32:14 +00001176 *counters = c->counters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001177
Harald Welteaae69be2004-08-29 23:32:14 +00001178 return standard_target_map(c->verdict);
Harald Welte0113fe72004-01-06 19:04:02 +00001179}
1180
1181static int
Harald Welteaae69be2004-08-29 23:32:14 +00001182iptcc_standard_map(struct rule_head *r, int verdict)
Harald Welte0113fe72004-01-06 19:04:02 +00001183{
Harald Welteaae69be2004-08-29 23:32:14 +00001184 STRUCT_ENTRY *e = r->entry;
Rusty Russell79dee072000-05-02 16:45:16 +00001185 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001186
Rusty Russell79dee072000-05-02 16:45:16 +00001187 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001188
Rusty Russell67088e72000-05-10 01:18:57 +00001189 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001190 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001191 errno = EINVAL;
1192 return 0;
1193 }
1194 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +00001195 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
1196 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +00001197 t->verdict = verdict;
1198
Harald Welteaae69be2004-08-29 23:32:14 +00001199 r->type = IPTCC_R_STANDARD;
1200
Marc Bouchere6869a82000-03-20 06:03:29 +00001201 return 1;
1202}
Rusty Russell7e53bf92000-03-20 07:03:28 +00001203
Marc Bouchere6869a82000-03-20 06:03:29 +00001204static int
Harald Welteaae69be2004-08-29 23:32:14 +00001205iptcc_map_target(const TC_HANDLE_T handle,
1206 struct rule_head *r)
Marc Bouchere6869a82000-03-20 06:03:29 +00001207{
Harald Welteaae69be2004-08-29 23:32:14 +00001208 STRUCT_ENTRY *e = r->entry;
Harald Welte0113fe72004-01-06 19:04:02 +00001209 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001210
Marc Bouchere6869a82000-03-20 06:03:29 +00001211 /* Maybe it's empty (=> fall through) */
Harald Welteaae69be2004-08-29 23:32:14 +00001212 if (strcmp(t->u.user.name, "") == 0) {
1213 r->type = IPTCC_R_FALLTHROUGH;
1214 return 1;
1215 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001216 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +00001217 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001218 return iptcc_standard_map(r, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001219 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001220 return iptcc_standard_map(r, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +00001221 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001222 return iptcc_standard_map(r, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001223 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001224 return iptcc_standard_map(r, RETURN);
Rusty Russell79dee072000-05-02 16:45:16 +00001225 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001226 /* Can't jump to builtins. */
1227 errno = EINVAL;
1228 return 0;
1229 } else {
1230 /* Maybe it's an existing chain name. */
Harald Welteaae69be2004-08-29 23:32:14 +00001231 struct chain_head *c;
1232 DEBUGP("trying to find chain `%s': ", t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +00001233
Harald Welteaae69be2004-08-29 23:32:14 +00001234 c = iptcc_find_label(t->u.user.name, handle);
1235 if (c) {
1236 DEBUGP_C("found!\n");
1237 r->type = IPTCC_R_JUMP;
1238 r->jump = c;
1239 c->references++;
1240 return 1;
1241 }
1242 DEBUGP_C("not found :(\n");
Marc Bouchere6869a82000-03-20 06:03:29 +00001243 }
1244
1245 /* Must be a module? If not, kernel will reject... */
1246 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001247 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001248 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001249 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Harald Welteaae69be2004-08-29 23:32:14 +00001250
1251 set_changed(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001252 return 1;
1253}
1254
Harald Welte0113fe72004-01-06 19:04:02 +00001255/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001256int
Rusty Russell79dee072000-05-02 16:45:16 +00001257TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1258 const STRUCT_ENTRY *e,
1259 unsigned int rulenum,
1260 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001261{
Harald Welteaae69be2004-08-29 23:32:14 +00001262 struct chain_head *c;
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001263 struct rule_head *r;
1264 struct list_head *prev;
Marc Bouchere6869a82000-03-20 06:03:29 +00001265
Rusty Russell79dee072000-05-02 16:45:16 +00001266 iptc_fn = TC_INSERT_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001267
1268 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001269 errno = ENOENT;
1270 return 0;
1271 }
1272
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001273 /* first rulenum index = 0
1274 first c->num_rules index = 1 */
1275 if (rulenum > c->num_rules) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001276 errno = E2BIG;
1277 return 0;
1278 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001279
Martin Josefsson631f3612004-09-23 19:25:06 +00001280 /* If we are inserting at the end just take advantage of the
1281 double linked list, insert will happen before the entry
1282 prev points to. */
1283 if (rulenum == c->num_rules) {
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001284 prev = &c->rules;
Martin Josefssona5616dc2004-10-24 22:27:31 +00001285 } else if (rulenum + 1 <= c->num_rules/2) {
Martin Josefsson631f3612004-09-23 19:25:06 +00001286 r = iptcc_get_rule_num(c, rulenum + 1);
Martin Josefssona5616dc2004-10-24 22:27:31 +00001287 prev = &r->list;
1288 } else {
1289 r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
Martin Josefsson631f3612004-09-23 19:25:06 +00001290 prev = &r->list;
1291 }
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001292
Harald Welteaae69be2004-08-29 23:32:14 +00001293 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1294 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001295 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001296 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001297
Harald Welteaae69be2004-08-29 23:32:14 +00001298 memcpy(r->entry, e, e->next_offset);
1299 r->counter_map.maptype = COUNTER_MAP_SET;
1300
1301 if (!iptcc_map_target(*handle, r)) {
1302 free(r);
1303 return 0;
1304 }
1305
Martin Josefssoneb066cc2004-09-22 21:04:07 +00001306 list_add_tail(&r->list, prev);
Harald Welteaae69be2004-08-29 23:32:14 +00001307 c->num_rules++;
1308
1309 set_changed(*handle);
1310
1311 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001312}
1313
1314/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1315int
Rusty Russell79dee072000-05-02 16:45:16 +00001316TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1317 const STRUCT_ENTRY *e,
1318 unsigned int rulenum,
1319 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001320{
Harald Welteaae69be2004-08-29 23:32:14 +00001321 struct chain_head *c;
1322 struct rule_head *r, *old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001323
Rusty Russell79dee072000-05-02 16:45:16 +00001324 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001325
Harald Welteaae69be2004-08-29 23:32:14 +00001326 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001327 errno = ENOENT;
1328 return 0;
1329 }
1330
Martin Josefsson8e795b02004-09-22 21:31:09 +00001331 if (!(old = iptcc_get_rule_num(c, rulenum + 1))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001332 errno = E2BIG;
1333 return 0;
1334 }
1335
Harald Welteaae69be2004-08-29 23:32:14 +00001336 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1337 errno = ENOMEM;
Marc Bouchere6869a82000-03-20 06:03:29 +00001338 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001339 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001340
Harald Welteaae69be2004-08-29 23:32:14 +00001341 memcpy(r->entry, e, e->next_offset);
1342 r->counter_map.maptype = COUNTER_MAP_SET;
1343
1344 if (!iptcc_map_target(*handle, r)) {
1345 free(r);
Harald Welte0113fe72004-01-06 19:04:02 +00001346 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001347 }
Harald Welte0113fe72004-01-06 19:04:02 +00001348
Harald Welteaae69be2004-08-29 23:32:14 +00001349 list_add(&r->list, &old->list);
1350 iptcc_delete_rule(old);
1351
1352 set_changed(*handle);
1353
1354 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001355}
1356
Harald Welte0113fe72004-01-06 19:04:02 +00001357/* Append entry `fw' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001358 rulenum = length of chain. */
1359int
Rusty Russell79dee072000-05-02 16:45:16 +00001360TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1361 const STRUCT_ENTRY *e,
1362 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001363{
Harald Welteaae69be2004-08-29 23:32:14 +00001364 struct chain_head *c;
1365 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00001366
Rusty Russell79dee072000-05-02 16:45:16 +00001367 iptc_fn = TC_APPEND_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001368 if (!(c = iptcc_find_label(chain, *handle))) {
1369 DEBUGP("unable to find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001370 errno = ENOENT;
1371 return 0;
1372 }
1373
Harald Welteaae69be2004-08-29 23:32:14 +00001374 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1375 DEBUGP("unable to allocate rule for chain `%s'\n", chain);
1376 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001377 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001378 }
Harald Welte0113fe72004-01-06 19:04:02 +00001379
Harald Welteaae69be2004-08-29 23:32:14 +00001380 memcpy(r->entry, e, e->next_offset);
1381 r->counter_map.maptype = COUNTER_MAP_SET;
1382
1383 if (!iptcc_map_target(*handle, r)) {
Martin Josefsson12009532004-09-23 18:24:29 +00001384 DEBUGP("unable to map target of rule for chain `%s'\n", chain);
Harald Welteaae69be2004-08-29 23:32:14 +00001385 free(r);
1386 return 0;
1387 }
1388
1389 list_add_tail(&r->list, &c->rules);
1390 c->num_rules++;
1391
1392 set_changed(*handle);
1393
1394 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001395}
1396
1397static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001398match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001399 const unsigned char *a_elems,
1400 const unsigned char *b_elems,
1401 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001402{
Rusty Russell79dee072000-05-02 16:45:16 +00001403 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001404 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001405
1406 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001407 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001408
Rusty Russell228e98d2000-04-27 10:28:06 +00001409 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001410 return 1;
1411
Rusty Russell228e98d2000-04-27 10:28:06 +00001412 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001413 return 1;
1414
Rusty Russell73ef09b2000-07-03 10:24:04 +00001415 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001416
Rusty Russell73ef09b2000-07-03 10:24:04 +00001417 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001418 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001419 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001420 *maskptr += i;
1421 return 0;
1422}
1423
1424static inline int
1425target_different(const unsigned char *a_targdata,
1426 const unsigned char *b_targdata,
1427 unsigned int tdatasize,
1428 const unsigned char *mask)
1429{
1430 unsigned int i;
1431 for (i = 0; i < tdatasize; i++)
1432 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1433 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001434
1435 return 0;
1436}
1437
Rusty Russell79dee072000-05-02 16:45:16 +00001438static int
1439is_same(const STRUCT_ENTRY *a,
1440 const STRUCT_ENTRY *b,
1441 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001442
Harald Welte0113fe72004-01-06 19:04:02 +00001443/* Delete the first rule in `chain' which matches `fw'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001444int
Rusty Russell79dee072000-05-02 16:45:16 +00001445TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1446 const STRUCT_ENTRY *origfw,
1447 unsigned char *matchmask,
1448 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001449{
Harald Welteaae69be2004-08-29 23:32:14 +00001450 struct chain_head *c;
Harald Weltefe537072004-08-30 20:28:53 +00001451 struct rule_head *r;
Harald Welte0113fe72004-01-06 19:04:02 +00001452 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001453
Rusty Russell79dee072000-05-02 16:45:16 +00001454 iptc_fn = TC_DELETE_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001455 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001456 errno = ENOENT;
1457 return 0;
1458 }
1459
Harald Welte0113fe72004-01-06 19:04:02 +00001460 fw = malloc(origfw->next_offset);
1461 if (fw == NULL) {
1462 errno = ENOMEM;
1463 return 0;
1464 }
1465
Harald Weltefe537072004-08-30 20:28:53 +00001466 list_for_each_entry(r, &c->rules, list) {
Harald Welte0113fe72004-01-06 19:04:02 +00001467
1468 memcpy(fw, origfw, origfw->next_offset);
1469
Harald Weltefe537072004-08-30 20:28:53 +00001470#if 0
Harald Welte0113fe72004-01-06 19:04:02 +00001471 /* FIXME: handle this in is_same --RR */
1472 if (!map_target(*handle, fw, offset, &discard)) {
1473 free(fw);
1474 return 0;
1475 }
Harald Welte0113fe72004-01-06 19:04:02 +00001476#endif
Harald Weltefe537072004-08-30 20:28:53 +00001477
1478 e = r->entry;
1479
Harald Welte0113fe72004-01-06 19:04:02 +00001480 if (is_same(e, fw, matchmask)) {
Harald Weltefe537072004-08-30 20:28:53 +00001481 /* If we are about to delete the rule that is the
1482 * current iterator, move rule iterator back. next
1483 * pointer will then point to real next node */
1484 if (r == (*handle)->rule_iterator_cur) {
1485 (*handle)->rule_iterator_cur =
1486 list_entry((*handle)->rule_iterator_cur->list.prev,
1487 struct rule_head, list);
1488 }
1489
1490 c->num_rules--;
1491 iptcc_delete_rule(r);
Martin Josefsson2a5dbbb2004-09-22 21:37:41 +00001492
1493 set_changed(*handle);
Harald Weltefe537072004-08-30 20:28:53 +00001494 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001495 }
1496 }
1497
Harald Welte0113fe72004-01-06 19:04:02 +00001498 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001499 errno = ENOENT;
1500 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001501}
Harald Welteaae69be2004-08-29 23:32:14 +00001502
Marc Bouchere6869a82000-03-20 06:03:29 +00001503
1504/* Delete the rule in position `rulenum' in `chain'. */
1505int
Rusty Russell79dee072000-05-02 16:45:16 +00001506TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1507 unsigned int rulenum,
1508 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001509{
Harald Welteaae69be2004-08-29 23:32:14 +00001510 struct chain_head *c;
1511 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00001512
Rusty Russell79dee072000-05-02 16:45:16 +00001513 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001514
1515 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001516 errno = ENOENT;
1517 return 0;
1518 }
1519
Martin Josefssona5616dc2004-10-24 22:27:31 +00001520 if (rulenum >= c->num_rules) {
Martin Josefsson631f3612004-09-23 19:25:06 +00001521 errno = E2BIG;
1522 return 0;
1523 }
1524
1525 /* Take advantage of the double linked list if possible. */
Martin Josefssona5616dc2004-10-24 22:27:31 +00001526 if (rulenum + 1 <= c->num_rules/2) {
1527 r = iptcc_get_rule_num(c, rulenum + 1);
1528 } else {
1529 r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum);
Marc Bouchere6869a82000-03-20 06:03:29 +00001530 }
1531
Harald Welteaae69be2004-08-29 23:32:14 +00001532 /* If we are about to delete the rule that is the current
1533 * iterator, move rule iterator back. next pointer will then
1534 * point to real next node */
1535 if (r == (*handle)->rule_iterator_cur) {
1536 (*handle)->rule_iterator_cur =
1537 list_entry((*handle)->rule_iterator_cur->list.prev,
1538 struct rule_head, list);
Harald Welte0113fe72004-01-06 19:04:02 +00001539 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001540
Harald Welteaae69be2004-08-29 23:32:14 +00001541 c->num_rules--;
1542 iptcc_delete_rule(r);
1543
Martin Josefsson2a5dbbb2004-09-22 21:37:41 +00001544 set_changed(*handle);
1545
Harald Welteaae69be2004-08-29 23:32:14 +00001546 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001547}
1548
1549/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1550 NULL and sets errno. */
1551const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001552TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1553 STRUCT_ENTRY *entry,
1554 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001555{
1556 errno = ENOSYS;
1557 return NULL;
1558}
1559
1560/* Flushes the entries in the given chain (ie. empties chain). */
1561int
Rusty Russell79dee072000-05-02 16:45:16 +00001562TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001563{
Harald Welteaae69be2004-08-29 23:32:14 +00001564 struct chain_head *c;
1565 struct rule_head *r, *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001566
Harald Welte0113fe72004-01-06 19:04:02 +00001567 iptc_fn = TC_FLUSH_ENTRIES;
Harald Welteaae69be2004-08-29 23:32:14 +00001568 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001569 errno = ENOENT;
1570 return 0;
1571 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001572
Harald Welteaae69be2004-08-29 23:32:14 +00001573 list_for_each_entry_safe(r, tmp, &c->rules, list) {
1574 iptcc_delete_rule(r);
1575 }
1576
1577 c->num_rules = 0;
1578
1579 set_changed(*handle);
1580
1581 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001582}
1583
1584/* Zeroes the counters in a chain. */
1585int
Rusty Russell79dee072000-05-02 16:45:16 +00001586TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001587{
Harald Welteaae69be2004-08-29 23:32:14 +00001588 struct chain_head *c;
1589 struct rule_head *r;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001590
Harald Welteaae69be2004-08-29 23:32:14 +00001591 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001592 errno = ENOENT;
1593 return 0;
1594 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001595
Harald Welteaae69be2004-08-29 23:32:14 +00001596 list_for_each_entry(r, &c->rules, list) {
1597 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1598 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00001599 }
Harald Welteaae69be2004-08-29 23:32:14 +00001600
Rusty Russell175f6412000-03-24 09:32:20 +00001601 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001602
Marc Bouchere6869a82000-03-20 06:03:29 +00001603 return 1;
1604}
1605
Harald Welte1cef74d2001-01-05 15:22:59 +00001606STRUCT_COUNTERS *
1607TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1608 unsigned int rulenum,
1609 TC_HANDLE_T *handle)
1610{
Harald Welteaae69be2004-08-29 23:32:14 +00001611 struct chain_head *c;
1612 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001613
1614 iptc_fn = TC_READ_COUNTER;
1615 CHECK(*handle);
1616
Harald Welteaae69be2004-08-29 23:32:14 +00001617 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001618 errno = ENOENT;
1619 return NULL;
1620 }
1621
Harald Welteaae69be2004-08-29 23:32:14 +00001622 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001623 errno = E2BIG;
1624 return NULL;
1625 }
1626
Harald Welteaae69be2004-08-29 23:32:14 +00001627 return &r->entry[0].counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00001628}
1629
1630int
1631TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1632 unsigned int rulenum,
1633 TC_HANDLE_T *handle)
1634{
Harald Welteaae69be2004-08-29 23:32:14 +00001635 struct chain_head *c;
1636 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001637
1638 iptc_fn = TC_ZERO_COUNTER;
1639 CHECK(*handle);
1640
Harald Welteaae69be2004-08-29 23:32:14 +00001641 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001642 errno = ENOENT;
1643 return 0;
1644 }
1645
Harald Welteaae69be2004-08-29 23:32:14 +00001646 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001647 errno = E2BIG;
1648 return 0;
1649 }
1650
Harald Welteaae69be2004-08-29 23:32:14 +00001651 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1652 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Harald Welte1cef74d2001-01-05 15:22:59 +00001653
1654 set_changed(*handle);
1655
1656 return 1;
1657}
1658
1659int
1660TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1661 unsigned int rulenum,
1662 STRUCT_COUNTERS *counters,
1663 TC_HANDLE_T *handle)
1664{
Harald Welteaae69be2004-08-29 23:32:14 +00001665 struct chain_head *c;
1666 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001667 STRUCT_ENTRY *e;
Harald Welte1cef74d2001-01-05 15:22:59 +00001668
1669 iptc_fn = TC_SET_COUNTER;
1670 CHECK(*handle);
1671
Harald Welteaae69be2004-08-29 23:32:14 +00001672 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001673 errno = ENOENT;
1674 return 0;
1675 }
Harald Welte0113fe72004-01-06 19:04:02 +00001676
Harald Welteaae69be2004-08-29 23:32:14 +00001677 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001678 errno = E2BIG;
1679 return 0;
1680 }
1681
Harald Welteaae69be2004-08-29 23:32:14 +00001682 e = r->entry;
1683 r->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte0113fe72004-01-06 19:04:02 +00001684
1685 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00001686
1687 set_changed(*handle);
1688
1689 return 1;
1690}
1691
Marc Bouchere6869a82000-03-20 06:03:29 +00001692/* Creates a new chain. */
1693/* To create a chain, create two rules: error node and unconditional
1694 * return. */
1695int
Rusty Russell79dee072000-05-02 16:45:16 +00001696TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001697{
Harald Welteaae69be2004-08-29 23:32:14 +00001698 static struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001699
Rusty Russell79dee072000-05-02 16:45:16 +00001700 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001701
1702 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1703 QUEUE, RETURN. */
Harald Welteaae69be2004-08-29 23:32:14 +00001704 if (iptcc_find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001705 || strcmp(chain, LABEL_DROP) == 0
1706 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001707 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001708 || strcmp(chain, LABEL_RETURN) == 0) {
Harald Welteaae69be2004-08-29 23:32:14 +00001709 DEBUGP("Chain `%s' already exists\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001710 errno = EEXIST;
1711 return 0;
1712 }
1713
Rusty Russell79dee072000-05-02 16:45:16 +00001714 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Harald Welteaae69be2004-08-29 23:32:14 +00001715 DEBUGP("Chain name `%s' too long\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001716 errno = EINVAL;
1717 return 0;
1718 }
1719
Harald Welteaae69be2004-08-29 23:32:14 +00001720 c = iptcc_alloc_chain_head(chain, 0);
1721 if (!c) {
1722 DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
1723 errno = ENOMEM;
1724 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001725
Harald Welteaae69be2004-08-29 23:32:14 +00001726 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001727
Harald Welteaae69be2004-08-29 23:32:14 +00001728 DEBUGP("Creating chain `%s'\n", chain);
1729 list_add_tail(&c->list, &(*handle)->chains);
Harald Welte0113fe72004-01-06 19:04:02 +00001730
1731 set_changed(*handle);
1732
Harald Welteaae69be2004-08-29 23:32:14 +00001733 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001734}
1735
1736/* Get the number of references to this chain. */
1737int
Rusty Russell79dee072000-05-02 16:45:16 +00001738TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1739 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001740{
Harald Welteaae69be2004-08-29 23:32:14 +00001741 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001742
Harald Welteaae69be2004-08-29 23:32:14 +00001743 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001744 errno = ENOENT;
1745 return 0;
1746 }
1747
Harald Welteaae69be2004-08-29 23:32:14 +00001748 *ref = c->references;
1749
Marc Bouchere6869a82000-03-20 06:03:29 +00001750 return 1;
1751}
1752
1753/* Deletes a chain. */
1754int
Rusty Russell79dee072000-05-02 16:45:16 +00001755TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001756{
Marc Bouchere6869a82000-03-20 06:03:29 +00001757 unsigned int references;
Harald Welteaae69be2004-08-29 23:32:14 +00001758 struct chain_head *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001759
Rusty Russell79dee072000-05-02 16:45:16 +00001760 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001761
Harald Welteaae69be2004-08-29 23:32:14 +00001762 if (!(c = iptcc_find_label(chain, *handle))) {
1763 DEBUGP("cannot find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001764 errno = ENOENT;
1765 return 0;
1766 }
1767
Harald Welteaae69be2004-08-29 23:32:14 +00001768 if (TC_BUILTIN(chain, *handle)) {
1769 DEBUGP("cannot remove builtin chain `%s'\n", chain);
1770 errno = EINVAL;
1771 return 0;
1772 }
1773
1774 if (!TC_GET_REFERENCES(&references, chain, handle)) {
1775 DEBUGP("cannot get references on chain `%s'\n", chain);
1776 return 0;
1777 }
1778
1779 if (references > 0) {
1780 DEBUGP("chain `%s' still has references\n", chain);
1781 errno = EMLINK;
1782 return 0;
1783 }
1784
1785 if (c->num_rules) {
1786 DEBUGP("chain `%s' is not empty\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001787 errno = ENOTEMPTY;
1788 return 0;
1789 }
1790
Harald Welteaae69be2004-08-29 23:32:14 +00001791 /* If we are about to delete the chain that is the current
1792 * iterator, move chain iterator firward. */
1793 if (c == (*handle)->chain_iterator_cur)
1794 iptcc_chain_iterator_advance(*handle);
Harald Welte0113fe72004-01-06 19:04:02 +00001795
Harald Welteaae69be2004-08-29 23:32:14 +00001796 list_del(&c->list);
1797 free(c);
Harald Welte0113fe72004-01-06 19:04:02 +00001798
Harald Welteaae69be2004-08-29 23:32:14 +00001799 DEBUGP("chain `%s' deleted\n", chain);
1800
1801 set_changed(*handle);
1802
1803 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001804}
1805
1806/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001807int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1808 const IPT_CHAINLABEL newname,
1809 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001810{
Harald Welteaae69be2004-08-29 23:32:14 +00001811 struct chain_head *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001812 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001813
Harald Welte1de80462000-10-30 12:00:27 +00001814 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1815 QUEUE, RETURN. */
Harald Welteaae69be2004-08-29 23:32:14 +00001816 if (iptcc_find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001817 || strcmp(newname, LABEL_DROP) == 0
1818 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001819 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001820 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001821 errno = EEXIST;
1822 return 0;
1823 }
1824
Harald Welteaae69be2004-08-29 23:32:14 +00001825 if (!(c = iptcc_find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001826 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001827 errno = ENOENT;
1828 return 0;
1829 }
1830
Rusty Russell79dee072000-05-02 16:45:16 +00001831 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001832 errno = EINVAL;
1833 return 0;
1834 }
1835
Harald Welteaae69be2004-08-29 23:32:14 +00001836 strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
1837
Harald Welte0113fe72004-01-06 19:04:02 +00001838 set_changed(*handle);
1839
Marc Bouchere6869a82000-03-20 06:03:29 +00001840 return 1;
1841}
1842
1843/* Sets the policy on a built-in chain. */
1844int
Rusty Russell79dee072000-05-02 16:45:16 +00001845TC_SET_POLICY(const IPT_CHAINLABEL chain,
1846 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001847 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001848 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001849{
Harald Welteaae69be2004-08-29 23:32:14 +00001850 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001851
Rusty Russell79dee072000-05-02 16:45:16 +00001852 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001853
Harald Welteaae69be2004-08-29 23:32:14 +00001854 if (!(c = iptcc_find_label(chain, *handle))) {
1855 DEBUGP("cannot find chain `%s'\n", chain);
1856 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001857 return 0;
1858 }
1859
Harald Welteaae69be2004-08-29 23:32:14 +00001860 if (!iptcc_is_builtin(c)) {
1861 DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
1862 errno = ENOENT;
1863 return 0;
1864 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001865
Rusty Russell79dee072000-05-02 16:45:16 +00001866 if (strcmp(policy, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001867 c->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001868 else if (strcmp(policy, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001869 c->verdict = -NF_DROP - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001870 else {
1871 errno = EINVAL;
1872 return 0;
1873 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001874
Harald Welte1cef74d2001-01-05 15:22:59 +00001875 if (counters) {
1876 /* set byte and packet counters */
Harald Welteaae69be2004-08-29 23:32:14 +00001877 memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
1878 c->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00001879 } else {
Harald Welteaae69be2004-08-29 23:32:14 +00001880 c->counter_map.maptype = COUNTER_MAP_NOMAP;
Harald Welte1cef74d2001-01-05 15:22:59 +00001881 }
1882
Rusty Russell175f6412000-03-24 09:32:20 +00001883 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001884
Marc Bouchere6869a82000-03-20 06:03:29 +00001885 return 1;
1886}
1887
1888/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001889 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001890 libiptc.c:833: fixed or forbidden register was spilled.
1891 This may be due to a compiler bug or to impossible asm
1892 statements or clauses.
1893*/
1894static void
Rusty Russell79dee072000-05-02 16:45:16 +00001895subtract_counters(STRUCT_COUNTERS *answer,
1896 const STRUCT_COUNTERS *a,
1897 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001898{
1899 answer->pcnt = a->pcnt - b->pcnt;
1900 answer->bcnt = a->bcnt - b->bcnt;
1901}
1902
Harald Welteaae69be2004-08-29 23:32:14 +00001903
1904static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
1905 unsigned int index)
1906{
1907 newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
1908 DEBUGP_C("NOMAP => zero\n");
1909}
1910
1911static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
1912 STRUCT_REPLACE *repl,
1913 unsigned int index,
1914 unsigned int mappos)
1915{
1916 /* Original read: X.
1917 * Atomic read on replacement: X + Y.
1918 * Currently in kernel: Z.
1919 * Want in kernel: X + Y + Z.
1920 * => Add in X + Y
1921 * => Add in replacement read.
1922 */
1923 newcounters->counters[index] = repl->counters[mappos];
1924 DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
1925}
1926
1927static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
1928 STRUCT_REPLACE *repl,
1929 unsigned int index,
1930 unsigned int mappos,
1931 STRUCT_COUNTERS *counters)
1932{
1933 /* Original read: X.
1934 * Atomic read on replacement: X + Y.
1935 * Currently in kernel: Z.
1936 * Want in kernel: Y + Z.
1937 * => Add in Y.
1938 * => Add in (replacement read - original read).
1939 */
1940 subtract_counters(&newcounters->counters[index],
1941 &repl->counters[mappos],
1942 counters);
1943 DEBUGP_C("ZEROED => mappos %u\n", mappos);
1944}
1945
1946static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
1947 unsigned int index,
1948 STRUCT_COUNTERS *counters)
1949{
1950 /* Want to set counter (iptables-restore) */
1951
1952 memcpy(&newcounters->counters[index], counters,
1953 sizeof(STRUCT_COUNTERS));
1954
1955 DEBUGP_C("SET\n");
1956}
1957
1958
Marc Bouchere6869a82000-03-20 06:03:29 +00001959int
Rusty Russell79dee072000-05-02 16:45:16 +00001960TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001961{
1962 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001963 STRUCT_REPLACE *repl;
1964 STRUCT_COUNTERS_INFO *newcounters;
Harald Welteaae69be2004-08-29 23:32:14 +00001965 struct chain_head *c;
1966 int ret;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001967 size_t counterlen;
Harald Welteaae69be2004-08-29 23:32:14 +00001968 int new_number;
1969 unsigned int new_size;
Marc Bouchere6869a82000-03-20 06:03:29 +00001970
1971 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001972
Marc Bouchere6869a82000-03-20 06:03:29 +00001973#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001974 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001975#endif
1976
1977 /* Don't commit if nothing changed. */
1978 if (!(*handle)->changed)
1979 goto finished;
1980
Harald Welteaae69be2004-08-29 23:32:14 +00001981 new_number = iptcc_compile_table_prep(*handle, &new_size);
1982 if (new_number < 0) {
1983 errno = ENOMEM;
1984 return 0;
1985 }
1986
1987 repl = malloc(sizeof(*repl) + new_size);
Marc Bouchere6869a82000-03-20 06:03:29 +00001988 if (!repl) {
1989 errno = ENOMEM;
1990 return 0;
1991 }
Martin Josefssonad3b4f92004-09-22 22:04:07 +00001992 memset(repl, 0, sizeof(*repl) + new_size);
Harald Welteaae69be2004-08-29 23:32:14 +00001993
1994 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1995 + sizeof(STRUCT_COUNTERS) * new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00001996
1997 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001998 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001999 * (*handle)->info.num_entries);
2000 if (!repl->counters) {
2001 free(repl);
2002 errno = ENOMEM;
2003 return 0;
2004 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002005 /* These are the counters we're going to put back, later. */
2006 newcounters = malloc(counterlen);
2007 if (!newcounters) {
2008 free(repl->counters);
2009 free(repl);
2010 errno = ENOMEM;
2011 return 0;
2012 }
Harald Welteaae69be2004-08-29 23:32:14 +00002013 memset(newcounters, 0, counterlen);
Marc Bouchere6869a82000-03-20 06:03:29 +00002014
2015 strcpy(repl->name, (*handle)->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00002016 repl->num_entries = new_number;
2017 repl->size = new_size;
2018
Marc Bouchere6869a82000-03-20 06:03:29 +00002019 repl->num_counters = (*handle)->info.num_entries;
2020 repl->valid_hooks = (*handle)->info.valid_hooks;
Harald Welteaae69be2004-08-29 23:32:14 +00002021
2022 DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
2023 repl->num_entries, repl->size, repl->num_counters);
2024
2025 ret = iptcc_compile_table(*handle, repl);
2026 if (ret < 0) {
2027 errno = ret;
2028 free(repl->counters);
2029 free(repl);
2030 return 0;
2031 }
2032
2033
2034#ifdef IPTC_DEBUG2
2035 {
2036 int fd = open("/tmp/libiptc-so_set_replace.blob",
2037 O_CREAT|O_WRONLY);
2038 if (fd >= 0) {
2039 write(fd, repl, sizeof(*repl) + repl->size);
2040 close(fd);
2041 }
2042 }
2043#endif
Marc Bouchere6869a82000-03-20 06:03:29 +00002044
Rusty Russell79dee072000-05-02 16:45:16 +00002045 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Harald Welteaae69be2004-08-29 23:32:14 +00002046 sizeof(*repl) + repl->size) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002047 free(repl->counters);
2048 free(repl);
2049 free(newcounters);
2050 return 0;
2051 }
2052
2053 /* Put counters back. */
2054 strcpy(newcounters->name, (*handle)->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00002055 newcounters->num_counters = new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00002056
Harald Welteaae69be2004-08-29 23:32:14 +00002057 list_for_each_entry(c, &(*handle)->chains, list) {
2058 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00002059
Harald Welteaae69be2004-08-29 23:32:14 +00002060 /* Builtin chains have their own counters */
2061 if (iptcc_is_builtin(c)) {
2062 DEBUGP("counter for chain-index %u: ", c->foot_index);
2063 switch(c->counter_map.maptype) {
2064 case COUNTER_MAP_NOMAP:
2065 counters_nomap(newcounters, c->foot_index);
2066 break;
2067 case COUNTER_MAP_NORMAL_MAP:
2068 counters_normal_map(newcounters, repl,
2069 c->foot_index,
2070 c->counter_map.mappos);
2071 break;
2072 case COUNTER_MAP_ZEROED:
2073 counters_map_zeroed(newcounters, repl,
2074 c->foot_index,
2075 c->counter_map.mappos,
2076 &c->counters);
2077 break;
2078 case COUNTER_MAP_SET:
2079 counters_map_set(newcounters, c->foot_index,
2080 &c->counters);
2081 break;
2082 }
2083 }
Harald Welte1cef74d2001-01-05 15:22:59 +00002084
Harald Welteaae69be2004-08-29 23:32:14 +00002085 list_for_each_entry(r, &c->rules, list) {
2086 DEBUGP("counter for index %u: ", r->index);
2087 switch (r->counter_map.maptype) {
2088 case COUNTER_MAP_NOMAP:
2089 counters_nomap(newcounters, r->index);
2090 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002091
Harald Welteaae69be2004-08-29 23:32:14 +00002092 case COUNTER_MAP_NORMAL_MAP:
2093 counters_normal_map(newcounters, repl,
2094 r->index,
2095 r->counter_map.mappos);
2096 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002097
Harald Welteaae69be2004-08-29 23:32:14 +00002098 case COUNTER_MAP_ZEROED:
2099 counters_map_zeroed(newcounters, repl,
2100 r->index,
2101 r->counter_map.mappos,
2102 &r->entry->counters);
2103 break;
2104
2105 case COUNTER_MAP_SET:
2106 counters_map_set(newcounters, r->index,
2107 &r->entry->counters);
2108 break;
2109 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002110 }
2111 }
Rusty Russell62527ce2000-09-04 09:45:54 +00002112
Harald Welteaae69be2004-08-29 23:32:14 +00002113
Rusty Russell62527ce2000-09-04 09:45:54 +00002114#ifdef KERNEL_64_USERSPACE_32
2115 {
2116 /* Kernel will think that pointer should be 64-bits, and get
2117 padding. So we accomodate here (assumption: alignment of
2118 `counters' is on 64-bit boundary). */
2119 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
2120 if ((unsigned long)&newcounters->counters % 8 != 0) {
2121 fprintf(stderr,
2122 "counters alignment incorrect! Mail rusty!\n");
2123 abort();
2124 }
2125 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00002126 }
Rusty Russell62527ce2000-09-04 09:45:54 +00002127#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00002128
Harald Welteaae69be2004-08-29 23:32:14 +00002129#ifdef IPTC_DEBUG2
2130 {
2131 int fd = open("/tmp/libiptc-so_set_add_counters.blob",
2132 O_CREAT|O_WRONLY);
2133 if (fd >= 0) {
2134 write(fd, newcounters, counterlen);
2135 close(fd);
2136 }
2137 }
2138#endif
2139
Rusty Russell79dee072000-05-02 16:45:16 +00002140 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
2141 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002142 free(repl->counters);
2143 free(repl);
2144 free(newcounters);
2145 return 0;
2146 }
2147
2148 free(repl->counters);
2149 free(repl);
2150 free(newcounters);
2151
2152 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00002153 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00002154 return 1;
2155}
2156
2157/* Get raw socket. */
2158int
Rusty Russell79dee072000-05-02 16:45:16 +00002159TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00002160{
2161 return sockfd;
2162}
2163
2164/* Translates errno numbers into more human-readable form than strerror. */
2165const char *
Rusty Russell79dee072000-05-02 16:45:16 +00002166TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00002167{
2168 unsigned int i;
2169 struct table_struct {
2170 void *fn;
2171 int err;
2172 const char *message;
2173 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00002174 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002175 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002176 { TC_INIT, ENOENT,
2177 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002178 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
2179 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
2180 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00002181 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00002182 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
2183 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
2184 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
2185 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00002186 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
2187 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00002188 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
2189 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002190 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00002191 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00002192 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002193 { TC_CHECK_PACKET, ENOSYS,
2194 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002195 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00002196 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002197 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002198 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002199 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00002200 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00002201 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002202
2203 { NULL, 0, "Incompatible with this kernel" },
2204 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
2205 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
2206 { NULL, ENOMEM, "Memory allocation problem" },
2207 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002208 };
2209
2210 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
2211 if ((!table[i].fn || table[i].fn == iptc_fn)
2212 && table[i].err == err)
2213 return table[i].message;
2214 }
2215
2216 return strerror(err);
2217}