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