blob: f6a0233ae70e1d1d4af07ad010f7e9784c38ef96 [file] [log] [blame]
Harald Weltefe537072004-08-30 20:28:53 +00001/* Library which manipulates firewall rules. Version $Revision: 1.48 $ */
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
298/* Returns chain head if found, otherwise NULL. */
299static struct chain_head *
300iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset)
301{
302 struct list_head *pos;
303
304 if (list_empty(&handle->chains))
305 return NULL;
306
307 list_for_each(pos, &handle->chains) {
308 struct chain_head *c = list_entry(pos, struct chain_head, list);
309 if (offset >= c->head_offset && offset <= c->foot_offset)
310 return c;
Harald Welte0113fe72004-01-06 19:04:02 +0000311 }
312
Harald Welteaae69be2004-08-29 23:32:14 +0000313 return NULL;
Harald Welte0113fe72004-01-06 19:04:02 +0000314}
Harald Welteaae69be2004-08-29 23:32:14 +0000315/* Returns chain head if found, otherwise NULL. */
316static struct chain_head *
317iptcc_find_label(const char *name, TC_HANDLE_T handle)
318{
319 struct list_head *pos;
320
321 if (list_empty(&handle->chains))
322 return NULL;
323
324 list_for_each(pos, &handle->chains) {
325 struct chain_head *c = list_entry(pos, struct chain_head, list);
326 if (!strcmp(c->name, name))
327 return c;
328 }
329
330 return NULL;
331}
332
333/* called when rule is to be removed from cache */
334static void iptcc_delete_rule(struct rule_head *r)
335{
336 DEBUGP("deleting rule %p (offset %u)\n", r, r->offset);
337 /* clean up reference count of called chain */
338 if (r->type == IPTCC_R_JUMP
339 && r->jump)
340 r->jump->references--;
341
342 list_del(&r->list);
343 free(r);
344}
345
346
347/**********************************************************************
348 * RULESET PARSER (blob -> cache)
349 **********************************************************************/
350
351static int alphasort(const void *a, const void *b)
352{
353 return strcmp(((struct chain_head *)a)->name,
354 ((struct chain_head *)b)->name);
355}
356
357/* Delete policy rule of previous chain, since cache doesn't contain
358 * chain policy rules.
359 * WARNING: This function has ugly design and relies on a lot of context, only
360 * to be called from specific places within the parser */
361static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num)
362{
363 if (h->chain_iterator_cur) {
364 /* policy rule is last rule */
365 struct rule_head *pr = (struct rule_head *)
366 h->chain_iterator_cur->rules.prev;
367
368 /* save verdict */
369 h->chain_iterator_cur->verdict =
370 *(int *)GET_TARGET(pr->entry)->data;
371
372 /* save counter and counter_map information */
373 h->chain_iterator_cur->counter_map.maptype =
374 COUNTER_MAP_NORMAL_MAP;
375 h->chain_iterator_cur->counter_map.mappos = num-1;
376 memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters,
377 sizeof(h->chain_iterator_cur->counters));
378
379 /* foot_offset points to verdict rule */
380 h->chain_iterator_cur->foot_index = num;
381 h->chain_iterator_cur->foot_offset = pr->offset;
382
383 /* delete rule from cache */
384 iptcc_delete_rule(pr);
385
386 return 1;
387 }
388 return 0;
389}
390
391/* Another ugly helper function split out of cache_add_entry to make it less
392 * spaghetti code */
393static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c,
394 unsigned int offset, unsigned int *num)
395{
396 __iptcc_p_del_policy(h, *num);
397
398 c->head_offset = offset;
399 c->index = *num;
400
401 list_add_tail(&c->list, &h->chains);
402 h->chain_iterator_cur = c;
403}
404
405/* main parser function: add an entry from the blob to the cache */
406static int cache_add_entry(STRUCT_ENTRY *e,
407 TC_HANDLE_T h,
408 STRUCT_ENTRY **prev,
409 unsigned int *num)
410{
411 unsigned int builtin;
412 unsigned int offset = (char *)e - (char *)h->entries->entrytable;
413
414 DEBUGP("entering...");
415
416 /* Last entry ("policy rule"). End it.*/
417 if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) {
418 /* This is the ERROR node at the end of the chain */
419 DEBUGP_C("%u:%u: end of table:\n", *num, offset);
420
421 __iptcc_p_del_policy(h, *num);
422
423 h->chain_iterator_cur = NULL;
424 goto out_inc;
425 }
426
427 /* We know this is the start of a new chain if it's an ERROR
428 * target, or a hook entry point */
429
430 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
431 struct chain_head *c =
432 iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0);
433 DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset,
434 (char *)c->name, c);
435 if (!c) {
436 errno = -ENOMEM;
437 return -1;
438 }
439
440 __iptcc_p_add_chain(h, c, offset, num);
441
442 } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) {
443 struct chain_head *c =
444 iptcc_alloc_chain_head((char *)hooknames[builtin-1],
445 builtin);
446 DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n",
447 *num, offset, c, &c->rules);
448 if (!c) {
449 errno = -ENOMEM;
450 return -1;
451 }
452
453 c->hooknum = builtin;
454
455 __iptcc_p_add_chain(h, c, offset, num);
456
457 /* FIXME: this is ugly. */
458 goto new_rule;
459 } else {
460 /* has to be normal rule */
461 struct rule_head *r;
462new_rule:
463
464 if (!(r = iptcc_alloc_rule(h->chain_iterator_cur,
465 e->next_offset))) {
466 errno = ENOMEM;
467 return -1;
468 }
469 DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r);
470
471 r->index = *num;
472 r->offset = offset;
473 memcpy(r->entry, e, e->next_offset);
474 r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
475 r->counter_map.mappos = r->index;
476
477 /* handling of jumps, etc. */
478 if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) {
479 STRUCT_STANDARD_TARGET *t;
480
481 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
482 if (t->target.u.target_size
483 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
484 errno = EINVAL;
485 return -1;
486 }
487
488 if (t->verdict < 0) {
489 DEBUGP_C("standard, verdict=%d\n", t->verdict);
490 r->type = IPTCC_R_STANDARD;
491 } else if (t->verdict == r->offset+e->next_offset) {
492 DEBUGP_C("fallthrough\n");
493 r->type = IPTCC_R_FALLTHROUGH;
494 } else {
495 DEBUGP_C("jump, target=%u\n", t->verdict);
496 r->type = IPTCC_R_JUMP;
497 /* Jump target fixup has to be deferred
498 * until second pass, since we migh not
499 * yet have parsed the target */
500 }
501 }
502
503 list_add_tail(&r->list, &h->chain_iterator_cur->rules);
504 }
505out_inc:
506 (*num)++;
507 return 0;
508}
509
510
511/* parse an iptables blob into it's pieces */
512static int parse_table(TC_HANDLE_T h)
513{
514 STRUCT_ENTRY *prev;
515 unsigned int num = 0;
516 struct chain_head *c;
517
518 /* First pass: over ruleset blob */
519 ENTRY_ITERATE(h->entries->entrytable, h->entries->size,
520 cache_add_entry, h, &prev, &num);
521
522 /* Second pass: fixup parsed data from first pass */
523 list_for_each_entry(c, &h->chains, list) {
524 struct rule_head *r;
525 list_for_each_entry(r, &c->rules, list) {
526 struct chain_head *c;
527 STRUCT_STANDARD_TARGET *t;
528
529 if (r->type != IPTCC_R_JUMP)
530 continue;
531
532 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
533 c = iptcc_find_chain_by_offset(h, t->verdict);
534 if (!c)
535 return -1;
536 r->jump = c;
537 c->references++;
538 }
539 }
540
541 /* FIXME: sort chains */
542
543 return 1;
544}
545
546
547/**********************************************************************
548 * RULESET COMPILATION (cache -> blob)
549 **********************************************************************/
550
551/* Convenience structures */
552struct iptcb_chain_start{
553 STRUCT_ENTRY e;
554 struct ipt_error_target name;
555};
556#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \
557 ALIGN(sizeof(struct ipt_error_target)))
558
559struct iptcb_chain_foot {
560 STRUCT_ENTRY e;
561 STRUCT_STANDARD_TARGET target;
562};
563#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \
564 ALIGN(sizeof(STRUCT_STANDARD_TARGET)))
565
566struct iptcb_chain_error {
567 STRUCT_ENTRY entry;
568 struct ipt_error_target target;
569};
570#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \
571 ALIGN(sizeof(struct ipt_error_target)))
572
573
574
575/* compile rule from cache into blob */
576static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r)
577{
578 /* handle jumps */
579 if (r->type == IPTCC_R_JUMP) {
580 STRUCT_STANDARD_TARGET *t;
581 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
582 /* memset for memcmp convenience on delete/replace */
583 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
584 strcpy(t->target.u.user.name, STANDARD_TARGET);
585 /* Jumps can only happen to builtin chains, so we
586 * can safely assume that they always have a header */
587 t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE;
588 } else if (r->type == IPTCC_R_FALLTHROUGH) {
589 STRUCT_STANDARD_TARGET *t;
590 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry);
591 t->verdict = r->offset + r->size;
592 }
593
594 /* copy entry from cache to blob */
595 memcpy((char *)repl->entries+r->offset, r->entry, r->size);
596
597 return 1;
598}
599
600/* compile chain from cache into blob */
601static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c)
602{
603 int ret;
604 struct rule_head *r;
605 struct iptcb_chain_start *head;
606 struct iptcb_chain_foot *foot;
607
608 /* only user-defined chains have heaer */
609 if (!iptcc_is_builtin(c)) {
610 /* put chain header in place */
611 head = (void *)repl->entries + c->head_offset;
612 head->e.target_offset = sizeof(STRUCT_ENTRY);
613 head->e.next_offset = IPTCB_CHAIN_START_SIZE;
614 strcpy(head->name.t.u.user.name, ERROR_TARGET);
615 head->name.t.u.target_size =
616 ALIGN(sizeof(struct ipt_error_target));
617 strcpy(head->name.error, c->name);
618 } else {
619 repl->hook_entry[c->hooknum-1] = c->head_offset;
620 repl->underflow[c->hooknum-1] = c->foot_offset;
621 }
622
623 /* iterate over rules */
624 list_for_each_entry(r, &c->rules, list) {
625 ret = iptcc_compile_rule(h, repl, r);
626 if (ret < 0)
627 return ret;
628 }
629
630 /* put chain footer in place */
631 foot = (void *)repl->entries + c->foot_offset;
632 foot->e.target_offset = sizeof(STRUCT_ENTRY);
633 foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE;
634 strcpy(foot->target.target.u.user.name, STANDARD_TARGET);
635 foot->target.target.u.target_size =
636 ALIGN(sizeof(STRUCT_STANDARD_TARGET));
637 /* builtin targets have verdict, others return */
638 if (iptcc_is_builtin(c))
639 foot->target.verdict = c->verdict;
640 else
641 foot->target.verdict = RETURN;
642 /* set policy-counters */
643 memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS));
644
645 return 0;
646}
647
648/* calculate offset and number for every rule in the cache */
649static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c,
650 int *offset, int *num)
651{
652 struct rule_head *r;
653
654 c->head_offset = *offset;
655 DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset);
656
657 if (!iptcc_is_builtin(c)) {
658 /* Chain has header */
659 *offset += sizeof(STRUCT_ENTRY)
660 + ALIGN(sizeof(struct ipt_error_target));
661 (*num)++;
662 }
663
664 list_for_each_entry(r, &c->rules, list) {
665 DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num);
666 r->offset = *offset;
667 r->index = *num;
668 *offset += r->size;
669 (*num)++;
670 }
671
672 DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num,
673 *offset, *num);
674 c->foot_offset = *offset;
675 c->foot_index = *num;
676 *offset += sizeof(STRUCT_ENTRY)
677 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
678 (*num)++;
679
680 return 1;
681}
682
683/* put the pieces back together again */
684static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size)
685{
686 struct chain_head *c;
687 unsigned int offset = 0, num = 0;
688 int ret = 0;
689
690 /* First pass: calculate offset for every rule */
691 list_for_each_entry(c, &h->chains, list) {
692 ret = iptcc_compile_chain_offsets(h, c, &offset, &num);
693 if (ret < 0)
694 return ret;
695 }
696
697 /* Append one error rule at end of chain */
698 num++;
699 offset += sizeof(STRUCT_ENTRY)
700 + ALIGN(sizeof(struct ipt_error_target));
701
702 /* ruleset size is now in offset */
703 *size = offset;
704 return num;
705}
706
707static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl)
708{
709 struct chain_head *c;
710 struct iptcb_chain_error *error;
711
712 /* Second pass: copy from cache to offsets, fill in jumps */
713 list_for_each_entry(c, &h->chains, list) {
714 int ret = iptcc_compile_chain(h, repl, c);
715 if (ret < 0)
716 return ret;
717 }
718
719 /* Append error rule at end of chain */
720 error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE;
721 error->entry.target_offset = sizeof(STRUCT_ENTRY);
722 error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE;
723 error->target.t.u.user.target_size =
724 ALIGN(sizeof(struct ipt_error_target));
725 strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET);
726 strcpy((char *)&error->target.error, "ERROR");
727
728 return 1;
729}
730
731/**********************************************************************
732 * EXTERNAL API (operates on cache only)
733 **********************************************************************/
Marc Bouchere6869a82000-03-20 06:03:29 +0000734
735/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000736static TC_HANDLE_T
Harald Welte0113fe72004-01-06 19:04:02 +0000737alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +0000738{
739 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000740 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000741
Harald Welteaae69be2004-08-29 23:32:14 +0000742 len = sizeof(STRUCT_TC_HANDLE) + size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000743
Harald Welteaae69be2004-08-29 23:32:14 +0000744 h = malloc(sizeof(STRUCT_TC_HANDLE));
745 if (!h) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000746 errno = ENOMEM;
747 return NULL;
748 }
Harald Welteaae69be2004-08-29 23:32:14 +0000749 memset(h, 0, sizeof(*h));
750 INIT_LIST_HEAD(&h->chains);
Marc Bouchere6869a82000-03-20 06:03:29 +0000751 strcpy(h->info.name, tablename);
Harald Welteaae69be2004-08-29 23:32:14 +0000752
753 h->entries = malloc(size);
754 if (!h->entries)
755 goto out_free_handle;
756
757 strcpy(h->entries->name, tablename);
Marc Bouchere6869a82000-03-20 06:03:29 +0000758
759 return h;
Harald Welteaae69be2004-08-29 23:32:14 +0000760
761out_free_handle:
762 free(h);
763
764 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000765}
766
Harald Welteaae69be2004-08-29 23:32:14 +0000767
Rusty Russell79dee072000-05-02 16:45:16 +0000768TC_HANDLE_T
769TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000770{
Rusty Russell79dee072000-05-02 16:45:16 +0000771 TC_HANDLE_T h;
772 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +0000773 int tmp;
774 socklen_t s;
775
Rusty Russell79dee072000-05-02 16:45:16 +0000776 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000777
Martin Josefssone560fd62003-06-13 16:56:51 +0000778 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000779 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000780 sockfd = -1;
781 }
Harald Welte366454b2002-01-07 13:46:50 +0000782
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000783 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
784 errno = EINVAL;
785 return NULL;
786 }
787
Rusty Russell79dee072000-05-02 16:45:16 +0000788 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000789 if (sockfd < 0)
790 return NULL;
791
792 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000793
Marc Bouchere6869a82000-03-20 06:03:29 +0000794 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000795 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000796 return NULL;
797
Harald Welteaae69be2004-08-29 23:32:14 +0000798 DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n",
799 info.valid_hooks, info.num_entries, info.size);
800
Harald Welte0113fe72004-01-06 19:04:02 +0000801 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000802 == NULL) {
803 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000804 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000805 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000806 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000807
Marc Bouchere6869a82000-03-20 06:03:29 +0000808 /* Initialize current state */
809 h->info = info;
Harald Welte0113fe72004-01-06 19:04:02 +0000810
Harald Welteaae69be2004-08-29 23:32:14 +0000811 h->entries->size = h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000812
Rusty Russell79dee072000-05-02 16:45:16 +0000813 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000814
Harald Welteaae69be2004-08-29 23:32:14 +0000815 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries,
816 &tmp) < 0)
817 goto error;
818
819#ifdef IPTC_DEBUG2
820 {
821 int fd = open("/tmp/libiptc-so_get_entries.blob",
822 O_CREAT|O_WRONLY);
823 if (fd >= 0) {
824 write(fd, h->entries, tmp);
825 close(fd);
826 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000827 }
Harald Welteaae69be2004-08-29 23:32:14 +0000828#endif
829
830 if (parse_table(h) < 0)
831 goto error;
Rusty Russell7e53bf92000-03-20 07:03:28 +0000832
Marc Bouchere6869a82000-03-20 06:03:29 +0000833 CHECK(h);
834 return h;
Harald Welteaae69be2004-08-29 23:32:14 +0000835error:
836 TC_FREE(&h);
837 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000838}
839
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000840void
841TC_FREE(TC_HANDLE_T *h)
842{
Harald Welteaae69be2004-08-29 23:32:14 +0000843 struct chain_head *c, *tmp;
844
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000845 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000846 sockfd = -1;
Harald Welteaae69be2004-08-29 23:32:14 +0000847
848 list_for_each_entry_safe(c, tmp, &(*h)->chains, list) {
849 struct rule_head *r, *rtmp;
850
851 list_for_each_entry_safe(r, rtmp, &c->rules, list) {
852 free(r);
853 }
854
855 free(c);
856 }
857
858 free((*h)->entries);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000859 free(*h);
Harald Welteaae69be2004-08-29 23:32:14 +0000860
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000861 *h = NULL;
862}
863
Marc Bouchere6869a82000-03-20 06:03:29 +0000864static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000865print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000866{
Rusty Russell228e98d2000-04-27 10:28:06 +0000867 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000868 return 0;
869}
870
Rusty Russell79dee072000-05-02 16:45:16 +0000871static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
872
Marc Bouchere6869a82000-03-20 06:03:29 +0000873void
Rusty Russell79dee072000-05-02 16:45:16 +0000874TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000875{
876 CHECK(handle);
Harald Welteaae69be2004-08-29 23:32:14 +0000877#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000878 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000879 IPTABLES_VERSION,
Harald Welteaae69be2004-08-29 23:32:14 +0000880 handle->new_number, handle->entries->size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000881 printf("Table `%s'\n", handle->info.name);
882 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000883 handle->info.hook_entry[HOOK_PRE_ROUTING],
884 handle->info.hook_entry[HOOK_LOCAL_IN],
885 handle->info.hook_entry[HOOK_FORWARD],
886 handle->info.hook_entry[HOOK_LOCAL_OUT],
887 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000888 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000889 handle->info.underflow[HOOK_PRE_ROUTING],
890 handle->info.underflow[HOOK_LOCAL_IN],
891 handle->info.underflow[HOOK_FORWARD],
892 handle->info.underflow[HOOK_LOCAL_OUT],
893 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000894
Harald Welteaae69be2004-08-29 23:32:14 +0000895 ENTRY_ITERATE(handle->entries->entrytable, handle->entries->size,
Rusty Russell79dee072000-05-02 16:45:16 +0000896 dump_entry, handle);
Harald Welteaae69be2004-08-29 23:32:14 +0000897#endif
Harald Welte0113fe72004-01-06 19:04:02 +0000898}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000899
Marc Bouchere6869a82000-03-20 06:03:29 +0000900/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000901int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000902{
Harald Welteaae69be2004-08-29 23:32:14 +0000903 return iptcc_find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000904}
905
Harald Welteaae69be2004-08-29 23:32:14 +0000906static void iptcc_chain_iterator_advance(TC_HANDLE_T handle)
907{
908 struct chain_head *c = handle->chain_iterator_cur;
909
910 if (c->list.next == &handle->chains)
911 handle->chain_iterator_cur = NULL;
912 else
913 handle->chain_iterator_cur =
914 list_entry(c->list.next, struct chain_head, list);
915}
Marc Bouchere6869a82000-03-20 06:03:29 +0000916
Rusty Russell30fd6e52000-04-23 09:16:06 +0000917/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000918const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000919TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000920{
Harald Welteaae69be2004-08-29 23:32:14 +0000921 struct chain_head *c = list_entry((*handle)->chains.next,
922 struct chain_head, list);
923
924 iptc_fn = TC_FIRST_CHAIN;
925
926
927 if (list_empty(&(*handle)->chains)) {
928 DEBUGP(": no chains\n");
Harald Welte0113fe72004-01-06 19:04:02 +0000929 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +0000930 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000931
Harald Welteaae69be2004-08-29 23:32:14 +0000932 (*handle)->chain_iterator_cur = c;
933 iptcc_chain_iterator_advance(*handle);
Harald Welte0113fe72004-01-06 19:04:02 +0000934
Harald Welteaae69be2004-08-29 23:32:14 +0000935 DEBUGP(": returning `%s'\n", c->name);
936 return c->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000937}
938
Rusty Russell30fd6e52000-04-23 09:16:06 +0000939/* Iterator functions to run through the chains. Returns NULL at end. */
940const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000941TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000942{
Harald Welteaae69be2004-08-29 23:32:14 +0000943 struct chain_head *c = (*handle)->chain_iterator_cur;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000944
Harald Welteaae69be2004-08-29 23:32:14 +0000945 iptc_fn = TC_NEXT_CHAIN;
946
947 if (!c) {
948 DEBUGP(": no more chains\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +0000949 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +0000950 }
Rusty Russell30fd6e52000-04-23 09:16:06 +0000951
Harald Welteaae69be2004-08-29 23:32:14 +0000952 iptcc_chain_iterator_advance(*handle);
953
954 DEBUGP(": returning `%s'\n", c->name);
955 return c->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000956}
957
958/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000959const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000960TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000961{
Harald Welteaae69be2004-08-29 23:32:14 +0000962 struct chain_head *c;
963 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000964
Harald Welteaae69be2004-08-29 23:32:14 +0000965 iptc_fn = TC_FIRST_RULE;
966
967 DEBUGP("first rule(%s): ", chain);
968
969 c = iptcc_find_label(chain, *handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000970 if (!c) {
971 errno = ENOENT;
972 return NULL;
973 }
974
975 /* Empty chain: single return/policy rule */
Harald Welteaae69be2004-08-29 23:32:14 +0000976 if (list_empty(&c->rules)) {
977 DEBUGP_C("no rules, returning NULL\n");
Rusty Russell30fd6e52000-04-23 09:16:06 +0000978 return NULL;
Harald Welteaae69be2004-08-29 23:32:14 +0000979 }
Rusty Russell30fd6e52000-04-23 09:16:06 +0000980
Harald Welteaae69be2004-08-29 23:32:14 +0000981 r = list_entry(c->rules.next, struct rule_head, list);
982 (*handle)->rule_iterator_cur = r;
983 DEBUGP_C("%p\n", r);
984
985 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000986}
987
988/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000989const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000990TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000991{
Harald Welteaae69be2004-08-29 23:32:14 +0000992 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000993
Harald Welteaae69be2004-08-29 23:32:14 +0000994 DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur);
995
996 if (!(*handle)->rule_iterator_cur) {
997 DEBUGP_C("returning NULL\n");
998 return NULL;
999 }
1000
1001 r = list_entry((*handle)->rule_iterator_cur->list.next,
1002 struct rule_head, list);
1003
1004 iptc_fn = TC_NEXT_RULE;
1005
1006 DEBUGP_C("next=%p, head=%p...", &r->list,
1007 &(*handle)->rule_iterator_cur->chain->rules);
1008
1009 if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) {
1010 (*handle)->rule_iterator_cur = NULL;
1011 DEBUGP_C("finished, returning NULL\n");
1012 return NULL;
1013 }
1014
1015 (*handle)->rule_iterator_cur = r;
1016
1017 /* NOTE: prev is without any influence ! */
1018 DEBUGP_C("returning rule %p\n", r);
1019 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001020}
1021
Marc Bouchere6869a82000-03-20 06:03:29 +00001022/* How many rules in this chain? */
1023unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +00001024TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001025{
Harald Welteaae69be2004-08-29 23:32:14 +00001026 struct chain_head *c;
1027 iptc_fn = TC_NUM_RULES;
Marc Bouchere6869a82000-03-20 06:03:29 +00001028 CHECK(*handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001029
1030 c = iptcc_find_label(chain, *handle);
1031 if (!c) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001032 errno = ENOENT;
1033 return (unsigned int)-1;
1034 }
Harald Welteaae69be2004-08-29 23:32:14 +00001035
1036 return c->num_rules;
Marc Bouchere6869a82000-03-20 06:03:29 +00001037}
1038
Rusty Russell79dee072000-05-02 16:45:16 +00001039const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
1040 unsigned int n,
1041 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001042{
Harald Welteaae69be2004-08-29 23:32:14 +00001043 struct chain_head *c;
1044 struct rule_head *r;
1045
1046 iptc_fn = TC_GET_RULE;
Marc Bouchere6869a82000-03-20 06:03:29 +00001047
1048 CHECK(*handle);
Harald Welteaae69be2004-08-29 23:32:14 +00001049
1050 c = iptcc_find_label(chain, *handle);
1051 if (!c) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001052 errno = ENOENT;
1053 return NULL;
1054 }
1055
Harald Welteaae69be2004-08-29 23:32:14 +00001056 r = iptcc_get_rule_num(c, n);
1057 if (!r)
1058 return NULL;
1059 return r->entry;
Marc Bouchere6869a82000-03-20 06:03:29 +00001060}
1061
1062/* Returns a pointer to the target name of this position. */
Harald Welteaae69be2004-08-29 23:32:14 +00001063const char *standard_target_map(int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +00001064{
Harald Welteaae69be2004-08-29 23:32:14 +00001065 switch (verdict) {
1066 case RETURN:
1067 return LABEL_RETURN;
1068 break;
1069 case -NF_ACCEPT-1:
1070 return LABEL_ACCEPT;
1071 break;
1072 case -NF_DROP-1:
1073 return LABEL_DROP;
1074 break;
1075 case -NF_QUEUE-1:
1076 return LABEL_QUEUE;
1077 break;
1078 default:
1079 fprintf(stderr, "ERROR: %d not a valid target)\n",
1080 verdict);
1081 abort();
1082 break;
1083 }
1084 /* not reached */
1085 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +00001086}
1087
Harald Welteaae69be2004-08-29 23:32:14 +00001088/* Returns a pointer to the target name of this position. */
1089const char *TC_GET_TARGET(const STRUCT_ENTRY *ce,
1090 TC_HANDLE_T *handle)
1091{
1092 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
1093 struct rule_head *r = container_of(e, struct rule_head, entry);
1094
1095 iptc_fn = TC_GET_TARGET;
1096
1097 switch(r->type) {
1098 int spos;
1099 case IPTCC_R_FALLTHROUGH:
1100 return "";
1101 break;
1102 case IPTCC_R_JUMP:
1103 DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name);
1104 return r->jump->name;
1105 break;
1106 case IPTCC_R_STANDARD:
1107 spos = *(int *)GET_TARGET(e)->data;
1108 DEBUGP("r=%p, spos=%d'\n", r, spos);
1109 return standard_target_map(spos);
1110 break;
1111 case IPTCC_R_MODULE:
1112 return GET_TARGET(e)->u.user.name;
1113 break;
1114 }
1115 return NULL;
1116}
Marc Bouchere6869a82000-03-20 06:03:29 +00001117/* Is this a built-in chain? Actually returns hook + 1. */
1118int
Rusty Russell79dee072000-05-02 16:45:16 +00001119TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001120{
Harald Welteaae69be2004-08-29 23:32:14 +00001121 struct chain_head *c;
1122
1123 iptc_fn = TC_BUILTIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001124
Harald Welteaae69be2004-08-29 23:32:14 +00001125 c = iptcc_find_label(chain, handle);
1126 if (!c) {
1127 errno = ENOENT;
1128 return -1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001129 }
Harald Welteaae69be2004-08-29 23:32:14 +00001130
1131 return iptcc_is_builtin(c);
Marc Bouchere6869a82000-03-20 06:03:29 +00001132}
1133
1134/* Get the policy of a given built-in chain */
1135const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001136TC_GET_POLICY(const char *chain,
1137 STRUCT_COUNTERS *counters,
1138 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001139{
Harald Welteaae69be2004-08-29 23:32:14 +00001140 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001141
Harald Welteaae69be2004-08-29 23:32:14 +00001142 iptc_fn = TC_GET_POLICY;
1143
1144 DEBUGP("called for chain %s\n", chain);
1145
1146 c = iptcc_find_label(chain, *handle);
1147 if (!c) {
1148 errno = ENOENT;
1149 return NULL;
1150 }
1151
1152 if (!iptcc_is_builtin(c))
Marc Bouchere6869a82000-03-20 06:03:29 +00001153 return NULL;
1154
Harald Welteaae69be2004-08-29 23:32:14 +00001155 *counters = c->counters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001156
Harald Welteaae69be2004-08-29 23:32:14 +00001157 return standard_target_map(c->verdict);
Harald Welte0113fe72004-01-06 19:04:02 +00001158}
1159
1160static int
Harald Welteaae69be2004-08-29 23:32:14 +00001161iptcc_standard_map(struct rule_head *r, int verdict)
Harald Welte0113fe72004-01-06 19:04:02 +00001162{
Harald Welteaae69be2004-08-29 23:32:14 +00001163 STRUCT_ENTRY *e = r->entry;
Rusty Russell79dee072000-05-02 16:45:16 +00001164 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001165
Rusty Russell79dee072000-05-02 16:45:16 +00001166 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001167
Rusty Russell67088e72000-05-10 01:18:57 +00001168 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001169 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001170 errno = EINVAL;
1171 return 0;
1172 }
1173 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +00001174 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
1175 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +00001176 t->verdict = verdict;
1177
Harald Welteaae69be2004-08-29 23:32:14 +00001178 r->type = IPTCC_R_STANDARD;
1179
Marc Bouchere6869a82000-03-20 06:03:29 +00001180 return 1;
1181}
Rusty Russell7e53bf92000-03-20 07:03:28 +00001182
Marc Bouchere6869a82000-03-20 06:03:29 +00001183static int
Harald Welteaae69be2004-08-29 23:32:14 +00001184iptcc_map_target(const TC_HANDLE_T handle,
1185 struct rule_head *r)
Marc Bouchere6869a82000-03-20 06:03:29 +00001186{
Harald Welteaae69be2004-08-29 23:32:14 +00001187 STRUCT_ENTRY *e = r->entry;
Harald Welte0113fe72004-01-06 19:04:02 +00001188 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001189
Marc Bouchere6869a82000-03-20 06:03:29 +00001190 /* Maybe it's empty (=> fall through) */
Harald Welteaae69be2004-08-29 23:32:14 +00001191 if (strcmp(t->u.user.name, "") == 0) {
1192 r->type = IPTCC_R_FALLTHROUGH;
1193 return 1;
1194 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001195 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +00001196 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001197 return iptcc_standard_map(r, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001198 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001199 return iptcc_standard_map(r, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +00001200 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001201 return iptcc_standard_map(r, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001202 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001203 return iptcc_standard_map(r, RETURN);
Rusty Russell79dee072000-05-02 16:45:16 +00001204 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001205 /* Can't jump to builtins. */
1206 errno = EINVAL;
1207 return 0;
1208 } else {
1209 /* Maybe it's an existing chain name. */
Harald Welteaae69be2004-08-29 23:32:14 +00001210 struct chain_head *c;
1211 DEBUGP("trying to find chain `%s': ", t->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +00001212
Harald Welteaae69be2004-08-29 23:32:14 +00001213 c = iptcc_find_label(t->u.user.name, handle);
1214 if (c) {
1215 DEBUGP_C("found!\n");
1216 r->type = IPTCC_R_JUMP;
1217 r->jump = c;
1218 c->references++;
1219 return 1;
1220 }
1221 DEBUGP_C("not found :(\n");
Marc Bouchere6869a82000-03-20 06:03:29 +00001222 }
1223
1224 /* Must be a module? If not, kernel will reject... */
1225 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001226 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001227 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001228 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Harald Welteaae69be2004-08-29 23:32:14 +00001229
1230 set_changed(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001231 return 1;
1232}
1233
Harald Welte0113fe72004-01-06 19:04:02 +00001234/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001235int
Rusty Russell79dee072000-05-02 16:45:16 +00001236TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1237 const STRUCT_ENTRY *e,
1238 unsigned int rulenum,
1239 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001240{
Harald Welteaae69be2004-08-29 23:32:14 +00001241 struct chain_head *c;
1242 struct rule_head *r, *prev;
Marc Bouchere6869a82000-03-20 06:03:29 +00001243
Rusty Russell79dee072000-05-02 16:45:16 +00001244 iptc_fn = TC_INSERT_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001245
1246 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001247 errno = ENOENT;
1248 return 0;
1249 }
1250
Harald Welteaae69be2004-08-29 23:32:14 +00001251 prev = iptcc_get_rule_num(c, rulenum);
1252 if (!prev) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001253 errno = E2BIG;
1254 return 0;
1255 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001256
Harald Welteaae69be2004-08-29 23:32:14 +00001257 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1258 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001259 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001260 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001261
Harald Welteaae69be2004-08-29 23:32:14 +00001262 memcpy(r->entry, e, e->next_offset);
1263 r->counter_map.maptype = COUNTER_MAP_SET;
1264
1265 if (!iptcc_map_target(*handle, r)) {
1266 free(r);
1267 return 0;
1268 }
1269
1270 list_add_tail(&r->list, &prev->list);
1271 c->num_rules++;
1272
1273 set_changed(*handle);
1274
1275 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001276}
1277
1278/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1279int
Rusty Russell79dee072000-05-02 16:45:16 +00001280TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1281 const STRUCT_ENTRY *e,
1282 unsigned int rulenum,
1283 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001284{
Harald Welteaae69be2004-08-29 23:32:14 +00001285 struct chain_head *c;
1286 struct rule_head *r, *old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001287
Rusty Russell79dee072000-05-02 16:45:16 +00001288 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001289
Harald Welteaae69be2004-08-29 23:32:14 +00001290 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001291 errno = ENOENT;
1292 return 0;
1293 }
1294
Harald Welteaae69be2004-08-29 23:32:14 +00001295 if (!(old = iptcc_get_rule_num(c, rulenum))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001296 errno = E2BIG;
1297 return 0;
1298 }
1299
Harald Welteaae69be2004-08-29 23:32:14 +00001300 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1301 errno = ENOMEM;
Marc Bouchere6869a82000-03-20 06:03:29 +00001302 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001303 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001304
Harald Welteaae69be2004-08-29 23:32:14 +00001305 memcpy(r->entry, e, e->next_offset);
1306 r->counter_map.maptype = COUNTER_MAP_SET;
1307
1308 if (!iptcc_map_target(*handle, r)) {
1309 free(r);
Harald Welte0113fe72004-01-06 19:04:02 +00001310 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001311 }
Harald Welte0113fe72004-01-06 19:04:02 +00001312
Harald Welteaae69be2004-08-29 23:32:14 +00001313 list_add(&r->list, &old->list);
1314 iptcc_delete_rule(old);
1315
1316 set_changed(*handle);
1317
1318 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001319}
1320
Harald Welte0113fe72004-01-06 19:04:02 +00001321/* Append entry `fw' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001322 rulenum = length of chain. */
1323int
Rusty Russell79dee072000-05-02 16:45:16 +00001324TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1325 const STRUCT_ENTRY *e,
1326 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001327{
Harald Welteaae69be2004-08-29 23:32:14 +00001328 struct chain_head *c;
1329 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00001330
Rusty Russell79dee072000-05-02 16:45:16 +00001331 iptc_fn = TC_APPEND_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001332 if (!(c = iptcc_find_label(chain, *handle))) {
1333 DEBUGP("unable to find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001334 errno = ENOENT;
1335 return 0;
1336 }
1337
Harald Welteaae69be2004-08-29 23:32:14 +00001338 if (!(r = iptcc_alloc_rule(c, e->next_offset))) {
1339 DEBUGP("unable to allocate rule for chain `%s'\n", chain);
1340 errno = ENOMEM;
Harald Welte0113fe72004-01-06 19:04:02 +00001341 return 0;
Harald Welteaae69be2004-08-29 23:32:14 +00001342 }
Harald Welte0113fe72004-01-06 19:04:02 +00001343
Harald Welteaae69be2004-08-29 23:32:14 +00001344 memcpy(r->entry, e, e->next_offset);
1345 r->counter_map.maptype = COUNTER_MAP_SET;
1346
1347 if (!iptcc_map_target(*handle, r)) {
1348 DEBUGP("unable to ma target of rule for chain `%s'\n", chain);
1349 free(r);
1350 return 0;
1351 }
1352
1353 list_add_tail(&r->list, &c->rules);
1354 c->num_rules++;
1355
1356 set_changed(*handle);
1357
1358 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001359}
1360
1361static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001362match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001363 const unsigned char *a_elems,
1364 const unsigned char *b_elems,
1365 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001366{
Rusty Russell79dee072000-05-02 16:45:16 +00001367 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001368 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001369
1370 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001371 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001372
Rusty Russell228e98d2000-04-27 10:28:06 +00001373 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001374 return 1;
1375
Rusty Russell228e98d2000-04-27 10:28:06 +00001376 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001377 return 1;
1378
Rusty Russell73ef09b2000-07-03 10:24:04 +00001379 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001380
Rusty Russell73ef09b2000-07-03 10:24:04 +00001381 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001382 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001383 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001384 *maskptr += i;
1385 return 0;
1386}
1387
1388static inline int
1389target_different(const unsigned char *a_targdata,
1390 const unsigned char *b_targdata,
1391 unsigned int tdatasize,
1392 const unsigned char *mask)
1393{
1394 unsigned int i;
1395 for (i = 0; i < tdatasize; i++)
1396 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1397 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001398
1399 return 0;
1400}
1401
Rusty Russell79dee072000-05-02 16:45:16 +00001402static int
1403is_same(const STRUCT_ENTRY *a,
1404 const STRUCT_ENTRY *b,
1405 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001406
Harald Welte0113fe72004-01-06 19:04:02 +00001407/* Delete the first rule in `chain' which matches `fw'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001408int
Rusty Russell79dee072000-05-02 16:45:16 +00001409TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1410 const STRUCT_ENTRY *origfw,
1411 unsigned char *matchmask,
1412 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001413{
Harald Welteaae69be2004-08-29 23:32:14 +00001414 struct chain_head *c;
Harald Weltefe537072004-08-30 20:28:53 +00001415 struct rule_head *r;
Harald Welte0113fe72004-01-06 19:04:02 +00001416 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001417
Rusty Russell79dee072000-05-02 16:45:16 +00001418 iptc_fn = TC_DELETE_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001419 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001420 errno = ENOENT;
1421 return 0;
1422 }
1423
Harald Welte0113fe72004-01-06 19:04:02 +00001424 fw = malloc(origfw->next_offset);
1425 if (fw == NULL) {
1426 errno = ENOMEM;
1427 return 0;
1428 }
1429
Harald Weltefe537072004-08-30 20:28:53 +00001430 list_for_each_entry(r, &c->rules, list) {
Harald Welte0113fe72004-01-06 19:04:02 +00001431
1432 memcpy(fw, origfw, origfw->next_offset);
1433
Harald Weltefe537072004-08-30 20:28:53 +00001434#if 0
Harald Welte0113fe72004-01-06 19:04:02 +00001435 /* FIXME: handle this in is_same --RR */
1436 if (!map_target(*handle, fw, offset, &discard)) {
1437 free(fw);
1438 return 0;
1439 }
Harald Welte0113fe72004-01-06 19:04:02 +00001440#endif
Harald Weltefe537072004-08-30 20:28:53 +00001441
1442 e = r->entry;
1443
Harald Welte0113fe72004-01-06 19:04:02 +00001444 if (is_same(e, fw, matchmask)) {
Harald Weltefe537072004-08-30 20:28:53 +00001445 /* If we are about to delete the rule that is the
1446 * current iterator, move rule iterator back. next
1447 * pointer will then point to real next node */
1448 if (r == (*handle)->rule_iterator_cur) {
1449 (*handle)->rule_iterator_cur =
1450 list_entry((*handle)->rule_iterator_cur->list.prev,
1451 struct rule_head, list);
1452 }
1453
1454 c->num_rules--;
1455 iptcc_delete_rule(r);
1456 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001457 }
1458 }
1459
Harald Welte0113fe72004-01-06 19:04:02 +00001460 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001461 errno = ENOENT;
1462 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001463}
Harald Welteaae69be2004-08-29 23:32:14 +00001464
Marc Bouchere6869a82000-03-20 06:03:29 +00001465
1466/* Delete the rule in position `rulenum' in `chain'. */
1467int
Rusty Russell79dee072000-05-02 16:45:16 +00001468TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1469 unsigned int rulenum,
1470 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001471{
Harald Welteaae69be2004-08-29 23:32:14 +00001472 struct chain_head *c;
1473 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00001474
Rusty Russell79dee072000-05-02 16:45:16 +00001475 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welteaae69be2004-08-29 23:32:14 +00001476
1477 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001478 errno = ENOENT;
1479 return 0;
1480 }
1481
Harald Welteaae69be2004-08-29 23:32:14 +00001482 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001483 errno = E2BIG;
1484 return 0;
1485 }
1486
Harald Welteaae69be2004-08-29 23:32:14 +00001487 /* If we are about to delete the rule that is the current
1488 * iterator, move rule iterator back. next pointer will then
1489 * point to real next node */
1490 if (r == (*handle)->rule_iterator_cur) {
1491 (*handle)->rule_iterator_cur =
1492 list_entry((*handle)->rule_iterator_cur->list.prev,
1493 struct rule_head, list);
Harald Welte0113fe72004-01-06 19:04:02 +00001494 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001495
Harald Welteaae69be2004-08-29 23:32:14 +00001496 c->num_rules--;
1497 iptcc_delete_rule(r);
1498
1499 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001500}
1501
1502/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1503 NULL and sets errno. */
1504const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001505TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1506 STRUCT_ENTRY *entry,
1507 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001508{
1509 errno = ENOSYS;
1510 return NULL;
1511}
1512
1513/* Flushes the entries in the given chain (ie. empties chain). */
1514int
Rusty Russell79dee072000-05-02 16:45:16 +00001515TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001516{
Harald Welteaae69be2004-08-29 23:32:14 +00001517 struct chain_head *c;
1518 struct rule_head *r, *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001519
Harald Welte0113fe72004-01-06 19:04:02 +00001520 iptc_fn = TC_FLUSH_ENTRIES;
Harald Welteaae69be2004-08-29 23:32:14 +00001521 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001522 errno = ENOENT;
1523 return 0;
1524 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001525
Harald Welteaae69be2004-08-29 23:32:14 +00001526 list_for_each_entry_safe(r, tmp, &c->rules, list) {
1527 iptcc_delete_rule(r);
1528 }
1529
1530 c->num_rules = 0;
1531
1532 set_changed(*handle);
1533
1534 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001535}
1536
1537/* Zeroes the counters in a chain. */
1538int
Rusty Russell79dee072000-05-02 16:45:16 +00001539TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001540{
Harald Welteaae69be2004-08-29 23:32:14 +00001541 struct chain_head *c;
1542 struct rule_head *r;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001543
Harald Welteaae69be2004-08-29 23:32:14 +00001544 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001545 errno = ENOENT;
1546 return 0;
1547 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001548
Harald Welteaae69be2004-08-29 23:32:14 +00001549 list_for_each_entry(r, &c->rules, list) {
1550 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1551 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00001552 }
Harald Welteaae69be2004-08-29 23:32:14 +00001553
Rusty Russell175f6412000-03-24 09:32:20 +00001554 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001555
Marc Bouchere6869a82000-03-20 06:03:29 +00001556 return 1;
1557}
1558
Harald Welte1cef74d2001-01-05 15:22:59 +00001559STRUCT_COUNTERS *
1560TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1561 unsigned int rulenum,
1562 TC_HANDLE_T *handle)
1563{
Harald Welteaae69be2004-08-29 23:32:14 +00001564 struct chain_head *c;
1565 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001566
1567 iptc_fn = TC_READ_COUNTER;
1568 CHECK(*handle);
1569
Harald Welteaae69be2004-08-29 23:32:14 +00001570 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001571 errno = ENOENT;
1572 return NULL;
1573 }
1574
Harald Welteaae69be2004-08-29 23:32:14 +00001575 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001576 errno = E2BIG;
1577 return NULL;
1578 }
1579
Harald Welteaae69be2004-08-29 23:32:14 +00001580 return &r->entry[0].counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00001581}
1582
1583int
1584TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1585 unsigned int rulenum,
1586 TC_HANDLE_T *handle)
1587{
Harald Welteaae69be2004-08-29 23:32:14 +00001588 struct chain_head *c;
1589 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001590
1591 iptc_fn = TC_ZERO_COUNTER;
1592 CHECK(*handle);
1593
Harald Welteaae69be2004-08-29 23:32:14 +00001594 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001595 errno = ENOENT;
1596 return 0;
1597 }
1598
Harald Welteaae69be2004-08-29 23:32:14 +00001599 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001600 errno = E2BIG;
1601 return 0;
1602 }
1603
Harald Welteaae69be2004-08-29 23:32:14 +00001604 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1605 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Harald Welte1cef74d2001-01-05 15:22:59 +00001606
1607 set_changed(*handle);
1608
1609 return 1;
1610}
1611
1612int
1613TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1614 unsigned int rulenum,
1615 STRUCT_COUNTERS *counters,
1616 TC_HANDLE_T *handle)
1617{
Harald Welteaae69be2004-08-29 23:32:14 +00001618 struct chain_head *c;
1619 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001620 STRUCT_ENTRY *e;
Harald Welte1cef74d2001-01-05 15:22:59 +00001621
1622 iptc_fn = TC_SET_COUNTER;
1623 CHECK(*handle);
1624
Harald Welteaae69be2004-08-29 23:32:14 +00001625 if (!(c = iptcc_find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001626 errno = ENOENT;
1627 return 0;
1628 }
Harald Welte0113fe72004-01-06 19:04:02 +00001629
Harald Welteaae69be2004-08-29 23:32:14 +00001630 if (!(r = iptcc_get_rule_num(c, rulenum))) {
Harald Welte0113fe72004-01-06 19:04:02 +00001631 errno = E2BIG;
1632 return 0;
1633 }
1634
Harald Welteaae69be2004-08-29 23:32:14 +00001635 e = r->entry;
1636 r->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte0113fe72004-01-06 19:04:02 +00001637
1638 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00001639
1640 set_changed(*handle);
1641
1642 return 1;
1643}
1644
Marc Bouchere6869a82000-03-20 06:03:29 +00001645/* Creates a new chain. */
1646/* To create a chain, create two rules: error node and unconditional
1647 * return. */
1648int
Rusty Russell79dee072000-05-02 16:45:16 +00001649TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001650{
Harald Welteaae69be2004-08-29 23:32:14 +00001651 static struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001652
Rusty Russell79dee072000-05-02 16:45:16 +00001653 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001654
1655 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1656 QUEUE, RETURN. */
Harald Welteaae69be2004-08-29 23:32:14 +00001657 if (iptcc_find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001658 || strcmp(chain, LABEL_DROP) == 0
1659 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001660 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001661 || strcmp(chain, LABEL_RETURN) == 0) {
Harald Welteaae69be2004-08-29 23:32:14 +00001662 DEBUGP("Chain `%s' already exists\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001663 errno = EEXIST;
1664 return 0;
1665 }
1666
Rusty Russell79dee072000-05-02 16:45:16 +00001667 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Harald Welteaae69be2004-08-29 23:32:14 +00001668 DEBUGP("Chain name `%s' too long\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001669 errno = EINVAL;
1670 return 0;
1671 }
1672
Harald Welteaae69be2004-08-29 23:32:14 +00001673 c = iptcc_alloc_chain_head(chain, 0);
1674 if (!c) {
1675 DEBUGP("Cannot allocate memory for chain `%s'\n", chain);
1676 errno = ENOMEM;
1677 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001678
Harald Welteaae69be2004-08-29 23:32:14 +00001679 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001680
Harald Welteaae69be2004-08-29 23:32:14 +00001681 DEBUGP("Creating chain `%s'\n", chain);
1682 list_add_tail(&c->list, &(*handle)->chains);
Harald Welte0113fe72004-01-06 19:04:02 +00001683
1684 set_changed(*handle);
1685
Harald Welteaae69be2004-08-29 23:32:14 +00001686 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001687}
1688
1689/* Get the number of references to this chain. */
1690int
Rusty Russell79dee072000-05-02 16:45:16 +00001691TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1692 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001693{
Harald Welteaae69be2004-08-29 23:32:14 +00001694 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001695
Harald Welteaae69be2004-08-29 23:32:14 +00001696 if (!(c = iptcc_find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001697 errno = ENOENT;
1698 return 0;
1699 }
1700
Harald Welteaae69be2004-08-29 23:32:14 +00001701 *ref = c->references;
1702
Marc Bouchere6869a82000-03-20 06:03:29 +00001703 return 1;
1704}
1705
1706/* Deletes a chain. */
1707int
Rusty Russell79dee072000-05-02 16:45:16 +00001708TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001709{
Marc Bouchere6869a82000-03-20 06:03:29 +00001710 unsigned int references;
Harald Welteaae69be2004-08-29 23:32:14 +00001711 struct chain_head *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001712
Rusty Russell79dee072000-05-02 16:45:16 +00001713 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001714
Harald Welteaae69be2004-08-29 23:32:14 +00001715 if (!(c = iptcc_find_label(chain, *handle))) {
1716 DEBUGP("cannot find chain `%s'\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001717 errno = ENOENT;
1718 return 0;
1719 }
1720
Harald Welteaae69be2004-08-29 23:32:14 +00001721 if (TC_BUILTIN(chain, *handle)) {
1722 DEBUGP("cannot remove builtin chain `%s'\n", chain);
1723 errno = EINVAL;
1724 return 0;
1725 }
1726
1727 if (!TC_GET_REFERENCES(&references, chain, handle)) {
1728 DEBUGP("cannot get references on chain `%s'\n", chain);
1729 return 0;
1730 }
1731
1732 if (references > 0) {
1733 DEBUGP("chain `%s' still has references\n", chain);
1734 errno = EMLINK;
1735 return 0;
1736 }
1737
1738 if (c->num_rules) {
1739 DEBUGP("chain `%s' is not empty\n", chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001740 errno = ENOTEMPTY;
1741 return 0;
1742 }
1743
Harald Welteaae69be2004-08-29 23:32:14 +00001744 /* If we are about to delete the chain that is the current
1745 * iterator, move chain iterator firward. */
1746 if (c == (*handle)->chain_iterator_cur)
1747 iptcc_chain_iterator_advance(*handle);
Harald Welte0113fe72004-01-06 19:04:02 +00001748
Harald Welteaae69be2004-08-29 23:32:14 +00001749 list_del(&c->list);
1750 free(c);
Harald Welte0113fe72004-01-06 19:04:02 +00001751
Harald Welteaae69be2004-08-29 23:32:14 +00001752 DEBUGP("chain `%s' deleted\n", chain);
1753
1754 set_changed(*handle);
1755
1756 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001757}
1758
1759/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001760int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1761 const IPT_CHAINLABEL newname,
1762 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001763{
Harald Welteaae69be2004-08-29 23:32:14 +00001764 struct chain_head *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001765 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001766
Harald Welte1de80462000-10-30 12:00:27 +00001767 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1768 QUEUE, RETURN. */
Harald Welteaae69be2004-08-29 23:32:14 +00001769 if (iptcc_find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001770 || strcmp(newname, LABEL_DROP) == 0
1771 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001772 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001773 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001774 errno = EEXIST;
1775 return 0;
1776 }
1777
Harald Welteaae69be2004-08-29 23:32:14 +00001778 if (!(c = iptcc_find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001779 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001780 errno = ENOENT;
1781 return 0;
1782 }
1783
Rusty Russell79dee072000-05-02 16:45:16 +00001784 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001785 errno = EINVAL;
1786 return 0;
1787 }
1788
Harald Welteaae69be2004-08-29 23:32:14 +00001789 strncpy(c->name, newname, sizeof(IPT_CHAINLABEL));
1790
Harald Welte0113fe72004-01-06 19:04:02 +00001791 set_changed(*handle);
1792
Marc Bouchere6869a82000-03-20 06:03:29 +00001793 return 1;
1794}
1795
1796/* Sets the policy on a built-in chain. */
1797int
Rusty Russell79dee072000-05-02 16:45:16 +00001798TC_SET_POLICY(const IPT_CHAINLABEL chain,
1799 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001800 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001801 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001802{
Harald Welteaae69be2004-08-29 23:32:14 +00001803 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001804
Rusty Russell79dee072000-05-02 16:45:16 +00001805 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001806
Harald Welteaae69be2004-08-29 23:32:14 +00001807 if (!(c = iptcc_find_label(chain, *handle))) {
1808 DEBUGP("cannot find chain `%s'\n", chain);
1809 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001810 return 0;
1811 }
1812
Harald Welteaae69be2004-08-29 23:32:14 +00001813 if (!iptcc_is_builtin(c)) {
1814 DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain);
1815 errno = ENOENT;
1816 return 0;
1817 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001818
Rusty Russell79dee072000-05-02 16:45:16 +00001819 if (strcmp(policy, LABEL_ACCEPT) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001820 c->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001821 else if (strcmp(policy, LABEL_DROP) == 0)
Harald Welteaae69be2004-08-29 23:32:14 +00001822 c->verdict = -NF_DROP - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001823 else {
1824 errno = EINVAL;
1825 return 0;
1826 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001827
Harald Welte1cef74d2001-01-05 15:22:59 +00001828 if (counters) {
1829 /* set byte and packet counters */
Harald Welteaae69be2004-08-29 23:32:14 +00001830 memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS));
1831 c->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00001832 } else {
Harald Welteaae69be2004-08-29 23:32:14 +00001833 c->counter_map.maptype = COUNTER_MAP_NOMAP;
Harald Welte1cef74d2001-01-05 15:22:59 +00001834 }
1835
Rusty Russell175f6412000-03-24 09:32:20 +00001836 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001837
Marc Bouchere6869a82000-03-20 06:03:29 +00001838 return 1;
1839}
1840
1841/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001842 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001843 libiptc.c:833: fixed or forbidden register was spilled.
1844 This may be due to a compiler bug or to impossible asm
1845 statements or clauses.
1846*/
1847static void
Rusty Russell79dee072000-05-02 16:45:16 +00001848subtract_counters(STRUCT_COUNTERS *answer,
1849 const STRUCT_COUNTERS *a,
1850 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001851{
1852 answer->pcnt = a->pcnt - b->pcnt;
1853 answer->bcnt = a->bcnt - b->bcnt;
1854}
1855
Harald Welteaae69be2004-08-29 23:32:14 +00001856
1857static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters,
1858 unsigned int index)
1859{
1860 newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0});
1861 DEBUGP_C("NOMAP => zero\n");
1862}
1863
1864static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters,
1865 STRUCT_REPLACE *repl,
1866 unsigned int index,
1867 unsigned int mappos)
1868{
1869 /* Original read: X.
1870 * Atomic read on replacement: X + Y.
1871 * Currently in kernel: Z.
1872 * Want in kernel: X + Y + Z.
1873 * => Add in X + Y
1874 * => Add in replacement read.
1875 */
1876 newcounters->counters[index] = repl->counters[mappos];
1877 DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos);
1878}
1879
1880static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters,
1881 STRUCT_REPLACE *repl,
1882 unsigned int index,
1883 unsigned int mappos,
1884 STRUCT_COUNTERS *counters)
1885{
1886 /* Original read: X.
1887 * Atomic read on replacement: X + Y.
1888 * Currently in kernel: Z.
1889 * Want in kernel: Y + Z.
1890 * => Add in Y.
1891 * => Add in (replacement read - original read).
1892 */
1893 subtract_counters(&newcounters->counters[index],
1894 &repl->counters[mappos],
1895 counters);
1896 DEBUGP_C("ZEROED => mappos %u\n", mappos);
1897}
1898
1899static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters,
1900 unsigned int index,
1901 STRUCT_COUNTERS *counters)
1902{
1903 /* Want to set counter (iptables-restore) */
1904
1905 memcpy(&newcounters->counters[index], counters,
1906 sizeof(STRUCT_COUNTERS));
1907
1908 DEBUGP_C("SET\n");
1909}
1910
1911
Marc Bouchere6869a82000-03-20 06:03:29 +00001912int
Rusty Russell79dee072000-05-02 16:45:16 +00001913TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001914{
1915 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001916 STRUCT_REPLACE *repl;
1917 STRUCT_COUNTERS_INFO *newcounters;
Harald Welteaae69be2004-08-29 23:32:14 +00001918 struct chain_head *c;
1919 int ret;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001920 size_t counterlen;
Harald Welteaae69be2004-08-29 23:32:14 +00001921 int new_number;
1922 unsigned int new_size;
Marc Bouchere6869a82000-03-20 06:03:29 +00001923
1924 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001925
Marc Bouchere6869a82000-03-20 06:03:29 +00001926#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001927 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001928#endif
1929
1930 /* Don't commit if nothing changed. */
1931 if (!(*handle)->changed)
1932 goto finished;
1933
Harald Welteaae69be2004-08-29 23:32:14 +00001934 new_number = iptcc_compile_table_prep(*handle, &new_size);
1935 if (new_number < 0) {
1936 errno = ENOMEM;
1937 return 0;
1938 }
1939
1940 repl = malloc(sizeof(*repl) + new_size);
Marc Bouchere6869a82000-03-20 06:03:29 +00001941 if (!repl) {
1942 errno = ENOMEM;
1943 return 0;
1944 }
Harald Welteaae69be2004-08-29 23:32:14 +00001945 memset(repl, 0, sizeof(*repl));
1946
1947 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1948 + sizeof(STRUCT_COUNTERS) * new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00001949
1950 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001951 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001952 * (*handle)->info.num_entries);
1953 if (!repl->counters) {
1954 free(repl);
1955 errno = ENOMEM;
1956 return 0;
1957 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001958 /* These are the counters we're going to put back, later. */
1959 newcounters = malloc(counterlen);
1960 if (!newcounters) {
1961 free(repl->counters);
1962 free(repl);
1963 errno = ENOMEM;
1964 return 0;
1965 }
Harald Welteaae69be2004-08-29 23:32:14 +00001966 memset(newcounters, 0, counterlen);
Marc Bouchere6869a82000-03-20 06:03:29 +00001967
1968 strcpy(repl->name, (*handle)->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00001969 repl->num_entries = new_number;
1970 repl->size = new_size;
1971
Marc Bouchere6869a82000-03-20 06:03:29 +00001972 repl->num_counters = (*handle)->info.num_entries;
1973 repl->valid_hooks = (*handle)->info.valid_hooks;
Harald Welteaae69be2004-08-29 23:32:14 +00001974
1975 DEBUGP("num_entries=%u, size=%u, num_counters=%u\n",
1976 repl->num_entries, repl->size, repl->num_counters);
1977
1978 ret = iptcc_compile_table(*handle, repl);
1979 if (ret < 0) {
1980 errno = ret;
1981 free(repl->counters);
1982 free(repl);
1983 return 0;
1984 }
1985
1986
1987#ifdef IPTC_DEBUG2
1988 {
1989 int fd = open("/tmp/libiptc-so_set_replace.blob",
1990 O_CREAT|O_WRONLY);
1991 if (fd >= 0) {
1992 write(fd, repl, sizeof(*repl) + repl->size);
1993 close(fd);
1994 }
1995 }
1996#endif
Marc Bouchere6869a82000-03-20 06:03:29 +00001997
Rusty Russell79dee072000-05-02 16:45:16 +00001998 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Harald Welteaae69be2004-08-29 23:32:14 +00001999 sizeof(*repl) + repl->size) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002000 free(repl->counters);
2001 free(repl);
2002 free(newcounters);
2003 return 0;
2004 }
2005
2006 /* Put counters back. */
2007 strcpy(newcounters->name, (*handle)->info.name);
Harald Welteaae69be2004-08-29 23:32:14 +00002008 newcounters->num_counters = new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00002009
Harald Welteaae69be2004-08-29 23:32:14 +00002010 list_for_each_entry(c, &(*handle)->chains, list) {
2011 struct rule_head *r;
Marc Bouchere6869a82000-03-20 06:03:29 +00002012
Harald Welteaae69be2004-08-29 23:32:14 +00002013 /* Builtin chains have their own counters */
2014 if (iptcc_is_builtin(c)) {
2015 DEBUGP("counter for chain-index %u: ", c->foot_index);
2016 switch(c->counter_map.maptype) {
2017 case COUNTER_MAP_NOMAP:
2018 counters_nomap(newcounters, c->foot_index);
2019 break;
2020 case COUNTER_MAP_NORMAL_MAP:
2021 counters_normal_map(newcounters, repl,
2022 c->foot_index,
2023 c->counter_map.mappos);
2024 break;
2025 case COUNTER_MAP_ZEROED:
2026 counters_map_zeroed(newcounters, repl,
2027 c->foot_index,
2028 c->counter_map.mappos,
2029 &c->counters);
2030 break;
2031 case COUNTER_MAP_SET:
2032 counters_map_set(newcounters, c->foot_index,
2033 &c->counters);
2034 break;
2035 }
2036 }
Harald Welte1cef74d2001-01-05 15:22:59 +00002037
Harald Welteaae69be2004-08-29 23:32:14 +00002038 list_for_each_entry(r, &c->rules, list) {
2039 DEBUGP("counter for index %u: ", r->index);
2040 switch (r->counter_map.maptype) {
2041 case COUNTER_MAP_NOMAP:
2042 counters_nomap(newcounters, r->index);
2043 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002044
Harald Welteaae69be2004-08-29 23:32:14 +00002045 case COUNTER_MAP_NORMAL_MAP:
2046 counters_normal_map(newcounters, repl,
2047 r->index,
2048 r->counter_map.mappos);
2049 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00002050
Harald Welteaae69be2004-08-29 23:32:14 +00002051 case COUNTER_MAP_ZEROED:
2052 counters_map_zeroed(newcounters, repl,
2053 r->index,
2054 r->counter_map.mappos,
2055 &r->entry->counters);
2056 break;
2057
2058 case COUNTER_MAP_SET:
2059 counters_map_set(newcounters, r->index,
2060 &r->entry->counters);
2061 break;
2062 }
Marc Bouchere6869a82000-03-20 06:03:29 +00002063 }
2064 }
Rusty Russell62527ce2000-09-04 09:45:54 +00002065
Harald Welteaae69be2004-08-29 23:32:14 +00002066
Rusty Russell62527ce2000-09-04 09:45:54 +00002067#ifdef KERNEL_64_USERSPACE_32
2068 {
2069 /* Kernel will think that pointer should be 64-bits, and get
2070 padding. So we accomodate here (assumption: alignment of
2071 `counters' is on 64-bit boundary). */
2072 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
2073 if ((unsigned long)&newcounters->counters % 8 != 0) {
2074 fprintf(stderr,
2075 "counters alignment incorrect! Mail rusty!\n");
2076 abort();
2077 }
2078 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00002079 }
Rusty Russell62527ce2000-09-04 09:45:54 +00002080#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00002081
Harald Welteaae69be2004-08-29 23:32:14 +00002082#ifdef IPTC_DEBUG2
2083 {
2084 int fd = open("/tmp/libiptc-so_set_add_counters.blob",
2085 O_CREAT|O_WRONLY);
2086 if (fd >= 0) {
2087 write(fd, newcounters, counterlen);
2088 close(fd);
2089 }
2090 }
2091#endif
2092
Rusty Russell79dee072000-05-02 16:45:16 +00002093 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
2094 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00002095 free(repl->counters);
2096 free(repl);
2097 free(newcounters);
2098 return 0;
2099 }
2100
2101 free(repl->counters);
2102 free(repl);
2103 free(newcounters);
2104
2105 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00002106 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00002107 return 1;
2108}
2109
2110/* Get raw socket. */
2111int
Rusty Russell79dee072000-05-02 16:45:16 +00002112TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00002113{
2114 return sockfd;
2115}
2116
2117/* Translates errno numbers into more human-readable form than strerror. */
2118const char *
Rusty Russell79dee072000-05-02 16:45:16 +00002119TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00002120{
2121 unsigned int i;
2122 struct table_struct {
2123 void *fn;
2124 int err;
2125 const char *message;
2126 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00002127 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002128 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002129 { TC_INIT, ENOENT,
2130 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002131 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
2132 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
2133 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00002134 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00002135 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
2136 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
2137 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
2138 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00002139 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
2140 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00002141 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
2142 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002143 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00002144 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00002145 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002146 { TC_CHECK_PACKET, ENOSYS,
2147 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002148 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00002149 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002150 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00002151 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00002152 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00002153 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00002154 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00002155
2156 { NULL, 0, "Incompatible with this kernel" },
2157 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
2158 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
2159 { NULL, ENOMEM, "Memory allocation problem" },
2160 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00002161 };
2162
2163 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
2164 if ((!table[i].fn || table[i].fn == iptc_fn)
2165 && table[i].err == err)
2166 return table[i].message;
2167 }
2168
2169 return strerror(err);
2170}