blob: 0dad6f48348be42cd06165daf4471e7be58e090e [file] [log] [blame]
Martin Josefssone560fd62003-06-13 16:56:51 +00001/* Library which manipulates firewall rules. Version $Revision: 1.37 $ */
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
Martin Josefssone560fd62003-06-13 16:56:51 +0000237 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000238 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000239 sockfd = -1;
240 }
Harald Welte366454b2002-01-07 13:46:50 +0000241
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000242 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
243 errno = EINVAL;
244 return NULL;
245 }
246
Rusty Russell79dee072000-05-02 16:45:16 +0000247 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000248 if (sockfd < 0)
249 return NULL;
250
251 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000252
Marc Bouchere6869a82000-03-20 06:03:29 +0000253 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000254 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000255 return NULL;
256
257 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000258 == NULL) {
259 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000260 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000261 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000262 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000263
264/* Too hard --RR */
265#if 0
266 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
267 dynlib = dlopen(pathname, RTLD_NOW);
268 if (!dynlib) {
269 errno = ENOENT;
270 return NULL;
271 }
272 h->hooknames = dlsym(dynlib, "hooknames");
273 if (!h->hooknames) {
274 errno = ENOENT;
275 return NULL;
276 }
277#else
278 h->hooknames = hooknames;
279#endif
280
281 /* Initialize current state */
282 h->info = info;
283 h->new_number = h->info.num_entries;
284 for (i = 0; i < h->info.num_entries; i++)
285 h->counter_map[i]
286 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
287
288 h->entries.size = h->info.size;
289
Rusty Russell79dee072000-05-02 16:45:16 +0000290 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000291
Rusty Russell79dee072000-05-02 16:45:16 +0000292 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000293 &tmp) < 0) {
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000294 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000295 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000296 free(h);
297 return NULL;
298 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000299
Marc Bouchere6869a82000-03-20 06:03:29 +0000300 CHECK(h);
301 return h;
302}
303
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000304void
305TC_FREE(TC_HANDLE_T *h)
306{
307 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000308 sockfd = -1;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000309 if ((*h)->cache_chain_heads)
310 free((*h)->cache_chain_heads);
311 free(*h);
312 *h = NULL;
313}
314
Marc Bouchere6869a82000-03-20 06:03:29 +0000315static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000316print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000317{
Rusty Russell228e98d2000-04-27 10:28:06 +0000318 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000319 return 0;
320}
321
Rusty Russell79dee072000-05-02 16:45:16 +0000322static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
323
Marc Bouchere6869a82000-03-20 06:03:29 +0000324void
Rusty Russell79dee072000-05-02 16:45:16 +0000325TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000326{
327 CHECK(handle);
328
329 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000330 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000331 handle->new_number, handle->entries.size);
332 printf("Table `%s'\n", handle->info.name);
333 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000334 handle->info.hook_entry[HOOK_PRE_ROUTING],
335 handle->info.hook_entry[HOOK_LOCAL_IN],
336 handle->info.hook_entry[HOOK_FORWARD],
337 handle->info.hook_entry[HOOK_LOCAL_OUT],
338 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000339 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000340 handle->info.underflow[HOOK_PRE_ROUTING],
341 handle->info.underflow[HOOK_LOCAL_IN],
342 handle->info.underflow[HOOK_FORWARD],
343 handle->info.underflow[HOOK_LOCAL_OUT],
344 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000345
Rusty Russell725d97a2000-07-07 08:54:22 +0000346 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000347 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000348}
349
Rusty Russell30fd6e52000-04-23 09:16:06 +0000350/* Returns 0 if not hook entry, else hooknumber + 1 */
351static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000352is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000353{
354 unsigned int i;
355
Rusty Russell79dee072000-05-02 16:45:16 +0000356 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000357 if ((h->info.valid_hooks & (1 << i))
358 && get_entry(h, h->info.hook_entry[i]) == e)
359 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000360 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000361 return 0;
362}
363
Rusty Russell30fd6e52000-04-23 09:16:06 +0000364static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000365add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000366{
367 unsigned int builtin;
368
369 /* Last entry. End it. */
370 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
371 /* This is the ERROR node at end of the table */
372 h->cache_chain_heads[h->cache_num_chains-1].end = *prev;
373 return 0;
374 }
375
376 /* We know this is the start of a new chain if it's an ERROR
377 target, or a hook entry point */
Rusty Russell67088e72000-05-10 01:18:57 +0000378 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000379 /* prev was last entry in previous chain */
380 h->cache_chain_heads[h->cache_num_chains-1].end
381 = *prev;
382
383 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
Rusty Russell79dee072000-05-02 16:45:16 +0000384 (const char *)GET_TARGET(e)->data);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000385 h->cache_chain_heads[h->cache_num_chains].start
386 = (void *)e + e->next_offset;
387 h->cache_num_chains++;
388 } else if ((builtin = is_hook_entry(e, h)) != 0) {
389 if (h->cache_num_chains > 0)
390 /* prev was last entry in previous chain */
391 h->cache_chain_heads[h->cache_num_chains-1].end
392 = *prev;
393
394 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
395 h->hooknames[builtin-1]);
396 h->cache_chain_heads[h->cache_num_chains].start
397 = (void *)e;
398 h->cache_num_chains++;
399 }
400
401 *prev = e;
402 return 0;
403}
404
Rusty Russell30fd6e52000-04-23 09:16:06 +0000405static int alphasort(const void *a, const void *b)
406{
407 return strcmp(((struct chain_cache *)a)->name,
408 ((struct chain_cache *)b)->name);
409}
410
Rusty Russell79dee072000-05-02 16:45:16 +0000411static int populate_cache(TC_HANDLE_T h)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000412{
413 unsigned int i;
Rusty Russell79dee072000-05-02 16:45:16 +0000414 STRUCT_ENTRY *prev;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000415
416 /* # chains < # rules / 2 + num builtins - 1 */
417 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
418 * sizeof(struct chain_cache));
419 if (!h->cache_chain_heads) {
420 errno = ENOMEM;
421 return 0;
422 }
423
424 h->cache_num_chains = 0;
425 h->cache_num_builtins = 0;
426
427 /* Count builtins */
Rusty Russell79dee072000-05-02 16:45:16 +0000428 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000429 if (h->info.valid_hooks & (1 << i))
430 h->cache_num_builtins++;
431 }
432
433 prev = NULL;
Rusty Russell725d97a2000-07-07 08:54:22 +0000434 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000435 add_chain, h, &prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000436
Rusty Russell30fd6e52000-04-23 09:16:06 +0000437 qsort(h->cache_chain_heads + h->cache_num_builtins,
438 h->cache_num_chains - h->cache_num_builtins,
439 sizeof(struct chain_cache), alphasort);
440
441 return 1;
442}
443
444/* Returns cache ptr if found, otherwise NULL. */
445static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000446find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000447{
Rusty Russell849779c2000-04-23 15:51:51 +0000448 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000449
450 if (handle->cache_chain_heads == NULL
451 && !populate_cache(handle))
452 return NULL;
453
Rusty Russell849779c2000-04-23 15:51:51 +0000454 /* FIXME: Linear search through builtins, then binary --RR */
455 for (i = 0; i < handle->cache_num_chains; i++) {
456 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
457 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000458 }
459
Rusty Russell849779c2000-04-23 15:51:51 +0000460 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000461}
462
Marc Bouchere6869a82000-03-20 06:03:29 +0000463/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000464int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000465{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000466 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000467}
468
469/* Returns the position of the final (ie. unconditional) element. */
470static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000471get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000472{
473 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000474 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000475
476 last_off = start;
477 e = get_entry(handle, start);
478
479 /* Terminate when we meet a error label or a hook entry. */
480 for (off = start + e->next_offset;
481 off < handle->entries.size;
482 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000483 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000484 unsigned int i;
485
486 e = get_entry(handle, off);
487
488 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000489 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000490 if ((handle->info.valid_hooks & (1 << i))
491 && off == handle->info.hook_entry[i])
492 return last_off;
493 }
494
495 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000496 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000497 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000498 return last_off;
499 }
500 /* SHOULD NEVER HAPPEN */
501 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
502 handle->entries.size, off);
503 abort();
504}
505
Rusty Russell30fd6e52000-04-23 09:16:06 +0000506/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000507const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000508TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000509{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000510 if ((*handle)->cache_chain_heads == NULL
511 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000512 return NULL;
513
Rusty Russell30fd6e52000-04-23 09:16:06 +0000514 (*handle)->cache_chain_iteration
515 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000516
Rusty Russell30fd6e52000-04-23 09:16:06 +0000517 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000518}
519
Rusty Russell30fd6e52000-04-23 09:16:06 +0000520/* Iterator functions to run through the chains. Returns NULL at end. */
521const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000522TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000523{
524 (*handle)->cache_chain_iteration++;
525
526 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000527 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000528 return NULL;
529
530 return (*handle)->cache_chain_iteration->name;
531}
532
533/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000534const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000535TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000536{
537 struct chain_cache *c;
538
539 c = find_label(chain, *handle);
540 if (!c) {
541 errno = ENOENT;
542 return NULL;
543 }
544
545 /* Empty chain: single return/policy rule */
546 if (c->start == c->end)
547 return NULL;
548
549 (*handle)->cache_rule_end = c->end;
550 return c->start;
551}
552
553/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000554const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000555TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000556{
557 if ((void *)prev + prev->next_offset
558 == (void *)(*handle)->cache_rule_end)
559 return NULL;
560
561 return (void *)prev + prev->next_offset;
562}
563
564#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000565/* How many rules in this chain? */
566unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000567TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000568{
569 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000570 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000571
572 CHECK(*handle);
573 if (!find_label(&off, chain, *handle)) {
574 errno = ENOENT;
575 return (unsigned int)-1;
576 }
577
578 start = get_entry(*handle, off);
579 end = get_entry(*handle, get_chain_end(*handle, off));
580
581 return entry2index(*handle, end) - entry2index(*handle, start);
582}
583
584/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000585const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
586 unsigned int n,
587 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000588{
589 unsigned int pos = 0, chainindex;
590
591 CHECK(*handle);
592 if (!find_label(&pos, chain, *handle)) {
593 errno = ENOENT;
594 return NULL;
595 }
596
597 chainindex = entry2index(*handle, get_entry(*handle, pos));
598
599 return index2entry(*handle, chainindex + n);
600}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000601#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000602
Rusty Russell30fd6e52000-04-23 09:16:06 +0000603static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000604target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000605{
606 int spos;
607 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000608 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000609
Rusty Russell30fd6e52000-04-23 09:16:06 +0000610 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000611 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000612
Rusty Russell79dee072000-05-02 16:45:16 +0000613 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
614 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000615
616 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000617 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000618 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000619 if (spos == RETURN)
620 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000621 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000622 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000623 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000624 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000625 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000626 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000627
628 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
629 entry2offset(handle, e), handle->entries.size,
630 spos);
631 abort();
632 }
633
634 jumpto = get_entry(handle, spos);
635
636 /* Fall through rule */
637 if (jumpto == (void *)e + e->next_offset)
638 return "";
639
640 /* Must point to head of a chain: ie. after error rule */
641 labelidx = entry2index(handle, jumpto) - 1;
642 return get_errorlabel(handle, index2offset(handle, labelidx));
643}
644
645/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000646const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
647 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000648{
Marc Bouchere6869a82000-03-20 06:03:29 +0000649 return target_name(*handle, e);
650}
651
652/* Is this a built-in chain? Actually returns hook + 1. */
653int
Rusty Russell79dee072000-05-02 16:45:16 +0000654TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000655{
656 unsigned int i;
657
Rusty Russell79dee072000-05-02 16:45:16 +0000658 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000659 if ((handle->info.valid_hooks & (1 << i))
660 && handle->hooknames[i]
661 && strcmp(handle->hooknames[i], chain) == 0)
662 return i+1;
663 }
664 return 0;
665}
666
667/* Get the policy of a given built-in chain */
668const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000669TC_GET_POLICY(const char *chain,
670 STRUCT_COUNTERS *counters,
671 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000672{
673 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000674 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000675 int hook;
676
Rusty Russell79dee072000-05-02 16:45:16 +0000677 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000678 if (hook != 0)
679 start = (*handle)->info.hook_entry[hook-1];
680 else
681 return NULL;
682
683 e = get_entry(*handle, get_chain_end(*handle, start));
684 *counters = e->counters;
685
686 return target_name(*handle, e);
687}
688
689static int
Rusty Russell79dee072000-05-02 16:45:16 +0000690correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000691 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000692 unsigned int offset, int delta_offset)
693{
Rusty Russell79dee072000-05-02 16:45:16 +0000694 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000695 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000696
697 /* Trap: insert of fall-through rule. Don't change fall-through
698 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000699 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000700 && t->verdict > (int)offset
701 && !(curr == offset &&
702 t->verdict == curr + e->next_offset)) {
703 t->verdict += delta_offset;
704 }
705
706 return 0;
707}
708
709/* Adjusts standard verdict jump positions after an insertion/deletion. */
710static int
Rusty Russell79dee072000-05-02 16:45:16 +0000711set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000712{
Rusty Russell725d97a2000-07-07 08:54:22 +0000713 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000714 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000715 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000716 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000717
Rusty Russell175f6412000-03-24 09:32:20 +0000718 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000719 return 1;
720}
721
722/* If prepend is set, then we are prepending to a chain: if the
723 * insertion position is an entry point, keep the entry point. */
724static int
725insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000726 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000727 unsigned int offset, unsigned int num_rules_offset,
728 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000729 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000730{
Rusty Russell79dee072000-05-02 16:45:16 +0000731 TC_HANDLE_T newh;
732 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000733 unsigned int i;
734
735 if (offset >= (*handle)->entries.size) {
736 errno = EINVAL;
737 return 0;
738 }
739
740 newinfo = (*handle)->info;
741
742 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000743 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000744 /* Entry points to START of chain, so keep same if
745 inserting on at that point. */
746 if ((*handle)->info.hook_entry[i] > offset)
747 newinfo.hook_entry[i] += rules_size;
748
749 /* Underflow always points to END of chain (policy),
750 so if something is inserted at same point, it
751 should be advanced. */
752 if ((*handle)->info.underflow[i] >= offset)
753 newinfo.underflow[i] += rules_size;
754 }
755
756 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000757 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000758 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000759 if (!newh)
760 return 0;
761 newh->info = newinfo;
762
763 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000764 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000765 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000766 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000767 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000768 memcpy((char *)newh->entries.entrytable + offset + rules_size,
769 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000770 (*handle)->entries.size - offset);
771
772 /* Move counter map. */
773 /* Copy pre... */
774 memcpy(newh->counter_map, (*handle)->counter_map,
775 sizeof(struct counter_map) * num_rules_offset);
776 /* ... copy post */
777 memcpy(newh->counter_map + num_rules_offset + num_rules,
778 (*handle)->counter_map + num_rules_offset,
779 sizeof(struct counter_map) * ((*handle)->new_number
780 - num_rules_offset));
781 /* Set intermediates to no counter copy */
782 for (i = 0; i < num_rules; i++)
783 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000784 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000785
786 newh->new_number = (*handle)->new_number + num_rules;
787 newh->entries.size = (*handle)->entries.size + rules_size;
788 newh->hooknames = (*handle)->hooknames;
789
Rusty Russell30fd6e52000-04-23 09:16:06 +0000790 if ((*handle)->cache_chain_heads)
791 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +0000792 free(*handle);
793 *handle = newh;
794
795 return set_verdict(offset, rules_size, handle);
796}
797
798static int
799delete_rules(unsigned int num_rules, unsigned int rules_size,
800 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000801 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000802{
803 unsigned int i;
804
805 if (offset + rules_size > (*handle)->entries.size) {
806 errno = EINVAL;
807 return 0;
808 }
809
810 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000811 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000812 /* In practice, we never delete up to a hook entry,
813 since the built-in chains are always first,
814 so these two are never equal */
815 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
816 (*handle)->info.hook_entry[i] -= rules_size;
817 else if ((*handle)->info.hook_entry[i] > offset) {
818 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
819 i, (*handle)->info.hook_entry[i], offset);
820 abort();
821 }
822
823 /* Underflow points to policy (terminal) rule in
824 built-in, so sequality is valid here (when deleting
825 the last rule). */
826 if ((*handle)->info.underflow[i] >= offset + rules_size)
827 (*handle)->info.underflow[i] -= rules_size;
828 else if ((*handle)->info.underflow[i] > offset) {
829 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
830 i, (*handle)->info.underflow[i], offset);
831 abort();
832 }
833 }
834
835 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000836 memmove((char *)(*handle)->entries.entrytable + offset,
837 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000838 (*handle)->entries.size - (offset + rules_size));
839
840 /* Move the counter map down. */
841 memmove(&(*handle)->counter_map[num_rules_offset],
842 &(*handle)->counter_map[num_rules_offset + num_rules],
843 sizeof(struct counter_map)
844 * ((*handle)->new_number - (num_rules + num_rules_offset)));
845
846 /* Fix numbers */
847 (*handle)->new_number -= num_rules;
848 (*handle)->entries.size -= rules_size;
849
850 return set_verdict(offset, -(int)rules_size, handle);
851}
852
853static int
Rusty Russell79dee072000-05-02 16:45:16 +0000854standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000855{
Rusty Russell79dee072000-05-02 16:45:16 +0000856 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000857
Rusty Russell79dee072000-05-02 16:45:16 +0000858 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000859
Rusty Russell67088e72000-05-10 01:18:57 +0000860 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000861 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000862 errno = EINVAL;
863 return 0;
864 }
865 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000866 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
867 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000868 t->verdict = verdict;
869
870 return 1;
871}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000872
Marc Bouchere6869a82000-03-20 06:03:29 +0000873static int
Rusty Russell79dee072000-05-02 16:45:16 +0000874map_target(const TC_HANDLE_T handle,
875 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000876 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000877 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000878{
Rusty Russell79dee072000-05-02 16:45:16 +0000879 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000880
881 /* Save old target (except data, which we don't change, except for
882 standard case, where we don't care). */
883 *old = *t;
884
885 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000886 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000887 return standard_map(e, offset + e->next_offset);
888 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000889 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000890 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000891 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000892 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000893 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000894 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000895 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
896 return standard_map(e, RETURN);
897 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000898 /* Can't jump to builtins. */
899 errno = EINVAL;
900 return 0;
901 } else {
902 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000903 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000904
Rusty Russell228e98d2000-04-27 10:28:06 +0000905 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000906 if (c)
907 return standard_map(e, entry2offset(handle, c->start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000908 }
909
910 /* Must be a module? If not, kernel will reject... */
911 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000912 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000913 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000914 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000915 return 1;
916}
917
918static void
Rusty Russell79dee072000-05-02 16:45:16 +0000919unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000920{
Rusty Russell79dee072000-05-02 16:45:16 +0000921 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000922
923 /* Save old target (except data, which we don't change, except for
924 standard case, where we don't care). */
925 *t = *old;
926}
927
928/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
929int
Rusty Russell79dee072000-05-02 16:45:16 +0000930TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
931 const STRUCT_ENTRY *e,
932 unsigned int rulenum,
933 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000934{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000935 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000936 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000937 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000938 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000939 int ret;
940
Rusty Russell79dee072000-05-02 16:45:16 +0000941 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000942 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000943 errno = ENOENT;
944 return 0;
945 }
946
Rusty Russell30fd6e52000-04-23 09:16:06 +0000947 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000948
Rusty Russell14a1c912000-08-26 04:41:10 +0000949 tmp = index2entry(*handle, chainindex + rulenum);
950 if (!tmp || tmp > c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000951 errno = E2BIG;
952 return 0;
953 }
954 offset = index2offset(*handle, chainindex + rulenum);
955
956 /* Mapping target actually alters entry, but that's
957 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +0000958 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000959 return 0;
960
961 ret = insert_rules(1, e->next_offset, e, offset,
962 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000963 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000964 return ret;
965}
966
967/* Atomically replace rule `rulenum' in `chain' with `fw'. */
968int
Rusty Russell79dee072000-05-02 16:45:16 +0000969TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
970 const STRUCT_ENTRY *e,
971 unsigned int rulenum,
972 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000973{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000974 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000975 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000976 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000977 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000978 int ret;
979
Rusty Russell79dee072000-05-02 16:45:16 +0000980 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +0000981
Rusty Russell30fd6e52000-04-23 09:16:06 +0000982 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000983 errno = ENOENT;
984 return 0;
985 }
986
Rusty Russell30fd6e52000-04-23 09:16:06 +0000987 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000988
Rusty Russell14a1c912000-08-26 04:41:10 +0000989 tmp = index2entry(*handle, chainindex + rulenum);
990 if (!tmp || tmp >= c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000991 errno = E2BIG;
992 return 0;
993 }
994
995 offset = index2offset(*handle, chainindex + rulenum);
996 /* Replace = delete and insert. */
997 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
998 offset, chainindex + rulenum, handle))
999 return 0;
1000
Rusty Russell79dee072000-05-02 16:45:16 +00001001 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001002 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001003
1004 ret = insert_rules(1, e->next_offset, e, offset,
1005 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001006 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001007 return ret;
1008}
1009
1010/* Append entry `fw' to chain `chain'. Equivalent to insert with
1011 rulenum = length of chain. */
1012int
Rusty Russell79dee072000-05-02 16:45:16 +00001013TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1014 const STRUCT_ENTRY *e,
1015 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001016{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001017 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001018 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001019 int ret;
1020
Rusty Russell79dee072000-05-02 16:45:16 +00001021 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001022 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001023 errno = ENOENT;
1024 return 0;
1025 }
1026
Rusty Russell79dee072000-05-02 16:45:16 +00001027 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001028 entry2offset(*handle, c->end), &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001029 return 0;
1030
Rusty Russell30fd6e52000-04-23 09:16:06 +00001031 ret = insert_rules(1, e->next_offset, e,
1032 entry2offset(*handle, c->end),
1033 entry2index(*handle, c->end),
Marc Bouchere6869a82000-03-20 06:03:29 +00001034 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001035 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001036 return ret;
1037}
1038
1039static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001040match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001041 const unsigned char *a_elems,
1042 const unsigned char *b_elems,
1043 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001044{
Rusty Russell79dee072000-05-02 16:45:16 +00001045 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001046 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001047
1048 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001049 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001050
Rusty Russell228e98d2000-04-27 10:28:06 +00001051 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001052 return 1;
1053
Rusty Russell228e98d2000-04-27 10:28:06 +00001054 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001055 return 1;
1056
Rusty Russell73ef09b2000-07-03 10:24:04 +00001057 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001058
Rusty Russell73ef09b2000-07-03 10:24:04 +00001059 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001060 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001061 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001062 *maskptr += i;
1063 return 0;
1064}
1065
1066static inline int
1067target_different(const unsigned char *a_targdata,
1068 const unsigned char *b_targdata,
1069 unsigned int tdatasize,
1070 const unsigned char *mask)
1071{
1072 unsigned int i;
1073 for (i = 0; i < tdatasize; i++)
1074 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1075 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001076
1077 return 0;
1078}
1079
Rusty Russell79dee072000-05-02 16:45:16 +00001080static int
1081is_same(const STRUCT_ENTRY *a,
1082 const STRUCT_ENTRY *b,
1083 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001084
1085/* Delete the first rule in `chain' which matches `fw'. */
1086int
Rusty Russell79dee072000-05-02 16:45:16 +00001087TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1088 const STRUCT_ENTRY *origfw,
1089 unsigned char *matchmask,
1090 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001091{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001092 unsigned int offset;
1093 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001094 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001095
Rusty Russell79dee072000-05-02 16:45:16 +00001096 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001097 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001098 errno = ENOENT;
1099 return 0;
1100 }
1101
1102 fw = malloc(origfw->next_offset);
1103 if (fw == NULL) {
1104 errno = ENOMEM;
1105 return 0;
1106 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001107
Rusty Russell30fd6e52000-04-23 09:16:06 +00001108 for (offset = entry2offset(*handle, c->start);
1109 offset < entry2offset(*handle, c->end);
1110 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001111 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001112
1113 memcpy(fw, origfw, origfw->next_offset);
1114
1115 /* FIXME: handle this in is_same --RR */
1116 if (!map_target(*handle, fw, offset, &discard)) {
1117 free(fw);
1118 return 0;
1119 }
1120 e = get_entry(*handle, offset);
1121
1122#if 0
1123 printf("Deleting:\n");
1124 dump_entry(newe);
1125#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001126 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001127 int ret;
1128 ret = delete_rules(1, e->next_offset,
1129 offset, entry2index(*handle, e),
1130 handle);
1131 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001132 return ret;
1133 }
1134 }
1135
1136 free(fw);
1137 errno = ENOENT;
1138 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001139}
Marc Bouchere6869a82000-03-20 06:03:29 +00001140
1141/* Delete the rule in position `rulenum' in `chain'. */
1142int
Rusty Russell79dee072000-05-02 16:45:16 +00001143TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1144 unsigned int rulenum,
1145 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001146{
Marc Bouchere6869a82000-03-20 06:03:29 +00001147 unsigned int index;
1148 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001149 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001150 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001151
Rusty Russell79dee072000-05-02 16:45:16 +00001152 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001153 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001154 errno = ENOENT;
1155 return 0;
1156 }
1157
Rusty Russell30fd6e52000-04-23 09:16:06 +00001158 index = entry2index(*handle, c->start) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001159
Rusty Russell30fd6e52000-04-23 09:16:06 +00001160 if (index >= entry2index(*handle, c->end)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001161 errno = E2BIG;
1162 return 0;
1163 }
1164
1165 e = index2entry(*handle, index);
1166 if (e == NULL) {
1167 errno = EINVAL;
1168 return 0;
1169 }
1170
1171 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1172 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001173 return ret;
1174}
1175
1176/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1177 NULL and sets errno. */
1178const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001179TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1180 STRUCT_ENTRY *entry,
1181 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001182{
1183 errno = ENOSYS;
1184 return NULL;
1185}
1186
1187/* Flushes the entries in the given chain (ie. empties chain). */
1188int
Rusty Russell79dee072000-05-02 16:45:16 +00001189TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001190{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001191 unsigned int startindex, endindex;
1192 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001193 int ret;
1194
Rusty Russell79dee072000-05-02 16:45:16 +00001195 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001196 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001197 errno = ENOENT;
1198 return 0;
1199 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001200 startindex = entry2index(*handle, c->start);
1201 endindex = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001202
1203 ret = delete_rules(endindex - startindex,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001204 (char *)c->end - (char *)c->start,
1205 entry2offset(*handle, c->start), startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001206 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001207 return ret;
1208}
1209
1210/* Zeroes the counters in a chain. */
1211int
Rusty Russell79dee072000-05-02 16:45:16 +00001212TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001213{
1214 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001215 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001216
Rusty Russell30fd6e52000-04-23 09:16:06 +00001217 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001218 errno = ENOENT;
1219 return 0;
1220 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001221
Rusty Russell30fd6e52000-04-23 09:16:06 +00001222 i = entry2index(*handle, c->start);
1223 end = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001224
1225 for (; i <= end; i++) {
1226 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1227 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1228 }
Rusty Russell175f6412000-03-24 09:32:20 +00001229 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001230
Marc Bouchere6869a82000-03-20 06:03:29 +00001231 return 1;
1232}
1233
Harald Welte1cef74d2001-01-05 15:22:59 +00001234STRUCT_COUNTERS *
1235TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1236 unsigned int rulenum,
1237 TC_HANDLE_T *handle)
1238{
1239 STRUCT_ENTRY *e;
1240 struct chain_cache *c;
1241 unsigned int chainindex, end;
1242
1243 iptc_fn = TC_READ_COUNTER;
1244 CHECK(*handle);
1245
1246 if (!(c = find_label(chain, *handle))) {
1247 errno = ENOENT;
1248 return NULL;
1249 }
1250
1251 chainindex = entry2index(*handle, c->start);
1252 end = entry2index(*handle, c->end);
1253
1254 if (chainindex + rulenum > end) {
1255 errno = E2BIG;
1256 return NULL;
1257 }
1258
1259 e = index2entry(*handle, chainindex + rulenum);
1260
1261 return &e->counters;
1262}
1263
1264int
1265TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1266 unsigned int rulenum,
1267 TC_HANDLE_T *handle)
1268{
1269 STRUCT_ENTRY *e;
1270 struct chain_cache *c;
1271 unsigned int chainindex, end;
1272
1273 iptc_fn = TC_ZERO_COUNTER;
1274 CHECK(*handle);
1275
1276 if (!(c = find_label(chain, *handle))) {
1277 errno = ENOENT;
1278 return 0;
1279 }
1280
1281 chainindex = entry2index(*handle, c->start);
1282 end = entry2index(*handle, c->end);
1283
1284 if (chainindex + rulenum > end) {
1285 errno = E2BIG;
1286 return 0;
1287 }
1288
1289 e = index2entry(*handle, chainindex + rulenum);
1290
1291 if ((*handle)->counter_map[chainindex + rulenum].maptype
1292 == COUNTER_MAP_NORMAL_MAP) {
1293 (*handle)->counter_map[chainindex + rulenum].maptype
1294 = COUNTER_MAP_ZEROED;
1295 }
1296
1297 set_changed(*handle);
1298
1299 return 1;
1300}
1301
1302int
1303TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1304 unsigned int rulenum,
1305 STRUCT_COUNTERS *counters,
1306 TC_HANDLE_T *handle)
1307{
1308 STRUCT_ENTRY *e;
1309 struct chain_cache *c;
1310 unsigned int chainindex, end;
1311
1312 iptc_fn = TC_SET_COUNTER;
1313 CHECK(*handle);
1314
1315 if (!(c = find_label(chain, *handle))) {
1316 errno = ENOENT;
1317 return 0;
1318 }
1319
1320 chainindex = entry2index(*handle, c->start);
1321 end = entry2index(*handle, c->end);
1322
1323 if (chainindex + rulenum > end) {
1324 errno = E2BIG;
1325 return 0;
1326 }
1327
1328 e = index2entry(*handle, chainindex + rulenum);
1329
1330 (*handle)->counter_map[chainindex + rulenum].maptype
1331 = COUNTER_MAP_SET;
1332
1333 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1334
1335 set_changed(*handle);
1336
1337 return 1;
1338}
1339
Marc Bouchere6869a82000-03-20 06:03:29 +00001340/* Creates a new chain. */
1341/* To create a chain, create two rules: error node and unconditional
1342 * return. */
1343int
Rusty Russell79dee072000-05-02 16:45:16 +00001344TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001345{
Marc Bouchere6869a82000-03-20 06:03:29 +00001346 int ret;
1347 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001348 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001349 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001350 STRUCT_ENTRY ret;
1351 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001352 } newc;
1353
Rusty Russell79dee072000-05-02 16:45:16 +00001354 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001355
1356 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1357 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001358 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001359 || strcmp(chain, LABEL_DROP) == 0
1360 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001361 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001362 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001363 errno = EEXIST;
1364 return 0;
1365 }
1366
Rusty Russell79dee072000-05-02 16:45:16 +00001367 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001368 errno = EINVAL;
1369 return 0;
1370 }
1371
1372 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001373 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001374 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001375 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001376 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001377 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001378 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001379 strcpy(newc.name.error, chain);
1380
Rusty Russell79dee072000-05-02 16:45:16 +00001381 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001382 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001383 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001384 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001385 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001386 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001387 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001388 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001389
1390 /* Add just before terminal entry */
1391 ret = insert_rules(2, sizeof(newc), &newc.head,
1392 index2offset(*handle, (*handle)->new_number - 1),
1393 (*handle)->new_number - 1,
1394 0, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001395 return ret;
1396}
1397
1398static int
Rusty Russell79dee072000-05-02 16:45:16 +00001399count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001400{
Rusty Russell79dee072000-05-02 16:45:16 +00001401 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001402
Rusty Russell79dee072000-05-02 16:45:16 +00001403 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1404 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001405
1406 if (t->verdict == offset)
1407 (*ref)++;
1408 }
1409
1410 return 0;
1411}
1412
1413/* Get the number of references to this chain. */
1414int
Rusty Russell79dee072000-05-02 16:45:16 +00001415TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1416 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001417{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001418 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001419
Rusty Russell30fd6e52000-04-23 09:16:06 +00001420 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001421 errno = ENOENT;
1422 return 0;
1423 }
1424
1425 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001426 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001427 (*handle)->entries.size,
1428 count_ref, entry2offset(*handle, c->start), ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001429 return 1;
1430}
1431
1432/* Deletes a chain. */
1433int
Rusty Russell79dee072000-05-02 16:45:16 +00001434TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001435{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001436 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001437 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001438 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001439 int ret;
1440
Rusty Russell79dee072000-05-02 16:45:16 +00001441 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001442 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001443
Rusty Russell79dee072000-05-02 16:45:16 +00001444 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001445
Rusty Russell79dee072000-05-02 16:45:16 +00001446 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001447 errno = EINVAL;
1448 return 0;
1449 }
1450
1451 if (references > 0) {
1452 errno = EMLINK;
1453 return 0;
1454 }
1455
Rusty Russell30fd6e52000-04-23 09:16:06 +00001456 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001457 errno = ENOENT;
1458 return 0;
1459 }
1460
Rusty Russell30fd6e52000-04-23 09:16:06 +00001461 if ((void *)c->start != c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001462 errno = ENOTEMPTY;
1463 return 0;
1464 }
1465
1466 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001467 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001468 labeloff = index2offset(*handle, labelidx);
1469
1470 ret = delete_rules(2,
1471 get_entry(*handle, labeloff)->next_offset
Rusty Russell30fd6e52000-04-23 09:16:06 +00001472 + c->start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001473 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001474 return ret;
1475}
1476
1477/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001478int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1479 const IPT_CHAINLABEL newname,
1480 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001481{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001482 unsigned int labeloff, labelidx;
1483 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001484 struct ipt_error_target *t;
1485
Rusty Russell79dee072000-05-02 16:45:16 +00001486 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001487
Harald Welte1de80462000-10-30 12:00:27 +00001488 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1489 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001490 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001491 || strcmp(newname, LABEL_DROP) == 0
1492 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001493 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001494 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001495 errno = EEXIST;
1496 return 0;
1497 }
1498
Rusty Russell30fd6e52000-04-23 09:16:06 +00001499 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001500 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001501 errno = ENOENT;
1502 return 0;
1503 }
1504
Rusty Russell79dee072000-05-02 16:45:16 +00001505 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001506 errno = EINVAL;
1507 return 0;
1508 }
1509
1510 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001511 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001512 labeloff = index2offset(*handle, labelidx);
1513
1514 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001515 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001516
1517 memset(t->error, 0, sizeof(t->error));
1518 strcpy(t->error, newname);
Rusty Russell175f6412000-03-24 09:32:20 +00001519 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001520
Marc Bouchere6869a82000-03-20 06:03:29 +00001521 return 1;
1522}
1523
1524/* Sets the policy on a built-in chain. */
1525int
Rusty Russell79dee072000-05-02 16:45:16 +00001526TC_SET_POLICY(const IPT_CHAINLABEL chain,
1527 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001528 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001529 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001530{
1531 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001532 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001533 STRUCT_ENTRY *e;
1534 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001535
Rusty Russell79dee072000-05-02 16:45:16 +00001536 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001537 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001538 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001539 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001540 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001541 return 0;
1542 } else
1543 hook--;
1544
1545 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1546 if (policyoff != (*handle)->info.underflow[hook]) {
1547 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1548 chain, policyoff, (*handle)->info.underflow[hook]);
1549 return 0;
1550 }
1551
1552 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001553 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001554
Rusty Russell79dee072000-05-02 16:45:16 +00001555 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001556 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001557 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001558 t->verdict = -NF_DROP - 1;
1559 else {
1560 errno = EINVAL;
1561 return 0;
1562 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001563
1564 ctrindex = entry2index(*handle, e);
1565
1566 if (counters) {
1567 /* set byte and packet counters */
1568 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1569
1570 (*handle)->counter_map[ctrindex].maptype
1571 = COUNTER_MAP_SET;
1572
1573 } else {
1574 (*handle)->counter_map[ctrindex]
1575 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1576 }
1577
Rusty Russell175f6412000-03-24 09:32:20 +00001578 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001579
Marc Bouchere6869a82000-03-20 06:03:29 +00001580 return 1;
1581}
1582
1583/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001584 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001585 libiptc.c:833: fixed or forbidden register was spilled.
1586 This may be due to a compiler bug or to impossible asm
1587 statements or clauses.
1588*/
1589static void
Rusty Russell79dee072000-05-02 16:45:16 +00001590subtract_counters(STRUCT_COUNTERS *answer,
1591 const STRUCT_COUNTERS *a,
1592 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001593{
1594 answer->pcnt = a->pcnt - b->pcnt;
1595 answer->bcnt = a->bcnt - b->bcnt;
1596}
1597
1598int
Rusty Russell79dee072000-05-02 16:45:16 +00001599TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001600{
1601 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001602 STRUCT_REPLACE *repl;
1603 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001604 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001605 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001606
1607 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001608
1609 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1610 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1611
Marc Bouchere6869a82000-03-20 06:03:29 +00001612#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001613 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001614#endif
1615
1616 /* Don't commit if nothing changed. */
1617 if (!(*handle)->changed)
1618 goto finished;
1619
1620 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1621 if (!repl) {
1622 errno = ENOMEM;
1623 return 0;
1624 }
1625
1626 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001627 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001628 * (*handle)->info.num_entries);
1629 if (!repl->counters) {
1630 free(repl);
1631 errno = ENOMEM;
1632 return 0;
1633 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001634
Marc Bouchere6869a82000-03-20 06:03:29 +00001635 /* These are the counters we're going to put back, later. */
1636 newcounters = malloc(counterlen);
1637 if (!newcounters) {
1638 free(repl->counters);
1639 free(repl);
1640 errno = ENOMEM;
1641 return 0;
1642 }
1643
1644 strcpy(repl->name, (*handle)->info.name);
1645 repl->num_entries = (*handle)->new_number;
1646 repl->size = (*handle)->entries.size;
1647 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1648 sizeof(repl->hook_entry));
1649 memcpy(repl->underflow, (*handle)->info.underflow,
1650 sizeof(repl->underflow));
1651 repl->num_counters = (*handle)->info.num_entries;
1652 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001653 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001654 (*handle)->entries.size);
1655
Rusty Russell79dee072000-05-02 16:45:16 +00001656 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001657 sizeof(*repl) + (*handle)->entries.size) < 0) {
1658 free(repl->counters);
1659 free(repl);
1660 free(newcounters);
1661 return 0;
1662 }
1663
1664 /* Put counters back. */
1665 strcpy(newcounters->name, (*handle)->info.name);
1666 newcounters->num_counters = (*handle)->new_number;
1667 for (i = 0; i < (*handle)->new_number; i++) {
1668 unsigned int mappos = (*handle)->counter_map[i].mappos;
1669 switch ((*handle)->counter_map[i].maptype) {
1670 case COUNTER_MAP_NOMAP:
1671 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001672 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001673 break;
1674
1675 case COUNTER_MAP_NORMAL_MAP:
1676 /* Original read: X.
1677 * Atomic read on replacement: X + Y.
1678 * Currently in kernel: Z.
1679 * Want in kernel: X + Y + Z.
1680 * => Add in X + Y
1681 * => Add in replacement read.
1682 */
1683 newcounters->counters[i] = repl->counters[mappos];
1684 break;
1685
1686 case COUNTER_MAP_ZEROED:
1687 /* Original read: X.
1688 * Atomic read on replacement: X + Y.
1689 * Currently in kernel: Z.
1690 * Want in kernel: Y + Z.
1691 * => Add in Y.
1692 * => Add in (replacement read - original read).
1693 */
1694 subtract_counters(&newcounters->counters[i],
1695 &repl->counters[mappos],
1696 &index2entry(*handle, i)->counters);
1697 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001698
1699 case COUNTER_MAP_SET:
1700 /* Want to set counter (iptables-restore) */
1701
1702 memcpy(&newcounters->counters[i],
1703 &index2entry(*handle, i)->counters,
1704 sizeof(STRUCT_COUNTERS));
1705
1706 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001707 }
1708 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001709
1710#ifdef KERNEL_64_USERSPACE_32
1711 {
1712 /* Kernel will think that pointer should be 64-bits, and get
1713 padding. So we accomodate here (assumption: alignment of
1714 `counters' is on 64-bit boundary). */
1715 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1716 if ((unsigned long)&newcounters->counters % 8 != 0) {
1717 fprintf(stderr,
1718 "counters alignment incorrect! Mail rusty!\n");
1719 abort();
1720 }
1721 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001722 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001723#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001724
Rusty Russell79dee072000-05-02 16:45:16 +00001725 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1726 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001727 free(repl->counters);
1728 free(repl);
1729 free(newcounters);
1730 return 0;
1731 }
1732
1733 free(repl->counters);
1734 free(repl);
1735 free(newcounters);
1736
1737 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001738 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001739 return 1;
1740}
1741
1742/* Get raw socket. */
1743int
Rusty Russell79dee072000-05-02 16:45:16 +00001744TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001745{
1746 return sockfd;
1747}
1748
1749/* Translates errno numbers into more human-readable form than strerror. */
1750const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001751TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001752{
1753 unsigned int i;
1754 struct table_struct {
1755 void *fn;
1756 int err;
1757 const char *message;
1758 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001759 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001760 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001761 { TC_INIT, ENOENT,
1762 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001763 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1764 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1765 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001766 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001767 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1768 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1769 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1770 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001771 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1772 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001773 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1774 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001775 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001776 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001777 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001778 { TC_CHECK_PACKET, ENOSYS,
1779 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001780 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001781 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001782 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001783 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001784 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001785 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001786 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001787
1788 { NULL, 0, "Incompatible with this kernel" },
1789 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1790 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1791 { NULL, ENOMEM, "Memory allocation problem" },
1792 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001793 };
1794
1795 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1796 if ((!table[i].fn || table[i].fn == iptc_fn)
1797 && table[i].err == err)
1798 return table[i].message;
1799 }
1800
1801 return strerror(err);
1802}