blob: ad20251f959b74b617a79d1fca40f16d8c64c17e [file] [log] [blame]
Harald Welte9e033802004-01-06 18:59:46 +00001/* Library which manipulates firewall rules. Version $Revision: 1.41 $ */
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).
13 * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
14 *
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 Welte9e033802004-01-06 18:59:46 +000018 * - speed 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 Welte9e033802004-01-06 18:59:46 +000021 * 2003-Jun-30: Harald Welte <laforge@netfilter.org>:
22 * - reimplementation from scratch. *sigh*. I hope nobody has to touch
23 * this code ever again.
Harald Welte3ea8f402003-06-23 18:25:59 +000024 */
Harald Welte9e033802004-01-06 18:59:46 +000025#include "linux_listhelp.h"
Marc Bouchere6869a82000-03-20 06:03:29 +000026
Marc Bouchere6869a82000-03-20 06:03:29 +000027#ifndef IPT_LIB_DIR
28#define IPT_LIB_DIR "/usr/local/lib/iptables"
29#endif
30
31static int sockfd = -1;
32static void *iptc_fn = NULL;
33
34static const char *hooknames[]
Rusty Russell79dee072000-05-02 16:45:16 +000035= { [HOOK_PRE_ROUTING] "PREROUTING",
36 [HOOK_LOCAL_IN] "INPUT",
37 [HOOK_FORWARD] "FORWARD",
38 [HOOK_LOCAL_OUT] "OUTPUT",
Rusty Russell10758b72000-09-14 07:37:33 +000039 [HOOK_POST_ROUTING] "POSTROUTING",
40#ifdef HOOK_DROPPING
41 [HOOK_DROPPING] "DROPPING"
42#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000043};
44
45struct counter_map
46{
47 enum {
48 COUNTER_MAP_NOMAP,
49 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000050 COUNTER_MAP_ZEROED,
51 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000052 } maptype;
53 unsigned int mappos;
54};
55
56/* Convenience structures */
57struct ipt_error_target
58{
Rusty Russell79dee072000-05-02 16:45:16 +000059 STRUCT_ENTRY_TARGET t;
60 char error[TABLE_MAXNAMELEN];
Marc Bouchere6869a82000-03-20 06:03:29 +000061};
62
Harald Welte9e033802004-01-06 18:59:46 +000063struct rule_head
Rusty Russell30fd6e52000-04-23 09:16:06 +000064{
Harald Welte9e033802004-01-06 18:59:46 +000065 struct list_head list; /* list of rules in chain */
66
67 struct chain_head *chain; /* we're part of this chain */
68
69 struct chain_head *jumpto; /* target of this rule, in case
70 it is a jump rule */
71
72 struct counter_map counter_map;
73
74 unsigned int size; /* size of rule */
75 STRUCT_ENTRY *entry_blob; /* pointer to entry in blob */
76 STRUCT_ENTRY entry[0];
77};
78
79struct chain_head
80{
81 struct list_head list;
82
Rusty Russell79dee072000-05-02 16:45:16 +000083 char name[TABLE_MAXNAMELEN];
Harald Welte9e033802004-01-06 18:59:46 +000084 unsigned int hooknum;
85 struct list_head rules;
86 struct rule_head *firstrule; /* first (ERROR) rule */
87 struct rule_head *lastrule; /* last (RETURN) rule */
Rusty Russell30fd6e52000-04-23 09:16:06 +000088};
89
Rusty Russell79dee072000-05-02 16:45:16 +000090STRUCT_TC_HANDLE
Marc Bouchere6869a82000-03-20 06:03:29 +000091{
92 /* Have changes been made? */
93 int changed;
Harald Welte9e033802004-01-06 18:59:46 +000094
95 /* linked list of chains in this table */
96 struct list_head chains;
97
98 /* current position of first_chain() / next_chain() */
99 struct chain_head *chain_iterator_cur;
100
101 /* current position of first_rule() / next_rule() */
102 struct rule_head *rule_iterator_cur;
103
104 /* the structure we receive from getsockopt() */
Rusty Russell79dee072000-05-02 16:45:16 +0000105 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +0000106
Marc Bouchere6869a82000-03-20 06:03:29 +0000107 /* Array of hook names */
108 const char **hooknames;
Harald Welte9e033802004-01-06 18:59:46 +0000109#if 0
110 /* Size in here reflects original state. */
111
Marc Bouchere6869a82000-03-20 06:03:29 +0000112
Rusty Russell30fd6e52000-04-23 09:16:06 +0000113 /* Cached position of chain heads (NULL = no cache). */
114 unsigned int cache_num_chains;
115 unsigned int cache_num_builtins;
116 struct chain_cache *cache_chain_heads;
117
118 /* Chain iterator: current chain cache entry. */
119 struct chain_cache *cache_chain_iteration;
120
121 /* Rule iterator: terminal rule */
Rusty Russell79dee072000-05-02 16:45:16 +0000122 STRUCT_ENTRY *cache_rule_end;
Rusty Russell175f6412000-03-24 09:32:20 +0000123
Marc Bouchere6869a82000-03-20 06:03:29 +0000124 /* Number in here reflects current state. */
125 unsigned int new_number;
Harald Welte9e033802004-01-06 18:59:46 +0000126#endif
Rusty Russell79dee072000-05-02 16:45:16 +0000127 STRUCT_GET_ENTRIES entries;
Marc Bouchere6869a82000-03-20 06:03:29 +0000128};
129
Rusty Russell175f6412000-03-24 09:32:20 +0000130static void
Rusty Russell79dee072000-05-02 16:45:16 +0000131set_changed(TC_HANDLE_T h)
Rusty Russell175f6412000-03-24 09:32:20 +0000132{
Rusty Russell175f6412000-03-24 09:32:20 +0000133 h->changed = 1;
134}
135
Harald Welte380ba5f2002-02-13 16:19:55 +0000136#ifdef IPTC_DEBUG
Rusty Russell79dee072000-05-02 16:45:16 +0000137static void do_check(TC_HANDLE_T h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000138#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000139#else
140#define CHECK(h)
141#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000142
Harald Welte9e033802004-01-06 18:59:46 +0000143static struct rule_head *ruleh_alloc(unsigned int size)
144{
145 struct rule_head *ruleh = malloc(sizeof(*ruleh)+size);
146 if (!ruleh)
147 return NULL;
148
149 memset(ruleh, 0, sizeof(*ruleh)+size);
150 ruleh->size = size;
151
152 return ruleh;
153}
154
155static void ruleh_free(struct rule_head *ruleh)
156{
157 list_del(&ruleh->list);
158 free(ruleh);
159}
160
161static struct chain_head *chainh_alloc(TC_HANDLE_T h, const char *name)
162{
163 struct chain_head *chainh = malloc(sizeof(*chainh));
164 if (!chainh)
165 return NULL;
166
167 memset(chainh, 0, sizeof(*chainh));
168 strncpy(chainh->name, name, sizeof(&chainh->name));
169 list_append(&chainh->list, &h->chains);
170
171 return chainh;
172}
173
174static void
175chainh_clean(struct chain_head *chainh)
176{
177 /* FIXME */
178 struct list_head *cur_item, *item2;
179
180 list_for_each_safe(cur_item, item2, &chainh->rules) {
181 struct rule_head *ruleh = list_entry(cur_item,
182 struct rule_head,
183 list);
184 ruleh_free(ruleh);
185 }
186}
187
188static void
189chainh_free(struct chain_head *chainh)
190{
191 chainh_clean(chainh);
192 list_del(&chainh->list);
193}
194
195static struct chain_head *
196chainh_find(TC_HANDLE_T h, const IPT_CHAINLABEL name)
197{
198 struct list_head *cur;
199
200 list_for_each(cur, &h->chains) {
201 struct chain_head *ch = list_entry(cur, struct chain_head,
202 list);
203 if (!strcmp(name, ch->name))
204 return ch;
205 }
206 return NULL;
207}
208
209/* Returns chain head if found, otherwise NULL. */
210static struct chain_head *
211find_label(const char *name, TC_HANDLE_T handle)
212{
213 return chainh_find(handle, name);
214}
215
216
217/*
218 * functions that directly operate on the blob
219 */
220
221static inline unsigned long
222entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
223{
224 return (void *)e - (void *)h->entries.entrytable;
225}
226
227static inline STRUCT_ENTRY *
228get_entry(TC_HANDLE_T h, unsigned int offset)
229{
230 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
231}
232
233/* needed by entry2index */
Marc Bouchere6869a82000-03-20 06:03:29 +0000234static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000235get_number(const STRUCT_ENTRY *i,
236 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000237 unsigned int *pos)
238{
239 if (i == seek)
240 return 1;
241 (*pos)++;
242 return 0;
243}
244
245static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000246entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000247{
248 unsigned int pos = 0;
249
Rusty Russell725d97a2000-07-07 08:54:22 +0000250 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000251 get_number, seek, &pos) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000252 fprintf(stderr, "ERROR: offset %i not an entry!\n",
Rusty Russell725d97a2000-07-07 08:54:22 +0000253 (char *)seek - (char *)h->entries.entrytable);
Marc Bouchere6869a82000-03-20 06:03:29 +0000254 abort();
255 }
256 return pos;
257}
258
259static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000260get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000261 unsigned int number,
262 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000263 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000264{
265 if (*pos == number) {
266 *pe = i;
267 return 1;
268 }
269 (*pos)++;
270 return 0;
271}
272
Rusty Russell79dee072000-05-02 16:45:16 +0000273static STRUCT_ENTRY *
274index2entry(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000275{
276 unsigned int pos = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000277 STRUCT_ENTRY *ret = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000278
Rusty Russell725d97a2000-07-07 08:54:22 +0000279 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000280 get_entry_n, index, &pos, &ret);
Marc Bouchere6869a82000-03-20 06:03:29 +0000281
282 return ret;
283}
284
Harald Welte3ea8f402003-06-23 18:25:59 +0000285static inline unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000286index2offset(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000287{
288 return entry2offset(h, index2entry(h, index));
289}
290
Harald Welte9e033802004-01-06 18:59:46 +0000291static char *
292get_errorlabel(TC_HANDLE_T h, unsigned int offset)
293{
294 STRUCT_ENTRY *e;
295
296 e = get_entry(h, offset);
297 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
298 fprintf(stderr, "ERROR: offset %u not an error node!\n",
299 offset);
300 abort();
301 }
302
303 return (char *)GET_TARGET(e)->data;
304}
305
306#if 0
Harald Welte3ea8f402003-06-23 18:25:59 +0000307static inline STRUCT_ENTRY *
308offset2entry(TC_HANDLE_T h, unsigned int offset)
309{
310 return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
311}
312
313static inline unsigned int
314offset2index(const TC_HANDLE_T h, unsigned int offset)
315{
316 return entry2index(h, offset2entry(h, offset));
317}
318
319
Harald Welte9e033802004-01-06 18:59:46 +0000320#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000321
322/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000323static TC_HANDLE_T
Harald Welte9e033802004-01-06 18:59:46 +0000324alloc_tc_handle(const char *tablename, unsigned int size,
325 unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +0000326{
327 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000328 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000329
Rusty Russell79dee072000-05-02 16:45:16 +0000330 len = sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000331 + size
332 + num_rules * sizeof(struct counter_map);
333
334 if ((h = malloc(len)) == NULL) {
335 errno = ENOMEM;
336 return NULL;
337 }
338
339 h->changed = 0;
Harald Welte9e033802004-01-06 18:59:46 +0000340
Marc Bouchere6869a82000-03-20 06:03:29 +0000341 strcpy(h->info.name, tablename);
342 strcpy(h->entries.name, tablename);
Harald Welte9e033802004-01-06 18:59:46 +0000343 INIT_LIST_HEAD(&h->chains);
Marc Bouchere6869a82000-03-20 06:03:29 +0000344
345 return h;
346}
347
Harald Welte9e033802004-01-06 18:59:46 +0000348/* get the name of the chain that we jump to */
349static char *
350parse_jumptarget(const STRUCT_ENTRY *e, TC_HANDLE_T h)
351{
352 STRUCT_ENTRY *jumpto;
353 int spos, labelidx;
354
355 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) {
356 /* called for non-standard target */
357 return "__FIXME";
358 }
359 /* Standard target: evaluate */
360 spos = *(int *)GET_TARGET(e)->data;
361 if (spos < 0) {
362 return "__FIXME";
363 }
364
365 jumpto = get_entry(h, spos);
366
367 /* Fall through rule */
368 if (jumpto == (void *)e + e->next_offset)
369 return "";
370
371 /* Must point to head of a chain: ie. after error rule */
372 /* FIXME: this needs to deal with internal jump targets */
373 labelidx = entry2index(h, jumpto) - 1;
374 return get_errorlabel(h, index2offset(h, labelidx));
375}
376
377/* parser functions */
378
379struct rule_head *
380append_entrycopy(const STRUCT_ENTRY *e, struct rule_head *prev)
381{
382 struct rule_head *ruleh = ruleh_alloc(e->next_offset);
383 if (!ruleh)
384 return NULL;
385
386 memcpy(&ruleh->entry, e, e->next_offset);
387 ruleh->chain = prev->chain;
388 ruleh->entry_blob = e;
389 list_append(&ruleh->list, &prev->list);
390
391 return ruleh;
392}
393
394/* have to return 0 on success, bcf ENTRY_ITERATE */
395static inline int
396parse_entry(const STRUCT_ENTRY *e, TC_HANDLE_T h, struct chain_head **curchain)
397{
398 int i;
399 union tgt_u {
400 STRUCT_ENTRY_TARGET ent;
401 STRUCT_STANDARD_TARGET std;
402 struct ipt_error_target err;
403 } *tgt;
404
405 struct rule_head *lastrule = list_entry((*curchain)->rules.prev,
406 struct rule_head, list);
407 struct rule_head *newrule;
408
409 tgt = (union tgt_u *) GET_TARGET(e);
410
411 if (e->target_offset == sizeof(STRUCT_ENTRY)
412 && (strcmp(tgt->ent.u.user.name, IPT_STANDARD_TARGET) == 0)) {
413 /* jump to somewhere else */
414 char *targname;
415 struct chain_head *chainh;
416
417 newrule = append_entrycopy(e, lastrule);
418
419 targname = parse_jumptarget(e, h);
420 if (!(chainh = find_label(targname, h))) {
421 chainh = chainh_alloc(h, targname);
422 }
423 if (!chainh) {
424 errno = ENOMEM;
425 return 1;
426 }
427 newrule->jumpto = chainh;
428
429 } else if (e->target_offset == sizeof(STRUCT_ENTRY)
430 && e->next_offset == sizeof(STRUCT_ENTRY)
431 + ALIGN(sizeof(struct ipt_error_target))
432 && !strcmp(tgt->ent.u.user.name, ERROR_TARGET)) {
433 /* chain head */
434 *curchain = chainh_find(h, tgt->err.error);
435 if (!(*curchain)) {
436 *curchain = chainh_alloc(h, tgt->err.error);
437 /* FIXME: error handling */
438 }
439 newrule = append_entrycopy(e, lastrule);
440 (*curchain)->firstrule = newrule;
441
442 } else if (e->target_offset == sizeof(STRUCT_ENTRY)
443 && e->next_offset == sizeof(STRUCT_ENTRY)
444 + ALIGN(sizeof(STRUCT_STANDARD_TARGET))
445 && tgt->std.verdict == RETURN) {
446 /* chain end */
447 newrule = append_entrycopy(e, lastrule);
448 (*curchain)->lastrule = newrule;
449 *curchain = NULL;
450 } else {
451 /* normal rule */
452 newrule = append_entrycopy(e, lastrule);
453 }
454
455 /* create counter map entry */
456 newrule->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
457 newrule->counter_map.mappos = entry2index(h, e);
458
459 /* iterate over hook_entries, needed to connect builtin
460 * chains with hook numbers */
461 for (i = 0; i < NUMHOOKS; i++) {
462 if (!(h->info.valid_hooks & (1 << i)))
463 continue;
464 if (h->info.hook_entry[i] == entry2offset(h, e)) {
465 /* found hook entry point */
466 if (*curchain)
467 (*curchain)->hooknum = i;
468 }
469 if (h->info.underflow[i] == entry2offset(h, e)) {
470 /* found underflow point */
471 }
472 }
473
474 return 0;
475}
476
477static int parse_ruleset(TC_HANDLE_T h)
478{
479 struct chain_head *curchain;
480
481 /* iterate over ruleset; create linked list of rule_head/chain_head */
482 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
483 parse_entry, h, &curchain)) {
484 /* some error happened while iterating */
485 return 0;
486 }
487
488 return 1;
489}
490
Rusty Russell79dee072000-05-02 16:45:16 +0000491TC_HANDLE_T
492TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000493{
Rusty Russell79dee072000-05-02 16:45:16 +0000494 TC_HANDLE_T h;
495 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +0000496 int tmp;
497 socklen_t s;
498
Rusty Russell79dee072000-05-02 16:45:16 +0000499 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000500
Martin Josefssone560fd62003-06-13 16:56:51 +0000501 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000502 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000503 sockfd = -1;
504 }
Harald Welte366454b2002-01-07 13:46:50 +0000505
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000506 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
507 errno = EINVAL;
508 return NULL;
509 }
510
Rusty Russell79dee072000-05-02 16:45:16 +0000511 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000512 if (sockfd < 0)
513 return NULL;
514
515 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000516
Marc Bouchere6869a82000-03-20 06:03:29 +0000517 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000518 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000519 return NULL;
520
Harald Welte9e033802004-01-06 18:59:46 +0000521 if ((h = alloc_tc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000522 == NULL) {
523 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000524 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000525 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000526 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000527
528/* Too hard --RR */
529#if 0
530 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
531 dynlib = dlopen(pathname, RTLD_NOW);
532 if (!dynlib) {
533 errno = ENOENT;
534 return NULL;
535 }
536 h->hooknames = dlsym(dynlib, "hooknames");
537 if (!h->hooknames) {
538 errno = ENOENT;
539 return NULL;
540 }
541#else
542 h->hooknames = hooknames;
543#endif
544
545 /* Initialize current state */
546 h->info = info;
Harald Welte9e033802004-01-06 18:59:46 +0000547 //h->new_number = h->info.num_entries;
548 //
Marc Bouchere6869a82000-03-20 06:03:29 +0000549 h->entries.size = h->info.size;
550
Rusty Russell79dee072000-05-02 16:45:16 +0000551 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000552
Rusty Russell79dee072000-05-02 16:45:16 +0000553 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000554 &tmp) < 0) {
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000555 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000556 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000557 free(h);
558 return NULL;
559 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000560
Marc Bouchere6869a82000-03-20 06:03:29 +0000561 CHECK(h);
Harald Welte9e033802004-01-06 18:59:46 +0000562 parse_ruleset(h);
563
Marc Bouchere6869a82000-03-20 06:03:29 +0000564 return h;
565}
566
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000567void
568TC_FREE(TC_HANDLE_T *h)
569{
Harald Welte9e033802004-01-06 18:59:46 +0000570 struct list_head *cur_item, *item2;
571
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000572 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000573 sockfd = -1;
Harald Welte9e033802004-01-06 18:59:46 +0000574
575 /* free all chains */
576 list_for_each_safe(cur_item, item2, &(*h)->chains) {
577 struct chain_head *chead = list_entry(cur_item,
578 struct chain_head,
579 list);
580 chainh_free(chead);
581 }
582
583 /* FIXME: free all other ressources we might be using */
584
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000585 free(*h);
586 *h = NULL;
587}
588
Marc Bouchere6869a82000-03-20 06:03:29 +0000589static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000590print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000591{
Rusty Russell228e98d2000-04-27 10:28:06 +0000592 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000593 return 0;
594}
595
Rusty Russell79dee072000-05-02 16:45:16 +0000596static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
597
Harald Welte9e033802004-01-06 18:59:46 +0000598#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000599void
Rusty Russell79dee072000-05-02 16:45:16 +0000600TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000601{
602 CHECK(handle);
603
604 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000605 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000606 handle->new_number, handle->entries.size);
607 printf("Table `%s'\n", handle->info.name);
608 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000609 handle->info.hook_entry[HOOK_PRE_ROUTING],
610 handle->info.hook_entry[HOOK_LOCAL_IN],
611 handle->info.hook_entry[HOOK_FORWARD],
612 handle->info.hook_entry[HOOK_LOCAL_OUT],
613 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000614 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000615 handle->info.underflow[HOOK_PRE_ROUTING],
616 handle->info.underflow[HOOK_LOCAL_IN],
617 handle->info.underflow[HOOK_FORWARD],
618 handle->info.underflow[HOOK_LOCAL_OUT],
619 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000620
Rusty Russell725d97a2000-07-07 08:54:22 +0000621 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000622 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000623}
624
Rusty Russell30fd6e52000-04-23 09:16:06 +0000625/* Returns 0 if not hook entry, else hooknumber + 1 */
626static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000627is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000628{
629 unsigned int i;
630
Rusty Russell79dee072000-05-02 16:45:16 +0000631 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000632 if ((h->info.valid_hooks & (1 << i))
633 && get_entry(h, h->info.hook_entry[i]) == e)
634 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000635 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000636 return 0;
637}
638
Rusty Russell30fd6e52000-04-23 09:16:06 +0000639
Rusty Russell30fd6e52000-04-23 09:16:06 +0000640static int alphasort(const void *a, const void *b)
641{
642 return strcmp(((struct chain_cache *)a)->name,
643 ((struct chain_cache *)b)->name);
644}
Harald Welte9e033802004-01-06 18:59:46 +0000645#endif
Rusty Russell30fd6e52000-04-23 09:16:06 +0000646
Marc Bouchere6869a82000-03-20 06:03:29 +0000647/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000648int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000649{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000650 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000651}
652
Harald Welte9e033802004-01-06 18:59:46 +0000653#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000654/* Returns the position of the final (ie. unconditional) element. */
655static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000656get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000657{
658 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000659 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000660
661 last_off = start;
662 e = get_entry(handle, start);
663
664 /* Terminate when we meet a error label or a hook entry. */
665 for (off = start + e->next_offset;
666 off < handle->entries.size;
667 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000668 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000669 unsigned int i;
670
671 e = get_entry(handle, off);
672
673 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000674 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000675 if ((handle->info.valid_hooks & (1 << i))
676 && off == handle->info.hook_entry[i])
677 return last_off;
678 }
679
680 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000681 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000682 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000683 return last_off;
684 }
685 /* SHOULD NEVER HAPPEN */
686 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
687 handle->entries.size, off);
688 abort();
689}
Harald Welte9e033802004-01-06 18:59:46 +0000690#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000691
Rusty Russell30fd6e52000-04-23 09:16:06 +0000692/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000693const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000694TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000695{
Harald Welte9e033802004-01-06 18:59:46 +0000696 struct chain_head *firsthead = list_entry((*handle)->chains.next,
697 struct chain_head, list);
698 (*handle)->chain_iterator_cur = firsthead;
Marc Bouchere6869a82000-03-20 06:03:29 +0000699
Harald Welte9e033802004-01-06 18:59:46 +0000700 return firsthead->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000701}
702
Rusty Russell30fd6e52000-04-23 09:16:06 +0000703/* Iterator functions to run through the chains. Returns NULL at end. */
704const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000705TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000706{
Harald Welte9e033802004-01-06 18:59:46 +0000707 struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
708 (*handle)->chain_iterator_cur = next;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000709
Harald Welte9e033802004-01-06 18:59:46 +0000710 if (&next->list == &(*handle)->chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000711 return NULL;
712
Harald Welte9e033802004-01-06 18:59:46 +0000713 return next->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000714}
715
716/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000717const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000718TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000719{
Harald Welte9e033802004-01-06 18:59:46 +0000720 struct chain_head *c;
721 struct rule_head *r;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000722
723 c = find_label(chain, *handle);
724 if (!c) {
725 errno = ENOENT;
726 return NULL;
727 }
728
729 /* Empty chain: single return/policy rule */
Harald Welte9e033802004-01-06 18:59:46 +0000730 if (list_empty(&c->rules))
Rusty Russell30fd6e52000-04-23 09:16:06 +0000731 return NULL;
732
Harald Welte9e033802004-01-06 18:59:46 +0000733 r = list_entry(c->rules.next, struct rule_head, list);
734 (*handle)->rule_iterator_cur = r;
735
736 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000737}
738
739/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000740const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000741TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000742{
Harald Welte9e033802004-01-06 18:59:46 +0000743 struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
744
745 if (&r->list == &r->chain->rules)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000746 return NULL;
747
Harald Welte9e033802004-01-06 18:59:46 +0000748 /* NOTE: prev is without any influence ! */
749 return r->entry;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000750}
751
752#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000753/* How many rules in this chain? */
754unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000755TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000756{
757 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000758 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000759
760 CHECK(*handle);
761 if (!find_label(&off, chain, *handle)) {
762 errno = ENOENT;
763 return (unsigned int)-1;
764 }
765
766 start = get_entry(*handle, off);
767 end = get_entry(*handle, get_chain_end(*handle, off));
768
769 return entry2index(*handle, end) - entry2index(*handle, start);
770}
771
772/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000773const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
774 unsigned int n,
775 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000776{
777 unsigned int pos = 0, chainindex;
778
779 CHECK(*handle);
780 if (!find_label(&pos, chain, *handle)) {
781 errno = ENOENT;
782 return NULL;
783 }
784
785 chainindex = entry2index(*handle, get_entry(*handle, pos));
786
787 return index2entry(*handle, chainindex + n);
788}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000789#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000790
Rusty Russell30fd6e52000-04-23 09:16:06 +0000791static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000792target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000793{
794 int spos;
Marc Bouchere6869a82000-03-20 06:03:29 +0000795
Rusty Russell30fd6e52000-04-23 09:16:06 +0000796 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000797 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000798
Rusty Russell79dee072000-05-02 16:45:16 +0000799 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
800 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000801
802 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000803 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000804 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000805 if (spos == RETURN)
806 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000807 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000808 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000809 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000810 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000811 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000812 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000813
Harald Welte9e033802004-01-06 18:59:46 +0000814 fprintf(stderr, "ERROR: entry %p not a valid target (%d)\n",
815 e, spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000816 abort();
817 }
818
Harald Welte9e033802004-01-06 18:59:46 +0000819#if 0
820// jumpto = get_entry(handle, spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000821
822 /* Fall through rule */
823 if (jumpto == (void *)e + e->next_offset)
824 return "";
825
826 /* Must point to head of a chain: ie. after error rule */
Harald Welte9e033802004-01-06 18:59:46 +0000827 /* FIXME: this needs to deal with internal jump targets */
Marc Bouchere6869a82000-03-20 06:03:29 +0000828 labelidx = entry2index(handle, jumpto) - 1;
829 return get_errorlabel(handle, index2offset(handle, labelidx));
Harald Welte9e033802004-01-06 18:59:46 +0000830#endif
831 return "";
Marc Bouchere6869a82000-03-20 06:03:29 +0000832}
833
834/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000835const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
836 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000837{
Marc Bouchere6869a82000-03-20 06:03:29 +0000838 return target_name(*handle, e);
839}
840
841/* Is this a built-in chain? Actually returns hook + 1. */
842int
Rusty Russell79dee072000-05-02 16:45:16 +0000843TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000844{
845 unsigned int i;
846
Rusty Russell79dee072000-05-02 16:45:16 +0000847 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000848 if ((handle->info.valid_hooks & (1 << i))
849 && handle->hooknames[i]
850 && strcmp(handle->hooknames[i], chain) == 0)
851 return i+1;
852 }
853 return 0;
854}
855
856/* Get the policy of a given built-in chain */
857const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000858TC_GET_POLICY(const char *chain,
859 STRUCT_COUNTERS *counters,
860 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000861{
Rusty Russell79dee072000-05-02 16:45:16 +0000862 STRUCT_ENTRY *e;
Harald Welte9e033802004-01-06 18:59:46 +0000863 struct chain_head *chainh;
864 struct rule_head *ruleh;
Marc Bouchere6869a82000-03-20 06:03:29 +0000865 int hook;
866
Rusty Russell79dee072000-05-02 16:45:16 +0000867 hook = TC_BUILTIN(chain, *handle);
Harald Welte9e033802004-01-06 18:59:46 +0000868 if (hook == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000869 return NULL;
870
Harald Welte9e033802004-01-06 18:59:46 +0000871 chainh = find_label(chain, *handle);
872 if (!chainh) {
873 errno = ENOENT;
874 return NULL;
875 }
876
877 ruleh = chainh->lastrule;
878
879 e = ruleh->entry;
Marc Bouchere6869a82000-03-20 06:03:29 +0000880 *counters = e->counters;
881
882 return target_name(*handle, e);
883}
884
Harald Welte9e033802004-01-06 18:59:46 +0000885#if 0
886static int
Rusty Russell79dee072000-05-02 16:45:16 +0000887correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000888 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000889 unsigned int offset, int delta_offset)
890{
Rusty Russell79dee072000-05-02 16:45:16 +0000891 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000892 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000893
894 /* Trap: insert of fall-through rule. Don't change fall-through
895 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000896 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000897 && t->verdict > (int)offset
898 && !(curr == offset &&
899 t->verdict == curr + e->next_offset)) {
900 t->verdict += delta_offset;
901 }
902
903 return 0;
904}
905
906/* Adjusts standard verdict jump positions after an insertion/deletion. */
907static int
Rusty Russell79dee072000-05-02 16:45:16 +0000908set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000909{
Rusty Russell725d97a2000-07-07 08:54:22 +0000910 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000911 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000912 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000913 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000914
Rusty Russell175f6412000-03-24 09:32:20 +0000915 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000916 return 1;
917}
Harald Welte9e033802004-01-06 18:59:46 +0000918#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000919
Marc Bouchere6869a82000-03-20 06:03:29 +0000920
Marc Bouchere6869a82000-03-20 06:03:29 +0000921
922static int
Rusty Russell79dee072000-05-02 16:45:16 +0000923standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000924{
Rusty Russell79dee072000-05-02 16:45:16 +0000925 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000926
Rusty Russell79dee072000-05-02 16:45:16 +0000927 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000928
Rusty Russell67088e72000-05-10 01:18:57 +0000929 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000930 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000931 errno = EINVAL;
932 return 0;
933 }
934 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000935 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
936 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000937 t->verdict = verdict;
938
939 return 1;
940}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000941
Marc Bouchere6869a82000-03-20 06:03:29 +0000942static int
Rusty Russell79dee072000-05-02 16:45:16 +0000943map_target(const TC_HANDLE_T handle,
944 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000945 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000946 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000947{
Harald Welte9e033802004-01-06 18:59:46 +0000948 STRUCT_ENTRY_TARGET *t = (STRUCT_ENTRY_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000949
950 /* Save old target (except data, which we don't change, except for
951 standard case, where we don't care). */
952 *old = *t;
953
954 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000955 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000956 return standard_map(e, offset + e->next_offset);
957 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000958 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000959 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000960 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000961 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000962 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000963 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000964 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
965 return standard_map(e, RETURN);
966 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000967 /* Can't jump to builtins. */
968 errno = EINVAL;
969 return 0;
970 } else {
971 /* Maybe it's an existing chain name. */
Harald Welte9e033802004-01-06 18:59:46 +0000972 struct chain_head *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000973
Harald Welte9e033802004-01-06 18:59:46 +0000974#if 0
975 /* FIXME */
Rusty Russell228e98d2000-04-27 10:28:06 +0000976 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000977 if (c)
Harald Welte3ea8f402003-06-23 18:25:59 +0000978 return standard_map(e, c->start_off);
Harald Welte9e033802004-01-06 18:59:46 +0000979#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000980 }
981
982 /* Must be a module? If not, kernel will reject... */
983 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000984 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000985 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000986 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000987 return 1;
988}
989
990static void
Rusty Russell79dee072000-05-02 16:45:16 +0000991unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000992{
Rusty Russell79dee072000-05-02 16:45:16 +0000993 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000994
995 /* Save old target (except data, which we don't change, except for
996 standard case, where we don't care). */
997 *t = *old;
998}
999
Harald Welte9e033802004-01-06 18:59:46 +00001000static struct rule_head *
1001ruleh_get_n(struct chain_head *chead, int rulenum)
1002{
1003 int i = 0;
1004 struct list_head *list;
1005
1006
1007 list_for_each(list, &chead->rules) {
1008 struct rule_head *rhead = list_entry(list, struct rule_head,
1009 list);
1010 i++;
1011 if (i == rulenum)
1012 return rhead;
1013 }
1014 return NULL;
1015}
1016
1017/* Insert the entry `e' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001018int
Rusty Russell79dee072000-05-02 16:45:16 +00001019TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1020 const STRUCT_ENTRY *e,
1021 unsigned int rulenum,
1022 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001023{
Harald Welte9e033802004-01-06 18:59:46 +00001024 struct chain_head *c;
1025 struct rule_head *prev;
Marc Bouchere6869a82000-03-20 06:03:29 +00001026
Rusty Russell79dee072000-05-02 16:45:16 +00001027 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001028 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001029 errno = ENOENT;
1030 return 0;
1031 }
1032
Harald Welte9e033802004-01-06 18:59:46 +00001033 prev = ruleh_get_n(c, rulenum-1);
1034 if (!prev) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001035 errno = E2BIG;
1036 return 0;
1037 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001038
Harald Welte9e033802004-01-06 18:59:46 +00001039 if (append_entrycopy(e, prev))
1040 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001041
Harald Welte9e033802004-01-06 18:59:46 +00001042 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001043}
1044
1045/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1046int
Rusty Russell79dee072000-05-02 16:45:16 +00001047TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1048 const STRUCT_ENTRY *e,
1049 unsigned int rulenum,
1050 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001051{
Harald Welte9e033802004-01-06 18:59:46 +00001052 struct chain_head *c;
1053 struct rule_head *repl;
Marc Bouchere6869a82000-03-20 06:03:29 +00001054
Rusty Russell79dee072000-05-02 16:45:16 +00001055 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001056
Rusty Russell30fd6e52000-04-23 09:16:06 +00001057 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001058 errno = ENOENT;
1059 return 0;
1060 }
1061
Harald Welte9e033802004-01-06 18:59:46 +00001062 repl = ruleh_get_n(c, rulenum);
1063 if (!repl) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001064 errno = E2BIG;
1065 return 0;
1066 }
1067
Harald Welte9e033802004-01-06 18:59:46 +00001068 if (!append_entrycopy(e, repl)) {
1069 errno = ENOMEM;
Marc Bouchere6869a82000-03-20 06:03:29 +00001070 return 0;
Harald Welte9e033802004-01-06 18:59:46 +00001071 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001072
Harald Welte9e033802004-01-06 18:59:46 +00001073 ruleh_free(repl);
1074 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001075}
1076
Harald Welte9e033802004-01-06 18:59:46 +00001077/* Append entry `e' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001078 rulenum = length of chain. */
1079int
Rusty Russell79dee072000-05-02 16:45:16 +00001080TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1081 const STRUCT_ENTRY *e,
1082 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001083{
Harald Welte9e033802004-01-06 18:59:46 +00001084 struct chain_head *c;
1085 struct rule_head *rhead;
Marc Bouchere6869a82000-03-20 06:03:29 +00001086
Rusty Russell79dee072000-05-02 16:45:16 +00001087 iptc_fn = TC_APPEND_ENTRY;
Harald Welte9e033802004-01-06 18:59:46 +00001088
Rusty Russell30fd6e52000-04-23 09:16:06 +00001089 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001090 errno = ENOENT;
1091 return 0;
1092 }
1093
Harald Welte9e033802004-01-06 18:59:46 +00001094 rhead = list_entry(c->rules.prev, struct rule_head, list);
1095 if(append_entrycopy(e, rhead))
1096 return 1;
1097
1098 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001099}
1100
1101static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001102match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001103 const unsigned char *a_elems,
1104 const unsigned char *b_elems,
1105 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001106{
Rusty Russell79dee072000-05-02 16:45:16 +00001107 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001108 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001109
1110 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001111 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001112
Rusty Russell228e98d2000-04-27 10:28:06 +00001113 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001114 return 1;
1115
Rusty Russell228e98d2000-04-27 10:28:06 +00001116 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001117 return 1;
1118
Rusty Russell73ef09b2000-07-03 10:24:04 +00001119 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001120
Rusty Russell73ef09b2000-07-03 10:24:04 +00001121 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001122 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001123 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001124 *maskptr += i;
1125 return 0;
1126}
1127
1128static inline int
1129target_different(const unsigned char *a_targdata,
1130 const unsigned char *b_targdata,
1131 unsigned int tdatasize,
1132 const unsigned char *mask)
1133{
1134 unsigned int i;
1135 for (i = 0; i < tdatasize; i++)
1136 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1137 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001138
1139 return 0;
1140}
1141
Rusty Russell79dee072000-05-02 16:45:16 +00001142static int
1143is_same(const STRUCT_ENTRY *a,
1144 const STRUCT_ENTRY *b,
1145 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001146
Harald Welte9e033802004-01-06 18:59:46 +00001147/* Delete the first rule in `chain' which matches `origfw'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001148int
Rusty Russell79dee072000-05-02 16:45:16 +00001149TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1150 const STRUCT_ENTRY *origfw,
1151 unsigned char *matchmask,
1152 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001153{
Harald Welte9e033802004-01-06 18:59:46 +00001154 struct chain_head *c;
1155 struct list_head *cur, *cur2;
Marc Bouchere6869a82000-03-20 06:03:29 +00001156
Rusty Russell79dee072000-05-02 16:45:16 +00001157 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001158 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001159 errno = ENOENT;
1160 return 0;
1161 }
1162
Harald Welte9e033802004-01-06 18:59:46 +00001163 list_for_each_safe(cur, cur2, &c->rules) {
1164 struct rule_head *rhead = list_entry(cur, struct rule_head,
1165 list);
1166 if (is_same(rhead->entry, origfw, matchmask)) {
1167 ruleh_free(rhead);
1168 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001169 }
1170 }
1171
Marc Bouchere6869a82000-03-20 06:03:29 +00001172 errno = ENOENT;
1173 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001174}
Marc Bouchere6869a82000-03-20 06:03:29 +00001175
1176/* Delete the rule in position `rulenum' in `chain'. */
1177int
Rusty Russell79dee072000-05-02 16:45:16 +00001178TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1179 unsigned int rulenum,
1180 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001181{
Harald Welte9e033802004-01-06 18:59:46 +00001182 struct chain_head *chainh;
1183 struct rule_head *rhead;
Marc Bouchere6869a82000-03-20 06:03:29 +00001184
Rusty Russell79dee072000-05-02 16:45:16 +00001185 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welte9e033802004-01-06 18:59:46 +00001186
1187 if (!(chainh = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001188 errno = ENOENT;
1189 return 0;
1190 }
1191
Harald Welte9e033802004-01-06 18:59:46 +00001192 rhead = ruleh_get_n(chainh, rulenum);
1193 if (!rhead) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001194 errno = E2BIG;
1195 return 0;
1196 }
1197
Harald Welte9e033802004-01-06 18:59:46 +00001198 ruleh_free(rhead);
Marc Bouchere6869a82000-03-20 06:03:29 +00001199
Harald Welte9e033802004-01-06 18:59:46 +00001200 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001201}
1202
1203/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1204 NULL and sets errno. */
1205const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001206TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1207 STRUCT_ENTRY *entry,
1208 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001209{
1210 errno = ENOSYS;
1211 return NULL;
1212}
1213
1214/* Flushes the entries in the given chain (ie. empties chain). */
1215int
Rusty Russell79dee072000-05-02 16:45:16 +00001216TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001217{
Harald Welte9e033802004-01-06 18:59:46 +00001218 struct list_head *cur, *cur2;
1219 struct chain_head *chainh;
Marc Bouchere6869a82000-03-20 06:03:29 +00001220
Harald Welte9e033802004-01-06 18:59:46 +00001221 if (!(chainh = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001222 errno = ENOENT;
1223 return 0;
1224 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001225
Harald Welte9e033802004-01-06 18:59:46 +00001226 list_for_each_safe(cur, cur2, &chainh->rules) {
1227 struct rule_head *ruleh = list_entry(cur, struct rule_head,
1228 list);
1229 /* don't free the entry and policy/return entries */
1230 if (ruleh != chainh->firstrule && ruleh != chainh->lastrule)
1231 ruleh_free(ruleh);
1232 }
1233 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001234}
1235
1236/* Zeroes the counters in a chain. */
1237int
Rusty Russell79dee072000-05-02 16:45:16 +00001238TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001239{
Harald Welte9e033802004-01-06 18:59:46 +00001240 struct chain_head *c;
1241 struct list_head *cur;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001242
Rusty Russell30fd6e52000-04-23 09:16:06 +00001243 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001244 errno = ENOENT;
1245 return 0;
1246 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001247
Harald Welte9e033802004-01-06 18:59:46 +00001248 list_for_each(cur, c->rules.next) {
1249 struct rule_head *r = list_entry(cur, struct rule_head, list);
1250 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1251 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00001252 }
Rusty Russell175f6412000-03-24 09:32:20 +00001253 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001254
Marc Bouchere6869a82000-03-20 06:03:29 +00001255 return 1;
1256}
1257
Harald Welte1cef74d2001-01-05 15:22:59 +00001258STRUCT_COUNTERS *
1259TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1260 unsigned int rulenum,
1261 TC_HANDLE_T *handle)
1262{
1263 STRUCT_ENTRY *e;
Harald Welte9e033802004-01-06 18:59:46 +00001264 struct chain_head *c;
1265 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001266
1267 iptc_fn = TC_READ_COUNTER;
1268 CHECK(*handle);
1269
Harald Welte9e033802004-01-06 18:59:46 +00001270 if (!(c = find_label(chain, *handle) )
1271 || !(r = ruleh_get_n(c, rulenum))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001272 errno = ENOENT;
1273 return NULL;
1274 }
1275
Harald Welte9e033802004-01-06 18:59:46 +00001276 return &r->entry->counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00001277}
1278
1279int
1280TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1281 unsigned int rulenum,
1282 TC_HANDLE_T *handle)
1283{
1284 STRUCT_ENTRY *e;
Harald Welte9e033802004-01-06 18:59:46 +00001285 struct chain_head *c;
1286 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001287
1288 iptc_fn = TC_ZERO_COUNTER;
1289 CHECK(*handle);
1290
Harald Welte9e033802004-01-06 18:59:46 +00001291 if (!(c = find_label(chain, *handle))
1292 || !(r = ruleh_get_n(c, rulenum))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001293 errno = ENOENT;
1294 return 0;
1295 }
1296
Harald Welte9e033802004-01-06 18:59:46 +00001297 if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
1298 r->counter_map.maptype = COUNTER_MAP_ZEROED;
Harald Welte1cef74d2001-01-05 15:22:59 +00001299
1300 set_changed(*handle);
1301
1302 return 1;
1303}
1304
1305int
1306TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1307 unsigned int rulenum,
1308 STRUCT_COUNTERS *counters,
1309 TC_HANDLE_T *handle)
1310{
1311 STRUCT_ENTRY *e;
Harald Welte9e033802004-01-06 18:59:46 +00001312 struct chain_head *c;
1313 struct rule_head *r;
Harald Welte1cef74d2001-01-05 15:22:59 +00001314
1315 iptc_fn = TC_SET_COUNTER;
1316 CHECK(*handle);
1317
Harald Welte9e033802004-01-06 18:59:46 +00001318 if (!(c = find_label(chain, *handle))
1319 || !(r = ruleh_get_n(c, rulenum))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001320 errno = ENOENT;
1321 return 0;
1322 }
Harald Welte9e033802004-01-06 18:59:46 +00001323
1324 r->counter_map.maptype = COUNTER_MAP_SET;
1325 memcpy(&r->entry->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00001326
1327 set_changed(*handle);
1328
1329 return 1;
1330}
1331
Marc Bouchere6869a82000-03-20 06:03:29 +00001332/* Creates a new chain. */
1333/* To create a chain, create two rules: error node and unconditional
1334 * return. */
1335int
Rusty Russell79dee072000-05-02 16:45:16 +00001336TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001337{
Marc Bouchere6869a82000-03-20 06:03:29 +00001338 int ret;
Harald Welte9e033802004-01-06 18:59:46 +00001339 struct chainstart {
Rusty Russell79dee072000-05-02 16:45:16 +00001340 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001341 struct ipt_error_target name;
Harald Welte9e033802004-01-06 18:59:46 +00001342 } *newc1;
1343 struct chainend {
Rusty Russell79dee072000-05-02 16:45:16 +00001344 STRUCT_ENTRY ret;
1345 STRUCT_STANDARD_TARGET target;
Harald Welte9e033802004-01-06 18:59:46 +00001346 } *newc2;
1347 struct rule_head *newr1, *newr2;
1348 struct chain_head *chead;
Marc Bouchere6869a82000-03-20 06:03:29 +00001349
Rusty Russell79dee072000-05-02 16:45:16 +00001350 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001351
1352 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1353 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001354 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001355 || strcmp(chain, LABEL_DROP) == 0
1356 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001357 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001358 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001359 errno = EEXIST;
1360 return 0;
1361 }
1362
Rusty Russell79dee072000-05-02 16:45:16 +00001363 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001364 errno = EINVAL;
1365 return 0;
1366 }
1367
Harald Welte9e033802004-01-06 18:59:46 +00001368 chead = chainh_alloc(*handle, chain);
1369 if (!chead) {
1370 errno = ENOMEM;
1371 return 0;
1372 }
1373
1374 newr1 = ruleh_alloc(sizeof(*newc1));
1375 if (!newr1) {
1376 chainh_free(chead);
1377 return 0;
1378 }
1379 newc1 = (struct chainstart *) newr1->entry;
1380
1381 newr2 = ruleh_alloc(sizeof(*newc2));
1382 if (!newr2) {
1383 chainh_free(chead);
1384 ruleh_free(newr1);
1385 return 0;
1386 }
1387 newc2 = (struct chainend *) newr2->entry;
1388
1389 newc1->head.target_offset = sizeof(STRUCT_ENTRY);
1390 newc1->head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001391 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001392 + ALIGN(sizeof(struct ipt_error_target));
Harald Welte9e033802004-01-06 18:59:46 +00001393 strcpy(newc1->name.t.u.user.name, ERROR_TARGET);
1394 newc1->name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
1395 strcpy(newc1->name.error, chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001396
Harald Welte9e033802004-01-06 18:59:46 +00001397 newc2->ret.target_offset = sizeof(STRUCT_ENTRY);
1398 newc2->ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001399 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001400 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte9e033802004-01-06 18:59:46 +00001401 strcpy(newc2->target.target.u.user.name, STANDARD_TARGET);
1402 newc2->target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001403 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte9e033802004-01-06 18:59:46 +00001404 newc2->target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001405
Harald Welte9e033802004-01-06 18:59:46 +00001406 list_prepend(&newr1->list, &chead->rules);
1407 chead->firstrule = newr1;
1408 list_append(&newr2->list, &chead->rules);
1409 chead->lastrule = newr2;
Harald Weltefbc85232003-06-24 17:37:21 +00001410
Harald Welte9e033802004-01-06 18:59:46 +00001411 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001412}
1413
Harald Welte9e033802004-01-06 18:59:46 +00001414#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +00001415static int
Rusty Russell79dee072000-05-02 16:45:16 +00001416count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001417{
Rusty Russell79dee072000-05-02 16:45:16 +00001418 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001419
Rusty Russell79dee072000-05-02 16:45:16 +00001420 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1421 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001422
1423 if (t->verdict == offset)
1424 (*ref)++;
1425 }
1426
1427 return 0;
1428}
1429
1430/* Get the number of references to this chain. */
1431int
Rusty Russell79dee072000-05-02 16:45:16 +00001432TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1433 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001434{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001435 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001436
Rusty Russell30fd6e52000-04-23 09:16:06 +00001437 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001438 errno = ENOENT;
1439 return 0;
1440 }
1441
1442 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001443 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001444 (*handle)->entries.size,
Harald Welte3ea8f402003-06-23 18:25:59 +00001445 count_ref, c->start_off, ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001446 return 1;
1447}
Harald Welte9e033802004-01-06 18:59:46 +00001448#endif
1449
1450static unsigned int
1451count_rules(struct chain_head *chainh)
1452{
1453 unsigned int numrules = 0;
1454 struct list_head *cur;
1455
1456 list_for_each(cur, &chainh->rules) {
1457 numrules++;
1458 }
1459
1460 if (numrules <=2)
1461 return 0;
1462 else
1463 return numrules-2;
1464}
Marc Bouchere6869a82000-03-20 06:03:29 +00001465
1466/* Deletes a chain. */
1467int
Rusty Russell79dee072000-05-02 16:45:16 +00001468TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001469{
Marc Bouchere6869a82000-03-20 06:03:29 +00001470 unsigned int references;
Harald Welte9e033802004-01-06 18:59:46 +00001471 struct chain_head *chainh;
Marc Bouchere6869a82000-03-20 06:03:29 +00001472
Harald Welte9e033802004-01-06 18:59:46 +00001473#if 0
Rusty Russell79dee072000-05-02 16:45:16 +00001474 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001475 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001476
Rusty Russell79dee072000-05-02 16:45:16 +00001477 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001478
Rusty Russell79dee072000-05-02 16:45:16 +00001479 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001480 errno = EINVAL;
1481 return 0;
1482 }
1483
1484 if (references > 0) {
1485 errno = EMLINK;
1486 return 0;
1487 }
Harald Welte9e033802004-01-06 18:59:46 +00001488#endif
Marc Bouchere6869a82000-03-20 06:03:29 +00001489
Harald Welte9e033802004-01-06 18:59:46 +00001490 if (!(chainh = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001491 errno = ENOENT;
1492 return 0;
1493 }
1494
Harald Welte9e033802004-01-06 18:59:46 +00001495 if (!(count_rules(chainh) == 0)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001496 errno = ENOTEMPTY;
1497 return 0;
1498 }
1499
Harald Welte9e033802004-01-06 18:59:46 +00001500 chainh_free(chainh);
1501 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001502}
1503
1504/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001505int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1506 const IPT_CHAINLABEL newname,
1507 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001508{
Harald Welte9e033802004-01-06 18:59:46 +00001509 struct chain_head *c;
1510 struct rule_head *ruleh;
Marc Bouchere6869a82000-03-20 06:03:29 +00001511 struct ipt_error_target *t;
1512
Rusty Russell79dee072000-05-02 16:45:16 +00001513 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001514
Harald Welte1de80462000-10-30 12:00:27 +00001515 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1516 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001517 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001518 || strcmp(newname, LABEL_DROP) == 0
1519 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001520 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001521 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001522 errno = EEXIST;
1523 return 0;
1524 }
1525
Rusty Russell30fd6e52000-04-23 09:16:06 +00001526 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001527 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001528 errno = ENOENT;
1529 return 0;
1530 }
1531
Rusty Russell79dee072000-05-02 16:45:16 +00001532 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001533 errno = EINVAL;
1534 return 0;
1535 }
1536
Harald Welte9e033802004-01-06 18:59:46 +00001537 ruleh = list_entry(&c->rules.next, struct rule_head, list);
Marc Bouchere6869a82000-03-20 06:03:29 +00001538
1539 t = (struct ipt_error_target *)
Harald Welte9e033802004-01-06 18:59:46 +00001540 GET_TARGET(ruleh->entry);
Marc Bouchere6869a82000-03-20 06:03:29 +00001541
1542 memset(t->error, 0, sizeof(t->error));
1543 strcpy(t->error, newname);
Harald Weltefbc85232003-06-24 17:37:21 +00001544
Marc Bouchere6869a82000-03-20 06:03:29 +00001545 return 1;
1546}
1547
1548/* Sets the policy on a built-in chain. */
1549int
Rusty Russell79dee072000-05-02 16:45:16 +00001550TC_SET_POLICY(const IPT_CHAINLABEL chain,
1551 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001552 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001553 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001554{
Harald Welte9e033802004-01-06 18:59:46 +00001555 int ctrindex;
Marc Bouchere6869a82000-03-20 06:03:29 +00001556 unsigned int hook;
Harald Welte9e033802004-01-06 18:59:46 +00001557 struct chain_head *chainh;
1558 struct rule_head *policyrh;
Rusty Russell79dee072000-05-02 16:45:16 +00001559 STRUCT_ENTRY *e;
1560 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001561
Rusty Russell79dee072000-05-02 16:45:16 +00001562 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001563 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001564 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001565 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001566 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001567 return 0;
1568 } else
1569 hook--;
1570
Harald Welte9e033802004-01-06 18:59:46 +00001571 if (!(chainh = find_label(chain, *handle))) {
1572 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001573 return 0;
1574 }
1575
Harald Welte9e033802004-01-06 18:59:46 +00001576 policyrh = chainh->lastrule;
1577 if (policyrh) {
1578 printf("ERROR: Policy for `%s' non-existant", chain);
1579 return 0;
1580 }
1581
1582 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(policyrh->entry);
Marc Bouchere6869a82000-03-20 06:03:29 +00001583
Rusty Russell79dee072000-05-02 16:45:16 +00001584 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001585 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001586 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001587 t->verdict = -NF_DROP - 1;
1588 else {
1589 errno = EINVAL;
1590 return 0;
1591 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001592
1593 ctrindex = entry2index(*handle, e);
1594
1595 if (counters) {
1596 /* set byte and packet counters */
1597 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1598
Harald Welte9e033802004-01-06 18:59:46 +00001599 policyrh->counter_map.maptype = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00001600
1601 } else {
Harald Welte9e033802004-01-06 18:59:46 +00001602 policyrh->counter_map.maptype = COUNTER_MAP_NOMAP;
1603 policyrh->counter_map.mappos = 0;
Harald Welte1cef74d2001-01-05 15:22:59 +00001604 }
1605
Rusty Russell175f6412000-03-24 09:32:20 +00001606 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001607
Marc Bouchere6869a82000-03-20 06:03:29 +00001608 return 1;
1609}
1610
1611/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001612 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001613 libiptc.c:833: fixed or forbidden register was spilled.
1614 This may be due to a compiler bug or to impossible asm
1615 statements or clauses.
1616*/
1617static void
Rusty Russell79dee072000-05-02 16:45:16 +00001618subtract_counters(STRUCT_COUNTERS *answer,
1619 const STRUCT_COUNTERS *a,
1620 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001621{
1622 answer->pcnt = a->pcnt - b->pcnt;
1623 answer->bcnt = a->bcnt - b->bcnt;
1624}
1625
1626int
Rusty Russell79dee072000-05-02 16:45:16 +00001627TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001628{
1629 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001630 STRUCT_REPLACE *repl;
1631 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001632 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001633 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001634
1635 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001636
1637 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1638 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1639
Marc Bouchere6869a82000-03-20 06:03:29 +00001640#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001641 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001642#endif
1643
1644 /* Don't commit if nothing changed. */
1645 if (!(*handle)->changed)
1646 goto finished;
1647
1648 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1649 if (!repl) {
1650 errno = ENOMEM;
1651 return 0;
1652 }
1653
1654 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001655 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001656 * (*handle)->info.num_entries);
1657 if (!repl->counters) {
1658 free(repl);
1659 errno = ENOMEM;
1660 return 0;
1661 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001662
Marc Bouchere6869a82000-03-20 06:03:29 +00001663 /* These are the counters we're going to put back, later. */
1664 newcounters = malloc(counterlen);
1665 if (!newcounters) {
1666 free(repl->counters);
1667 free(repl);
1668 errno = ENOMEM;
1669 return 0;
1670 }
1671
1672 strcpy(repl->name, (*handle)->info.name);
1673 repl->num_entries = (*handle)->new_number;
1674 repl->size = (*handle)->entries.size;
1675 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1676 sizeof(repl->hook_entry));
1677 memcpy(repl->underflow, (*handle)->info.underflow,
1678 sizeof(repl->underflow));
1679 repl->num_counters = (*handle)->info.num_entries;
1680 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001681 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001682 (*handle)->entries.size);
1683
Rusty Russell79dee072000-05-02 16:45:16 +00001684 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001685 sizeof(*repl) + (*handle)->entries.size) < 0) {
1686 free(repl->counters);
1687 free(repl);
1688 free(newcounters);
1689 return 0;
1690 }
1691
1692 /* Put counters back. */
1693 strcpy(newcounters->name, (*handle)->info.name);
1694 newcounters->num_counters = (*handle)->new_number;
1695 for (i = 0; i < (*handle)->new_number; i++) {
1696 unsigned int mappos = (*handle)->counter_map[i].mappos;
1697 switch ((*handle)->counter_map[i].maptype) {
1698 case COUNTER_MAP_NOMAP:
1699 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001700 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001701 break;
1702
1703 case COUNTER_MAP_NORMAL_MAP:
1704 /* Original read: X.
1705 * Atomic read on replacement: X + Y.
1706 * Currently in kernel: Z.
1707 * Want in kernel: X + Y + Z.
1708 * => Add in X + Y
1709 * => Add in replacement read.
1710 */
1711 newcounters->counters[i] = repl->counters[mappos];
1712 break;
1713
1714 case COUNTER_MAP_ZEROED:
1715 /* Original read: X.
1716 * Atomic read on replacement: X + Y.
1717 * Currently in kernel: Z.
1718 * Want in kernel: Y + Z.
1719 * => Add in Y.
1720 * => Add in (replacement read - original read).
1721 */
1722 subtract_counters(&newcounters->counters[i],
1723 &repl->counters[mappos],
1724 &index2entry(*handle, i)->counters);
1725 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001726
1727 case COUNTER_MAP_SET:
1728 /* Want to set counter (iptables-restore) */
1729
1730 memcpy(&newcounters->counters[i],
1731 &index2entry(*handle, i)->counters,
1732 sizeof(STRUCT_COUNTERS));
1733
1734 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001735 }
1736 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001737
1738#ifdef KERNEL_64_USERSPACE_32
1739 {
1740 /* Kernel will think that pointer should be 64-bits, and get
1741 padding. So we accomodate here (assumption: alignment of
1742 `counters' is on 64-bit boundary). */
1743 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1744 if ((unsigned long)&newcounters->counters % 8 != 0) {
1745 fprintf(stderr,
1746 "counters alignment incorrect! Mail rusty!\n");
1747 abort();
1748 }
1749 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001750 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001751#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001752
Rusty Russell79dee072000-05-02 16:45:16 +00001753 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1754 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001755 free(repl->counters);
1756 free(repl);
1757 free(newcounters);
1758 return 0;
1759 }
1760
1761 free(repl->counters);
1762 free(repl);
1763 free(newcounters);
1764
1765 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001766 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001767 return 1;
1768}
1769
1770/* Get raw socket. */
1771int
Rusty Russell79dee072000-05-02 16:45:16 +00001772TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001773{
1774 return sockfd;
1775}
1776
1777/* Translates errno numbers into more human-readable form than strerror. */
1778const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001779TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001780{
1781 unsigned int i;
1782 struct table_struct {
1783 void *fn;
1784 int err;
1785 const char *message;
1786 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001787 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001788 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001789 { TC_INIT, ENOENT,
1790 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001791 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1792 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1793 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001794 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001795 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1796 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1797 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1798 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001799 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1800 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001801 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1802 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001803 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001804 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001805 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001806 { TC_CHECK_PACKET, ENOSYS,
1807 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001808 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001809 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001810 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001811 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001812 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001813 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001814 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001815
1816 { NULL, 0, "Incompatible with this kernel" },
1817 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1818 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1819 { NULL, ENOMEM, "Memory allocation problem" },
1820 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001821 };
1822
1823 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1824 if ((!table[i].fn || table[i].fn == iptc_fn)
1825 && table[i].err == err)
1826 return table[i].message;
1827 }
1828
1829 return strerror(err);
1830}