blob: 55b708b8053a39871c67ddd6dfd003809821efff [file] [log] [blame]
Tomáš Lejdar23a6b452003-04-30 15:57:01 +00001/* Library which manipulates firewall rules. Version $Revision: 1.35 $ */
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
Tomáš Lejdar23a6b452003-04-30 15:57:01 +0000507 == (*handle)->cache_num_chains) {
508 free((*handle)->cache_chain_heads);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000509 return NULL;
Tomáš Lejdar23a6b452003-04-30 15:57:01 +0000510 }
Rusty Russell30fd6e52000-04-23 09:16:06 +0000511
512 return (*handle)->cache_chain_iteration->name;
513}
514
515/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000516const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000517TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000518{
519 struct chain_cache *c;
520
521 c = find_label(chain, *handle);
522 if (!c) {
523 errno = ENOENT;
524 return NULL;
525 }
526
527 /* Empty chain: single return/policy rule */
528 if (c->start == c->end)
529 return NULL;
530
531 (*handle)->cache_rule_end = c->end;
532 return c->start;
533}
534
535/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000536const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000537TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000538{
539 if ((void *)prev + prev->next_offset
540 == (void *)(*handle)->cache_rule_end)
541 return NULL;
542
543 return (void *)prev + prev->next_offset;
544}
545
546#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000547/* How many rules in this chain? */
548unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000549TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000550{
551 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000552 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000553
554 CHECK(*handle);
555 if (!find_label(&off, chain, *handle)) {
556 errno = ENOENT;
557 return (unsigned int)-1;
558 }
559
560 start = get_entry(*handle, off);
561 end = get_entry(*handle, get_chain_end(*handle, off));
562
563 return entry2index(*handle, end) - entry2index(*handle, start);
564}
565
566/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000567const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
568 unsigned int n,
569 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000570{
571 unsigned int pos = 0, chainindex;
572
573 CHECK(*handle);
574 if (!find_label(&pos, chain, *handle)) {
575 errno = ENOENT;
576 return NULL;
577 }
578
579 chainindex = entry2index(*handle, get_entry(*handle, pos));
580
581 return index2entry(*handle, chainindex + n);
582}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000583#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000584
Rusty Russell30fd6e52000-04-23 09:16:06 +0000585static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000586target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000587{
588 int spos;
589 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000590 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000591
Rusty Russell30fd6e52000-04-23 09:16:06 +0000592 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000593 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000594
Rusty Russell79dee072000-05-02 16:45:16 +0000595 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
596 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000597
598 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000599 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000600 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000601 if (spos == RETURN)
602 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000603 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000604 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000605 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000606 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000607 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000608 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000609
610 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
611 entry2offset(handle, e), handle->entries.size,
612 spos);
613 abort();
614 }
615
616 jumpto = get_entry(handle, spos);
617
618 /* Fall through rule */
619 if (jumpto == (void *)e + e->next_offset)
620 return "";
621
622 /* Must point to head of a chain: ie. after error rule */
623 labelidx = entry2index(handle, jumpto) - 1;
624 return get_errorlabel(handle, index2offset(handle, labelidx));
625}
626
627/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000628const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
629 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000630{
Marc Bouchere6869a82000-03-20 06:03:29 +0000631 return target_name(*handle, e);
632}
633
634/* Is this a built-in chain? Actually returns hook + 1. */
635int
Rusty Russell79dee072000-05-02 16:45:16 +0000636TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000637{
638 unsigned int i;
639
Rusty Russell79dee072000-05-02 16:45:16 +0000640 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000641 if ((handle->info.valid_hooks & (1 << i))
642 && handle->hooknames[i]
643 && strcmp(handle->hooknames[i], chain) == 0)
644 return i+1;
645 }
646 return 0;
647}
648
649/* Get the policy of a given built-in chain */
650const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000651TC_GET_POLICY(const char *chain,
652 STRUCT_COUNTERS *counters,
653 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000654{
655 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000656 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000657 int hook;
658
Rusty Russell79dee072000-05-02 16:45:16 +0000659 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000660 if (hook != 0)
661 start = (*handle)->info.hook_entry[hook-1];
662 else
663 return NULL;
664
665 e = get_entry(*handle, get_chain_end(*handle, start));
666 *counters = e->counters;
667
668 return target_name(*handle, e);
669}
670
671static int
Rusty Russell79dee072000-05-02 16:45:16 +0000672correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000673 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000674 unsigned int offset, int delta_offset)
675{
Rusty Russell79dee072000-05-02 16:45:16 +0000676 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000677 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000678
679 /* Trap: insert of fall-through rule. Don't change fall-through
680 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000681 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000682 && t->verdict > (int)offset
683 && !(curr == offset &&
684 t->verdict == curr + e->next_offset)) {
685 t->verdict += delta_offset;
686 }
687
688 return 0;
689}
690
691/* Adjusts standard verdict jump positions after an insertion/deletion. */
692static int
Rusty Russell79dee072000-05-02 16:45:16 +0000693set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000694{
Rusty Russell725d97a2000-07-07 08:54:22 +0000695 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000696 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000697 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000698 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000699
Rusty Russell175f6412000-03-24 09:32:20 +0000700 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000701 return 1;
702}
703
704/* If prepend is set, then we are prepending to a chain: if the
705 * insertion position is an entry point, keep the entry point. */
706static int
707insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000708 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000709 unsigned int offset, unsigned int num_rules_offset,
710 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000711 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000712{
Rusty Russell79dee072000-05-02 16:45:16 +0000713 TC_HANDLE_T newh;
714 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000715 unsigned int i;
716
717 if (offset >= (*handle)->entries.size) {
718 errno = EINVAL;
719 return 0;
720 }
721
722 newinfo = (*handle)->info;
723
724 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000725 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000726 /* Entry points to START of chain, so keep same if
727 inserting on at that point. */
728 if ((*handle)->info.hook_entry[i] > offset)
729 newinfo.hook_entry[i] += rules_size;
730
731 /* Underflow always points to END of chain (policy),
732 so if something is inserted at same point, it
733 should be advanced. */
734 if ((*handle)->info.underflow[i] >= offset)
735 newinfo.underflow[i] += rules_size;
736 }
737
738 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000739 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000740 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000741 if (!newh)
742 return 0;
743 newh->info = newinfo;
744
745 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000746 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000747 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000748 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000749 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000750 memcpy((char *)newh->entries.entrytable + offset + rules_size,
751 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000752 (*handle)->entries.size - offset);
753
754 /* Move counter map. */
755 /* Copy pre... */
756 memcpy(newh->counter_map, (*handle)->counter_map,
757 sizeof(struct counter_map) * num_rules_offset);
758 /* ... copy post */
759 memcpy(newh->counter_map + num_rules_offset + num_rules,
760 (*handle)->counter_map + num_rules_offset,
761 sizeof(struct counter_map) * ((*handle)->new_number
762 - num_rules_offset));
763 /* Set intermediates to no counter copy */
764 for (i = 0; i < num_rules; i++)
765 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000766 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000767
768 newh->new_number = (*handle)->new_number + num_rules;
769 newh->entries.size = (*handle)->entries.size + rules_size;
770 newh->hooknames = (*handle)->hooknames;
771
Rusty Russell30fd6e52000-04-23 09:16:06 +0000772 if ((*handle)->cache_chain_heads)
773 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +0000774 free(*handle);
775 *handle = newh;
776
777 return set_verdict(offset, rules_size, handle);
778}
779
780static int
781delete_rules(unsigned int num_rules, unsigned int rules_size,
782 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000783 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000784{
785 unsigned int i;
786
787 if (offset + rules_size > (*handle)->entries.size) {
788 errno = EINVAL;
789 return 0;
790 }
791
792 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000793 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000794 /* In practice, we never delete up to a hook entry,
795 since the built-in chains are always first,
796 so these two are never equal */
797 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
798 (*handle)->info.hook_entry[i] -= rules_size;
799 else if ((*handle)->info.hook_entry[i] > offset) {
800 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
801 i, (*handle)->info.hook_entry[i], offset);
802 abort();
803 }
804
805 /* Underflow points to policy (terminal) rule in
806 built-in, so sequality is valid here (when deleting
807 the last rule). */
808 if ((*handle)->info.underflow[i] >= offset + rules_size)
809 (*handle)->info.underflow[i] -= rules_size;
810 else if ((*handle)->info.underflow[i] > offset) {
811 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
812 i, (*handle)->info.underflow[i], offset);
813 abort();
814 }
815 }
816
817 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000818 memmove((char *)(*handle)->entries.entrytable + offset,
819 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000820 (*handle)->entries.size - (offset + rules_size));
821
822 /* Move the counter map down. */
823 memmove(&(*handle)->counter_map[num_rules_offset],
824 &(*handle)->counter_map[num_rules_offset + num_rules],
825 sizeof(struct counter_map)
826 * ((*handle)->new_number - (num_rules + num_rules_offset)));
827
828 /* Fix numbers */
829 (*handle)->new_number -= num_rules;
830 (*handle)->entries.size -= rules_size;
831
832 return set_verdict(offset, -(int)rules_size, handle);
833}
834
835static int
Rusty Russell79dee072000-05-02 16:45:16 +0000836standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000837{
Rusty Russell79dee072000-05-02 16:45:16 +0000838 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000839
Rusty Russell79dee072000-05-02 16:45:16 +0000840 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000841
Rusty Russell67088e72000-05-10 01:18:57 +0000842 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000843 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000844 errno = EINVAL;
845 return 0;
846 }
847 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000848 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
849 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000850 t->verdict = verdict;
851
852 return 1;
853}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000854
Marc Bouchere6869a82000-03-20 06:03:29 +0000855static int
Rusty Russell79dee072000-05-02 16:45:16 +0000856map_target(const TC_HANDLE_T handle,
857 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000858 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000859 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000860{
Rusty Russell79dee072000-05-02 16:45:16 +0000861 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000862
863 /* Save old target (except data, which we don't change, except for
864 standard case, where we don't care). */
865 *old = *t;
866
867 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000868 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000869 return standard_map(e, offset + e->next_offset);
870 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000871 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000872 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000873 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000874 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000875 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000876 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000877 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
878 return standard_map(e, RETURN);
879 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000880 /* Can't jump to builtins. */
881 errno = EINVAL;
882 return 0;
883 } else {
884 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000885 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000886
Rusty Russell228e98d2000-04-27 10:28:06 +0000887 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000888 if (c)
889 return standard_map(e, entry2offset(handle, c->start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000890 }
891
892 /* Must be a module? If not, kernel will reject... */
893 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000894 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000895 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000896 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000897 return 1;
898}
899
900static void
Rusty Russell79dee072000-05-02 16:45:16 +0000901unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000902{
Rusty Russell79dee072000-05-02 16:45:16 +0000903 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000904
905 /* Save old target (except data, which we don't change, except for
906 standard case, where we don't care). */
907 *t = *old;
908}
909
910/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
911int
Rusty Russell79dee072000-05-02 16:45:16 +0000912TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
913 const STRUCT_ENTRY *e,
914 unsigned int rulenum,
915 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000916{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000917 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000918 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000919 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000920 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000921 int ret;
922
Rusty Russell79dee072000-05-02 16:45:16 +0000923 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000924 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000925 errno = ENOENT;
926 return 0;
927 }
928
Rusty Russell30fd6e52000-04-23 09:16:06 +0000929 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000930
Rusty Russell14a1c912000-08-26 04:41:10 +0000931 tmp = index2entry(*handle, chainindex + rulenum);
932 if (!tmp || tmp > c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000933 errno = E2BIG;
934 return 0;
935 }
936 offset = index2offset(*handle, chainindex + rulenum);
937
938 /* Mapping target actually alters entry, but that's
939 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +0000940 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000941 return 0;
942
943 ret = insert_rules(1, e->next_offset, e, offset,
944 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000945 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000946 return ret;
947}
948
949/* Atomically replace rule `rulenum' in `chain' with `fw'. */
950int
Rusty Russell79dee072000-05-02 16:45:16 +0000951TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
952 const STRUCT_ENTRY *e,
953 unsigned int rulenum,
954 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000955{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000956 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000957 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000958 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000959 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000960 int ret;
961
Rusty Russell79dee072000-05-02 16:45:16 +0000962 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +0000963
Rusty Russell30fd6e52000-04-23 09:16:06 +0000964 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000965 errno = ENOENT;
966 return 0;
967 }
968
Rusty Russell30fd6e52000-04-23 09:16:06 +0000969 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000970
Rusty Russell14a1c912000-08-26 04:41:10 +0000971 tmp = index2entry(*handle, chainindex + rulenum);
972 if (!tmp || tmp >= c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000973 errno = E2BIG;
974 return 0;
975 }
976
977 offset = index2offset(*handle, chainindex + rulenum);
978 /* Replace = delete and insert. */
979 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
980 offset, chainindex + rulenum, handle))
981 return 0;
982
Rusty Russell79dee072000-05-02 16:45:16 +0000983 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000984 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000985
986 ret = insert_rules(1, e->next_offset, e, offset,
987 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000988 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000989 return ret;
990}
991
992/* Append entry `fw' to chain `chain'. Equivalent to insert with
993 rulenum = length of chain. */
994int
Rusty Russell79dee072000-05-02 16:45:16 +0000995TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
996 const STRUCT_ENTRY *e,
997 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000998{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000999 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001000 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001001 int ret;
1002
Rusty Russell79dee072000-05-02 16:45:16 +00001003 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001004 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001005 errno = ENOENT;
1006 return 0;
1007 }
1008
Rusty Russell79dee072000-05-02 16:45:16 +00001009 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001010 entry2offset(*handle, c->end), &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001011 return 0;
1012
Rusty Russell30fd6e52000-04-23 09:16:06 +00001013 ret = insert_rules(1, e->next_offset, e,
1014 entry2offset(*handle, c->end),
1015 entry2index(*handle, c->end),
Marc Bouchere6869a82000-03-20 06:03:29 +00001016 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001017 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001018 return ret;
1019}
1020
1021static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001022match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001023 const unsigned char *a_elems,
1024 const unsigned char *b_elems,
1025 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001026{
Rusty Russell79dee072000-05-02 16:45:16 +00001027 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001028 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001029
1030 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001031 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001032
Rusty Russell228e98d2000-04-27 10:28:06 +00001033 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001034 return 1;
1035
Rusty Russell228e98d2000-04-27 10:28:06 +00001036 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001037 return 1;
1038
Rusty Russell73ef09b2000-07-03 10:24:04 +00001039 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001040
Rusty Russell73ef09b2000-07-03 10:24:04 +00001041 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001042 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001043 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001044 *maskptr += i;
1045 return 0;
1046}
1047
1048static inline int
1049target_different(const unsigned char *a_targdata,
1050 const unsigned char *b_targdata,
1051 unsigned int tdatasize,
1052 const unsigned char *mask)
1053{
1054 unsigned int i;
1055 for (i = 0; i < tdatasize; i++)
1056 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1057 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001058
1059 return 0;
1060}
1061
Rusty Russell79dee072000-05-02 16:45:16 +00001062static int
1063is_same(const STRUCT_ENTRY *a,
1064 const STRUCT_ENTRY *b,
1065 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001066
1067/* Delete the first rule in `chain' which matches `fw'. */
1068int
Rusty Russell79dee072000-05-02 16:45:16 +00001069TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1070 const STRUCT_ENTRY *origfw,
1071 unsigned char *matchmask,
1072 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001073{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001074 unsigned int offset;
1075 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001076 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001077
Rusty Russell79dee072000-05-02 16:45:16 +00001078 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001079 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001080 errno = ENOENT;
1081 return 0;
1082 }
1083
1084 fw = malloc(origfw->next_offset);
1085 if (fw == NULL) {
1086 errno = ENOMEM;
1087 return 0;
1088 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001089
Rusty Russell30fd6e52000-04-23 09:16:06 +00001090 for (offset = entry2offset(*handle, c->start);
1091 offset < entry2offset(*handle, c->end);
1092 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001093 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001094
1095 memcpy(fw, origfw, origfw->next_offset);
1096
1097 /* FIXME: handle this in is_same --RR */
1098 if (!map_target(*handle, fw, offset, &discard)) {
1099 free(fw);
1100 return 0;
1101 }
1102 e = get_entry(*handle, offset);
1103
1104#if 0
1105 printf("Deleting:\n");
1106 dump_entry(newe);
1107#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001108 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001109 int ret;
1110 ret = delete_rules(1, e->next_offset,
1111 offset, entry2index(*handle, e),
1112 handle);
1113 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001114 return ret;
1115 }
1116 }
1117
1118 free(fw);
1119 errno = ENOENT;
1120 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001121}
Marc Bouchere6869a82000-03-20 06:03:29 +00001122
1123/* Delete the rule in position `rulenum' in `chain'. */
1124int
Rusty Russell79dee072000-05-02 16:45:16 +00001125TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1126 unsigned int rulenum,
1127 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001128{
Marc Bouchere6869a82000-03-20 06:03:29 +00001129 unsigned int index;
1130 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001131 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001132 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001133
Rusty Russell79dee072000-05-02 16:45:16 +00001134 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001135 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001136 errno = ENOENT;
1137 return 0;
1138 }
1139
Rusty Russell30fd6e52000-04-23 09:16:06 +00001140 index = entry2index(*handle, c->start) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001141
Rusty Russell30fd6e52000-04-23 09:16:06 +00001142 if (index >= entry2index(*handle, c->end)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001143 errno = E2BIG;
1144 return 0;
1145 }
1146
1147 e = index2entry(*handle, index);
1148 if (e == NULL) {
1149 errno = EINVAL;
1150 return 0;
1151 }
1152
1153 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1154 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001155 return ret;
1156}
1157
1158/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1159 NULL and sets errno. */
1160const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001161TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1162 STRUCT_ENTRY *entry,
1163 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001164{
1165 errno = ENOSYS;
1166 return NULL;
1167}
1168
1169/* Flushes the entries in the given chain (ie. empties chain). */
1170int
Rusty Russell79dee072000-05-02 16:45:16 +00001171TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001172{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001173 unsigned int startindex, endindex;
1174 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001175 int ret;
1176
Rusty Russell79dee072000-05-02 16:45:16 +00001177 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001178 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001179 errno = ENOENT;
1180 return 0;
1181 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001182 startindex = entry2index(*handle, c->start);
1183 endindex = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001184
1185 ret = delete_rules(endindex - startindex,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001186 (char *)c->end - (char *)c->start,
1187 entry2offset(*handle, c->start), startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001188 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001189 return ret;
1190}
1191
1192/* Zeroes the counters in a chain. */
1193int
Rusty Russell79dee072000-05-02 16:45:16 +00001194TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001195{
1196 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001197 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001198
Rusty Russell30fd6e52000-04-23 09:16:06 +00001199 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001200 errno = ENOENT;
1201 return 0;
1202 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001203
Rusty Russell30fd6e52000-04-23 09:16:06 +00001204 i = entry2index(*handle, c->start);
1205 end = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001206
1207 for (; i <= end; i++) {
1208 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1209 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1210 }
Rusty Russell175f6412000-03-24 09:32:20 +00001211 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001212
Marc Bouchere6869a82000-03-20 06:03:29 +00001213 return 1;
1214}
1215
Harald Welte1cef74d2001-01-05 15:22:59 +00001216STRUCT_COUNTERS *
1217TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1218 unsigned int rulenum,
1219 TC_HANDLE_T *handle)
1220{
1221 STRUCT_ENTRY *e;
1222 struct chain_cache *c;
1223 unsigned int chainindex, end;
1224
1225 iptc_fn = TC_READ_COUNTER;
1226 CHECK(*handle);
1227
1228 if (!(c = find_label(chain, *handle))) {
1229 errno = ENOENT;
1230 return NULL;
1231 }
1232
1233 chainindex = entry2index(*handle, c->start);
1234 end = entry2index(*handle, c->end);
1235
1236 if (chainindex + rulenum > end) {
1237 errno = E2BIG;
1238 return NULL;
1239 }
1240
1241 e = index2entry(*handle, chainindex + rulenum);
1242
1243 return &e->counters;
1244}
1245
1246int
1247TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1248 unsigned int rulenum,
1249 TC_HANDLE_T *handle)
1250{
1251 STRUCT_ENTRY *e;
1252 struct chain_cache *c;
1253 unsigned int chainindex, end;
1254
1255 iptc_fn = TC_ZERO_COUNTER;
1256 CHECK(*handle);
1257
1258 if (!(c = find_label(chain, *handle))) {
1259 errno = ENOENT;
1260 return 0;
1261 }
1262
1263 chainindex = entry2index(*handle, c->start);
1264 end = entry2index(*handle, c->end);
1265
1266 if (chainindex + rulenum > end) {
1267 errno = E2BIG;
1268 return 0;
1269 }
1270
1271 e = index2entry(*handle, chainindex + rulenum);
1272
1273 if ((*handle)->counter_map[chainindex + rulenum].maptype
1274 == COUNTER_MAP_NORMAL_MAP) {
1275 (*handle)->counter_map[chainindex + rulenum].maptype
1276 = COUNTER_MAP_ZEROED;
1277 }
1278
1279 set_changed(*handle);
1280
1281 return 1;
1282}
1283
1284int
1285TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1286 unsigned int rulenum,
1287 STRUCT_COUNTERS *counters,
1288 TC_HANDLE_T *handle)
1289{
1290 STRUCT_ENTRY *e;
1291 struct chain_cache *c;
1292 unsigned int chainindex, end;
1293
1294 iptc_fn = TC_SET_COUNTER;
1295 CHECK(*handle);
1296
1297 if (!(c = find_label(chain, *handle))) {
1298 errno = ENOENT;
1299 return 0;
1300 }
1301
1302 chainindex = entry2index(*handle, c->start);
1303 end = entry2index(*handle, c->end);
1304
1305 if (chainindex + rulenum > end) {
1306 errno = E2BIG;
1307 return 0;
1308 }
1309
1310 e = index2entry(*handle, chainindex + rulenum);
1311
1312 (*handle)->counter_map[chainindex + rulenum].maptype
1313 = COUNTER_MAP_SET;
1314
1315 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1316
1317 set_changed(*handle);
1318
1319 return 1;
1320}
1321
Marc Bouchere6869a82000-03-20 06:03:29 +00001322/* Creates a new chain. */
1323/* To create a chain, create two rules: error node and unconditional
1324 * return. */
1325int
Rusty Russell79dee072000-05-02 16:45:16 +00001326TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001327{
Marc Bouchere6869a82000-03-20 06:03:29 +00001328 int ret;
1329 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001330 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001331 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001332 STRUCT_ENTRY ret;
1333 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001334 } newc;
1335
Rusty Russell79dee072000-05-02 16:45:16 +00001336 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001337
1338 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1339 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001340 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001341 || strcmp(chain, LABEL_DROP) == 0
1342 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001343 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001344 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001345 errno = EEXIST;
1346 return 0;
1347 }
1348
Rusty Russell79dee072000-05-02 16:45:16 +00001349 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001350 errno = EINVAL;
1351 return 0;
1352 }
1353
1354 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001355 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001356 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001357 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001358 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001359 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001360 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001361 strcpy(newc.name.error, chain);
1362
Rusty Russell79dee072000-05-02 16:45:16 +00001363 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001364 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001365 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001366 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001367 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001368 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001369 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001370 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001371
1372 /* Add just before terminal entry */
1373 ret = insert_rules(2, sizeof(newc), &newc.head,
1374 index2offset(*handle, (*handle)->new_number - 1),
1375 (*handle)->new_number - 1,
1376 0, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001377 return ret;
1378}
1379
1380static int
Rusty Russell79dee072000-05-02 16:45:16 +00001381count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001382{
Rusty Russell79dee072000-05-02 16:45:16 +00001383 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001384
Rusty Russell79dee072000-05-02 16:45:16 +00001385 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1386 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001387
1388 if (t->verdict == offset)
1389 (*ref)++;
1390 }
1391
1392 return 0;
1393}
1394
1395/* Get the number of references to this chain. */
1396int
Rusty Russell79dee072000-05-02 16:45:16 +00001397TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1398 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001399{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001400 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001401
Rusty Russell30fd6e52000-04-23 09:16:06 +00001402 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001403 errno = ENOENT;
1404 return 0;
1405 }
1406
1407 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001408 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001409 (*handle)->entries.size,
1410 count_ref, entry2offset(*handle, c->start), ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001411 return 1;
1412}
1413
1414/* Deletes a chain. */
1415int
Rusty Russell79dee072000-05-02 16:45:16 +00001416TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001417{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001418 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001419 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001420 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001421 int ret;
1422
Rusty Russell79dee072000-05-02 16:45:16 +00001423 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001424 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001425
Rusty Russell79dee072000-05-02 16:45:16 +00001426 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001427
Rusty Russell79dee072000-05-02 16:45:16 +00001428 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001429 errno = EINVAL;
1430 return 0;
1431 }
1432
1433 if (references > 0) {
1434 errno = EMLINK;
1435 return 0;
1436 }
1437
Rusty Russell30fd6e52000-04-23 09:16:06 +00001438 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001439 errno = ENOENT;
1440 return 0;
1441 }
1442
Rusty Russell30fd6e52000-04-23 09:16:06 +00001443 if ((void *)c->start != c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001444 errno = ENOTEMPTY;
1445 return 0;
1446 }
1447
1448 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001449 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001450 labeloff = index2offset(*handle, labelidx);
1451
1452 ret = delete_rules(2,
1453 get_entry(*handle, labeloff)->next_offset
Rusty Russell30fd6e52000-04-23 09:16:06 +00001454 + c->start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001455 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001456 return ret;
1457}
1458
1459/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001460int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1461 const IPT_CHAINLABEL newname,
1462 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001463{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001464 unsigned int labeloff, labelidx;
1465 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001466 struct ipt_error_target *t;
1467
Rusty Russell79dee072000-05-02 16:45:16 +00001468 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001469
Harald Welte1de80462000-10-30 12:00:27 +00001470 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1471 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001472 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001473 || strcmp(newname, LABEL_DROP) == 0
1474 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001475 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001476 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001477 errno = EEXIST;
1478 return 0;
1479 }
1480
Rusty Russell30fd6e52000-04-23 09:16:06 +00001481 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001482 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001483 errno = ENOENT;
1484 return 0;
1485 }
1486
Rusty Russell79dee072000-05-02 16:45:16 +00001487 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001488 errno = EINVAL;
1489 return 0;
1490 }
1491
1492 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001493 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001494 labeloff = index2offset(*handle, labelidx);
1495
1496 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001497 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001498
1499 memset(t->error, 0, sizeof(t->error));
1500 strcpy(t->error, newname);
Rusty Russell175f6412000-03-24 09:32:20 +00001501 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001502
Marc Bouchere6869a82000-03-20 06:03:29 +00001503 return 1;
1504}
1505
1506/* Sets the policy on a built-in chain. */
1507int
Rusty Russell79dee072000-05-02 16:45:16 +00001508TC_SET_POLICY(const IPT_CHAINLABEL chain,
1509 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001510 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001511 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001512{
1513 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001514 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001515 STRUCT_ENTRY *e;
1516 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001517
Rusty Russell79dee072000-05-02 16:45:16 +00001518 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001519 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001520 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001521 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001522 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001523 return 0;
1524 } else
1525 hook--;
1526
1527 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1528 if (policyoff != (*handle)->info.underflow[hook]) {
1529 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1530 chain, policyoff, (*handle)->info.underflow[hook]);
1531 return 0;
1532 }
1533
1534 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001535 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001536
Rusty Russell79dee072000-05-02 16:45:16 +00001537 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001538 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001539 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001540 t->verdict = -NF_DROP - 1;
1541 else {
1542 errno = EINVAL;
1543 return 0;
1544 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001545
1546 ctrindex = entry2index(*handle, e);
1547
1548 if (counters) {
1549 /* set byte and packet counters */
1550 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1551
1552 (*handle)->counter_map[ctrindex].maptype
1553 = COUNTER_MAP_SET;
1554
1555 } else {
1556 (*handle)->counter_map[ctrindex]
1557 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1558 }
1559
Rusty Russell175f6412000-03-24 09:32:20 +00001560 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001561
Marc Bouchere6869a82000-03-20 06:03:29 +00001562 return 1;
1563}
1564
1565/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001566 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001567 libiptc.c:833: fixed or forbidden register was spilled.
1568 This may be due to a compiler bug or to impossible asm
1569 statements or clauses.
1570*/
1571static void
Rusty Russell79dee072000-05-02 16:45:16 +00001572subtract_counters(STRUCT_COUNTERS *answer,
1573 const STRUCT_COUNTERS *a,
1574 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001575{
1576 answer->pcnt = a->pcnt - b->pcnt;
1577 answer->bcnt = a->bcnt - b->bcnt;
1578}
1579
1580int
Rusty Russell79dee072000-05-02 16:45:16 +00001581TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001582{
1583 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001584 STRUCT_REPLACE *repl;
1585 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001586 unsigned int i;
1587 size_t counterlen
Rusty Russell79dee072000-05-02 16:45:16 +00001588 = sizeof(STRUCT_COUNTERS_INFO)
1589 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00001590
1591 CHECK(*handle);
1592#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001593 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001594#endif
1595
1596 /* Don't commit if nothing changed. */
1597 if (!(*handle)->changed)
1598 goto finished;
1599
1600 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1601 if (!repl) {
1602 errno = ENOMEM;
1603 return 0;
1604 }
1605
1606 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001607 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001608 * (*handle)->info.num_entries);
1609 if (!repl->counters) {
1610 free(repl);
1611 errno = ENOMEM;
1612 return 0;
1613 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001614
Marc Bouchere6869a82000-03-20 06:03:29 +00001615 /* These are the counters we're going to put back, later. */
1616 newcounters = malloc(counterlen);
1617 if (!newcounters) {
1618 free(repl->counters);
1619 free(repl);
1620 errno = ENOMEM;
1621 return 0;
1622 }
1623
1624 strcpy(repl->name, (*handle)->info.name);
1625 repl->num_entries = (*handle)->new_number;
1626 repl->size = (*handle)->entries.size;
1627 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1628 sizeof(repl->hook_entry));
1629 memcpy(repl->underflow, (*handle)->info.underflow,
1630 sizeof(repl->underflow));
1631 repl->num_counters = (*handle)->info.num_entries;
1632 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001633 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001634 (*handle)->entries.size);
1635
Rusty Russell79dee072000-05-02 16:45:16 +00001636 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001637 sizeof(*repl) + (*handle)->entries.size) < 0) {
1638 free(repl->counters);
1639 free(repl);
1640 free(newcounters);
1641 return 0;
1642 }
1643
1644 /* Put counters back. */
1645 strcpy(newcounters->name, (*handle)->info.name);
1646 newcounters->num_counters = (*handle)->new_number;
1647 for (i = 0; i < (*handle)->new_number; i++) {
1648 unsigned int mappos = (*handle)->counter_map[i].mappos;
1649 switch ((*handle)->counter_map[i].maptype) {
1650 case COUNTER_MAP_NOMAP:
1651 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001652 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001653 break;
1654
1655 case COUNTER_MAP_NORMAL_MAP:
1656 /* Original read: X.
1657 * Atomic read on replacement: X + Y.
1658 * Currently in kernel: Z.
1659 * Want in kernel: X + Y + Z.
1660 * => Add in X + Y
1661 * => Add in replacement read.
1662 */
1663 newcounters->counters[i] = repl->counters[mappos];
1664 break;
1665
1666 case COUNTER_MAP_ZEROED:
1667 /* Original read: X.
1668 * Atomic read on replacement: X + Y.
1669 * Currently in kernel: Z.
1670 * Want in kernel: Y + Z.
1671 * => Add in Y.
1672 * => Add in (replacement read - original read).
1673 */
1674 subtract_counters(&newcounters->counters[i],
1675 &repl->counters[mappos],
1676 &index2entry(*handle, i)->counters);
1677 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001678
1679 case COUNTER_MAP_SET:
1680 /* Want to set counter (iptables-restore) */
1681
1682 memcpy(&newcounters->counters[i],
1683 &index2entry(*handle, i)->counters,
1684 sizeof(STRUCT_COUNTERS));
1685
1686 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001687 }
1688 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001689
1690#ifdef KERNEL_64_USERSPACE_32
1691 {
1692 /* Kernel will think that pointer should be 64-bits, and get
1693 padding. So we accomodate here (assumption: alignment of
1694 `counters' is on 64-bit boundary). */
1695 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1696 if ((unsigned long)&newcounters->counters % 8 != 0) {
1697 fprintf(stderr,
1698 "counters alignment incorrect! Mail rusty!\n");
1699 abort();
1700 }
1701 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001702 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001703#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001704
Rusty Russell79dee072000-05-02 16:45:16 +00001705 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1706 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001707 free(repl->counters);
1708 free(repl);
1709 free(newcounters);
1710 return 0;
1711 }
1712
1713 free(repl->counters);
1714 free(repl);
1715 free(newcounters);
1716
1717 finished:
Rusty Russell30fd6e52000-04-23 09:16:06 +00001718 if ((*handle)->cache_chain_heads)
1719 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +00001720 free(*handle);
1721 *handle = NULL;
1722 return 1;
1723}
1724
1725/* Get raw socket. */
1726int
Rusty Russell79dee072000-05-02 16:45:16 +00001727TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001728{
1729 return sockfd;
1730}
1731
1732/* Translates errno numbers into more human-readable form than strerror. */
1733const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001734TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001735{
1736 unsigned int i;
1737 struct table_struct {
1738 void *fn;
1739 int err;
1740 const char *message;
1741 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001742 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001743 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001744 { TC_INIT, ENOENT,
1745 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001746 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1747 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1748 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001749 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001750 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1751 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1752 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1753 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001754 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1755 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001756 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1757 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001758 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001759 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001760 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001761 { TC_CHECK_PACKET, ENOSYS,
1762 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001763 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001764 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001765 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001766 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001767 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001768 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001769 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001770
1771 { NULL, 0, "Incompatible with this kernel" },
1772 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1773 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1774 { NULL, ENOMEM, "Memory allocation problem" },
1775 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001776 };
1777
1778 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1779 if ((!table[i].fn || table[i].fn == iptc_fn)
1780 && table[i].err == err)
1781 return table[i].message;
1782 }
1783
1784 return strerror(err);
1785}