blob: 9109703303bf9067df491e588e32ec6b0441021d [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* Library which manipulates firewall rules. Version 0.1. */
2
3/* Architecture of firewall rules is as follows:
4 *
5 * Chains go INPUT, FORWARD, OUTPUT then user chains.
6 * Each user chain starts with an ERROR node.
7 * Every chain ends with an unconditional jump: a RETURN for user chains,
8 * and a POLICY for built-ins.
9 */
10
11/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
12 COPYING for details). */
13
14#include <assert.h>
15#include <string.h>
16#include <errno.h>
17#include <stdlib.h>
18#include <stdio.h>
19
20#if !defined(__GLIBC__) || (__GLIBC__ < 2)
21typedef unsigned int socklen_t;
22#endif
23
24#include <libiptc/libiptc.h>
25
26#define IP_VERSION 4
27#define IP_OFFSET 0x1FFF
28
29#ifndef IPT_LIB_DIR
30#define IPT_LIB_DIR "/usr/local/lib/iptables"
31#endif
32
33static int sockfd = -1;
34static void *iptc_fn = NULL;
35
36static const char *hooknames[]
37= { [NF_IP_PRE_ROUTING] "PREROUTING",
38 [NF_IP_LOCAL_IN] "INPUT",
39 [NF_IP_FORWARD] "FORWARD",
40 [NF_IP_LOCAL_OUT] "OUTPUT",
41 [NF_IP_POST_ROUTING] "POSTROUTING"
42};
43
44struct counter_map
45{
46 enum {
47 COUNTER_MAP_NOMAP,
48 COUNTER_MAP_NORMAL_MAP,
49 COUNTER_MAP_ZEROED
50 } maptype;
51 unsigned int mappos;
52};
53
54/* Convenience structures */
55struct ipt_error_target
56{
57 struct ipt_entry_target t;
58 char error[IPT_TABLE_MAXNAMELEN];
59};
60
61struct iptc_handle
62{
63 /* Have changes been made? */
64 int changed;
65 /* Size in here reflects original state. */
66 struct ipt_getinfo info;
67
68 struct counter_map *counter_map;
69 /* Array of hook names */
70 const char **hooknames;
71
72 /* Number in here reflects current state. */
73 unsigned int new_number;
74 struct ipt_get_entries entries;
75};
76
77static void do_check(iptc_handle_t h, unsigned int line);
78#define CHECK(h) do_check((h), __LINE__)
79
80static inline int
81get_number(const struct ipt_entry *i,
82 const struct ipt_entry *seek,
83 unsigned int *pos)
84{
85 if (i == seek)
86 return 1;
87 (*pos)++;
88 return 0;
89}
90
91static unsigned int
92entry2index(const iptc_handle_t h, const struct ipt_entry *seek)
93{
94 unsigned int pos = 0;
95
96 if (IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
97 get_number, seek, &pos) == 0) {
98 fprintf(stderr, "ERROR: offset %i not an entry!\n",
99 (unsigned char *)seek - h->entries.entries);
100 abort();
101 }
102 return pos;
103}
104
105static inline int
106get_entry_n(struct ipt_entry *i,
107 unsigned int number,
108 unsigned int *pos,
109 struct ipt_entry **pe)
110{
111 if (*pos == number) {
112 *pe = i;
113 return 1;
114 }
115 (*pos)++;
116 return 0;
117}
118
119static struct ipt_entry *
120index2entry(iptc_handle_t h, unsigned int index)
121{
122 unsigned int pos = 0;
123 struct ipt_entry *ret = NULL;
124
125 IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
126 get_entry_n, index, &pos, &ret);
127
128 return ret;
129}
130
131static inline struct ipt_entry *
132get_entry(iptc_handle_t h, unsigned int offset)
133{
134 return (struct ipt_entry *)(h->entries.entries + offset);
135}
136
137static inline unsigned long
138entry2offset(const iptc_handle_t h, const struct ipt_entry *e)
139{
140 return (unsigned char *)e - h->entries.entries;
141}
142
143static unsigned long
144index2offset(iptc_handle_t h, unsigned int index)
145{
146 return entry2offset(h, index2entry(h, index));
147}
148
149static const char *
150get_errorlabel(iptc_handle_t h, unsigned int offset)
151{
152 struct ipt_entry *e;
153
154 e = get_entry(h, offset);
155 if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) != 0) {
156 fprintf(stderr, "ERROR: offset %u not an error node!\n",
157 offset);
158 abort();
159 }
160
161 return (const char *)ipt_get_target(e)->data;
162}
163
164/* Allocate handle of given size */
165static iptc_handle_t
166alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
167{
168 size_t len;
169 iptc_handle_t h;
170
171 len = sizeof(struct iptc_handle)
172 + size
173 + num_rules * sizeof(struct counter_map);
174
175 if ((h = malloc(len)) == NULL) {
176 errno = ENOMEM;
177 return NULL;
178 }
179
180 h->changed = 0;
181 h->counter_map = (void *)h
182 + sizeof(struct iptc_handle)
183 + size;
184 strcpy(h->info.name, tablename);
185 strcpy(h->entries.name, tablename);
186
187 return h;
188}
189
190iptc_handle_t
191iptc_init(const char *tablename)
192{
193 iptc_handle_t h;
194 struct ipt_getinfo info;
195 unsigned int i;
196 int tmp;
197 socklen_t s;
198
199 iptc_fn = iptc_init;
200
201 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
202 if (sockfd < 0)
203 return NULL;
204
205 s = sizeof(info);
206 if (strlen(tablename) >= IPT_TABLE_MAXNAMELEN) {
207 errno = EINVAL;
208 return NULL;
209 }
210 strcpy(info.name, tablename);
211 if (getsockopt(sockfd, IPPROTO_IP, IPT_SO_GET_INFO, &info, &s) < 0)
212 return NULL;
213
214 if ((h = alloc_handle(info.name, info.size, info.num_entries))
215 == NULL)
216 return NULL;
217
218/* Too hard --RR */
219#if 0
220 sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
221 dynlib = dlopen(pathname, RTLD_NOW);
222 if (!dynlib) {
223 errno = ENOENT;
224 return NULL;
225 }
226 h->hooknames = dlsym(dynlib, "hooknames");
227 if (!h->hooknames) {
228 errno = ENOENT;
229 return NULL;
230 }
231#else
232 h->hooknames = hooknames;
233#endif
234
235 /* Initialize current state */
236 h->info = info;
237 h->new_number = h->info.num_entries;
238 for (i = 0; i < h->info.num_entries; i++)
239 h->counter_map[i]
240 = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
241
242 h->entries.size = h->info.size;
243
244 tmp = sizeof(struct ipt_get_entries) + h->info.size;
245
246 if (getsockopt(sockfd, IPPROTO_IP, IPT_SO_GET_ENTRIES, &h->entries,
247 &tmp) < 0) {
248 free(h);
249 return NULL;
250 }
251
252 CHECK(h);
253 return h;
254}
255
256#define IP_PARTS_NATIVE(n) \
257(unsigned int)((n)>>24)&0xFF, \
258(unsigned int)((n)>>16)&0xFF, \
259(unsigned int)((n)>>8)&0xFF, \
260(unsigned int)((n)&0xFF)
261
262#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
263
264static inline int
265print_match(const struct ipt_entry_match *m)
266{
267 printf("Match name: `%s'\n", m->u.name);
268 return 0;
269}
270
271int
272dump_entry(struct ipt_entry *e, const iptc_handle_t handle)
273{
274 size_t i;
275 struct ipt_entry_target *t;
276
277 printf("Entry %u (%lu):\n", entry2index(handle, e),
278 entry2offset(handle, e));
279 printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
280 IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
281 printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
282 IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
283 printf("Interface: `%s'/", e->ip.iniface);
284 for (i = 0; i < IFNAMSIZ; i++)
285 printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
286 printf("to `%s'/", e->ip.outiface);
287 for (i = 0; i < IFNAMSIZ; i++)
288 printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
289 printf("\nProtocol: %u\n", e->ip.proto);
290 printf("Flags: %02X\n", e->ip.flags);
291 printf("Invflags: %02X\n", e->ip.invflags);
292 printf("Counters: %llu packets, %llu bytes\n",
293 e->counters.pcnt, e->counters.bcnt);
294 printf("Cache: %08X ", e->nfcache);
295 if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
296 if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
297 if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
298 if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
299 if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
300 if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
301 if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
302 if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
303 if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
304 if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
305 if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
306 if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
307 if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
308 printf("\n");
309
310 IPT_MATCH_ITERATE(e, print_match);
311
312 t = ipt_get_target(e);
313 printf("Target name: `%s' [%u]\n", t->u.name, t->target_size);
314 if (strcmp(t->u.name, IPT_STANDARD_TARGET) == 0) {
315 int pos = *(int *)t->data;
316 if (pos < 0)
317 printf("verdict=%s\n",
318 pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
319 : pos == -NF_DROP-1 ? "NF_DROP"
320 : pos == -NF_QUEUE-1 ? "NF_QUEUE"
321 : pos == IPT_RETURN ? "RETURN"
322 : "UNKNOWN");
323 else
324 printf("verdict=%u\n", pos);
325 } else if (strcmp(t->u.name, IPT_ERROR_TARGET) == 0)
326 printf("error=`%s'\n", t->data);
327
328 printf("\n");
329 return 0;
330}
331
332void
333dump_entries(const iptc_handle_t handle)
334{
335 CHECK(handle);
336
337 printf("libiptc v%s. %u entries, %u bytes.\n",
338 NETFILTER_VERSION,
339 handle->new_number, handle->entries.size);
340 printf("Table `%s'\n", handle->info.name);
341 printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
342 handle->info.hook_entry[NF_IP_PRE_ROUTING],
343 handle->info.hook_entry[NF_IP_LOCAL_IN],
344 handle->info.hook_entry[NF_IP_FORWARD],
345 handle->info.hook_entry[NF_IP_LOCAL_OUT],
346 handle->info.hook_entry[NF_IP_POST_ROUTING]);
347 printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
348 handle->info.underflow[NF_IP_PRE_ROUTING],
349 handle->info.underflow[NF_IP_LOCAL_IN],
350 handle->info.underflow[NF_IP_FORWARD],
351 handle->info.underflow[NF_IP_LOCAL_OUT],
352 handle->info.underflow[NF_IP_POST_ROUTING]);
353
354 IPT_ENTRY_ITERATE(handle->entries.entries, handle->entries.size,
355 dump_entry, handle);
356}
357
358static inline int
359find_user_label(struct ipt_entry *e, unsigned int *off, const char *name)
360{
361 /* Increment first: they want offset of entry AFTER label */
362 (*off) += e->next_offset;
363
364 if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) == 0
365 && strcmp(ipt_get_target(e)->data, name) == 0)
366 return 1;
367
368 return 0;
369}
370
371/* Returns offset of label. */
372static int
373find_label(unsigned int *off,
374 const char *name,
375 const iptc_handle_t handle)
376{
377 unsigned int i;
378
379 /* Builtin chain name? */
380 i = iptc_builtin(name, handle);
381 if (i != 0) {
382 *off = handle->info.hook_entry[i-1];
383 return 1;
384 }
385
386 /* User chain name? */
387 *off = 0;
388 if (IPT_ENTRY_ITERATE(handle->entries.entries, handle->entries.size,
389 find_user_label, off, name) != 0) {
390 /* last error node doesn't count */
391 if (*off != handle->entries.size)
392 return 1;
393 }
394
395 return 0;
396}
397
398/* Does this chain exist? */
399int iptc_is_chain(const char *chain, const iptc_handle_t handle)
400{
401 unsigned int dummy;
402
403 /* avoid infinite recursion */
404#if 0
405 CHECK(handle);
406#endif
407
408 return find_label(&dummy, chain, handle);
409}
410
411/* Returns the position of the final (ie. unconditional) element. */
412static unsigned int
413get_chain_end(const iptc_handle_t handle, unsigned int start)
414{
415 unsigned int last_off, off;
416 struct ipt_entry *e;
417
418 last_off = start;
419 e = get_entry(handle, start);
420
421 /* Terminate when we meet a error label or a hook entry. */
422 for (off = start + e->next_offset;
423 off < handle->entries.size;
424 last_off = off, off += e->next_offset) {
425 struct ipt_entry_target *t;
426 unsigned int i;
427
428 e = get_entry(handle, off);
429
430 /* We hit an entry point. */
431 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
432 if ((handle->info.valid_hooks & (1 << i))
433 && off == handle->info.hook_entry[i])
434 return last_off;
435 }
436
437 /* We hit a user chain label */
438 t = ipt_get_target(e);
439 if (strcmp(t->u.name, IPT_ERROR_TARGET) == 0)
440 return last_off;
441 }
442 /* SHOULD NEVER HAPPEN */
443 fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
444 handle->entries.size, off);
445 abort();
446}
447
448/* Iterator functions to run through the chains; prev = NULL means
449 first chain. Returns NULL at end. */
450const char *
451iptc_next_chain(const char *prev, iptc_handle_t *handle)
452{
453 unsigned int pos;
454 unsigned int i;
455 struct ipt_entry *e;
456
457 CHECK(*handle);
458 if (!prev)
459 pos = 0;
460 else {
461 if (!find_label(&pos, prev, *handle)) {
462 errno = ENOENT;
463 return NULL;
464 }
465 pos = get_chain_end(*handle, pos);
466 /* Next entry. */
467 e = get_entry(*handle, pos);
468 pos += e->next_offset;
469 }
470 e = get_entry(*handle, pos);
471
472 /* Return names of entry points if it is one. */
473 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
474 if (((*handle)->info.valid_hooks & (1 << i))
475 && pos == (*handle)->info.hook_entry[i])
476 return (*handle)->hooknames[i];
477 }
478 /* If this is the last element, iteration finished */
479 if (pos + e->next_offset == (*handle)->entries.size)
480 return NULL;
481
482 if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) != 0) {
483 /* SHOULD NEVER HAPPEN */
484 fprintf(stderr, "ERROR: position %u/%u not an error label\n",
485 pos, (*handle)->entries.size);
486 abort();
487 }
488
489 return (const char *)ipt_get_target(e)->data;
490}
491
492/* How many rules in this chain? */
493unsigned int
494iptc_num_rules(const char *chain, iptc_handle_t *handle)
495{
496 unsigned int off = 0;
497 struct ipt_entry *start, *end;
498
499 CHECK(*handle);
500 if (!find_label(&off, chain, *handle)) {
501 errno = ENOENT;
502 return (unsigned int)-1;
503 }
504
505 start = get_entry(*handle, off);
506 end = get_entry(*handle, get_chain_end(*handle, off));
507
508 return entry2index(*handle, end) - entry2index(*handle, start);
509}
510
511/* Get n'th rule in this chain. */
512const struct ipt_entry *iptc_get_rule(const char *chain,
513 unsigned int n,
514 iptc_handle_t *handle)
515{
516 unsigned int pos = 0, chainindex;
517
518 CHECK(*handle);
519 if (!find_label(&pos, chain, *handle)) {
520 errno = ENOENT;
521 return NULL;
522 }
523
524 chainindex = entry2index(*handle, get_entry(*handle, pos));
525
526 return index2entry(*handle, chainindex + n);
527}
528
529static const char *target_name(iptc_handle_t handle, struct ipt_entry *e)
530{
531 int spos;
532 unsigned int labelidx;
533 struct ipt_entry *jumpto;
534
535 if (strcmp(ipt_get_target(e)->u.name, IPT_STANDARD_TARGET) != 0)
536 return ipt_get_target(e)->u.name;
537
538 /* Standard target: evaluate */
539 spos = *(int *)ipt_get_target(e)->data;
540 if (spos < 0) {
541 if (spos == IPT_RETURN)
542 return IPTC_LABEL_RETURN;
543 else if (spos == -NF_ACCEPT-1)
544 return IPTC_LABEL_ACCEPT;
545 else if (spos == -NF_DROP-1)
546 return IPTC_LABEL_DROP;
547 else if (spos == -NF_ACCEPT-1)
548 return IPTC_LABEL_QUEUE;
549
550 fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
551 entry2offset(handle, e), handle->entries.size,
552 spos);
553 abort();
554 }
555
556 jumpto = get_entry(handle, spos);
557
558 /* Fall through rule */
559 if (jumpto == (void *)e + e->next_offset)
560 return "";
561
562 /* Must point to head of a chain: ie. after error rule */
563 labelidx = entry2index(handle, jumpto) - 1;
564 return get_errorlabel(handle, index2offset(handle, labelidx));
565}
566
567/* Returns a pointer to the target name of this position. */
568const char *iptc_get_target(const char *chain,
569 unsigned int n,
570 iptc_handle_t *handle)
571{
572 unsigned int pos = 0, chainindex;
573 struct ipt_entry *e;
574
575 CHECK(*handle);
576 if (!find_label(&pos, chain, *handle)) {
577 errno = ENOENT;
578 return NULL;
579 }
580
581 chainindex = entry2index(*handle, get_entry(*handle, pos));
582 e = index2entry(*handle, chainindex + n);
583
584 return target_name(*handle, e);
585}
586
587/* Is this a built-in chain? Actually returns hook + 1. */
588int
589iptc_builtin(const char *chain, const iptc_handle_t handle)
590{
591 unsigned int i;
592
593 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
594 if ((handle->info.valid_hooks & (1 << i))
595 && handle->hooknames[i]
596 && strcmp(handle->hooknames[i], chain) == 0)
597 return i+1;
598 }
599 return 0;
600}
601
602/* Get the policy of a given built-in chain */
603const char *
604iptc_get_policy(const char *chain,
605 struct ipt_counters *counters,
606 iptc_handle_t *handle)
607{
608 unsigned int start;
609 struct ipt_entry *e;
610 int hook;
611
612 CHECK(*handle);
613 hook = iptc_builtin(chain, *handle);
614 if (hook != 0)
615 start = (*handle)->info.hook_entry[hook-1];
616 else
617 return NULL;
618
619 e = get_entry(*handle, get_chain_end(*handle, start));
620 *counters = e->counters;
621
622 return target_name(*handle, e);
623}
624
625static int
626correct_verdict(struct ipt_entry *e,
627 unsigned char *base,
628 unsigned int offset, int delta_offset)
629{
630 struct ipt_standard_target *t = (void *)ipt_get_target(e);
631 unsigned int curr = (unsigned char *)e - base;
632
633 /* Trap: insert of fall-through rule. Don't change fall-through
634 verdict to jump-over-next-rule. */
635 if (strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0
636 && t->verdict > (int)offset
637 && !(curr == offset &&
638 t->verdict == curr + e->next_offset)) {
639 t->verdict += delta_offset;
640 }
641
642 return 0;
643}
644
645/* Adjusts standard verdict jump positions after an insertion/deletion. */
646static int
647set_verdict(unsigned int offset, int delta_offset, iptc_handle_t *handle)
648{
649 IPT_ENTRY_ITERATE((*handle)->entries.entries,
650 (*handle)->entries.size,
651 correct_verdict, (*handle)->entries.entries,
652 offset, delta_offset);
653
654 (*handle)->changed = 1;
655 return 1;
656}
657
658/* If prepend is set, then we are prepending to a chain: if the
659 * insertion position is an entry point, keep the entry point. */
660static int
661insert_rules(unsigned int num_rules, unsigned int rules_size,
662 const struct ipt_entry *insert,
663 unsigned int offset, unsigned int num_rules_offset,
664 int prepend,
665 iptc_handle_t *handle)
666{
667 iptc_handle_t newh;
668 struct ipt_getinfo newinfo;
669 unsigned int i;
670
671 if (offset >= (*handle)->entries.size) {
672 errno = EINVAL;
673 return 0;
674 }
675
676 newinfo = (*handle)->info;
677
678 /* Fix up entry points. */
679 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
680 /* Entry points to START of chain, so keep same if
681 inserting on at that point. */
682 if ((*handle)->info.hook_entry[i] > offset)
683 newinfo.hook_entry[i] += rules_size;
684
685 /* Underflow always points to END of chain (policy),
686 so if something is inserted at same point, it
687 should be advanced. */
688 if ((*handle)->info.underflow[i] >= offset)
689 newinfo.underflow[i] += rules_size;
690 }
691
692 newh = alloc_handle((*handle)->info.name,
693 (*handle)->info.size + rules_size,
694 (*handle)->info.num_entries + num_rules);
695 if (!newh)
696 return 0;
697 newh->info = newinfo;
698
699 /* Copy pre... */
700 memcpy(newh->entries.entries, (*handle)->entries.entries, offset);
701 /* ... Insert new ... */
702 memcpy(newh->entries.entries + offset, insert, rules_size);
703 /* ... copy post */
704 memcpy(newh->entries.entries + offset + rules_size,
705 (*handle)->entries.entries + offset,
706 (*handle)->entries.size - offset);
707
708 /* Move counter map. */
709 /* Copy pre... */
710 memcpy(newh->counter_map, (*handle)->counter_map,
711 sizeof(struct counter_map) * num_rules_offset);
712 /* ... copy post */
713 memcpy(newh->counter_map + num_rules_offset + num_rules,
714 (*handle)->counter_map + num_rules_offset,
715 sizeof(struct counter_map) * ((*handle)->new_number
716 - num_rules_offset));
717 /* Set intermediates to no counter copy */
718 for (i = 0; i < num_rules; i++)
719 newh->counter_map[num_rules_offset+i]
720 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
721
722 newh->new_number = (*handle)->new_number + num_rules;
723 newh->entries.size = (*handle)->entries.size + rules_size;
724 newh->hooknames = (*handle)->hooknames;
725
726 free(*handle);
727 *handle = newh;
728
729 return set_verdict(offset, rules_size, handle);
730}
731
732static int
733delete_rules(unsigned int num_rules, unsigned int rules_size,
734 unsigned int offset, unsigned int num_rules_offset,
735 iptc_handle_t *handle)
736{
737 unsigned int i;
738
739 if (offset + rules_size > (*handle)->entries.size) {
740 errno = EINVAL;
741 return 0;
742 }
743
744 /* Fix up entry points. */
745 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
746 /* In practice, we never delete up to a hook entry,
747 since the built-in chains are always first,
748 so these two are never equal */
749 if ((*handle)->info.hook_entry[i] >= offset + rules_size)
750 (*handle)->info.hook_entry[i] -= rules_size;
751 else if ((*handle)->info.hook_entry[i] > offset) {
752 fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
753 i, (*handle)->info.hook_entry[i], offset);
754 abort();
755 }
756
757 /* Underflow points to policy (terminal) rule in
758 built-in, so sequality is valid here (when deleting
759 the last rule). */
760 if ((*handle)->info.underflow[i] >= offset + rules_size)
761 (*handle)->info.underflow[i] -= rules_size;
762 else if ((*handle)->info.underflow[i] > offset) {
763 fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
764 i, (*handle)->info.underflow[i], offset);
765 abort();
766 }
767 }
768
769 /* Move the rules down. */
770 memmove((*handle)->entries.entries + offset,
771 (*handle)->entries.entries + offset + rules_size,
772 (*handle)->entries.size - (offset + rules_size));
773
774 /* Move the counter map down. */
775 memmove(&(*handle)->counter_map[num_rules_offset],
776 &(*handle)->counter_map[num_rules_offset + num_rules],
777 sizeof(struct counter_map)
778 * ((*handle)->new_number - (num_rules + num_rules_offset)));
779
780 /* Fix numbers */
781 (*handle)->new_number -= num_rules;
782 (*handle)->entries.size -= rules_size;
783
784 return set_verdict(offset, -(int)rules_size, handle);
785}
786
787static int
788standard_map(struct ipt_entry *e, int verdict)
789{
790 struct ipt_standard_target *t;
791
792 t = (struct ipt_standard_target *)ipt_get_target(e);
793
794 if (t->target.target_size != IPT_ALIGN(sizeof(struct ipt_standard_target))) {
795 errno = EINVAL;
796 return 0;
797 }
798 /* memset for memcmp convenience on delete/replace */
799 memset(t->target.u.name, 0, IPT_FUNCTION_MAXNAMELEN);
800 strcpy(t->target.u.name, IPT_STANDARD_TARGET);
801 t->verdict = verdict;
802
803 return 1;
804}
805
806static int
807map_target(const iptc_handle_t handle,
808 struct ipt_entry *e,
809 unsigned int offset,
810 struct ipt_entry_target *old)
811{
812 struct ipt_entry_target *t = ipt_get_target(e);
813
814 /* Save old target (except data, which we don't change, except for
815 standard case, where we don't care). */
816 *old = *t;
817
818 /* Maybe it's empty (=> fall through) */
819 if (strcmp(t->u.name, "") == 0)
820 return standard_map(e, offset + e->next_offset);
821 /* Maybe it's a standard target name... */
822 else if (strcmp(t->u.name, IPTC_LABEL_ACCEPT) == 0)
823 return standard_map(e, -NF_ACCEPT - 1);
824 else if (strcmp(t->u.name, IPTC_LABEL_DROP) == 0)
825 return standard_map(e, -NF_DROP - 1);
826 else if (strcmp(t->u.name, IPTC_LABEL_QUEUE) == 0)
827 return standard_map(e, -NF_QUEUE - 1);
828 else if (strcmp(t->u.name, IPTC_LABEL_RETURN) == 0)
829 return standard_map(e, IPT_RETURN);
830 else if (iptc_builtin(t->u.name, handle)) {
831 /* Can't jump to builtins. */
832 errno = EINVAL;
833 return 0;
834 } else {
835 /* Maybe it's an existing chain name. */
836 unsigned int exists;
837
838 if (find_label(&exists, t->u.name, handle))
839 return standard_map(e, exists);
840 }
841
842 /* Must be a module? If not, kernel will reject... */
843 /* memset to all 0 for your memcmp convenience. */
844 memset(t->u.name + strlen(t->u.name),
845 0,
846 IPT_FUNCTION_MAXNAMELEN - strlen(t->u.name));
847 return 1;
848}
849
850static void
851unmap_target(struct ipt_entry *e, struct ipt_entry_target *old)
852{
853 struct ipt_entry_target *t = ipt_get_target(e);
854
855 /* Save old target (except data, which we don't change, except for
856 standard case, where we don't care). */
857 *t = *old;
858}
859
860/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
861int
862iptc_insert_entry(const ipt_chainlabel chain,
863 const struct ipt_entry *e,
864 unsigned int rulenum,
865 iptc_handle_t *handle)
866{
867 unsigned int chainoff, chainindex, offset;
868 struct ipt_entry_target old;
869 int ret;
870
871 CHECK(*handle);
872 iptc_fn = iptc_insert_entry;
873 if (!find_label(&chainoff, chain, *handle)) {
874 errno = ENOENT;
875 return 0;
876 }
877
878 chainindex = entry2index(*handle, get_entry(*handle, chainoff));
879
880 if (index2entry(*handle, chainindex + rulenum)
881 > get_entry(*handle, get_chain_end(*handle, chainoff))) {
882 errno = E2BIG;
883 return 0;
884 }
885 offset = index2offset(*handle, chainindex + rulenum);
886
887 /* Mapping target actually alters entry, but that's
888 transparent to the caller. */
889 if (!map_target(*handle, (struct ipt_entry *)e, offset, &old))
890 return 0;
891
892 ret = insert_rules(1, e->next_offset, e, offset,
893 chainindex + rulenum, rulenum == 0, handle);
894 unmap_target((struct ipt_entry *)e, &old);
895 CHECK(*handle);
896 return ret;
897}
898
899/* Atomically replace rule `rulenum' in `chain' with `fw'. */
900int
901iptc_replace_entry(const ipt_chainlabel chain,
902 const struct ipt_entry *e,
903 unsigned int rulenum,
904 iptc_handle_t *handle)
905{
906 unsigned int chainoff, chainindex, offset;
907 struct ipt_entry_target old;
908 int ret;
909
910 CHECK(*handle);
911 iptc_fn = iptc_replace_entry;
912
913 if (!find_label(&chainoff, chain, *handle)) {
914 errno = ENOENT;
915 return 0;
916 }
917
918 chainindex = entry2index(*handle, get_entry(*handle, chainoff));
919
920 if (index2entry(*handle, chainindex + rulenum)
921 >= get_entry(*handle, get_chain_end(*handle, chainoff))) {
922 errno = E2BIG;
923 return 0;
924 }
925
926 offset = index2offset(*handle, chainindex + rulenum);
927 /* Replace = delete and insert. */
928 if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
929 offset, chainindex + rulenum, handle))
930 return 0;
931
932 if (!map_target(*handle, (struct ipt_entry *)e, offset, &old))
933 return 0;
934 CHECK(*handle);
935
936 ret = insert_rules(1, e->next_offset, e, offset,
937 chainindex + rulenum, 1, handle);
938 unmap_target((struct ipt_entry *)e, &old);
939 CHECK(*handle);
940 return ret;
941}
942
943/* Append entry `fw' to chain `chain'. Equivalent to insert with
944 rulenum = length of chain. */
945int
946iptc_append_entry(const ipt_chainlabel chain,
947 const struct ipt_entry *e,
948 iptc_handle_t *handle)
949{
950 unsigned int startoff, endoff;
951 struct ipt_entry_target old;
952 int ret;
953
954 CHECK(*handle);
955 iptc_fn = iptc_append_entry;
956 if (!find_label(&startoff, chain, *handle)) {
957 errno = ENOENT;
958 return 0;
959 }
960
961 endoff = get_chain_end(*handle, startoff);
962 if (!map_target(*handle, (struct ipt_entry *)e, endoff, &old))
963 return 0;
964
965 ret = insert_rules(1, e->next_offset, e, endoff,
966 entry2index(*handle, get_entry(*handle, endoff)),
967 0, handle);
968 unmap_target((struct ipt_entry *)e, &old);
969 CHECK(*handle);
970 return ret;
971}
972
973static inline int
974match_different(const struct ipt_entry_match *a,
975 const char *a_elems,
976 const char *b_elems)
977{
978 const struct ipt_entry_match *b;
979
980 /* Offset of b is the same as a. */
981 b = (void *)b_elems + (a_elems - (char *)a);
982
983 if (a->match_size != b->match_size)
984 return 1;
985
986 if (strcmp(a->u.name, b->u.name) != 0)
987 return 1;
988
989 /* FIXME: If kernel modifies these (eg. RATE), then we'll
990 never match --RR */
991 if (memcmp(a->data, b->data, a->match_size - sizeof(*a)) != 0)
992 return 1;
993
994 return 0;
995}
996
997static inline int
998is_same(const struct ipt_entry *a, const struct ipt_entry *b)
999{
1000 unsigned int i;
1001 struct ipt_entry_target *ta, *tb;
1002
1003 if (a->ip.src.s_addr != b->ip.src.s_addr
1004 || a->ip.dst.s_addr != b->ip.dst.s_addr
1005 || a->ip.smsk.s_addr != b->ip.smsk.s_addr
1006 || a->ip.smsk.s_addr != b->ip.smsk.s_addr
1007 || a->ip.proto != b->ip.proto
1008 || a->ip.flags != b->ip.flags
1009 || a->ip.invflags != b->ip.invflags)
1010 return 0;
1011
1012 for (i = 0; i < IFNAMSIZ; i++) {
1013 if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
1014 return 0;
1015 if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
1016 != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
1017 return 0;
1018 if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
1019 return 0;
1020 if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
1021 != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
1022 return 0;
1023 }
1024
1025 if (a->nfcache != b->nfcache
1026 || a->target_offset != b->target_offset
1027 || a->next_offset != b->next_offset)
1028 return 0;
1029
1030 if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems))
1031 return 0;
1032
1033 ta = ipt_get_target((struct ipt_entry *)a);
1034 tb = ipt_get_target((struct ipt_entry *)b);
1035 if (ta->target_size != tb->target_size)
1036 return 0;
1037 if (strcmp(ta->u.name, tb->u.name) != 0)
1038 return 0;
1039 /* FIXME: If kernel modifies these, then we never match --RR */
1040 if (memcmp(ta->data, tb->data, ta->target_size - sizeof(*ta)) != 0)
1041 return 0;
1042
1043 return 1;
1044}
1045
1046/* Delete the first rule in `chain' which matches `fw'. */
1047int
1048iptc_delete_entry(const ipt_chainlabel chain,
1049 const struct ipt_entry *origfw,
1050 iptc_handle_t *handle)
1051{
1052 unsigned int offset, lastoff;
1053 struct ipt_entry *e, *fw;
1054
1055 CHECK(*handle);
1056 iptc_fn = iptc_delete_entry;
1057 if (!find_label(&offset, chain, *handle)) {
1058 errno = ENOENT;
1059 return 0;
1060 }
1061
1062 fw = malloc(origfw->next_offset);
1063 if (fw == NULL) {
1064 errno = ENOMEM;
1065 return 0;
1066 }
1067 lastoff = get_chain_end(*handle, offset);
1068
1069 for (; offset < lastoff; offset += e->next_offset) {
1070 struct ipt_entry_target discard;
1071
1072 memcpy(fw, origfw, origfw->next_offset);
1073
1074 /* FIXME: handle this in is_same --RR */
1075 if (!map_target(*handle, fw, offset, &discard)) {
1076 free(fw);
1077 return 0;
1078 }
1079 e = get_entry(*handle, offset);
1080
1081#if 0
1082 printf("Deleting:\n");
1083 dump_entry(newe);
1084#endif
1085 if (is_same(e, fw)) {
1086 int ret;
1087 ret = delete_rules(1, e->next_offset,
1088 offset, entry2index(*handle, e),
1089 handle);
1090 free(fw);
1091 CHECK(*handle);
1092 return ret;
1093 }
1094 }
1095
1096 free(fw);
1097 errno = ENOENT;
1098 return 0;
1099}
1100
1101/* Delete the rule in position `rulenum' in `chain'. */
1102int
1103iptc_delete_num_entry(const ipt_chainlabel chain,
1104 unsigned int rulenum,
1105 iptc_handle_t *handle)
1106{
1107 unsigned int chainstart;
1108 unsigned int index;
1109 int ret;
1110 struct ipt_entry *e;
1111
1112 CHECK(*handle);
1113 iptc_fn = iptc_delete_num_entry;
1114 if (!find_label(&chainstart, chain, *handle)) {
1115 errno = ENOENT;
1116 return 0;
1117 }
1118
1119 index = entry2index(*handle, get_entry(*handle, chainstart))
1120 + rulenum;
1121
1122 if (index
1123 >= entry2index(*handle,
1124 get_entry(*handle,
1125 get_chain_end(*handle, chainstart)))) {
1126 errno = E2BIG;
1127 return 0;
1128 }
1129
1130 e = index2entry(*handle, index);
1131 if (e == NULL) {
1132 errno = EINVAL;
1133 return 0;
1134 }
1135
1136 ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
1137 index, handle);
1138 CHECK(*handle);
1139 return ret;
1140}
1141
1142/* Check the packet `fw' on chain `chain'. Returns the verdict, or
1143 NULL and sets errno. */
1144const char *
1145iptc_check_packet(const ipt_chainlabel chain,
1146 struct ipt_entry *entry,
1147 iptc_handle_t *handle)
1148{
1149 errno = ENOSYS;
1150 return NULL;
1151}
1152
1153/* Flushes the entries in the given chain (ie. empties chain). */
1154int
1155iptc_flush_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
1156{
1157 unsigned int startoff, endoff, startindex, endindex;
1158 int ret;
1159
1160 CHECK(*handle);
1161 iptc_fn = iptc_flush_entries;
1162 if (!find_label(&startoff, chain, *handle)) {
1163 errno = ENOENT;
1164 return 0;
1165 }
1166 endoff = get_chain_end(*handle, startoff);
1167 startindex = entry2index(*handle, get_entry(*handle, startoff));
1168 endindex = entry2index(*handle, get_entry(*handle, endoff));
1169
1170 ret = delete_rules(endindex - startindex,
1171 endoff - startoff, startoff, startindex,
1172 handle);
1173 CHECK(*handle);
1174 return ret;
1175}
1176
1177/* Zeroes the counters in a chain. */
1178int
1179iptc_zero_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
1180{
1181 unsigned int i, end;
1182
1183 CHECK(*handle);
1184 if (!find_label(&i, chain, *handle)) {
1185 errno = ENOENT;
1186 return 0;
1187 }
1188 end = get_chain_end(*handle, i);
1189
1190 i = entry2index(*handle, get_entry(*handle, i));
1191 end = entry2index(*handle, get_entry(*handle, end));
1192
1193 for (; i <= end; i++) {
1194 if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
1195 (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
1196 }
1197 (*handle)->changed = 1;
1198
1199 CHECK(*handle);
1200 return 1;
1201}
1202
1203/* Creates a new chain. */
1204/* To create a chain, create two rules: error node and unconditional
1205 * return. */
1206int
1207iptc_create_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
1208{
1209 unsigned int pos;
1210 int ret;
1211 struct {
1212 struct ipt_entry head;
1213 struct ipt_error_target name;
1214 struct ipt_entry ret;
1215 struct ipt_standard_target target;
1216 } newc;
1217
1218 CHECK(*handle);
1219 iptc_fn = iptc_create_chain;
1220
1221 /* find_label doesn't cover built-in targets: DROP, ACCEPT,
1222 QUEUE, RETURN. */
1223 if (find_label(&pos, chain, *handle)
1224 || strcmp(chain, IPTC_LABEL_DROP) == 0
1225 || strcmp(chain, IPTC_LABEL_ACCEPT) == 0
1226 || strcmp(chain, IPTC_LABEL_QUEUE) == 0
1227 || strcmp(chain, IPTC_LABEL_RETURN) == 0) {
1228 errno = EEXIST;
1229 return 0;
1230 }
1231
1232 if (strlen(chain)+1 > sizeof(ipt_chainlabel)) {
1233 errno = EINVAL;
1234 return 0;
1235 }
1236
1237 memset(&newc, 0, sizeof(newc));
1238 newc.head.target_offset = sizeof(struct ipt_entry);
1239 newc.head.next_offset
1240 = sizeof(struct ipt_entry) + sizeof(struct ipt_error_target);
1241 strcpy(newc.name.t.u.name, IPT_ERROR_TARGET);
1242 newc.name.t.target_size = sizeof(struct ipt_error_target);
1243 strcpy(newc.name.error, chain);
1244
1245 newc.ret.target_offset = sizeof(struct ipt_entry);
1246 newc.ret.next_offset
1247 = sizeof(struct ipt_entry)+sizeof(struct ipt_standard_target);
1248 strcpy(newc.target.target.u.name, IPT_STANDARD_TARGET);
1249 newc.target.target.target_size = sizeof(struct ipt_standard_target);
1250 newc.target.verdict = IPT_RETURN;
1251
1252 /* Add just before terminal entry */
1253 ret = insert_rules(2, sizeof(newc), &newc.head,
1254 index2offset(*handle, (*handle)->new_number - 1),
1255 (*handle)->new_number - 1,
1256 0, handle);
1257 CHECK(*handle);
1258 return ret;
1259}
1260
1261static int
1262count_ref(struct ipt_entry *e, unsigned int offset, unsigned int *ref)
1263{
1264 struct ipt_standard_target *t;
1265
1266 if (strcmp(ipt_get_target(e)->u.name, IPT_STANDARD_TARGET) == 0) {
1267 t = (struct ipt_standard_target *)ipt_get_target(e);
1268
1269 if (t->verdict == offset)
1270 (*ref)++;
1271 }
1272
1273 return 0;
1274}
1275
1276/* Get the number of references to this chain. */
1277int
1278iptc_get_references(unsigned int *ref, const ipt_chainlabel chain,
1279 iptc_handle_t *handle)
1280{
1281 unsigned int offset;
1282
1283 CHECK(*handle);
1284 if (!find_label(&offset, chain, *handle)) {
1285 errno = ENOENT;
1286 return 0;
1287 }
1288
1289 *ref = 0;
1290 IPT_ENTRY_ITERATE((*handle)->entries.entries,
1291 (*handle)->entries.size,
1292 count_ref, offset, ref);
1293 return 1;
1294}
1295
1296/* Deletes a chain. */
1297int
1298iptc_delete_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
1299{
1300 unsigned int chainoff, labelidx, labeloff;
1301 unsigned int references;
1302 struct ipt_entry *e;
1303 int ret;
1304
1305 CHECK(*handle);
1306 if (!iptc_get_references(&references, chain, handle))
1307 return 0;
1308
1309 iptc_fn = iptc_delete_chain;
1310
1311 if (iptc_builtin(chain, *handle)) {
1312 errno = EINVAL;
1313 return 0;
1314 }
1315
1316 if (references > 0) {
1317 errno = EMLINK;
1318 return 0;
1319 }
1320
1321 if (!find_label(&chainoff, chain, *handle)) {
1322 errno = ENOENT;
1323 return 0;
1324 }
1325
1326 e = get_entry(*handle, chainoff);
1327 if (get_chain_end(*handle, chainoff) != chainoff) {
1328 errno = ENOTEMPTY;
1329 return 0;
1330 }
1331
1332 /* Need label index: preceeds chain start */
1333 labelidx = entry2index(*handle, e) - 1;
1334 labeloff = index2offset(*handle, labelidx);
1335
1336 ret = delete_rules(2,
1337 get_entry(*handle, labeloff)->next_offset
1338 + e->next_offset,
1339 labeloff, labelidx, handle);
1340 CHECK(*handle);
1341 return ret;
1342}
1343
1344/* Renames a chain. */
1345int iptc_rename_chain(const ipt_chainlabel oldname,
1346 const ipt_chainlabel newname,
1347 iptc_handle_t *handle)
1348{
1349 unsigned int chainoff, labeloff, labelidx;
1350 struct ipt_error_target *t;
1351
1352 CHECK(*handle);
1353 iptc_fn = iptc_rename_chain;
1354
1355 /* find_label doesn't cover built-in targets: DROP, ACCEPT
1356 RETURN. */
1357 if (find_label(&chainoff, newname, *handle)
1358 || strcmp(newname, IPTC_LABEL_DROP) == 0
1359 || strcmp(newname, IPTC_LABEL_ACCEPT) == 0
1360 || strcmp(newname, IPTC_LABEL_RETURN) == 0) {
1361 errno = EEXIST;
1362 return 0;
1363 }
1364
1365 if (!find_label(&chainoff, oldname, *handle)
1366 || iptc_builtin(oldname, *handle)) {
1367 errno = ENOENT;
1368 return 0;
1369 }
1370
1371 if (strlen(newname)+1 > sizeof(ipt_chainlabel)) {
1372 errno = EINVAL;
1373 return 0;
1374 }
1375
1376 /* Need label index: preceeds chain start */
1377 labelidx = entry2index(*handle, get_entry(*handle, chainoff)) - 1;
1378 labeloff = index2offset(*handle, labelidx);
1379
1380 t = (struct ipt_error_target *)
1381 ipt_get_target(get_entry(*handle, labeloff));
1382
1383 memset(t->error, 0, sizeof(t->error));
1384 strcpy(t->error, newname);
1385 (*handle)->changed = 1;
1386
1387 CHECK(*handle);
1388 return 1;
1389}
1390
1391/* Sets the policy on a built-in chain. */
1392int
1393iptc_set_policy(const ipt_chainlabel chain,
1394 const ipt_chainlabel policy,
1395 iptc_handle_t *handle)
1396{
1397 unsigned int hook;
1398 unsigned int policyoff;
1399 struct ipt_entry *e;
1400 struct ipt_standard_target *t;
1401
1402 CHECK(*handle);
1403 /* Figure out which chain. */
1404 hook = iptc_builtin(chain, *handle);
1405 if (hook == 0) {
1406 errno = EINVAL;
1407 return 0;
1408 } else
1409 hook--;
1410
1411 policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
1412 if (policyoff != (*handle)->info.underflow[hook]) {
1413 printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
1414 chain, policyoff, (*handle)->info.underflow[hook]);
1415 return 0;
1416 }
1417
1418 e = get_entry(*handle, policyoff);
1419 t = (struct ipt_standard_target *)ipt_get_target(e);
1420
1421 if (strcmp(policy, IPTC_LABEL_ACCEPT) == 0)
1422 t->verdict = -NF_ACCEPT - 1;
1423 else if (strcmp(policy, IPTC_LABEL_DROP) == 0)
1424 t->verdict = -NF_DROP - 1;
1425 else {
1426 errno = EINVAL;
1427 return 0;
1428 }
1429 (*handle)->counter_map[entry2index(*handle, e)]
1430 = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
1431 (*handle)->changed = 1;
1432
1433 CHECK(*handle);
1434 return 1;
1435}
1436
1437/* Without this, on gcc 2.7.2.3, we get:
1438 libiptc.c: In function `iptc_commit':
1439 libiptc.c:833: fixed or forbidden register was spilled.
1440 This may be due to a compiler bug or to impossible asm
1441 statements or clauses.
1442*/
1443static void
1444subtract_counters(struct ipt_counters *answer,
1445 const struct ipt_counters *a,
1446 const struct ipt_counters *b)
1447{
1448 answer->pcnt = a->pcnt - b->pcnt;
1449 answer->bcnt = a->bcnt - b->bcnt;
1450}
1451
1452int
1453iptc_commit(iptc_handle_t *handle)
1454{
1455 /* Replace, then map back the counters. */
1456 struct ipt_replace *repl;
1457 struct ipt_counters_info *newcounters;
1458 unsigned int i;
1459 size_t counterlen
1460 = sizeof(struct ipt_counters_info)
1461 + sizeof(struct ipt_counters) * (*handle)->new_number;
1462
1463 CHECK(*handle);
1464#if 0
1465 dump_entries(*handle);
1466#endif
1467
1468 /* Don't commit if nothing changed. */
1469 if (!(*handle)->changed)
1470 goto finished;
1471
1472 repl = malloc(sizeof(*repl) + (*handle)->entries.size);
1473 if (!repl) {
1474 errno = ENOMEM;
1475 return 0;
1476 }
1477
1478 /* These are the old counters we will get from kernel */
1479 repl->counters = malloc(sizeof(struct ipt_counters)
1480 * (*handle)->info.num_entries);
1481 if (!repl->counters) {
1482 free(repl);
1483 errno = ENOMEM;
1484 return 0;
1485 }
1486
1487 /* These are the counters we're going to put back, later. */
1488 newcounters = malloc(counterlen);
1489 if (!newcounters) {
1490 free(repl->counters);
1491 free(repl);
1492 errno = ENOMEM;
1493 return 0;
1494 }
1495
1496 strcpy(repl->name, (*handle)->info.name);
1497 repl->num_entries = (*handle)->new_number;
1498 repl->size = (*handle)->entries.size;
1499 memcpy(repl->hook_entry, (*handle)->info.hook_entry,
1500 sizeof(repl->hook_entry));
1501 memcpy(repl->underflow, (*handle)->info.underflow,
1502 sizeof(repl->underflow));
1503 repl->num_counters = (*handle)->info.num_entries;
1504 repl->valid_hooks = (*handle)->info.valid_hooks;
1505 memcpy(repl->entries, (*handle)->entries.entries,
1506 (*handle)->entries.size);
1507
1508 if (setsockopt(sockfd, IPPROTO_IP, IPT_SO_SET_REPLACE, repl,
1509 sizeof(*repl) + (*handle)->entries.size) < 0) {
1510 free(repl->counters);
1511 free(repl);
1512 free(newcounters);
1513 return 0;
1514 }
1515
1516 /* Put counters back. */
1517 strcpy(newcounters->name, (*handle)->info.name);
1518 newcounters->num_counters = (*handle)->new_number;
1519 for (i = 0; i < (*handle)->new_number; i++) {
1520 unsigned int mappos = (*handle)->counter_map[i].mappos;
1521 switch ((*handle)->counter_map[i].maptype) {
1522 case COUNTER_MAP_NOMAP:
1523 newcounters->counters[i]
1524 = ((struct ipt_counters){ 0, 0 });
1525 break;
1526
1527 case COUNTER_MAP_NORMAL_MAP:
1528 /* Original read: X.
1529 * Atomic read on replacement: X + Y.
1530 * Currently in kernel: Z.
1531 * Want in kernel: X + Y + Z.
1532 * => Add in X + Y
1533 * => Add in replacement read.
1534 */
1535 newcounters->counters[i] = repl->counters[mappos];
1536 break;
1537
1538 case COUNTER_MAP_ZEROED:
1539 /* Original read: X.
1540 * Atomic read on replacement: X + Y.
1541 * Currently in kernel: Z.
1542 * Want in kernel: Y + Z.
1543 * => Add in Y.
1544 * => Add in (replacement read - original read).
1545 */
1546 subtract_counters(&newcounters->counters[i],
1547 &repl->counters[mappos],
1548 &index2entry(*handle, i)->counters);
1549 break;
1550 }
1551 }
1552
1553 if (setsockopt(sockfd, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS,
1554 newcounters, counterlen) < 0) {
1555 free(repl->counters);
1556 free(repl);
1557 free(newcounters);
1558 return 0;
1559 }
1560
1561 free(repl->counters);
1562 free(repl);
1563 free(newcounters);
1564
1565 finished:
1566 free(*handle);
1567 *handle = NULL;
1568 return 1;
1569}
1570
1571/* Get raw socket. */
1572int
1573iptc_get_raw_socket()
1574{
1575 return sockfd;
1576}
1577
1578/* Translates errno numbers into more human-readable form than strerror. */
1579const char *
1580iptc_strerror(int err)
1581{
1582 unsigned int i;
1583 struct table_struct {
1584 void *fn;
1585 int err;
1586 const char *message;
1587 } table [] =
1588 { { NULL, 0, "Incompatible with this kernel" },
1589 { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
1590 { NULL, ENOSYS, "Will be implemented real soon. I promise." },
1591 { NULL, ENOMEM, "Memory allocation problem" },
1592 { iptc_init, EPERM, "Permission denied (you must be root)" },
1593 { iptc_init, EINVAL, "Module is wrong version" },
1594 { iptc_delete_chain, ENOTEMPTY, "Chain is not empty" },
1595 { iptc_delete_chain, EINVAL, "Can't delete built-in chain" },
1596 { iptc_delete_chain, EMLINK,
1597 "Can't delete chain with references left" },
1598 { iptc_create_chain, EEXIST, "Chain already exists" },
1599 { iptc_insert_entry, E2BIG, "Index of insertion too big" },
1600 { iptc_replace_entry, E2BIG, "Index of replacement too big" },
1601 { iptc_delete_num_entry, E2BIG, "Index of deletion too big" },
1602 { iptc_insert_entry, ELOOP, "Loop found in table" },
1603 { iptc_insert_entry, EINVAL, "Target problem" },
1604 /* EINVAL for CHECK probably means bad interface. */
1605 { iptc_check_packet, EINVAL,
1606 "bad arguments (does that interface exist?)" },
1607 /* ENOENT for DELETE probably means no matching rule */
1608 { iptc_delete_entry, ENOENT,
1609 "bad rule (does a matching rule exist in that chain?)" },
1610 { NULL, ENOENT, "No extended target/match by that name" }
1611 };
1612
1613 for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
1614 if ((!table[i].fn || table[i].fn == iptc_fn)
1615 && table[i].err == err)
1616 return table[i].message;
1617 }
1618
1619 return strerror(err);
1620}
1621
1622/***************************** DEBUGGING ********************************/
1623static inline int
1624unconditional(const struct ipt_ip *ip)
1625{
1626 unsigned int i;
1627
1628 for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
1629 if (((u_int32_t *)ip)[i])
1630 return 0;
1631
1632 return 1;
1633}
1634
1635static inline int
1636check_match(const struct ipt_entry_match *m, unsigned int *off)
1637{
1638 assert(m->match_size >= sizeof(struct ipt_entry_match));
1639
1640 (*off) += m->match_size;
1641 return 0;
1642}
1643
1644static inline int
1645check_entry(const struct ipt_entry *e, unsigned int *i, unsigned int *off,
1646 unsigned int user_offset, int *was_return,
1647 iptc_handle_t h)
1648{
1649 unsigned int toff;
1650 struct ipt_standard_target *t;
1651
1652 assert(e->target_offset >= sizeof(struct ipt_entry));
1653 assert(e->next_offset >= e->target_offset
1654 + sizeof(struct ipt_entry_target));
1655 toff = sizeof(struct ipt_entry);
1656 IPT_MATCH_ITERATE(e, check_match, &toff);
1657
1658 assert(toff == e->target_offset);
1659
1660 t = (struct ipt_standard_target *)
1661 ipt_get_target((struct ipt_entry *)e);
1662 assert(t->target.target_size == e->next_offset - e->target_offset);
1663 assert(!iptc_is_chain(t->target.u.name, h));
1664
1665 if (strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0) {
1666 assert(t->target.target_size
1667 == IPT_ALIGN(sizeof(struct ipt_standard_target)));
1668
1669 assert(t->verdict == -NF_DROP-1
1670 || t->verdict == -NF_ACCEPT-1
1671 || t->verdict == IPT_RETURN
1672 || t->verdict < (int)h->entries.size);
1673
1674 if (t->verdict >= 0) {
1675 struct ipt_entry *te = get_entry(h, t->verdict);
1676 int idx;
1677
1678 idx = entry2index(h, te);
1679 assert(strcmp(ipt_get_target(te)->u.name,
1680 IPT_ERROR_TARGET)
1681 != 0);
1682 assert(te != e);
1683
1684 /* Prior node must be error node, or this node. */
1685 assert(t->verdict == entry2offset(h, e)+e->next_offset
1686 || strcmp(ipt_get_target(index2entry(h, idx-1))
1687 ->u.name, IPT_ERROR_TARGET)
1688 == 0);
1689 }
1690
1691 if (t->verdict == IPT_RETURN
1692 && unconditional(&e->ip)
1693 && e->target_offset == sizeof(*e))
1694 *was_return = 1;
1695 else
1696 *was_return = 0;
1697 } else if (strcmp(t->target.u.name, IPT_ERROR_TARGET) == 0) {
1698 assert(t->target.target_size
1699 == IPT_ALIGN(sizeof(struct ipt_error_target)));
1700
1701 /* If this is in user area, previous must have been return */
1702 if (*off > user_offset)
1703 assert(*was_return);
1704
1705 *was_return = 0;
1706 }
1707 else *was_return = 0;
1708
1709 if (*off == user_offset)
1710 assert(strcmp(t->target.u.name, IPT_ERROR_TARGET) == 0);
1711
1712 (*off) += e->next_offset;
1713 (*i)++;
1714 return 0;
1715}
1716
1717/* Do every conceivable sanity check on the handle */
1718static void
1719do_check(iptc_handle_t h, unsigned int line)
1720{
1721 unsigned int i, n;
1722 unsigned int user_offset; /* Offset of first user chain */
1723 int was_return;
1724
1725 assert(h->changed == 0 || h->changed == 1);
1726 if (strcmp(h->info.name, "filter") == 0) {
1727 assert(h->info.valid_hooks
1728 == (1 << NF_IP_LOCAL_IN
1729 | 1 << NF_IP_FORWARD
1730 | 1 << NF_IP_LOCAL_OUT));
1731
1732 /* Hooks should be first three */
1733 assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
1734
1735 n = get_chain_end(h, 0);
1736 n += get_entry(h, n)->next_offset;
1737 assert(h->info.hook_entry[NF_IP_FORWARD] == n);
1738
1739 n = get_chain_end(h, n);
1740 n += get_entry(h, n)->next_offset;
1741 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
1742
1743 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
1744 } else if (strcmp(h->info.name, "nat") == 0) {
1745 assert(h->info.valid_hooks
1746 == (1 << NF_IP_PRE_ROUTING
1747 | 1 << NF_IP_POST_ROUTING
1748 | 1 << NF_IP_LOCAL_OUT));
1749
1750 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
1751
1752 n = get_chain_end(h, 0);
1753 n += get_entry(h, n)->next_offset;
1754 assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
1755
1756 n = get_chain_end(h, n);
1757 n += get_entry(h, n)->next_offset;
1758 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
1759
1760 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
1761 } else if (strcmp(h->info.name, "mangle") == 0) {
1762 assert(h->info.valid_hooks
1763 == (1 << NF_IP_PRE_ROUTING
1764 | 1 << NF_IP_LOCAL_OUT));
1765
1766 /* Hooks should be first three */
1767 assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
1768
1769 n = get_chain_end(h, 0);
1770 n += get_entry(h, n)->next_offset;
1771 assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
1772
1773 user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
1774 } else
1775 abort();
1776
1777 /* User chain == end of last builtin + policy entry */
1778 user_offset = get_chain_end(h, user_offset);
1779 user_offset += get_entry(h, user_offset)->next_offset;
1780
1781 /* Overflows should be end of entry chains, and unconditional
1782 policy nodes. */
1783 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1784 struct ipt_entry *e;
1785 struct ipt_standard_target *t;
1786
1787 if (!(h->info.valid_hooks & (1 << i)))
1788 continue;
1789 assert(h->info.underflow[i]
1790 == get_chain_end(h, h->info.hook_entry[i]));
1791
1792 e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
1793 assert(unconditional(&e->ip));
1794 assert(e->target_offset == sizeof(*e));
1795 assert(e->next_offset == sizeof(*e) + sizeof(*t));
1796 t = (struct ipt_standard_target *)ipt_get_target(e);
1797
1798 assert(strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0);
1799 assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
1800
1801 /* Hooks and underflows must be valid entries */
1802 entry2index(h, get_entry(h, h->info.hook_entry[i]));
1803 entry2index(h, get_entry(h, h->info.underflow[i]));
1804 }
1805
1806 assert(h->info.size
1807 >= h->info.num_entries * (sizeof(struct ipt_entry)
1808 +sizeof(struct ipt_standard_target)));
1809
1810 assert(h->entries.size
1811 >= (h->new_number
1812 * (sizeof(struct ipt_entry)
1813 + sizeof(struct ipt_standard_target))));
1814 assert(strcmp(h->info.name, h->entries.name) == 0);
1815
1816 i = 0; n = 0;
1817 was_return = 0;
1818 /* Check all the entries. */
1819 IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
1820 check_entry, &i, &n, user_offset, &was_return, h);
1821
1822 assert(i == h->new_number);
1823 assert(n == h->entries.size);
1824
1825 /* Final entry must be error node */
1826 assert(strcmp(ipt_get_target(index2entry(h, h->new_number-1))->u.name,
1827 IPT_ERROR_TARGET) == 0);
1828}