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