1 /* Library which manipulates firewall rules. Version $Revision: 1.40 $ */
3 /* Architecture of firewall rules is as follows:
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.
11 /* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12 * COPYING for details).
13 * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
15 * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
16 * - Reimplementation of chain cache to use offsets instead of entries
17 * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
18 * - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
19 * don't rebuild the chain cache after every operation, instead fix it
20 * up after a ruleset change.
24 #define IPT_LIB_DIR "/usr/lib/iptables"
29 GET_TARGET(STRUCT_ENTRY *e)
31 return (void *)e + e->target_offset;
35 static int sockfd = -1;
36 static void *iptc_fn = NULL;
38 static const char *hooknames[]
39 = { [HOOK_PRE_ROUTING] "PREROUTING",
40 [HOOK_LOCAL_IN] "INPUT",
41 [HOOK_FORWARD] "FORWARD",
42 [HOOK_LOCAL_OUT] "OUTPUT",
43 [HOOK_POST_ROUTING] "POSTROUTING",
45 [HOOK_DROPPING] "DROPPING"
53 COUNTER_MAP_NORMAL_MAP,
60 /* Convenience structures */
61 struct ipt_error_target
63 STRUCT_ENTRY_TARGET t;
64 char error[TABLE_MAXNAMELEN];
69 char name[TABLE_MAXNAMELEN];
70 /* This is the first rule in chain. */
71 unsigned int start_off;
72 /* Last rule in chain */
78 /* Have changes been made? */
80 /* Size in here reflects original state. */
83 struct counter_map *counter_map;
84 /* Array of hook names */
85 const char **hooknames;
87 /* Cached position of chain heads (NULL = no cache). */
88 unsigned int cache_num_chains;
89 unsigned int cache_num_builtins;
90 struct chain_cache *cache_chain_heads;
92 /* Chain iterator: current chain cache entry. */
93 struct chain_cache *cache_chain_iteration;
95 /* Rule iterator: terminal rule */
96 STRUCT_ENTRY *cache_rule_end;
98 /* Number in here reflects current state. */
99 unsigned int new_number;
100 STRUCT_GET_ENTRIES entries;
104 set_changed(TC_HANDLE_T h)
110 static void do_check(TC_HANDLE_T h, unsigned int line);
111 #define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
117 get_number(const STRUCT_ENTRY *i,
118 const STRUCT_ENTRY *seek,
128 entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
130 unsigned int pos = 0;
132 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
133 get_number, seek, &pos) == 0) {
134 fprintf(stderr, "ERROR: offset %i not an entry!\n",
135 (char *)seek - (char *)h->entries.entrytable);
142 get_entry_n(STRUCT_ENTRY *i,
147 if (*pos == number) {
155 static STRUCT_ENTRY *
156 index2entry(TC_HANDLE_T h, unsigned int index)
158 unsigned int pos = 0;
159 STRUCT_ENTRY *ret = NULL;
161 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
162 get_entry_n, index, &pos, &ret);
167 static inline STRUCT_ENTRY *
168 get_entry(TC_HANDLE_T h, unsigned int offset)
170 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
173 static inline unsigned long
174 entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
176 return (void *)e - (void *)h->entries.entrytable;
179 static inline unsigned long
180 index2offset(TC_HANDLE_T h, unsigned int index)
182 return entry2offset(h, index2entry(h, index));
185 static inline STRUCT_ENTRY *
186 offset2entry(TC_HANDLE_T h, unsigned int offset)
188 return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
191 static inline unsigned int
192 offset2index(const TC_HANDLE_T h, unsigned int offset)
194 return entry2index(h, offset2entry(h, offset));
199 get_errorlabel(TC_HANDLE_T h, unsigned int offset)
203 e = get_entry(h, offset);
204 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
205 fprintf(stderr, "ERROR: offset %u not an error node!\n",
210 return (const char *)GET_TARGET(e)->data;
213 /* Allocate handle of given size */
215 alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
220 len = sizeof(STRUCT_TC_HANDLE)
222 + num_rules * sizeof(struct counter_map);
224 if ((h = malloc(len)) == NULL) {
230 h->cache_num_chains = 0;
231 h->cache_chain_heads = NULL;
232 h->counter_map = (void *)h
233 + sizeof(STRUCT_TC_HANDLE)
235 strcpy(h->info.name, tablename);
236 strcpy(h->entries.name, tablename);
242 TC_INIT(const char *tablename)
257 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
262 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
268 strcpy(info.name, tablename);
269 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
272 if ((h = alloc_handle(info.name, info.size, info.num_entries))
281 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
282 dynlib = dlopen(pathname, RTLD_NOW);
287 h->hooknames = dlsym(dynlib, "hooknames");
293 h->hooknames = hooknames;
296 /* Initialize current state */
298 h->new_number = h->info.num_entries;
299 for (i = 0; i < h->info.num_entries; i++)
301 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
303 h->entries.size = h->info.size;
305 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
307 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
320 TC_FREE(TC_HANDLE_T *h)
324 if ((*h)->cache_chain_heads)
325 free((*h)->cache_chain_heads);
331 print_match(const STRUCT_ENTRY_MATCH *m)
333 printf("Match name: `%s'\n", m->u.user.name);
337 static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
340 TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
344 printf("libiptc v%s. %u entries, %u bytes.\n",
346 handle->new_number, handle->entries.size);
347 printf("Table `%s'\n", handle->info.name);
348 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
349 handle->info.hook_entry[HOOK_PRE_ROUTING],
350 handle->info.hook_entry[HOOK_LOCAL_IN],
351 handle->info.hook_entry[HOOK_FORWARD],
352 handle->info.hook_entry[HOOK_LOCAL_OUT],
353 handle->info.hook_entry[HOOK_POST_ROUTING]);
354 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
355 handle->info.underflow[HOOK_PRE_ROUTING],
356 handle->info.underflow[HOOK_LOCAL_IN],
357 handle->info.underflow[HOOK_FORWARD],
358 handle->info.underflow[HOOK_LOCAL_OUT],
359 handle->info.underflow[HOOK_POST_ROUTING]);
361 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
365 /* Returns 0 if not hook entry, else hooknumber + 1 */
366 static inline unsigned int
367 is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
371 for (i = 0; i < NUMHOOKS; i++) {
372 if ((h->info.valid_hooks & (1 << i))
373 && get_entry(h, h->info.hook_entry[i]) == e)
380 add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
382 unsigned int builtin;
384 /* Last entry. End it. */
385 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
386 /* This is the ERROR node at end of the table */
387 h->cache_chain_heads[h->cache_num_chains-1].end_off =
388 entry2offset(h, *prev);
392 /* We know this is the start of a new chain if it's an ERROR
393 target, or a hook entry point */
394 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
395 /* prev was last entry in previous chain */
396 h->cache_chain_heads[h->cache_num_chains-1].end_off
397 = entry2offset(h, *prev);
399 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
400 (const char *)GET_TARGET(e)->data);
401 h->cache_chain_heads[h->cache_num_chains].start_off
402 = entry2offset(h, (void *)e + e->next_offset);
403 h->cache_num_chains++;
404 } else if ((builtin = is_hook_entry(e, h)) != 0) {
405 if (h->cache_num_chains > 0)
406 /* prev was last entry in previous chain */
407 h->cache_chain_heads[h->cache_num_chains-1].end_off
408 = entry2offset(h, *prev);
410 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
411 h->hooknames[builtin-1]);
412 h->cache_chain_heads[h->cache_num_chains].start_off
413 = entry2offset(h, (void *)e);
414 h->cache_num_chains++;
421 static int alphasort(const void *a, const void *b)
423 return strcmp(((struct chain_cache *)a)->name,
424 ((struct chain_cache *)b)->name);
427 static int populate_cache(TC_HANDLE_T h)
432 /* # chains < # rules / 2 + num builtins - 1 */
433 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
434 * sizeof(struct chain_cache));
435 if (!h->cache_chain_heads) {
440 h->cache_num_chains = 0;
441 h->cache_num_builtins = 0;
444 for (i = 0; i < NUMHOOKS; i++) {
445 if (h->info.valid_hooks & (1 << i))
446 h->cache_num_builtins++;
450 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
451 add_chain, h, &prev);
453 qsort(h->cache_chain_heads + h->cache_num_builtins,
454 h->cache_num_chains - h->cache_num_builtins,
455 sizeof(struct chain_cache), alphasort);
461 correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
463 int i; /* needs to be signed because deleting first
464 chain can make it drop to -1 */
469 for (i = 0; i < h->cache_num_chains; i++) {
470 struct chain_cache *cc = &h->cache_chain_heads[i];
473 /* take care about deleted chains */
474 if (cc->start_off >= offset+delta
475 && cc->end_off <= offset) {
476 /* this chain is within the deleted range,
477 * let's remove it from the cache */
481 h->cache_num_chains--;
482 if (i+1 >= h->cache_num_chains)
484 start = &h->cache_chain_heads[i+1];
485 size = (h->cache_num_chains-i)
486 * sizeof(struct chain_cache);
487 memmove(cc, start, size);
489 /* iterate over same index again, since
490 * it is now a different chain */
496 if (cc->start_off > offset)
497 cc->start_off += delta;
499 if (cc->end_off >= offset)
500 cc->end_off += delta;
502 /* HW_FIXME: sorting might be needed, but just in case a new chain was
509 add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
510 unsigned int end_off)
512 struct chain_cache *ccs = realloc(h->cache_chain_heads,
513 (h->new_number / 2 + 4 + 1)
514 * sizeof(struct chain_cache));
515 struct chain_cache *newcc;
520 h->cache_chain_heads = ccs;
521 newcc = &h->cache_chain_heads[h->cache_num_chains];
522 h->cache_num_chains++;
524 strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
525 newcc->start_off = start_off;
526 newcc->end_off = end_off;
531 /* Returns cache ptr if found, otherwise NULL. */
532 static struct chain_cache *
533 find_label(const char *name, TC_HANDLE_T handle)
537 if (handle->cache_chain_heads == NULL
538 && !populate_cache(handle))
541 /* FIXME: Linear search through builtins, then binary --RR */
542 for (i = 0; i < handle->cache_num_chains; i++) {
543 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
544 return &handle->cache_chain_heads[i];
550 /* Does this chain exist? */
551 int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
553 return find_label(chain, handle) != NULL;
556 /* Returns the position of the final (ie. unconditional) element. */
558 get_chain_end(const TC_HANDLE_T handle, unsigned int start)
560 unsigned int last_off, off;
564 e = get_entry(handle, start);
566 /* Terminate when we meet a error label or a hook entry. */
567 for (off = start + e->next_offset;
568 off < handle->entries.size;
569 last_off = off, off += e->next_offset) {
570 STRUCT_ENTRY_TARGET *t;
573 e = get_entry(handle, off);
575 /* We hit an entry point. */
576 for (i = 0; i < NUMHOOKS; i++) {
577 if ((handle->info.valid_hooks & (1 << i))
578 && off == handle->info.hook_entry[i])
582 /* We hit a user chain label */
584 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
587 /* SHOULD NEVER HAPPEN */
588 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
589 handle->entries.size, off);
593 /* Iterator functions to run through the chains. */
595 TC_FIRST_CHAIN(TC_HANDLE_T *handle)
597 if ((*handle)->cache_chain_heads == NULL
598 && !populate_cache(*handle))
601 (*handle)->cache_chain_iteration
602 = &(*handle)->cache_chain_heads[0];
604 return (*handle)->cache_chain_iteration->name;
607 /* Iterator functions to run through the chains. Returns NULL at end. */
609 TC_NEXT_CHAIN(TC_HANDLE_T *handle)
611 (*handle)->cache_chain_iteration++;
613 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
614 == (*handle)->cache_num_chains)
617 return (*handle)->cache_chain_iteration->name;
620 /* Get first rule in the given chain: NULL for empty chain. */
622 TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
624 struct chain_cache *c;
626 c = find_label(chain, *handle);
632 /* Empty chain: single return/policy rule */
633 if (c->start_off == c->end_off)
636 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
637 return offset2entry(*handle, c->start_off);
640 /* Returns NULL when rules run out. */
642 TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
644 if ((void *)prev + prev->next_offset
645 == (void *)(*handle)->cache_rule_end)
648 return (void *)prev + prev->next_offset;
652 /* How many rules in this chain? */
654 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
656 unsigned int off = 0;
657 STRUCT_ENTRY *start, *end;
660 if (!find_label(&off, chain, *handle)) {
662 return (unsigned int)-1;
665 start = get_entry(*handle, off);
666 end = get_entry(*handle, get_chain_end(*handle, off));
668 return entry2index(*handle, end) - entry2index(*handle, start);
671 /* Get n'th rule in this chain. */
672 const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
676 unsigned int pos = 0, chainindex;
679 if (!find_label(&pos, chain, *handle)) {
684 chainindex = entry2index(*handle, get_entry(*handle, pos));
686 return index2entry(*handle, chainindex + n);
691 target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
694 unsigned int labelidx;
695 STRUCT_ENTRY *jumpto;
697 /* To avoid const warnings */
698 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
700 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
701 return GET_TARGET(e)->u.user.name;
703 /* Standard target: evaluate */
704 spos = *(int *)GET_TARGET(e)->data;
708 else if (spos == -NF_ACCEPT-1)
710 else if (spos == -NF_DROP-1)
712 else if (spos == -NF_QUEUE-1)
715 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
716 entry2offset(handle, e), handle->entries.size,
721 jumpto = get_entry(handle, spos);
723 /* Fall through rule */
724 if (jumpto == (void *)e + e->next_offset)
727 /* Must point to head of a chain: ie. after error rule */
728 labelidx = entry2index(handle, jumpto) - 1;
729 return get_errorlabel(handle, index2offset(handle, labelidx));
732 /* Returns a pointer to the target name of this position. */
733 const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
736 return target_name(*handle, e);
739 /* Is this a built-in chain? Actually returns hook + 1. */
741 TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
745 for (i = 0; i < NUMHOOKS; i++) {
746 if ((handle->info.valid_hooks & (1 << i))
747 && handle->hooknames[i]
748 && strcmp(handle->hooknames[i], chain) == 0)
754 /* Get the policy of a given built-in chain */
756 TC_GET_POLICY(const char *chain,
757 STRUCT_COUNTERS *counters,
764 hook = TC_BUILTIN(chain, *handle);
766 start = (*handle)->info.hook_entry[hook-1];
770 e = get_entry(*handle, get_chain_end(*handle, start));
771 *counters = e->counters;
773 return target_name(*handle, e);
777 correct_verdict(STRUCT_ENTRY *e,
779 unsigned int offset, int delta_offset)
781 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
782 unsigned int curr = (char *)e - base;
784 /* Trap: insert of fall-through rule. Don't change fall-through
785 verdict to jump-over-next-rule. */
786 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
787 && t->verdict > (int)offset
788 && !(curr == offset &&
789 t->verdict == curr + e->next_offset)) {
790 t->verdict += delta_offset;
796 /* Adjusts standard verdict jump positions after an insertion/deletion. */
798 set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
800 ENTRY_ITERATE((*handle)->entries.entrytable,
801 (*handle)->entries.size,
802 correct_verdict, (char *)(*handle)->entries.entrytable,
803 offset, delta_offset);
805 set_changed(*handle);
809 /* If prepend is set, then we are prepending to a chain: if the
810 * insertion position is an entry point, keep the entry point. */
812 insert_rules(unsigned int num_rules, unsigned int rules_size,
813 const STRUCT_ENTRY *insert,
814 unsigned int offset, unsigned int num_rules_offset,
819 STRUCT_GETINFO newinfo;
822 if (offset >= (*handle)->entries.size) {
827 newinfo = (*handle)->info;
829 /* Fix up entry points. */
830 for (i = 0; i < NUMHOOKS; i++) {
831 /* Entry points to START of chain, so keep same if
832 inserting on at that point. */
833 if ((*handle)->info.hook_entry[i] > offset)
834 newinfo.hook_entry[i] += rules_size;
836 /* Underflow always points to END of chain (policy),
837 so if something is inserted at same point, it
838 should be advanced. */
839 if ((*handle)->info.underflow[i] >= offset)
840 newinfo.underflow[i] += rules_size;
843 newh = alloc_handle((*handle)->info.name,
844 (*handle)->entries.size + rules_size,
845 (*handle)->new_number + num_rules);
848 newh->info = newinfo;
851 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
852 /* ... Insert new ... */
853 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
855 memcpy((char *)newh->entries.entrytable + offset + rules_size,
856 (char *)(*handle)->entries.entrytable + offset,
857 (*handle)->entries.size - offset);
859 /* Move counter map. */
861 memcpy(newh->counter_map, (*handle)->counter_map,
862 sizeof(struct counter_map) * num_rules_offset);
864 memcpy(newh->counter_map + num_rules_offset + num_rules,
865 (*handle)->counter_map + num_rules_offset,
866 sizeof(struct counter_map) * ((*handle)->new_number
867 - num_rules_offset));
868 /* Set intermediates to no counter copy */
869 for (i = 0; i < num_rules; i++)
870 newh->counter_map[num_rules_offset+i]
871 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
873 newh->new_number = (*handle)->new_number + num_rules;
874 newh->entries.size = (*handle)->entries.size + rules_size;
875 newh->hooknames = (*handle)->hooknames;
877 newh->cache_chain_heads = (*handle)->cache_chain_heads;
878 newh->cache_num_builtins = (*handle)->cache_num_builtins;
879 newh->cache_num_chains = (*handle)->cache_num_chains;
880 newh->cache_rule_end = (*handle)->cache_rule_end;
881 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
882 if (!correct_cache(newh, offset, rules_size)) {
890 return set_verdict(offset, rules_size, handle);
894 delete_rules(unsigned int num_rules, unsigned int rules_size,
895 unsigned int offset, unsigned int num_rules_offset,
900 if (offset + rules_size > (*handle)->entries.size) {
905 /* Fix up entry points. */
906 for (i = 0; i < NUMHOOKS; i++) {
907 /* In practice, we never delete up to a hook entry,
908 since the built-in chains are always first,
909 so these two are never equal */
910 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
911 (*handle)->info.hook_entry[i] -= rules_size;
912 else if ((*handle)->info.hook_entry[i] > offset) {
913 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
914 i, (*handle)->info.hook_entry[i], offset);
918 /* Underflow points to policy (terminal) rule in
919 built-in, so sequality is valid here (when deleting
921 if ((*handle)->info.underflow[i] >= offset + rules_size)
922 (*handle)->info.underflow[i] -= rules_size;
923 else if ((*handle)->info.underflow[i] > offset) {
924 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
925 i, (*handle)->info.underflow[i], offset);
930 /* Move the rules down. */
931 memmove((char *)(*handle)->entries.entrytable + offset,
932 (char *)(*handle)->entries.entrytable + offset + rules_size,
933 (*handle)->entries.size - (offset + rules_size));
935 /* Move the counter map down. */
936 memmove(&(*handle)->counter_map[num_rules_offset],
937 &(*handle)->counter_map[num_rules_offset + num_rules],
938 sizeof(struct counter_map)
939 * ((*handle)->new_number - (num_rules + num_rules_offset)));
942 (*handle)->new_number -= num_rules;
943 (*handle)->entries.size -= rules_size;
945 /* Fix the chain cache */
946 if (!correct_cache(*handle, offset, -(int)rules_size))
949 return set_verdict(offset, -(int)rules_size, handle);
953 standard_map(STRUCT_ENTRY *e, int verdict)
955 STRUCT_STANDARD_TARGET *t;
957 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
959 if (t->target.u.target_size
960 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
964 /* memset for memcmp convenience on delete/replace */
965 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
966 strcpy(t->target.u.user.name, STANDARD_TARGET);
967 t->verdict = verdict;
973 map_target(const TC_HANDLE_T handle,
976 STRUCT_ENTRY_TARGET *old)
978 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
980 /* Save old target (except data, which we don't change, except for
981 standard case, where we don't care). */
984 /* Maybe it's empty (=> fall through) */
985 if (strcmp(t->u.user.name, "") == 0)
986 return standard_map(e, offset + e->next_offset);
987 /* Maybe it's a standard target name... */
988 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
989 return standard_map(e, -NF_ACCEPT - 1);
990 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
991 return standard_map(e, -NF_DROP - 1);
992 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
993 return standard_map(e, -NF_QUEUE - 1);
994 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
995 return standard_map(e, RETURN);
996 else if (TC_BUILTIN(t->u.user.name, handle)) {
997 /* Can't jump to builtins. */
1001 /* Maybe it's an existing chain name. */
1002 struct chain_cache *c;
1004 c = find_label(t->u.user.name, handle);
1006 return standard_map(e, c->start_off);
1009 /* Must be a module? If not, kernel will reject... */
1010 /* memset to all 0 for your memcmp convenience. */
1011 memset(t->u.user.name + strlen(t->u.user.name),
1013 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
1018 unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
1020 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
1022 /* Save old target (except data, which we don't change, except for
1023 standard case, where we don't care). */
1027 /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
1029 TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1030 const STRUCT_ENTRY *e,
1031 unsigned int rulenum,
1032 TC_HANDLE_T *handle)
1034 unsigned int chainindex, offset;
1035 STRUCT_ENTRY_TARGET old;
1036 struct chain_cache *c;
1040 iptc_fn = TC_INSERT_ENTRY;
1041 if (!(c = find_label(chain, *handle))) {
1046 chainindex = offset2index(*handle, c->start_off);
1048 tmp = index2entry(*handle, chainindex + rulenum);
1049 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
1053 offset = index2offset(*handle, chainindex + rulenum);
1055 /* Mapping target actually alters entry, but that's
1056 transparent to the caller. */
1057 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1060 ret = insert_rules(1, e->next_offset, e, offset,
1061 chainindex + rulenum, rulenum == 0, handle);
1062 unmap_target((STRUCT_ENTRY *)e, &old);
1066 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
1068 TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1069 const STRUCT_ENTRY *e,
1070 unsigned int rulenum,
1071 TC_HANDLE_T *handle)
1073 unsigned int chainindex, offset;
1074 STRUCT_ENTRY_TARGET old;
1075 struct chain_cache *c;
1079 iptc_fn = TC_REPLACE_ENTRY;
1081 if (!(c = find_label(chain, *handle))) {
1086 chainindex = offset2index(*handle, c->start_off);
1088 tmp = index2entry(*handle, chainindex + rulenum);
1089 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
1094 offset = index2offset(*handle, chainindex + rulenum);
1095 /* Replace = delete and insert. */
1096 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1097 offset, chainindex + rulenum, handle))
1100 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1103 ret = insert_rules(1, e->next_offset, e, offset,
1104 chainindex + rulenum, 1, handle);
1105 unmap_target((STRUCT_ENTRY *)e, &old);
1109 /* Append entry `fw' to chain `chain'. Equivalent to insert with
1110 rulenum = length of chain. */
1112 TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1113 const STRUCT_ENTRY *e,
1114 TC_HANDLE_T *handle)
1116 struct chain_cache *c;
1117 STRUCT_ENTRY_TARGET old;
1120 iptc_fn = TC_APPEND_ENTRY;
1121 if (!(c = find_label(chain, *handle))) {
1126 if (!map_target(*handle, (STRUCT_ENTRY *)e,
1130 ret = insert_rules(1, e->next_offset, e, c->end_off,
1131 offset2index(*handle, c->end_off), 0, handle);
1132 unmap_target((STRUCT_ENTRY *)e, &old);
1137 match_different(const STRUCT_ENTRY_MATCH *a,
1138 const unsigned char *a_elems,
1139 const unsigned char *b_elems,
1140 unsigned char **maskptr)
1142 const STRUCT_ENTRY_MATCH *b;
1145 /* Offset of b is the same as a. */
1146 b = (void *)b_elems + ((unsigned char *)a - a_elems);
1148 if (a->u.match_size != b->u.match_size)
1151 if (strcmp(a->u.user.name, b->u.user.name) != 0)
1154 *maskptr += ALIGN(sizeof(*a));
1156 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
1157 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
1164 target_different(const unsigned char *a_targdata,
1165 const unsigned char *b_targdata,
1166 unsigned int tdatasize,
1167 const unsigned char *mask)
1170 for (i = 0; i < tdatasize; i++)
1171 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1178 is_same(const STRUCT_ENTRY *a,
1179 const STRUCT_ENTRY *b,
1180 unsigned char *matchmask);
1182 /* Delete the first rule in `chain' which matches `fw'. */
1184 TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1185 const STRUCT_ENTRY *origfw,
1186 unsigned char *matchmask,
1187 TC_HANDLE_T *handle)
1189 unsigned int offset;
1190 struct chain_cache *c;
1191 STRUCT_ENTRY *e, *fw;
1193 iptc_fn = TC_DELETE_ENTRY;
1194 if (!(c = find_label(chain, *handle))) {
1199 fw = malloc(origfw->next_offset);
1205 for (offset = c->start_off; offset < c->end_off;
1206 offset += e->next_offset) {
1207 STRUCT_ENTRY_TARGET discard;
1209 memcpy(fw, origfw, origfw->next_offset);
1211 /* FIXME: handle this in is_same --RR */
1212 if (!map_target(*handle, fw, offset, &discard)) {
1216 e = get_entry(*handle, offset);
1219 printf("Deleting:\n");
1222 if (is_same(e, fw, matchmask)) {
1224 ret = delete_rules(1, e->next_offset,
1225 offset, entry2index(*handle, e),
1237 /* Delete the rule in position `rulenum' in `chain'. */
1239 TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1240 unsigned int rulenum,
1241 TC_HANDLE_T *handle)
1246 struct chain_cache *c;
1248 iptc_fn = TC_DELETE_NUM_ENTRY;
1249 if (!(c = find_label(chain, *handle))) {
1254 index = offset2index(*handle, c->start_off) + rulenum;
1256 if (index >= offset2index(*handle, c->end_off)) {
1261 e = index2entry(*handle, index);
1267 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1272 /* Check the packet `fw' on chain `chain'. Returns the verdict, or
1273 NULL and sets errno. */
1275 TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1276 STRUCT_ENTRY *entry,
1277 TC_HANDLE_T *handle)
1283 /* Flushes the entries in the given chain (ie. empties chain). */
1285 TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1287 unsigned int startindex, endindex;
1288 STRUCT_ENTRY *startentry, *endentry;
1289 struct chain_cache *c;
1292 iptc_fn = TC_FLUSH_ENTRIES;
1293 if (!(c = find_label(chain, *handle))) {
1297 startindex = offset2index(*handle, c->start_off);
1298 endindex = offset2index(*handle, c->end_off);
1299 startentry = offset2entry(*handle, c->start_off);
1300 endentry = offset2entry(*handle, c->end_off);
1302 ret = delete_rules(endindex - startindex,
1303 (char *)endentry - (char *)startentry,
1304 c->start_off, startindex,
1309 /* Zeroes the counters in a chain. */
1311 TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1313 unsigned int i, end;
1314 struct chain_cache *c;
1316 if (!(c = find_label(chain, *handle))) {
1321 i = offset2index(*handle, c->start_off);
1322 end = offset2index(*handle, c->end_off);
1324 for (; i <= end; i++) {
1325 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1326 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1328 set_changed(*handle);
1334 TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1335 unsigned int rulenum,
1336 TC_HANDLE_T *handle)
1339 struct chain_cache *c;
1340 unsigned int chainindex, end;
1342 iptc_fn = TC_READ_COUNTER;
1345 if (!(c = find_label(chain, *handle))) {
1350 chainindex = offset2index(*handle, c->start_off);
1351 end = offset2index(*handle, c->end_off);
1353 if (chainindex + rulenum > end) {
1358 e = index2entry(*handle, chainindex + rulenum);
1360 return &e->counters;
1364 TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1365 unsigned int rulenum,
1366 TC_HANDLE_T *handle)
1369 struct chain_cache *c;
1370 unsigned int chainindex, end;
1372 iptc_fn = TC_ZERO_COUNTER;
1375 if (!(c = find_label(chain, *handle))) {
1380 chainindex = offset2index(*handle, c->start_off);
1381 end = offset2index(*handle, c->end_off);
1383 if (chainindex + rulenum > end) {
1388 e = index2entry(*handle, chainindex + rulenum);
1390 if ((*handle)->counter_map[chainindex + rulenum].maptype
1391 == COUNTER_MAP_NORMAL_MAP) {
1392 (*handle)->counter_map[chainindex + rulenum].maptype
1393 = COUNTER_MAP_ZEROED;
1396 set_changed(*handle);
1402 TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1403 unsigned int rulenum,
1404 STRUCT_COUNTERS *counters,
1405 TC_HANDLE_T *handle)
1408 struct chain_cache *c;
1409 unsigned int chainindex, end;
1411 iptc_fn = TC_SET_COUNTER;
1414 if (!(c = find_label(chain, *handle))) {
1419 chainindex = offset2index(*handle, c->start_off);
1420 end = offset2index(*handle, c->end_off);
1422 if (chainindex + rulenum > end) {
1427 e = index2entry(*handle, chainindex + rulenum);
1429 (*handle)->counter_map[chainindex + rulenum].maptype
1432 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1434 set_changed(*handle);
1439 /* Creates a new chain. */
1440 /* To create a chain, create two rules: error node and unconditional
1443 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1448 struct ipt_error_target name;
1450 STRUCT_STANDARD_TARGET target;
1452 unsigned int destination;
1454 iptc_fn = TC_CREATE_CHAIN;
1456 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1458 if (find_label(chain, *handle)
1459 || strcmp(chain, LABEL_DROP) == 0
1460 || strcmp(chain, LABEL_ACCEPT) == 0
1461 || strcmp(chain, LABEL_QUEUE) == 0
1462 || strcmp(chain, LABEL_RETURN) == 0) {
1467 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
1472 memset(&newc, 0, sizeof(newc));
1473 newc.head.target_offset = sizeof(STRUCT_ENTRY);
1474 newc.head.next_offset
1475 = sizeof(STRUCT_ENTRY)
1476 + ALIGN(sizeof(struct ipt_error_target));
1477 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
1478 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
1479 strcpy(newc.name.error, chain);
1481 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
1482 newc.ret.next_offset
1483 = sizeof(STRUCT_ENTRY)
1484 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1485 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
1486 newc.target.target.u.target_size
1487 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1488 newc.target.verdict = RETURN;
1490 destination = index2offset(*handle, (*handle)->new_number -1);
1492 /* Add just before terminal entry */
1493 ret = insert_rules(2, sizeof(newc), &newc.head,
1495 (*handle)->new_number - 1,
1498 set_changed(*handle);
1500 /* add chain cache info for this chain */
1501 add_chain_cache(*handle, chain,
1502 destination+newc.head.next_offset,
1503 destination+newc.head.next_offset);
1509 count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
1511 STRUCT_STANDARD_TARGET *t;
1513 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1514 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1516 if (t->verdict == offset)
1523 /* Get the number of references to this chain. */
1525 TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1526 TC_HANDLE_T *handle)
1528 struct chain_cache *c;
1530 if (!(c = find_label(chain, *handle))) {
1536 ENTRY_ITERATE((*handle)->entries.entrytable,
1537 (*handle)->entries.size,
1538 count_ref, c->start_off, ref);
1542 /* Deletes a chain. */
1544 TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1546 unsigned int labelidx, labeloff;
1547 unsigned int references;
1548 struct chain_cache *c;
1550 STRUCT_ENTRY *start;
1552 if (!TC_GET_REFERENCES(&references, chain, handle))
1555 iptc_fn = TC_DELETE_CHAIN;
1557 if (TC_BUILTIN(chain, *handle)) {
1562 if (references > 0) {
1567 if (!(c = find_label(chain, *handle))) {
1572 if (c->start_off != c->end_off) {
1577 /* Need label index: preceeds chain start */
1578 labelidx = offset2index(*handle, c->start_off) - 1;
1579 labeloff = index2offset(*handle, labelidx);
1581 start = offset2entry(*handle, c->start_off);
1583 ret = delete_rules(2,
1584 get_entry(*handle, labeloff)->next_offset
1585 + start->next_offset,
1586 labeloff, labelidx, handle);
1590 /* Renames a chain. */
1591 int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1592 const IPT_CHAINLABEL newname,
1593 TC_HANDLE_T *handle)
1595 unsigned int labeloff, labelidx;
1596 struct chain_cache *c;
1597 struct ipt_error_target *t;
1599 iptc_fn = TC_RENAME_CHAIN;
1601 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1603 if (find_label(newname, *handle)
1604 || strcmp(newname, LABEL_DROP) == 0
1605 || strcmp(newname, LABEL_ACCEPT) == 0
1606 || strcmp(newname, LABEL_QUEUE) == 0
1607 || strcmp(newname, LABEL_RETURN) == 0) {
1612 if (!(c = find_label(oldname, *handle))
1613 || TC_BUILTIN(oldname, *handle)) {
1618 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
1623 /* Need label index: preceeds chain start */
1624 labelidx = offset2index(*handle, c->start_off) - 1;
1625 labeloff = index2offset(*handle, labelidx);
1627 t = (struct ipt_error_target *)
1628 GET_TARGET(get_entry(*handle, labeloff));
1630 memset(t->error, 0, sizeof(t->error));
1631 strcpy(t->error, newname);
1633 /* update chain cache */
1634 memset(c->name, 0, sizeof(c->name));
1635 strcpy(c->name, newname);
1637 set_changed(*handle);
1642 /* Sets the policy on a built-in chain. */
1644 TC_SET_POLICY(const IPT_CHAINLABEL chain,
1645 const IPT_CHAINLABEL policy,
1646 STRUCT_COUNTERS *counters,
1647 TC_HANDLE_T *handle)
1650 unsigned int policyoff, ctrindex;
1652 STRUCT_STANDARD_TARGET *t;
1654 iptc_fn = TC_SET_POLICY;
1655 /* Figure out which chain. */
1656 hook = TC_BUILTIN(chain, *handle);
1663 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1664 if (policyoff != (*handle)->info.underflow[hook]) {
1665 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1666 chain, policyoff, (*handle)->info.underflow[hook]);
1670 e = get_entry(*handle, policyoff);
1671 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1673 if (strcmp(policy, LABEL_ACCEPT) == 0)
1674 t->verdict = -NF_ACCEPT - 1;
1675 else if (strcmp(policy, LABEL_DROP) == 0)
1676 t->verdict = -NF_DROP - 1;
1682 ctrindex = entry2index(*handle, e);
1685 /* set byte and packet counters */
1686 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1688 (*handle)->counter_map[ctrindex].maptype
1692 (*handle)->counter_map[ctrindex]
1693 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1696 set_changed(*handle);
1701 /* Without this, on gcc 2.7.2.3, we get:
1702 libiptc.c: In function `TC_COMMIT':
1703 libiptc.c:833: fixed or forbidden register was spilled.
1704 This may be due to a compiler bug or to impossible asm
1705 statements or clauses.
1708 subtract_counters(STRUCT_COUNTERS *answer,
1709 const STRUCT_COUNTERS *a,
1710 const STRUCT_COUNTERS *b)
1712 answer->pcnt = a->pcnt - b->pcnt;
1713 answer->bcnt = a->bcnt - b->bcnt;
1717 TC_COMMIT(TC_HANDLE_T *handle)
1719 /* Replace, then map back the counters. */
1720 STRUCT_REPLACE *repl;
1721 STRUCT_COUNTERS_INFO *newcounters;
1727 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1728 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1731 TC_DUMP_ENTRIES(*handle);
1734 /* Don't commit if nothing changed. */
1735 if (!(*handle)->changed)
1738 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1744 /* These are the old counters we will get from kernel */
1745 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
1746 * (*handle)->info.num_entries);
1747 if (!repl->counters) {
1753 /* These are the counters we're going to put back, later. */
1754 newcounters = malloc(counterlen);
1756 free(repl->counters);
1762 strcpy(repl->name, (*handle)->info.name);
1763 repl->num_entries = (*handle)->new_number;
1764 repl->size = (*handle)->entries.size;
1765 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1766 sizeof(repl->hook_entry));
1767 memcpy(repl->underflow, (*handle)->info.underflow,
1768 sizeof(repl->underflow));
1769 repl->num_counters = (*handle)->info.num_entries;
1770 repl->valid_hooks = (*handle)->info.valid_hooks;
1771 memcpy(repl->entries, (*handle)->entries.entrytable,
1772 (*handle)->entries.size);
1774 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
1775 sizeof(*repl) + (*handle)->entries.size) < 0) {
1776 free(repl->counters);
1782 /* Put counters back. */
1783 strcpy(newcounters->name, (*handle)->info.name);
1784 newcounters->num_counters = (*handle)->new_number;
1785 for (i = 0; i < (*handle)->new_number; i++) {
1786 unsigned int mappos = (*handle)->counter_map[i].mappos;
1787 switch ((*handle)->counter_map[i].maptype) {
1788 case COUNTER_MAP_NOMAP:
1789 newcounters->counters[i]
1790 = ((STRUCT_COUNTERS){ 0, 0 });
1793 case COUNTER_MAP_NORMAL_MAP:
1794 /* Original read: X.
1795 * Atomic read on replacement: X + Y.
1796 * Currently in kernel: Z.
1797 * Want in kernel: X + Y + Z.
1799 * => Add in replacement read.
1801 newcounters->counters[i] = repl->counters[mappos];
1804 case COUNTER_MAP_ZEROED:
1805 /* Original read: X.
1806 * Atomic read on replacement: X + Y.
1807 * Currently in kernel: Z.
1808 * Want in kernel: Y + Z.
1810 * => Add in (replacement read - original read).
1812 subtract_counters(&newcounters->counters[i],
1813 &repl->counters[mappos],
1814 &index2entry(*handle, i)->counters);
1817 case COUNTER_MAP_SET:
1818 /* Want to set counter (iptables-restore) */
1820 memcpy(&newcounters->counters[i],
1821 &index2entry(*handle, i)->counters,
1822 sizeof(STRUCT_COUNTERS));
1828 #ifdef KERNEL_64_USERSPACE_32
1830 /* Kernel will think that pointer should be 64-bits, and get
1831 padding. So we accomodate here (assumption: alignment of
1832 `counters' is on 64-bit boundary). */
1833 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1834 if ((unsigned long)&newcounters->counters % 8 != 0) {
1836 "counters alignment incorrect! Mail rusty!\n");
1839 *kernptr = newcounters->counters;
1841 #endif /* KERNEL_64_USERSPACE_32 */
1843 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1844 newcounters, counterlen) < 0) {
1845 free(repl->counters);
1851 free(repl->counters);
1860 /* Get raw socket. */
1867 /* Translates errno numbers into more human-readable form than strerror. */
1869 TC_STRERROR(int err)
1872 struct table_struct {
1875 const char *message;
1877 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
1878 { TC_INIT, EINVAL, "Module is wrong version" },
1880 "Table does not exist (do you need to insmod?)" },
1881 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1882 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1883 { TC_DELETE_CHAIN, EMLINK,
1884 "Can't delete chain with references left" },
1885 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1886 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1887 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1888 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
1889 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1890 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
1891 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1892 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
1893 /* EINVAL for CHECK probably means bad interface. */
1894 { TC_CHECK_PACKET, EINVAL,
1895 "Bad arguments (does that interface exist?)" },
1896 { TC_CHECK_PACKET, ENOSYS,
1897 "Checking will most likely never get implemented" },
1898 /* ENOENT for DELETE probably means no matching rule */
1899 { TC_DELETE_ENTRY, ENOENT,
1900 "Bad rule (does a matching rule exist in that chain?)" },
1901 { TC_SET_POLICY, ENOENT,
1902 "Bad built-in chain name" },
1903 { TC_SET_POLICY, EINVAL,
1904 "Bad policy name" },
1906 { NULL, 0, "Incompatible with this kernel" },
1907 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1908 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1909 { NULL, ENOMEM, "Memory allocation problem" },
1910 { NULL, ENOENT, "No chain/target/match by that name" },
1913 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1914 if ((!table[i].fn || table[i].fn == iptc_fn)
1915 && table[i].err == err)
1916 return table[i].message;
1919 return strerror(err);