blob: 2a24b8ae4a89b35f3d5863aa6572fccfafa08483 [file] [log] [blame]
Harald Weltee0072942001-01-23 22:55:04 +00001/* Library which manipulates firewall rules. Version $Revision: 1.29 $ */
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__
Rusty Russell79dee072000-05-02 16:45:16 +000019static STRUCT_ENTRY_TARGET *
20GET_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
Rusty Russell30fd6e52000-04-23 09:16:06 +0000107#ifndef NDEBUG
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
Rusty Russell79dee072000-05-02 16:45:16 +0000237 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000238 if (sockfd < 0)
239 return NULL;
240
241 s = sizeof(info);
Rusty Russell79dee072000-05-02 16:45:16 +0000242 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000243 errno = EINVAL;
244 return NULL;
245 }
246 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000247 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000248 return NULL;
249
250 if ((h = alloc_handle(info.name, info.size, info.num_entries))
251 == NULL)
252 return NULL;
253
254/* Too hard --RR */
255#if 0
256 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
257 dynlib = dlopen(pathname, RTLD_NOW);
258 if (!dynlib) {
259 errno = ENOENT;
260 return NULL;
261 }
262 h->hooknames = dlsym(dynlib, "hooknames");
263 if (!h->hooknames) {
264 errno = ENOENT;
265 return NULL;
266 }
267#else
268 h->hooknames = hooknames;
269#endif
270
271 /* Initialize current state */
272 h->info = info;
273 h->new_number = h->info.num_entries;
274 for (i = 0; i < h->info.num_entries; i++)
275 h->counter_map[i]
276 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
277
278 h->entries.size = h->info.size;
279
Rusty Russell79dee072000-05-02 16:45:16 +0000280 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000281
Rusty Russell79dee072000-05-02 16:45:16 +0000282 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000283 &tmp) < 0) {
284 free(h);
285 return NULL;
286 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000287
Marc Bouchere6869a82000-03-20 06:03:29 +0000288 CHECK(h);
289 return h;
290}
291
Marc Bouchere6869a82000-03-20 06:03:29 +0000292static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000293print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000294{
Rusty Russell228e98d2000-04-27 10:28:06 +0000295 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000296 return 0;
297}
298
Rusty Russell79dee072000-05-02 16:45:16 +0000299static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
300
Marc Bouchere6869a82000-03-20 06:03:29 +0000301void
Rusty Russell79dee072000-05-02 16:45:16 +0000302TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000303{
304 CHECK(handle);
305
306 printf("libiptc v%s. %u entries, %u bytes.\n",
307 NETFILTER_VERSION,
308 handle->new_number, handle->entries.size);
309 printf("Table `%s'\n", handle->info.name);
310 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000311 handle->info.hook_entry[HOOK_PRE_ROUTING],
312 handle->info.hook_entry[HOOK_LOCAL_IN],
313 handle->info.hook_entry[HOOK_FORWARD],
314 handle->info.hook_entry[HOOK_LOCAL_OUT],
315 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000316 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000317 handle->info.underflow[HOOK_PRE_ROUTING],
318 handle->info.underflow[HOOK_LOCAL_IN],
319 handle->info.underflow[HOOK_FORWARD],
320 handle->info.underflow[HOOK_LOCAL_OUT],
321 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000322
Rusty Russell725d97a2000-07-07 08:54:22 +0000323 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000324 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000325}
326
Rusty Russell30fd6e52000-04-23 09:16:06 +0000327/* Returns 0 if not hook entry, else hooknumber + 1 */
328static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000329is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000330{
331 unsigned int i;
332
Rusty Russell79dee072000-05-02 16:45:16 +0000333 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000334 if ((h->info.valid_hooks & (1 << i))
335 && get_entry(h, h->info.hook_entry[i]) == e)
336 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000337 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000338 return 0;
339}
340
Rusty Russell30fd6e52000-04-23 09:16:06 +0000341static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000342add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000343{
344 unsigned int builtin;
345
346 /* Last entry. End it. */
347 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
348 /* This is the ERROR node at end of the table */
349 h->cache_chain_heads[h->cache_num_chains-1].end = *prev;
350 return 0;
351 }
352
353 /* We know this is the start of a new chain if it's an ERROR
354 target, or a hook entry point */
Rusty Russell67088e72000-05-10 01:18:57 +0000355 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000356 /* prev was last entry in previous chain */
357 h->cache_chain_heads[h->cache_num_chains-1].end
358 = *prev;
359
360 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
Rusty Russell79dee072000-05-02 16:45:16 +0000361 (const char *)GET_TARGET(e)->data);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000362 h->cache_chain_heads[h->cache_num_chains].start
363 = (void *)e + e->next_offset;
364 h->cache_num_chains++;
365 } else if ((builtin = is_hook_entry(e, h)) != 0) {
366 if (h->cache_num_chains > 0)
367 /* prev was last entry in previous chain */
368 h->cache_chain_heads[h->cache_num_chains-1].end
369 = *prev;
370
371 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
372 h->hooknames[builtin-1]);
373 h->cache_chain_heads[h->cache_num_chains].start
374 = (void *)e;
375 h->cache_num_chains++;
376 }
377
378 *prev = e;
379 return 0;
380}
381
Rusty Russell30fd6e52000-04-23 09:16:06 +0000382static int alphasort(const void *a, const void *b)
383{
384 return strcmp(((struct chain_cache *)a)->name,
385 ((struct chain_cache *)b)->name);
386}
387
Rusty Russell79dee072000-05-02 16:45:16 +0000388static int populate_cache(TC_HANDLE_T h)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000389{
390 unsigned int i;
Rusty Russell79dee072000-05-02 16:45:16 +0000391 STRUCT_ENTRY *prev;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000392
393 /* # chains < # rules / 2 + num builtins - 1 */
394 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
395 * sizeof(struct chain_cache));
396 if (!h->cache_chain_heads) {
397 errno = ENOMEM;
398 return 0;
399 }
400
401 h->cache_num_chains = 0;
402 h->cache_num_builtins = 0;
403
404 /* Count builtins */
Rusty Russell79dee072000-05-02 16:45:16 +0000405 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000406 if (h->info.valid_hooks & (1 << i))
407 h->cache_num_builtins++;
408 }
409
410 prev = NULL;
Rusty Russell725d97a2000-07-07 08:54:22 +0000411 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000412 add_chain, h, &prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000413
Rusty Russell30fd6e52000-04-23 09:16:06 +0000414 qsort(h->cache_chain_heads + h->cache_num_builtins,
415 h->cache_num_chains - h->cache_num_builtins,
416 sizeof(struct chain_cache), alphasort);
417
418 return 1;
419}
420
421/* Returns cache ptr if found, otherwise NULL. */
422static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000423find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000424{
Rusty Russell849779c2000-04-23 15:51:51 +0000425 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000426
427 if (handle->cache_chain_heads == NULL
428 && !populate_cache(handle))
429 return NULL;
430
Rusty Russell849779c2000-04-23 15:51:51 +0000431 /* FIXME: Linear search through builtins, then binary --RR */
432 for (i = 0; i < handle->cache_num_chains; i++) {
433 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
434 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000435 }
436
Rusty Russell849779c2000-04-23 15:51:51 +0000437 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000438}
439
Marc Bouchere6869a82000-03-20 06:03:29 +0000440/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000441int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000442{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000443 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000444}
445
446/* Returns the position of the final (ie. unconditional) element. */
447static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000448get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000449{
450 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000451 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000452
453 last_off = start;
454 e = get_entry(handle, start);
455
456 /* Terminate when we meet a error label or a hook entry. */
457 for (off = start + e->next_offset;
458 off < handle->entries.size;
459 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000460 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000461 unsigned int i;
462
463 e = get_entry(handle, off);
464
465 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000466 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000467 if ((handle->info.valid_hooks & (1 << i))
468 && off == handle->info.hook_entry[i])
469 return last_off;
470 }
471
472 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000473 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000474 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000475 return last_off;
476 }
477 /* SHOULD NEVER HAPPEN */
478 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
479 handle->entries.size, off);
480 abort();
481}
482
Rusty Russell30fd6e52000-04-23 09:16:06 +0000483/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000484const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000485TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000486{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000487 if ((*handle)->cache_chain_heads == NULL
488 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000489 return NULL;
490
Rusty Russell30fd6e52000-04-23 09:16:06 +0000491 (*handle)->cache_chain_iteration
492 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000493
Rusty Russell30fd6e52000-04-23 09:16:06 +0000494 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000495}
496
Rusty Russell30fd6e52000-04-23 09:16:06 +0000497/* Iterator functions to run through the chains. Returns NULL at end. */
498const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000499TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000500{
501 (*handle)->cache_chain_iteration++;
502
503 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
504 == (*handle)->cache_num_chains)
505 return NULL;
506
507 return (*handle)->cache_chain_iteration->name;
508}
509
510/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000511const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000512TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000513{
514 struct chain_cache *c;
515
516 c = find_label(chain, *handle);
517 if (!c) {
518 errno = ENOENT;
519 return NULL;
520 }
521
522 /* Empty chain: single return/policy rule */
523 if (c->start == c->end)
524 return NULL;
525
526 (*handle)->cache_rule_end = c->end;
527 return c->start;
528}
529
530/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000531const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000532TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000533{
534 if ((void *)prev + prev->next_offset
535 == (void *)(*handle)->cache_rule_end)
536 return NULL;
537
538 return (void *)prev + prev->next_offset;
539}
540
541#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000542/* How many rules in this chain? */
543unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000544TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000545{
546 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000547 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000548
549 CHECK(*handle);
550 if (!find_label(&off, chain, *handle)) {
551 errno = ENOENT;
552 return (unsigned int)-1;
553 }
554
555 start = get_entry(*handle, off);
556 end = get_entry(*handle, get_chain_end(*handle, off));
557
558 return entry2index(*handle, end) - entry2index(*handle, start);
559}
560
561/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000562const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
563 unsigned int n,
564 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000565{
566 unsigned int pos = 0, chainindex;
567
568 CHECK(*handle);
569 if (!find_label(&pos, chain, *handle)) {
570 errno = ENOENT;
571 return NULL;
572 }
573
574 chainindex = entry2index(*handle, get_entry(*handle, pos));
575
576 return index2entry(*handle, chainindex + n);
577}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000578#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000579
Rusty Russell30fd6e52000-04-23 09:16:06 +0000580static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000581target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000582{
583 int spos;
584 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000585 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000586
Rusty Russell30fd6e52000-04-23 09:16:06 +0000587 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000588 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000589
Rusty Russell79dee072000-05-02 16:45:16 +0000590 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
591 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000592
593 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000594 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000595 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000596 if (spos == RETURN)
597 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000598 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000599 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000600 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000601 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000602 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000603 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000604
605 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
606 entry2offset(handle, e), handle->entries.size,
607 spos);
608 abort();
609 }
610
611 jumpto = get_entry(handle, spos);
612
613 /* Fall through rule */
614 if (jumpto == (void *)e + e->next_offset)
615 return "";
616
617 /* Must point to head of a chain: ie. after error rule */
618 labelidx = entry2index(handle, jumpto) - 1;
619 return get_errorlabel(handle, index2offset(handle, labelidx));
620}
621
622/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000623const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
624 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000625{
Marc Bouchere6869a82000-03-20 06:03:29 +0000626 return target_name(*handle, e);
627}
628
629/* Is this a built-in chain? Actually returns hook + 1. */
630int
Rusty Russell79dee072000-05-02 16:45:16 +0000631TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000632{
633 unsigned int i;
634
Rusty Russell79dee072000-05-02 16:45:16 +0000635 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000636 if ((handle->info.valid_hooks & (1 << i))
637 && handle->hooknames[i]
638 && strcmp(handle->hooknames[i], chain) == 0)
639 return i+1;
640 }
641 return 0;
642}
643
644/* Get the policy of a given built-in chain */
645const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000646TC_GET_POLICY(const char *chain,
647 STRUCT_COUNTERS *counters,
648 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000649{
650 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000651 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000652 int hook;
653
Rusty Russell79dee072000-05-02 16:45:16 +0000654 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000655 if (hook != 0)
656 start = (*handle)->info.hook_entry[hook-1];
657 else
658 return NULL;
659
660 e = get_entry(*handle, get_chain_end(*handle, start));
661 *counters = e->counters;
662
663 return target_name(*handle, e);
664}
665
666static int
Rusty Russell79dee072000-05-02 16:45:16 +0000667correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000668 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000669 unsigned int offset, int delta_offset)
670{
Rusty Russell79dee072000-05-02 16:45:16 +0000671 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000672 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000673
674 /* Trap: insert of fall-through rule. Don't change fall-through
675 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000676 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000677 && t->verdict > (int)offset
678 && !(curr == offset &&
679 t->verdict == curr + e->next_offset)) {
680 t->verdict += delta_offset;
681 }
682
683 return 0;
684}
685
686/* Adjusts standard verdict jump positions after an insertion/deletion. */
687static int
Rusty Russell79dee072000-05-02 16:45:16 +0000688set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000689{
Rusty Russell725d97a2000-07-07 08:54:22 +0000690 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000691 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000692 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000693 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000694
Rusty Russell175f6412000-03-24 09:32:20 +0000695 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000696 return 1;
697}
698
699/* If prepend is set, then we are prepending to a chain: if the
700 * insertion position is an entry point, keep the entry point. */
701static int
702insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000703 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000704 unsigned int offset, unsigned int num_rules_offset,
705 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000706 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000707{
Rusty Russell79dee072000-05-02 16:45:16 +0000708 TC_HANDLE_T newh;
709 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000710 unsigned int i;
711
712 if (offset >= (*handle)->entries.size) {
713 errno = EINVAL;
714 return 0;
715 }
716
717 newinfo = (*handle)->info;
718
719 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000720 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000721 /* Entry points to START of chain, so keep same if
722 inserting on at that point. */
723 if ((*handle)->info.hook_entry[i] > offset)
724 newinfo.hook_entry[i] += rules_size;
725
726 /* Underflow always points to END of chain (policy),
727 so if something is inserted at same point, it
728 should be advanced. */
729 if ((*handle)->info.underflow[i] >= offset)
730 newinfo.underflow[i] += rules_size;
731 }
732
733 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000734 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000735 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000736 if (!newh)
737 return 0;
738 newh->info = newinfo;
739
740 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000741 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000742 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000743 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000744 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000745 memcpy((char *)newh->entries.entrytable + offset + rules_size,
746 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000747 (*handle)->entries.size - offset);
748
749 /* Move counter map. */
750 /* Copy pre... */
751 memcpy(newh->counter_map, (*handle)->counter_map,
752 sizeof(struct counter_map) * num_rules_offset);
753 /* ... copy post */
754 memcpy(newh->counter_map + num_rules_offset + num_rules,
755 (*handle)->counter_map + num_rules_offset,
756 sizeof(struct counter_map) * ((*handle)->new_number
757 - num_rules_offset));
758 /* Set intermediates to no counter copy */
759 for (i = 0; i < num_rules; i++)
760 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000761 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000762
763 newh->new_number = (*handle)->new_number + num_rules;
764 newh->entries.size = (*handle)->entries.size + rules_size;
765 newh->hooknames = (*handle)->hooknames;
766
Rusty Russell30fd6e52000-04-23 09:16:06 +0000767 if ((*handle)->cache_chain_heads)
768 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +0000769 free(*handle);
770 *handle = newh;
771
772 return set_verdict(offset, rules_size, handle);
773}
774
775static int
776delete_rules(unsigned int num_rules, unsigned int rules_size,
777 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000778 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000779{
780 unsigned int i;
781
782 if (offset + rules_size > (*handle)->entries.size) {
783 errno = EINVAL;
784 return 0;
785 }
786
787 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000788 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000789 /* In practice, we never delete up to a hook entry,
790 since the built-in chains are always first,
791 so these two are never equal */
792 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
793 (*handle)->info.hook_entry[i] -= rules_size;
794 else if ((*handle)->info.hook_entry[i] > offset) {
795 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
796 i, (*handle)->info.hook_entry[i], offset);
797 abort();
798 }
799
800 /* Underflow points to policy (terminal) rule in
801 built-in, so sequality is valid here (when deleting
802 the last rule). */
803 if ((*handle)->info.underflow[i] >= offset + rules_size)
804 (*handle)->info.underflow[i] -= rules_size;
805 else if ((*handle)->info.underflow[i] > offset) {
806 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
807 i, (*handle)->info.underflow[i], offset);
808 abort();
809 }
810 }
811
812 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000813 memmove((char *)(*handle)->entries.entrytable + offset,
814 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000815 (*handle)->entries.size - (offset + rules_size));
816
817 /* Move the counter map down. */
818 memmove(&(*handle)->counter_map[num_rules_offset],
819 &(*handle)->counter_map[num_rules_offset + num_rules],
820 sizeof(struct counter_map)
821 * ((*handle)->new_number - (num_rules + num_rules_offset)));
822
823 /* Fix numbers */
824 (*handle)->new_number -= num_rules;
825 (*handle)->entries.size -= rules_size;
826
827 return set_verdict(offset, -(int)rules_size, handle);
828}
829
830static int
Rusty Russell79dee072000-05-02 16:45:16 +0000831standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000832{
Rusty Russell79dee072000-05-02 16:45:16 +0000833 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000834
Rusty Russell79dee072000-05-02 16:45:16 +0000835 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000836
Rusty Russell67088e72000-05-10 01:18:57 +0000837 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000838 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000839 errno = EINVAL;
840 return 0;
841 }
842 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000843 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
844 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000845 t->verdict = verdict;
846
847 return 1;
848}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000849
Marc Bouchere6869a82000-03-20 06:03:29 +0000850static int
Rusty Russell79dee072000-05-02 16:45:16 +0000851map_target(const TC_HANDLE_T handle,
852 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000853 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000854 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000855{
Rusty Russell79dee072000-05-02 16:45:16 +0000856 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000857
858 /* Save old target (except data, which we don't change, except for
859 standard case, where we don't care). */
860 *old = *t;
861
862 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000863 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000864 return standard_map(e, offset + e->next_offset);
865 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000866 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000867 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000868 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000869 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000870 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000871 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000872 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
873 return standard_map(e, RETURN);
874 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000875 /* Can't jump to builtins. */
876 errno = EINVAL;
877 return 0;
878 } else {
879 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +0000880 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +0000881
Rusty Russell228e98d2000-04-27 10:28:06 +0000882 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000883 if (c)
884 return standard_map(e, entry2offset(handle, c->start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000885 }
886
887 /* Must be a module? If not, kernel will reject... */
888 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +0000889 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +0000890 0,
Rusty Russell79dee072000-05-02 16:45:16 +0000891 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +0000892 return 1;
893}
894
895static void
Rusty Russell79dee072000-05-02 16:45:16 +0000896unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000897{
Rusty Russell79dee072000-05-02 16:45:16 +0000898 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000899
900 /* Save old target (except data, which we don't change, except for
901 standard case, where we don't care). */
902 *t = *old;
903}
904
905/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
906int
Rusty Russell79dee072000-05-02 16:45:16 +0000907TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
908 const STRUCT_ENTRY *e,
909 unsigned int rulenum,
910 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000911{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000912 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000913 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000914 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000915 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000916 int ret;
917
Rusty Russell79dee072000-05-02 16:45:16 +0000918 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000919 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000920 errno = ENOENT;
921 return 0;
922 }
923
Rusty Russell30fd6e52000-04-23 09:16:06 +0000924 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000925
Rusty Russell14a1c912000-08-26 04:41:10 +0000926 tmp = index2entry(*handle, chainindex + rulenum);
927 if (!tmp || tmp > c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000928 errno = E2BIG;
929 return 0;
930 }
931 offset = index2offset(*handle, chainindex + rulenum);
932
933 /* Mapping target actually alters entry, but that's
934 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +0000935 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000936 return 0;
937
938 ret = insert_rules(1, e->next_offset, e, offset,
939 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000940 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000941 return ret;
942}
943
944/* Atomically replace rule `rulenum' in `chain' with `fw'. */
945int
Rusty Russell79dee072000-05-02 16:45:16 +0000946TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
947 const STRUCT_ENTRY *e,
948 unsigned int rulenum,
949 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000950{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000951 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +0000952 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000953 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +0000954 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +0000955 int ret;
956
Rusty Russell79dee072000-05-02 16:45:16 +0000957 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +0000958
Rusty Russell30fd6e52000-04-23 09:16:06 +0000959 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000960 errno = ENOENT;
961 return 0;
962 }
963
Rusty Russell30fd6e52000-04-23 09:16:06 +0000964 chainindex = entry2index(*handle, c->start);
Marc Bouchere6869a82000-03-20 06:03:29 +0000965
Rusty Russell14a1c912000-08-26 04:41:10 +0000966 tmp = index2entry(*handle, chainindex + rulenum);
967 if (!tmp || tmp >= c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000968 errno = E2BIG;
969 return 0;
970 }
971
972 offset = index2offset(*handle, chainindex + rulenum);
973 /* Replace = delete and insert. */
974 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
975 offset, chainindex + rulenum, handle))
976 return 0;
977
Rusty Russell79dee072000-05-02 16:45:16 +0000978 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +0000979 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +0000980
981 ret = insert_rules(1, e->next_offset, e, offset,
982 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +0000983 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +0000984 return ret;
985}
986
987/* Append entry `fw' to chain `chain'. Equivalent to insert with
988 rulenum = length of chain. */
989int
Rusty Russell79dee072000-05-02 16:45:16 +0000990TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
991 const STRUCT_ENTRY *e,
992 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000993{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000994 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +0000995 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +0000996 int ret;
997
Rusty Russell79dee072000-05-02 16:45:16 +0000998 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000999 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001000 errno = ENOENT;
1001 return 0;
1002 }
1003
Rusty Russell79dee072000-05-02 16:45:16 +00001004 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001005 entry2offset(*handle, c->end), &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001006 return 0;
1007
Rusty Russell30fd6e52000-04-23 09:16:06 +00001008 ret = insert_rules(1, e->next_offset, e,
1009 entry2offset(*handle, c->end),
1010 entry2index(*handle, c->end),
Marc Bouchere6869a82000-03-20 06:03:29 +00001011 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001012 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001013 return ret;
1014}
1015
1016static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001017match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001018 const unsigned char *a_elems,
1019 const unsigned char *b_elems,
1020 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001021{
Rusty Russell79dee072000-05-02 16:45:16 +00001022 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001023 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001024
1025 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001026 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001027
Rusty Russell228e98d2000-04-27 10:28:06 +00001028 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001029 return 1;
1030
Rusty Russell228e98d2000-04-27 10:28:06 +00001031 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001032 return 1;
1033
Rusty Russell73ef09b2000-07-03 10:24:04 +00001034 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001035
Rusty Russell73ef09b2000-07-03 10:24:04 +00001036 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001037 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001038 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001039 *maskptr += i;
1040 return 0;
1041}
1042
1043static inline int
1044target_different(const unsigned char *a_targdata,
1045 const unsigned char *b_targdata,
1046 unsigned int tdatasize,
1047 const unsigned char *mask)
1048{
1049 unsigned int i;
1050 for (i = 0; i < tdatasize; i++)
1051 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1052 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001053
1054 return 0;
1055}
1056
Rusty Russell79dee072000-05-02 16:45:16 +00001057static int
1058is_same(const STRUCT_ENTRY *a,
1059 const STRUCT_ENTRY *b,
1060 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001061
1062/* Delete the first rule in `chain' which matches `fw'. */
1063int
Rusty Russell79dee072000-05-02 16:45:16 +00001064TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1065 const STRUCT_ENTRY *origfw,
1066 unsigned char *matchmask,
1067 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001068{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001069 unsigned int offset;
1070 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001071 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001072
Rusty Russell79dee072000-05-02 16:45:16 +00001073 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001074 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001075 errno = ENOENT;
1076 return 0;
1077 }
1078
1079 fw = malloc(origfw->next_offset);
1080 if (fw == NULL) {
1081 errno = ENOMEM;
1082 return 0;
1083 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001084
Rusty Russell30fd6e52000-04-23 09:16:06 +00001085 for (offset = entry2offset(*handle, c->start);
1086 offset < entry2offset(*handle, c->end);
1087 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001088 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001089
1090 memcpy(fw, origfw, origfw->next_offset);
1091
1092 /* FIXME: handle this in is_same --RR */
1093 if (!map_target(*handle, fw, offset, &discard)) {
1094 free(fw);
1095 return 0;
1096 }
1097 e = get_entry(*handle, offset);
1098
1099#if 0
1100 printf("Deleting:\n");
1101 dump_entry(newe);
1102#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001103 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001104 int ret;
1105 ret = delete_rules(1, e->next_offset,
1106 offset, entry2index(*handle, e),
1107 handle);
1108 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001109 return ret;
1110 }
1111 }
1112
1113 free(fw);
1114 errno = ENOENT;
1115 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001116}
Marc Bouchere6869a82000-03-20 06:03:29 +00001117
1118/* Delete the rule in position `rulenum' in `chain'. */
1119int
Rusty Russell79dee072000-05-02 16:45:16 +00001120TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1121 unsigned int rulenum,
1122 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001123{
Marc Bouchere6869a82000-03-20 06:03:29 +00001124 unsigned int index;
1125 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001126 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001127 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001128
Rusty Russell79dee072000-05-02 16:45:16 +00001129 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001130 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001131 errno = ENOENT;
1132 return 0;
1133 }
1134
Rusty Russell30fd6e52000-04-23 09:16:06 +00001135 index = entry2index(*handle, c->start) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001136
Rusty Russell30fd6e52000-04-23 09:16:06 +00001137 if (index >= entry2index(*handle, c->end)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001138 errno = E2BIG;
1139 return 0;
1140 }
1141
1142 e = index2entry(*handle, index);
1143 if (e == NULL) {
1144 errno = EINVAL;
1145 return 0;
1146 }
1147
1148 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1149 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001150 return ret;
1151}
1152
1153/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1154 NULL and sets errno. */
1155const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001156TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1157 STRUCT_ENTRY *entry,
1158 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001159{
1160 errno = ENOSYS;
1161 return NULL;
1162}
1163
1164/* Flushes the entries in the given chain (ie. empties chain). */
1165int
Rusty Russell79dee072000-05-02 16:45:16 +00001166TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001167{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001168 unsigned int startindex, endindex;
1169 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001170 int ret;
1171
Rusty Russell79dee072000-05-02 16:45:16 +00001172 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001173 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001174 errno = ENOENT;
1175 return 0;
1176 }
Rusty Russell30fd6e52000-04-23 09:16:06 +00001177 startindex = entry2index(*handle, c->start);
1178 endindex = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001179
1180 ret = delete_rules(endindex - startindex,
Rusty Russell30fd6e52000-04-23 09:16:06 +00001181 (char *)c->end - (char *)c->start,
1182 entry2offset(*handle, c->start), startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001183 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001184 return ret;
1185}
1186
1187/* Zeroes the counters in a chain. */
1188int
Rusty Russell79dee072000-05-02 16:45:16 +00001189TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001190{
1191 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001192 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001193
Rusty Russell30fd6e52000-04-23 09:16:06 +00001194 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001195 errno = ENOENT;
1196 return 0;
1197 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001198
Rusty Russell30fd6e52000-04-23 09:16:06 +00001199 i = entry2index(*handle, c->start);
1200 end = entry2index(*handle, c->end);
Marc Bouchere6869a82000-03-20 06:03:29 +00001201
1202 for (; i <= end; i++) {
1203 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1204 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1205 }
Rusty Russell175f6412000-03-24 09:32:20 +00001206 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001207
Marc Bouchere6869a82000-03-20 06:03:29 +00001208 return 1;
1209}
1210
Harald Welte1cef74d2001-01-05 15:22:59 +00001211STRUCT_COUNTERS *
1212TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1213 unsigned int rulenum,
1214 TC_HANDLE_T *handle)
1215{
1216 STRUCT_ENTRY *e;
1217 struct chain_cache *c;
1218 unsigned int chainindex, end;
1219
1220 iptc_fn = TC_READ_COUNTER;
1221 CHECK(*handle);
1222
1223 if (!(c = find_label(chain, *handle))) {
1224 errno = ENOENT;
1225 return NULL;
1226 }
1227
1228 chainindex = entry2index(*handle, c->start);
1229 end = entry2index(*handle, c->end);
1230
1231 if (chainindex + rulenum > end) {
1232 errno = E2BIG;
1233 return NULL;
1234 }
1235
1236 e = index2entry(*handle, chainindex + rulenum);
1237
1238 return &e->counters;
1239}
1240
1241int
1242TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1243 unsigned int rulenum,
1244 TC_HANDLE_T *handle)
1245{
1246 STRUCT_ENTRY *e;
1247 struct chain_cache *c;
1248 unsigned int chainindex, end;
1249
1250 iptc_fn = TC_ZERO_COUNTER;
1251 CHECK(*handle);
1252
1253 if (!(c = find_label(chain, *handle))) {
1254 errno = ENOENT;
1255 return 0;
1256 }
1257
1258 chainindex = entry2index(*handle, c->start);
1259 end = entry2index(*handle, c->end);
1260
1261 if (chainindex + rulenum > end) {
1262 errno = E2BIG;
1263 return 0;
1264 }
1265
1266 e = index2entry(*handle, chainindex + rulenum);
1267
1268 if ((*handle)->counter_map[chainindex + rulenum].maptype
1269 == COUNTER_MAP_NORMAL_MAP) {
1270 (*handle)->counter_map[chainindex + rulenum].maptype
1271 = COUNTER_MAP_ZEROED;
1272 }
1273
1274 set_changed(*handle);
1275
1276 return 1;
1277}
1278
1279int
1280TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1281 unsigned int rulenum,
1282 STRUCT_COUNTERS *counters,
1283 TC_HANDLE_T *handle)
1284{
1285 STRUCT_ENTRY *e;
1286 struct chain_cache *c;
1287 unsigned int chainindex, end;
1288
1289 iptc_fn = TC_SET_COUNTER;
1290 CHECK(*handle);
1291
1292 if (!(c = find_label(chain, *handle))) {
1293 errno = ENOENT;
1294 return 0;
1295 }
1296
1297 chainindex = entry2index(*handle, c->start);
1298 end = entry2index(*handle, c->end);
1299
1300 if (chainindex + rulenum > end) {
1301 errno = E2BIG;
1302 return 0;
1303 }
1304
1305 e = index2entry(*handle, chainindex + rulenum);
1306
1307 (*handle)->counter_map[chainindex + rulenum].maptype
1308 = COUNTER_MAP_SET;
1309
1310 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1311
1312 set_changed(*handle);
1313
1314 return 1;
1315}
1316
Marc Bouchere6869a82000-03-20 06:03:29 +00001317/* Creates a new chain. */
1318/* To create a chain, create two rules: error node and unconditional
1319 * return. */
1320int
Rusty Russell79dee072000-05-02 16:45:16 +00001321TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001322{
Marc Bouchere6869a82000-03-20 06:03:29 +00001323 int ret;
1324 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001325 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001326 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001327 STRUCT_ENTRY ret;
1328 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001329 } newc;
1330
Rusty Russell79dee072000-05-02 16:45:16 +00001331 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001332
1333 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1334 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001335 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001336 || strcmp(chain, LABEL_DROP) == 0
1337 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001338 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001339 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001340 errno = EEXIST;
1341 return 0;
1342 }
1343
Rusty Russell79dee072000-05-02 16:45:16 +00001344 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001345 errno = EINVAL;
1346 return 0;
1347 }
1348
1349 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001350 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001351 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001352 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001353 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001354 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001355 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001356 strcpy(newc.name.error, chain);
1357
Rusty Russell79dee072000-05-02 16:45:16 +00001358 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001359 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001360 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001361 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001362 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001363 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001364 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001365 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001366
1367 /* Add just before terminal entry */
1368 ret = insert_rules(2, sizeof(newc), &newc.head,
1369 index2offset(*handle, (*handle)->new_number - 1),
1370 (*handle)->new_number - 1,
1371 0, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001372 return ret;
1373}
1374
1375static int
Rusty Russell79dee072000-05-02 16:45:16 +00001376count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001377{
Rusty Russell79dee072000-05-02 16:45:16 +00001378 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001379
Rusty Russell79dee072000-05-02 16:45:16 +00001380 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1381 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001382
1383 if (t->verdict == offset)
1384 (*ref)++;
1385 }
1386
1387 return 0;
1388}
1389
1390/* Get the number of references to this chain. */
1391int
Rusty Russell79dee072000-05-02 16:45:16 +00001392TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1393 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001394{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001395 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001396
Rusty Russell30fd6e52000-04-23 09:16:06 +00001397 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001398 errno = ENOENT;
1399 return 0;
1400 }
1401
1402 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001403 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001404 (*handle)->entries.size,
1405 count_ref, entry2offset(*handle, c->start), ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001406 return 1;
1407}
1408
1409/* Deletes a chain. */
1410int
Rusty Russell79dee072000-05-02 16:45:16 +00001411TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001412{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001413 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001414 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001415 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001416 int ret;
1417
Rusty Russell79dee072000-05-02 16:45:16 +00001418 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001419 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001420
Rusty Russell79dee072000-05-02 16:45:16 +00001421 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001422
Rusty Russell79dee072000-05-02 16:45:16 +00001423 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001424 errno = EINVAL;
1425 return 0;
1426 }
1427
1428 if (references > 0) {
1429 errno = EMLINK;
1430 return 0;
1431 }
1432
Rusty Russell30fd6e52000-04-23 09:16:06 +00001433 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001434 errno = ENOENT;
1435 return 0;
1436 }
1437
Rusty Russell30fd6e52000-04-23 09:16:06 +00001438 if ((void *)c->start != c->end) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001439 errno = ENOTEMPTY;
1440 return 0;
1441 }
1442
1443 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001444 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001445 labeloff = index2offset(*handle, labelidx);
1446
1447 ret = delete_rules(2,
1448 get_entry(*handle, labeloff)->next_offset
Rusty Russell30fd6e52000-04-23 09:16:06 +00001449 + c->start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001450 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001451 return ret;
1452}
1453
1454/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001455int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1456 const IPT_CHAINLABEL newname,
1457 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001458{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001459 unsigned int labeloff, labelidx;
1460 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001461 struct ipt_error_target *t;
1462
Rusty Russell79dee072000-05-02 16:45:16 +00001463 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001464
Harald Welte1de80462000-10-30 12:00:27 +00001465 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1466 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001467 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001468 || strcmp(newname, LABEL_DROP) == 0
1469 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001470 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001471 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001472 errno = EEXIST;
1473 return 0;
1474 }
1475
Rusty Russell30fd6e52000-04-23 09:16:06 +00001476 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001477 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001478 errno = ENOENT;
1479 return 0;
1480 }
1481
Rusty Russell79dee072000-05-02 16:45:16 +00001482 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001483 errno = EINVAL;
1484 return 0;
1485 }
1486
1487 /* Need label index: preceeds chain start */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001488 labelidx = entry2index(*handle, c->start) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001489 labeloff = index2offset(*handle, labelidx);
1490
1491 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001492 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001493
1494 memset(t->error, 0, sizeof(t->error));
1495 strcpy(t->error, newname);
Rusty Russell175f6412000-03-24 09:32:20 +00001496 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001497
Marc Bouchere6869a82000-03-20 06:03:29 +00001498 return 1;
1499}
1500
1501/* Sets the policy on a built-in chain. */
1502int
Rusty Russell79dee072000-05-02 16:45:16 +00001503TC_SET_POLICY(const IPT_CHAINLABEL chain,
1504 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001505 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001506 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001507{
1508 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001509 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001510 STRUCT_ENTRY *e;
1511 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001512
Rusty Russell79dee072000-05-02 16:45:16 +00001513 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001514 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001515 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001516 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001517 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001518 return 0;
1519 } else
1520 hook--;
1521
1522 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1523 if (policyoff != (*handle)->info.underflow[hook]) {
1524 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1525 chain, policyoff, (*handle)->info.underflow[hook]);
1526 return 0;
1527 }
1528
1529 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001530 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001531
Rusty Russell79dee072000-05-02 16:45:16 +00001532 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001533 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001534 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001535 t->verdict = -NF_DROP - 1;
1536 else {
1537 errno = EINVAL;
1538 return 0;
1539 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001540
1541 ctrindex = entry2index(*handle, e);
1542
1543 if (counters) {
1544 /* set byte and packet counters */
1545 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1546
1547 (*handle)->counter_map[ctrindex].maptype
1548 = COUNTER_MAP_SET;
1549
1550 } else {
1551 (*handle)->counter_map[ctrindex]
1552 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1553 }
1554
Rusty Russell175f6412000-03-24 09:32:20 +00001555 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001556
Marc Bouchere6869a82000-03-20 06:03:29 +00001557 return 1;
1558}
1559
1560/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001561 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001562 libiptc.c:833: fixed or forbidden register was spilled.
1563 This may be due to a compiler bug or to impossible asm
1564 statements or clauses.
1565*/
1566static void
Rusty Russell79dee072000-05-02 16:45:16 +00001567subtract_counters(STRUCT_COUNTERS *answer,
1568 const STRUCT_COUNTERS *a,
1569 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001570{
1571 answer->pcnt = a->pcnt - b->pcnt;
1572 answer->bcnt = a->bcnt - b->bcnt;
1573}
1574
1575int
Rusty Russell79dee072000-05-02 16:45:16 +00001576TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001577{
1578 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001579 STRUCT_REPLACE *repl;
1580 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001581 unsigned int i;
1582 size_t counterlen
Rusty Russell79dee072000-05-02 16:45:16 +00001583 = sizeof(STRUCT_COUNTERS_INFO)
1584 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
Marc Bouchere6869a82000-03-20 06:03:29 +00001585
1586 CHECK(*handle);
1587#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001588 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001589#endif
1590
1591 /* Don't commit if nothing changed. */
1592 if (!(*handle)->changed)
1593 goto finished;
1594
1595 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1596 if (!repl) {
1597 errno = ENOMEM;
1598 return 0;
1599 }
1600
1601 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001602 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001603 * (*handle)->info.num_entries);
1604 if (!repl->counters) {
1605 free(repl);
1606 errno = ENOMEM;
1607 return 0;
1608 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001609
Marc Bouchere6869a82000-03-20 06:03:29 +00001610 /* These are the counters we're going to put back, later. */
1611 newcounters = malloc(counterlen);
1612 if (!newcounters) {
1613 free(repl->counters);
1614 free(repl);
1615 errno = ENOMEM;
1616 return 0;
1617 }
1618
1619 strcpy(repl->name, (*handle)->info.name);
1620 repl->num_entries = (*handle)->new_number;
1621 repl->size = (*handle)->entries.size;
1622 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1623 sizeof(repl->hook_entry));
1624 memcpy(repl->underflow, (*handle)->info.underflow,
1625 sizeof(repl->underflow));
1626 repl->num_counters = (*handle)->info.num_entries;
1627 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001628 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001629 (*handle)->entries.size);
1630
Rusty Russell79dee072000-05-02 16:45:16 +00001631 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001632 sizeof(*repl) + (*handle)->entries.size) < 0) {
1633 free(repl->counters);
1634 free(repl);
1635 free(newcounters);
1636 return 0;
1637 }
1638
1639 /* Put counters back. */
1640 strcpy(newcounters->name, (*handle)->info.name);
1641 newcounters->num_counters = (*handle)->new_number;
1642 for (i = 0; i < (*handle)->new_number; i++) {
1643 unsigned int mappos = (*handle)->counter_map[i].mappos;
1644 switch ((*handle)->counter_map[i].maptype) {
1645 case COUNTER_MAP_NOMAP:
1646 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001647 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001648 break;
1649
1650 case COUNTER_MAP_NORMAL_MAP:
1651 /* Original read: X.
1652 * Atomic read on replacement: X + Y.
1653 * Currently in kernel: Z.
1654 * Want in kernel: X + Y + Z.
1655 * => Add in X + Y
1656 * => Add in replacement read.
1657 */
1658 newcounters->counters[i] = repl->counters[mappos];
1659 break;
1660
1661 case COUNTER_MAP_ZEROED:
1662 /* Original read: X.
1663 * Atomic read on replacement: X + Y.
1664 * Currently in kernel: Z.
1665 * Want in kernel: Y + Z.
1666 * => Add in Y.
1667 * => Add in (replacement read - original read).
1668 */
1669 subtract_counters(&newcounters->counters[i],
1670 &repl->counters[mappos],
1671 &index2entry(*handle, i)->counters);
1672 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001673
1674 case COUNTER_MAP_SET:
1675 /* Want to set counter (iptables-restore) */
1676
1677 memcpy(&newcounters->counters[i],
1678 &index2entry(*handle, i)->counters,
1679 sizeof(STRUCT_COUNTERS));
1680
1681 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001682 }
1683 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001684
1685#ifdef KERNEL_64_USERSPACE_32
1686 {
1687 /* Kernel will think that pointer should be 64-bits, and get
1688 padding. So we accomodate here (assumption: alignment of
1689 `counters' is on 64-bit boundary). */
1690 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1691 if ((unsigned long)&newcounters->counters % 8 != 0) {
1692 fprintf(stderr,
1693 "counters alignment incorrect! Mail rusty!\n");
1694 abort();
1695 }
1696 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001697 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001698#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001699
Rusty Russell79dee072000-05-02 16:45:16 +00001700 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1701 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001702 free(repl->counters);
1703 free(repl);
1704 free(newcounters);
1705 return 0;
1706 }
1707
1708 free(repl->counters);
1709 free(repl);
1710 free(newcounters);
1711
1712 finished:
Rusty Russell30fd6e52000-04-23 09:16:06 +00001713 if ((*handle)->cache_chain_heads)
1714 free((*handle)->cache_chain_heads);
Marc Bouchere6869a82000-03-20 06:03:29 +00001715 free(*handle);
1716 *handle = NULL;
1717 return 1;
1718}
1719
1720/* Get raw socket. */
1721int
Rusty Russell79dee072000-05-02 16:45:16 +00001722TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001723{
1724 return sockfd;
1725}
1726
1727/* Translates errno numbers into more human-readable form than strerror. */
1728const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001729TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001730{
1731 unsigned int i;
1732 struct table_struct {
1733 void *fn;
1734 int err;
1735 const char *message;
1736 } table [] =
1737 { { NULL, 0, "Incompatible with this kernel" },
1738 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1739 { NULL, ENOSYS, "Will be implemented real soon. I promise." },
1740 { NULL, ENOMEM, "Memory allocation problem" },
Rusty Russell79dee072000-05-02 16:45:16 +00001741 { TC_INIT, EPERM, "Permission denied (you must be root)" },
1742 { TC_INIT, EINVAL, "Module is wrong version" },
Rusty Russelld57390e2000-08-23 07:54:20 +00001743 { TC_INIT, ENOENT, "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001744 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1745 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1746 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001747 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001748 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1749 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1750 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1751 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001752 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1753 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001754 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1755 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001756 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001757 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001758 "Bad arguments (does that interface exist?)" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001759 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001760 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001761 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001762 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001763 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001764 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001765 "Bad policy name" },
Rusty Russellf68ce292000-06-05 17:54:47 +00001766 { NULL, ENOENT, "No chain/target/match by that name" }
Marc Bouchere6869a82000-03-20 06:03:29 +00001767 };
1768
1769 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1770 if ((!table[i].fn || table[i].fn == iptc_fn)
1771 && table[i].err == err)
1772 return table[i].message;
1773 }
1774
1775 return strerror(err);
1776}