blob: fa735637b80220b50e34bd04956243aa07566dc0 [file] [log] [blame]
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001/* Library which manipulates firewall rules. Version $Revision: 1.36 $ */
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
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000240 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
241 errno = EINVAL;
242 return NULL;
243 }
244
Rusty Russell79dee072000-05-02 16:45:16 +0000245 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000246 if (sockfd < 0)
247 return NULL;
248
249 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000250
Marc Bouchere6869a82000-03-20 06:03:29 +0000251 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000252 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000253 return NULL;
254
255 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000256 == NULL) {
257 close(sockfd);
Marc Bouchere6869a82000-03-20 06:03:29 +0000258 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000259 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000260
261/* Too hard --RR */
262#if 0
263 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
264 dynlib = dlopen(pathname, RTLD_NOW);
265 if (!dynlib) {
266 errno = ENOENT;
267 return NULL;
268 }
269 h->hooknames = dlsym(dynlib, "hooknames");
270 if (!h->hooknames) {
271 errno = ENOENT;
272 return NULL;
273 }
274#else
275 h->hooknames = hooknames;
276#endif
277
278 /* Initialize current state */
279 h->info = info;
280 h->new_number = h->info.num_entries;
281 for (i = 0; i < h->info.num_entries; i++)
282 h->counter_map[i]
283 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
284
285 h->entries.size = h->info.size;
286
Rusty Russell79dee072000-05-02 16:45:16 +0000287 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000288
Rusty Russell79dee072000-05-02 16:45:16 +0000289 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000290 &tmp) < 0) {
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000291 close(sockfd);
Marc Bouchere6869a82000-03-20 06:03:29 +0000292 free(h);
293 return NULL;
294 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000295
Marc Bouchere6869a82000-03-20 06:03:29 +0000296 CHECK(h);
297 return h;
298}
299
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000300void
301TC_FREE(TC_HANDLE_T *h)
302{
303 close(sockfd);
304 if ((*h)->cache_chain_heads)
305 free((*h)->cache_chain_heads);
306 free(*h);
307 *h = NULL;
308}
309
Marc Bouchere6869a82000-03-20 06:03:29 +0000310static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000311print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000312{
Rusty Russell228e98d2000-04-27 10:28:06 +0000313 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000314 return 0;
315}
316
Rusty Russell79dee072000-05-02 16:45:16 +0000317static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
318
Marc Bouchere6869a82000-03-20 06:03:29 +0000319void
Rusty Russell79dee072000-05-02 16:45:16 +0000320TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000321{
322 CHECK(handle);
323
324 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000325 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000326 handle->new_number, handle->entries.size);
327 printf("Table `%s'\n", handle->info.name);
328 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000329 handle->info.hook_entry[HOOK_PRE_ROUTING],
330 handle->info.hook_entry[HOOK_LOCAL_IN],
331 handle->info.hook_entry[HOOK_FORWARD],
332 handle->info.hook_entry[HOOK_LOCAL_OUT],
333 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000334 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000335 handle->info.underflow[HOOK_PRE_ROUTING],
336 handle->info.underflow[HOOK_LOCAL_IN],
337 handle->info.underflow[HOOK_FORWARD],
338 handle->info.underflow[HOOK_LOCAL_OUT],
339 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000340
Rusty Russell725d97a2000-07-07 08:54:22 +0000341 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000342 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000343}
344
Rusty Russell30fd6e52000-04-23 09:16:06 +0000345/* Returns 0 if not hook entry, else hooknumber + 1 */
346static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000347is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000348{
349 unsigned int i;
350
Rusty Russell79dee072000-05-02 16:45:16 +0000351 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000352 if ((h->info.valid_hooks & (1 << i))
353 && get_entry(h, h->info.hook_entry[i]) == e)
354 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000355 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000356 return 0;
357}
358
Rusty Russell30fd6e52000-04-23 09:16:06 +0000359static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000360add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000361{
362 unsigned int builtin;
363
364 /* Last entry. End it. */
365 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
366 /* This is the ERROR node at end of the table */
367 h->cache_chain_heads[h->cache_num_chains-1].end = *prev;
368 return 0;
369 }
370
371 /* We know this is the start of a new chain if it's an ERROR
372 target, or a hook entry point */
Rusty Russell67088e72000-05-10 01:18:57 +0000373 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000374 /* prev was last entry in previous chain */
375 h->cache_chain_heads[h->cache_num_chains-1].end
376 = *prev;
377
378 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
Rusty Russell79dee072000-05-02 16:45:16 +0000379 (const char *)GET_TARGET(e)->data);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000380 h->cache_chain_heads[h->cache_num_chains].start
381 = (void *)e + e->next_offset;
382 h->cache_num_chains++;
383 } else if ((builtin = is_hook_entry(e, h)) != 0) {
384 if (h->cache_num_chains > 0)
385 /* prev was last entry in previous chain */
386 h->cache_chain_heads[h->cache_num_chains-1].end
387 = *prev;
388
389 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
390 h->hooknames[builtin-1]);
391 h->cache_chain_heads[h->cache_num_chains].start
392 = (void *)e;
393 h->cache_num_chains++;
394 }
395
396 *prev = e;
397 return 0;
398}
399
Rusty Russell30fd6e52000-04-23 09:16:06 +0000400static int alphasort(const void *a, const void *b)
401{
402 return strcmp(((struct chain_cache *)a)->name,
403 ((struct chain_cache *)b)->name);
404}
405
Rusty Russell79dee072000-05-02 16:45:16 +0000406static int populate_cache(TC_HANDLE_T h)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000407{
408 unsigned int i;
Rusty Russell79dee072000-05-02 16:45:16 +0000409 STRUCT_ENTRY *prev;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000410
411 /* # chains < # rules / 2 + num builtins - 1 */
412 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
413 * sizeof(struct chain_cache));
414 if (!h->cache_chain_heads) {
415 errno = ENOMEM;
416 return 0;
417 }
418
419 h->cache_num_chains = 0;
420 h->cache_num_builtins = 0;
421
422 /* Count builtins */
Rusty Russell79dee072000-05-02 16:45:16 +0000423 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000424 if (h->info.valid_hooks & (1 << i))
425 h->cache_num_builtins++;
426 }
427
428 prev = NULL;
Rusty Russell725d97a2000-07-07 08:54:22 +0000429 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000430 add_chain, h, &prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000431
Rusty Russell30fd6e52000-04-23 09:16:06 +0000432 qsort(h->cache_chain_heads + h->cache_num_builtins,
433 h->cache_num_chains - h->cache_num_builtins,
434 sizeof(struct chain_cache), alphasort);
435
436 return 1;
437}
438
439/* Returns cache ptr if found, otherwise NULL. */
440static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000441find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000442{
Rusty Russell849779c2000-04-23 15:51:51 +0000443 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000444
445 if (handle->cache_chain_heads == NULL
446 && !populate_cache(handle))
447 return NULL;
448
Rusty Russell849779c2000-04-23 15:51:51 +0000449 /* FIXME: Linear search through builtins, then binary --RR */
450 for (i = 0; i < handle->cache_num_chains; i++) {
451 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
452 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000453 }
454
Rusty Russell849779c2000-04-23 15:51:51 +0000455 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000456}
457
Marc Bouchere6869a82000-03-20 06:03:29 +0000458/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000459int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000460{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000461 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000462}
463
464/* Returns the position of the final (ie. unconditional) element. */
465static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000466get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000467{
468 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000469 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000470
471 last_off = start;
472 e = get_entry(handle, start);
473
474 /* Terminate when we meet a error label or a hook entry. */
475 for (off = start + e->next_offset;
476 off < handle->entries.size;
477 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000478 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000479 unsigned int i;
480
481 e = get_entry(handle, off);
482
483 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000484 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000485 if ((handle->info.valid_hooks & (1 << i))
486 && off == handle->info.hook_entry[i])
487 return last_off;
488 }
489
490 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000491 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000492 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000493 return last_off;
494 }
495 /* SHOULD NEVER HAPPEN */
496 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
497 handle->entries.size, off);
498 abort();
499}
500
Rusty Russell30fd6e52000-04-23 09:16:06 +0000501/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000502const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000503TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000504{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000505 if ((*handle)->cache_chain_heads == NULL
506 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000507 return NULL;
508
Rusty Russell30fd6e52000-04-23 09:16:06 +0000509 (*handle)->cache_chain_iteration
510 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000511
Rusty Russell30fd6e52000-04-23 09:16:06 +0000512 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000513}
514
Rusty Russell30fd6e52000-04-23 09:16:06 +0000515/* Iterator functions to run through the chains. Returns NULL at end. */
516const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000517TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000518{
519 (*handle)->cache_chain_iteration++;
520
521 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000522 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000523 return NULL;
524
525 return (*handle)->cache_chain_iteration->name;
526}
527
528/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000529const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000530TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000531{
532 struct chain_cache *c;
533
534 c = find_label(chain, *handle);
535 if (!c) {
536 errno = ENOENT;
537 return NULL;
538 }
539
540 /* Empty chain: single return/policy rule */
541 if (c->start == c->end)
542 return NULL;
543
544 (*handle)->cache_rule_end = c->end;
545 return c->start;
546}
547
548/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000549const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000550TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000551{
552 if ((void *)prev + prev->next_offset
553 == (void *)(*handle)->cache_rule_end)
554 return NULL;
555
556 return (void *)prev + prev->next_offset;
557}
558
559#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000560/* How many rules in this chain? */
561unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000562TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000563{
564 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000565 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000566
567 CHECK(*handle);
568 if (!find_label(&off, chain, *handle)) {
569 errno = ENOENT;
570 return (unsigned int)-1;
571 }
572
573 start = get_entry(*handle, off);
574 end = get_entry(*handle, get_chain_end(*handle, off));
575
576 return entry2index(*handle, end) - entry2index(*handle, start);
577}
578
579/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000580const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
581 unsigned int n,
582 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000583{
584 unsigned int pos = 0, chainindex;
585
586 CHECK(*handle);
587 if (!find_label(&pos, chain, *handle)) {
588 errno = ENOENT;
589 return NULL;
590 }
591
592 chainindex = entry2index(*handle, get_entry(*handle, pos));
593
594 return index2entry(*handle, chainindex + n);
595}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000596#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000597
Rusty Russell30fd6e52000-04-23 09:16:06 +0000598static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000599target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000600{
601 int spos;
602 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000603 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000604
Rusty Russell30fd6e52000-04-23 09:16:06 +0000605 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000606 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000607
Rusty Russell79dee072000-05-02 16:45:16 +0000608 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
609 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000610
611 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000612 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000613 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000614 if (spos == RETURN)
615 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000616 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000617 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000618 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000619 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000620 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000621 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000622
623 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
624 entry2offset(handle, e), handle->entries.size,
625 spos);
626 abort();
627 }
628
629 jumpto = get_entry(handle, spos);
630
631 /* Fall through rule */
632 if (jumpto == (void *)e + e->next_offset)
633 return "";
634
635 /* Must point to head of a chain: ie. after error rule */
636 labelidx = entry2index(handle, jumpto) - 1;
637 return get_errorlabel(handle, index2offset(handle, labelidx));
638}
639
640/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000641const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
642 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000643{
Marc Bouchere6869a82000-03-20 06:03:29 +0000644 return target_name(*handle, e);
645}
646
647/* Is this a built-in chain? Actually returns hook + 1. */
648int
Rusty Russell79dee072000-05-02 16:45:16 +0000649TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000650{
651 unsigned int i;
652
Rusty Russell79dee072000-05-02 16:45:16 +0000653 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000654 if ((handle->info.valid_hooks & (1 << i))
655 && handle->hooknames[i]
656 && strcmp(handle->hooknames[i], chain) == 0)
657 return i+1;
658 }
659 return 0;
660}
661
662/* Get the policy of a given built-in chain */
663const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000664TC_GET_POLICY(const char *chain,
665 STRUCT_COUNTERS *counters,
666 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000667{
668 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000669 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000670 int hook;
671
Rusty Russell79dee072000-05-02 16:45:16 +0000672 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000673 if (hook != 0)
674 start = (*handle)->info.hook_entry[hook-1];
675 else
676 return NULL;
677
678 e = get_entry(*handle, get_chain_end(*handle, start));
679 *counters = e->counters;
680
681 return target_name(*handle, e);
682}
683
684static int
Rusty Russell79dee072000-05-02 16:45:16 +0000685correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000686 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000687 unsigned int offset, int delta_offset)
688{
Rusty Russell79dee072000-05-02 16:45:16 +0000689 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000690 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000691
692 /* Trap: insert of fall-through rule. Don't change fall-through
693 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000694 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000695 && t->verdict > (int)offset
696 && !(curr == offset &&
697 t->verdict == curr + e->next_offset)) {
698 t->verdict += delta_offset;
699 }
700
701 return 0;
702}
703
704/* Adjusts standard verdict jump positions after an insertion/deletion. */
705static int
Rusty Russell79dee072000-05-02 16:45:16 +0000706set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000707{
Rusty Russell725d97a2000-07-07 08:54:22 +0000708 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000709 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000710 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000711 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000712
Rusty Russell175f6412000-03-24 09:32:20 +0000713 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000714 return 1;
715}
716
717/* If prepend is set, then we are prepending to a chain: if the
718 * insertion position is an entry point, keep the entry point. */
719static int
720insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000721 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000722 unsigned int offset, unsigned int num_rules_offset,
723 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000724 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000725{
Rusty Russell79dee072000-05-02 16:45:16 +0000726 TC_HANDLE_T newh;
727 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000728 unsigned int i;
729
730 if (offset >= (*handle)->entries.size) {
731 errno = EINVAL;
732 return 0;
733 }
734
735 newinfo = (*handle)->info;
736
737 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000738 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000739 /* Entry points to START of chain, so keep same if
740 inserting on at that point. */
741 if ((*handle)->info.hook_entry[i] > offset)
742 newinfo.hook_entry[i] += rules_size;
743
744 /* Underflow always points to END of chain (policy),
745 so if something is inserted at same point, it
746 should be advanced. */
747 if ((*handle)->info.underflow[i] >= offset)
748 newinfo.underflow[i] += rules_size;
749 }
750
751 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000752 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000753 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000754 if (!newh)
755 return 0;
756 newh->info = newinfo;
757
758 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000759 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000760 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000761 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000762 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000763 memcpy((char *)newh->entries.entrytable + offset + rules_size,
764 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000765 (*handle)->entries.size - offset);
766
767 /* Move counter map. */
768 /* Copy pre... */
769 memcpy(newh->counter_map, (*handle)->counter_map,
770 sizeof(struct counter_map) * num_rules_offset);
771 /* ... copy post */
772 memcpy(newh->counter_map + num_rules_offset + num_rules,
773 (*handle)->counter_map + num_rules_offset,
774 sizeof(struct counter_map) * ((*handle)->new_number
775 - num_rules_offset));
776 /* Set intermediates to no counter copy */
777 for (i = 0; i < num_rules; i++)
778 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000779 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000780
781 newh->new_number = (*handle)->new_number + num_rules;
782 newh->entries.size = (*handle)->entries.size + rules_size;
783 newh->hooknames = (*handle)->hooknames;
784
Rusty Russell30fd6e52000-04-23 09:16:06 +0000785 if ((*handle)->cache_chain_heads)
786 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +0000787 free(*handle);
788 *handle = newh;
789
790 return set_verdict(offset, rules_size, handle);
791}
792
793static int
794delete_rules(unsigned int num_rules, unsigned int rules_size,
795 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000796 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000797{
798 unsigned int i;
799
800 if (offset + rules_size > (*handle)->entries.size) {
801 errno = EINVAL;
802 return 0;
803 }
804
805 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000806 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000807 /* In practice, we never delete up to a hook entry,
808 since the built-in chains are always first,
809 so these two are never equal */
810 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
811 (*handle)->info.hook_entry[i] -= rules_size;
812 else if ((*handle)->info.hook_entry[i] > offset) {
813 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
814 i, (*handle)->info.hook_entry[i], offset);
815 abort();
816 }
817
818 /* Underflow points to policy (terminal) rule in
819 built-in, so sequality is valid here (when deleting
820 the last rule). */
821 if ((*handle)->info.underflow[i] >= offset + rules_size)
822 (*handle)->info.underflow[i] -= rules_size;
823 else if ((*handle)->info.underflow[i] > offset) {
824 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
825 i, (*handle)->info.underflow[i], offset);
826 abort();
827 }
828 }
829
830 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000831 memmove((char *)(*handle)->entries.entrytable + offset,
832 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000833 (*handle)->entries.size - (offset + rules_size));
834
835 /* Move the counter map down. */
836 memmove(&(*handle)->counter_map[num_rules_offset],
837 &(*handle)->counter_map[num_rules_offset + num_rules],
838 sizeof(struct counter_map)
839 * ((*handle)->new_number - (num_rules + num_rules_offset)));
840
841 /* Fix numbers */
842 (*handle)->new_number -= num_rules;
843 (*handle)->entries.size -= rules_size;
844
845 return set_verdict(offset, -(int)rules_size, handle);
846}
847
848static int
Rusty Russell79dee072000-05-02 16:45:16 +0000849standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000850{
Rusty Russell79dee072000-05-02 16:45:16 +0000851 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000852
Rusty Russell79dee072000-05-02 16:45:16 +0000853 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000854
Rusty Russell67088e72000-05-10 01:18:57 +0000855 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000856 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000857 errno = EINVAL;
858 return 0;
859 }
860 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000861 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
862 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000863 t->verdict = verdict;
864
865 return 1;
866}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000867
Marc Bouchere6869a82000-03-20 06:03:29 +0000868static int
Rusty Russell79dee072000-05-02 16:45:16 +0000869map_target(const TC_HANDLE_T handle,
870 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000871 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000872 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000873{
Rusty Russell79dee072000-05-02 16:45:16 +0000874 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000875
876 /* Save old target (except data, which we don't change, except for
877 standard case, where we don't care). */
878 *old = *t;
879
880 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000881 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000882 return standard_map(e, offset + e->next_offset);
883 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000884 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000885 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000886 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000887 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000888 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000889 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000890 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
891 return standard_map(e, RETURN);
892 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000893 /* Can't jump to builtins. */
894 errno = EINVAL;
895 return 0;
896 } else {
897 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000898 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000899
Rusty Russell228e98d2000-04-27 10:28:06 +0000900 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000901 if (c)
902 return standard_map(e, entry2offset(handle, c->start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000903 }
904
905 /* Must be a module? If not, kernel will reject... */
906 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000907 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000908 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000909 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000910 return 1;
911}
912
913static void
Rusty Russell79dee072000-05-02 16:45:16 +0000914unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000915{
Rusty Russell79dee072000-05-02 16:45:16 +0000916 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000917
918 /* Save old target (except data, which we don't change, except for
919 standard case, where we don't care). */
920 *t = *old;
921}
922
923/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
924int
Rusty Russell79dee072000-05-02 16:45:16 +0000925TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
926 const STRUCT_ENTRY *e,
927 unsigned int rulenum,
928 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000929{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000930 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000931 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000932 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000933 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000934 int ret;
935
Rusty Russell79dee072000-05-02 16:45:16 +0000936 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000937 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000938 errno = ENOENT;
939 return 0;
940 }
941
Rusty Russell30fd6e52000-04-23 09:16:06 +0000942 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000943
Rusty Russell14a1c912000-08-26 04:41:10 +0000944 tmp = index2entry(*handle, chainindex + rulenum);
945 if (!tmp || tmp > c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000946 errno = E2BIG;
947 return 0;
948 }
949 offset = index2offset(*handle, chainindex + rulenum);
950
951 /* Mapping target actually alters entry, but that's
952 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +0000953 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000954 return 0;
955
956 ret = insert_rules(1, e->next_offset, e, offset,
957 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000958 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000959 return ret;
960}
961
962/* Atomically replace rule `rulenum' in `chain' with `fw'. */
963int
Rusty Russell79dee072000-05-02 16:45:16 +0000964TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
965 const STRUCT_ENTRY *e,
966 unsigned int rulenum,
967 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000968{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000969 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000970 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000971 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000972 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000973 int ret;
974
Rusty Russell79dee072000-05-02 16:45:16 +0000975 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +0000976
Rusty Russell30fd6e52000-04-23 09:16:06 +0000977 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000978 errno = ENOENT;
979 return 0;
980 }
981
Rusty Russell30fd6e52000-04-23 09:16:06 +0000982 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000983
Rusty Russell14a1c912000-08-26 04:41:10 +0000984 tmp = index2entry(*handle, chainindex + rulenum);
985 if (!tmp || tmp >= c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000986 errno = E2BIG;
987 return 0;
988 }
989
990 offset = index2offset(*handle, chainindex + rulenum);
991 /* Replace = delete and insert. */
992 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
993 offset, chainindex + rulenum, handle))
994 return 0;
995
Rusty Russell79dee072000-05-02 16:45:16 +0000996 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000997 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000998
999 ret = insert_rules(1, e->next_offset, e, offset,
1000 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001001 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001002 return ret;
1003}
1004
1005/* Append entry `fw' to chain `chain'. Equivalent to insert with
1006 rulenum = length of chain. */
1007int
Rusty Russell79dee072000-05-02 16:45:16 +00001008TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1009 const STRUCT_ENTRY *e,
1010 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001011{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001012 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001013 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001014 int ret;
1015
Rusty Russell79dee072000-05-02 16:45:16 +00001016 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001017 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001018 errno = ENOENT;
1019 return 0;
1020 }
1021
Rusty Russell79dee072000-05-02 16:45:16 +00001022 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001023 entry2offset(*handle, c->end), &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001024 return 0;
1025
Rusty Russell30fd6e52000-04-23 09:16:06 +00001026 ret = insert_rules(1, e->next_offset, e,
1027 entry2offset(*handle, c->end),
1028 entry2index(*handle, c->end),
Marc Bouchere6869a82000-03-20 06:03:29 +00001029 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001030 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001031 return ret;
1032}
1033
1034static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001035match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001036 const unsigned char *a_elems,
1037 const unsigned char *b_elems,
1038 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001039{
Rusty Russell79dee072000-05-02 16:45:16 +00001040 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001041 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001042
1043 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001044 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001045
Rusty Russell228e98d2000-04-27 10:28:06 +00001046 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001047 return 1;
1048
Rusty Russell228e98d2000-04-27 10:28:06 +00001049 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001050 return 1;
1051
Rusty Russell73ef09b2000-07-03 10:24:04 +00001052 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001053
Rusty Russell73ef09b2000-07-03 10:24:04 +00001054 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001055 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001056 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001057 *maskptr += i;
1058 return 0;
1059}
1060
1061static inline int
1062target_different(const unsigned char *a_targdata,
1063 const unsigned char *b_targdata,
1064 unsigned int tdatasize,
1065 const unsigned char *mask)
1066{
1067 unsigned int i;
1068 for (i = 0; i < tdatasize; i++)
1069 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1070 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001071
1072 return 0;
1073}
1074
Rusty Russell79dee072000-05-02 16:45:16 +00001075static int
1076is_same(const STRUCT_ENTRY *a,
1077 const STRUCT_ENTRY *b,
1078 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001079
1080/* Delete the first rule in `chain' which matches `fw'. */
1081int
Rusty Russell79dee072000-05-02 16:45:16 +00001082TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1083 const STRUCT_ENTRY *origfw,
1084 unsigned char *matchmask,
1085 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001086{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001087 unsigned int offset;
1088 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001089 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001090
Rusty Russell79dee072000-05-02 16:45:16 +00001091 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001092 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001093 errno = ENOENT;
1094 return 0;
1095 }
1096
1097 fw = malloc(origfw->next_offset);
1098 if (fw == NULL) {
1099 errno = ENOMEM;
1100 return 0;
1101 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001102
Rusty Russell30fd6e52000-04-23 09:16:06 +00001103 for (offset = entry2offset(*handle, c->start);
1104 offset < entry2offset(*handle, c->end);
1105 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001106 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001107
1108 memcpy(fw, origfw, origfw->next_offset);
1109
1110 /* FIXME: handle this in is_same --RR */
1111 if (!map_target(*handle, fw, offset, &discard)) {
1112 free(fw);
1113 return 0;
1114 }
1115 e = get_entry(*handle, offset);
1116
1117#if 0
1118 printf("Deleting:\n");
1119 dump_entry(newe);
1120#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001121 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001122 int ret;
1123 ret = delete_rules(1, e->next_offset,
1124 offset, entry2index(*handle, e),
1125 handle);
1126 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001127 return ret;
1128 }
1129 }
1130
1131 free(fw);
1132 errno = ENOENT;
1133 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001134}
Marc Bouchere6869a82000-03-20 06:03:29 +00001135
1136/* Delete the rule in position `rulenum' in `chain'. */
1137int
Rusty Russell79dee072000-05-02 16:45:16 +00001138TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1139 unsigned int rulenum,
1140 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001141{
Marc Bouchere6869a82000-03-20 06:03:29 +00001142 unsigned int index;
1143 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001144 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001145 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001146
Rusty Russell79dee072000-05-02 16:45:16 +00001147 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001148 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001149 errno = ENOENT;
1150 return 0;
1151 }
1152
Rusty Russell30fd6e52000-04-23 09:16:06 +00001153 index = entry2index(*handle, c->start) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001154
Rusty Russell30fd6e52000-04-23 09:16:06 +00001155 if (index >= entry2index(*handle, c->end)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001156 errno = E2BIG;
1157 return 0;
1158 }
1159
1160 e = index2entry(*handle, index);
1161 if (e == NULL) {
1162 errno = EINVAL;
1163 return 0;
1164 }
1165
1166 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1167 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001168 return ret;
1169}
1170
1171/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1172 NULL and sets errno. */
1173const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001174TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1175 STRUCT_ENTRY *entry,
1176 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001177{
1178 errno = ENOSYS;
1179 return NULL;
1180}
1181
1182/* Flushes the entries in the given chain (ie. empties chain). */
1183int
Rusty Russell79dee072000-05-02 16:45:16 +00001184TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001185{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001186 unsigned int startindex, endindex;
1187 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001188 int ret;
1189
Rusty Russell79dee072000-05-02 16:45:16 +00001190 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001191 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001192 errno = ENOENT;
1193 return 0;
1194 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001195 startindex = entry2index(*handle, c->start);
1196 endindex = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001197
1198 ret = delete_rules(endindex - startindex,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001199 (char *)c->end - (char *)c->start,
1200 entry2offset(*handle, c->start), startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001201 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001202 return ret;
1203}
1204
1205/* Zeroes the counters in a chain. */
1206int
Rusty Russell79dee072000-05-02 16:45:16 +00001207TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001208{
1209 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001210 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001211
Rusty Russell30fd6e52000-04-23 09:16:06 +00001212 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001213 errno = ENOENT;
1214 return 0;
1215 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001216
Rusty Russell30fd6e52000-04-23 09:16:06 +00001217 i = entry2index(*handle, c->start);
1218 end = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001219
1220 for (; i <= end; i++) {
1221 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1222 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1223 }
Rusty Russell175f6412000-03-24 09:32:20 +00001224 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001225
Marc Bouchere6869a82000-03-20 06:03:29 +00001226 return 1;
1227}
1228
Harald Welte1cef74d2001-01-05 15:22:59 +00001229STRUCT_COUNTERS *
1230TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1231 unsigned int rulenum,
1232 TC_HANDLE_T *handle)
1233{
1234 STRUCT_ENTRY *e;
1235 struct chain_cache *c;
1236 unsigned int chainindex, end;
1237
1238 iptc_fn = TC_READ_COUNTER;
1239 CHECK(*handle);
1240
1241 if (!(c = find_label(chain, *handle))) {
1242 errno = ENOENT;
1243 return NULL;
1244 }
1245
1246 chainindex = entry2index(*handle, c->start);
1247 end = entry2index(*handle, c->end);
1248
1249 if (chainindex + rulenum > end) {
1250 errno = E2BIG;
1251 return NULL;
1252 }
1253
1254 e = index2entry(*handle, chainindex + rulenum);
1255
1256 return &e->counters;
1257}
1258
1259int
1260TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1261 unsigned int rulenum,
1262 TC_HANDLE_T *handle)
1263{
1264 STRUCT_ENTRY *e;
1265 struct chain_cache *c;
1266 unsigned int chainindex, end;
1267
1268 iptc_fn = TC_ZERO_COUNTER;
1269 CHECK(*handle);
1270
1271 if (!(c = find_label(chain, *handle))) {
1272 errno = ENOENT;
1273 return 0;
1274 }
1275
1276 chainindex = entry2index(*handle, c->start);
1277 end = entry2index(*handle, c->end);
1278
1279 if (chainindex + rulenum > end) {
1280 errno = E2BIG;
1281 return 0;
1282 }
1283
1284 e = index2entry(*handle, chainindex + rulenum);
1285
1286 if ((*handle)->counter_map[chainindex + rulenum].maptype
1287 == COUNTER_MAP_NORMAL_MAP) {
1288 (*handle)->counter_map[chainindex + rulenum].maptype
1289 = COUNTER_MAP_ZEROED;
1290 }
1291
1292 set_changed(*handle);
1293
1294 return 1;
1295}
1296
1297int
1298TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1299 unsigned int rulenum,
1300 STRUCT_COUNTERS *counters,
1301 TC_HANDLE_T *handle)
1302{
1303 STRUCT_ENTRY *e;
1304 struct chain_cache *c;
1305 unsigned int chainindex, end;
1306
1307 iptc_fn = TC_SET_COUNTER;
1308 CHECK(*handle);
1309
1310 if (!(c = find_label(chain, *handle))) {
1311 errno = ENOENT;
1312 return 0;
1313 }
1314
1315 chainindex = entry2index(*handle, c->start);
1316 end = entry2index(*handle, c->end);
1317
1318 if (chainindex + rulenum > end) {
1319 errno = E2BIG;
1320 return 0;
1321 }
1322
1323 e = index2entry(*handle, chainindex + rulenum);
1324
1325 (*handle)->counter_map[chainindex + rulenum].maptype
1326 = COUNTER_MAP_SET;
1327
1328 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1329
1330 set_changed(*handle);
1331
1332 return 1;
1333}
1334
Marc Bouchere6869a82000-03-20 06:03:29 +00001335/* Creates a new chain. */
1336/* To create a chain, create two rules: error node and unconditional
1337 * return. */
1338int
Rusty Russell79dee072000-05-02 16:45:16 +00001339TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001340{
Marc Bouchere6869a82000-03-20 06:03:29 +00001341 int ret;
1342 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001343 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001344 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001345 STRUCT_ENTRY ret;
1346 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001347 } newc;
1348
Rusty Russell79dee072000-05-02 16:45:16 +00001349 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001350
1351 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1352 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001353 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001354 || strcmp(chain, LABEL_DROP) == 0
1355 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001356 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001357 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001358 errno = EEXIST;
1359 return 0;
1360 }
1361
Rusty Russell79dee072000-05-02 16:45:16 +00001362 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001363 errno = EINVAL;
1364 return 0;
1365 }
1366
1367 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001368 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001369 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001370 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001371 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001372 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001373 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001374 strcpy(newc.name.error, chain);
1375
Rusty Russell79dee072000-05-02 16:45:16 +00001376 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001377 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001378 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001379 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001380 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001381 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001382 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001383 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001384
1385 /* Add just before terminal entry */
1386 ret = insert_rules(2, sizeof(newc), &newc.head,
1387 index2offset(*handle, (*handle)->new_number - 1),
1388 (*handle)->new_number - 1,
1389 0, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001390 return ret;
1391}
1392
1393static int
Rusty Russell79dee072000-05-02 16:45:16 +00001394count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001395{
Rusty Russell79dee072000-05-02 16:45:16 +00001396 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001397
Rusty Russell79dee072000-05-02 16:45:16 +00001398 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1399 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001400
1401 if (t->verdict == offset)
1402 (*ref)++;
1403 }
1404
1405 return 0;
1406}
1407
1408/* Get the number of references to this chain. */
1409int
Rusty Russell79dee072000-05-02 16:45:16 +00001410TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1411 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001412{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001413 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001414
Rusty Russell30fd6e52000-04-23 09:16:06 +00001415 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001416 errno = ENOENT;
1417 return 0;
1418 }
1419
1420 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001421 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001422 (*handle)->entries.size,
1423 count_ref, entry2offset(*handle, c->start), ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001424 return 1;
1425}
1426
1427/* Deletes a chain. */
1428int
Rusty Russell79dee072000-05-02 16:45:16 +00001429TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001430{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001431 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001432 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001433 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001434 int ret;
1435
Rusty Russell79dee072000-05-02 16:45:16 +00001436 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001437 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001438
Rusty Russell79dee072000-05-02 16:45:16 +00001439 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001440
Rusty Russell79dee072000-05-02 16:45:16 +00001441 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001442 errno = EINVAL;
1443 return 0;
1444 }
1445
1446 if (references > 0) {
1447 errno = EMLINK;
1448 return 0;
1449 }
1450
Rusty Russell30fd6e52000-04-23 09:16:06 +00001451 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001452 errno = ENOENT;
1453 return 0;
1454 }
1455
Rusty Russell30fd6e52000-04-23 09:16:06 +00001456 if ((void *)c->start != c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001457 errno = ENOTEMPTY;
1458 return 0;
1459 }
1460
1461 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001462 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001463 labeloff = index2offset(*handle, labelidx);
1464
1465 ret = delete_rules(2,
1466 get_entry(*handle, labeloff)->next_offset
Rusty Russell30fd6e52000-04-23 09:16:06 +00001467 + c->start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001468 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001469 return ret;
1470}
1471
1472/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001473int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1474 const IPT_CHAINLABEL newname,
1475 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001476{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001477 unsigned int labeloff, labelidx;
1478 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001479 struct ipt_error_target *t;
1480
Rusty Russell79dee072000-05-02 16:45:16 +00001481 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001482
Harald Welte1de80462000-10-30 12:00:27 +00001483 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1484 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001485 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001486 || strcmp(newname, LABEL_DROP) == 0
1487 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001488 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001489 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001490 errno = EEXIST;
1491 return 0;
1492 }
1493
Rusty Russell30fd6e52000-04-23 09:16:06 +00001494 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001495 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001496 errno = ENOENT;
1497 return 0;
1498 }
1499
Rusty Russell79dee072000-05-02 16:45:16 +00001500 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001501 errno = EINVAL;
1502 return 0;
1503 }
1504
1505 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001506 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001507 labeloff = index2offset(*handle, labelidx);
1508
1509 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001510 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001511
1512 memset(t->error, 0, sizeof(t->error));
1513 strcpy(t->error, newname);
Rusty Russell175f6412000-03-24 09:32:20 +00001514 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001515
Marc Bouchere6869a82000-03-20 06:03:29 +00001516 return 1;
1517}
1518
1519/* Sets the policy on a built-in chain. */
1520int
Rusty Russell79dee072000-05-02 16:45:16 +00001521TC_SET_POLICY(const IPT_CHAINLABEL chain,
1522 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001523 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001524 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001525{
1526 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001527 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001528 STRUCT_ENTRY *e;
1529 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001530
Rusty Russell79dee072000-05-02 16:45:16 +00001531 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001532 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001533 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001534 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001535 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001536 return 0;
1537 } else
1538 hook--;
1539
1540 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1541 if (policyoff != (*handle)->info.underflow[hook]) {
1542 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1543 chain, policyoff, (*handle)->info.underflow[hook]);
1544 return 0;
1545 }
1546
1547 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001548 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001549
Rusty Russell79dee072000-05-02 16:45:16 +00001550 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001551 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001552 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001553 t->verdict = -NF_DROP - 1;
1554 else {
1555 errno = EINVAL;
1556 return 0;
1557 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001558
1559 ctrindex = entry2index(*handle, e);
1560
1561 if (counters) {
1562 /* set byte and packet counters */
1563 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1564
1565 (*handle)->counter_map[ctrindex].maptype
1566 = COUNTER_MAP_SET;
1567
1568 } else {
1569 (*handle)->counter_map[ctrindex]
1570 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1571 }
1572
Rusty Russell175f6412000-03-24 09:32:20 +00001573 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001574
Marc Bouchere6869a82000-03-20 06:03:29 +00001575 return 1;
1576}
1577
1578/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001579 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001580 libiptc.c:833: fixed or forbidden register was spilled.
1581 This may be due to a compiler bug or to impossible asm
1582 statements or clauses.
1583*/
1584static void
Rusty Russell79dee072000-05-02 16:45:16 +00001585subtract_counters(STRUCT_COUNTERS *answer,
1586 const STRUCT_COUNTERS *a,
1587 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001588{
1589 answer->pcnt = a->pcnt - b->pcnt;
1590 answer->bcnt = a->bcnt - b->bcnt;
1591}
1592
1593int
Rusty Russell79dee072000-05-02 16:45:16 +00001594TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001595{
1596 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001597 STRUCT_REPLACE *repl;
1598 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001599 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001600 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001601
1602 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001603
1604 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1605 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1606
Marc Bouchere6869a82000-03-20 06:03:29 +00001607#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001608 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001609#endif
1610
1611 /* Don't commit if nothing changed. */
1612 if (!(*handle)->changed)
1613 goto finished;
1614
1615 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1616 if (!repl) {
1617 errno = ENOMEM;
1618 return 0;
1619 }
1620
1621 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001622 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001623 * (*handle)->info.num_entries);
1624 if (!repl->counters) {
1625 free(repl);
1626 errno = ENOMEM;
1627 return 0;
1628 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001629
Marc Bouchere6869a82000-03-20 06:03:29 +00001630 /* These are the counters we're going to put back, later. */
1631 newcounters = malloc(counterlen);
1632 if (!newcounters) {
1633 free(repl->counters);
1634 free(repl);
1635 errno = ENOMEM;
1636 return 0;
1637 }
1638
1639 strcpy(repl->name, (*handle)->info.name);
1640 repl->num_entries = (*handle)->new_number;
1641 repl->size = (*handle)->entries.size;
1642 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1643 sizeof(repl->hook_entry));
1644 memcpy(repl->underflow, (*handle)->info.underflow,
1645 sizeof(repl->underflow));
1646 repl->num_counters = (*handle)->info.num_entries;
1647 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001648 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001649 (*handle)->entries.size);
1650
Rusty Russell79dee072000-05-02 16:45:16 +00001651 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001652 sizeof(*repl) + (*handle)->entries.size) < 0) {
1653 free(repl->counters);
1654 free(repl);
1655 free(newcounters);
1656 return 0;
1657 }
1658
1659 /* Put counters back. */
1660 strcpy(newcounters->name, (*handle)->info.name);
1661 newcounters->num_counters = (*handle)->new_number;
1662 for (i = 0; i < (*handle)->new_number; i++) {
1663 unsigned int mappos = (*handle)->counter_map[i].mappos;
1664 switch ((*handle)->counter_map[i].maptype) {
1665 case COUNTER_MAP_NOMAP:
1666 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001667 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001668 break;
1669
1670 case COUNTER_MAP_NORMAL_MAP:
1671 /* Original read: X.
1672 * Atomic read on replacement: X + Y.
1673 * Currently in kernel: Z.
1674 * Want in kernel: X + Y + Z.
1675 * => Add in X + Y
1676 * => Add in replacement read.
1677 */
1678 newcounters->counters[i] = repl->counters[mappos];
1679 break;
1680
1681 case COUNTER_MAP_ZEROED:
1682 /* Original read: X.
1683 * Atomic read on replacement: X + Y.
1684 * Currently in kernel: Z.
1685 * Want in kernel: Y + Z.
1686 * => Add in Y.
1687 * => Add in (replacement read - original read).
1688 */
1689 subtract_counters(&newcounters->counters[i],
1690 &repl->counters[mappos],
1691 &index2entry(*handle, i)->counters);
1692 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001693
1694 case COUNTER_MAP_SET:
1695 /* Want to set counter (iptables-restore) */
1696
1697 memcpy(&newcounters->counters[i],
1698 &index2entry(*handle, i)->counters,
1699 sizeof(STRUCT_COUNTERS));
1700
1701 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001702 }
1703 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001704
1705#ifdef KERNEL_64_USERSPACE_32
1706 {
1707 /* Kernel will think that pointer should be 64-bits, and get
1708 padding. So we accomodate here (assumption: alignment of
1709 `counters' is on 64-bit boundary). */
1710 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1711 if ((unsigned long)&newcounters->counters % 8 != 0) {
1712 fprintf(stderr,
1713 "counters alignment incorrect! Mail rusty!\n");
1714 abort();
1715 }
1716 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001717 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001718#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001719
Rusty Russell79dee072000-05-02 16:45:16 +00001720 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1721 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001722 free(repl->counters);
1723 free(repl);
1724 free(newcounters);
1725 return 0;
1726 }
1727
1728 free(repl->counters);
1729 free(repl);
1730 free(newcounters);
1731
1732 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001733 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001734 return 1;
1735}
1736
1737/* Get raw socket. */
1738int
Rusty Russell79dee072000-05-02 16:45:16 +00001739TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001740{
1741 return sockfd;
1742}
1743
1744/* Translates errno numbers into more human-readable form than strerror. */
1745const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001746TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001747{
1748 unsigned int i;
1749 struct table_struct {
1750 void *fn;
1751 int err;
1752 const char *message;
1753 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001754 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001755 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001756 { TC_INIT, ENOENT,
1757 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001758 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1759 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1760 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001761 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001762 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1763 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1764 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1765 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001766 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1767 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001768 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1769 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001770 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001771 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001772 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001773 { TC_CHECK_PACKET, ENOSYS,
1774 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001775 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001776 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001777 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001778 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001779 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001780 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001781 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001782
1783 { NULL, 0, "Incompatible with this kernel" },
1784 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1785 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1786 { NULL, ENOMEM, "Memory allocation problem" },
1787 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001788 };
1789
1790 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1791 if ((!table[i].fn || table[i].fn == iptc_fn)
1792 && table[i].err == err)
1793 return table[i].message;
1794 }
1795
1796 return strerror(err);
1797}