blob: 985b9b0d1ef020e4deff2e612b2c9ede63716f77 [file] [log] [blame]
Harald Welte80fe35d2002-05-29 13:08:15 +00001/* Library which manipulates firewall rules. Version $Revision: 1.34 $ */
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
11/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12 COPYING for details). */
13
Marc Bouchere6869a82000-03-20 06:03:29 +000014#ifndef IPT_LIB_DIR
15#define IPT_LIB_DIR "/usr/local/lib/iptables"
16#endif
17
Rusty Russell4e242f82000-05-31 06:33:50 +000018#ifndef __OPTIMIZE__
Harald Welteec81ca72001-05-26 04:35:49 +000019STRUCT_ENTRY_TARGET *
Rusty Russell79dee072000-05-02 16:45:16 +000020GET_TARGET(STRUCT_ENTRY *e)
Rusty Russell30fd6e52000-04-23 09:16:06 +000021{
22 return (void *)e + e->target_offset;
23}
24#endif
25
Marc Bouchere6869a82000-03-20 06:03:29 +000026static int sockfd = -1;
27static void *iptc_fn = NULL;
28
29static const char *hooknames[]
Rusty Russell79dee072000-05-02 16:45:16 +000030= { [HOOK_PRE_ROUTING] "PREROUTING",
31 [HOOK_LOCAL_IN] "INPUT",
32 [HOOK_FORWARD] "FORWARD",
33 [HOOK_LOCAL_OUT] "OUTPUT",
Rusty Russell10758b72000-09-14 07:37:33 +000034 [HOOK_POST_ROUTING] "POSTROUTING",
35#ifdef HOOK_DROPPING
36 [HOOK_DROPPING] "DROPPING"
37#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000038};
39
40struct counter_map
41{
42 enum {
43 COUNTER_MAP_NOMAP,
44 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000045 COUNTER_MAP_ZEROED,
46 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000047 } maptype;
48 unsigned int mappos;
49};
50
51/* Convenience structures */
52struct ipt_error_target
53{
Rusty Russell79dee072000-05-02 16:45:16 +000054 STRUCT_ENTRY_TARGET t;
55 char error[TABLE_MAXNAMELEN];
Marc Bouchere6869a82000-03-20 06:03:29 +000056};
57
Rusty Russell30fd6e52000-04-23 09:16:06 +000058struct chain_cache
59{
Rusty Russell79dee072000-05-02 16:45:16 +000060 char name[TABLE_MAXNAMELEN];
Rusty Russell30fd6e52000-04-23 09:16:06 +000061 /* This is the first rule in chain. */
Rusty Russell79dee072000-05-02 16:45:16 +000062 STRUCT_ENTRY *start;
Rusty Russell30fd6e52000-04-23 09:16:06 +000063 /* Last rule in chain */
Rusty Russell79dee072000-05-02 16:45:16 +000064 STRUCT_ENTRY *end;
Rusty Russell30fd6e52000-04-23 09:16:06 +000065};
66
Rusty Russell79dee072000-05-02 16:45:16 +000067STRUCT_TC_HANDLE
Marc Bouchere6869a82000-03-20 06:03:29 +000068{
69 /* Have changes been made? */
70 int changed;
71 /* Size in here reflects original state. */
Rusty Russell79dee072000-05-02 16:45:16 +000072 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +000073
74 struct counter_map *counter_map;
75 /* Array of hook names */
76 const char **hooknames;
77
Rusty Russell30fd6e52000-04-23 09:16:06 +000078 /* Cached position of chain heads (NULL = no cache). */
79 unsigned int cache_num_chains;
80 unsigned int cache_num_builtins;
81 struct chain_cache *cache_chain_heads;
82
83 /* Chain iterator: current chain cache entry. */
84 struct chain_cache *cache_chain_iteration;
85
86 /* Rule iterator: terminal rule */
Rusty Russell79dee072000-05-02 16:45:16 +000087 STRUCT_ENTRY *cache_rule_end;
Rusty Russell175f6412000-03-24 09:32:20 +000088
Marc Bouchere6869a82000-03-20 06:03:29 +000089 /* Number in here reflects current state. */
90 unsigned int new_number;
Rusty Russell79dee072000-05-02 16:45:16 +000091 STRUCT_GET_ENTRIES entries;
Marc Bouchere6869a82000-03-20 06:03:29 +000092};
93
Rusty Russell175f6412000-03-24 09:32:20 +000094static void
Rusty Russell79dee072000-05-02 16:45:16 +000095set_changed(TC_HANDLE_T h)
Rusty Russell175f6412000-03-24 09:32:20 +000096{
Rusty Russell30fd6e52000-04-23 09:16:06 +000097 if (h->cache_chain_heads) {
98 free(h->cache_chain_heads);
99 h->cache_chain_heads = NULL;
100 h->cache_num_chains = 0;
101 h->cache_chain_iteration = NULL;
102 h->cache_rule_end = NULL;
103 }
Rusty Russell175f6412000-03-24 09:32:20 +0000104 h->changed = 1;
105}
106
Harald Welte380ba5f2002-02-13 16:19:55 +0000107#ifdef IPTC_DEBUG
Rusty Russell79dee072000-05-02 16:45:16 +0000108static void do_check(TC_HANDLE_T h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000109#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000110#else
111#define CHECK(h)
112#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000113
114static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000115get_number(const STRUCT_ENTRY *i,
116 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000117 unsigned int *pos)
118{
119 if (i == seek)
120 return 1;
121 (*pos)++;
122 return 0;
123}
124
125static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000126entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000127{
128 unsigned int pos = 0;
129
Rusty Russell725d97a2000-07-07 08:54:22 +0000130 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000131 get_number, seek, &pos) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000132 fprintf(stderr, "ERROR: offset %i not an entry!\n",
Rusty Russell725d97a2000-07-07 08:54:22 +0000133 (char *)seek - (char *)h->entries.entrytable);
Marc Bouchere6869a82000-03-20 06:03:29 +0000134 abort();
135 }
136 return pos;
137}
138
139static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000140get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000141 unsigned int number,
142 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000143 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000144{
145 if (*pos == number) {
146 *pe = i;
147 return 1;
148 }
149 (*pos)++;
150 return 0;
151}
152
Rusty Russell79dee072000-05-02 16:45:16 +0000153static STRUCT_ENTRY *
154index2entry(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000155{
156 unsigned int pos = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000157 STRUCT_ENTRY *ret = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000158
Rusty Russell725d97a2000-07-07 08:54:22 +0000159 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000160 get_entry_n, index, &pos, &ret);
Marc Bouchere6869a82000-03-20 06:03:29 +0000161
162 return ret;
163}
164
Rusty Russell79dee072000-05-02 16:45:16 +0000165static inline STRUCT_ENTRY *
166get_entry(TC_HANDLE_T h, unsigned int offset)
Marc Bouchere6869a82000-03-20 06:03:29 +0000167{
Rusty Russell725d97a2000-07-07 08:54:22 +0000168 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000169}
170
171static inline unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000172entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
Marc Bouchere6869a82000-03-20 06:03:29 +0000173{
Rusty Russell725d97a2000-07-07 08:54:22 +0000174 return (char *)e - (char *)h->entries.entrytable;
Marc Bouchere6869a82000-03-20 06:03:29 +0000175}
176
177static unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000178index2offset(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000179{
180 return entry2offset(h, index2entry(h, index));
181}
182
183static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000184get_errorlabel(TC_HANDLE_T h, unsigned int offset)
Marc Bouchere6869a82000-03-20 06:03:29 +0000185{
Rusty Russell79dee072000-05-02 16:45:16 +0000186 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000187
188 e = get_entry(h, offset);
Rusty Russell67088e72000-05-10 01:18:57 +0000189 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000190 fprintf(stderr, "ERROR: offset %u not an error node!\n",
191 offset);
192 abort();
193 }
194
Rusty Russell79dee072000-05-02 16:45:16 +0000195 return (const char *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000196}
197
198/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000199static TC_HANDLE_T
Marc Bouchere6869a82000-03-20 06:03:29 +0000200alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
201{
202 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000203 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000204
Rusty Russell79dee072000-05-02 16:45:16 +0000205 len = sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000206 + size
207 + num_rules * sizeof(struct counter_map);
208
209 if ((h = malloc(len)) == NULL) {
210 errno = ENOMEM;
211 return NULL;
212 }
213
214 h->changed = 0;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000215 h->cache_num_chains = 0;
216 h->cache_chain_heads = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000217 h->counter_map = (void *)h
Rusty Russell79dee072000-05-02 16:45:16 +0000218 + sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000219 + size;
220 strcpy(h->info.name, tablename);
221 strcpy(h->entries.name, tablename);
222
223 return h;
224}
225
Rusty Russell79dee072000-05-02 16:45:16 +0000226TC_HANDLE_T
227TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000228{
Rusty Russell79dee072000-05-02 16:45:16 +0000229 TC_HANDLE_T h;
230 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +0000231 unsigned int i;
232 int tmp;
233 socklen_t s;
234
Rusty Russell79dee072000-05-02 16:45:16 +0000235 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000236
Harald Welte366454b2002-01-07 13:46:50 +0000237 if (sockfd != -1)
238 close(sockfd);
239
Rusty Russell79dee072000-05-02 16:45:16 +0000240 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000241 if (sockfd < 0)
242 return NULL;
243
244 s = sizeof(info);
Rusty Russell79dee072000-05-02 16:45:16 +0000245 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000246 errno = EINVAL;
247 return NULL;
248 }
249 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000250 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000251 return NULL;
252
253 if ((h = alloc_handle(info.name, info.size, info.num_entries))
254 == NULL)
255 return NULL;
256
257/* Too hard --RR */
258#if 0
259 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
260 dynlib = dlopen(pathname, RTLD_NOW);
261 if (!dynlib) {
262 errno = ENOENT;
263 return NULL;
264 }
265 h->hooknames = dlsym(dynlib, "hooknames");
266 if (!h->hooknames) {
267 errno = ENOENT;
268 return NULL;
269 }
270#else
271 h->hooknames = hooknames;
272#endif
273
274 /* Initialize current state */
275 h->info = info;
276 h->new_number = h->info.num_entries;
277 for (i = 0; i < h->info.num_entries; i++)
278 h->counter_map[i]
279 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
280
281 h->entries.size = h->info.size;
282
Rusty Russell79dee072000-05-02 16:45:16 +0000283 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000284
Rusty Russell79dee072000-05-02 16:45:16 +0000285 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000286 &tmp) < 0) {
287 free(h);
288 return NULL;
289 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000290
Marc Bouchere6869a82000-03-20 06:03:29 +0000291 CHECK(h);
292 return h;
293}
294
Marc Bouchere6869a82000-03-20 06:03:29 +0000295static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000296print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000297{
Rusty Russell228e98d2000-04-27 10:28:06 +0000298 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000299 return 0;
300}
301
Rusty Russell79dee072000-05-02 16:45:16 +0000302static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
303
Marc Bouchere6869a82000-03-20 06:03:29 +0000304void
Rusty Russell79dee072000-05-02 16:45:16 +0000305TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000306{
307 CHECK(handle);
308
309 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000310 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000311 handle->new_number, handle->entries.size);
312 printf("Table `%s'\n", handle->info.name);
313 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000314 handle->info.hook_entry[HOOK_PRE_ROUTING],
315 handle->info.hook_entry[HOOK_LOCAL_IN],
316 handle->info.hook_entry[HOOK_FORWARD],
317 handle->info.hook_entry[HOOK_LOCAL_OUT],
318 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000319 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000320 handle->info.underflow[HOOK_PRE_ROUTING],
321 handle->info.underflow[HOOK_LOCAL_IN],
322 handle->info.underflow[HOOK_FORWARD],
323 handle->info.underflow[HOOK_LOCAL_OUT],
324 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000325
Rusty Russell725d97a2000-07-07 08:54:22 +0000326 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000327 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000328}
329
Rusty Russell30fd6e52000-04-23 09:16:06 +0000330/* Returns 0 if not hook entry, else hooknumber + 1 */
331static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000332is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000333{
334 unsigned int i;
335
Rusty Russell79dee072000-05-02 16:45:16 +0000336 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000337 if ((h->info.valid_hooks & (1 << i))
338 && get_entry(h, h->info.hook_entry[i]) == e)
339 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000340 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000341 return 0;
342}
343
Rusty Russell30fd6e52000-04-23 09:16:06 +0000344static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000345add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000346{
347 unsigned int builtin;
348
349 /* Last entry. End it. */
350 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
351 /* This is the ERROR node at end of the table */
352 h->cache_chain_heads[h->cache_num_chains-1].end = *prev;
353 return 0;
354 }
355
356 /* We know this is the start of a new chain if it's an ERROR
357 target, or a hook entry point */
Rusty Russell67088e72000-05-10 01:18:57 +0000358 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000359 /* prev was last entry in previous chain */
360 h->cache_chain_heads[h->cache_num_chains-1].end
361 = *prev;
362
363 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
Rusty Russell79dee072000-05-02 16:45:16 +0000364 (const char *)GET_TARGET(e)->data);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000365 h->cache_chain_heads[h->cache_num_chains].start
366 = (void *)e + e->next_offset;
367 h->cache_num_chains++;
368 } else if ((builtin = is_hook_entry(e, h)) != 0) {
369 if (h->cache_num_chains > 0)
370 /* prev was last entry in previous chain */
371 h->cache_chain_heads[h->cache_num_chains-1].end
372 = *prev;
373
374 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
375 h->hooknames[builtin-1]);
376 h->cache_chain_heads[h->cache_num_chains].start
377 = (void *)e;
378 h->cache_num_chains++;
379 }
380
381 *prev = e;
382 return 0;
383}
384
Rusty Russell30fd6e52000-04-23 09:16:06 +0000385static int alphasort(const void *a, const void *b)
386{
387 return strcmp(((struct chain_cache *)a)->name,
388 ((struct chain_cache *)b)->name);
389}
390
Rusty Russell79dee072000-05-02 16:45:16 +0000391static int populate_cache(TC_HANDLE_T h)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000392{
393 unsigned int i;
Rusty Russell79dee072000-05-02 16:45:16 +0000394 STRUCT_ENTRY *prev;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000395
396 /* # chains < # rules / 2 + num builtins - 1 */
397 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
398 * sizeof(struct chain_cache));
399 if (!h->cache_chain_heads) {
400 errno = ENOMEM;
401 return 0;
402 }
403
404 h->cache_num_chains = 0;
405 h->cache_num_builtins = 0;
406
407 /* Count builtins */
Rusty Russell79dee072000-05-02 16:45:16 +0000408 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000409 if (h->info.valid_hooks & (1 << i))
410 h->cache_num_builtins++;
411 }
412
413 prev = NULL;
Rusty Russell725d97a2000-07-07 08:54:22 +0000414 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000415 add_chain, h, &prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000416
Rusty Russell30fd6e52000-04-23 09:16:06 +0000417 qsort(h->cache_chain_heads + h->cache_num_builtins,
418 h->cache_num_chains - h->cache_num_builtins,
419 sizeof(struct chain_cache), alphasort);
420
421 return 1;
422}
423
424/* Returns cache ptr if found, otherwise NULL. */
425static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000426find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000427{
Rusty Russell849779c2000-04-23 15:51:51 +0000428 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000429
430 if (handle->cache_chain_heads == NULL
431 && !populate_cache(handle))
432 return NULL;
433
Rusty Russell849779c2000-04-23 15:51:51 +0000434 /* FIXME: Linear search through builtins, then binary --RR */
435 for (i = 0; i < handle->cache_num_chains; i++) {
436 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
437 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000438 }
439
Rusty Russell849779c2000-04-23 15:51:51 +0000440 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000441}
442
Marc Bouchere6869a82000-03-20 06:03:29 +0000443/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000444int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000445{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000446 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000447}
448
449/* Returns the position of the final (ie. unconditional) element. */
450static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000451get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000452{
453 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000454 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000455
456 last_off = start;
457 e = get_entry(handle, start);
458
459 /* Terminate when we meet a error label or a hook entry. */
460 for (off = start + e->next_offset;
461 off < handle->entries.size;
462 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000463 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000464 unsigned int i;
465
466 e = get_entry(handle, off);
467
468 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000469 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000470 if ((handle->info.valid_hooks & (1 << i))
471 && off == handle->info.hook_entry[i])
472 return last_off;
473 }
474
475 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000476 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000477 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000478 return last_off;
479 }
480 /* SHOULD NEVER HAPPEN */
481 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
482 handle->entries.size, off);
483 abort();
484}
485
Rusty Russell30fd6e52000-04-23 09:16:06 +0000486/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000487const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000488TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000489{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000490 if ((*handle)->cache_chain_heads == NULL
491 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000492 return NULL;
493
Rusty Russell30fd6e52000-04-23 09:16:06 +0000494 (*handle)->cache_chain_iteration
495 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000496
Rusty Russell30fd6e52000-04-23 09:16:06 +0000497 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000498}
499
Rusty Russell30fd6e52000-04-23 09:16:06 +0000500/* Iterator functions to run through the chains. Returns NULL at end. */
501const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000502TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000503{
504 (*handle)->cache_chain_iteration++;
505
506 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
507 == (*handle)->cache_num_chains)
508 return NULL;
509
510 return (*handle)->cache_chain_iteration->name;
511}
512
513/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000514const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000515TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000516{
517 struct chain_cache *c;
518
519 c = find_label(chain, *handle);
520 if (!c) {
521 errno = ENOENT;
522 return NULL;
523 }
524
525 /* Empty chain: single return/policy rule */
526 if (c->start == c->end)
527 return NULL;
528
529 (*handle)->cache_rule_end = c->end;
530 return c->start;
531}
532
533/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000534const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000535TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000536{
537 if ((void *)prev + prev->next_offset
538 == (void *)(*handle)->cache_rule_end)
539 return NULL;
540
541 return (void *)prev + prev->next_offset;
542}
543
544#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000545/* How many rules in this chain? */
546unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000547TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000548{
549 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000550 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000551
552 CHECK(*handle);
553 if (!find_label(&off, chain, *handle)) {
554 errno = ENOENT;
555 return (unsigned int)-1;
556 }
557
558 start = get_entry(*handle, off);
559 end = get_entry(*handle, get_chain_end(*handle, off));
560
561 return entry2index(*handle, end) - entry2index(*handle, start);
562}
563
564/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000565const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
566 unsigned int n,
567 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000568{
569 unsigned int pos = 0, chainindex;
570
571 CHECK(*handle);
572 if (!find_label(&pos, chain, *handle)) {
573 errno = ENOENT;
574 return NULL;
575 }
576
577 chainindex = entry2index(*handle, get_entry(*handle, pos));
578
579 return index2entry(*handle, chainindex + n);
580}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000581#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000582
Rusty Russell30fd6e52000-04-23 09:16:06 +0000583static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000584target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000585{
586 int spos;
587 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000588 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000589
Rusty Russell30fd6e52000-04-23 09:16:06 +0000590 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000591 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000592
Rusty Russell79dee072000-05-02 16:45:16 +0000593 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
594 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000595
596 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000597 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000598 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000599 if (spos == RETURN)
600 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000601 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000602 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000603 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000604 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000605 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000606 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000607
608 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
609 entry2offset(handle, e), handle->entries.size,
610 spos);
611 abort();
612 }
613
614 jumpto = get_entry(handle, spos);
615
616 /* Fall through rule */
617 if (jumpto == (void *)e + e->next_offset)
618 return "";
619
620 /* Must point to head of a chain: ie. after error rule */
621 labelidx = entry2index(handle, jumpto) - 1;
622 return get_errorlabel(handle, index2offset(handle, labelidx));
623}
624
625/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000626const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
627 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000628{
Marc Bouchere6869a82000-03-20 06:03:29 +0000629 return target_name(*handle, e);
630}
631
632/* Is this a built-in chain? Actually returns hook + 1. */
633int
Rusty Russell79dee072000-05-02 16:45:16 +0000634TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000635{
636 unsigned int i;
637
Rusty Russell79dee072000-05-02 16:45:16 +0000638 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000639 if ((handle->info.valid_hooks & (1 << i))
640 && handle->hooknames[i]
641 && strcmp(handle->hooknames[i], chain) == 0)
642 return i+1;
643 }
644 return 0;
645}
646
647/* Get the policy of a given built-in chain */
648const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000649TC_GET_POLICY(const char *chain,
650 STRUCT_COUNTERS *counters,
651 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000652{
653 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000654 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000655 int hook;
656
Rusty Russell79dee072000-05-02 16:45:16 +0000657 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000658 if (hook != 0)
659 start = (*handle)->info.hook_entry[hook-1];
660 else
661 return NULL;
662
663 e = get_entry(*handle, get_chain_end(*handle, start));
664 *counters = e->counters;
665
666 return target_name(*handle, e);
667}
668
669static int
Rusty Russell79dee072000-05-02 16:45:16 +0000670correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000671 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000672 unsigned int offset, int delta_offset)
673{
Rusty Russell79dee072000-05-02 16:45:16 +0000674 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000675 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000676
677 /* Trap: insert of fall-through rule. Don't change fall-through
678 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000679 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000680 && t->verdict > (int)offset
681 && !(curr == offset &&
682 t->verdict == curr + e->next_offset)) {
683 t->verdict += delta_offset;
684 }
685
686 return 0;
687}
688
689/* Adjusts standard verdict jump positions after an insertion/deletion. */
690static int
Rusty Russell79dee072000-05-02 16:45:16 +0000691set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000692{
Rusty Russell725d97a2000-07-07 08:54:22 +0000693 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000694 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000695 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000696 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000697
Rusty Russell175f6412000-03-24 09:32:20 +0000698 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000699 return 1;
700}
701
702/* If prepend is set, then we are prepending to a chain: if the
703 * insertion position is an entry point, keep the entry point. */
704static int
705insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000706 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000707 unsigned int offset, unsigned int num_rules_offset,
708 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000709 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000710{
Rusty Russell79dee072000-05-02 16:45:16 +0000711 TC_HANDLE_T newh;
712 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000713 unsigned int i;
714
715 if (offset >= (*handle)->entries.size) {
716 errno = EINVAL;
717 return 0;
718 }
719
720 newinfo = (*handle)->info;
721
722 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000723 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000724 /* Entry points to START of chain, so keep same if
725 inserting on at that point. */
726 if ((*handle)->info.hook_entry[i] > offset)
727 newinfo.hook_entry[i] += rules_size;
728
729 /* Underflow always points to END of chain (policy),
730 so if something is inserted at same point, it
731 should be advanced. */
732 if ((*handle)->info.underflow[i] >= offset)
733 newinfo.underflow[i] += rules_size;
734 }
735
736 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000737 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000738 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000739 if (!newh)
740 return 0;
741 newh->info = newinfo;
742
743 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000744 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000745 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000746 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000747 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000748 memcpy((char *)newh->entries.entrytable + offset + rules_size,
749 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000750 (*handle)->entries.size - offset);
751
752 /* Move counter map. */
753 /* Copy pre... */
754 memcpy(newh->counter_map, (*handle)->counter_map,
755 sizeof(struct counter_map) * num_rules_offset);
756 /* ... copy post */
757 memcpy(newh->counter_map + num_rules_offset + num_rules,
758 (*handle)->counter_map + num_rules_offset,
759 sizeof(struct counter_map) * ((*handle)->new_number
760 - num_rules_offset));
761 /* Set intermediates to no counter copy */
762 for (i = 0; i < num_rules; i++)
763 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000764 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000765
766 newh->new_number = (*handle)->new_number + num_rules;
767 newh->entries.size = (*handle)->entries.size + rules_size;
768 newh->hooknames = (*handle)->hooknames;
769
Rusty Russell30fd6e52000-04-23 09:16:06 +0000770 if ((*handle)->cache_chain_heads)
771 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +0000772 free(*handle);
773 *handle = newh;
774
775 return set_verdict(offset, rules_size, handle);
776}
777
778static int
779delete_rules(unsigned int num_rules, unsigned int rules_size,
780 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000781 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000782{
783 unsigned int i;
784
785 if (offset + rules_size > (*handle)->entries.size) {
786 errno = EINVAL;
787 return 0;
788 }
789
790 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000791 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000792 /* In practice, we never delete up to a hook entry,
793 since the built-in chains are always first,
794 so these two are never equal */
795 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
796 (*handle)->info.hook_entry[i] -= rules_size;
797 else if ((*handle)->info.hook_entry[i] > offset) {
798 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
799 i, (*handle)->info.hook_entry[i], offset);
800 abort();
801 }
802
803 /* Underflow points to policy (terminal) rule in
804 built-in, so sequality is valid here (when deleting
805 the last rule). */
806 if ((*handle)->info.underflow[i] >= offset + rules_size)
807 (*handle)->info.underflow[i] -= rules_size;
808 else if ((*handle)->info.underflow[i] > offset) {
809 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
810 i, (*handle)->info.underflow[i], offset);
811 abort();
812 }
813 }
814
815 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000816 memmove((char *)(*handle)->entries.entrytable + offset,
817 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000818 (*handle)->entries.size - (offset + rules_size));
819
820 /* Move the counter map down. */
821 memmove(&(*handle)->counter_map[num_rules_offset],
822 &(*handle)->counter_map[num_rules_offset + num_rules],
823 sizeof(struct counter_map)
824 * ((*handle)->new_number - (num_rules + num_rules_offset)));
825
826 /* Fix numbers */
827 (*handle)->new_number -= num_rules;
828 (*handle)->entries.size -= rules_size;
829
830 return set_verdict(offset, -(int)rules_size, handle);
831}
832
833static int
Rusty Russell79dee072000-05-02 16:45:16 +0000834standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000835{
Rusty Russell79dee072000-05-02 16:45:16 +0000836 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000837
Rusty Russell79dee072000-05-02 16:45:16 +0000838 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000839
Rusty Russell67088e72000-05-10 01:18:57 +0000840 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000841 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000842 errno = EINVAL;
843 return 0;
844 }
845 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000846 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
847 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000848 t->verdict = verdict;
849
850 return 1;
851}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000852
Marc Bouchere6869a82000-03-20 06:03:29 +0000853static int
Rusty Russell79dee072000-05-02 16:45:16 +0000854map_target(const TC_HANDLE_T handle,
855 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000856 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000857 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000858{
Rusty Russell79dee072000-05-02 16:45:16 +0000859 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000860
861 /* Save old target (except data, which we don't change, except for
862 standard case, where we don't care). */
863 *old = *t;
864
865 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000866 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000867 return standard_map(e, offset + e->next_offset);
868 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000869 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000870 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000871 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000872 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000873 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000874 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000875 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
876 return standard_map(e, RETURN);
877 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000878 /* Can't jump to builtins. */
879 errno = EINVAL;
880 return 0;
881 } else {
882 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000883 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000884
Rusty Russell228e98d2000-04-27 10:28:06 +0000885 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000886 if (c)
887 return standard_map(e, entry2offset(handle, c->start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000888 }
889
890 /* Must be a module? If not, kernel will reject... */
891 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000892 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000893 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000894 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000895 return 1;
896}
897
898static void
Rusty Russell79dee072000-05-02 16:45:16 +0000899unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000900{
Rusty Russell79dee072000-05-02 16:45:16 +0000901 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000902
903 /* Save old target (except data, which we don't change, except for
904 standard case, where we don't care). */
905 *t = *old;
906}
907
908/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
909int
Rusty Russell79dee072000-05-02 16:45:16 +0000910TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
911 const STRUCT_ENTRY *e,
912 unsigned int rulenum,
913 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000914{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000915 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000916 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000917 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000918 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000919 int ret;
920
Rusty Russell79dee072000-05-02 16:45:16 +0000921 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000922 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000923 errno = ENOENT;
924 return 0;
925 }
926
Rusty Russell30fd6e52000-04-23 09:16:06 +0000927 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000928
Rusty Russell14a1c912000-08-26 04:41:10 +0000929 tmp = index2entry(*handle, chainindex + rulenum);
930 if (!tmp || tmp > c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000931 errno = E2BIG;
932 return 0;
933 }
934 offset = index2offset(*handle, chainindex + rulenum);
935
936 /* Mapping target actually alters entry, but that's
937 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +0000938 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000939 return 0;
940
941 ret = insert_rules(1, e->next_offset, e, offset,
942 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000943 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000944 return ret;
945}
946
947/* Atomically replace rule `rulenum' in `chain' with `fw'. */
948int
Rusty Russell79dee072000-05-02 16:45:16 +0000949TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
950 const STRUCT_ENTRY *e,
951 unsigned int rulenum,
952 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000953{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000954 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000955 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000956 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000957 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000958 int ret;
959
Rusty Russell79dee072000-05-02 16:45:16 +0000960 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +0000961
Rusty Russell30fd6e52000-04-23 09:16:06 +0000962 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000963 errno = ENOENT;
964 return 0;
965 }
966
Rusty Russell30fd6e52000-04-23 09:16:06 +0000967 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000968
Rusty Russell14a1c912000-08-26 04:41:10 +0000969 tmp = index2entry(*handle, chainindex + rulenum);
970 if (!tmp || tmp >= c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000971 errno = E2BIG;
972 return 0;
973 }
974
975 offset = index2offset(*handle, chainindex + rulenum);
976 /* Replace = delete and insert. */
977 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
978 offset, chainindex + rulenum, handle))
979 return 0;
980
Rusty Russell79dee072000-05-02 16:45:16 +0000981 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000982 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000983
984 ret = insert_rules(1, e->next_offset, e, offset,
985 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000986 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000987 return ret;
988}
989
990/* Append entry `fw' to chain `chain'. Equivalent to insert with
991 rulenum = length of chain. */
992int
Rusty Russell79dee072000-05-02 16:45:16 +0000993TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
994 const STRUCT_ENTRY *e,
995 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000996{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000997 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +0000998 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +0000999 int ret;
1000
Rusty Russell79dee072000-05-02 16:45:16 +00001001 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001002 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001003 errno = ENOENT;
1004 return 0;
1005 }
1006
Rusty Russell79dee072000-05-02 16:45:16 +00001007 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001008 entry2offset(*handle, c->end), &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001009 return 0;
1010
Rusty Russell30fd6e52000-04-23 09:16:06 +00001011 ret = insert_rules(1, e->next_offset, e,
1012 entry2offset(*handle, c->end),
1013 entry2index(*handle, c->end),
Marc Bouchere6869a82000-03-20 06:03:29 +00001014 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001015 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001016 return ret;
1017}
1018
1019static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001020match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001021 const unsigned char *a_elems,
1022 const unsigned char *b_elems,
1023 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001024{
Rusty Russell79dee072000-05-02 16:45:16 +00001025 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001026 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001027
1028 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001029 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001030
Rusty Russell228e98d2000-04-27 10:28:06 +00001031 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001032 return 1;
1033
Rusty Russell228e98d2000-04-27 10:28:06 +00001034 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001035 return 1;
1036
Rusty Russell73ef09b2000-07-03 10:24:04 +00001037 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001038
Rusty Russell73ef09b2000-07-03 10:24:04 +00001039 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001040 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001041 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001042 *maskptr += i;
1043 return 0;
1044}
1045
1046static inline int
1047target_different(const unsigned char *a_targdata,
1048 const unsigned char *b_targdata,
1049 unsigned int tdatasize,
1050 const unsigned char *mask)
1051{
1052 unsigned int i;
1053 for (i = 0; i < tdatasize; i++)
1054 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1055 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001056
1057 return 0;
1058}
1059
Rusty Russell79dee072000-05-02 16:45:16 +00001060static int
1061is_same(const STRUCT_ENTRY *a,
1062 const STRUCT_ENTRY *b,
1063 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001064
1065/* Delete the first rule in `chain' which matches `fw'. */
1066int
Rusty Russell79dee072000-05-02 16:45:16 +00001067TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1068 const STRUCT_ENTRY *origfw,
1069 unsigned char *matchmask,
1070 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001071{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001072 unsigned int offset;
1073 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001074 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001075
Rusty Russell79dee072000-05-02 16:45:16 +00001076 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001077 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001078 errno = ENOENT;
1079 return 0;
1080 }
1081
1082 fw = malloc(origfw->next_offset);
1083 if (fw == NULL) {
1084 errno = ENOMEM;
1085 return 0;
1086 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001087
Rusty Russell30fd6e52000-04-23 09:16:06 +00001088 for (offset = entry2offset(*handle, c->start);
1089 offset < entry2offset(*handle, c->end);
1090 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001091 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001092
1093 memcpy(fw, origfw, origfw->next_offset);
1094
1095 /* FIXME: handle this in is_same --RR */
1096 if (!map_target(*handle, fw, offset, &discard)) {
1097 free(fw);
1098 return 0;
1099 }
1100 e = get_entry(*handle, offset);
1101
1102#if 0
1103 printf("Deleting:\n");
1104 dump_entry(newe);
1105#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001106 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001107 int ret;
1108 ret = delete_rules(1, e->next_offset,
1109 offset, entry2index(*handle, e),
1110 handle);
1111 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001112 return ret;
1113 }
1114 }
1115
1116 free(fw);
1117 errno = ENOENT;
1118 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001119}
Marc Bouchere6869a82000-03-20 06:03:29 +00001120
1121/* Delete the rule in position `rulenum' in `chain'. */
1122int
Rusty Russell79dee072000-05-02 16:45:16 +00001123TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1124 unsigned int rulenum,
1125 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001126{
Marc Bouchere6869a82000-03-20 06:03:29 +00001127 unsigned int index;
1128 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001129 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001130 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001131
Rusty Russell79dee072000-05-02 16:45:16 +00001132 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001133 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001134 errno = ENOENT;
1135 return 0;
1136 }
1137
Rusty Russell30fd6e52000-04-23 09:16:06 +00001138 index = entry2index(*handle, c->start) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001139
Rusty Russell30fd6e52000-04-23 09:16:06 +00001140 if (index >= entry2index(*handle, c->end)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001141 errno = E2BIG;
1142 return 0;
1143 }
1144
1145 e = index2entry(*handle, index);
1146 if (e == NULL) {
1147 errno = EINVAL;
1148 return 0;
1149 }
1150
1151 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1152 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001153 return ret;
1154}
1155
1156/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1157 NULL and sets errno. */
1158const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001159TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1160 STRUCT_ENTRY *entry,
1161 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001162{
1163 errno = ENOSYS;
1164 return NULL;
1165}
1166
1167/* Flushes the entries in the given chain (ie. empties chain). */
1168int
Rusty Russell79dee072000-05-02 16:45:16 +00001169TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001170{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001171 unsigned int startindex, endindex;
1172 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001173 int ret;
1174
Rusty Russell79dee072000-05-02 16:45:16 +00001175 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001176 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001177 errno = ENOENT;
1178 return 0;
1179 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001180 startindex = entry2index(*handle, c->start);
1181 endindex = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001182
1183 ret = delete_rules(endindex - startindex,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001184 (char *)c->end - (char *)c->start,
1185 entry2offset(*handle, c->start), startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001186 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001187 return ret;
1188}
1189
1190/* Zeroes the counters in a chain. */
1191int
Rusty Russell79dee072000-05-02 16:45:16 +00001192TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001193{
1194 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001195 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001196
Rusty Russell30fd6e52000-04-23 09:16:06 +00001197 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001198 errno = ENOENT;
1199 return 0;
1200 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001201
Rusty Russell30fd6e52000-04-23 09:16:06 +00001202 i = entry2index(*handle, c->start);
1203 end = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001204
1205 for (; i <= end; i++) {
1206 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1207 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1208 }
Rusty Russell175f6412000-03-24 09:32:20 +00001209 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001210
Marc Bouchere6869a82000-03-20 06:03:29 +00001211 return 1;
1212}
1213
Harald Welte1cef74d2001-01-05 15:22:59 +00001214STRUCT_COUNTERS *
1215TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1216 unsigned int rulenum,
1217 TC_HANDLE_T *handle)
1218{
1219 STRUCT_ENTRY *e;
1220 struct chain_cache *c;
1221 unsigned int chainindex, end;
1222
1223 iptc_fn = TC_READ_COUNTER;
1224 CHECK(*handle);
1225
1226 if (!(c = find_label(chain, *handle))) {
1227 errno = ENOENT;
1228 return NULL;
1229 }
1230
1231 chainindex = entry2index(*handle, c->start);
1232 end = entry2index(*handle, c->end);
1233
1234 if (chainindex + rulenum > end) {
1235 errno = E2BIG;
1236 return NULL;
1237 }
1238
1239 e = index2entry(*handle, chainindex + rulenum);
1240
1241 return &e->counters;
1242}
1243
1244int
1245TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1246 unsigned int rulenum,
1247 TC_HANDLE_T *handle)
1248{
1249 STRUCT_ENTRY *e;
1250 struct chain_cache *c;
1251 unsigned int chainindex, end;
1252
1253 iptc_fn = TC_ZERO_COUNTER;
1254 CHECK(*handle);
1255
1256 if (!(c = find_label(chain, *handle))) {
1257 errno = ENOENT;
1258 return 0;
1259 }
1260
1261 chainindex = entry2index(*handle, c->start);
1262 end = entry2index(*handle, c->end);
1263
1264 if (chainindex + rulenum > end) {
1265 errno = E2BIG;
1266 return 0;
1267 }
1268
1269 e = index2entry(*handle, chainindex + rulenum);
1270
1271 if ((*handle)->counter_map[chainindex + rulenum].maptype
1272 == COUNTER_MAP_NORMAL_MAP) {
1273 (*handle)->counter_map[chainindex + rulenum].maptype
1274 = COUNTER_MAP_ZEROED;
1275 }
1276
1277 set_changed(*handle);
1278
1279 return 1;
1280}
1281
1282int
1283TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1284 unsigned int rulenum,
1285 STRUCT_COUNTERS *counters,
1286 TC_HANDLE_T *handle)
1287{
1288 STRUCT_ENTRY *e;
1289 struct chain_cache *c;
1290 unsigned int chainindex, end;
1291
1292 iptc_fn = TC_SET_COUNTER;
1293 CHECK(*handle);
1294
1295 if (!(c = find_label(chain, *handle))) {
1296 errno = ENOENT;
1297 return 0;
1298 }
1299
1300 chainindex = entry2index(*handle, c->start);
1301 end = entry2index(*handle, c->end);
1302
1303 if (chainindex + rulenum > end) {
1304 errno = E2BIG;
1305 return 0;
1306 }
1307
1308 e = index2entry(*handle, chainindex + rulenum);
1309
1310 (*handle)->counter_map[chainindex + rulenum].maptype
1311 = COUNTER_MAP_SET;
1312
1313 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1314
1315 set_changed(*handle);
1316
1317 return 1;
1318}
1319
Marc Bouchere6869a82000-03-20 06:03:29 +00001320/* Creates a new chain. */
1321/* To create a chain, create two rules: error node and unconditional
1322 * return. */
1323int
Rusty Russell79dee072000-05-02 16:45:16 +00001324TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001325{
Marc Bouchere6869a82000-03-20 06:03:29 +00001326 int ret;
1327 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001328 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001329 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001330 STRUCT_ENTRY ret;
1331 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001332 } newc;
1333
Rusty Russell79dee072000-05-02 16:45:16 +00001334 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001335
1336 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1337 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001338 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001339 || strcmp(chain, LABEL_DROP) == 0
1340 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001341 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001342 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001343 errno = EEXIST;
1344 return 0;
1345 }
1346
Rusty Russell79dee072000-05-02 16:45:16 +00001347 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001348 errno = EINVAL;
1349 return 0;
1350 }
1351
1352 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001353 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001354 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001355 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001356 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001357 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001358 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001359 strcpy(newc.name.error, chain);
1360
Rusty Russell79dee072000-05-02 16:45:16 +00001361 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001362 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001363 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001364 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001365 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001366 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001367 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001368 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001369
1370 /* Add just before terminal entry */
1371 ret = insert_rules(2, sizeof(newc), &newc.head,
1372 index2offset(*handle, (*handle)->new_number - 1),
1373 (*handle)->new_number - 1,
1374 0, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001375 return ret;
1376}
1377
1378static int
Rusty Russell79dee072000-05-02 16:45:16 +00001379count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001380{
Rusty Russell79dee072000-05-02 16:45:16 +00001381 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001382
Rusty Russell79dee072000-05-02 16:45:16 +00001383 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1384 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001385
1386 if (t->verdict == offset)
1387 (*ref)++;
1388 }
1389
1390 return 0;
1391}
1392
1393/* Get the number of references to this chain. */
1394int
Rusty Russell79dee072000-05-02 16:45:16 +00001395TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1396 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001397{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001398 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001399
Rusty Russell30fd6e52000-04-23 09:16:06 +00001400 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001401 errno = ENOENT;
1402 return 0;
1403 }
1404
1405 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001406 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001407 (*handle)->entries.size,
1408 count_ref, entry2offset(*handle, c->start), ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001409 return 1;
1410}
1411
1412/* Deletes a chain. */
1413int
Rusty Russell79dee072000-05-02 16:45:16 +00001414TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001415{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001416 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001417 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001418 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001419 int ret;
1420
Rusty Russell79dee072000-05-02 16:45:16 +00001421 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001422 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001423
Rusty Russell79dee072000-05-02 16:45:16 +00001424 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001425
Rusty Russell79dee072000-05-02 16:45:16 +00001426 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001427 errno = EINVAL;
1428 return 0;
1429 }
1430
1431 if (references > 0) {
1432 errno = EMLINK;
1433 return 0;
1434 }
1435
Rusty Russell30fd6e52000-04-23 09:16:06 +00001436 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001437 errno = ENOENT;
1438 return 0;
1439 }
1440
Rusty Russell30fd6e52000-04-23 09:16:06 +00001441 if ((void *)c->start != c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001442 errno = ENOTEMPTY;
1443 return 0;
1444 }
1445
1446 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001447 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001448 labeloff = index2offset(*handle, labelidx);
1449
1450 ret = delete_rules(2,
1451 get_entry(*handle, labeloff)->next_offset
Rusty Russell30fd6e52000-04-23 09:16:06 +00001452 + c->start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001453 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001454 return ret;
1455}
1456
1457/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001458int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1459 const IPT_CHAINLABEL newname,
1460 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001461{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001462 unsigned int labeloff, labelidx;
1463 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001464 struct ipt_error_target *t;
1465
Rusty Russell79dee072000-05-02 16:45:16 +00001466 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001467
Harald Welte1de80462000-10-30 12:00:27 +00001468 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1469 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001470 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001471 || strcmp(newname, LABEL_DROP) == 0
1472 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001473 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001474 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001475 errno = EEXIST;
1476 return 0;
1477 }
1478
Rusty Russell30fd6e52000-04-23 09:16:06 +00001479 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001480 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001481 errno = ENOENT;
1482 return 0;
1483 }
1484
Rusty Russell79dee072000-05-02 16:45:16 +00001485 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001486 errno = EINVAL;
1487 return 0;
1488 }
1489
1490 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001491 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001492 labeloff = index2offset(*handle, labelidx);
1493
1494 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001495 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001496
1497 memset(t->error, 0, sizeof(t->error));
1498 strcpy(t->error, newname);
Rusty Russell175f6412000-03-24 09:32:20 +00001499 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001500
Marc Bouchere6869a82000-03-20 06:03:29 +00001501 return 1;
1502}
1503
1504/* Sets the policy on a built-in chain. */
1505int
Rusty Russell79dee072000-05-02 16:45:16 +00001506TC_SET_POLICY(const IPT_CHAINLABEL chain,
1507 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001508 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001509 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001510{
1511 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001512 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001513 STRUCT_ENTRY *e;
1514 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001515
Rusty Russell79dee072000-05-02 16:45:16 +00001516 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001517 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001518 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001519 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001520 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001521 return 0;
1522 } else
1523 hook--;
1524
1525 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1526 if (policyoff != (*handle)->info.underflow[hook]) {
1527 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1528 chain, policyoff, (*handle)->info.underflow[hook]);
1529 return 0;
1530 }
1531
1532 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001533 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001534
Rusty Russell79dee072000-05-02 16:45:16 +00001535 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001536 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001537 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001538 t->verdict = -NF_DROP - 1;
1539 else {
1540 errno = EINVAL;
1541 return 0;
1542 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001543
1544 ctrindex = entry2index(*handle, e);
1545
1546 if (counters) {
1547 /* set byte and packet counters */
1548 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1549
1550 (*handle)->counter_map[ctrindex].maptype
1551 = COUNTER_MAP_SET;
1552
1553 } else {
1554 (*handle)->counter_map[ctrindex]
1555 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1556 }
1557
Rusty Russell175f6412000-03-24 09:32:20 +00001558 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001559
Marc Bouchere6869a82000-03-20 06:03:29 +00001560 return 1;
1561}
1562
1563/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001564 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001565 libiptc.c:833: fixed or forbidden register was spilled.
1566 This may be due to a compiler bug or to impossible asm
1567 statements or clauses.
1568*/
1569static void
Rusty Russell79dee072000-05-02 16:45:16 +00001570subtract_counters(STRUCT_COUNTERS *answer,
1571 const STRUCT_COUNTERS *a,
1572 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001573{
1574 answer->pcnt = a->pcnt - b->pcnt;
1575 answer->bcnt = a->bcnt - b->bcnt;
1576}
1577
1578int
Rusty Russell79dee072000-05-02 16:45:16 +00001579TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001580{
1581 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001582 STRUCT_REPLACE *repl;
1583 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001584 unsigned int i;
1585 size_t counterlen
Rusty Russell79dee072000-05-02 16:45:16 +00001586 = sizeof(STRUCT_COUNTERS_INFO)
1587 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00001588
1589 CHECK(*handle);
1590#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001591 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001592#endif
1593
1594 /* Don't commit if nothing changed. */
1595 if (!(*handle)->changed)
1596 goto finished;
1597
1598 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1599 if (!repl) {
1600 errno = ENOMEM;
1601 return 0;
1602 }
1603
1604 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001605 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001606 * (*handle)->info.num_entries);
1607 if (!repl->counters) {
1608 free(repl);
1609 errno = ENOMEM;
1610 return 0;
1611 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001612
Marc Bouchere6869a82000-03-20 06:03:29 +00001613 /* These are the counters we're going to put back, later. */
1614 newcounters = malloc(counterlen);
1615 if (!newcounters) {
1616 free(repl->counters);
1617 free(repl);
1618 errno = ENOMEM;
1619 return 0;
1620 }
1621
1622 strcpy(repl->name, (*handle)->info.name);
1623 repl->num_entries = (*handle)->new_number;
1624 repl->size = (*handle)->entries.size;
1625 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1626 sizeof(repl->hook_entry));
1627 memcpy(repl->underflow, (*handle)->info.underflow,
1628 sizeof(repl->underflow));
1629 repl->num_counters = (*handle)->info.num_entries;
1630 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001631 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001632 (*handle)->entries.size);
1633
Rusty Russell79dee072000-05-02 16:45:16 +00001634 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001635 sizeof(*repl) + (*handle)->entries.size) < 0) {
1636 free(repl->counters);
1637 free(repl);
1638 free(newcounters);
1639 return 0;
1640 }
1641
1642 /* Put counters back. */
1643 strcpy(newcounters->name, (*handle)->info.name);
1644 newcounters->num_counters = (*handle)->new_number;
1645 for (i = 0; i < (*handle)->new_number; i++) {
1646 unsigned int mappos = (*handle)->counter_map[i].mappos;
1647 switch ((*handle)->counter_map[i].maptype) {
1648 case COUNTER_MAP_NOMAP:
1649 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001650 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001651 break;
1652
1653 case COUNTER_MAP_NORMAL_MAP:
1654 /* Original read: X.
1655 * Atomic read on replacement: X + Y.
1656 * Currently in kernel: Z.
1657 * Want in kernel: X + Y + Z.
1658 * => Add in X + Y
1659 * => Add in replacement read.
1660 */
1661 newcounters->counters[i] = repl->counters[mappos];
1662 break;
1663
1664 case COUNTER_MAP_ZEROED:
1665 /* Original read: X.
1666 * Atomic read on replacement: X + Y.
1667 * Currently in kernel: Z.
1668 * Want in kernel: Y + Z.
1669 * => Add in Y.
1670 * => Add in (replacement read - original read).
1671 */
1672 subtract_counters(&newcounters->counters[i],
1673 &repl->counters[mappos],
1674 &index2entry(*handle, i)->counters);
1675 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001676
1677 case COUNTER_MAP_SET:
1678 /* Want to set counter (iptables-restore) */
1679
1680 memcpy(&newcounters->counters[i],
1681 &index2entry(*handle, i)->counters,
1682 sizeof(STRUCT_COUNTERS));
1683
1684 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001685 }
1686 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001687
1688#ifdef KERNEL_64_USERSPACE_32
1689 {
1690 /* Kernel will think that pointer should be 64-bits, and get
1691 padding. So we accomodate here (assumption: alignment of
1692 `counters' is on 64-bit boundary). */
1693 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1694 if ((unsigned long)&newcounters->counters % 8 != 0) {
1695 fprintf(stderr,
1696 "counters alignment incorrect! Mail rusty!\n");
1697 abort();
1698 }
1699 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001700 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001701#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001702
Rusty Russell79dee072000-05-02 16:45:16 +00001703 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1704 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001705 free(repl->counters);
1706 free(repl);
1707 free(newcounters);
1708 return 0;
1709 }
1710
1711 free(repl->counters);
1712 free(repl);
1713 free(newcounters);
1714
1715 finished:
Rusty Russell30fd6e52000-04-23 09:16:06 +00001716 if ((*handle)->cache_chain_heads)
1717 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +00001718 free(*handle);
1719 *handle = NULL;
1720 return 1;
1721}
1722
1723/* Get raw socket. */
1724int
Rusty Russell79dee072000-05-02 16:45:16 +00001725TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001726{
1727 return sockfd;
1728}
1729
1730/* Translates errno numbers into more human-readable form than strerror. */
1731const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001732TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001733{
1734 unsigned int i;
1735 struct table_struct {
1736 void *fn;
1737 int err;
1738 const char *message;
1739 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001740 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001741 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001742 { TC_INIT, ENOENT,
1743 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001744 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1745 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1746 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001747 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001748 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1749 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1750 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1751 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001752 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1753 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001754 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1755 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001756 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001757 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001758 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001759 { TC_CHECK_PACKET, ENOSYS,
1760 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001761 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001762 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001763 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001764 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001765 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001766 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001767 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001768
1769 { NULL, 0, "Incompatible with this kernel" },
1770 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1771 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1772 { NULL, ENOMEM, "Memory allocation problem" },
1773 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001774 };
1775
1776 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1777 if ((!table[i].fn || table[i].fn == iptc_fn)
1778 && table[i].err == err)
1779 return table[i].message;
1780 }
1781
1782 return strerror(err);
1783}