blob: 982158661daea7ad7fab8889c64f8edacf388150 [file] [log] [blame]
Stephane Ouellette7cd00282004-05-14 08:21:06 +00001/* Library which manipulates firewall rules. Version $Revision: 1.44 $ */
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
Harald Welte3ea8f402003-06-23 18:25:59 +000011/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12 * COPYING for details).
13 * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
14 *
Harald Weltefbc85232003-06-24 17:37:21 +000015 * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
Harald Welte3ea8f402003-06-23 18:25:59 +000016 * - Reimplementation of chain cache to use offsets instead of entries
Harald Weltefbc85232003-06-24 17:37:21 +000017 * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
Harald Welte0113fe72004-01-06 19:04:02 +000018 * - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
Harald Weltefbc85232003-06-24 17:37:21 +000019 * don't rebuild the chain cache after every operation, instead fix it
20 * up after a ruleset change.
Harald Welte3ea8f402003-06-23 18:25:59 +000021 */
Marc Bouchere6869a82000-03-20 06:03:29 +000022
Stephane Ouellette7cd00282004-05-14 08:21:06 +000023#include<sys/types.h>
24#include<sys/socket.h>
25
Marc Bouchere6869a82000-03-20 06:03:29 +000026#ifndef IPT_LIB_DIR
27#define IPT_LIB_DIR "/usr/local/lib/iptables"
28#endif
29
Harald Welte0113fe72004-01-06 19:04:02 +000030#ifndef __OPTIMIZE__
31STRUCT_ENTRY_TARGET *
32GET_TARGET(STRUCT_ENTRY *e)
33{
34 return (void *)e + e->target_offset;
35}
36#endif
37
Marc Bouchere6869a82000-03-20 06:03:29 +000038static int sockfd = -1;
39static void *iptc_fn = NULL;
40
41static const char *hooknames[]
Rusty Russell79dee072000-05-02 16:45:16 +000042= { [HOOK_PRE_ROUTING] "PREROUTING",
43 [HOOK_LOCAL_IN] "INPUT",
44 [HOOK_FORWARD] "FORWARD",
45 [HOOK_LOCAL_OUT] "OUTPUT",
Rusty Russell10758b72000-09-14 07:37:33 +000046 [HOOK_POST_ROUTING] "POSTROUTING",
47#ifdef HOOK_DROPPING
48 [HOOK_DROPPING] "DROPPING"
49#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000050};
51
52struct counter_map
53{
54 enum {
55 COUNTER_MAP_NOMAP,
56 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000057 COUNTER_MAP_ZEROED,
58 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000059 } maptype;
60 unsigned int mappos;
61};
62
63/* Convenience structures */
64struct ipt_error_target
65{
Rusty Russell79dee072000-05-02 16:45:16 +000066 STRUCT_ENTRY_TARGET t;
67 char error[TABLE_MAXNAMELEN];
Marc Bouchere6869a82000-03-20 06:03:29 +000068};
69
Harald Welte0113fe72004-01-06 19:04:02 +000070struct chain_cache
Rusty Russell30fd6e52000-04-23 09:16:06 +000071{
Rusty Russell79dee072000-05-02 16:45:16 +000072 char name[TABLE_MAXNAMELEN];
Harald Welte0113fe72004-01-06 19:04:02 +000073 /* This is the first rule in chain. */
74 unsigned int start_off;
75 /* Last rule in chain */
76 unsigned int end_off;
Rusty Russell30fd6e52000-04-23 09:16:06 +000077};
78
Rusty Russell79dee072000-05-02 16:45:16 +000079STRUCT_TC_HANDLE
Marc Bouchere6869a82000-03-20 06:03:29 +000080{
81 /* Have changes been made? */
82 int changed;
Harald Welte0113fe72004-01-06 19:04:02 +000083 /* Size in here reflects original state. */
Rusty Russell79dee072000-05-02 16:45:16 +000084 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +000085
Harald Welte0113fe72004-01-06 19:04:02 +000086 struct counter_map *counter_map;
Marc Bouchere6869a82000-03-20 06:03:29 +000087 /* Array of hook names */
88 const char **hooknames;
89
Rusty Russell30fd6e52000-04-23 09:16:06 +000090 /* Cached position of chain heads (NULL = no cache). */
91 unsigned int cache_num_chains;
92 unsigned int cache_num_builtins;
93 struct chain_cache *cache_chain_heads;
94
95 /* Chain iterator: current chain cache entry. */
96 struct chain_cache *cache_chain_iteration;
97
98 /* Rule iterator: terminal rule */
Rusty Russell79dee072000-05-02 16:45:16 +000099 STRUCT_ENTRY *cache_rule_end;
Rusty Russell175f6412000-03-24 09:32:20 +0000100
Marc Bouchere6869a82000-03-20 06:03:29 +0000101 /* Number in here reflects current state. */
102 unsigned int new_number;
Rusty Russell79dee072000-05-02 16:45:16 +0000103 STRUCT_GET_ENTRIES entries;
Marc Bouchere6869a82000-03-20 06:03:29 +0000104};
105
Rusty Russell175f6412000-03-24 09:32:20 +0000106static void
Rusty Russell79dee072000-05-02 16:45:16 +0000107set_changed(TC_HANDLE_T h)
Rusty Russell175f6412000-03-24 09:32:20 +0000108{
Rusty Russell175f6412000-03-24 09:32:20 +0000109 h->changed = 1;
110}
111
Harald Welte380ba5f2002-02-13 16:19:55 +0000112#ifdef IPTC_DEBUG
Rusty Russell79dee072000-05-02 16:45:16 +0000113static void do_check(TC_HANDLE_T h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000114#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000115#else
116#define CHECK(h)
117#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000118
119static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000120get_number(const STRUCT_ENTRY *i,
121 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000122 unsigned int *pos)
123{
124 if (i == seek)
125 return 1;
126 (*pos)++;
127 return 0;
128}
129
130static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000131entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000132{
133 unsigned int pos = 0;
134
Rusty Russell725d97a2000-07-07 08:54:22 +0000135 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000136 get_number, seek, &pos) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000137 fprintf(stderr, "ERROR: offset %i not an entry!\n",
Rusty Russell725d97a2000-07-07 08:54:22 +0000138 (char *)seek - (char *)h->entries.entrytable);
Marc Bouchere6869a82000-03-20 06:03:29 +0000139 abort();
140 }
141 return pos;
142}
143
144static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000145get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000146 unsigned int number,
147 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000148 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000149{
150 if (*pos == number) {
151 *pe = i;
152 return 1;
153 }
154 (*pos)++;
155 return 0;
156}
157
Rusty Russell79dee072000-05-02 16:45:16 +0000158static STRUCT_ENTRY *
159index2entry(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000160{
161 unsigned int pos = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000162 STRUCT_ENTRY *ret = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000163
Rusty Russell725d97a2000-07-07 08:54:22 +0000164 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000165 get_entry_n, index, &pos, &ret);
Marc Bouchere6869a82000-03-20 06:03:29 +0000166
167 return ret;
168}
169
Harald Welte0113fe72004-01-06 19:04:02 +0000170static inline STRUCT_ENTRY *
171get_entry(TC_HANDLE_T h, unsigned int offset)
172{
173 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
174}
175
176static inline unsigned long
177entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
178{
179 return (void *)e - (void *)h->entries.entrytable;
180}
181
Harald Welte3ea8f402003-06-23 18:25:59 +0000182static inline unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000183index2offset(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000184{
185 return entry2offset(h, index2entry(h, index));
186}
187
Harald Welte3ea8f402003-06-23 18:25:59 +0000188static inline STRUCT_ENTRY *
189offset2entry(TC_HANDLE_T h, unsigned int offset)
190{
191 return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
192}
193
194static inline unsigned int
195offset2index(const TC_HANDLE_T h, unsigned int offset)
196{
197 return entry2index(h, offset2entry(h, offset));
198}
199
200
Harald Welte0113fe72004-01-06 19:04:02 +0000201static const char *
202get_errorlabel(TC_HANDLE_T h, unsigned int offset)
203{
204 STRUCT_ENTRY *e;
205
206 e = get_entry(h, offset);
207 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
208 fprintf(stderr, "ERROR: offset %u not an error node!\n",
209 offset);
210 abort();
211 }
212
213 return (const char *)GET_TARGET(e)->data;
214}
Marc Bouchere6869a82000-03-20 06:03:29 +0000215
216/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000217static TC_HANDLE_T
Harald Welte0113fe72004-01-06 19:04:02 +0000218alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +0000219{
220 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000221 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000222
Rusty Russell79dee072000-05-02 16:45:16 +0000223 len = sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000224 + size
225 + num_rules * sizeof(struct counter_map);
226
227 if ((h = malloc(len)) == NULL) {
228 errno = ENOMEM;
229 return NULL;
230 }
231
232 h->changed = 0;
Harald Welte0113fe72004-01-06 19:04:02 +0000233 h->cache_num_chains = 0;
234 h->cache_chain_heads = NULL;
235 h->counter_map = (void *)h
236 + sizeof(STRUCT_TC_HANDLE)
237 + size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000238 strcpy(h->info.name, tablename);
239 strcpy(h->entries.name, tablename);
240
241 return h;
242}
243
Rusty Russell79dee072000-05-02 16:45:16 +0000244TC_HANDLE_T
245TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000246{
Rusty Russell79dee072000-05-02 16:45:16 +0000247 TC_HANDLE_T h;
248 STRUCT_GETINFO info;
Harald Welte0113fe72004-01-06 19:04:02 +0000249 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +0000250 int tmp;
251 socklen_t s;
252
Rusty Russell79dee072000-05-02 16:45:16 +0000253 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000254
Martin Josefssone560fd62003-06-13 16:56:51 +0000255 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000256 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000257 sockfd = -1;
258 }
Harald Welte366454b2002-01-07 13:46:50 +0000259
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000260 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
261 errno = EINVAL;
262 return NULL;
263 }
264
Rusty Russell79dee072000-05-02 16:45:16 +0000265 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000266 if (sockfd < 0)
267 return NULL;
268
269 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000270
Marc Bouchere6869a82000-03-20 06:03:29 +0000271 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000272 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000273 return NULL;
274
Harald Welte0113fe72004-01-06 19:04:02 +0000275 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000276 == NULL) {
277 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000278 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000279 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000280 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000281
282/* Too hard --RR */
283#if 0
284 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
285 dynlib = dlopen(pathname, RTLD_NOW);
286 if (!dynlib) {
287 errno = ENOENT;
288 return NULL;
289 }
290 h->hooknames = dlsym(dynlib, "hooknames");
291 if (!h->hooknames) {
292 errno = ENOENT;
293 return NULL;
294 }
295#else
296 h->hooknames = hooknames;
297#endif
298
299 /* Initialize current state */
300 h->info = info;
Harald Welte0113fe72004-01-06 19:04:02 +0000301 h->new_number = h->info.num_entries;
302 for (i = 0; i < h->info.num_entries; i++)
303 h->counter_map[i]
304 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
305
Marc Bouchere6869a82000-03-20 06:03:29 +0000306 h->entries.size = h->info.size;
307
Rusty Russell79dee072000-05-02 16:45:16 +0000308 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000309
Rusty Russell79dee072000-05-02 16:45:16 +0000310 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000311 &tmp) < 0) {
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000312 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000313 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000314 free(h);
315 return NULL;
316 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000317
Marc Bouchere6869a82000-03-20 06:03:29 +0000318 CHECK(h);
319 return h;
320}
321
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000322void
323TC_FREE(TC_HANDLE_T *h)
324{
325 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000326 sockfd = -1;
Harald Welte0113fe72004-01-06 19:04:02 +0000327 if ((*h)->cache_chain_heads)
328 free((*h)->cache_chain_heads);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000329 free(*h);
330 *h = NULL;
331}
332
Marc Bouchere6869a82000-03-20 06:03:29 +0000333static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000334print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000335{
Rusty Russell228e98d2000-04-27 10:28:06 +0000336 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000337 return 0;
338}
339
Rusty Russell79dee072000-05-02 16:45:16 +0000340static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
341
Marc Bouchere6869a82000-03-20 06:03:29 +0000342void
Rusty Russell79dee072000-05-02 16:45:16 +0000343TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000344{
345 CHECK(handle);
346
347 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000348 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000349 handle->new_number, handle->entries.size);
350 printf("Table `%s'\n", handle->info.name);
351 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000352 handle->info.hook_entry[HOOK_PRE_ROUTING],
353 handle->info.hook_entry[HOOK_LOCAL_IN],
354 handle->info.hook_entry[HOOK_FORWARD],
355 handle->info.hook_entry[HOOK_LOCAL_OUT],
356 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000357 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000358 handle->info.underflow[HOOK_PRE_ROUTING],
359 handle->info.underflow[HOOK_LOCAL_IN],
360 handle->info.underflow[HOOK_FORWARD],
361 handle->info.underflow[HOOK_LOCAL_OUT],
362 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000363
Rusty Russell725d97a2000-07-07 08:54:22 +0000364 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000365 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000366}
367
Rusty Russell30fd6e52000-04-23 09:16:06 +0000368/* Returns 0 if not hook entry, else hooknumber + 1 */
369static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000370is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000371{
372 unsigned int i;
373
Rusty Russell79dee072000-05-02 16:45:16 +0000374 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000375 if ((h->info.valid_hooks & (1 << i))
376 && get_entry(h, h->info.hook_entry[i]) == e)
377 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000378 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000379 return 0;
380}
381
Harald Welte0113fe72004-01-06 19:04:02 +0000382static inline int
383add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
384{
385 unsigned int builtin;
386
387 /* Last entry. End it. */
388 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
389 /* This is the ERROR node at end of the table */
390 h->cache_chain_heads[h->cache_num_chains-1].end_off =
391 entry2offset(h, *prev);
392 return 0;
393 }
394
395 /* We know this is the start of a new chain if it's an ERROR
396 target, or a hook entry point */
397 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
398 /* prev was last entry in previous chain */
399 h->cache_chain_heads[h->cache_num_chains-1].end_off
400 = entry2offset(h, *prev);
401
402 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
403 (const char *)GET_TARGET(e)->data);
404 h->cache_chain_heads[h->cache_num_chains].start_off
405 = entry2offset(h, (void *)e + e->next_offset);
406 h->cache_num_chains++;
407 } else if ((builtin = is_hook_entry(e, h)) != 0) {
408 if (h->cache_num_chains > 0)
409 /* prev was last entry in previous chain */
410 h->cache_chain_heads[h->cache_num_chains-1].end_off
411 = entry2offset(h, *prev);
412
413 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
414 h->hooknames[builtin-1]);
415 h->cache_chain_heads[h->cache_num_chains].start_off
416 = entry2offset(h, (void *)e);
417 h->cache_num_chains++;
418 }
419
420 *prev = e;
421 return 0;
422}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000423
Rusty Russell30fd6e52000-04-23 09:16:06 +0000424static int alphasort(const void *a, const void *b)
425{
426 return strcmp(((struct chain_cache *)a)->name,
427 ((struct chain_cache *)b)->name);
428}
Harald Welte0113fe72004-01-06 19:04:02 +0000429
430static int populate_cache(TC_HANDLE_T h)
431{
432 unsigned int i;
433 STRUCT_ENTRY *prev;
434
435 /* # chains < # rules / 2 + num builtins - 1 */
436 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
437 * sizeof(struct chain_cache));
438 if (!h->cache_chain_heads) {
439 errno = ENOMEM;
440 return 0;
441 }
442
443 h->cache_num_chains = 0;
444 h->cache_num_builtins = 0;
445
446 /* Count builtins */
447 for (i = 0; i < NUMHOOKS; i++) {
448 if (h->info.valid_hooks & (1 << i))
449 h->cache_num_builtins++;
450 }
451
452 prev = NULL;
453 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
454 add_chain, h, &prev);
455
456 qsort(h->cache_chain_heads + h->cache_num_builtins,
457 h->cache_num_chains - h->cache_num_builtins,
458 sizeof(struct chain_cache), alphasort);
459
460 return 1;
461}
462
463static int
464correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
465{
466 int i; /* needs to be signed because deleting first
467 chain can make it drop to -1 */
468
469 if (!delta)
470 return 1;
471
472 for (i = 0; i < h->cache_num_chains; i++) {
473 struct chain_cache *cc = &h->cache_chain_heads[i];
474
475 if (delta < 0) {
476 /* take care about deleted chains */
477 if (cc->start_off > offset+delta
478 && cc->end_off < offset) {
479 /* this chain is within the deleted range,
480 * let's remove it from the cache */
481 void *start;
482 unsigned int size;
483
484 h->cache_num_chains--;
485
486 /* no need for memmove since we are
487 * removing the last entry */
488 if (i >= h->cache_num_chains)
489 continue;
490
491 start = &h->cache_chain_heads[i+1];
492 size = (h->cache_num_chains-i)
493 * sizeof(struct chain_cache);
494 memmove(cc, start, size);
495
496 /* iterate over same index again, since
497 * it is now a different chain */
498 i--;
499 continue;
500 }
501 }
502
503 if (cc->start_off > offset)
504 cc->start_off += delta;
505
506 if (cc->end_off >= offset)
507 cc->end_off += delta;
508 }
509 /* HW_FIXME: sorting might be needed, but just in case a new chain was
510 * added */
511
512 return 1;
513}
514
515static int
516add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
517 unsigned int end_off)
518{
519 struct chain_cache *ccs = realloc(h->cache_chain_heads,
520 (h->new_number / 2 + 4 + 1)
521 * sizeof(struct chain_cache));
522 struct chain_cache *newcc;
523
524 if (!ccs)
525 return 0;
526
527 h->cache_chain_heads = ccs;
528 newcc = &h->cache_chain_heads[h->cache_num_chains];
529 h->cache_num_chains++;
530
531 strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
Karsten Desler073df8f2004-01-31 15:33:55 +0000532 newcc->name[TABLE_MAXNAMELEN-1] = '\0';
Harald Welte0113fe72004-01-06 19:04:02 +0000533 newcc->start_off = start_off;
534 newcc->end_off = end_off;
535
536 return 1;
537}
538
539/* Returns cache ptr if found, otherwise NULL. */
540static struct chain_cache *
541find_label(const char *name, TC_HANDLE_T handle)
542{
543 unsigned int i;
544
545 if (handle->cache_chain_heads == NULL
546 && !populate_cache(handle))
547 return NULL;
548
549 /* FIXME: Linear search through builtins, then binary --RR */
550 for (i = 0; i < handle->cache_num_chains; i++) {
551 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
552 return &handle->cache_chain_heads[i];
553 }
554
555 return NULL;
556}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000557
Marc Bouchere6869a82000-03-20 06:03:29 +0000558/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000559int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000560{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000561 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000562}
563
564/* Returns the position of the final (ie. unconditional) element. */
565static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000566get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000567{
568 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000569 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000570
571 last_off = start;
572 e = get_entry(handle, start);
573
574 /* Terminate when we meet a error label or a hook entry. */
575 for (off = start + e->next_offset;
576 off < handle->entries.size;
577 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000578 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000579 unsigned int i;
580
581 e = get_entry(handle, off);
582
583 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000584 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000585 if ((handle->info.valid_hooks & (1 << i))
586 && off == handle->info.hook_entry[i])
587 return last_off;
588 }
589
590 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000591 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000592 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000593 return last_off;
594 }
595 /* SHOULD NEVER HAPPEN */
596 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
597 handle->entries.size, off);
598 abort();
599}
600
Rusty Russell30fd6e52000-04-23 09:16:06 +0000601/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000602const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000603TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000604{
Harald Welte0113fe72004-01-06 19:04:02 +0000605 if ((*handle)->cache_chain_heads == NULL
606 && !populate_cache(*handle))
607 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000608
Harald Welte0113fe72004-01-06 19:04:02 +0000609 (*handle)->cache_chain_iteration
610 = &(*handle)->cache_chain_heads[0];
611
612 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000613}
614
Rusty Russell30fd6e52000-04-23 09:16:06 +0000615/* Iterator functions to run through the chains. Returns NULL at end. */
616const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000617TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000618{
Harald Welte0113fe72004-01-06 19:04:02 +0000619 (*handle)->cache_chain_iteration++;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000620
Harald Welte0113fe72004-01-06 19:04:02 +0000621 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
622 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000623 return NULL;
624
Harald Welte0113fe72004-01-06 19:04:02 +0000625 return (*handle)->cache_chain_iteration->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000626}
627
628/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000629const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000630TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000631{
Harald Welte0113fe72004-01-06 19:04:02 +0000632 struct chain_cache *c;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000633
634 c = find_label(chain, *handle);
635 if (!c) {
636 errno = ENOENT;
637 return NULL;
638 }
639
640 /* Empty chain: single return/policy rule */
Harald Welte0113fe72004-01-06 19:04:02 +0000641 if (c->start_off == c->end_off)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000642 return NULL;
643
Harald Welte0113fe72004-01-06 19:04:02 +0000644 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
645 return offset2entry(*handle, c->start_off);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000646}
647
648/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000649const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000650TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000651{
Harald Welte0113fe72004-01-06 19:04:02 +0000652 if ((void *)prev + prev->next_offset
653 == (void *)(*handle)->cache_rule_end)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000654 return NULL;
655
Harald Welte0113fe72004-01-06 19:04:02 +0000656 return (void *)prev + prev->next_offset;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000657}
658
659#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000660/* How many rules in this chain? */
661unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000662TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000663{
664 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000665 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000666
667 CHECK(*handle);
668 if (!find_label(&off, chain, *handle)) {
669 errno = ENOENT;
670 return (unsigned int)-1;
671 }
672
673 start = get_entry(*handle, off);
674 end = get_entry(*handle, get_chain_end(*handle, off));
675
676 return entry2index(*handle, end) - entry2index(*handle, start);
677}
678
679/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000680const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
681 unsigned int n,
682 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000683{
684 unsigned int pos = 0, chainindex;
685
686 CHECK(*handle);
687 if (!find_label(&pos, chain, *handle)) {
688 errno = ENOENT;
689 return NULL;
690 }
691
692 chainindex = entry2index(*handle, get_entry(*handle, pos));
693
694 return index2entry(*handle, chainindex + n);
695}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000696#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000697
Rusty Russell30fd6e52000-04-23 09:16:06 +0000698static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000699target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000700{
701 int spos;
Harald Welte0113fe72004-01-06 19:04:02 +0000702 unsigned int labelidx;
703 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000704
Rusty Russell30fd6e52000-04-23 09:16:06 +0000705 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000706 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000707
Rusty Russell79dee072000-05-02 16:45:16 +0000708 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
709 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000710
711 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000712 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000713 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000714 if (spos == RETURN)
715 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000716 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000717 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000718 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000719 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000720 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000721 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000722
Harald Welte0113fe72004-01-06 19:04:02 +0000723 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
724 entry2offset(handle, e), handle->entries.size,
725 spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000726 abort();
727 }
728
Harald Welte0113fe72004-01-06 19:04:02 +0000729 jumpto = get_entry(handle, spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000730
731 /* Fall through rule */
732 if (jumpto == (void *)e + e->next_offset)
733 return "";
734
735 /* Must point to head of a chain: ie. after error rule */
736 labelidx = entry2index(handle, jumpto) - 1;
737 return get_errorlabel(handle, index2offset(handle, labelidx));
738}
739
740/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000741const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
742 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000743{
Marc Bouchere6869a82000-03-20 06:03:29 +0000744 return target_name(*handle, e);
745}
746
747/* Is this a built-in chain? Actually returns hook + 1. */
748int
Rusty Russell79dee072000-05-02 16:45:16 +0000749TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000750{
751 unsigned int i;
752
Rusty Russell79dee072000-05-02 16:45:16 +0000753 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000754 if ((handle->info.valid_hooks & (1 << i))
755 && handle->hooknames[i]
756 && strcmp(handle->hooknames[i], chain) == 0)
757 return i+1;
758 }
759 return 0;
760}
761
762/* Get the policy of a given built-in chain */
763const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000764TC_GET_POLICY(const char *chain,
765 STRUCT_COUNTERS *counters,
766 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000767{
Harald Welte0113fe72004-01-06 19:04:02 +0000768 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000769 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000770 int hook;
771
Rusty Russell79dee072000-05-02 16:45:16 +0000772 hook = TC_BUILTIN(chain, *handle);
Harald Welte0113fe72004-01-06 19:04:02 +0000773 if (hook != 0)
774 start = (*handle)->info.hook_entry[hook-1];
775 else
Marc Bouchere6869a82000-03-20 06:03:29 +0000776 return NULL;
777
Harald Welte0113fe72004-01-06 19:04:02 +0000778 e = get_entry(*handle, get_chain_end(*handle, start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000779 *counters = e->counters;
780
781 return target_name(*handle, e);
782}
783
Harald Welte0113fe72004-01-06 19:04:02 +0000784static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000785correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000786 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000787 unsigned int offset, int delta_offset)
788{
Rusty Russell79dee072000-05-02 16:45:16 +0000789 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000790 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000791
792 /* Trap: insert of fall-through rule. Don't change fall-through
793 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000794 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000795 && t->verdict > (int)offset
796 && !(curr == offset &&
797 t->verdict == curr + e->next_offset)) {
798 t->verdict += delta_offset;
799 }
800
801 return 0;
802}
803
804/* Adjusts standard verdict jump positions after an insertion/deletion. */
805static int
Rusty Russell79dee072000-05-02 16:45:16 +0000806set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000807{
Rusty Russell725d97a2000-07-07 08:54:22 +0000808 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000809 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000810 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000811 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000812
Rusty Russell175f6412000-03-24 09:32:20 +0000813 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000814 return 1;
815}
816
Harald Welte0113fe72004-01-06 19:04:02 +0000817/* If prepend is set, then we are prepending to a chain: if the
818 * insertion position is an entry point, keep the entry point. */
819static int
820insert_rules(unsigned int num_rules, unsigned int rules_size,
821 const STRUCT_ENTRY *insert,
822 unsigned int offset, unsigned int num_rules_offset,
823 int prepend,
824 TC_HANDLE_T *handle)
825{
826 TC_HANDLE_T newh;
827 STRUCT_GETINFO newinfo;
828 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +0000829
Harald Welte0113fe72004-01-06 19:04:02 +0000830 if (offset >= (*handle)->entries.size) {
831 errno = EINVAL;
832 return 0;
833 }
834
835 newinfo = (*handle)->info;
836
837 /* Fix up entry points. */
838 for (i = 0; i < NUMHOOKS; i++) {
839 /* Entry points to START of chain, so keep same if
840 inserting on at that point. */
841 if ((*handle)->info.hook_entry[i] > offset)
842 newinfo.hook_entry[i] += rules_size;
843
844 /* Underflow always points to END of chain (policy),
845 so if something is inserted at same point, it
846 should be advanced. */
847 if ((*handle)->info.underflow[i] >= offset)
848 newinfo.underflow[i] += rules_size;
849 }
850
851 newh = alloc_handle((*handle)->info.name,
852 (*handle)->entries.size + rules_size,
853 (*handle)->new_number + num_rules);
854 if (!newh)
855 return 0;
856 newh->info = newinfo;
857
858 /* Copy pre... */
859 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
860 /* ... Insert new ... */
861 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
862 /* ... copy post */
863 memcpy((char *)newh->entries.entrytable + offset + rules_size,
864 (char *)(*handle)->entries.entrytable + offset,
865 (*handle)->entries.size - offset);
866
867 /* Move counter map. */
868 /* Copy pre... */
869 memcpy(newh->counter_map, (*handle)->counter_map,
870 sizeof(struct counter_map) * num_rules_offset);
871 /* ... copy post */
872 memcpy(newh->counter_map + num_rules_offset + num_rules,
873 (*handle)->counter_map + num_rules_offset,
874 sizeof(struct counter_map) * ((*handle)->new_number
875 - num_rules_offset));
876 /* Set intermediates to no counter copy */
877 for (i = 0; i < num_rules; i++)
878 newh->counter_map[num_rules_offset+i]
879 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
880
881 newh->new_number = (*handle)->new_number + num_rules;
882 newh->entries.size = (*handle)->entries.size + rules_size;
883 newh->hooknames = (*handle)->hooknames;
884
885 newh->cache_chain_heads = (*handle)->cache_chain_heads;
886 newh->cache_num_builtins = (*handle)->cache_num_builtins;
887 newh->cache_num_chains = (*handle)->cache_num_chains;
888 newh->cache_rule_end = (*handle)->cache_rule_end;
889 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
890 if (!correct_cache(newh, offset, rules_size)) {
891 free(newh);
892 return 0;
893 }
894
895 free(*handle);
896 *handle = newh;
897
898 return set_verdict(offset, rules_size, handle);
899}
900
901static int
902delete_rules(unsigned int num_rules, unsigned int rules_size,
903 unsigned int offset, unsigned int num_rules_offset,
904 TC_HANDLE_T *handle)
905{
906 unsigned int i;
907
908 if (offset + rules_size > (*handle)->entries.size) {
909 errno = EINVAL;
910 return 0;
911 }
912
913 /* Fix up entry points. */
914 for (i = 0; i < NUMHOOKS; i++) {
915 /* In practice, we never delete up to a hook entry,
916 since the built-in chains are always first,
917 so these two are never equal */
918 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
919 (*handle)->info.hook_entry[i] -= rules_size;
920 else if ((*handle)->info.hook_entry[i] > offset) {
921 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
922 i, (*handle)->info.hook_entry[i], offset);
923 abort();
924 }
925
926 /* Underflow points to policy (terminal) rule in
927 built-in, so sequality is valid here (when deleting
928 the last rule). */
929 if ((*handle)->info.underflow[i] >= offset + rules_size)
930 (*handle)->info.underflow[i] -= rules_size;
931 else if ((*handle)->info.underflow[i] > offset) {
932 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
933 i, (*handle)->info.underflow[i], offset);
934 abort();
935 }
936 }
937
938 /* Move the rules down. */
939 memmove((char *)(*handle)->entries.entrytable + offset,
940 (char *)(*handle)->entries.entrytable + offset + rules_size,
941 (*handle)->entries.size - (offset + rules_size));
942
943 /* Move the counter map down. */
944 memmove(&(*handle)->counter_map[num_rules_offset],
945 &(*handle)->counter_map[num_rules_offset + num_rules],
946 sizeof(struct counter_map)
947 * ((*handle)->new_number - (num_rules + num_rules_offset)));
948
949 /* Fix numbers */
950 (*handle)->new_number -= num_rules;
951 (*handle)->entries.size -= rules_size;
952
953 /* Fix the chain cache */
954 if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
955 return 0;
956
957 return set_verdict(offset, -(int)rules_size, handle);
958}
Marc Bouchere6869a82000-03-20 06:03:29 +0000959
960static int
Rusty Russell79dee072000-05-02 16:45:16 +0000961standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000962{
Rusty Russell79dee072000-05-02 16:45:16 +0000963 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000964
Rusty Russell79dee072000-05-02 16:45:16 +0000965 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000966
Rusty Russell67088e72000-05-10 01:18:57 +0000967 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000968 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000969 errno = EINVAL;
970 return 0;
971 }
972 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000973 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
974 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000975 t->verdict = verdict;
976
977 return 1;
978}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000979
Marc Bouchere6869a82000-03-20 06:03:29 +0000980static int
Rusty Russell79dee072000-05-02 16:45:16 +0000981map_target(const TC_HANDLE_T handle,
982 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000983 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000984 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000985{
Harald Welte0113fe72004-01-06 19:04:02 +0000986 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000987
988 /* Save old target (except data, which we don't change, except for
989 standard case, where we don't care). */
990 *old = *t;
991
992 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000993 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000994 return standard_map(e, offset + e->next_offset);
995 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000996 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000997 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000998 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000999 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +00001000 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001001 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +00001002 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
1003 return standard_map(e, RETURN);
1004 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001005 /* Can't jump to builtins. */
1006 errno = EINVAL;
1007 return 0;
1008 } else {
1009 /* Maybe it's an existing chain name. */
Harald Welte0113fe72004-01-06 19:04:02 +00001010 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001011
Rusty Russell228e98d2000-04-27 10:28:06 +00001012 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +00001013 if (c)
Harald Welte3ea8f402003-06-23 18:25:59 +00001014 return standard_map(e, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001015 }
1016
1017 /* Must be a module? If not, kernel will reject... */
1018 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001019 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001020 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001021 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +00001022 return 1;
1023}
1024
1025static void
Rusty Russell79dee072000-05-02 16:45:16 +00001026unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +00001027{
Rusty Russell79dee072000-05-02 16:45:16 +00001028 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001029
1030 /* Save old target (except data, which we don't change, except for
1031 standard case, where we don't care). */
1032 *t = *old;
1033}
1034
Harald Welte0113fe72004-01-06 19:04:02 +00001035/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001036int
Rusty Russell79dee072000-05-02 16:45:16 +00001037TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1038 const STRUCT_ENTRY *e,
1039 unsigned int rulenum,
1040 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001041{
Harald Welte0113fe72004-01-06 19:04:02 +00001042 unsigned int chainindex, offset;
1043 STRUCT_ENTRY_TARGET old;
1044 struct chain_cache *c;
1045 STRUCT_ENTRY *tmp;
1046 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001047
Rusty Russell79dee072000-05-02 16:45:16 +00001048 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001049 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001050 errno = ENOENT;
1051 return 0;
1052 }
1053
Harald Welte0113fe72004-01-06 19:04:02 +00001054 chainindex = offset2index(*handle, c->start_off);
1055
1056 tmp = index2entry(*handle, chainindex + rulenum);
1057 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001058 errno = E2BIG;
1059 return 0;
1060 }
Harald Welte0113fe72004-01-06 19:04:02 +00001061 offset = index2offset(*handle, chainindex + rulenum);
Marc Bouchere6869a82000-03-20 06:03:29 +00001062
Harald Welte0113fe72004-01-06 19:04:02 +00001063 /* Mapping target actually alters entry, but that's
1064 transparent to the caller. */
1065 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1066 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001067
Harald Welte0113fe72004-01-06 19:04:02 +00001068 ret = insert_rules(1, e->next_offset, e, offset,
1069 chainindex + rulenum, rulenum == 0, handle);
1070 unmap_target((STRUCT_ENTRY *)e, &old);
1071 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001072}
1073
1074/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1075int
Rusty Russell79dee072000-05-02 16:45:16 +00001076TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1077 const STRUCT_ENTRY *e,
1078 unsigned int rulenum,
1079 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001080{
Harald Welte0113fe72004-01-06 19:04:02 +00001081 unsigned int chainindex, offset;
1082 STRUCT_ENTRY_TARGET old;
1083 struct chain_cache *c;
1084 STRUCT_ENTRY *tmp;
1085 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001086
Rusty Russell79dee072000-05-02 16:45:16 +00001087 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001088
Rusty Russell30fd6e52000-04-23 09:16:06 +00001089 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001090 errno = ENOENT;
1091 return 0;
1092 }
1093
Harald Welte0113fe72004-01-06 19:04:02 +00001094 chainindex = offset2index(*handle, c->start_off);
1095
1096 tmp = index2entry(*handle, chainindex + rulenum);
1097 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001098 errno = E2BIG;
1099 return 0;
1100 }
1101
Harald Welte0113fe72004-01-06 19:04:02 +00001102 offset = index2offset(*handle, chainindex + rulenum);
1103 /* Replace = delete and insert. */
1104 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1105 offset, chainindex + rulenum, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001106 return 0;
1107
Harald Welte0113fe72004-01-06 19:04:02 +00001108 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1109 return 0;
1110
1111 ret = insert_rules(1, e->next_offset, e, offset,
1112 chainindex + rulenum, 1, handle);
1113 unmap_target((STRUCT_ENTRY *)e, &old);
1114 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001115}
1116
Harald Welte0113fe72004-01-06 19:04:02 +00001117/* Append entry `fw' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001118 rulenum = length of chain. */
1119int
Rusty Russell79dee072000-05-02 16:45:16 +00001120TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1121 const STRUCT_ENTRY *e,
1122 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001123{
Harald Welte0113fe72004-01-06 19:04:02 +00001124 struct chain_cache *c;
1125 STRUCT_ENTRY_TARGET old;
1126 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001127
Rusty Russell79dee072000-05-02 16:45:16 +00001128 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001129 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001130 errno = ENOENT;
1131 return 0;
1132 }
1133
Harald Welte0113fe72004-01-06 19:04:02 +00001134 if (!map_target(*handle, (STRUCT_ENTRY *)e,
1135 c->end_off, &old))
1136 return 0;
1137
1138 ret = insert_rules(1, e->next_offset, e, c->end_off,
1139 offset2index(*handle, c->end_off), 0, handle);
1140 unmap_target((STRUCT_ENTRY *)e, &old);
1141 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001142}
1143
1144static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001145match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001146 const unsigned char *a_elems,
1147 const unsigned char *b_elems,
1148 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001149{
Rusty Russell79dee072000-05-02 16:45:16 +00001150 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001151 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001152
1153 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001154 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001155
Rusty Russell228e98d2000-04-27 10:28:06 +00001156 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001157 return 1;
1158
Rusty Russell228e98d2000-04-27 10:28:06 +00001159 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001160 return 1;
1161
Rusty Russell73ef09b2000-07-03 10:24:04 +00001162 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001163
Rusty Russell73ef09b2000-07-03 10:24:04 +00001164 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001165 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001166 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001167 *maskptr += i;
1168 return 0;
1169}
1170
1171static inline int
1172target_different(const unsigned char *a_targdata,
1173 const unsigned char *b_targdata,
1174 unsigned int tdatasize,
1175 const unsigned char *mask)
1176{
1177 unsigned int i;
1178 for (i = 0; i < tdatasize; i++)
1179 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1180 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001181
1182 return 0;
1183}
1184
Rusty Russell79dee072000-05-02 16:45:16 +00001185static int
1186is_same(const STRUCT_ENTRY *a,
1187 const STRUCT_ENTRY *b,
1188 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001189
Harald Welte0113fe72004-01-06 19:04:02 +00001190/* Delete the first rule in `chain' which matches `fw'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001191int
Rusty Russell79dee072000-05-02 16:45:16 +00001192TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1193 const STRUCT_ENTRY *origfw,
1194 unsigned char *matchmask,
1195 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001196{
Harald Welte0113fe72004-01-06 19:04:02 +00001197 unsigned int offset;
1198 struct chain_cache *c;
1199 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001200
Rusty Russell79dee072000-05-02 16:45:16 +00001201 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001202 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001203 errno = ENOENT;
1204 return 0;
1205 }
1206
Harald Welte0113fe72004-01-06 19:04:02 +00001207 fw = malloc(origfw->next_offset);
1208 if (fw == NULL) {
1209 errno = ENOMEM;
1210 return 0;
1211 }
1212
1213 for (offset = c->start_off; offset < c->end_off;
1214 offset += e->next_offset) {
1215 STRUCT_ENTRY_TARGET discard;
1216
1217 memcpy(fw, origfw, origfw->next_offset);
1218
1219 /* FIXME: handle this in is_same --RR */
1220 if (!map_target(*handle, fw, offset, &discard)) {
1221 free(fw);
1222 return 0;
1223 }
1224 e = get_entry(*handle, offset);
1225
1226#if 0
1227 printf("Deleting:\n");
1228 dump_entry(newe);
1229#endif
1230 if (is_same(e, fw, matchmask)) {
1231 int ret;
1232 ret = delete_rules(1, e->next_offset,
1233 offset, entry2index(*handle, e),
1234 handle);
1235 free(fw);
1236 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001237 }
1238 }
1239
Harald Welte0113fe72004-01-06 19:04:02 +00001240 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001241 errno = ENOENT;
1242 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001243}
Marc Bouchere6869a82000-03-20 06:03:29 +00001244
1245/* Delete the rule in position `rulenum' in `chain'. */
1246int
Rusty Russell79dee072000-05-02 16:45:16 +00001247TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1248 unsigned int rulenum,
1249 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001250{
Harald Welte0113fe72004-01-06 19:04:02 +00001251 unsigned int index;
1252 int ret;
1253 STRUCT_ENTRY *e;
1254 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001255
Rusty Russell79dee072000-05-02 16:45:16 +00001256 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welte0113fe72004-01-06 19:04:02 +00001257 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001258 errno = ENOENT;
1259 return 0;
1260 }
1261
Harald Welte0113fe72004-01-06 19:04:02 +00001262 index = offset2index(*handle, c->start_off) + rulenum;
1263
1264 if (index >= offset2index(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001265 errno = E2BIG;
1266 return 0;
1267 }
1268
Harald Welte0113fe72004-01-06 19:04:02 +00001269 e = index2entry(*handle, index);
1270 if (e == NULL) {
1271 errno = EINVAL;
1272 return 0;
1273 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001274
Harald Welte0113fe72004-01-06 19:04:02 +00001275 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1276 index, handle);
1277 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001278}
1279
1280/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1281 NULL and sets errno. */
1282const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001283TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1284 STRUCT_ENTRY *entry,
1285 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001286{
1287 errno = ENOSYS;
1288 return NULL;
1289}
1290
1291/* Flushes the entries in the given chain (ie. empties chain). */
1292int
Rusty Russell79dee072000-05-02 16:45:16 +00001293TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001294{
Harald Welte0113fe72004-01-06 19:04:02 +00001295 unsigned int startindex, endindex;
1296 STRUCT_ENTRY *startentry, *endentry;
1297 struct chain_cache *c;
1298 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001299
Harald Welte0113fe72004-01-06 19:04:02 +00001300 iptc_fn = TC_FLUSH_ENTRIES;
1301 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001302 errno = ENOENT;
1303 return 0;
1304 }
Harald Welte0113fe72004-01-06 19:04:02 +00001305 startindex = offset2index(*handle, c->start_off);
1306 endindex = offset2index(*handle, c->end_off);
1307 startentry = offset2entry(*handle, c->start_off);
1308 endentry = offset2entry(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001309
Harald Welte0113fe72004-01-06 19:04:02 +00001310 ret = delete_rules(endindex - startindex,
1311 (char *)endentry - (char *)startentry,
1312 c->start_off, startindex,
1313 handle);
1314 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001315}
1316
1317/* Zeroes the counters in a chain. */
1318int
Rusty Russell79dee072000-05-02 16:45:16 +00001319TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001320{
Harald Welte0113fe72004-01-06 19:04:02 +00001321 unsigned int i, end;
1322 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001323
Rusty Russell30fd6e52000-04-23 09:16:06 +00001324 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001325 errno = ENOENT;
1326 return 0;
1327 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001328
Harald Welte0113fe72004-01-06 19:04:02 +00001329 i = offset2index(*handle, c->start_off);
1330 end = offset2index(*handle, c->end_off);
1331
1332 for (; i <= end; i++) {
1333 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1334 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00001335 }
Rusty Russell175f6412000-03-24 09:32:20 +00001336 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001337
Marc Bouchere6869a82000-03-20 06:03:29 +00001338 return 1;
1339}
1340
Harald Welte1cef74d2001-01-05 15:22:59 +00001341STRUCT_COUNTERS *
1342TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1343 unsigned int rulenum,
1344 TC_HANDLE_T *handle)
1345{
1346 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001347 struct chain_cache *c;
1348 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001349
1350 iptc_fn = TC_READ_COUNTER;
1351 CHECK(*handle);
1352
Harald Welte0113fe72004-01-06 19:04:02 +00001353 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001354 errno = ENOENT;
1355 return NULL;
1356 }
1357
Harald Welte0113fe72004-01-06 19:04:02 +00001358 chainindex = offset2index(*handle, c->start_off);
1359 end = offset2index(*handle, c->end_off);
1360
1361 if (chainindex + rulenum > end) {
1362 errno = E2BIG;
1363 return NULL;
1364 }
1365
1366 e = index2entry(*handle, chainindex + rulenum);
1367
1368 return &e->counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00001369}
1370
1371int
1372TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1373 unsigned int rulenum,
1374 TC_HANDLE_T *handle)
1375{
1376 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001377 struct chain_cache *c;
1378 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001379
1380 iptc_fn = TC_ZERO_COUNTER;
1381 CHECK(*handle);
1382
Harald Welte0113fe72004-01-06 19:04:02 +00001383 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001384 errno = ENOENT;
1385 return 0;
1386 }
1387
Harald Welte0113fe72004-01-06 19:04:02 +00001388 chainindex = offset2index(*handle, c->start_off);
1389 end = offset2index(*handle, c->end_off);
1390
1391 if (chainindex + rulenum > end) {
1392 errno = E2BIG;
1393 return 0;
1394 }
1395
1396 e = index2entry(*handle, chainindex + rulenum);
1397
1398 if ((*handle)->counter_map[chainindex + rulenum].maptype
1399 == COUNTER_MAP_NORMAL_MAP) {
1400 (*handle)->counter_map[chainindex + rulenum].maptype
1401 = COUNTER_MAP_ZEROED;
1402 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001403
1404 set_changed(*handle);
1405
1406 return 1;
1407}
1408
1409int
1410TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1411 unsigned int rulenum,
1412 STRUCT_COUNTERS *counters,
1413 TC_HANDLE_T *handle)
1414{
1415 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001416 struct chain_cache *c;
1417 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001418
1419 iptc_fn = TC_SET_COUNTER;
1420 CHECK(*handle);
1421
Harald Welte0113fe72004-01-06 19:04:02 +00001422 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001423 errno = ENOENT;
1424 return 0;
1425 }
Harald Welte0113fe72004-01-06 19:04:02 +00001426
1427 chainindex = offset2index(*handle, c->start_off);
1428 end = offset2index(*handle, c->end_off);
1429
1430 if (chainindex + rulenum > end) {
1431 errno = E2BIG;
1432 return 0;
1433 }
1434
1435 e = index2entry(*handle, chainindex + rulenum);
1436
1437 (*handle)->counter_map[chainindex + rulenum].maptype
1438 = COUNTER_MAP_SET;
1439
1440 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00001441
1442 set_changed(*handle);
1443
1444 return 1;
1445}
1446
Marc Bouchere6869a82000-03-20 06:03:29 +00001447/* Creates a new chain. */
1448/* To create a chain, create two rules: error node and unconditional
1449 * return. */
1450int
Rusty Russell79dee072000-05-02 16:45:16 +00001451TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001452{
Marc Bouchere6869a82000-03-20 06:03:29 +00001453 int ret;
Harald Welte0113fe72004-01-06 19:04:02 +00001454 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001455 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001456 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001457 STRUCT_ENTRY ret;
1458 STRUCT_STANDARD_TARGET target;
Harald Welte0113fe72004-01-06 19:04:02 +00001459 } newc;
1460 unsigned int destination;
Marc Bouchere6869a82000-03-20 06:03:29 +00001461
Rusty Russell79dee072000-05-02 16:45:16 +00001462 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001463
1464 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1465 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001466 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001467 || strcmp(chain, LABEL_DROP) == 0
1468 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001469 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001470 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001471 errno = EEXIST;
1472 return 0;
1473 }
1474
Rusty Russell79dee072000-05-02 16:45:16 +00001475 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001476 errno = EINVAL;
1477 return 0;
1478 }
1479
Harald Welte0113fe72004-01-06 19:04:02 +00001480 memset(&newc, 0, sizeof(newc));
1481 newc.head.target_offset = sizeof(STRUCT_ENTRY);
1482 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001483 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001484 + ALIGN(sizeof(struct ipt_error_target));
Harald Welte0113fe72004-01-06 19:04:02 +00001485 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
1486 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
1487 strcpy(newc.name.error, chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001488
Harald Welte0113fe72004-01-06 19:04:02 +00001489 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
1490 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001491 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001492 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte0113fe72004-01-06 19:04:02 +00001493 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
1494 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001495 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte0113fe72004-01-06 19:04:02 +00001496 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001497
Harald Welte0113fe72004-01-06 19:04:02 +00001498 destination = index2offset(*handle, (*handle)->new_number -1);
Harald Weltefbc85232003-06-24 17:37:21 +00001499
Harald Welte0113fe72004-01-06 19:04:02 +00001500 /* Add just before terminal entry */
1501 ret = insert_rules(2, sizeof(newc), &newc.head,
1502 destination,
1503 (*handle)->new_number - 1,
1504 0, handle);
1505
1506 set_changed(*handle);
1507
1508 /* add chain cache info for this chain */
1509 add_chain_cache(*handle, chain,
1510 destination+newc.head.next_offset,
1511 destination+newc.head.next_offset);
1512
1513 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001514}
1515
1516static int
Rusty Russell79dee072000-05-02 16:45:16 +00001517count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001518{
Rusty Russell79dee072000-05-02 16:45:16 +00001519 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001520
Rusty Russell79dee072000-05-02 16:45:16 +00001521 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1522 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001523
1524 if (t->verdict == offset)
1525 (*ref)++;
1526 }
1527
1528 return 0;
1529}
1530
1531/* Get the number of references to this chain. */
1532int
Rusty Russell79dee072000-05-02 16:45:16 +00001533TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1534 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001535{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001536 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001537
Rusty Russell30fd6e52000-04-23 09:16:06 +00001538 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001539 errno = ENOENT;
1540 return 0;
1541 }
1542
1543 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001544 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001545 (*handle)->entries.size,
Harald Welte3ea8f402003-06-23 18:25:59 +00001546 count_ref, c->start_off, ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001547 return 1;
1548}
1549
1550/* Deletes a chain. */
1551int
Rusty Russell79dee072000-05-02 16:45:16 +00001552TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001553{
Harald Welte0113fe72004-01-06 19:04:02 +00001554 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001555 unsigned int references;
Harald Welte0113fe72004-01-06 19:04:02 +00001556 struct chain_cache *c;
1557 int ret;
1558 STRUCT_ENTRY *start;
Marc Bouchere6869a82000-03-20 06:03:29 +00001559
Rusty Russell79dee072000-05-02 16:45:16 +00001560 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001561 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001562
Rusty Russell79dee072000-05-02 16:45:16 +00001563 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001564
Rusty Russell79dee072000-05-02 16:45:16 +00001565 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001566 errno = EINVAL;
1567 return 0;
1568 }
1569
1570 if (references > 0) {
1571 errno = EMLINK;
1572 return 0;
1573 }
1574
Harald Welte0113fe72004-01-06 19:04:02 +00001575 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001576 errno = ENOENT;
1577 return 0;
1578 }
1579
Harald Welte0113fe72004-01-06 19:04:02 +00001580 if (c->start_off != c->end_off) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001581 errno = ENOTEMPTY;
1582 return 0;
1583 }
1584
Harald Welte0113fe72004-01-06 19:04:02 +00001585 /* Need label index: preceeds chain start */
1586 labelidx = offset2index(*handle, c->start_off) - 1;
1587 labeloff = index2offset(*handle, labelidx);
1588
1589 start = offset2entry(*handle, c->start_off);
1590
1591 ret = delete_rules(2,
1592 get_entry(*handle, labeloff)->next_offset
1593 + start->next_offset,
1594 labeloff, labelidx, handle);
1595 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001596}
1597
1598/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001599int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1600 const IPT_CHAINLABEL newname,
1601 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001602{
Harald Welte0113fe72004-01-06 19:04:02 +00001603 unsigned int labeloff, labelidx;
1604 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001605 struct ipt_error_target *t;
1606
Rusty Russell79dee072000-05-02 16:45:16 +00001607 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001608
Harald Welte1de80462000-10-30 12:00:27 +00001609 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1610 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001611 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001612 || strcmp(newname, LABEL_DROP) == 0
1613 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001614 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001615 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001616 errno = EEXIST;
1617 return 0;
1618 }
1619
Rusty Russell30fd6e52000-04-23 09:16:06 +00001620 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001621 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001622 errno = ENOENT;
1623 return 0;
1624 }
1625
Rusty Russell79dee072000-05-02 16:45:16 +00001626 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001627 errno = EINVAL;
1628 return 0;
1629 }
1630
Harald Welte0113fe72004-01-06 19:04:02 +00001631 /* Need label index: preceeds chain start */
1632 labelidx = offset2index(*handle, c->start_off) - 1;
1633 labeloff = index2offset(*handle, labelidx);
Marc Bouchere6869a82000-03-20 06:03:29 +00001634
1635 t = (struct ipt_error_target *)
Harald Welte0113fe72004-01-06 19:04:02 +00001636 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001637
1638 memset(t->error, 0, sizeof(t->error));
1639 strcpy(t->error, newname);
Harald Weltefbc85232003-06-24 17:37:21 +00001640
Harald Welte0113fe72004-01-06 19:04:02 +00001641 /* update chain cache */
1642 memset(c->name, 0, sizeof(c->name));
1643 strcpy(c->name, newname);
1644
1645 set_changed(*handle);
1646
Marc Bouchere6869a82000-03-20 06:03:29 +00001647 return 1;
1648}
1649
1650/* Sets the policy on a built-in chain. */
1651int
Rusty Russell79dee072000-05-02 16:45:16 +00001652TC_SET_POLICY(const IPT_CHAINLABEL chain,
1653 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001654 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001655 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001656{
1657 unsigned int hook;
Harald Welte0113fe72004-01-06 19:04:02 +00001658 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001659 STRUCT_ENTRY *e;
1660 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001661
Rusty Russell79dee072000-05-02 16:45:16 +00001662 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001663 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001664 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001665 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001666 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001667 return 0;
1668 } else
1669 hook--;
1670
Harald Welte0113fe72004-01-06 19:04:02 +00001671 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1672 if (policyoff != (*handle)->info.underflow[hook]) {
1673 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1674 chain, policyoff, (*handle)->info.underflow[hook]);
Marc Bouchere6869a82000-03-20 06:03:29 +00001675 return 0;
1676 }
1677
Harald Welte0113fe72004-01-06 19:04:02 +00001678 e = get_entry(*handle, policyoff);
1679 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001680
Rusty Russell79dee072000-05-02 16:45:16 +00001681 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001682 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001683 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001684 t->verdict = -NF_DROP - 1;
1685 else {
1686 errno = EINVAL;
1687 return 0;
1688 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001689
1690 ctrindex = entry2index(*handle, e);
1691
1692 if (counters) {
1693 /* set byte and packet counters */
1694 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1695
Harald Welte0113fe72004-01-06 19:04:02 +00001696 (*handle)->counter_map[ctrindex].maptype
1697 = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00001698
1699 } else {
Harald Welte0113fe72004-01-06 19:04:02 +00001700 (*handle)->counter_map[ctrindex]
1701 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
Harald Welte1cef74d2001-01-05 15:22:59 +00001702 }
1703
Rusty Russell175f6412000-03-24 09:32:20 +00001704 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001705
Marc Bouchere6869a82000-03-20 06:03:29 +00001706 return 1;
1707}
1708
1709/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001710 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001711 libiptc.c:833: fixed or forbidden register was spilled.
1712 This may be due to a compiler bug or to impossible asm
1713 statements or clauses.
1714*/
1715static void
Rusty Russell79dee072000-05-02 16:45:16 +00001716subtract_counters(STRUCT_COUNTERS *answer,
1717 const STRUCT_COUNTERS *a,
1718 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001719{
1720 answer->pcnt = a->pcnt - b->pcnt;
1721 answer->bcnt = a->bcnt - b->bcnt;
1722}
1723
1724int
Rusty Russell79dee072000-05-02 16:45:16 +00001725TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001726{
1727 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001728 STRUCT_REPLACE *repl;
1729 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001730 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001731 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001732
1733 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001734
1735 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1736 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1737
Marc Bouchere6869a82000-03-20 06:03:29 +00001738#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001739 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001740#endif
1741
1742 /* Don't commit if nothing changed. */
1743 if (!(*handle)->changed)
1744 goto finished;
1745
1746 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1747 if (!repl) {
1748 errno = ENOMEM;
1749 return 0;
1750 }
1751
1752 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001753 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001754 * (*handle)->info.num_entries);
1755 if (!repl->counters) {
1756 free(repl);
1757 errno = ENOMEM;
1758 return 0;
1759 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001760
Marc Bouchere6869a82000-03-20 06:03:29 +00001761 /* These are the counters we're going to put back, later. */
1762 newcounters = malloc(counterlen);
1763 if (!newcounters) {
1764 free(repl->counters);
1765 free(repl);
1766 errno = ENOMEM;
1767 return 0;
1768 }
1769
1770 strcpy(repl->name, (*handle)->info.name);
1771 repl->num_entries = (*handle)->new_number;
1772 repl->size = (*handle)->entries.size;
1773 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1774 sizeof(repl->hook_entry));
1775 memcpy(repl->underflow, (*handle)->info.underflow,
1776 sizeof(repl->underflow));
1777 repl->num_counters = (*handle)->info.num_entries;
1778 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001779 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001780 (*handle)->entries.size);
1781
Rusty Russell79dee072000-05-02 16:45:16 +00001782 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001783 sizeof(*repl) + (*handle)->entries.size) < 0) {
1784 free(repl->counters);
1785 free(repl);
1786 free(newcounters);
1787 return 0;
1788 }
1789
1790 /* Put counters back. */
1791 strcpy(newcounters->name, (*handle)->info.name);
1792 newcounters->num_counters = (*handle)->new_number;
1793 for (i = 0; i < (*handle)->new_number; i++) {
1794 unsigned int mappos = (*handle)->counter_map[i].mappos;
1795 switch ((*handle)->counter_map[i].maptype) {
1796 case COUNTER_MAP_NOMAP:
1797 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001798 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001799 break;
1800
1801 case COUNTER_MAP_NORMAL_MAP:
1802 /* Original read: X.
1803 * Atomic read on replacement: X + Y.
1804 * Currently in kernel: Z.
1805 * Want in kernel: X + Y + Z.
1806 * => Add in X + Y
1807 * => Add in replacement read.
1808 */
1809 newcounters->counters[i] = repl->counters[mappos];
1810 break;
1811
1812 case COUNTER_MAP_ZEROED:
1813 /* Original read: X.
1814 * Atomic read on replacement: X + Y.
1815 * Currently in kernel: Z.
1816 * Want in kernel: Y + Z.
1817 * => Add in Y.
1818 * => Add in (replacement read - original read).
1819 */
1820 subtract_counters(&newcounters->counters[i],
1821 &repl->counters[mappos],
1822 &index2entry(*handle, i)->counters);
1823 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001824
1825 case COUNTER_MAP_SET:
1826 /* Want to set counter (iptables-restore) */
1827
1828 memcpy(&newcounters->counters[i],
1829 &index2entry(*handle, i)->counters,
1830 sizeof(STRUCT_COUNTERS));
1831
1832 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001833 }
1834 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001835
1836#ifdef KERNEL_64_USERSPACE_32
1837 {
1838 /* Kernel will think that pointer should be 64-bits, and get
1839 padding. So we accomodate here (assumption: alignment of
1840 `counters' is on 64-bit boundary). */
1841 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1842 if ((unsigned long)&newcounters->counters % 8 != 0) {
1843 fprintf(stderr,
1844 "counters alignment incorrect! Mail rusty!\n");
1845 abort();
1846 }
1847 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001848 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001849#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001850
Rusty Russell79dee072000-05-02 16:45:16 +00001851 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1852 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001853 free(repl->counters);
1854 free(repl);
1855 free(newcounters);
1856 return 0;
1857 }
1858
1859 free(repl->counters);
1860 free(repl);
1861 free(newcounters);
1862
1863 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001864 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001865 return 1;
1866}
1867
1868/* Get raw socket. */
1869int
Rusty Russell79dee072000-05-02 16:45:16 +00001870TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001871{
1872 return sockfd;
1873}
1874
1875/* Translates errno numbers into more human-readable form than strerror. */
1876const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001877TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001878{
1879 unsigned int i;
1880 struct table_struct {
1881 void *fn;
1882 int err;
1883 const char *message;
1884 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001885 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001886 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001887 { TC_INIT, ENOENT,
1888 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001889 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1890 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1891 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001892 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001893 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1894 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1895 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1896 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001897 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1898 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001899 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1900 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001901 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001902 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001903 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001904 { TC_CHECK_PACKET, ENOSYS,
1905 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001906 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001907 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001908 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001909 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001910 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001911 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001912 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001913
1914 { NULL, 0, "Incompatible with this kernel" },
1915 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1916 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1917 { NULL, ENOMEM, "Memory allocation problem" },
1918 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001919 };
1920
1921 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1922 if ((!table[i].fn || table[i].fn == iptc_fn)
1923 && table[i].err == err)
1924 return table[i].message;
1925 }
1926
1927 return strerror(err);
1928}