blob: 3aacb547e3f0cf9c38ee4a61ac1867e1e2b1e5ce [file] [log] [blame]
Karsten Desler073df8f2004-01-31 15:33:55 +00001/* Library which manipulates firewall rules. Version $Revision: 1.43 $ */
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
Marc Bouchere6869a82000-03-20 06:03:29 +000023#ifndef IPT_LIB_DIR
24#define IPT_LIB_DIR "/usr/local/lib/iptables"
25#endif
26
Harald Welte0113fe72004-01-06 19:04:02 +000027#ifndef __OPTIMIZE__
28STRUCT_ENTRY_TARGET *
29GET_TARGET(STRUCT_ENTRY *e)
30{
31 return (void *)e + e->target_offset;
32}
33#endif
34
Marc Bouchere6869a82000-03-20 06:03:29 +000035static int sockfd = -1;
36static void *iptc_fn = NULL;
37
38static const char *hooknames[]
Rusty Russell79dee072000-05-02 16:45:16 +000039= { [HOOK_PRE_ROUTING] "PREROUTING",
40 [HOOK_LOCAL_IN] "INPUT",
41 [HOOK_FORWARD] "FORWARD",
42 [HOOK_LOCAL_OUT] "OUTPUT",
Rusty Russell10758b72000-09-14 07:37:33 +000043 [HOOK_POST_ROUTING] "POSTROUTING",
44#ifdef HOOK_DROPPING
45 [HOOK_DROPPING] "DROPPING"
46#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000047};
48
49struct counter_map
50{
51 enum {
52 COUNTER_MAP_NOMAP,
53 COUNTER_MAP_NORMAL_MAP,
Harald Welte1cef74d2001-01-05 15:22:59 +000054 COUNTER_MAP_ZEROED,
55 COUNTER_MAP_SET
Marc Bouchere6869a82000-03-20 06:03:29 +000056 } maptype;
57 unsigned int mappos;
58};
59
60/* Convenience structures */
61struct ipt_error_target
62{
Rusty Russell79dee072000-05-02 16:45:16 +000063 STRUCT_ENTRY_TARGET t;
64 char error[TABLE_MAXNAMELEN];
Marc Bouchere6869a82000-03-20 06:03:29 +000065};
66
Harald Welte0113fe72004-01-06 19:04:02 +000067struct chain_cache
Rusty Russell30fd6e52000-04-23 09:16:06 +000068{
Rusty Russell79dee072000-05-02 16:45:16 +000069 char name[TABLE_MAXNAMELEN];
Harald Welte0113fe72004-01-06 19:04:02 +000070 /* This is the first rule in chain. */
71 unsigned int start_off;
72 /* Last rule in chain */
73 unsigned int end_off;
Rusty Russell30fd6e52000-04-23 09:16:06 +000074};
75
Rusty Russell79dee072000-05-02 16:45:16 +000076STRUCT_TC_HANDLE
Marc Bouchere6869a82000-03-20 06:03:29 +000077{
78 /* Have changes been made? */
79 int changed;
Harald Welte0113fe72004-01-06 19:04:02 +000080 /* Size in here reflects original state. */
Rusty Russell79dee072000-05-02 16:45:16 +000081 STRUCT_GETINFO info;
Marc Bouchere6869a82000-03-20 06:03:29 +000082
Harald Welte0113fe72004-01-06 19:04:02 +000083 struct counter_map *counter_map;
Marc Bouchere6869a82000-03-20 06:03:29 +000084 /* Array of hook names */
85 const char **hooknames;
86
Rusty Russell30fd6e52000-04-23 09:16:06 +000087 /* Cached position of chain heads (NULL = no cache). */
88 unsigned int cache_num_chains;
89 unsigned int cache_num_builtins;
90 struct chain_cache *cache_chain_heads;
91
92 /* Chain iterator: current chain cache entry. */
93 struct chain_cache *cache_chain_iteration;
94
95 /* Rule iterator: terminal rule */
Rusty Russell79dee072000-05-02 16:45:16 +000096 STRUCT_ENTRY *cache_rule_end;
Rusty Russell175f6412000-03-24 09:32:20 +000097
Marc Bouchere6869a82000-03-20 06:03:29 +000098 /* Number in here reflects current state. */
99 unsigned int new_number;
Rusty Russell79dee072000-05-02 16:45:16 +0000100 STRUCT_GET_ENTRIES entries;
Marc Bouchere6869a82000-03-20 06:03:29 +0000101};
102
Rusty Russell175f6412000-03-24 09:32:20 +0000103static void
Rusty Russell79dee072000-05-02 16:45:16 +0000104set_changed(TC_HANDLE_T h)
Rusty Russell175f6412000-03-24 09:32:20 +0000105{
Rusty Russell175f6412000-03-24 09:32:20 +0000106 h->changed = 1;
107}
108
Harald Welte380ba5f2002-02-13 16:19:55 +0000109#ifdef IPTC_DEBUG
Rusty Russell79dee072000-05-02 16:45:16 +0000110static void do_check(TC_HANDLE_T h, unsigned int line);
Rusty Russell849779c2000-04-23 15:51:51 +0000111#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000112#else
113#define CHECK(h)
114#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000115
116static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000117get_number(const STRUCT_ENTRY *i,
118 const STRUCT_ENTRY *seek,
Marc Bouchere6869a82000-03-20 06:03:29 +0000119 unsigned int *pos)
120{
121 if (i == seek)
122 return 1;
123 (*pos)++;
124 return 0;
125}
126
127static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000128entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
Marc Bouchere6869a82000-03-20 06:03:29 +0000129{
130 unsigned int pos = 0;
131
Rusty Russell725d97a2000-07-07 08:54:22 +0000132 if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000133 get_number, seek, &pos) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000134 fprintf(stderr, "ERROR: offset %i not an entry!\n",
Rusty Russell725d97a2000-07-07 08:54:22 +0000135 (char *)seek - (char *)h->entries.entrytable);
Marc Bouchere6869a82000-03-20 06:03:29 +0000136 abort();
137 }
138 return pos;
139}
140
141static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000142get_entry_n(STRUCT_ENTRY *i,
Marc Bouchere6869a82000-03-20 06:03:29 +0000143 unsigned int number,
144 unsigned int *pos,
Rusty Russell79dee072000-05-02 16:45:16 +0000145 STRUCT_ENTRY **pe)
Marc Bouchere6869a82000-03-20 06:03:29 +0000146{
147 if (*pos == number) {
148 *pe = i;
149 return 1;
150 }
151 (*pos)++;
152 return 0;
153}
154
Rusty Russell79dee072000-05-02 16:45:16 +0000155static STRUCT_ENTRY *
156index2entry(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000157{
158 unsigned int pos = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000159 STRUCT_ENTRY *ret = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000160
Rusty Russell725d97a2000-07-07 08:54:22 +0000161 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000162 get_entry_n, index, &pos, &ret);
Marc Bouchere6869a82000-03-20 06:03:29 +0000163
164 return ret;
165}
166
Harald Welte0113fe72004-01-06 19:04:02 +0000167static inline STRUCT_ENTRY *
168get_entry(TC_HANDLE_T h, unsigned int offset)
169{
170 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
171}
172
173static inline unsigned long
174entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
175{
176 return (void *)e - (void *)h->entries.entrytable;
177}
178
Harald Welte3ea8f402003-06-23 18:25:59 +0000179static inline unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000180index2offset(TC_HANDLE_T h, unsigned int index)
Marc Bouchere6869a82000-03-20 06:03:29 +0000181{
182 return entry2offset(h, index2entry(h, index));
183}
184
Harald Welte3ea8f402003-06-23 18:25:59 +0000185static inline STRUCT_ENTRY *
186offset2entry(TC_HANDLE_T h, unsigned int offset)
187{
188 return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
189}
190
191static inline unsigned int
192offset2index(const TC_HANDLE_T h, unsigned int offset)
193{
194 return entry2index(h, offset2entry(h, offset));
195}
196
197
Harald Welte0113fe72004-01-06 19:04:02 +0000198static const char *
199get_errorlabel(TC_HANDLE_T h, unsigned int offset)
200{
201 STRUCT_ENTRY *e;
202
203 e = get_entry(h, offset);
204 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
205 fprintf(stderr, "ERROR: offset %u not an error node!\n",
206 offset);
207 abort();
208 }
209
210 return (const char *)GET_TARGET(e)->data;
211}
Marc Bouchere6869a82000-03-20 06:03:29 +0000212
213/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000214static TC_HANDLE_T
Harald Welte0113fe72004-01-06 19:04:02 +0000215alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
Marc Bouchere6869a82000-03-20 06:03:29 +0000216{
217 size_t len;
Rusty Russell79dee072000-05-02 16:45:16 +0000218 TC_HANDLE_T h;
Marc Bouchere6869a82000-03-20 06:03:29 +0000219
Rusty Russell79dee072000-05-02 16:45:16 +0000220 len = sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000221 + size
222 + num_rules * sizeof(struct counter_map);
223
224 if ((h = malloc(len)) == NULL) {
225 errno = ENOMEM;
226 return NULL;
227 }
228
229 h->changed = 0;
Harald Welte0113fe72004-01-06 19:04:02 +0000230 h->cache_num_chains = 0;
231 h->cache_chain_heads = NULL;
232 h->counter_map = (void *)h
233 + sizeof(STRUCT_TC_HANDLE)
234 + size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000235 strcpy(h->info.name, tablename);
236 strcpy(h->entries.name, tablename);
237
238 return h;
239}
240
Rusty Russell79dee072000-05-02 16:45:16 +0000241TC_HANDLE_T
242TC_INIT(const char *tablename)
Marc Bouchere6869a82000-03-20 06:03:29 +0000243{
Rusty Russell79dee072000-05-02 16:45:16 +0000244 TC_HANDLE_T h;
245 STRUCT_GETINFO info;
Harald Welte0113fe72004-01-06 19:04:02 +0000246 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +0000247 int tmp;
248 socklen_t s;
249
Rusty Russell79dee072000-05-02 16:45:16 +0000250 iptc_fn = TC_INIT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000251
Martin Josefssone560fd62003-06-13 16:56:51 +0000252 if (sockfd != -1) {
Harald Welte366454b2002-01-07 13:46:50 +0000253 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000254 sockfd = -1;
255 }
Harald Welte366454b2002-01-07 13:46:50 +0000256
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000257 if (strlen(tablename) >= TABLE_MAXNAMELEN) {
258 errno = EINVAL;
259 return NULL;
260 }
261
Rusty Russell79dee072000-05-02 16:45:16 +0000262 sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
Marc Bouchere6869a82000-03-20 06:03:29 +0000263 if (sockfd < 0)
264 return NULL;
265
266 s = sizeof(info);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000267
Marc Bouchere6869a82000-03-20 06:03:29 +0000268 strcpy(info.name, tablename);
Rusty Russell79dee072000-05-02 16:45:16 +0000269 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000270 return NULL;
271
Harald Welte0113fe72004-01-06 19:04:02 +0000272 if ((h = alloc_handle(info.name, info.size, info.num_entries))
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000273 == NULL) {
274 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000275 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000276 return NULL;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000277 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000278
279/* Too hard --RR */
280#if 0
281 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
282 dynlib = dlopen(pathname, RTLD_NOW);
283 if (!dynlib) {
284 errno = ENOENT;
285 return NULL;
286 }
287 h->hooknames = dlsym(dynlib, "hooknames");
288 if (!h->hooknames) {
289 errno = ENOENT;
290 return NULL;
291 }
292#else
293 h->hooknames = hooknames;
294#endif
295
296 /* Initialize current state */
297 h->info = info;
Harald Welte0113fe72004-01-06 19:04:02 +0000298 h->new_number = h->info.num_entries;
299 for (i = 0; i < h->info.num_entries; i++)
300 h->counter_map[i]
301 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
302
Marc Bouchere6869a82000-03-20 06:03:29 +0000303 h->entries.size = h->info.size;
304
Rusty Russell79dee072000-05-02 16:45:16 +0000305 tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
Marc Bouchere6869a82000-03-20 06:03:29 +0000306
Rusty Russell79dee072000-05-02 16:45:16 +0000307 if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
Marc Bouchere6869a82000-03-20 06:03:29 +0000308 &tmp) < 0) {
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000309 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000310 sockfd = -1;
Marc Bouchere6869a82000-03-20 06:03:29 +0000311 free(h);
312 return NULL;
313 }
Rusty Russell7e53bf92000-03-20 07:03:28 +0000314
Marc Bouchere6869a82000-03-20 06:03:29 +0000315 CHECK(h);
316 return h;
317}
318
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000319void
320TC_FREE(TC_HANDLE_T *h)
321{
322 close(sockfd);
Martin Josefssone560fd62003-06-13 16:56:51 +0000323 sockfd = -1;
Harald Welte0113fe72004-01-06 19:04:02 +0000324 if ((*h)->cache_chain_heads)
325 free((*h)->cache_chain_heads);
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000326 free(*h);
327 *h = NULL;
328}
329
Marc Bouchere6869a82000-03-20 06:03:29 +0000330static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000331print_match(const STRUCT_ENTRY_MATCH *m)
Marc Bouchere6869a82000-03-20 06:03:29 +0000332{
Rusty Russell228e98d2000-04-27 10:28:06 +0000333 printf("Match name: `%s'\n", m->u.user.name);
Marc Bouchere6869a82000-03-20 06:03:29 +0000334 return 0;
335}
336
Rusty Russell79dee072000-05-02 16:45:16 +0000337static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
338
Marc Bouchere6869a82000-03-20 06:03:29 +0000339void
Rusty Russell79dee072000-05-02 16:45:16 +0000340TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000341{
342 CHECK(handle);
343
344 printf("libiptc v%s. %u entries, %u bytes.\n",
Harald Welte80fe35d2002-05-29 13:08:15 +0000345 IPTABLES_VERSION,
Marc Bouchere6869a82000-03-20 06:03:29 +0000346 handle->new_number, handle->entries.size);
347 printf("Table `%s'\n", handle->info.name);
348 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000349 handle->info.hook_entry[HOOK_PRE_ROUTING],
350 handle->info.hook_entry[HOOK_LOCAL_IN],
351 handle->info.hook_entry[HOOK_FORWARD],
352 handle->info.hook_entry[HOOK_LOCAL_OUT],
353 handle->info.hook_entry[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000354 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
Rusty Russell67088e72000-05-10 01:18:57 +0000355 handle->info.underflow[HOOK_PRE_ROUTING],
356 handle->info.underflow[HOOK_LOCAL_IN],
357 handle->info.underflow[HOOK_FORWARD],
358 handle->info.underflow[HOOK_LOCAL_OUT],
359 handle->info.underflow[HOOK_POST_ROUTING]);
Marc Bouchere6869a82000-03-20 06:03:29 +0000360
Rusty Russell725d97a2000-07-07 08:54:22 +0000361 ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000362 dump_entry, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000363}
364
Rusty Russell30fd6e52000-04-23 09:16:06 +0000365/* Returns 0 if not hook entry, else hooknumber + 1 */
366static inline unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000367is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
Marc Bouchere6869a82000-03-20 06:03:29 +0000368{
369 unsigned int i;
370
Rusty Russell79dee072000-05-02 16:45:16 +0000371 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000372 if ((h->info.valid_hooks & (1 << i))
373 && get_entry(h, h->info.hook_entry[i]) == e)
374 return i+1;
Rusty Russell175f6412000-03-24 09:32:20 +0000375 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000376 return 0;
377}
378
Harald Welte0113fe72004-01-06 19:04:02 +0000379static inline int
380add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
381{
382 unsigned int builtin;
383
384 /* Last entry. End it. */
385 if (entry2offset(h, e) + e->next_offset == h->entries.size) {
386 /* This is the ERROR node at end of the table */
387 h->cache_chain_heads[h->cache_num_chains-1].end_off =
388 entry2offset(h, *prev);
389 return 0;
390 }
391
392 /* We know this is the start of a new chain if it's an ERROR
393 target, or a hook entry point */
394 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
395 /* prev was last entry in previous chain */
396 h->cache_chain_heads[h->cache_num_chains-1].end_off
397 = entry2offset(h, *prev);
398
399 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
400 (const char *)GET_TARGET(e)->data);
401 h->cache_chain_heads[h->cache_num_chains].start_off
402 = entry2offset(h, (void *)e + e->next_offset);
403 h->cache_num_chains++;
404 } else if ((builtin = is_hook_entry(e, h)) != 0) {
405 if (h->cache_num_chains > 0)
406 /* prev was last entry in previous chain */
407 h->cache_chain_heads[h->cache_num_chains-1].end_off
408 = entry2offset(h, *prev);
409
410 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
411 h->hooknames[builtin-1]);
412 h->cache_chain_heads[h->cache_num_chains].start_off
413 = entry2offset(h, (void *)e);
414 h->cache_num_chains++;
415 }
416
417 *prev = e;
418 return 0;
419}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000420
Rusty Russell30fd6e52000-04-23 09:16:06 +0000421static int alphasort(const void *a, const void *b)
422{
423 return strcmp(((struct chain_cache *)a)->name,
424 ((struct chain_cache *)b)->name);
425}
Harald Welte0113fe72004-01-06 19:04:02 +0000426
427static int populate_cache(TC_HANDLE_T h)
428{
429 unsigned int i;
430 STRUCT_ENTRY *prev;
431
432 /* # chains < # rules / 2 + num builtins - 1 */
433 h->cache_chain_heads = malloc((h->new_number / 2 + 4)
434 * sizeof(struct chain_cache));
435 if (!h->cache_chain_heads) {
436 errno = ENOMEM;
437 return 0;
438 }
439
440 h->cache_num_chains = 0;
441 h->cache_num_builtins = 0;
442
443 /* Count builtins */
444 for (i = 0; i < NUMHOOKS; i++) {
445 if (h->info.valid_hooks & (1 << i))
446 h->cache_num_builtins++;
447 }
448
449 prev = NULL;
450 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
451 add_chain, h, &prev);
452
453 qsort(h->cache_chain_heads + h->cache_num_builtins,
454 h->cache_num_chains - h->cache_num_builtins,
455 sizeof(struct chain_cache), alphasort);
456
457 return 1;
458}
459
460static int
461correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
462{
463 int i; /* needs to be signed because deleting first
464 chain can make it drop to -1 */
465
466 if (!delta)
467 return 1;
468
469 for (i = 0; i < h->cache_num_chains; i++) {
470 struct chain_cache *cc = &h->cache_chain_heads[i];
471
472 if (delta < 0) {
473 /* take care about deleted chains */
474 if (cc->start_off > offset+delta
475 && cc->end_off < offset) {
476 /* this chain is within the deleted range,
477 * let's remove it from the cache */
478 void *start;
479 unsigned int size;
480
481 h->cache_num_chains--;
482
483 /* no need for memmove since we are
484 * removing the last entry */
485 if (i >= h->cache_num_chains)
486 continue;
487
488 start = &h->cache_chain_heads[i+1];
489 size = (h->cache_num_chains-i)
490 * sizeof(struct chain_cache);
491 memmove(cc, start, size);
492
493 /* iterate over same index again, since
494 * it is now a different chain */
495 i--;
496 continue;
497 }
498 }
499
500 if (cc->start_off > offset)
501 cc->start_off += delta;
502
503 if (cc->end_off >= offset)
504 cc->end_off += delta;
505 }
506 /* HW_FIXME: sorting might be needed, but just in case a new chain was
507 * added */
508
509 return 1;
510}
511
512static int
513add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
514 unsigned int end_off)
515{
516 struct chain_cache *ccs = realloc(h->cache_chain_heads,
517 (h->new_number / 2 + 4 + 1)
518 * sizeof(struct chain_cache));
519 struct chain_cache *newcc;
520
521 if (!ccs)
522 return 0;
523
524 h->cache_chain_heads = ccs;
525 newcc = &h->cache_chain_heads[h->cache_num_chains];
526 h->cache_num_chains++;
527
528 strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
Karsten Desler073df8f2004-01-31 15:33:55 +0000529 newcc->name[TABLE_MAXNAMELEN-1] = '\0';
Harald Welte0113fe72004-01-06 19:04:02 +0000530 newcc->start_off = start_off;
531 newcc->end_off = end_off;
532
533 return 1;
534}
535
536/* Returns cache ptr if found, otherwise NULL. */
537static struct chain_cache *
538find_label(const char *name, TC_HANDLE_T handle)
539{
540 unsigned int i;
541
542 if (handle->cache_chain_heads == NULL
543 && !populate_cache(handle))
544 return NULL;
545
546 /* FIXME: Linear search through builtins, then binary --RR */
547 for (i = 0; i < handle->cache_num_chains; i++) {
548 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
549 return &handle->cache_chain_heads[i];
550 }
551
552 return NULL;
553}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000554
Marc Bouchere6869a82000-03-20 06:03:29 +0000555/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000556int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000557{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000558 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000559}
560
561/* Returns the position of the final (ie. unconditional) element. */
562static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000563get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000564{
565 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000566 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000567
568 last_off = start;
569 e = get_entry(handle, start);
570
571 /* Terminate when we meet a error label or a hook entry. */
572 for (off = start + e->next_offset;
573 off < handle->entries.size;
574 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000575 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000576 unsigned int i;
577
578 e = get_entry(handle, off);
579
580 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000581 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000582 if ((handle->info.valid_hooks & (1 << i))
583 && off == handle->info.hook_entry[i])
584 return last_off;
585 }
586
587 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000588 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000589 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000590 return last_off;
591 }
592 /* SHOULD NEVER HAPPEN */
593 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
594 handle->entries.size, off);
595 abort();
596}
597
Rusty Russell30fd6e52000-04-23 09:16:06 +0000598/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000599const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000600TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000601{
Harald Welte0113fe72004-01-06 19:04:02 +0000602 if ((*handle)->cache_chain_heads == NULL
603 && !populate_cache(*handle))
604 return NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000605
Harald Welte0113fe72004-01-06 19:04:02 +0000606 (*handle)->cache_chain_iteration
607 = &(*handle)->cache_chain_heads[0];
608
609 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000610}
611
Rusty Russell30fd6e52000-04-23 09:16:06 +0000612/* Iterator functions to run through the chains. Returns NULL at end. */
613const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000614TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000615{
Harald Welte0113fe72004-01-06 19:04:02 +0000616 (*handle)->cache_chain_iteration++;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000617
Harald Welte0113fe72004-01-06 19:04:02 +0000618 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
619 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000620 return NULL;
621
Harald Welte0113fe72004-01-06 19:04:02 +0000622 return (*handle)->cache_chain_iteration->name;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000623}
624
625/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000626const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000627TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000628{
Harald Welte0113fe72004-01-06 19:04:02 +0000629 struct chain_cache *c;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000630
631 c = find_label(chain, *handle);
632 if (!c) {
633 errno = ENOENT;
634 return NULL;
635 }
636
637 /* Empty chain: single return/policy rule */
Harald Welte0113fe72004-01-06 19:04:02 +0000638 if (c->start_off == c->end_off)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000639 return NULL;
640
Harald Welte0113fe72004-01-06 19:04:02 +0000641 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
642 return offset2entry(*handle, c->start_off);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000643}
644
645/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000646const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000647TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000648{
Harald Welte0113fe72004-01-06 19:04:02 +0000649 if ((void *)prev + prev->next_offset
650 == (void *)(*handle)->cache_rule_end)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000651 return NULL;
652
Harald Welte0113fe72004-01-06 19:04:02 +0000653 return (void *)prev + prev->next_offset;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000654}
655
656#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000657/* How many rules in this chain? */
658unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000659TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000660{
661 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000662 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000663
664 CHECK(*handle);
665 if (!find_label(&off, chain, *handle)) {
666 errno = ENOENT;
667 return (unsigned int)-1;
668 }
669
670 start = get_entry(*handle, off);
671 end = get_entry(*handle, get_chain_end(*handle, off));
672
673 return entry2index(*handle, end) - entry2index(*handle, start);
674}
675
676/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000677const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
678 unsigned int n,
679 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000680{
681 unsigned int pos = 0, chainindex;
682
683 CHECK(*handle);
684 if (!find_label(&pos, chain, *handle)) {
685 errno = ENOENT;
686 return NULL;
687 }
688
689 chainindex = entry2index(*handle, get_entry(*handle, pos));
690
691 return index2entry(*handle, chainindex + n);
692}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000693#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000694
Rusty Russell30fd6e52000-04-23 09:16:06 +0000695static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000696target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000697{
698 int spos;
Harald Welte0113fe72004-01-06 19:04:02 +0000699 unsigned int labelidx;
700 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000701
Rusty Russell30fd6e52000-04-23 09:16:06 +0000702 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000703 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000704
Rusty Russell79dee072000-05-02 16:45:16 +0000705 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
706 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000707
708 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000709 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000710 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000711 if (spos == RETURN)
712 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000713 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000714 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000715 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000716 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000717 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000718 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000719
Harald Welte0113fe72004-01-06 19:04:02 +0000720 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
721 entry2offset(handle, e), handle->entries.size,
722 spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000723 abort();
724 }
725
Harald Welte0113fe72004-01-06 19:04:02 +0000726 jumpto = get_entry(handle, spos);
Marc Bouchere6869a82000-03-20 06:03:29 +0000727
728 /* Fall through rule */
729 if (jumpto == (void *)e + e->next_offset)
730 return "";
731
732 /* Must point to head of a chain: ie. after error rule */
733 labelidx = entry2index(handle, jumpto) - 1;
734 return get_errorlabel(handle, index2offset(handle, labelidx));
735}
736
737/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000738const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
739 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000740{
Marc Bouchere6869a82000-03-20 06:03:29 +0000741 return target_name(*handle, e);
742}
743
744/* Is this a built-in chain? Actually returns hook + 1. */
745int
Rusty Russell79dee072000-05-02 16:45:16 +0000746TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000747{
748 unsigned int i;
749
Rusty Russell79dee072000-05-02 16:45:16 +0000750 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000751 if ((handle->info.valid_hooks & (1 << i))
752 && handle->hooknames[i]
753 && strcmp(handle->hooknames[i], chain) == 0)
754 return i+1;
755 }
756 return 0;
757}
758
759/* Get the policy of a given built-in chain */
760const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000761TC_GET_POLICY(const char *chain,
762 STRUCT_COUNTERS *counters,
763 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000764{
Harald Welte0113fe72004-01-06 19:04:02 +0000765 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000766 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000767 int hook;
768
Rusty Russell79dee072000-05-02 16:45:16 +0000769 hook = TC_BUILTIN(chain, *handle);
Harald Welte0113fe72004-01-06 19:04:02 +0000770 if (hook != 0)
771 start = (*handle)->info.hook_entry[hook-1];
772 else
Marc Bouchere6869a82000-03-20 06:03:29 +0000773 return NULL;
774
Harald Welte0113fe72004-01-06 19:04:02 +0000775 e = get_entry(*handle, get_chain_end(*handle, start));
Marc Bouchere6869a82000-03-20 06:03:29 +0000776 *counters = e->counters;
777
778 return target_name(*handle, e);
779}
780
Harald Welte0113fe72004-01-06 19:04:02 +0000781static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000782correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000783 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000784 unsigned int offset, int delta_offset)
785{
Rusty Russell79dee072000-05-02 16:45:16 +0000786 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000787 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000788
789 /* Trap: insert of fall-through rule. Don't change fall-through
790 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000791 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000792 && t->verdict > (int)offset
793 && !(curr == offset &&
794 t->verdict == curr + e->next_offset)) {
795 t->verdict += delta_offset;
796 }
797
798 return 0;
799}
800
801/* Adjusts standard verdict jump positions after an insertion/deletion. */
802static int
Rusty Russell79dee072000-05-02 16:45:16 +0000803set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000804{
Rusty Russell725d97a2000-07-07 08:54:22 +0000805 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000806 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000807 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000808 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000809
Rusty Russell175f6412000-03-24 09:32:20 +0000810 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000811 return 1;
812}
813
Harald Welte0113fe72004-01-06 19:04:02 +0000814/* If prepend is set, then we are prepending to a chain: if the
815 * insertion position is an entry point, keep the entry point. */
816static int
817insert_rules(unsigned int num_rules, unsigned int rules_size,
818 const STRUCT_ENTRY *insert,
819 unsigned int offset, unsigned int num_rules_offset,
820 int prepend,
821 TC_HANDLE_T *handle)
822{
823 TC_HANDLE_T newh;
824 STRUCT_GETINFO newinfo;
825 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +0000826
Harald Welte0113fe72004-01-06 19:04:02 +0000827 if (offset >= (*handle)->entries.size) {
828 errno = EINVAL;
829 return 0;
830 }
831
832 newinfo = (*handle)->info;
833
834 /* Fix up entry points. */
835 for (i = 0; i < NUMHOOKS; i++) {
836 /* Entry points to START of chain, so keep same if
837 inserting on at that point. */
838 if ((*handle)->info.hook_entry[i] > offset)
839 newinfo.hook_entry[i] += rules_size;
840
841 /* Underflow always points to END of chain (policy),
842 so if something is inserted at same point, it
843 should be advanced. */
844 if ((*handle)->info.underflow[i] >= offset)
845 newinfo.underflow[i] += rules_size;
846 }
847
848 newh = alloc_handle((*handle)->info.name,
849 (*handle)->entries.size + rules_size,
850 (*handle)->new_number + num_rules);
851 if (!newh)
852 return 0;
853 newh->info = newinfo;
854
855 /* Copy pre... */
856 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
857 /* ... Insert new ... */
858 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
859 /* ... copy post */
860 memcpy((char *)newh->entries.entrytable + offset + rules_size,
861 (char *)(*handle)->entries.entrytable + offset,
862 (*handle)->entries.size - offset);
863
864 /* Move counter map. */
865 /* Copy pre... */
866 memcpy(newh->counter_map, (*handle)->counter_map,
867 sizeof(struct counter_map) * num_rules_offset);
868 /* ... copy post */
869 memcpy(newh->counter_map + num_rules_offset + num_rules,
870 (*handle)->counter_map + num_rules_offset,
871 sizeof(struct counter_map) * ((*handle)->new_number
872 - num_rules_offset));
873 /* Set intermediates to no counter copy */
874 for (i = 0; i < num_rules; i++)
875 newh->counter_map[num_rules_offset+i]
876 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
877
878 newh->new_number = (*handle)->new_number + num_rules;
879 newh->entries.size = (*handle)->entries.size + rules_size;
880 newh->hooknames = (*handle)->hooknames;
881
882 newh->cache_chain_heads = (*handle)->cache_chain_heads;
883 newh->cache_num_builtins = (*handle)->cache_num_builtins;
884 newh->cache_num_chains = (*handle)->cache_num_chains;
885 newh->cache_rule_end = (*handle)->cache_rule_end;
886 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
887 if (!correct_cache(newh, offset, rules_size)) {
888 free(newh);
889 return 0;
890 }
891
892 free(*handle);
893 *handle = newh;
894
895 return set_verdict(offset, rules_size, handle);
896}
897
898static int
899delete_rules(unsigned int num_rules, unsigned int rules_size,
900 unsigned int offset, unsigned int num_rules_offset,
901 TC_HANDLE_T *handle)
902{
903 unsigned int i;
904
905 if (offset + rules_size > (*handle)->entries.size) {
906 errno = EINVAL;
907 return 0;
908 }
909
910 /* Fix up entry points. */
911 for (i = 0; i < NUMHOOKS; i++) {
912 /* In practice, we never delete up to a hook entry,
913 since the built-in chains are always first,
914 so these two are never equal */
915 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
916 (*handle)->info.hook_entry[i] -= rules_size;
917 else if ((*handle)->info.hook_entry[i] > offset) {
918 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
919 i, (*handle)->info.hook_entry[i], offset);
920 abort();
921 }
922
923 /* Underflow points to policy (terminal) rule in
924 built-in, so sequality is valid here (when deleting
925 the last rule). */
926 if ((*handle)->info.underflow[i] >= offset + rules_size)
927 (*handle)->info.underflow[i] -= rules_size;
928 else if ((*handle)->info.underflow[i] > offset) {
929 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
930 i, (*handle)->info.underflow[i], offset);
931 abort();
932 }
933 }
934
935 /* Move the rules down. */
936 memmove((char *)(*handle)->entries.entrytable + offset,
937 (char *)(*handle)->entries.entrytable + offset + rules_size,
938 (*handle)->entries.size - (offset + rules_size));
939
940 /* Move the counter map down. */
941 memmove(&(*handle)->counter_map[num_rules_offset],
942 &(*handle)->counter_map[num_rules_offset + num_rules],
943 sizeof(struct counter_map)
944 * ((*handle)->new_number - (num_rules + num_rules_offset)));
945
946 /* Fix numbers */
947 (*handle)->new_number -= num_rules;
948 (*handle)->entries.size -= rules_size;
949
950 /* Fix the chain cache */
951 if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
952 return 0;
953
954 return set_verdict(offset, -(int)rules_size, handle);
955}
Marc Bouchere6869a82000-03-20 06:03:29 +0000956
957static int
Rusty Russell79dee072000-05-02 16:45:16 +0000958standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000959{
Rusty Russell79dee072000-05-02 16:45:16 +0000960 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000961
Rusty Russell79dee072000-05-02 16:45:16 +0000962 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000963
Rusty Russell67088e72000-05-10 01:18:57 +0000964 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000965 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000966 errno = EINVAL;
967 return 0;
968 }
969 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000970 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
971 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000972 t->verdict = verdict;
973
974 return 1;
975}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000976
Marc Bouchere6869a82000-03-20 06:03:29 +0000977static int
Rusty Russell79dee072000-05-02 16:45:16 +0000978map_target(const TC_HANDLE_T handle,
979 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000980 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000981 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000982{
Harald Welte0113fe72004-01-06 19:04:02 +0000983 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000984
985 /* Save old target (except data, which we don't change, except for
986 standard case, where we don't care). */
987 *old = *t;
988
989 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000990 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000991 return standard_map(e, offset + e->next_offset);
992 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000993 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000994 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000995 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000996 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000997 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000998 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000999 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
1000 return standard_map(e, RETURN);
1001 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001002 /* Can't jump to builtins. */
1003 errno = EINVAL;
1004 return 0;
1005 } else {
1006 /* Maybe it's an existing chain name. */
Harald Welte0113fe72004-01-06 19:04:02 +00001007 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001008
Rusty Russell228e98d2000-04-27 10:28:06 +00001009 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +00001010 if (c)
Harald Welte3ea8f402003-06-23 18:25:59 +00001011 return standard_map(e, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001012 }
1013
1014 /* Must be a module? If not, kernel will reject... */
1015 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001016 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001017 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001018 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +00001019 return 1;
1020}
1021
1022static void
Rusty Russell79dee072000-05-02 16:45:16 +00001023unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +00001024{
Rusty Russell79dee072000-05-02 16:45:16 +00001025 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001026
1027 /* Save old target (except data, which we don't change, except for
1028 standard case, where we don't care). */
1029 *t = *old;
1030}
1031
Harald Welte0113fe72004-01-06 19:04:02 +00001032/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001033int
Rusty Russell79dee072000-05-02 16:45:16 +00001034TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1035 const STRUCT_ENTRY *e,
1036 unsigned int rulenum,
1037 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001038{
Harald Welte0113fe72004-01-06 19:04:02 +00001039 unsigned int chainindex, offset;
1040 STRUCT_ENTRY_TARGET old;
1041 struct chain_cache *c;
1042 STRUCT_ENTRY *tmp;
1043 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001044
Rusty Russell79dee072000-05-02 16:45:16 +00001045 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001046 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001047 errno = ENOENT;
1048 return 0;
1049 }
1050
Harald Welte0113fe72004-01-06 19:04:02 +00001051 chainindex = offset2index(*handle, c->start_off);
1052
1053 tmp = index2entry(*handle, chainindex + rulenum);
1054 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001055 errno = E2BIG;
1056 return 0;
1057 }
Harald Welte0113fe72004-01-06 19:04:02 +00001058 offset = index2offset(*handle, chainindex + rulenum);
Marc Bouchere6869a82000-03-20 06:03:29 +00001059
Harald Welte0113fe72004-01-06 19:04:02 +00001060 /* Mapping target actually alters entry, but that's
1061 transparent to the caller. */
1062 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1063 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001064
Harald Welte0113fe72004-01-06 19:04:02 +00001065 ret = insert_rules(1, e->next_offset, e, offset,
1066 chainindex + rulenum, rulenum == 0, handle);
1067 unmap_target((STRUCT_ENTRY *)e, &old);
1068 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001069}
1070
1071/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1072int
Rusty Russell79dee072000-05-02 16:45:16 +00001073TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1074 const STRUCT_ENTRY *e,
1075 unsigned int rulenum,
1076 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001077{
Harald Welte0113fe72004-01-06 19:04:02 +00001078 unsigned int chainindex, offset;
1079 STRUCT_ENTRY_TARGET old;
1080 struct chain_cache *c;
1081 STRUCT_ENTRY *tmp;
1082 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001083
Rusty Russell79dee072000-05-02 16:45:16 +00001084 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001085
Rusty Russell30fd6e52000-04-23 09:16:06 +00001086 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001087 errno = ENOENT;
1088 return 0;
1089 }
1090
Harald Welte0113fe72004-01-06 19:04:02 +00001091 chainindex = offset2index(*handle, c->start_off);
1092
1093 tmp = index2entry(*handle, chainindex + rulenum);
1094 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001095 errno = E2BIG;
1096 return 0;
1097 }
1098
Harald Welte0113fe72004-01-06 19:04:02 +00001099 offset = index2offset(*handle, chainindex + rulenum);
1100 /* Replace = delete and insert. */
1101 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1102 offset, chainindex + rulenum, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001103 return 0;
1104
Harald Welte0113fe72004-01-06 19:04:02 +00001105 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
1106 return 0;
1107
1108 ret = insert_rules(1, e->next_offset, e, offset,
1109 chainindex + rulenum, 1, handle);
1110 unmap_target((STRUCT_ENTRY *)e, &old);
1111 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001112}
1113
Harald Welte0113fe72004-01-06 19:04:02 +00001114/* Append entry `fw' to chain `chain'. Equivalent to insert with
Marc Bouchere6869a82000-03-20 06:03:29 +00001115 rulenum = length of chain. */
1116int
Rusty Russell79dee072000-05-02 16:45:16 +00001117TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1118 const STRUCT_ENTRY *e,
1119 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001120{
Harald Welte0113fe72004-01-06 19:04:02 +00001121 struct chain_cache *c;
1122 STRUCT_ENTRY_TARGET old;
1123 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001124
Rusty Russell79dee072000-05-02 16:45:16 +00001125 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001126 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001127 errno = ENOENT;
1128 return 0;
1129 }
1130
Harald Welte0113fe72004-01-06 19:04:02 +00001131 if (!map_target(*handle, (STRUCT_ENTRY *)e,
1132 c->end_off, &old))
1133 return 0;
1134
1135 ret = insert_rules(1, e->next_offset, e, c->end_off,
1136 offset2index(*handle, c->end_off), 0, handle);
1137 unmap_target((STRUCT_ENTRY *)e, &old);
1138 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001139}
1140
1141static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001142match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001143 const unsigned char *a_elems,
1144 const unsigned char *b_elems,
1145 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001146{
Rusty Russell79dee072000-05-02 16:45:16 +00001147 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001148 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001149
1150 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001151 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001152
Rusty Russell228e98d2000-04-27 10:28:06 +00001153 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001154 return 1;
1155
Rusty Russell228e98d2000-04-27 10:28:06 +00001156 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001157 return 1;
1158
Rusty Russell73ef09b2000-07-03 10:24:04 +00001159 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001160
Rusty Russell73ef09b2000-07-03 10:24:04 +00001161 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001162 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001163 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001164 *maskptr += i;
1165 return 0;
1166}
1167
1168static inline int
1169target_different(const unsigned char *a_targdata,
1170 const unsigned char *b_targdata,
1171 unsigned int tdatasize,
1172 const unsigned char *mask)
1173{
1174 unsigned int i;
1175 for (i = 0; i < tdatasize; i++)
1176 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1177 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001178
1179 return 0;
1180}
1181
Rusty Russell79dee072000-05-02 16:45:16 +00001182static int
1183is_same(const STRUCT_ENTRY *a,
1184 const STRUCT_ENTRY *b,
1185 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001186
Harald Welte0113fe72004-01-06 19:04:02 +00001187/* Delete the first rule in `chain' which matches `fw'. */
Marc Bouchere6869a82000-03-20 06:03:29 +00001188int
Rusty Russell79dee072000-05-02 16:45:16 +00001189TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1190 const STRUCT_ENTRY *origfw,
1191 unsigned char *matchmask,
1192 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001193{
Harald Welte0113fe72004-01-06 19:04:02 +00001194 unsigned int offset;
1195 struct chain_cache *c;
1196 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001197
Rusty Russell79dee072000-05-02 16:45:16 +00001198 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001199 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001200 errno = ENOENT;
1201 return 0;
1202 }
1203
Harald Welte0113fe72004-01-06 19:04:02 +00001204 fw = malloc(origfw->next_offset);
1205 if (fw == NULL) {
1206 errno = ENOMEM;
1207 return 0;
1208 }
1209
1210 for (offset = c->start_off; offset < c->end_off;
1211 offset += e->next_offset) {
1212 STRUCT_ENTRY_TARGET discard;
1213
1214 memcpy(fw, origfw, origfw->next_offset);
1215
1216 /* FIXME: handle this in is_same --RR */
1217 if (!map_target(*handle, fw, offset, &discard)) {
1218 free(fw);
1219 return 0;
1220 }
1221 e = get_entry(*handle, offset);
1222
1223#if 0
1224 printf("Deleting:\n");
1225 dump_entry(newe);
1226#endif
1227 if (is_same(e, fw, matchmask)) {
1228 int ret;
1229 ret = delete_rules(1, e->next_offset,
1230 offset, entry2index(*handle, e),
1231 handle);
1232 free(fw);
1233 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001234 }
1235 }
1236
Harald Welte0113fe72004-01-06 19:04:02 +00001237 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001238 errno = ENOENT;
1239 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001240}
Marc Bouchere6869a82000-03-20 06:03:29 +00001241
1242/* Delete the rule in position `rulenum' in `chain'. */
1243int
Rusty Russell79dee072000-05-02 16:45:16 +00001244TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1245 unsigned int rulenum,
1246 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001247{
Harald Welte0113fe72004-01-06 19:04:02 +00001248 unsigned int index;
1249 int ret;
1250 STRUCT_ENTRY *e;
1251 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001252
Rusty Russell79dee072000-05-02 16:45:16 +00001253 iptc_fn = TC_DELETE_NUM_ENTRY;
Harald Welte0113fe72004-01-06 19:04:02 +00001254 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001255 errno = ENOENT;
1256 return 0;
1257 }
1258
Harald Welte0113fe72004-01-06 19:04:02 +00001259 index = offset2index(*handle, c->start_off) + rulenum;
1260
1261 if (index >= offset2index(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001262 errno = E2BIG;
1263 return 0;
1264 }
1265
Harald Welte0113fe72004-01-06 19:04:02 +00001266 e = index2entry(*handle, index);
1267 if (e == NULL) {
1268 errno = EINVAL;
1269 return 0;
1270 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001271
Harald Welte0113fe72004-01-06 19:04:02 +00001272 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1273 index, handle);
1274 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001275}
1276
1277/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1278 NULL and sets errno. */
1279const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001280TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1281 STRUCT_ENTRY *entry,
1282 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001283{
1284 errno = ENOSYS;
1285 return NULL;
1286}
1287
1288/* Flushes the entries in the given chain (ie. empties chain). */
1289int
Rusty Russell79dee072000-05-02 16:45:16 +00001290TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001291{
Harald Welte0113fe72004-01-06 19:04:02 +00001292 unsigned int startindex, endindex;
1293 STRUCT_ENTRY *startentry, *endentry;
1294 struct chain_cache *c;
1295 int ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001296
Harald Welte0113fe72004-01-06 19:04:02 +00001297 iptc_fn = TC_FLUSH_ENTRIES;
1298 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001299 errno = ENOENT;
1300 return 0;
1301 }
Harald Welte0113fe72004-01-06 19:04:02 +00001302 startindex = offset2index(*handle, c->start_off);
1303 endindex = offset2index(*handle, c->end_off);
1304 startentry = offset2entry(*handle, c->start_off);
1305 endentry = offset2entry(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001306
Harald Welte0113fe72004-01-06 19:04:02 +00001307 ret = delete_rules(endindex - startindex,
1308 (char *)endentry - (char *)startentry,
1309 c->start_off, startindex,
1310 handle);
1311 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001312}
1313
1314/* Zeroes the counters in a chain. */
1315int
Rusty Russell79dee072000-05-02 16:45:16 +00001316TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001317{
Harald Welte0113fe72004-01-06 19:04:02 +00001318 unsigned int i, end;
1319 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001320
Rusty Russell30fd6e52000-04-23 09:16:06 +00001321 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001322 errno = ENOENT;
1323 return 0;
1324 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001325
Harald Welte0113fe72004-01-06 19:04:02 +00001326 i = offset2index(*handle, c->start_off);
1327 end = offset2index(*handle, c->end_off);
1328
1329 for (; i <= end; i++) {
1330 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1331 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
Marc Bouchere6869a82000-03-20 06:03:29 +00001332 }
Rusty Russell175f6412000-03-24 09:32:20 +00001333 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001334
Marc Bouchere6869a82000-03-20 06:03:29 +00001335 return 1;
1336}
1337
Harald Welte1cef74d2001-01-05 15:22:59 +00001338STRUCT_COUNTERS *
1339TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1340 unsigned int rulenum,
1341 TC_HANDLE_T *handle)
1342{
1343 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001344 struct chain_cache *c;
1345 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001346
1347 iptc_fn = TC_READ_COUNTER;
1348 CHECK(*handle);
1349
Harald Welte0113fe72004-01-06 19:04:02 +00001350 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001351 errno = ENOENT;
1352 return NULL;
1353 }
1354
Harald Welte0113fe72004-01-06 19:04:02 +00001355 chainindex = offset2index(*handle, c->start_off);
1356 end = offset2index(*handle, c->end_off);
1357
1358 if (chainindex + rulenum > end) {
1359 errno = E2BIG;
1360 return NULL;
1361 }
1362
1363 e = index2entry(*handle, chainindex + rulenum);
1364
1365 return &e->counters;
Harald Welte1cef74d2001-01-05 15:22:59 +00001366}
1367
1368int
1369TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1370 unsigned int rulenum,
1371 TC_HANDLE_T *handle)
1372{
1373 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001374 struct chain_cache *c;
1375 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001376
1377 iptc_fn = TC_ZERO_COUNTER;
1378 CHECK(*handle);
1379
Harald Welte0113fe72004-01-06 19:04:02 +00001380 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001381 errno = ENOENT;
1382 return 0;
1383 }
1384
Harald Welte0113fe72004-01-06 19:04:02 +00001385 chainindex = offset2index(*handle, c->start_off);
1386 end = offset2index(*handle, c->end_off);
1387
1388 if (chainindex + rulenum > end) {
1389 errno = E2BIG;
1390 return 0;
1391 }
1392
1393 e = index2entry(*handle, chainindex + rulenum);
1394
1395 if ((*handle)->counter_map[chainindex + rulenum].maptype
1396 == COUNTER_MAP_NORMAL_MAP) {
1397 (*handle)->counter_map[chainindex + rulenum].maptype
1398 = COUNTER_MAP_ZEROED;
1399 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001400
1401 set_changed(*handle);
1402
1403 return 1;
1404}
1405
1406int
1407TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1408 unsigned int rulenum,
1409 STRUCT_COUNTERS *counters,
1410 TC_HANDLE_T *handle)
1411{
1412 STRUCT_ENTRY *e;
Harald Welte0113fe72004-01-06 19:04:02 +00001413 struct chain_cache *c;
1414 unsigned int chainindex, end;
Harald Welte1cef74d2001-01-05 15:22:59 +00001415
1416 iptc_fn = TC_SET_COUNTER;
1417 CHECK(*handle);
1418
Harald Welte0113fe72004-01-06 19:04:02 +00001419 if (!(c = find_label(chain, *handle))) {
Harald Welte1cef74d2001-01-05 15:22:59 +00001420 errno = ENOENT;
1421 return 0;
1422 }
Harald Welte0113fe72004-01-06 19:04:02 +00001423
1424 chainindex = offset2index(*handle, c->start_off);
1425 end = offset2index(*handle, c->end_off);
1426
1427 if (chainindex + rulenum > end) {
1428 errno = E2BIG;
1429 return 0;
1430 }
1431
1432 e = index2entry(*handle, chainindex + rulenum);
1433
1434 (*handle)->counter_map[chainindex + rulenum].maptype
1435 = COUNTER_MAP_SET;
1436
1437 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
Harald Welte1cef74d2001-01-05 15:22:59 +00001438
1439 set_changed(*handle);
1440
1441 return 1;
1442}
1443
Marc Bouchere6869a82000-03-20 06:03:29 +00001444/* Creates a new chain. */
1445/* To create a chain, create two rules: error node and unconditional
1446 * return. */
1447int
Rusty Russell79dee072000-05-02 16:45:16 +00001448TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001449{
Marc Bouchere6869a82000-03-20 06:03:29 +00001450 int ret;
Harald Welte0113fe72004-01-06 19:04:02 +00001451 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001452 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001453 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001454 STRUCT_ENTRY ret;
1455 STRUCT_STANDARD_TARGET target;
Harald Welte0113fe72004-01-06 19:04:02 +00001456 } newc;
1457 unsigned int destination;
Marc Bouchere6869a82000-03-20 06:03:29 +00001458
Rusty Russell79dee072000-05-02 16:45:16 +00001459 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001460
1461 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1462 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001463 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001464 || strcmp(chain, LABEL_DROP) == 0
1465 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001466 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001467 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001468 errno = EEXIST;
1469 return 0;
1470 }
1471
Rusty Russell79dee072000-05-02 16:45:16 +00001472 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001473 errno = EINVAL;
1474 return 0;
1475 }
1476
Harald Welte0113fe72004-01-06 19:04:02 +00001477 memset(&newc, 0, sizeof(newc));
1478 newc.head.target_offset = sizeof(STRUCT_ENTRY);
1479 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001480 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001481 + ALIGN(sizeof(struct ipt_error_target));
Harald Welte0113fe72004-01-06 19:04:02 +00001482 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
1483 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
1484 strcpy(newc.name.error, chain);
Marc Bouchere6869a82000-03-20 06:03:29 +00001485
Harald Welte0113fe72004-01-06 19:04:02 +00001486 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
1487 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001488 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001489 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte0113fe72004-01-06 19:04:02 +00001490 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
1491 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001492 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Harald Welte0113fe72004-01-06 19:04:02 +00001493 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001494
Harald Welte0113fe72004-01-06 19:04:02 +00001495 destination = index2offset(*handle, (*handle)->new_number -1);
Harald Weltefbc85232003-06-24 17:37:21 +00001496
Harald Welte0113fe72004-01-06 19:04:02 +00001497 /* Add just before terminal entry */
1498 ret = insert_rules(2, sizeof(newc), &newc.head,
1499 destination,
1500 (*handle)->new_number - 1,
1501 0, handle);
1502
1503 set_changed(*handle);
1504
1505 /* add chain cache info for this chain */
1506 add_chain_cache(*handle, chain,
1507 destination+newc.head.next_offset,
1508 destination+newc.head.next_offset);
1509
1510 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001511}
1512
1513static int
Rusty Russell79dee072000-05-02 16:45:16 +00001514count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001515{
Rusty Russell79dee072000-05-02 16:45:16 +00001516 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001517
Rusty Russell79dee072000-05-02 16:45:16 +00001518 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1519 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001520
1521 if (t->verdict == offset)
1522 (*ref)++;
1523 }
1524
1525 return 0;
1526}
1527
1528/* Get the number of references to this chain. */
1529int
Rusty Russell79dee072000-05-02 16:45:16 +00001530TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1531 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001532{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001533 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001534
Rusty Russell30fd6e52000-04-23 09:16:06 +00001535 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001536 errno = ENOENT;
1537 return 0;
1538 }
1539
1540 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001541 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001542 (*handle)->entries.size,
Harald Welte3ea8f402003-06-23 18:25:59 +00001543 count_ref, c->start_off, ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001544 return 1;
1545}
1546
1547/* Deletes a chain. */
1548int
Rusty Russell79dee072000-05-02 16:45:16 +00001549TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001550{
Harald Welte0113fe72004-01-06 19:04:02 +00001551 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001552 unsigned int references;
Harald Welte0113fe72004-01-06 19:04:02 +00001553 struct chain_cache *c;
1554 int ret;
1555 STRUCT_ENTRY *start;
Marc Bouchere6869a82000-03-20 06:03:29 +00001556
Rusty Russell79dee072000-05-02 16:45:16 +00001557 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001558 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001559
Rusty Russell79dee072000-05-02 16:45:16 +00001560 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001561
Rusty Russell79dee072000-05-02 16:45:16 +00001562 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001563 errno = EINVAL;
1564 return 0;
1565 }
1566
1567 if (references > 0) {
1568 errno = EMLINK;
1569 return 0;
1570 }
1571
Harald Welte0113fe72004-01-06 19:04:02 +00001572 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001573 errno = ENOENT;
1574 return 0;
1575 }
1576
Harald Welte0113fe72004-01-06 19:04:02 +00001577 if (c->start_off != c->end_off) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001578 errno = ENOTEMPTY;
1579 return 0;
1580 }
1581
Harald Welte0113fe72004-01-06 19:04:02 +00001582 /* Need label index: preceeds chain start */
1583 labelidx = offset2index(*handle, c->start_off) - 1;
1584 labeloff = index2offset(*handle, labelidx);
1585
1586 start = offset2entry(*handle, c->start_off);
1587
1588 ret = delete_rules(2,
1589 get_entry(*handle, labeloff)->next_offset
1590 + start->next_offset,
1591 labeloff, labelidx, handle);
1592 return ret;
Marc Bouchere6869a82000-03-20 06:03:29 +00001593}
1594
1595/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001596int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1597 const IPT_CHAINLABEL newname,
1598 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001599{
Harald Welte0113fe72004-01-06 19:04:02 +00001600 unsigned int labeloff, labelidx;
1601 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001602 struct ipt_error_target *t;
1603
Rusty Russell79dee072000-05-02 16:45:16 +00001604 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001605
Harald Welte1de80462000-10-30 12:00:27 +00001606 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1607 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001608 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001609 || strcmp(newname, LABEL_DROP) == 0
1610 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001611 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001612 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001613 errno = EEXIST;
1614 return 0;
1615 }
1616
Rusty Russell30fd6e52000-04-23 09:16:06 +00001617 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001618 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001619 errno = ENOENT;
1620 return 0;
1621 }
1622
Rusty Russell79dee072000-05-02 16:45:16 +00001623 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001624 errno = EINVAL;
1625 return 0;
1626 }
1627
Harald Welte0113fe72004-01-06 19:04:02 +00001628 /* Need label index: preceeds chain start */
1629 labelidx = offset2index(*handle, c->start_off) - 1;
1630 labeloff = index2offset(*handle, labelidx);
Marc Bouchere6869a82000-03-20 06:03:29 +00001631
1632 t = (struct ipt_error_target *)
Harald Welte0113fe72004-01-06 19:04:02 +00001633 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001634
1635 memset(t->error, 0, sizeof(t->error));
1636 strcpy(t->error, newname);
Harald Weltefbc85232003-06-24 17:37:21 +00001637
Harald Welte0113fe72004-01-06 19:04:02 +00001638 /* update chain cache */
1639 memset(c->name, 0, sizeof(c->name));
1640 strcpy(c->name, newname);
1641
1642 set_changed(*handle);
1643
Marc Bouchere6869a82000-03-20 06:03:29 +00001644 return 1;
1645}
1646
1647/* Sets the policy on a built-in chain. */
1648int
Rusty Russell79dee072000-05-02 16:45:16 +00001649TC_SET_POLICY(const IPT_CHAINLABEL chain,
1650 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001651 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001652 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001653{
1654 unsigned int hook;
Harald Welte0113fe72004-01-06 19:04:02 +00001655 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001656 STRUCT_ENTRY *e;
1657 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001658
Rusty Russell79dee072000-05-02 16:45:16 +00001659 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001660 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001661 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001662 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001663 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001664 return 0;
1665 } else
1666 hook--;
1667
Harald Welte0113fe72004-01-06 19:04:02 +00001668 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1669 if (policyoff != (*handle)->info.underflow[hook]) {
1670 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1671 chain, policyoff, (*handle)->info.underflow[hook]);
Marc Bouchere6869a82000-03-20 06:03:29 +00001672 return 0;
1673 }
1674
Harald Welte0113fe72004-01-06 19:04:02 +00001675 e = get_entry(*handle, policyoff);
1676 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001677
Rusty Russell79dee072000-05-02 16:45:16 +00001678 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001679 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001680 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001681 t->verdict = -NF_DROP - 1;
1682 else {
1683 errno = EINVAL;
1684 return 0;
1685 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001686
1687 ctrindex = entry2index(*handle, e);
1688
1689 if (counters) {
1690 /* set byte and packet counters */
1691 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1692
Harald Welte0113fe72004-01-06 19:04:02 +00001693 (*handle)->counter_map[ctrindex].maptype
1694 = COUNTER_MAP_SET;
Harald Welte1cef74d2001-01-05 15:22:59 +00001695
1696 } else {
Harald Welte0113fe72004-01-06 19:04:02 +00001697 (*handle)->counter_map[ctrindex]
1698 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
Harald Welte1cef74d2001-01-05 15:22:59 +00001699 }
1700
Rusty Russell175f6412000-03-24 09:32:20 +00001701 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001702
Marc Bouchere6869a82000-03-20 06:03:29 +00001703 return 1;
1704}
1705
1706/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001707 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001708 libiptc.c:833: fixed or forbidden register was spilled.
1709 This may be due to a compiler bug or to impossible asm
1710 statements or clauses.
1711*/
1712static void
Rusty Russell79dee072000-05-02 16:45:16 +00001713subtract_counters(STRUCT_COUNTERS *answer,
1714 const STRUCT_COUNTERS *a,
1715 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001716{
1717 answer->pcnt = a->pcnt - b->pcnt;
1718 answer->bcnt = a->bcnt - b->bcnt;
1719}
1720
1721int
Rusty Russell79dee072000-05-02 16:45:16 +00001722TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001723{
1724 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001725 STRUCT_REPLACE *repl;
1726 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001727 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001728 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001729
1730 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001731
1732 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1733 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1734
Marc Bouchere6869a82000-03-20 06:03:29 +00001735#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001736 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001737#endif
1738
1739 /* Don't commit if nothing changed. */
1740 if (!(*handle)->changed)
1741 goto finished;
1742
1743 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1744 if (!repl) {
1745 errno = ENOMEM;
1746 return 0;
1747 }
1748
1749 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001750 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001751 * (*handle)->info.num_entries);
1752 if (!repl->counters) {
1753 free(repl);
1754 errno = ENOMEM;
1755 return 0;
1756 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001757
Marc Bouchere6869a82000-03-20 06:03:29 +00001758 /* These are the counters we're going to put back, later. */
1759 newcounters = malloc(counterlen);
1760 if (!newcounters) {
1761 free(repl->counters);
1762 free(repl);
1763 errno = ENOMEM;
1764 return 0;
1765 }
1766
1767 strcpy(repl->name, (*handle)->info.name);
1768 repl->num_entries = (*handle)->new_number;
1769 repl->size = (*handle)->entries.size;
1770 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1771 sizeof(repl->hook_entry));
1772 memcpy(repl->underflow, (*handle)->info.underflow,
1773 sizeof(repl->underflow));
1774 repl->num_counters = (*handle)->info.num_entries;
1775 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001776 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001777 (*handle)->entries.size);
1778
Rusty Russell79dee072000-05-02 16:45:16 +00001779 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001780 sizeof(*repl) + (*handle)->entries.size) < 0) {
1781 free(repl->counters);
1782 free(repl);
1783 free(newcounters);
1784 return 0;
1785 }
1786
1787 /* Put counters back. */
1788 strcpy(newcounters->name, (*handle)->info.name);
1789 newcounters->num_counters = (*handle)->new_number;
1790 for (i = 0; i < (*handle)->new_number; i++) {
1791 unsigned int mappos = (*handle)->counter_map[i].mappos;
1792 switch ((*handle)->counter_map[i].maptype) {
1793 case COUNTER_MAP_NOMAP:
1794 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001795 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001796 break;
1797
1798 case COUNTER_MAP_NORMAL_MAP:
1799 /* Original read: X.
1800 * Atomic read on replacement: X + Y.
1801 * Currently in kernel: Z.
1802 * Want in kernel: X + Y + Z.
1803 * => Add in X + Y
1804 * => Add in replacement read.
1805 */
1806 newcounters->counters[i] = repl->counters[mappos];
1807 break;
1808
1809 case COUNTER_MAP_ZEROED:
1810 /* Original read: X.
1811 * Atomic read on replacement: X + Y.
1812 * Currently in kernel: Z.
1813 * Want in kernel: Y + Z.
1814 * => Add in Y.
1815 * => Add in (replacement read - original read).
1816 */
1817 subtract_counters(&newcounters->counters[i],
1818 &repl->counters[mappos],
1819 &index2entry(*handle, i)->counters);
1820 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001821
1822 case COUNTER_MAP_SET:
1823 /* Want to set counter (iptables-restore) */
1824
1825 memcpy(&newcounters->counters[i],
1826 &index2entry(*handle, i)->counters,
1827 sizeof(STRUCT_COUNTERS));
1828
1829 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001830 }
1831 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001832
1833#ifdef KERNEL_64_USERSPACE_32
1834 {
1835 /* Kernel will think that pointer should be 64-bits, and get
1836 padding. So we accomodate here (assumption: alignment of
1837 `counters' is on 64-bit boundary). */
1838 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1839 if ((unsigned long)&newcounters->counters % 8 != 0) {
1840 fprintf(stderr,
1841 "counters alignment incorrect! Mail rusty!\n");
1842 abort();
1843 }
1844 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001845 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001846#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001847
Rusty Russell79dee072000-05-02 16:45:16 +00001848 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1849 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001850 free(repl->counters);
1851 free(repl);
1852 free(newcounters);
1853 return 0;
1854 }
1855
1856 free(repl->counters);
1857 free(repl);
1858 free(newcounters);
1859
1860 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001861 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001862 return 1;
1863}
1864
1865/* Get raw socket. */
1866int
Rusty Russell79dee072000-05-02 16:45:16 +00001867TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001868{
1869 return sockfd;
1870}
1871
1872/* Translates errno numbers into more human-readable form than strerror. */
1873const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001874TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001875{
1876 unsigned int i;
1877 struct table_struct {
1878 void *fn;
1879 int err;
1880 const char *message;
1881 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001882 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001883 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001884 { TC_INIT, ENOENT,
1885 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001886 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1887 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1888 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001889 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001890 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1891 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1892 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1893 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001894 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1895 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001896 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1897 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001898 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001899 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001900 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001901 { TC_CHECK_PACKET, ENOSYS,
1902 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001903 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001904 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001905 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001906 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001907 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001908 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001909 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001910
1911 { NULL, 0, "Incompatible with this kernel" },
1912 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1913 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1914 { NULL, ENOMEM, "Memory allocation problem" },
1915 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001916 };
1917
1918 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1919 if ((!table[i].fn || table[i].fn == iptc_fn)
1920 && table[i].err == err)
1921 return table[i].message;
1922 }
1923
1924 return strerror(err);
1925}