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