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