blob: 8636c8b003e645ba46e2ecdd7729b4d7aec01635 [file] [log] [blame]
Martin Josefsson0acde1f2003-07-05 20:11:11 +00001/* Library which manipulates firewall rules. Version $Revision: 1.40 $ */
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 */
Martin Josefsson0acde1f2003-07-05 20:11:11 +0000474 if (cc->start_off > offset+delta
475 && cc->end_off < offset) {
Harald Weltefbc85232003-06-24 17:37:21 +0000476 /* 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--;
Martin Josefsson0acde1f2003-07-05 20:11:11 +0000482
483 /* no need for memmove since we are
484 * removing the last entry */
485 if (i >= h->cache_num_chains)
Harald Weltefbc85232003-06-24 17:37:21 +0000486 continue;
Martin Josefsson0acde1f2003-07-05 20:11:11 +0000487
Harald Weltefbc85232003-06-24 17:37:21 +0000488 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);
529 newcc->start_off = start_off;
530 newcc->end_off = end_off;
531
532 return 1;
533}
534
Rusty Russell30fd6e52000-04-23 09:16:06 +0000535/* Returns cache ptr if found, otherwise NULL. */
536static struct chain_cache *
Rusty Russell79dee072000-05-02 16:45:16 +0000537find_label(const char *name, TC_HANDLE_T handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000538{
Rusty Russell849779c2000-04-23 15:51:51 +0000539 unsigned int i;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000540
541 if (handle->cache_chain_heads == NULL
542 && !populate_cache(handle))
543 return NULL;
544
Rusty Russell849779c2000-04-23 15:51:51 +0000545 /* FIXME: Linear search through builtins, then binary --RR */
546 for (i = 0; i < handle->cache_num_chains; i++) {
547 if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
548 return &handle->cache_chain_heads[i];
Rusty Russell30fd6e52000-04-23 09:16:06 +0000549 }
550
Rusty Russell849779c2000-04-23 15:51:51 +0000551 return NULL;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000552}
553
Marc Bouchere6869a82000-03-20 06:03:29 +0000554/* Does this chain exist? */
Rusty Russell79dee072000-05-02 16:45:16 +0000555int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000556{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000557 return find_label(chain, handle) != NULL;
Marc Bouchere6869a82000-03-20 06:03:29 +0000558}
559
560/* Returns the position of the final (ie. unconditional) element. */
561static unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000562get_chain_end(const TC_HANDLE_T handle, unsigned int start)
Marc Bouchere6869a82000-03-20 06:03:29 +0000563{
564 unsigned int last_off, off;
Rusty Russell79dee072000-05-02 16:45:16 +0000565 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000566
567 last_off = start;
568 e = get_entry(handle, start);
569
570 /* Terminate when we meet a error label or a hook entry. */
571 for (off = start + e->next_offset;
572 off < handle->entries.size;
573 last_off = off, off += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +0000574 STRUCT_ENTRY_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000575 unsigned int i;
576
577 e = get_entry(handle, off);
578
579 /* We hit an entry point. */
Rusty Russell79dee072000-05-02 16:45:16 +0000580 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000581 if ((handle->info.valid_hooks & (1 << i))
582 && off == handle->info.hook_entry[i])
583 return last_off;
584 }
585
586 /* We hit a user chain label */
Rusty Russell79dee072000-05-02 16:45:16 +0000587 t = GET_TARGET(e);
Rusty Russell67088e72000-05-10 01:18:57 +0000588 if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000589 return last_off;
590 }
591 /* SHOULD NEVER HAPPEN */
592 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
593 handle->entries.size, off);
594 abort();
595}
596
Rusty Russell30fd6e52000-04-23 09:16:06 +0000597/* Iterator functions to run through the chains. */
Marc Bouchere6869a82000-03-20 06:03:29 +0000598const char *
Philip Blundell8c700902000-05-15 02:17:52 +0000599TC_FIRST_CHAIN(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000600{
Rusty Russell30fd6e52000-04-23 09:16:06 +0000601 if ((*handle)->cache_chain_heads == NULL
602 && !populate_cache(*handle))
Marc Bouchere6869a82000-03-20 06:03:29 +0000603 return NULL;
604
Rusty Russell30fd6e52000-04-23 09:16:06 +0000605 (*handle)->cache_chain_iteration
606 = &(*handle)->cache_chain_heads[0];
Marc Bouchere6869a82000-03-20 06:03:29 +0000607
Rusty Russell30fd6e52000-04-23 09:16:06 +0000608 return (*handle)->cache_chain_iteration->name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000609}
610
Rusty Russell30fd6e52000-04-23 09:16:06 +0000611/* Iterator functions to run through the chains. Returns NULL at end. */
612const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000613TC_NEXT_CHAIN(TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000614{
615 (*handle)->cache_chain_iteration++;
616
617 if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
Martin Josefsson841e4ae2003-05-02 15:30:11 +0000618 == (*handle)->cache_num_chains)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000619 return NULL;
620
621 return (*handle)->cache_chain_iteration->name;
622}
623
624/* Get first rule in the given chain: NULL for empty chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000625const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000626TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000627{
628 struct chain_cache *c;
629
630 c = find_label(chain, *handle);
631 if (!c) {
632 errno = ENOENT;
633 return NULL;
634 }
635
636 /* Empty chain: single return/policy rule */
Harald Welte3ea8f402003-06-23 18:25:59 +0000637 if (c->start_off == c->end_off)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000638 return NULL;
639
Harald Welte3ea8f402003-06-23 18:25:59 +0000640 (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
641 return offset2entry(*handle, c->start_off);
Rusty Russell30fd6e52000-04-23 09:16:06 +0000642}
643
644/* Returns NULL when rules run out. */
Rusty Russell79dee072000-05-02 16:45:16 +0000645const STRUCT_ENTRY *
Philip Blundell8c700902000-05-15 02:17:52 +0000646TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
Rusty Russell30fd6e52000-04-23 09:16:06 +0000647{
648 if ((void *)prev + prev->next_offset
649 == (void *)(*handle)->cache_rule_end)
650 return NULL;
651
652 return (void *)prev + prev->next_offset;
653}
654
655#if 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000656/* How many rules in this chain? */
657unsigned int
Rusty Russell79dee072000-05-02 16:45:16 +0000658TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000659{
660 unsigned int off = 0;
Rusty Russell79dee072000-05-02 16:45:16 +0000661 STRUCT_ENTRY *start, *end;
Marc Bouchere6869a82000-03-20 06:03:29 +0000662
663 CHECK(*handle);
664 if (!find_label(&off, chain, *handle)) {
665 errno = ENOENT;
666 return (unsigned int)-1;
667 }
668
669 start = get_entry(*handle, off);
670 end = get_entry(*handle, get_chain_end(*handle, off));
671
672 return entry2index(*handle, end) - entry2index(*handle, start);
673}
674
675/* Get n'th rule in this chain. */
Rusty Russell79dee072000-05-02 16:45:16 +0000676const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
677 unsigned int n,
678 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000679{
680 unsigned int pos = 0, chainindex;
681
682 CHECK(*handle);
683 if (!find_label(&pos, chain, *handle)) {
684 errno = ENOENT;
685 return NULL;
686 }
687
688 chainindex = entry2index(*handle, get_entry(*handle, pos));
689
690 return index2entry(*handle, chainindex + n);
691}
Rusty Russell30fd6e52000-04-23 09:16:06 +0000692#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000693
Rusty Russell30fd6e52000-04-23 09:16:06 +0000694static const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000695target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
Marc Bouchere6869a82000-03-20 06:03:29 +0000696{
697 int spos;
698 unsigned int labelidx;
Rusty Russell79dee072000-05-02 16:45:16 +0000699 STRUCT_ENTRY *jumpto;
Marc Bouchere6869a82000-03-20 06:03:29 +0000700
Rusty Russell30fd6e52000-04-23 09:16:06 +0000701 /* To avoid const warnings */
Rusty Russell79dee072000-05-02 16:45:16 +0000702 STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
Rusty Russell30fd6e52000-04-23 09:16:06 +0000703
Rusty Russell79dee072000-05-02 16:45:16 +0000704 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
705 return GET_TARGET(e)->u.user.name;
Marc Bouchere6869a82000-03-20 06:03:29 +0000706
707 /* Standard target: evaluate */
Rusty Russell79dee072000-05-02 16:45:16 +0000708 spos = *(int *)GET_TARGET(e)->data;
Marc Bouchere6869a82000-03-20 06:03:29 +0000709 if (spos < 0) {
Rusty Russell79dee072000-05-02 16:45:16 +0000710 if (spos == RETURN)
711 return LABEL_RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +0000712 else if (spos == -NF_ACCEPT-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000713 return LABEL_ACCEPT;
Marc Bouchere6869a82000-03-20 06:03:29 +0000714 else if (spos == -NF_DROP-1)
Rusty Russell79dee072000-05-02 16:45:16 +0000715 return LABEL_DROP;
James Morris2f4e5d92000-03-24 02:13:51 +0000716 else if (spos == -NF_QUEUE-1)
Rusty Russell67088e72000-05-10 01:18:57 +0000717 return LABEL_QUEUE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000718
719 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
720 entry2offset(handle, e), handle->entries.size,
721 spos);
722 abort();
723 }
724
725 jumpto = get_entry(handle, spos);
726
727 /* Fall through rule */
728 if (jumpto == (void *)e + e->next_offset)
729 return "";
730
731 /* Must point to head of a chain: ie. after error rule */
732 labelidx = entry2index(handle, jumpto) - 1;
733 return get_errorlabel(handle, index2offset(handle, labelidx));
734}
735
736/* Returns a pointer to the target name of this position. */
Rusty Russell79dee072000-05-02 16:45:16 +0000737const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
738 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000739{
Marc Bouchere6869a82000-03-20 06:03:29 +0000740 return target_name(*handle, e);
741}
742
743/* Is this a built-in chain? Actually returns hook + 1. */
744int
Rusty Russell79dee072000-05-02 16:45:16 +0000745TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000746{
747 unsigned int i;
748
Rusty Russell79dee072000-05-02 16:45:16 +0000749 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000750 if ((handle->info.valid_hooks & (1 << i))
751 && handle->hooknames[i]
752 && strcmp(handle->hooknames[i], chain) == 0)
753 return i+1;
754 }
755 return 0;
756}
757
758/* Get the policy of a given built-in chain */
759const char *
Rusty Russell79dee072000-05-02 16:45:16 +0000760TC_GET_POLICY(const char *chain,
761 STRUCT_COUNTERS *counters,
762 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000763{
764 unsigned int start;
Rusty Russell79dee072000-05-02 16:45:16 +0000765 STRUCT_ENTRY *e;
Marc Bouchere6869a82000-03-20 06:03:29 +0000766 int hook;
767
Rusty Russell79dee072000-05-02 16:45:16 +0000768 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000769 if (hook != 0)
770 start = (*handle)->info.hook_entry[hook-1];
771 else
772 return NULL;
773
774 e = get_entry(*handle, get_chain_end(*handle, start));
775 *counters = e->counters;
776
777 return target_name(*handle, e);
778}
779
Harald Weltefbc85232003-06-24 17:37:21 +0000780static inline int
Rusty Russell79dee072000-05-02 16:45:16 +0000781correct_verdict(STRUCT_ENTRY *e,
Rusty Russell725d97a2000-07-07 08:54:22 +0000782 char *base,
Marc Bouchere6869a82000-03-20 06:03:29 +0000783 unsigned int offset, int delta_offset)
784{
Rusty Russell79dee072000-05-02 16:45:16 +0000785 STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
Rusty Russell725d97a2000-07-07 08:54:22 +0000786 unsigned int curr = (char *)e - base;
Marc Bouchere6869a82000-03-20 06:03:29 +0000787
788 /* Trap: insert of fall-through rule. Don't change fall-through
789 verdict to jump-over-next-rule. */
Rusty Russell79dee072000-05-02 16:45:16 +0000790 if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
Marc Bouchere6869a82000-03-20 06:03:29 +0000791 && t->verdict > (int)offset
792 && !(curr == offset &&
793 t->verdict == curr + e->next_offset)) {
794 t->verdict += delta_offset;
795 }
796
797 return 0;
798}
799
800/* Adjusts standard verdict jump positions after an insertion/deletion. */
801static int
Rusty Russell79dee072000-05-02 16:45:16 +0000802set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000803{
Rusty Russell725d97a2000-07-07 08:54:22 +0000804 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000805 (*handle)->entries.size,
Rusty Russell725d97a2000-07-07 08:54:22 +0000806 correct_verdict, (char *)(*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +0000807 offset, delta_offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000808
Rusty Russell175f6412000-03-24 09:32:20 +0000809 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +0000810 return 1;
811}
812
813/* If prepend is set, then we are prepending to a chain: if the
814 * insertion position is an entry point, keep the entry point. */
815static int
816insert_rules(unsigned int num_rules, unsigned int rules_size,
Rusty Russell79dee072000-05-02 16:45:16 +0000817 const STRUCT_ENTRY *insert,
Marc Bouchere6869a82000-03-20 06:03:29 +0000818 unsigned int offset, unsigned int num_rules_offset,
819 int prepend,
Rusty Russell79dee072000-05-02 16:45:16 +0000820 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000821{
Rusty Russell79dee072000-05-02 16:45:16 +0000822 TC_HANDLE_T newh;
823 STRUCT_GETINFO newinfo;
Marc Bouchere6869a82000-03-20 06:03:29 +0000824 unsigned int i;
825
826 if (offset >= (*handle)->entries.size) {
827 errno = EINVAL;
828 return 0;
829 }
830
831 newinfo = (*handle)->info;
832
833 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000834 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000835 /* Entry points to START of chain, so keep same if
836 inserting on at that point. */
837 if ((*handle)->info.hook_entry[i] > offset)
838 newinfo.hook_entry[i] += rules_size;
839
840 /* Underflow always points to END of chain (policy),
841 so if something is inserted at same point, it
842 should be advanced. */
843 if ((*handle)->info.underflow[i] >= offset)
844 newinfo.underflow[i] += rules_size;
845 }
846
847 newh = alloc_handle((*handle)->info.name,
Rusty Russell3c7a6c42000-09-19 07:01:46 +0000848 (*handle)->entries.size + rules_size,
Harald Welte1de80462000-10-30 12:00:27 +0000849 (*handle)->new_number + num_rules);
Marc Bouchere6869a82000-03-20 06:03:29 +0000850 if (!newh)
851 return 0;
852 newh->info = newinfo;
853
854 /* Copy pre... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000855 memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
Marc Bouchere6869a82000-03-20 06:03:29 +0000856 /* ... Insert new ... */
Rusty Russell725d97a2000-07-07 08:54:22 +0000857 memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
Marc Bouchere6869a82000-03-20 06:03:29 +0000858 /* ... copy post */
Rusty Russell725d97a2000-07-07 08:54:22 +0000859 memcpy((char *)newh->entries.entrytable + offset + rules_size,
860 (char *)(*handle)->entries.entrytable + offset,
Marc Bouchere6869a82000-03-20 06:03:29 +0000861 (*handle)->entries.size - offset);
862
863 /* Move counter map. */
864 /* Copy pre... */
865 memcpy(newh->counter_map, (*handle)->counter_map,
866 sizeof(struct counter_map) * num_rules_offset);
867 /* ... copy post */
868 memcpy(newh->counter_map + num_rules_offset + num_rules,
869 (*handle)->counter_map + num_rules_offset,
870 sizeof(struct counter_map) * ((*handle)->new_number
871 - num_rules_offset));
872 /* Set intermediates to no counter copy */
873 for (i = 0; i < num_rules; i++)
874 newh->counter_map[num_rules_offset+i]
Harald Weltee0072942001-01-23 22:55:04 +0000875 = ((struct counter_map){ COUNTER_MAP_SET, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +0000876
877 newh->new_number = (*handle)->new_number + num_rules;
878 newh->entries.size = (*handle)->entries.size + rules_size;
879 newh->hooknames = (*handle)->hooknames;
880
Harald Weltefbc85232003-06-24 17:37:21 +0000881 newh->cache_chain_heads = (*handle)->cache_chain_heads;
882 newh->cache_num_builtins = (*handle)->cache_num_builtins;
883 newh->cache_num_chains = (*handle)->cache_num_chains;
884 newh->cache_rule_end = (*handle)->cache_rule_end;
885 newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
886 if (!correct_cache(newh, offset, rules_size)) {
887 free(newh);
888 return 0;
889 }
890
Marc Bouchere6869a82000-03-20 06:03:29 +0000891 free(*handle);
892 *handle = newh;
893
894 return set_verdict(offset, rules_size, handle);
895}
896
897static int
898delete_rules(unsigned int num_rules, unsigned int rules_size,
899 unsigned int offset, unsigned int num_rules_offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000900 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +0000901{
902 unsigned int i;
903
904 if (offset + rules_size > (*handle)->entries.size) {
905 errno = EINVAL;
906 return 0;
907 }
908
909 /* Fix up entry points. */
Rusty Russell79dee072000-05-02 16:45:16 +0000910 for (i = 0; i < NUMHOOKS; i++) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000911 /* In practice, we never delete up to a hook entry,
912 since the built-in chains are always first,
913 so these two are never equal */
914 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
915 (*handle)->info.hook_entry[i] -= rules_size;
916 else if ((*handle)->info.hook_entry[i] > offset) {
917 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
918 i, (*handle)->info.hook_entry[i], offset);
919 abort();
920 }
921
922 /* Underflow points to policy (terminal) rule in
923 built-in, so sequality is valid here (when deleting
924 the last rule). */
925 if ((*handle)->info.underflow[i] >= offset + rules_size)
926 (*handle)->info.underflow[i] -= rules_size;
927 else if ((*handle)->info.underflow[i] > offset) {
928 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
929 i, (*handle)->info.underflow[i], offset);
930 abort();
931 }
932 }
933
934 /* Move the rules down. */
Rusty Russell725d97a2000-07-07 08:54:22 +0000935 memmove((char *)(*handle)->entries.entrytable + offset,
936 (char *)(*handle)->entries.entrytable + offset + rules_size,
Marc Bouchere6869a82000-03-20 06:03:29 +0000937 (*handle)->entries.size - (offset + rules_size));
938
939 /* Move the counter map down. */
940 memmove(&(*handle)->counter_map[num_rules_offset],
941 &(*handle)->counter_map[num_rules_offset + num_rules],
942 sizeof(struct counter_map)
943 * ((*handle)->new_number - (num_rules + num_rules_offset)));
944
945 /* Fix numbers */
946 (*handle)->new_number -= num_rules;
947 (*handle)->entries.size -= rules_size;
948
Harald Weltefbc85232003-06-24 17:37:21 +0000949 /* Fix the chain cache */
Martin Josefsson0acde1f2003-07-05 20:11:11 +0000950 if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
Harald Weltefbc85232003-06-24 17:37:21 +0000951 return 0;
952
Marc Bouchere6869a82000-03-20 06:03:29 +0000953 return set_verdict(offset, -(int)rules_size, handle);
954}
955
956static int
Rusty Russell79dee072000-05-02 16:45:16 +0000957standard_map(STRUCT_ENTRY *e, int verdict)
Marc Bouchere6869a82000-03-20 06:03:29 +0000958{
Rusty Russell79dee072000-05-02 16:45:16 +0000959 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +0000960
Rusty Russell79dee072000-05-02 16:45:16 +0000961 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000962
Rusty Russell67088e72000-05-10 01:18:57 +0000963 if (t->target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +0000964 != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
Marc Bouchere6869a82000-03-20 06:03:29 +0000965 errno = EINVAL;
966 return 0;
967 }
968 /* memset for memcmp convenience on delete/replace */
Rusty Russell79dee072000-05-02 16:45:16 +0000969 memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
970 strcpy(t->target.u.user.name, STANDARD_TARGET);
Marc Bouchere6869a82000-03-20 06:03:29 +0000971 t->verdict = verdict;
972
973 return 1;
974}
Rusty Russell7e53bf92000-03-20 07:03:28 +0000975
Marc Bouchere6869a82000-03-20 06:03:29 +0000976static int
Rusty Russell79dee072000-05-02 16:45:16 +0000977map_target(const TC_HANDLE_T handle,
978 STRUCT_ENTRY *e,
Marc Bouchere6869a82000-03-20 06:03:29 +0000979 unsigned int offset,
Rusty Russell79dee072000-05-02 16:45:16 +0000980 STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +0000981{
Rusty Russell79dee072000-05-02 16:45:16 +0000982 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +0000983
984 /* Save old target (except data, which we don't change, except for
985 standard case, where we don't care). */
986 *old = *t;
987
988 /* Maybe it's empty (=> fall through) */
Rusty Russell228e98d2000-04-27 10:28:06 +0000989 if (strcmp(t->u.user.name, "") == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000990 return standard_map(e, offset + e->next_offset);
991 /* Maybe it's a standard target name... */
Rusty Russell79dee072000-05-02 16:45:16 +0000992 else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000993 return standard_map(e, -NF_ACCEPT - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000994 else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000995 return standard_map(e, -NF_DROP - 1);
Rusty Russell67088e72000-05-10 01:18:57 +0000996 else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +0000997 return standard_map(e, -NF_QUEUE - 1);
Rusty Russell79dee072000-05-02 16:45:16 +0000998 else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
999 return standard_map(e, RETURN);
1000 else if (TC_BUILTIN(t->u.user.name, handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001001 /* Can't jump to builtins. */
1002 errno = EINVAL;
1003 return 0;
1004 } else {
1005 /* Maybe it's an existing chain name. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001006 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001007
Rusty Russell228e98d2000-04-27 10:28:06 +00001008 c = find_label(t->u.user.name, handle);
Rusty Russell30fd6e52000-04-23 09:16:06 +00001009 if (c)
Harald Welte3ea8f402003-06-23 18:25:59 +00001010 return standard_map(e, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001011 }
1012
1013 /* Must be a module? If not, kernel will reject... */
1014 /* memset to all 0 for your memcmp convenience. */
Rusty Russell228e98d2000-04-27 10:28:06 +00001015 memset(t->u.user.name + strlen(t->u.user.name),
Marc Bouchere6869a82000-03-20 06:03:29 +00001016 0,
Rusty Russell79dee072000-05-02 16:45:16 +00001017 FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
Marc Bouchere6869a82000-03-20 06:03:29 +00001018 return 1;
1019}
1020
1021static void
Rusty Russell79dee072000-05-02 16:45:16 +00001022unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
Marc Bouchere6869a82000-03-20 06:03:29 +00001023{
Rusty Russell79dee072000-05-02 16:45:16 +00001024 STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001025
1026 /* Save old target (except data, which we don't change, except for
1027 standard case, where we don't care). */
1028 *t = *old;
1029}
1030
1031/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
1032int
Rusty Russell79dee072000-05-02 16:45:16 +00001033TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
1034 const STRUCT_ENTRY *e,
1035 unsigned int rulenum,
1036 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001037{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001038 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +00001039 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001040 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +00001041 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001042 int ret;
1043
Rusty Russell79dee072000-05-02 16:45:16 +00001044 iptc_fn = TC_INSERT_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001045 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001046 errno = ENOENT;
1047 return 0;
1048 }
1049
Harald Welte3ea8f402003-06-23 18:25:59 +00001050 chainindex = offset2index(*handle, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001051
Rusty Russell14a1c912000-08-26 04:41:10 +00001052 tmp = index2entry(*handle, chainindex + rulenum);
Harald Welte3ea8f402003-06-23 18:25:59 +00001053 if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001054 errno = E2BIG;
1055 return 0;
1056 }
1057 offset = index2offset(*handle, chainindex + rulenum);
1058
1059 /* Mapping target actually alters entry, but that's
1060 transparent to the caller. */
Rusty Russell79dee072000-05-02 16:45:16 +00001061 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001062 return 0;
1063
1064 ret = insert_rules(1, e->next_offset, e, offset,
1065 chainindex + rulenum, rulenum == 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001066 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001067 return ret;
1068}
1069
1070/* Atomically replace rule `rulenum' in `chain' with `fw'. */
1071int
Rusty Russell79dee072000-05-02 16:45:16 +00001072TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
1073 const STRUCT_ENTRY *e,
1074 unsigned int rulenum,
1075 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001076{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001077 unsigned int chainindex, offset;
Rusty Russell79dee072000-05-02 16:45:16 +00001078 STRUCT_ENTRY_TARGET old;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001079 struct chain_cache *c;
Rusty Russell14a1c912000-08-26 04:41:10 +00001080 STRUCT_ENTRY *tmp;
Marc Bouchere6869a82000-03-20 06:03:29 +00001081 int ret;
1082
Rusty Russell79dee072000-05-02 16:45:16 +00001083 iptc_fn = TC_REPLACE_ENTRY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001084
Rusty Russell30fd6e52000-04-23 09:16:06 +00001085 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001086 errno = ENOENT;
1087 return 0;
1088 }
1089
Harald Welte3ea8f402003-06-23 18:25:59 +00001090 chainindex = offset2index(*handle, c->start_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001091
Rusty Russell14a1c912000-08-26 04:41:10 +00001092 tmp = index2entry(*handle, chainindex + rulenum);
Harald Welte3ea8f402003-06-23 18:25:59 +00001093 if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001094 errno = E2BIG;
1095 return 0;
1096 }
1097
1098 offset = index2offset(*handle, chainindex + rulenum);
1099 /* Replace = delete and insert. */
1100 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
1101 offset, chainindex + rulenum, handle))
1102 return 0;
1103
Rusty Russell79dee072000-05-02 16:45:16 +00001104 if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001105 return 0;
Marc Bouchere6869a82000-03-20 06:03:29 +00001106
1107 ret = insert_rules(1, e->next_offset, e, offset,
1108 chainindex + rulenum, 1, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001109 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001110 return ret;
1111}
1112
1113/* Append entry `fw' to chain `chain'. Equivalent to insert with
1114 rulenum = length of chain. */
1115int
Rusty Russell79dee072000-05-02 16:45:16 +00001116TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
1117 const STRUCT_ENTRY *e,
1118 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001119{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001120 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001121 STRUCT_ENTRY_TARGET old;
Marc Bouchere6869a82000-03-20 06:03:29 +00001122 int ret;
1123
Rusty Russell79dee072000-05-02 16:45:16 +00001124 iptc_fn = TC_APPEND_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001125 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001126 errno = ENOENT;
1127 return 0;
1128 }
1129
Rusty Russell79dee072000-05-02 16:45:16 +00001130 if (!map_target(*handle, (STRUCT_ENTRY *)e,
Harald Welte3ea8f402003-06-23 18:25:59 +00001131 c->end_off, &old))
Marc Bouchere6869a82000-03-20 06:03:29 +00001132 return 0;
1133
Harald Welte3ea8f402003-06-23 18:25:59 +00001134 ret = insert_rules(1, e->next_offset, e, c->end_off,
1135 offset2index(*handle, c->end_off), 0, handle);
Rusty Russell79dee072000-05-02 16:45:16 +00001136 unmap_target((STRUCT_ENTRY *)e, &old);
Marc Bouchere6869a82000-03-20 06:03:29 +00001137 return ret;
1138}
1139
1140static inline int
Rusty Russell79dee072000-05-02 16:45:16 +00001141match_different(const STRUCT_ENTRY_MATCH *a,
Rusty Russelledf14cf2000-04-19 11:26:44 +00001142 const unsigned char *a_elems,
1143 const unsigned char *b_elems,
1144 unsigned char **maskptr)
Marc Bouchere6869a82000-03-20 06:03:29 +00001145{
Rusty Russell79dee072000-05-02 16:45:16 +00001146 const STRUCT_ENTRY_MATCH *b;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001147 unsigned int i;
Marc Bouchere6869a82000-03-20 06:03:29 +00001148
1149 /* Offset of b is the same as a. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001150 b = (void *)b_elems + ((unsigned char *)a - a_elems);
Marc Bouchere6869a82000-03-20 06:03:29 +00001151
Rusty Russell228e98d2000-04-27 10:28:06 +00001152 if (a->u.match_size != b->u.match_size)
Marc Bouchere6869a82000-03-20 06:03:29 +00001153 return 1;
1154
Rusty Russell228e98d2000-04-27 10:28:06 +00001155 if (strcmp(a->u.user.name, b->u.user.name) != 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001156 return 1;
1157
Rusty Russell73ef09b2000-07-03 10:24:04 +00001158 *maskptr += ALIGN(sizeof(*a));
Rusty Russelledf14cf2000-04-19 11:26:44 +00001159
Rusty Russell73ef09b2000-07-03 10:24:04 +00001160 for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
Rusty Russelledf14cf2000-04-19 11:26:44 +00001161 if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
Rusty Russell90e712a2000-03-29 04:19:26 +00001162 return 1;
Rusty Russelledf14cf2000-04-19 11:26:44 +00001163 *maskptr += i;
1164 return 0;
1165}
1166
1167static inline int
1168target_different(const unsigned char *a_targdata,
1169 const unsigned char *b_targdata,
1170 unsigned int tdatasize,
1171 const unsigned char *mask)
1172{
1173 unsigned int i;
1174 for (i = 0; i < tdatasize; i++)
1175 if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
1176 return 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001177
1178 return 0;
1179}
1180
Rusty Russell79dee072000-05-02 16:45:16 +00001181static int
1182is_same(const STRUCT_ENTRY *a,
1183 const STRUCT_ENTRY *b,
1184 unsigned char *matchmask);
Marc Bouchere6869a82000-03-20 06:03:29 +00001185
1186/* Delete the first rule in `chain' which matches `fw'. */
1187int
Rusty Russell79dee072000-05-02 16:45:16 +00001188TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
1189 const STRUCT_ENTRY *origfw,
1190 unsigned char *matchmask,
1191 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001192{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001193 unsigned int offset;
1194 struct chain_cache *c;
Rusty Russell79dee072000-05-02 16:45:16 +00001195 STRUCT_ENTRY *e, *fw;
Marc Bouchere6869a82000-03-20 06:03:29 +00001196
Rusty Russell79dee072000-05-02 16:45:16 +00001197 iptc_fn = TC_DELETE_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001198 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001199 errno = ENOENT;
1200 return 0;
1201 }
1202
1203 fw = malloc(origfw->next_offset);
1204 if (fw == NULL) {
1205 errno = ENOMEM;
1206 return 0;
1207 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001208
Harald Welte3ea8f402003-06-23 18:25:59 +00001209 for (offset = c->start_off; offset < c->end_off;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001210 offset += e->next_offset) {
Rusty Russell79dee072000-05-02 16:45:16 +00001211 STRUCT_ENTRY_TARGET discard;
Marc Bouchere6869a82000-03-20 06:03:29 +00001212
1213 memcpy(fw, origfw, origfw->next_offset);
1214
1215 /* FIXME: handle this in is_same --RR */
1216 if (!map_target(*handle, fw, offset, &discard)) {
1217 free(fw);
1218 return 0;
1219 }
1220 e = get_entry(*handle, offset);
1221
1222#if 0
1223 printf("Deleting:\n");
1224 dump_entry(newe);
1225#endif
Rusty Russelledf14cf2000-04-19 11:26:44 +00001226 if (is_same(e, fw, matchmask)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001227 int ret;
1228 ret = delete_rules(1, e->next_offset,
1229 offset, entry2index(*handle, e),
1230 handle);
1231 free(fw);
Marc Bouchere6869a82000-03-20 06:03:29 +00001232 return ret;
1233 }
1234 }
1235
1236 free(fw);
1237 errno = ENOENT;
1238 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001239}
Marc Bouchere6869a82000-03-20 06:03:29 +00001240
1241/* Delete the rule in position `rulenum' in `chain'. */
1242int
Rusty Russell79dee072000-05-02 16:45:16 +00001243TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
1244 unsigned int rulenum,
1245 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001246{
Marc Bouchere6869a82000-03-20 06:03:29 +00001247 unsigned int index;
1248 int ret;
Rusty Russell79dee072000-05-02 16:45:16 +00001249 STRUCT_ENTRY *e;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001250 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001251
Rusty Russell79dee072000-05-02 16:45:16 +00001252 iptc_fn = TC_DELETE_NUM_ENTRY;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001253 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001254 errno = ENOENT;
1255 return 0;
1256 }
1257
Harald Welte3ea8f402003-06-23 18:25:59 +00001258 index = offset2index(*handle, c->start_off) + rulenum;
Marc Bouchere6869a82000-03-20 06:03:29 +00001259
Harald Welte3ea8f402003-06-23 18:25:59 +00001260 if (index >= offset2index(*handle, c->end_off)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001261 errno = E2BIG;
1262 return 0;
1263 }
1264
1265 e = index2entry(*handle, index);
1266 if (e == NULL) {
1267 errno = EINVAL;
1268 return 0;
1269 }
1270
1271 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1272 index, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001273 return ret;
1274}
1275
1276/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1277 NULL and sets errno. */
1278const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001279TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
1280 STRUCT_ENTRY *entry,
1281 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001282{
1283 errno = ENOSYS;
1284 return NULL;
1285}
1286
1287/* Flushes the entries in the given chain (ie. empties chain). */
1288int
Rusty Russell79dee072000-05-02 16:45:16 +00001289TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001290{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001291 unsigned int startindex, endindex;
Harald Welte3ea8f402003-06-23 18:25:59 +00001292 STRUCT_ENTRY *startentry, *endentry;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001293 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001294 int ret;
1295
Rusty Russell79dee072000-05-02 16:45:16 +00001296 iptc_fn = TC_FLUSH_ENTRIES;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001297 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001298 errno = ENOENT;
1299 return 0;
1300 }
Harald Welte3ea8f402003-06-23 18:25:59 +00001301 startindex = offset2index(*handle, c->start_off);
1302 endindex = offset2index(*handle, c->end_off);
1303 startentry = offset2entry(*handle, c->start_off);
1304 endentry = offset2entry(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001305
1306 ret = delete_rules(endindex - startindex,
Harald Welte3ea8f402003-06-23 18:25:59 +00001307 (char *)endentry - (char *)startentry,
1308 c->start_off, startindex,
Marc Bouchere6869a82000-03-20 06:03:29 +00001309 handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001310 return ret;
1311}
1312
1313/* Zeroes the counters in a chain. */
1314int
Rusty Russell79dee072000-05-02 16:45:16 +00001315TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001316{
1317 unsigned int i, end;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001318 struct chain_cache *c;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001319
Rusty Russell30fd6e52000-04-23 09:16:06 +00001320 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001321 errno = ENOENT;
1322 return 0;
1323 }
Marc Bouchere6869a82000-03-20 06:03:29 +00001324
Harald Welte3ea8f402003-06-23 18:25:59 +00001325 i = offset2index(*handle, c->start_off);
1326 end = offset2index(*handle, c->end_off);
Marc Bouchere6869a82000-03-20 06:03:29 +00001327
1328 for (; i <= end; i++) {
1329 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1330 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1331 }
Rusty Russell175f6412000-03-24 09:32:20 +00001332 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001333
Marc Bouchere6869a82000-03-20 06:03:29 +00001334 return 1;
1335}
1336
Harald Welte1cef74d2001-01-05 15:22:59 +00001337STRUCT_COUNTERS *
1338TC_READ_COUNTER(const IPT_CHAINLABEL chain,
1339 unsigned int rulenum,
1340 TC_HANDLE_T *handle)
1341{
1342 STRUCT_ENTRY *e;
1343 struct chain_cache *c;
1344 unsigned int chainindex, end;
1345
1346 iptc_fn = TC_READ_COUNTER;
1347 CHECK(*handle);
1348
1349 if (!(c = find_label(chain, *handle))) {
1350 errno = ENOENT;
1351 return NULL;
1352 }
1353
Harald Welte3ea8f402003-06-23 18:25:59 +00001354 chainindex = offset2index(*handle, c->start_off);
1355 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001356
1357 if (chainindex + rulenum > end) {
1358 errno = E2BIG;
1359 return NULL;
1360 }
1361
1362 e = index2entry(*handle, chainindex + rulenum);
1363
1364 return &e->counters;
1365}
1366
1367int
1368TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
1369 unsigned int rulenum,
1370 TC_HANDLE_T *handle)
1371{
1372 STRUCT_ENTRY *e;
1373 struct chain_cache *c;
1374 unsigned int chainindex, end;
1375
1376 iptc_fn = TC_ZERO_COUNTER;
1377 CHECK(*handle);
1378
1379 if (!(c = find_label(chain, *handle))) {
1380 errno = ENOENT;
1381 return 0;
1382 }
1383
Harald Welte3ea8f402003-06-23 18:25:59 +00001384 chainindex = offset2index(*handle, c->start_off);
1385 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001386
1387 if (chainindex + rulenum > end) {
1388 errno = E2BIG;
1389 return 0;
1390 }
1391
1392 e = index2entry(*handle, chainindex + rulenum);
1393
1394 if ((*handle)->counter_map[chainindex + rulenum].maptype
1395 == COUNTER_MAP_NORMAL_MAP) {
1396 (*handle)->counter_map[chainindex + rulenum].maptype
1397 = COUNTER_MAP_ZEROED;
1398 }
1399
1400 set_changed(*handle);
1401
1402 return 1;
1403}
1404
1405int
1406TC_SET_COUNTER(const IPT_CHAINLABEL chain,
1407 unsigned int rulenum,
1408 STRUCT_COUNTERS *counters,
1409 TC_HANDLE_T *handle)
1410{
1411 STRUCT_ENTRY *e;
1412 struct chain_cache *c;
1413 unsigned int chainindex, end;
1414
1415 iptc_fn = TC_SET_COUNTER;
1416 CHECK(*handle);
1417
1418 if (!(c = find_label(chain, *handle))) {
1419 errno = ENOENT;
1420 return 0;
1421 }
1422
Harald Welte3ea8f402003-06-23 18:25:59 +00001423 chainindex = offset2index(*handle, c->start_off);
1424 end = offset2index(*handle, c->end_off);
Harald Welte1cef74d2001-01-05 15:22:59 +00001425
1426 if (chainindex + rulenum > end) {
1427 errno = E2BIG;
1428 return 0;
1429 }
1430
1431 e = index2entry(*handle, chainindex + rulenum);
1432
1433 (*handle)->counter_map[chainindex + rulenum].maptype
1434 = COUNTER_MAP_SET;
1435
1436 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1437
1438 set_changed(*handle);
1439
1440 return 1;
1441}
1442
Marc Bouchere6869a82000-03-20 06:03:29 +00001443/* Creates a new chain. */
1444/* To create a chain, create two rules: error node and unconditional
1445 * return. */
1446int
Rusty Russell79dee072000-05-02 16:45:16 +00001447TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001448{
Marc Bouchere6869a82000-03-20 06:03:29 +00001449 int ret;
1450 struct {
Rusty Russell79dee072000-05-02 16:45:16 +00001451 STRUCT_ENTRY head;
Marc Bouchere6869a82000-03-20 06:03:29 +00001452 struct ipt_error_target name;
Rusty Russell79dee072000-05-02 16:45:16 +00001453 STRUCT_ENTRY ret;
1454 STRUCT_STANDARD_TARGET target;
Marc Bouchere6869a82000-03-20 06:03:29 +00001455 } newc;
Harald Weltefbc85232003-06-24 17:37:21 +00001456 unsigned int destination;
Marc Bouchere6869a82000-03-20 06:03:29 +00001457
Rusty Russell79dee072000-05-02 16:45:16 +00001458 iptc_fn = TC_CREATE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001459
1460 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1461 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001462 if (find_label(chain, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001463 || strcmp(chain, LABEL_DROP) == 0
1464 || strcmp(chain, LABEL_ACCEPT) == 0
Rusty Russell67088e72000-05-10 01:18:57 +00001465 || strcmp(chain, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001466 || strcmp(chain, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001467 errno = EEXIST;
1468 return 0;
1469 }
1470
Rusty Russell79dee072000-05-02 16:45:16 +00001471 if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001472 errno = EINVAL;
1473 return 0;
1474 }
1475
1476 memset(&newc, 0, sizeof(newc));
Rusty Russell79dee072000-05-02 16:45:16 +00001477 newc.head.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001478 newc.head.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001479 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001480 + ALIGN(sizeof(struct ipt_error_target));
Rusty Russell67088e72000-05-10 01:18:57 +00001481 strcpy(newc.name.t.u.user.name, ERROR_TARGET);
Philip Blundell8c700902000-05-15 02:17:52 +00001482 newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
Marc Bouchere6869a82000-03-20 06:03:29 +00001483 strcpy(newc.name.error, chain);
1484
Rusty Russell79dee072000-05-02 16:45:16 +00001485 newc.ret.target_offset = sizeof(STRUCT_ENTRY);
Marc Bouchere6869a82000-03-20 06:03:29 +00001486 newc.ret.next_offset
Rusty Russell67088e72000-05-10 01:18:57 +00001487 = sizeof(STRUCT_ENTRY)
Philip Blundell8c700902000-05-15 02:17:52 +00001488 + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001489 strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
Rusty Russell67088e72000-05-10 01:18:57 +00001490 newc.target.target.u.target_size
Philip Blundell8c700902000-05-15 02:17:52 +00001491 = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
Rusty Russell79dee072000-05-02 16:45:16 +00001492 newc.target.verdict = RETURN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001493
Harald Weltefbc85232003-06-24 17:37:21 +00001494 destination = index2offset(*handle, (*handle)->new_number -1);
1495
Marc Bouchere6869a82000-03-20 06:03:29 +00001496 /* Add just before terminal entry */
1497 ret = insert_rules(2, sizeof(newc), &newc.head,
Harald Weltefbc85232003-06-24 17:37:21 +00001498 destination,
Marc Bouchere6869a82000-03-20 06:03:29 +00001499 (*handle)->new_number - 1,
1500 0, handle);
Harald Weltefbc85232003-06-24 17:37:21 +00001501
1502 set_changed(*handle);
1503
1504 /* add chain cache info for this chain */
1505 add_chain_cache(*handle, chain,
1506 destination+newc.head.next_offset,
1507 destination+newc.head.next_offset);
1508
Marc Bouchere6869a82000-03-20 06:03:29 +00001509 return ret;
1510}
1511
1512static int
Rusty Russell79dee072000-05-02 16:45:16 +00001513count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
Marc Bouchere6869a82000-03-20 06:03:29 +00001514{
Rusty Russell79dee072000-05-02 16:45:16 +00001515 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001516
Rusty Russell79dee072000-05-02 16:45:16 +00001517 if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
1518 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001519
1520 if (t->verdict == offset)
1521 (*ref)++;
1522 }
1523
1524 return 0;
1525}
1526
1527/* Get the number of references to this chain. */
1528int
Rusty Russell79dee072000-05-02 16:45:16 +00001529TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
1530 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001531{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001532 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001533
Rusty Russell30fd6e52000-04-23 09:16:06 +00001534 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001535 errno = ENOENT;
1536 return 0;
1537 }
1538
1539 *ref = 0;
Rusty Russell725d97a2000-07-07 08:54:22 +00001540 ENTRY_ITERATE((*handle)->entries.entrytable,
Rusty Russell79dee072000-05-02 16:45:16 +00001541 (*handle)->entries.size,
Harald Welte3ea8f402003-06-23 18:25:59 +00001542 count_ref, c->start_off, ref);
Marc Bouchere6869a82000-03-20 06:03:29 +00001543 return 1;
1544}
1545
1546/* Deletes a chain. */
1547int
Rusty Russell79dee072000-05-02 16:45:16 +00001548TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001549{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001550 unsigned int labelidx, labeloff;
Marc Bouchere6869a82000-03-20 06:03:29 +00001551 unsigned int references;
Rusty Russell30fd6e52000-04-23 09:16:06 +00001552 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001553 int ret;
Harald Welte3ea8f402003-06-23 18:25:59 +00001554 STRUCT_ENTRY *start;
Marc Bouchere6869a82000-03-20 06:03:29 +00001555
Rusty Russell79dee072000-05-02 16:45:16 +00001556 if (!TC_GET_REFERENCES(&references, chain, handle))
Marc Bouchere6869a82000-03-20 06:03:29 +00001557 return 0;
Rusty Russell7e53bf92000-03-20 07:03:28 +00001558
Rusty Russell79dee072000-05-02 16:45:16 +00001559 iptc_fn = TC_DELETE_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001560
Rusty Russell79dee072000-05-02 16:45:16 +00001561 if (TC_BUILTIN(chain, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001562 errno = EINVAL;
1563 return 0;
1564 }
1565
1566 if (references > 0) {
1567 errno = EMLINK;
1568 return 0;
1569 }
1570
Rusty Russell30fd6e52000-04-23 09:16:06 +00001571 if (!(c = find_label(chain, *handle))) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001572 errno = ENOENT;
1573 return 0;
1574 }
1575
Harald Welte3ea8f402003-06-23 18:25:59 +00001576 if (c->start_off != c->end_off) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001577 errno = ENOTEMPTY;
1578 return 0;
1579 }
1580
1581 /* Need label index: preceeds chain start */
Harald Welte3ea8f402003-06-23 18:25:59 +00001582 labelidx = offset2index(*handle, c->start_off) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001583 labeloff = index2offset(*handle, labelidx);
1584
Harald Welte3ea8f402003-06-23 18:25:59 +00001585 start = offset2entry(*handle, c->start_off);
1586
Marc Bouchere6869a82000-03-20 06:03:29 +00001587 ret = delete_rules(2,
1588 get_entry(*handle, labeloff)->next_offset
Harald Welte3ea8f402003-06-23 18:25:59 +00001589 + start->next_offset,
Marc Bouchere6869a82000-03-20 06:03:29 +00001590 labeloff, labelidx, handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001591 return ret;
1592}
1593
1594/* Renames a chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001595int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
1596 const IPT_CHAINLABEL newname,
1597 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001598{
Rusty Russell30fd6e52000-04-23 09:16:06 +00001599 unsigned int labeloff, labelidx;
1600 struct chain_cache *c;
Marc Bouchere6869a82000-03-20 06:03:29 +00001601 struct ipt_error_target *t;
1602
Rusty Russell79dee072000-05-02 16:45:16 +00001603 iptc_fn = TC_RENAME_CHAIN;
Marc Bouchere6869a82000-03-20 06:03:29 +00001604
Harald Welte1de80462000-10-30 12:00:27 +00001605 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1606 QUEUE, RETURN. */
Rusty Russell30fd6e52000-04-23 09:16:06 +00001607 if (find_label(newname, *handle)
Rusty Russell79dee072000-05-02 16:45:16 +00001608 || strcmp(newname, LABEL_DROP) == 0
1609 || strcmp(newname, LABEL_ACCEPT) == 0
Harald Welte1de80462000-10-30 12:00:27 +00001610 || strcmp(newname, LABEL_QUEUE) == 0
Rusty Russell79dee072000-05-02 16:45:16 +00001611 || strcmp(newname, LABEL_RETURN) == 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001612 errno = EEXIST;
1613 return 0;
1614 }
1615
Rusty Russell30fd6e52000-04-23 09:16:06 +00001616 if (!(c = find_label(oldname, *handle))
Rusty Russell79dee072000-05-02 16:45:16 +00001617 || TC_BUILTIN(oldname, *handle)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001618 errno = ENOENT;
1619 return 0;
1620 }
1621
Rusty Russell79dee072000-05-02 16:45:16 +00001622 if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001623 errno = EINVAL;
1624 return 0;
1625 }
1626
1627 /* Need label index: preceeds chain start */
Harald Welte3ea8f402003-06-23 18:25:59 +00001628 labelidx = offset2index(*handle, c->start_off) - 1;
Marc Bouchere6869a82000-03-20 06:03:29 +00001629 labeloff = index2offset(*handle, labelidx);
1630
1631 t = (struct ipt_error_target *)
Rusty Russell79dee072000-05-02 16:45:16 +00001632 GET_TARGET(get_entry(*handle, labeloff));
Marc Bouchere6869a82000-03-20 06:03:29 +00001633
1634 memset(t->error, 0, sizeof(t->error));
1635 strcpy(t->error, newname);
Harald Weltefbc85232003-06-24 17:37:21 +00001636
1637 /* update chain cache */
1638 memset(c->name, 0, sizeof(c->name));
1639 strcpy(c->name, newname);
1640
Rusty Russell175f6412000-03-24 09:32:20 +00001641 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001642
Marc Bouchere6869a82000-03-20 06:03:29 +00001643 return 1;
1644}
1645
1646/* Sets the policy on a built-in chain. */
1647int
Rusty Russell79dee072000-05-02 16:45:16 +00001648TC_SET_POLICY(const IPT_CHAINLABEL chain,
1649 const IPT_CHAINLABEL policy,
Harald Welte1cef74d2001-01-05 15:22:59 +00001650 STRUCT_COUNTERS *counters,
Rusty Russell79dee072000-05-02 16:45:16 +00001651 TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001652{
1653 unsigned int hook;
Harald Welte1cef74d2001-01-05 15:22:59 +00001654 unsigned int policyoff, ctrindex;
Rusty Russell79dee072000-05-02 16:45:16 +00001655 STRUCT_ENTRY *e;
1656 STRUCT_STANDARD_TARGET *t;
Marc Bouchere6869a82000-03-20 06:03:29 +00001657
Rusty Russell79dee072000-05-02 16:45:16 +00001658 iptc_fn = TC_SET_POLICY;
Marc Bouchere6869a82000-03-20 06:03:29 +00001659 /* Figure out which chain. */
Rusty Russell79dee072000-05-02 16:45:16 +00001660 hook = TC_BUILTIN(chain, *handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001661 if (hook == 0) {
Marc Boucherc8264992000-04-22 22:34:44 +00001662 errno = ENOENT;
Marc Bouchere6869a82000-03-20 06:03:29 +00001663 return 0;
1664 } else
1665 hook--;
1666
1667 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1668 if (policyoff != (*handle)->info.underflow[hook]) {
1669 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1670 chain, policyoff, (*handle)->info.underflow[hook]);
1671 return 0;
1672 }
1673
1674 e = get_entry(*handle, policyoff);
Rusty Russell79dee072000-05-02 16:45:16 +00001675 t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
Marc Bouchere6869a82000-03-20 06:03:29 +00001676
Rusty Russell79dee072000-05-02 16:45:16 +00001677 if (strcmp(policy, LABEL_ACCEPT) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001678 t->verdict = -NF_ACCEPT - 1;
Rusty Russell79dee072000-05-02 16:45:16 +00001679 else if (strcmp(policy, LABEL_DROP) == 0)
Marc Bouchere6869a82000-03-20 06:03:29 +00001680 t->verdict = -NF_DROP - 1;
1681 else {
1682 errno = EINVAL;
1683 return 0;
1684 }
Harald Welte1cef74d2001-01-05 15:22:59 +00001685
1686 ctrindex = entry2index(*handle, e);
1687
1688 if (counters) {
1689 /* set byte and packet counters */
1690 memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
1691
1692 (*handle)->counter_map[ctrindex].maptype
1693 = COUNTER_MAP_SET;
1694
1695 } else {
1696 (*handle)->counter_map[ctrindex]
1697 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1698 }
1699
Rusty Russell175f6412000-03-24 09:32:20 +00001700 set_changed(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001701
Marc Bouchere6869a82000-03-20 06:03:29 +00001702 return 1;
1703}
1704
1705/* Without this, on gcc 2.7.2.3, we get:
Rusty Russell79dee072000-05-02 16:45:16 +00001706 libiptc.c: In function `TC_COMMIT':
Marc Bouchere6869a82000-03-20 06:03:29 +00001707 libiptc.c:833: fixed or forbidden register was spilled.
1708 This may be due to a compiler bug or to impossible asm
1709 statements or clauses.
1710*/
1711static void
Rusty Russell79dee072000-05-02 16:45:16 +00001712subtract_counters(STRUCT_COUNTERS *answer,
1713 const STRUCT_COUNTERS *a,
1714 const STRUCT_COUNTERS *b)
Marc Bouchere6869a82000-03-20 06:03:29 +00001715{
1716 answer->pcnt = a->pcnt - b->pcnt;
1717 answer->bcnt = a->bcnt - b->bcnt;
1718}
1719
1720int
Rusty Russell79dee072000-05-02 16:45:16 +00001721TC_COMMIT(TC_HANDLE_T *handle)
Marc Bouchere6869a82000-03-20 06:03:29 +00001722{
1723 /* Replace, then map back the counters. */
Rusty Russell79dee072000-05-02 16:45:16 +00001724 STRUCT_REPLACE *repl;
1725 STRUCT_COUNTERS_INFO *newcounters;
Marc Bouchere6869a82000-03-20 06:03:29 +00001726 unsigned int i;
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001727 size_t counterlen;
Marc Bouchere6869a82000-03-20 06:03:29 +00001728
1729 CHECK(*handle);
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001730
1731 counterlen = sizeof(STRUCT_COUNTERS_INFO)
1732 + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
1733
Marc Bouchere6869a82000-03-20 06:03:29 +00001734#if 0
Rusty Russell54c307e2000-09-04 06:47:28 +00001735 TC_DUMP_ENTRIES(*handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001736#endif
1737
1738 /* Don't commit if nothing changed. */
1739 if (!(*handle)->changed)
1740 goto finished;
1741
1742 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1743 if (!repl) {
1744 errno = ENOMEM;
1745 return 0;
1746 }
1747
1748 /* These are the old counters we will get from kernel */
Rusty Russell79dee072000-05-02 16:45:16 +00001749 repl->counters = malloc(sizeof(STRUCT_COUNTERS)
Marc Bouchere6869a82000-03-20 06:03:29 +00001750 * (*handle)->info.num_entries);
1751 if (!repl->counters) {
1752 free(repl);
1753 errno = ENOMEM;
1754 return 0;
1755 }
Rusty Russell7e53bf92000-03-20 07:03:28 +00001756
Marc Bouchere6869a82000-03-20 06:03:29 +00001757 /* These are the counters we're going to put back, later. */
1758 newcounters = malloc(counterlen);
1759 if (!newcounters) {
1760 free(repl->counters);
1761 free(repl);
1762 errno = ENOMEM;
1763 return 0;
1764 }
1765
1766 strcpy(repl->name, (*handle)->info.name);
1767 repl->num_entries = (*handle)->new_number;
1768 repl->size = (*handle)->entries.size;
1769 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1770 sizeof(repl->hook_entry));
1771 memcpy(repl->underflow, (*handle)->info.underflow,
1772 sizeof(repl->underflow));
1773 repl->num_counters = (*handle)->info.num_entries;
1774 repl->valid_hooks = (*handle)->info.valid_hooks;
Rusty Russell725d97a2000-07-07 08:54:22 +00001775 memcpy(repl->entries, (*handle)->entries.entrytable,
Marc Bouchere6869a82000-03-20 06:03:29 +00001776 (*handle)->entries.size);
1777
Rusty Russell79dee072000-05-02 16:45:16 +00001778 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
Marc Bouchere6869a82000-03-20 06:03:29 +00001779 sizeof(*repl) + (*handle)->entries.size) < 0) {
1780 free(repl->counters);
1781 free(repl);
1782 free(newcounters);
1783 return 0;
1784 }
1785
1786 /* Put counters back. */
1787 strcpy(newcounters->name, (*handle)->info.name);
1788 newcounters->num_counters = (*handle)->new_number;
1789 for (i = 0; i < (*handle)->new_number; i++) {
1790 unsigned int mappos = (*handle)->counter_map[i].mappos;
1791 switch ((*handle)->counter_map[i].maptype) {
1792 case COUNTER_MAP_NOMAP:
1793 newcounters->counters[i]
Rusty Russell79dee072000-05-02 16:45:16 +00001794 = ((STRUCT_COUNTERS){ 0, 0 });
Marc Bouchere6869a82000-03-20 06:03:29 +00001795 break;
1796
1797 case COUNTER_MAP_NORMAL_MAP:
1798 /* Original read: X.
1799 * Atomic read on replacement: X + Y.
1800 * Currently in kernel: Z.
1801 * Want in kernel: X + Y + Z.
1802 * => Add in X + Y
1803 * => Add in replacement read.
1804 */
1805 newcounters->counters[i] = repl->counters[mappos];
1806 break;
1807
1808 case COUNTER_MAP_ZEROED:
1809 /* Original read: X.
1810 * Atomic read on replacement: X + Y.
1811 * Currently in kernel: Z.
1812 * Want in kernel: Y + Z.
1813 * => Add in Y.
1814 * => Add in (replacement read - original read).
1815 */
1816 subtract_counters(&newcounters->counters[i],
1817 &repl->counters[mappos],
1818 &index2entry(*handle, i)->counters);
1819 break;
Harald Welte1cef74d2001-01-05 15:22:59 +00001820
1821 case COUNTER_MAP_SET:
1822 /* Want to set counter (iptables-restore) */
1823
1824 memcpy(&newcounters->counters[i],
1825 &index2entry(*handle, i)->counters,
1826 sizeof(STRUCT_COUNTERS));
1827
1828 break;
Marc Bouchere6869a82000-03-20 06:03:29 +00001829 }
1830 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001831
1832#ifdef KERNEL_64_USERSPACE_32
1833 {
1834 /* Kernel will think that pointer should be 64-bits, and get
1835 padding. So we accomodate here (assumption: alignment of
1836 `counters' is on 64-bit boundary). */
1837 u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
1838 if ((unsigned long)&newcounters->counters % 8 != 0) {
1839 fprintf(stderr,
1840 "counters alignment incorrect! Mail rusty!\n");
1841 abort();
1842 }
1843 *kernptr = newcounters->counters;
Rusty Russell54c307e2000-09-04 06:47:28 +00001844 }
Rusty Russell62527ce2000-09-04 09:45:54 +00001845#endif /* KERNEL_64_USERSPACE_32 */
Marc Bouchere6869a82000-03-20 06:03:29 +00001846
Rusty Russell79dee072000-05-02 16:45:16 +00001847 if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
1848 newcounters, counterlen) < 0) {
Marc Bouchere6869a82000-03-20 06:03:29 +00001849 free(repl->counters);
1850 free(repl);
1851 free(newcounters);
1852 return 0;
1853 }
1854
1855 free(repl->counters);
1856 free(repl);
1857 free(newcounters);
1858
1859 finished:
Martin Josefsson841e4ae2003-05-02 15:30:11 +00001860 TC_FREE(handle);
Marc Bouchere6869a82000-03-20 06:03:29 +00001861 return 1;
1862}
1863
1864/* Get raw socket. */
1865int
Rusty Russell79dee072000-05-02 16:45:16 +00001866TC_GET_RAW_SOCKET()
Marc Bouchere6869a82000-03-20 06:03:29 +00001867{
1868 return sockfd;
1869}
1870
1871/* Translates errno numbers into more human-readable form than strerror. */
1872const char *
Rusty Russell79dee072000-05-02 16:45:16 +00001873TC_STRERROR(int err)
Marc Bouchere6869a82000-03-20 06:03:29 +00001874{
1875 unsigned int i;
1876 struct table_struct {
1877 void *fn;
1878 int err;
1879 const char *message;
1880 } table [] =
Harald Welte4ccfa632001-07-30 15:12:43 +00001881 { { TC_INIT, EPERM, "Permission denied (you must be root)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001882 { TC_INIT, EINVAL, "Module is wrong version" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001883 { TC_INIT, ENOENT,
1884 "Table does not exist (do you need to insmod?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001885 { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
1886 { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
1887 { TC_DELETE_CHAIN, EMLINK,
Marc Bouchere6869a82000-03-20 06:03:29 +00001888 "Can't delete chain with references left" },
Rusty Russell79dee072000-05-02 16:45:16 +00001889 { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
1890 { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
1891 { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
1892 { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
Harald Welte1cef74d2001-01-05 15:22:59 +00001893 { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
1894 { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
Rusty Russell79dee072000-05-02 16:45:16 +00001895 { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
1896 { TC_INSERT_ENTRY, EINVAL, "Target problem" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001897 /* EINVAL for CHECK probably means bad interface. */
Rusty Russell79dee072000-05-02 16:45:16 +00001898 { TC_CHECK_PACKET, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001899 "Bad arguments (does that interface exist?)" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001900 { TC_CHECK_PACKET, ENOSYS,
1901 "Checking will most likely never get implemented" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001902 /* ENOENT for DELETE probably means no matching rule */
Rusty Russell79dee072000-05-02 16:45:16 +00001903 { TC_DELETE_ENTRY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001904 "Bad rule (does a matching rule exist in that chain?)" },
Rusty Russell79dee072000-05-02 16:45:16 +00001905 { TC_SET_POLICY, ENOENT,
Marc Boucherc8264992000-04-22 22:34:44 +00001906 "Bad built-in chain name" },
Rusty Russell79dee072000-05-02 16:45:16 +00001907 { TC_SET_POLICY, EINVAL,
Marc Boucherc8264992000-04-22 22:34:44 +00001908 "Bad policy name" },
Harald Welte4ccfa632001-07-30 15:12:43 +00001909
1910 { NULL, 0, "Incompatible with this kernel" },
1911 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1912 { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" },
1913 { NULL, ENOMEM, "Memory allocation problem" },
1914 { NULL, ENOENT, "No chain/target/match by that name" },
Marc Bouchere6869a82000-03-20 06:03:29 +00001915 };
1916
1917 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1918 if ((!table[i].fn || table[i].fn == iptc_fn)
1919 && table[i].err == err)
1920 return table[i].message;
1921 }
1922
1923 return strerror(err);
1924}