blob: e0cee2dfb3d0891be94a58f637d9a75dbfcd9053 [file] [log] [blame]
Harald Welte3ea8f402003-06-23 18:25:59 +00001/* Library which manipulates firewall rules. Version $Revision: 1.38 $ */
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>:
18 * - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
19 * 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
Rusty Russell4e242f82000-05-31 06:33:50 +000027#ifndef __OPTIMIZE__
Harald Welteec81ca72001-05-26 04:35:49 +000028STRUCT_ENTRY_TARGET *
Rusty Russell79dee072000-05-02 16:45:16 +000029GET_TARGET(STRUCT_ENTRY *e)
Rusty Russell30fd6e52000-04-23 09:16:06 +000030{
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
Rusty Russell30fd6e52000-04-23 09:16:06 +000067struct chain_cache
68{
Rusty Russell79dee072000-05-02 16:45:16 +000069 char name[TABLE_MAXNAMELEN];
Rusty Russell30fd6e52000-04-23 09:16:06 +000070 /* This is the first rule in chain. */
Harald Welte3ea8f402003-06-23 18:25:59 +000071 unsigned int start_off;
Rusty Russell30fd6e52000-04-23 09:16:06 +000072 /* Last rule in chain */
Harald Welte3ea8f402003-06-23 18:25:59 +000073 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;
80 /* 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
83 struct counter_map *counter_map;
84 /* 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
Rusty Russell79dee072000-05-02 16:45:16 +0000167static inline STRUCT_ENTRY *
168get_entry(TC_HANDLE_T h, unsigned int offset)
Marc Bouchere6869a82000-03-20 06:03:29 +0000169{
Rusty Russell725d97a2000-07-07 08:54:22 +0000170 return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000171}
172
173static inline unsigned long
Rusty Russell79dee072000-05-02 16:45:16 +0000174entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
Marc Bouchere6869a82000-03-20 06:03:29 +0000175{
Harald Welte3ea8f402003-06-23 18:25:59 +0000176 return (void *)e - (void *)h->entries.entrytable;
Marc Bouchere6869a82000-03-20 06:03:29 +0000177}
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
Marc Bouchere6869a82000-03-20 06:03:29 +0000198static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000199get_errorlabel(TC_HANDLE_T h, unsigned int offset)
Marc Bouchere6869a82000-03-20 06:03:29 +0000200{
Rusty Russell79dee072000-05-02 16:45:16 +0000201 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000202
203 e = get_entry(h, offset);
Rusty Russell67088e72000-05-10 01:18:57 +0000204 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000205 fprintf(stderr, "ERROR: offset %u not an error node!\n",
206 offset);
207 abort();
208 }
209
Rusty Russell79dee072000-05-02 16:45:16 +0000210 return (const char *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000211}
212
213/* Allocate handle of given size */
Rusty Russell79dee072000-05-02 16:45:16 +0000214static TC_HANDLE_T
Marc Bouchere6869a82000-03-20 06:03:29 +0000215alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
216{
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;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000230 h->cache_num_chains = 0;
231 h->cache_chain_heads = NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000232 h->counter_map = (void *)h
Rusty Russell79dee072000-05-02 16:45:16 +0000233 + sizeof(STRUCT_TC_HANDLE)
Marc Bouchere6869a82000-03-20 06:03:29 +0000234 + size;
235 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;
Marc Bouchere6869a82000-03-20 06:03:29 +0000246 unsigned int i;
247 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
272 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;
298 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
303 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;
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000324 if ((*h)->cache_chain_heads)
325 free((*h)->cache_chain_heads);
326 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
Rusty Russell30fd6e52000-04-23 09:16:06 +0000379static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000380add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000381{
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 */
Harald Welte3ea8f402003-06-23 18:25:59 +0000387 h->cache_chain_heads[h->cache_num_chains-1].end_off =
388 entry2offset(h, *prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000389 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 */
Rusty Russell67088e72000-05-10 01:18:57 +0000394 if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000395 /* prev was last entry in previous chain */
Harald Welte3ea8f402003-06-23 18:25:59 +0000396 h->cache_chain_heads[h->cache_num_chains-1].end_off
397 = entry2offset(h, *prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000398
399 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
Rusty Russell79dee072000-05-02 16:45:16 +0000400 (const char *)GET_TARGET(e)->data);
Harald Welte3ea8f402003-06-23 18:25:59 +0000401 h->cache_chain_heads[h->cache_num_chains].start_off
402 = entry2offset(h, (void *)e + e->next_offset);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000403 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 */
Harald Welte3ea8f402003-06-23 18:25:59 +0000407 h->cache_chain_heads[h->cache_num_chains-1].end_off
408 = entry2offset(h, *prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000409
410 strcpy(h->cache_chain_heads[h->cache_num_chains].name,
411 h->hooknames[builtin-1]);
Harald Welte3ea8f402003-06-23 18:25:59 +0000412 h->cache_chain_heads[h->cache_num_chains].start_off
413 = entry2offset(h, (void *)e);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000414 h->cache_num_chains++;
415 }
416
417 *prev = e;
418 return 0;
419}
420
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}
426
Rusty Russell79dee072000-05-02 16:45:16 +0000427static int populate_cache(TC_HANDLE_T h)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000428{
429 unsigned int i;
Rusty Russell79dee072000-05-02 16:45:16 +0000430 STRUCT_ENTRY *prev;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000431
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 */
Rusty Russell79dee072000-05-02 16:45:16 +0000444 for (i = 0; i < NUMHOOKS; i++) {
Rusty Russell30fd6e52000-04-23 09:16:06 +0000445 if (h->info.valid_hooks & (1 << i))
446 h->cache_num_builtins++;
447 }
448
449 prev = NULL;
Rusty Russell725d97a2000-07-07 08:54:22 +0000450 ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
Rusty Russell79dee072000-05-02 16:45:16 +0000451 add_chain, h, &prev);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000452
Rusty Russell30fd6e52000-04-23 09:16:06 +0000453 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
Harald Weltefbc85232003-06-24 17:37:21 +0000460static 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 if (i+1 >= h->cache_num_chains)
483 continue;
484 start = &h->cache_chain_heads[i+1];
485 size = (h->cache_num_chains-i)
486 * sizeof(struct chain_cache);
487 memmove(cc, start, size);
488
489 /* iterate over same index again, since
490 * it is now a different chain */
491 i--;
492 continue;
493 }
494 }
495
496 if (cc->start_off > offset)
497 cc->start_off += delta;
498
499 if (cc->end_off >= offset)
500 cc->end_off += delta;
501 }
502 /* HW_FIXME: sorting might be needed, but just in case a new chain was
503 * added */
504
505 return 1;
506}
507
508static int
509add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
510 unsigned int end_off)
511{
512 struct chain_cache *ccs = realloc(h->cache_chain_heads,
513 (h->new_number / 2 + 4 + 1)
514 * sizeof(struct chain_cache));
515 struct chain_cache *newcc;
516
517 if (!ccs)
518 return 0;
519
520 h->cache_chain_heads = ccs;
521 newcc = &h->cache_chain_heads[h->cache_num_chains];
522 h->cache_num_chains++;
523
524 strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
525 newcc->start_off = start_off;
526 newcc->end_off = end_off;
527
528 return 1;
529}
530
Rusty Russell30fd6e52000-04-23 09:16:06 +0000531/* Returns cache ptr if found, otherwise NULL. */
532static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000533find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000534{
Rusty Russell849779c2000-04-23 15:51:51 +0000535 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000536
537 if (handle->cache_chain_heads == NULL
538 && !populate_cache(handle))
539 return NULL;
540
Rusty Russell849779c2000-04-23 15:51:51 +0000541 /* FIXME: Linear search through builtins, then binary --RR */
542 for (i = 0; i < handle->cache_num_chains; i++) {
543 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
544 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000545 }
546
Rusty Russell849779c2000-04-23 15:51:51 +0000547 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000548}
549
Marc Bouchere6869a82000-03-20 06:03:29 +0000550/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000551int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000552{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000553 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000554}
555
556/* Returns the position of the final (ie. unconditional) element. */
557static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000558get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000559{
560 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000561 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000562
563 last_off = start;
564 e = get_entry(handle, start);
565
566 /* Terminate when we meet a error label or a hook entry. */
567 for (off = start + e->next_offset;
568 off < handle->entries.size;
569 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000570 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000571 unsigned int i;
572
573 e = get_entry(handle, off);
574
575 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000576 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000577 if ((handle->info.valid_hooks & (1 << i))
578 && off == handle->info.hook_entry[i])
579 return last_off;
580 }
581
582 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000583 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000584 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000585 return last_off;
586 }
587 /* SHOULD NEVER HAPPEN */
588 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
589 handle->entries.size, off);
590 abort();
591}
592
Rusty Russell30fd6e52000-04-23 09:16:06 +0000593/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000594const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000595TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000596{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000597 if ((*handle)->cache_chain_heads == NULL
598 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000599 return NULL;
600
Rusty Russell30fd6e52000-04-23 09:16:06 +0000601 (*handle)->cache_chain_iteration
602 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000603
Rusty Russell30fd6e52000-04-23 09:16:06 +0000604 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000605}
606
Rusty Russell30fd6e52000-04-23 09:16:06 +0000607/* Iterator functions to run through the chains. Returns NULL at end. */
608const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000609TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000610{
611 (*handle)->cache_chain_iteration++;
612
613 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000614 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000615 return NULL;
616
617 return (*handle)->cache_chain_iteration->name;
618}
619
620/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000621const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000622TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000623{
624 struct chain_cache *c;
625
626 c = find_label(chain, *handle);
627 if (!c) {
628 errno = ENOENT;
629 return NULL;
630 }
631
632 /* Empty chain: single return/policy rule */
Harald Welte3ea8f402003-06-23 18:25:59 +0000633 if (c->start_off == c->end_off)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000634 return NULL;
635
Harald Welte3ea8f402003-06-23 18:25:59 +0000636 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
637 return offset2entry(*handle, c->start_off);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000638}
639
640/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000641const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000642TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000643{
644 if ((void *)prev + prev->next_offset
645 == (void *)(*handle)->cache_rule_end)
646 return NULL;
647
648 return (void *)prev + prev->next_offset;
649}
650
651#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000652/* How many rules in this chain? */
653unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000654TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000655{
656 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000657 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000658
659 CHECK(*handle);
660 if (!find_label(&off, chain, *handle)) {
661 errno = ENOENT;
662 return (unsigned int)-1;
663 }
664
665 start = get_entry(*handle, off);
666 end = get_entry(*handle, get_chain_end(*handle, off));
667
668 return entry2index(*handle, end) - entry2index(*handle, start);
669}
670
671/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000672const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
673 unsigned int n,
674 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000675{
676 unsigned int pos = 0, chainindex;
677
678 CHECK(*handle);
679 if (!find_label(&pos, chain, *handle)) {
680 errno = ENOENT;
681 return NULL;
682 }
683
684 chainindex = entry2index(*handle, get_entry(*handle, pos));
685
686 return index2entry(*handle, chainindex + n);
687}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000688#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000689
Rusty Russell30fd6e52000-04-23 09:16:06 +0000690static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000691target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000692{
693 int spos;
694 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000695 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000696
Rusty Russell30fd6e52000-04-23 09:16:06 +0000697 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000698 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000699
Rusty Russell79dee072000-05-02 16:45:16 +0000700 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
701 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000702
703 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000704 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000705 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000706 if (spos == RETURN)
707 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000708 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000709 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000710 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000711 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000712 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000713 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000714
715 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
716 entry2offset(handle, e), handle->entries.size,
717 spos);
718 abort();
719 }
720
721 jumpto = get_entry(handle, spos);
722
723 /* Fall through rule */
724 if (jumpto == (void *)e + e->next_offset)
725 return "";
726
727 /* Must point to head of a chain: ie. after error rule */
728 labelidx = entry2index(handle, jumpto) - 1;
729 return get_errorlabel(handle, index2offset(handle, labelidx));
730}
731
732/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000733const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
734 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000735{
Marc Bouchere6869a82000-03-20 06:03:29 +0000736 return target_name(*handle, e);
737}
738
739/* Is this a built-in chain? Actually returns hook + 1. */
740int
Rusty Russell79dee072000-05-02 16:45:16 +0000741TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000742{
743 unsigned int i;
744
Rusty Russell79dee072000-05-02 16:45:16 +0000745 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000746 if ((handle->info.valid_hooks & (1 << i))
747 && handle->hooknames[i]
748 && strcmp(handle->hooknames[i], chain) == 0)
749 return i+1;
750 }
751 return 0;
752}
753
754/* Get the policy of a given built-in chain */
755const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000756TC_GET_POLICY(const char *chain,
757 STRUCT_COUNTERS *counters,
758 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000759{
760 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000761 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000762 int hook;
763
Rusty Russell79dee072000-05-02 16:45:16 +0000764 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000765 if (hook != 0)
766 start = (*handle)->info.hook_entry[hook-1];
767 else
768 return NULL;
769
770 e = get_entry(*handle, get_chain_end(*handle, start));
771 *counters = e->counters;
772
773 return target_name(*handle, e);
774}
775
Harald Weltefbc85232003-06-24 17:37:21 +0000776static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000777correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000778 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000779 unsigned int offset, int delta_offset)
780{
Rusty Russell79dee072000-05-02 16:45:16 +0000781 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000782 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000783
784 /* Trap: insert of fall-through rule. Don't change fall-through
785 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000786 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000787 && t->verdict > (int)offset
788 && !(curr == offset &&
789 t->verdict == curr + e->next_offset)) {
790 t->verdict += delta_offset;
791 }
792
793 return 0;
794}
795
796/* Adjusts standard verdict jump positions after an insertion/deletion. */
797static int
Rusty Russell79dee072000-05-02 16:45:16 +0000798set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000799{
Rusty Russell725d97a2000-07-07 08:54:22 +0000800 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000801 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000802 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000803 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000804
Rusty Russell175f6412000-03-24 09:32:20 +0000805 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000806 return 1;
807}
808
809/* If prepend is set, then we are prepending to a chain: if the
810 * insertion position is an entry point, keep the entry point. */
811static int
812insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000813 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000814 unsigned int offset, unsigned int num_rules_offset,
815 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000816 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000817{
Rusty Russell79dee072000-05-02 16:45:16 +0000818 TC_HANDLE_T newh;
819 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000820 unsigned int i;
821
822 if (offset >= (*handle)->entries.size) {
823 errno = EINVAL;
824 return 0;
825 }
826
827 newinfo = (*handle)->info;
828
829 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000830 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000831 /* Entry points to START of chain, so keep same if
832 inserting on at that point. */
833 if ((*handle)->info.hook_entry[i] > offset)
834 newinfo.hook_entry[i] += rules_size;
835
836 /* Underflow always points to END of chain (policy),
837 so if something is inserted at same point, it
838 should be advanced. */
839 if ((*handle)->info.underflow[i] >= offset)
840 newinfo.underflow[i] += rules_size;
841 }
842
843 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000844 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000845 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000846 if (!newh)
847 return 0;
848 newh->info = newinfo;
849
850 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000851 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000852 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000853 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000854 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000855 memcpy((char *)newh->entries.entrytable + offset + rules_size,
856 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000857 (*handle)->entries.size - offset);
858
859 /* Move counter map. */
860 /* Copy pre... */
861 memcpy(newh->counter_map, (*handle)->counter_map,
862 sizeof(struct counter_map) * num_rules_offset);
863 /* ... copy post */
864 memcpy(newh->counter_map + num_rules_offset + num_rules,
865 (*handle)->counter_map + num_rules_offset,
866 sizeof(struct counter_map) * ((*handle)->new_number
867 - num_rules_offset));
868 /* Set intermediates to no counter copy */
869 for (i = 0; i < num_rules; i++)
870 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000871 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000872
873 newh->new_number = (*handle)->new_number + num_rules;
874 newh->entries.size = (*handle)->entries.size + rules_size;
875 newh->hooknames = (*handle)->hooknames;
876
Harald Weltefbc85232003-06-24 17:37:21 +0000877 newh->cache_chain_heads = (*handle)->cache_chain_heads;
878 newh->cache_num_builtins = (*handle)->cache_num_builtins;
879 newh->cache_num_chains = (*handle)->cache_num_chains;
880 newh->cache_rule_end = (*handle)->cache_rule_end;
881 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
882 if (!correct_cache(newh, offset, rules_size)) {
883 free(newh);
884 return 0;
885 }
886
Marc Bouchere6869a82000-03-20 06:03:29 +0000887 free(*handle);
888 *handle = newh;
889
890 return set_verdict(offset, rules_size, handle);
891}
892
893static int
894delete_rules(unsigned int num_rules, unsigned int rules_size,
895 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000896 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000897{
898 unsigned int i;
899
900 if (offset + rules_size > (*handle)->entries.size) {
901 errno = EINVAL;
902 return 0;
903 }
904
905 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000906 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000907 /* In practice, we never delete up to a hook entry,
908 since the built-in chains are always first,
909 so these two are never equal */
910 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
911 (*handle)->info.hook_entry[i] -= rules_size;
912 else if ((*handle)->info.hook_entry[i] > offset) {
913 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
914 i, (*handle)->info.hook_entry[i], offset);
915 abort();
916 }
917
918 /* Underflow points to policy (terminal) rule in
919 built-in, so sequality is valid here (when deleting
920 the last rule). */
921 if ((*handle)->info.underflow[i] >= offset + rules_size)
922 (*handle)->info.underflow[i] -= rules_size;
923 else if ((*handle)->info.underflow[i] > offset) {
924 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
925 i, (*handle)->info.underflow[i], offset);
926 abort();
927 }
928 }
929
930 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000931 memmove((char *)(*handle)->entries.entrytable + offset,
932 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000933 (*handle)->entries.size - (offset + rules_size));
934
935 /* Move the counter map down. */
936 memmove(&(*handle)->counter_map[num_rules_offset],
937 &(*handle)->counter_map[num_rules_offset + num_rules],
938 sizeof(struct counter_map)
939 * ((*handle)->new_number - (num_rules + num_rules_offset)));
940
941 /* Fix numbers */
942 (*handle)->new_number -= num_rules;
943 (*handle)->entries.size -= rules_size;
944
Harald Weltefbc85232003-06-24 17:37:21 +0000945 /* Fix the chain cache */
946 if (!correct_cache(*handle, offset, -(int)rules_size))
947 return 0;
948
Marc Bouchere6869a82000-03-20 06:03:29 +0000949 return set_verdict(offset, -(int)rules_size, handle);
950}
951
952static int
Rusty Russell79dee072000-05-02 16:45:16 +0000953standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000954{
Rusty Russell79dee072000-05-02 16:45:16 +0000955 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000956
Rusty Russell79dee072000-05-02 16:45:16 +0000957 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000958
Rusty Russell67088e72000-05-10 01:18:57 +0000959 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000960 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000961 errno = EINVAL;
962 return 0;
963 }
964 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000965 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
966 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000967 t->verdict = verdict;
968
969 return 1;
970}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000971
Marc Bouchere6869a82000-03-20 06:03:29 +0000972static int
Rusty Russell79dee072000-05-02 16:45:16 +0000973map_target(const TC_HANDLE_T handle,
974 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000975 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000976 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000977{
Rusty Russell79dee072000-05-02 16:45:16 +0000978 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000979
980 /* Save old target (except data, which we don't change, except for
981 standard case, where we don't care). */
982 *old = *t;
983
984 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000985 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000986 return standard_map(e, offset + e->next_offset);
987 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000988 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000989 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000990 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000991 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000992 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000993 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000994 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
995 return standard_map(e, RETURN);
996 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000997 /* Can't jump to builtins. */
998 errno = EINVAL;
999 return 0;
1000 } else {
1001 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001002 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001003
Rusty Russell228e98d2000-04-27 10:28:06 +00001004 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +00001005 if (c)
Harald Welte3ea8f402003-06-23 18:25:59 +00001006 return standard_map(e, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001007 }
1008
1009 /* Must be a module? If not, kernel will reject... */
1010 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001011 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001012 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001013 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +00001014 return 1;
1015}
1016
1017static void
Rusty Russell79dee072000-05-02 16:45:16 +00001018unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +00001019{
Rusty Russell79dee072000-05-02 16:45:16 +00001020 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001021
1022 /* Save old target (except data, which we don't change, except for
1023 standard case, where we don't care). */
1024 *t = *old;
1025}
1026
1027/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
1028int
Rusty Russell79dee072000-05-02 16:45:16 +00001029TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1030 const STRUCT_ENTRY *e,
1031 unsigned int rulenum,
1032 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001033{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001034 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +00001035 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001036 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +00001037 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001038 int ret;
1039
Rusty Russell79dee072000-05-02 16:45:16 +00001040 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001041 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001042 errno = ENOENT;
1043 return 0;
1044 }
1045
Harald Welte3ea8f402003-06-23 18:25:59 +00001046 chainindex = offset2index(*handle, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001047
Rusty Russell14a1c912000-08-26 04:41:10 +00001048 tmp = index2entry(*handle, chainindex + rulenum);
Harald Welte3ea8f402003-06-23 18:25:59 +00001049 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001050 errno = E2BIG;
1051 return 0;
1052 }
1053 offset = index2offset(*handle, chainindex + rulenum);
1054
1055 /* Mapping target actually alters entry, but that's
1056 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +00001057 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001058 return 0;
1059
1060 ret = insert_rules(1, e->next_offset, e, offset,
1061 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001062 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001063 return ret;
1064}
1065
1066/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1067int
Rusty Russell79dee072000-05-02 16:45:16 +00001068TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1069 const STRUCT_ENTRY *e,
1070 unsigned int rulenum,
1071 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001072{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001073 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +00001074 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001075 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +00001076 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001077 int ret;
1078
Rusty Russell79dee072000-05-02 16:45:16 +00001079 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001080
Rusty Russell30fd6e52000-04-23 09:16:06 +00001081 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001082 errno = ENOENT;
1083 return 0;
1084 }
1085
Harald Welte3ea8f402003-06-23 18:25:59 +00001086 chainindex = offset2index(*handle, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001087
Rusty Russell14a1c912000-08-26 04:41:10 +00001088 tmp = index2entry(*handle, chainindex + rulenum);
Harald Welte3ea8f402003-06-23 18:25:59 +00001089 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001090 errno = E2BIG;
1091 return 0;
1092 }
1093
1094 offset = index2offset(*handle, chainindex + rulenum);
1095 /* Replace = delete and insert. */
1096 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1097 offset, chainindex + rulenum, handle))
1098 return 0;
1099
Rusty Russell79dee072000-05-02 16:45:16 +00001100 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001101 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001102
1103 ret = insert_rules(1, e->next_offset, e, offset,
1104 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001105 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001106 return ret;
1107}
1108
1109/* Append entry `fw' to chain `chain'. Equivalent to insert with
1110 rulenum = length of chain. */
1111int
Rusty Russell79dee072000-05-02 16:45:16 +00001112TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1113 const STRUCT_ENTRY *e,
1114 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001115{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001116 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001117 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001118 int ret;
1119
Rusty Russell79dee072000-05-02 16:45:16 +00001120 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001121 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001122 errno = ENOENT;
1123 return 0;
1124 }
1125
Rusty Russell79dee072000-05-02 16:45:16 +00001126 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Harald Welte3ea8f402003-06-23 18:25:59 +00001127 c->end_off, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001128 return 0;
1129
Harald Welte3ea8f402003-06-23 18:25:59 +00001130 ret = insert_rules(1, e->next_offset, e, c->end_off,
1131 offset2index(*handle, c->end_off), 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001132 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001133 return ret;
1134}
1135
1136static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001137match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001138 const unsigned char *a_elems,
1139 const unsigned char *b_elems,
1140 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001141{
Rusty Russell79dee072000-05-02 16:45:16 +00001142 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001143 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001144
1145 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001146 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001147
Rusty Russell228e98d2000-04-27 10:28:06 +00001148 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001149 return 1;
1150
Rusty Russell228e98d2000-04-27 10:28:06 +00001151 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001152 return 1;
1153
Rusty Russell73ef09b2000-07-03 10:24:04 +00001154 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001155
Rusty Russell73ef09b2000-07-03 10:24:04 +00001156 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001157 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001158 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001159 *maskptr += i;
1160 return 0;
1161}
1162
1163static inline int
1164target_different(const unsigned char *a_targdata,
1165 const unsigned char *b_targdata,
1166 unsigned int tdatasize,
1167 const unsigned char *mask)
1168{
1169 unsigned int i;
1170 for (i = 0; i < tdatasize; i++)
1171 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1172 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001173
1174 return 0;
1175}
1176
Rusty Russell79dee072000-05-02 16:45:16 +00001177static int
1178is_same(const STRUCT_ENTRY *a,
1179 const STRUCT_ENTRY *b,
1180 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001181
1182/* Delete the first rule in `chain' which matches `fw'. */
1183int
Rusty Russell79dee072000-05-02 16:45:16 +00001184TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1185 const STRUCT_ENTRY *origfw,
1186 unsigned char *matchmask,
1187 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001188{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001189 unsigned int offset;
1190 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001191 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001192
Rusty Russell79dee072000-05-02 16:45:16 +00001193 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001194 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001195 errno = ENOENT;
1196 return 0;
1197 }
1198
1199 fw = malloc(origfw->next_offset);
1200 if (fw == NULL) {
1201 errno = ENOMEM;
1202 return 0;
1203 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001204
Harald Welte3ea8f402003-06-23 18:25:59 +00001205 for (offset = c->start_off; offset < c->end_off;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001206 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001207 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001208
1209 memcpy(fw, origfw, origfw->next_offset);
1210
1211 /* FIXME: handle this in is_same --RR */
1212 if (!map_target(*handle, fw, offset, &discard)) {
1213 free(fw);
1214 return 0;
1215 }
1216 e = get_entry(*handle, offset);
1217
1218#if 0
1219 printf("Deleting:\n");
1220 dump_entry(newe);
1221#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001222 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001223 int ret;
1224 ret = delete_rules(1, e->next_offset,
1225 offset, entry2index(*handle, e),
1226 handle);
1227 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001228 return ret;
1229 }
1230 }
1231
1232 free(fw);
1233 errno = ENOENT;
1234 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001235}
Marc Bouchere6869a82000-03-20 06:03:29 +00001236
1237/* Delete the rule in position `rulenum' in `chain'. */
1238int
Rusty Russell79dee072000-05-02 16:45:16 +00001239TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1240 unsigned int rulenum,
1241 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001242{
Marc Bouchere6869a82000-03-20 06:03:29 +00001243 unsigned int index;
1244 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001245 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001246 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001247
Rusty Russell79dee072000-05-02 16:45:16 +00001248 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001249 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001250 errno = ENOENT;
1251 return 0;
1252 }
1253
Harald Welte3ea8f402003-06-23 18:25:59 +00001254 index = offset2index(*handle, c->start_off) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001255
Harald Welte3ea8f402003-06-23 18:25:59 +00001256 if (index >= offset2index(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001257 errno = E2BIG;
1258 return 0;
1259 }
1260
1261 e = index2entry(*handle, index);
1262 if (e == NULL) {
1263 errno = EINVAL;
1264 return 0;
1265 }
1266
1267 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1268 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001269 return ret;
1270}
1271
1272/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1273 NULL and sets errno. */
1274const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001275TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1276 STRUCT_ENTRY *entry,
1277 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001278{
1279 errno = ENOSYS;
1280 return NULL;
1281}
1282
1283/* Flushes the entries in the given chain (ie. empties chain). */
1284int
Rusty Russell79dee072000-05-02 16:45:16 +00001285TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001286{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001287 unsigned int startindex, endindex;
Harald Welte3ea8f402003-06-23 18:25:59 +00001288 STRUCT_ENTRY *startentry, *endentry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001289 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001290 int ret;
1291
Rusty Russell79dee072000-05-02 16:45:16 +00001292 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001293 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001294 errno = ENOENT;
1295 return 0;
1296 }
Harald Welte3ea8f402003-06-23 18:25:59 +00001297 startindex = offset2index(*handle, c->start_off);
1298 endindex = offset2index(*handle, c->end_off);
1299 startentry = offset2entry(*handle, c->start_off);
1300 endentry = offset2entry(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001301
1302 ret = delete_rules(endindex - startindex,
Harald Welte3ea8f402003-06-23 18:25:59 +00001303 (char *)endentry - (char *)startentry,
1304 c->start_off, startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001305 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001306 return ret;
1307}
1308
1309/* Zeroes the counters in a chain. */
1310int
Rusty Russell79dee072000-05-02 16:45:16 +00001311TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001312{
1313 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001314 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001315
Rusty Russell30fd6e52000-04-23 09:16:06 +00001316 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001317 errno = ENOENT;
1318 return 0;
1319 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001320
Harald Welte3ea8f402003-06-23 18:25:59 +00001321 i = offset2index(*handle, c->start_off);
1322 end = offset2index(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001323
1324 for (; i <= end; i++) {
1325 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1326 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1327 }
Rusty Russell175f6412000-03-24 09:32:20 +00001328 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001329
Marc Bouchere6869a82000-03-20 06:03:29 +00001330 return 1;
1331}
1332
Harald Welte1cef74d2001-01-05 15:22:59 +00001333STRUCT_COUNTERS *
1334TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1335 unsigned int rulenum,
1336 TC_HANDLE_T *handle)
1337{
1338 STRUCT_ENTRY *e;
1339 struct chain_cache *c;
1340 unsigned int chainindex, end;
1341
1342 iptc_fn = TC_READ_COUNTER;
1343 CHECK(*handle);
1344
1345 if (!(c = find_label(chain, *handle))) {
1346 errno = ENOENT;
1347 return NULL;
1348 }
1349
Harald Welte3ea8f402003-06-23 18:25:59 +00001350 chainindex = offset2index(*handle, c->start_off);
1351 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001352
1353 if (chainindex + rulenum > end) {
1354 errno = E2BIG;
1355 return NULL;
1356 }
1357
1358 e = index2entry(*handle, chainindex + rulenum);
1359
1360 return &e->counters;
1361}
1362
1363int
1364TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1365 unsigned int rulenum,
1366 TC_HANDLE_T *handle)
1367{
1368 STRUCT_ENTRY *e;
1369 struct chain_cache *c;
1370 unsigned int chainindex, end;
1371
1372 iptc_fn = TC_ZERO_COUNTER;
1373 CHECK(*handle);
1374
1375 if (!(c = find_label(chain, *handle))) {
1376 errno = ENOENT;
1377 return 0;
1378 }
1379
Harald Welte3ea8f402003-06-23 18:25:59 +00001380 chainindex = offset2index(*handle, c->start_off);
1381 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001382
1383 if (chainindex + rulenum > end) {
1384 errno = E2BIG;
1385 return 0;
1386 }
1387
1388 e = index2entry(*handle, chainindex + rulenum);
1389
1390 if ((*handle)->counter_map[chainindex + rulenum].maptype
1391 == COUNTER_MAP_NORMAL_MAP) {
1392 (*handle)->counter_map[chainindex + rulenum].maptype
1393 = COUNTER_MAP_ZEROED;
1394 }
1395
1396 set_changed(*handle);
1397
1398 return 1;
1399}
1400
1401int
1402TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1403 unsigned int rulenum,
1404 STRUCT_COUNTERS *counters,
1405 TC_HANDLE_T *handle)
1406{
1407 STRUCT_ENTRY *e;
1408 struct chain_cache *c;
1409 unsigned int chainindex, end;
1410
1411 iptc_fn = TC_SET_COUNTER;
1412 CHECK(*handle);
1413
1414 if (!(c = find_label(chain, *handle))) {
1415 errno = ENOENT;
1416 return 0;
1417 }
1418
Harald Welte3ea8f402003-06-23 18:25:59 +00001419 chainindex = offset2index(*handle, c->start_off);
1420 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001421
1422 if (chainindex + rulenum > end) {
1423 errno = E2BIG;
1424 return 0;
1425 }
1426
1427 e = index2entry(*handle, chainindex + rulenum);
1428
1429 (*handle)->counter_map[chainindex + rulenum].maptype
1430 = COUNTER_MAP_SET;
1431
1432 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1433
1434 set_changed(*handle);
1435
1436 return 1;
1437}
1438
Marc Bouchere6869a82000-03-20 06:03:29 +00001439/* Creates a new chain. */
1440/* To create a chain, create two rules: error node and unconditional
1441 * return. */
1442int
Rusty Russell79dee072000-05-02 16:45:16 +00001443TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001444{
Marc Bouchere6869a82000-03-20 06:03:29 +00001445 int ret;
1446 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001447 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001448 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001449 STRUCT_ENTRY ret;
1450 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001451 } newc;
Harald Weltefbc85232003-06-24 17:37:21 +00001452 unsigned int destination;
Marc Bouchere6869a82000-03-20 06:03:29 +00001453
Rusty Russell79dee072000-05-02 16:45:16 +00001454 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001455
1456 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1457 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001458 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001459 || strcmp(chain, LABEL_DROP) == 0
1460 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001461 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001462 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001463 errno = EEXIST;
1464 return 0;
1465 }
1466
Rusty Russell79dee072000-05-02 16:45:16 +00001467 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001468 errno = EINVAL;
1469 return 0;
1470 }
1471
1472 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001473 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001474 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001475 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001476 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001477 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001478 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001479 strcpy(newc.name.error, chain);
1480
Rusty Russell79dee072000-05-02 16:45:16 +00001481 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001482 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001483 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001484 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001485 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001486 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001487 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001488 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001489
Harald Weltefbc85232003-06-24 17:37:21 +00001490 destination = index2offset(*handle, (*handle)->new_number -1);
1491
Marc Bouchere6869a82000-03-20 06:03:29 +00001492 /* Add just before terminal entry */
1493 ret = insert_rules(2, sizeof(newc), &newc.head,
Harald Weltefbc85232003-06-24 17:37:21 +00001494 destination,
Marc Bouchere6869a82000-03-20 06:03:29 +00001495 (*handle)->new_number - 1,
1496 0, handle);
Harald Weltefbc85232003-06-24 17:37:21 +00001497
1498 set_changed(*handle);
1499
1500 /* add chain cache info for this chain */
1501 add_chain_cache(*handle, chain,
1502 destination+newc.head.next_offset,
1503 destination+newc.head.next_offset);
1504
Marc Bouchere6869a82000-03-20 06:03:29 +00001505 return ret;
1506}
1507
1508static int
Rusty Russell79dee072000-05-02 16:45:16 +00001509count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001510{
Rusty Russell79dee072000-05-02 16:45:16 +00001511 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001512
Rusty Russell79dee072000-05-02 16:45:16 +00001513 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1514 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001515
1516 if (t->verdict == offset)
1517 (*ref)++;
1518 }
1519
1520 return 0;
1521}
1522
1523/* Get the number of references to this chain. */
1524int
Rusty Russell79dee072000-05-02 16:45:16 +00001525TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1526 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001527{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001528 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001529
Rusty Russell30fd6e52000-04-23 09:16:06 +00001530 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001531 errno = ENOENT;
1532 return 0;
1533 }
1534
1535 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001536 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001537 (*handle)->entries.size,
Harald Welte3ea8f402003-06-23 18:25:59 +00001538 count_ref, c->start_off, ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001539 return 1;
1540}
1541
1542/* Deletes a chain. */
1543int
Rusty Russell79dee072000-05-02 16:45:16 +00001544TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001545{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001546 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001547 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001548 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001549 int ret;
Harald Welte3ea8f402003-06-23 18:25:59 +00001550 STRUCT_ENTRY *start;
Marc Bouchere6869a82000-03-20 06:03:29 +00001551
Rusty Russell79dee072000-05-02 16:45:16 +00001552 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001553 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001554
Rusty Russell79dee072000-05-02 16:45:16 +00001555 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001556
Rusty Russell79dee072000-05-02 16:45:16 +00001557 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001558 errno = EINVAL;
1559 return 0;
1560 }
1561
1562 if (references > 0) {
1563 errno = EMLINK;
1564 return 0;
1565 }
1566
Rusty Russell30fd6e52000-04-23 09:16:06 +00001567 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001568 errno = ENOENT;
1569 return 0;
1570 }
1571
Harald Welte3ea8f402003-06-23 18:25:59 +00001572 if (c->start_off != c->end_off) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001573 errno = ENOTEMPTY;
1574 return 0;
1575 }
1576
1577 /* Need label index: preceeds chain start */
Harald Welte3ea8f402003-06-23 18:25:59 +00001578 labelidx = offset2index(*handle, c->start_off) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001579 labeloff = index2offset(*handle, labelidx);
1580
Harald Welte3ea8f402003-06-23 18:25:59 +00001581 start = offset2entry(*handle, c->start_off);
1582
Marc Bouchere6869a82000-03-20 06:03:29 +00001583 ret = delete_rules(2,
1584 get_entry(*handle, labeloff)->next_offset
Harald Welte3ea8f402003-06-23 18:25:59 +00001585 + start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001586 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001587 return ret;
1588}
1589
1590/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001591int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1592 const IPT_CHAINLABEL newname,
1593 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001594{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001595 unsigned int labeloff, labelidx;
1596 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001597 struct ipt_error_target *t;
1598
Rusty Russell79dee072000-05-02 16:45:16 +00001599 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001600
Harald Welte1de80462000-10-30 12:00:27 +00001601 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1602 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001603 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001604 || strcmp(newname, LABEL_DROP) == 0
1605 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001606 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001607 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001608 errno = EEXIST;
1609 return 0;
1610 }
1611
Rusty Russell30fd6e52000-04-23 09:16:06 +00001612 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001613 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001614 errno = ENOENT;
1615 return 0;
1616 }
1617
Rusty Russell79dee072000-05-02 16:45:16 +00001618 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001619 errno = EINVAL;
1620 return 0;
1621 }
1622
1623 /* Need label index: preceeds chain start */
Harald Welte3ea8f402003-06-23 18:25:59 +00001624 labelidx = offset2index(*handle, c->start_off) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001625 labeloff = index2offset(*handle, labelidx);
1626
1627 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001628 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001629
1630 memset(t->error, 0, sizeof(t->error));
1631 strcpy(t->error, newname);
Harald Weltefbc85232003-06-24 17:37:21 +00001632
1633 /* update chain cache */
1634 memset(c->name, 0, sizeof(c->name));
1635 strcpy(c->name, newname);
1636
Rusty Russell175f6412000-03-24 09:32:20 +00001637 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001638
Marc Bouchere6869a82000-03-20 06:03:29 +00001639 return 1;
1640}
1641
1642/* Sets the policy on a built-in chain. */
1643int
Rusty Russell79dee072000-05-02 16:45:16 +00001644TC_SET_POLICY(const IPT_CHAINLABEL chain,
1645 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001646 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001647 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001648{
1649 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001650 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001651 STRUCT_ENTRY *e;
1652 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001653
Rusty Russell79dee072000-05-02 16:45:16 +00001654 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001655 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001656 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001657 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001658 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001659 return 0;
1660 } else
1661 hook--;
1662
1663 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1664 if (policyoff != (*handle)->info.underflow[hook]) {
1665 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1666 chain, policyoff, (*handle)->info.underflow[hook]);
1667 return 0;
1668 }
1669
1670 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001671 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001672
Rusty Russell79dee072000-05-02 16:45:16 +00001673 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001674 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001675 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001676 t->verdict = -NF_DROP - 1;
1677 else {
1678 errno = EINVAL;
1679 return 0;
1680 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001681
1682 ctrindex = entry2index(*handle, e);
1683
1684 if (counters) {
1685 /* set byte and packet counters */
1686 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1687
1688 (*handle)->counter_map[ctrindex].maptype
1689 = COUNTER_MAP_SET;
1690
1691 } else {
1692 (*handle)->counter_map[ctrindex]
1693 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1694 }
1695
Rusty Russell175f6412000-03-24 09:32:20 +00001696 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001697
Marc Bouchere6869a82000-03-20 06:03:29 +00001698 return 1;
1699}
1700
1701/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001702 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001703 libiptc.c:833: fixed or forbidden register was spilled.
1704 This may be due to a compiler bug or to impossible asm
1705 statements or clauses.
1706*/
1707static void
Rusty Russell79dee072000-05-02 16:45:16 +00001708subtract_counters(STRUCT_COUNTERS *answer,
1709 const STRUCT_COUNTERS *a,
1710 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001711{
1712 answer->pcnt = a->pcnt - b->pcnt;
1713 answer->bcnt = a->bcnt - b->bcnt;
1714}
1715
1716int
Rusty Russell79dee072000-05-02 16:45:16 +00001717TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001718{
1719 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001720 STRUCT_REPLACE *repl;
1721 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001722 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001723 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001724
1725 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001726
1727 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1728 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1729
Marc Bouchere6869a82000-03-20 06:03:29 +00001730#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001731 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001732#endif
1733
1734 /* Don't commit if nothing changed. */
1735 if (!(*handle)->changed)
1736 goto finished;
1737
1738 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1739 if (!repl) {
1740 errno = ENOMEM;
1741 return 0;
1742 }
1743
1744 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001745 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001746 * (*handle)->info.num_entries);
1747 if (!repl->counters) {
1748 free(repl);
1749 errno = ENOMEM;
1750 return 0;
1751 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001752
Marc Bouchere6869a82000-03-20 06:03:29 +00001753 /* These are the counters we're going to put back, later. */
1754 newcounters = malloc(counterlen);
1755 if (!newcounters) {
1756 free(repl->counters);
1757 free(repl);
1758 errno = ENOMEM;
1759 return 0;
1760 }
1761
1762 strcpy(repl->name, (*handle)->info.name);
1763 repl->num_entries = (*handle)->new_number;
1764 repl->size = (*handle)->entries.size;
1765 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1766 sizeof(repl->hook_entry));
1767 memcpy(repl->underflow, (*handle)->info.underflow,
1768 sizeof(repl->underflow));
1769 repl->num_counters = (*handle)->info.num_entries;
1770 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001771 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001772 (*handle)->entries.size);
1773
Rusty Russell79dee072000-05-02 16:45:16 +00001774 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001775 sizeof(*repl) + (*handle)->entries.size) < 0) {
1776 free(repl->counters);
1777 free(repl);
1778 free(newcounters);
1779 return 0;
1780 }
1781
1782 /* Put counters back. */
1783 strcpy(newcounters->name, (*handle)->info.name);
1784 newcounters->num_counters = (*handle)->new_number;
1785 for (i = 0; i < (*handle)->new_number; i++) {
1786 unsigned int mappos = (*handle)->counter_map[i].mappos;
1787 switch ((*handle)->counter_map[i].maptype) {
1788 case COUNTER_MAP_NOMAP:
1789 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001790 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001791 break;
1792
1793 case COUNTER_MAP_NORMAL_MAP:
1794 /* Original read: X.
1795 * Atomic read on replacement: X + Y.
1796 * Currently in kernel: Z.
1797 * Want in kernel: X + Y + Z.
1798 * => Add in X + Y
1799 * => Add in replacement read.
1800 */
1801 newcounters->counters[i] = repl->counters[mappos];
1802 break;
1803
1804 case COUNTER_MAP_ZEROED:
1805 /* Original read: X.
1806 * Atomic read on replacement: X + Y.
1807 * Currently in kernel: Z.
1808 * Want in kernel: Y + Z.
1809 * => Add in Y.
1810 * => Add in (replacement read - original read).
1811 */
1812 subtract_counters(&newcounters->counters[i],
1813 &repl->counters[mappos],
1814 &index2entry(*handle, i)->counters);
1815 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001816
1817 case COUNTER_MAP_SET:
1818 /* Want to set counter (iptables-restore) */
1819
1820 memcpy(&newcounters->counters[i],
1821 &index2entry(*handle, i)->counters,
1822 sizeof(STRUCT_COUNTERS));
1823
1824 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001825 }
1826 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001827
1828#ifdef KERNEL_64_USERSPACE_32
1829 {
1830 /* Kernel will think that pointer should be 64-bits, and get
1831 padding. So we accomodate here (assumption: alignment of
1832 `counters' is on 64-bit boundary). */
1833 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1834 if ((unsigned long)&newcounters->counters % 8 != 0) {
1835 fprintf(stderr,
1836 "counters alignment incorrect! Mail rusty!\n");
1837 abort();
1838 }
1839 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001840 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001841#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001842
Rusty Russell79dee072000-05-02 16:45:16 +00001843 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1844 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001845 free(repl->counters);
1846 free(repl);
1847 free(newcounters);
1848 return 0;
1849 }
1850
1851 free(repl->counters);
1852 free(repl);
1853 free(newcounters);
1854
1855 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001856 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001857 return 1;
1858}
1859
1860/* Get raw socket. */
1861int
Rusty Russell79dee072000-05-02 16:45:16 +00001862TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001863{
1864 return sockfd;
1865}
1866
1867/* Translates errno numbers into more human-readable form than strerror. */
1868const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001869TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001870{
1871 unsigned int i;
1872 struct table_struct {
1873 void *fn;
1874 int err;
1875 const char *message;
1876 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001877 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001878 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001879 { TC_INIT, ENOENT,
1880 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001881 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1882 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1883 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001884 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001885 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1886 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1887 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1888 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001889 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1890 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001891 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1892 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001893 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001894 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001895 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001896 { TC_CHECK_PACKET, ENOSYS,
1897 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001898 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001899 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001900 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001901 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001902 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001903 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001904 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001905
1906 { NULL, 0, "Incompatible with this kernel" },
1907 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1908 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1909 { NULL, ENOMEM, "Memory allocation problem" },
1910 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001911 };
1912
1913 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1914 if ((!table[i].fn || table[i].fn == iptc_fn)
1915 && table[i].err == err)
1916 return table[i].message;
1917 }
1918
1919 return strerror(err);
1920}