1 /* Library which manipulates firewall rules. Version $Revision: 1.41 $ */
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--;
483 /* no need for memmove since we are
484 * removing the last entry */
485 if (i >= h->cache_num_chains)
488 start = &h->cache_chain_heads[i+1];
489 size = (h->cache_num_chains-i)
490 * sizeof(struct chain_cache);
491 memmove(cc, start, size);
493 /* iterate over same index again, since
494 * it is now a different chain */
500 if (cc->start_off > offset)
501 cc->start_off += delta;
503 if (cc->end_off >= offset)
504 cc->end_off += delta;
506 /* HW_FIXME: sorting might be needed, but just in case a new chain was
513 add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
514 unsigned int end_off)
516 struct chain_cache *ccs = realloc(h->cache_chain_heads,
517 (h->new_number / 2 + 4 + 1)
518 * sizeof(struct chain_cache));
519 struct chain_cache *newcc;
524 h->cache_chain_heads = ccs;
525 newcc = &h->cache_chain_heads[h->cache_num_chains];
526 h->cache_num_chains++;
528 strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
529 newcc->start_off = start_off;
530 newcc->end_off = end_off;
535 /* Returns cache ptr if found, otherwise NULL. */
536 static struct chain_cache *
537 find_label(const char *name, TC_HANDLE_T handle)
541 if (handle->cache_chain_heads == NULL
542 && !populate_cache(handle))
545 /* FIXME: Linear search through builtins, then binary --RR */
546 for (i = 0; i < handle->cache_num_chains; i++) {
547 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
548 return &handle->cache_chain_heads[i];
554 /* Does this chain exist? */
555 int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
557 return find_label(chain, handle) != NULL;
560 /* Returns the position of the final (ie. unconditional) element. */
562 get_chain_end(const TC_HANDLE_T handle, unsigned int start)
564 unsigned int last_off, off;
568 e = get_entry(handle, start);
570 /* Terminate when we meet a error label or a hook entry. */
571 for (off = start + e->next_offset;
572 off < handle->entries.size;
573 last_off = off, off += e->next_offset) {
574 STRUCT_ENTRY_TARGET *t;
577 e = get_entry(handle, off);
579 /* We hit an entry point. */
580 for (i = 0; i < NUMHOOKS; i++) {
581 if ((handle->info.valid_hooks & (1 << i))
582 && off == handle->info.hook_entry[i])
586 /* We hit a user chain label */
588 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
591 /* SHOULD NEVER HAPPEN */
592 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
593 handle->entries.size, off);
597 /* Iterator functions to run through the chains. */
599 TC_FIRST_CHAIN(TC_HANDLE_T *handle)
601 if ((*handle)->cache_chain_heads == NULL
602 && !populate_cache(*handle))
605 (*handle)->cache_chain_iteration
606 = &(*handle)->cache_chain_heads[0];
608 return (*handle)->cache_chain_iteration->name;
611 /* Iterator functions to run through the chains. Returns NULL at end. */
613 TC_NEXT_CHAIN(TC_HANDLE_T *handle)
615 (*handle)->cache_chain_iteration++;
617 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
618 == (*handle)->cache_num_chains)
621 return (*handle)->cache_chain_iteration->name;
624 /* Get first rule in the given chain: NULL for empty chain. */
626 TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
628 struct chain_cache *c;
630 c = find_label(chain, *handle);
636 /* Empty chain: single return/policy rule */
637 if (c->start_off == c->end_off)
640 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
641 return offset2entry(*handle, c->start_off);
644 /* Returns NULL when rules run out. */
646 TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
648 if ((void *)prev + prev->next_offset
649 == (void *)(*handle)->cache_rule_end)
652 return (void *)prev + prev->next_offset;
656 /* How many rules in this chain? */
658 TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
660 unsigned int off = 0;
661 STRUCT_ENTRY *start, *end;
664 if (!find_label(&off, chain, *handle)) {
666 return (unsigned int)-1;
669 start = get_entry(*handle, off);
670 end = get_entry(*handle, get_chain_end(*handle, off));
672 return entry2index(*handle, end) - entry2index(*handle, start);
675 /* Get n'th rule in this chain. */
676 const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
680 unsigned int pos = 0, chainindex;
683 if (!find_label(&pos, chain, *handle)) {
688 chainindex = entry2index(*handle, get_entry(*handle, pos));
690 return index2entry(*handle, chainindex + n);
695 target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
698 unsigned int labelidx;
699 STRUCT_ENTRY *jumpto;
701 /* To avoid const warnings */
702 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
704 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
705 return GET_TARGET(e)->u.user.name;
707 /* Standard target: evaluate */
708 spos = *(int *)GET_TARGET(e)->data;
712 else if (spos == -NF_ACCEPT-1)
714 else if (spos == -NF_DROP-1)
716 else if (spos == -NF_QUEUE-1)
719 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
720 entry2offset(handle, e), handle->entries.size,
725 jumpto = get_entry(handle, spos);
727 /* Fall through rule */
728 if (jumpto == (void *)e + e->next_offset)
731 /* Must point to head of a chain: ie. after error rule */
732 labelidx = entry2index(handle, jumpto) - 1;
733 return get_errorlabel(handle, index2offset(handle, labelidx));
736 /* Returns a pointer to the target name of this position. */
737 const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
740 return target_name(*handle, e);
743 /* Is this a built-in chain? Actually returns hook + 1. */
745 TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
749 for (i = 0; i < NUMHOOKS; i++) {
750 if ((handle->info.valid_hooks & (1 << i))
751 && handle->hooknames[i]
752 && strcmp(handle->hooknames[i], chain) == 0)
758 /* Get the policy of a given built-in chain */
760 TC_GET_POLICY(const char *chain,
761 STRUCT_COUNTERS *counters,
768 hook = TC_BUILTIN(chain, *handle);
770 start = (*handle)->info.hook_entry[hook-1];
774 e = get_entry(*handle, get_chain_end(*handle, start));
775 *counters = e->counters;
777 return target_name(*handle, e);
781 correct_verdict(STRUCT_ENTRY *e,
783 unsigned int offset, int delta_offset)
785 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
786 unsigned int curr = (char *)e - base;
788 /* Trap: insert of fall-through rule. Don't change fall-through
789 verdict to jump-over-next-rule. */
790 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
791 && t->verdict > (int)offset
792 && !(curr == offset &&
793 t->verdict == curr + e->next_offset)) {
794 t->verdict += delta_offset;
800 /* Adjusts standard verdict jump positions after an insertion/deletion. */
802 set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
804 ENTRY_ITERATE((*handle)->entries.entrytable,
805 (*handle)->entries.size,
806 correct_verdict, (char *)(*handle)->entries.entrytable,
807 offset, delta_offset);
809 set_changed(*handle);
813 /* If prepend is set, then we are prepending to a chain: if the
814 * insertion position is an entry point, keep the entry point. */
816 insert_rules(unsigned int num_rules, unsigned int rules_size,
817 const STRUCT_ENTRY *insert,
818 unsigned int offset, unsigned int num_rules_offset,
823 STRUCT_GETINFO newinfo;
826 if (offset >= (*handle)->entries.size) {
831 newinfo = (*handle)->info;
833 /* Fix up entry points. */
834 for (i = 0; i < NUMHOOKS; i++) {
835 /* Entry points to START of chain, so keep same if
836 inserting on at that point. */
837 if ((*handle)->info.hook_entry[i] > offset)
838 newinfo.hook_entry[i] += rules_size;
840 /* Underflow always points to END of chain (policy),
841 so if something is inserted at same point, it
842 should be advanced. */
843 if ((*handle)->info.underflow[i] >= offset)
844 newinfo.underflow[i] += rules_size;
847 newh = alloc_handle((*handle)->info.name,
848 (*handle)->entries.size + rules_size,
849 (*handle)->new_number + num_rules);
852 newh->info = newinfo;
855 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
856 /* ... Insert new ... */
857 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
859 memcpy((char *)newh->entries.entrytable + offset + rules_size,
860 (char *)(*handle)->entries.entrytable + offset,
861 (*handle)->entries.size - offset);
863 /* Move counter map. */
865 memcpy(newh->counter_map, (*handle)->counter_map,
866 sizeof(struct counter_map) * num_rules_offset);
868 memcpy(newh->counter_map + num_rules_offset + num_rules,
869 (*handle)->counter_map + num_rules_offset,
870 sizeof(struct counter_map) * ((*handle)->new_number
871 - num_rules_offset));
872 /* Set intermediates to no counter copy */
873 for (i = 0; i < num_rules; i++)
874 newh->counter_map[num_rules_offset+i]
875 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
877 newh->new_number = (*handle)->new_number + num_rules;
878 newh->entries.size = (*handle)->entries.size + rules_size;
879 newh->hooknames = (*handle)->hooknames;
881 newh->cache_chain_heads = (*handle)->cache_chain_heads;
882 newh->cache_num_builtins = (*handle)->cache_num_builtins;
883 newh->cache_num_chains = (*handle)->cache_num_chains;
884 newh->cache_rule_end = (*handle)->cache_rule_end;
885 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
886 if (!correct_cache(newh, offset, rules_size)) {
894 return set_verdict(offset, rules_size, handle);
898 delete_rules(unsigned int num_rules, unsigned int rules_size,
899 unsigned int offset, unsigned int num_rules_offset,
904 if (offset + rules_size > (*handle)->entries.size) {
909 /* Fix up entry points. */
910 for (i = 0; i < NUMHOOKS; i++) {
911 /* In practice, we never delete up to a hook entry,
912 since the built-in chains are always first,
913 so these two are never equal */
914 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
915 (*handle)->info.hook_entry[i] -= rules_size;
916 else if ((*handle)->info.hook_entry[i] > offset) {
917 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
918 i, (*handle)->info.hook_entry[i], offset);
922 /* Underflow points to policy (terminal) rule in
923 built-in, so sequality is valid here (when deleting
925 if ((*handle)->info.underflow[i] >= offset + rules_size)
926 (*handle)->info.underflow[i] -= rules_size;
927 else if ((*handle)->info.underflow[i] > offset) {
928 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
929 i, (*handle)->info.underflow[i], offset);
934 /* Move the rules down. */
935 memmove((char *)(*handle)->entries.entrytable + offset,
936 (char *)(*handle)->entries.entrytable + offset + rules_size,
937 (*handle)->entries.size - (offset + rules_size));
939 /* Move the counter map down. */
940 memmove(&(*handle)->counter_map[num_rules_offset],
941 &(*handle)->counter_map[num_rules_offset + num_rules],
942 sizeof(struct counter_map)
943 * ((*handle)->new_number - (num_rules + num_rules_offset)));
946 (*handle)->new_number -= num_rules;
947 (*handle)->entries.size -= rules_size;
949 /* Fix the chain cache */
950 if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
953 return set_verdict(offset, -(int)rules_size, handle);
957 standard_map(STRUCT_ENTRY *e, int verdict)
959 STRUCT_STANDARD_TARGET *t;
961 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
963 if (t->target.u.target_size
964 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
968 /* memset for memcmp convenience on delete/replace */
969 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
970 strcpy(t->target.u.user.name, STANDARD_TARGET);
971 t->verdict = verdict;
977 map_target(const TC_HANDLE_T handle,
980 STRUCT_ENTRY_TARGET *old)
982 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
984 /* Save old target (except data, which we don't change, except for
985 standard case, where we don't care). */
988 /* Maybe it's empty (=> fall through) */
989 if (strcmp(t->u.user.name, "") == 0)
990 return standard_map(e, offset + e->next_offset);
991 /* Maybe it's a standard target name... */
992 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
993 return standard_map(e, -NF_ACCEPT - 1);
994 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
995 return standard_map(e, -NF_DROP - 1);
996 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
997 return standard_map(e, -NF_QUEUE - 1);
998 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
999 return standard_map(e, RETURN);
1000 else if (TC_BUILTIN(t->u.user.name, handle)) {
1001 /* Can't jump to builtins. */
1005 /* Maybe it's an existing chain name. */
1006 struct chain_cache *c;
1008 c = find_label(t->u.user.name, handle);
1010 return standard_map(e, c->start_off);
1013 /* Must be a module? If not, kernel will reject... */
1014 /* memset to all 0 for your memcmp convenience. */
1015 memset(t->u.user.name + strlen(t->u.user.name),
1017 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
1022 unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
1024 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
1026 /* Save old target (except data, which we don't change, except for
1027 standard case, where we don't care). */
1031 /* Insert the entry `fw' in chain `chain' into position `rulenum'. */
1033 TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1034 const STRUCT_ENTRY *e,
1035 unsigned int rulenum,
1036 TC_HANDLE_T *handle)
1038 unsigned int chainindex, offset;
1039 STRUCT_ENTRY_TARGET old;
1040 struct chain_cache *c;
1044 iptc_fn = TC_INSERT_ENTRY;
1045 if (!(c = find_label(chain, *handle))) {
1050 chainindex = offset2index(*handle, c->start_off);
1052 tmp = index2entry(*handle, chainindex + rulenum);
1053 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
1057 offset = index2offset(*handle, chainindex + rulenum);
1059 /* Mapping target actually alters entry, but that's
1060 transparent to the caller. */
1061 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1064 ret = insert_rules(1, e->next_offset, e, offset,
1065 chainindex + rulenum, rulenum == 0, handle);
1066 unmap_target((STRUCT_ENTRY *)e, &old);
1070 /* Atomically replace rule `rulenum' in `chain' with `fw'. */
1072 TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1073 const STRUCT_ENTRY *e,
1074 unsigned int rulenum,
1075 TC_HANDLE_T *handle)
1077 unsigned int chainindex, offset;
1078 STRUCT_ENTRY_TARGET old;
1079 struct chain_cache *c;
1083 iptc_fn = TC_REPLACE_ENTRY;
1085 if (!(c = find_label(chain, *handle))) {
1090 chainindex = offset2index(*handle, c->start_off);
1092 tmp = index2entry(*handle, chainindex + rulenum);
1093 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
1098 offset = index2offset(*handle, chainindex + rulenum);
1099 /* Replace = delete and insert. */
1100 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1101 offset, chainindex + rulenum, handle))
1104 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1107 ret = insert_rules(1, e->next_offset, e, offset,
1108 chainindex + rulenum, 1, handle);
1109 unmap_target((STRUCT_ENTRY *)e, &old);
1113 /* Append entry `fw' to chain `chain'. Equivalent to insert with
1114 rulenum = length of chain. */
1116 TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1117 const STRUCT_ENTRY *e,
1118 TC_HANDLE_T *handle)
1120 struct chain_cache *c;
1121 STRUCT_ENTRY_TARGET old;
1124 iptc_fn = TC_APPEND_ENTRY;
1125 if (!(c = find_label(chain, *handle))) {
1130 if (!map_target(*handle, (STRUCT_ENTRY *)e,
1134 ret = insert_rules(1, e->next_offset, e, c->end_off,
1135 offset2index(*handle, c->end_off), 0, handle);
1136 unmap_target((STRUCT_ENTRY *)e, &old);
1141 match_different(const STRUCT_ENTRY_MATCH *a,
1142 const unsigned char *a_elems,
1143 const unsigned char *b_elems,
1144 unsigned char **maskptr)
1146 const STRUCT_ENTRY_MATCH *b;
1149 /* Offset of b is the same as a. */
1150 b = (void *)b_elems + ((unsigned char *)a - a_elems);
1152 if (a->u.match_size != b->u.match_size)
1155 if (strcmp(a->u.user.name, b->u.user.name) != 0)
1158 *maskptr += ALIGN(sizeof(*a));
1160 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
1161 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
1168 target_different(const unsigned char *a_targdata,
1169 const unsigned char *b_targdata,
1170 unsigned int tdatasize,
1171 const unsigned char *mask)
1174 for (i = 0; i < tdatasize; i++)
1175 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1182 is_same(const STRUCT_ENTRY *a,
1183 const STRUCT_ENTRY *b,
1184 unsigned char *matchmask);
1186 /* Delete the first rule in `chain' which matches `fw'. */
1188 TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1189 const STRUCT_ENTRY *origfw,
1190 unsigned char *matchmask,
1191 TC_HANDLE_T *handle)
1193 unsigned int offset;
1194 struct chain_cache *c;
1195 STRUCT_ENTRY *e, *fw;
1197 iptc_fn = TC_DELETE_ENTRY;
1198 if (!(c = find_label(chain, *handle))) {
1203 fw = malloc(origfw->next_offset);
1209 for (offset = c->start_off; offset < c->end_off;
1210 offset += e->next_offset) {
1211 STRUCT_ENTRY_TARGET discard;
1213 memcpy(fw, origfw, origfw->next_offset);
1215 /* FIXME: handle this in is_same --RR */
1216 if (!map_target(*handle, fw, offset, &discard)) {
1220 e = get_entry(*handle, offset);
1223 printf("Deleting:\n");
1226 if (is_same(e, fw, matchmask)) {
1228 ret = delete_rules(1, e->next_offset,
1229 offset, entry2index(*handle, e),
1241 /* Delete the rule in position `rulenum' in `chain'. */
1243 TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1244 unsigned int rulenum,
1245 TC_HANDLE_T *handle)
1250 struct chain_cache *c;
1252 iptc_fn = TC_DELETE_NUM_ENTRY;
1253 if (!(c = find_label(chain, *handle))) {
1258 index = offset2index(*handle, c->start_off) + rulenum;
1260 if (index >= offset2index(*handle, c->end_off)) {
1265 e = index2entry(*handle, index);
1271 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1276 /* Check the packet `fw' on chain `chain'. Returns the verdict, or
1277 NULL and sets errno. */
1279 TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1280 STRUCT_ENTRY *entry,
1281 TC_HANDLE_T *handle)
1287 /* Flushes the entries in the given chain (ie. empties chain). */
1289 TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1291 unsigned int startindex, endindex;
1292 STRUCT_ENTRY *startentry, *endentry;
1293 struct chain_cache *c;
1296 iptc_fn = TC_FLUSH_ENTRIES;
1297 if (!(c = find_label(chain, *handle))) {
1301 startindex = offset2index(*handle, c->start_off);
1302 endindex = offset2index(*handle, c->end_off);
1303 startentry = offset2entry(*handle, c->start_off);
1304 endentry = offset2entry(*handle, c->end_off);
1306 ret = delete_rules(endindex - startindex,
1307 (char *)endentry - (char *)startentry,
1308 c->start_off, startindex,
1313 /* Zeroes the counters in a chain. */
1315 TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1317 unsigned int i, end;
1318 struct chain_cache *c;
1320 if (!(c = find_label(chain, *handle))) {
1325 i = offset2index(*handle, c->start_off);
1326 end = offset2index(*handle, c->end_off);
1328 for (; i <= end; i++) {
1329 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1330 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1332 set_changed(*handle);
1338 TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1339 unsigned int rulenum,
1340 TC_HANDLE_T *handle)
1343 struct chain_cache *c;
1344 unsigned int chainindex, end;
1346 iptc_fn = TC_READ_COUNTER;
1349 if (!(c = find_label(chain, *handle))) {
1354 chainindex = offset2index(*handle, c->start_off);
1355 end = offset2index(*handle, c->end_off);
1357 if (chainindex + rulenum > end) {
1362 e = index2entry(*handle, chainindex + rulenum);
1364 return &e->counters;
1368 TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1369 unsigned int rulenum,
1370 TC_HANDLE_T *handle)
1373 struct chain_cache *c;
1374 unsigned int chainindex, end;
1376 iptc_fn = TC_ZERO_COUNTER;
1379 if (!(c = find_label(chain, *handle))) {
1384 chainindex = offset2index(*handle, c->start_off);
1385 end = offset2index(*handle, c->end_off);
1387 if (chainindex + rulenum > end) {
1392 e = index2entry(*handle, chainindex + rulenum);
1394 if ((*handle)->counter_map[chainindex + rulenum].maptype
1395 == COUNTER_MAP_NORMAL_MAP) {
1396 (*handle)->counter_map[chainindex + rulenum].maptype
1397 = COUNTER_MAP_ZEROED;
1400 set_changed(*handle);
1406 TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1407 unsigned int rulenum,
1408 STRUCT_COUNTERS *counters,
1409 TC_HANDLE_T *handle)
1412 struct chain_cache *c;
1413 unsigned int chainindex, end;
1415 iptc_fn = TC_SET_COUNTER;
1418 if (!(c = find_label(chain, *handle))) {
1423 chainindex = offset2index(*handle, c->start_off);
1424 end = offset2index(*handle, c->end_off);
1426 if (chainindex + rulenum > end) {
1431 e = index2entry(*handle, chainindex + rulenum);
1433 (*handle)->counter_map[chainindex + rulenum].maptype
1436 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1438 set_changed(*handle);
1443 /* Creates a new chain. */
1444 /* To create a chain, create two rules: error node and unconditional
1447 TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1452 struct ipt_error_target name;
1454 STRUCT_STANDARD_TARGET target;
1456 unsigned int destination;
1458 iptc_fn = TC_CREATE_CHAIN;
1460 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1462 if (find_label(chain, *handle)
1463 || strcmp(chain, LABEL_DROP) == 0
1464 || strcmp(chain, LABEL_ACCEPT) == 0
1465 || strcmp(chain, LABEL_QUEUE) == 0
1466 || strcmp(chain, LABEL_RETURN) == 0) {
1471 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
1476 memset(&newc, 0, sizeof(newc));
1477 newc.head.target_offset = sizeof(STRUCT_ENTRY);
1478 newc.head.next_offset
1479 = sizeof(STRUCT_ENTRY)
1480 + ALIGN(sizeof(struct ipt_error_target));
1481 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
1482 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
1483 strcpy(newc.name.error, chain);
1485 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
1486 newc.ret.next_offset
1487 = sizeof(STRUCT_ENTRY)
1488 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1489 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
1490 newc.target.target.u.target_size
1491 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
1492 newc.target.verdict = RETURN;
1494 destination = index2offset(*handle, (*handle)->new_number -1);
1496 /* Add just before terminal entry */
1497 ret = insert_rules(2, sizeof(newc), &newc.head,
1499 (*handle)->new_number - 1,
1502 set_changed(*handle);
1504 /* add chain cache info for this chain */
1505 add_chain_cache(*handle, chain,
1506 destination+newc.head.next_offset,
1507 destination+newc.head.next_offset);
1513 count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
1515 STRUCT_STANDARD_TARGET *t;
1517 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1518 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1520 if (t->verdict == offset)
1527 /* Get the number of references to this chain. */
1529 TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1530 TC_HANDLE_T *handle)
1532 struct chain_cache *c;
1534 if (!(c = find_label(chain, *handle))) {
1540 ENTRY_ITERATE((*handle)->entries.entrytable,
1541 (*handle)->entries.size,
1542 count_ref, c->start_off, ref);
1546 /* Deletes a chain. */
1548 TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
1550 unsigned int labelidx, labeloff;
1551 unsigned int references;
1552 struct chain_cache *c;
1554 STRUCT_ENTRY *start;
1556 if (!TC_GET_REFERENCES(&references, chain, handle))
1559 iptc_fn = TC_DELETE_CHAIN;
1561 if (TC_BUILTIN(chain, *handle)) {
1566 if (references > 0) {
1571 if (!(c = find_label(chain, *handle))) {
1576 if (c->start_off != c->end_off) {
1581 /* Need label index: preceeds chain start */
1582 labelidx = offset2index(*handle, c->start_off) - 1;
1583 labeloff = index2offset(*handle, labelidx);
1585 start = offset2entry(*handle, c->start_off);
1587 ret = delete_rules(2,
1588 get_entry(*handle, labeloff)->next_offset
1589 + start->next_offset,
1590 labeloff, labelidx, handle);
1594 /* Renames a chain. */
1595 int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1596 const IPT_CHAINLABEL newname,
1597 TC_HANDLE_T *handle)
1599 unsigned int labeloff, labelidx;
1600 struct chain_cache *c;
1601 struct ipt_error_target *t;
1603 iptc_fn = TC_RENAME_CHAIN;
1605 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1607 if (find_label(newname, *handle)
1608 || strcmp(newname, LABEL_DROP) == 0
1609 || strcmp(newname, LABEL_ACCEPT) == 0
1610 || strcmp(newname, LABEL_QUEUE) == 0
1611 || strcmp(newname, LABEL_RETURN) == 0) {
1616 if (!(c = find_label(oldname, *handle))
1617 || TC_BUILTIN(oldname, *handle)) {
1622 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
1627 /* Need label index: preceeds chain start */
1628 labelidx = offset2index(*handle, c->start_off) - 1;
1629 labeloff = index2offset(*handle, labelidx);
1631 t = (struct ipt_error_target *)
1632 GET_TARGET(get_entry(*handle, labeloff));
1634 memset(t->error, 0, sizeof(t->error));
1635 strcpy(t->error, newname);
1637 /* update chain cache */
1638 memset(c->name, 0, sizeof(c->name));
1639 strcpy(c->name, newname);
1641 set_changed(*handle);
1646 /* Sets the policy on a built-in chain. */
1648 TC_SET_POLICY(const IPT_CHAINLABEL chain,
1649 const IPT_CHAINLABEL policy,
1650 STRUCT_COUNTERS *counters,
1651 TC_HANDLE_T *handle)
1654 unsigned int policyoff, ctrindex;
1656 STRUCT_STANDARD_TARGET *t;
1658 iptc_fn = TC_SET_POLICY;
1659 /* Figure out which chain. */
1660 hook = TC_BUILTIN(chain, *handle);
1667 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1668 if (policyoff != (*handle)->info.underflow[hook]) {
1669 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1670 chain, policyoff, (*handle)->info.underflow[hook]);
1674 e = get_entry(*handle, policyoff);
1675 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
1677 if (strcmp(policy, LABEL_ACCEPT) == 0)
1678 t->verdict = -NF_ACCEPT - 1;
1679 else if (strcmp(policy, LABEL_DROP) == 0)
1680 t->verdict = -NF_DROP - 1;
1686 ctrindex = entry2index(*handle, e);
1689 /* set byte and packet counters */
1690 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1692 (*handle)->counter_map[ctrindex].maptype
1696 (*handle)->counter_map[ctrindex]
1697 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1700 set_changed(*handle);
1705 /* Without this, on gcc 2.7.2.3, we get:
1706 libiptc.c: In function `TC_COMMIT':
1707 libiptc.c:833: fixed or forbidden register was spilled.
1708 This may be due to a compiler bug or to impossible asm
1709 statements or clauses.
1712 subtract_counters(STRUCT_COUNTERS *answer,
1713 const STRUCT_COUNTERS *a,
1714 const STRUCT_COUNTERS *b)
1716 answer->pcnt = a->pcnt - b->pcnt;
1717 answer->bcnt = a->bcnt - b->bcnt;
1721 TC_COMMIT(TC_HANDLE_T *handle)
1723 /* Replace, then map back the counters. */
1724 STRUCT_REPLACE *repl;
1725 STRUCT_COUNTERS_INFO *newcounters;
1731 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1732 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1735 TC_DUMP_ENTRIES(*handle);
1738 /* Don't commit if nothing changed. */
1739 if (!(*handle)->changed)
1742 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1748 /* These are the old counters we will get from kernel */
1749 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
1750 * (*handle)->info.num_entries);
1751 if (!repl->counters) {
1757 /* These are the counters we're going to put back, later. */
1758 newcounters = malloc(counterlen);
1760 free(repl->counters);
1766 strcpy(repl->name, (*handle)->info.name);
1767 repl->num_entries = (*handle)->new_number;
1768 repl->size = (*handle)->entries.size;
1769 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1770 sizeof(repl->hook_entry));
1771 memcpy(repl->underflow, (*handle)->info.underflow,
1772 sizeof(repl->underflow));
1773 repl->num_counters = (*handle)->info.num_entries;
1774 repl->valid_hooks = (*handle)->info.valid_hooks;
1775 memcpy(repl->entries, (*handle)->entries.entrytable,
1776 (*handle)->entries.size);
1778 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
1779 sizeof(*repl) + (*handle)->entries.size) < 0) {
1780 free(repl->counters);
1786 /* Put counters back. */
1787 strcpy(newcounters->name, (*handle)->info.name);
1788 newcounters->num_counters = (*handle)->new_number;
1789 for (i = 0; i < (*handle)->new_number; i++) {
1790 unsigned int mappos = (*handle)->counter_map[i].mappos;
1791 switch ((*handle)->counter_map[i].maptype) {
1792 case COUNTER_MAP_NOMAP:
1793 newcounters->counters[i]
1794 = ((STRUCT_COUNTERS){ 0, 0 });
1797 case COUNTER_MAP_NORMAL_MAP:
1798 /* Original read: X.
1799 * Atomic read on replacement: X + Y.
1800 * Currently in kernel: Z.
1801 * Want in kernel: X + Y + Z.
1803 * => Add in replacement read.
1805 newcounters->counters[i] = repl->counters[mappos];
1808 case COUNTER_MAP_ZEROED:
1809 /* Original read: X.
1810 * Atomic read on replacement: X + Y.
1811 * Currently in kernel: Z.
1812 * Want in kernel: Y + Z.
1814 * => Add in (replacement read - original read).
1816 subtract_counters(&newcounters->counters[i],
1817 &repl->counters[mappos],
1818 &index2entry(*handle, i)->counters);
1821 case COUNTER_MAP_SET:
1822 /* Want to set counter (iptables-restore) */
1824 memcpy(&newcounters->counters[i],
1825 &index2entry(*handle, i)->counters,
1826 sizeof(STRUCT_COUNTERS));
1832 #ifdef KERNEL_64_USERSPACE_32
1834 /* Kernel will think that pointer should be 64-bits, and get
1835 padding. So we accomodate here (assumption: alignment of
1836 `counters' is on 64-bit boundary). */
1837 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1838 if ((unsigned long)&newcounters->counters % 8 != 0) {
1840 "counters alignment incorrect! Mail rusty!\n");
1843 *kernptr = newcounters->counters;
1845 #endif /* KERNEL_64_USERSPACE_32 */
1847 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1848 newcounters, counterlen) < 0) {
1849 free(repl->counters);
1855 free(repl->counters);
1864 /* Get raw socket. */
1871 /* Translates errno numbers into more human-readable form than strerror. */
1873 TC_STRERROR(int err)
1876 struct table_struct {
1879 const char *message;
1881 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
1882 { TC_INIT, EINVAL, "Module is wrong version" },
1884 "Table does not exist (do you need to insmod?)" },
1885 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1886 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1887 { TC_DELETE_CHAIN, EMLINK,
1888 "Can't delete chain with references left" },
1889 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1890 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1891 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1892 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
1893 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1894 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
1895 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1896 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
1897 /* EINVAL for CHECK probably means bad interface. */
1898 { TC_CHECK_PACKET, EINVAL,
1899 "Bad arguments (does that interface exist?)" },
1900 { TC_CHECK_PACKET, ENOSYS,
1901 "Checking will most likely never get implemented" },
1902 /* ENOENT for DELETE probably means no matching rule */
1903 { TC_DELETE_ENTRY, ENOENT,
1904 "Bad rule (does a matching rule exist in that chain?)" },
1905 { TC_SET_POLICY, ENOENT,
1906 "Bad built-in chain name" },
1907 { TC_SET_POLICY, EINVAL,
1908 "Bad policy name" },
1910 { NULL, 0, "Incompatible with this kernel" },
1911 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1912 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1913 { NULL, ENOMEM, "Memory allocation problem" },
1914 { NULL, ENOENT, "No chain/target/match by that name" },
1917 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1918 if ((!table[i].fn || table[i].fn == iptc_fn)
1919 && table[i].err == err)
1920 return table[i].message;
1923 return strerror(err);