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