From 6f50d1857f3052d186a811df6b78e2cf15b0da3e Mon Sep 17 00:00:00 2001 From: Planet-Lab Support Date: Mon, 10 Jan 2005 20:29:31 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'iptables-1_2_9-2_3_1'. --- .#iptables.c.1.54 | 2298 ++++++++++++++++++++++++++ .#iptables.c.1.55 | 2303 +++++++++++++++++++++++++++ .cvsignore | 8 - Makefile.nolibnsl | 208 +++ extensions/.#libipt_ECN.c.1.7 | 180 +++ extensions/.#libipt_recent.c.1.6 | 229 +++ extensions/.cvsignore | 1 - extensions/libipt_icmp.c.print_type | 310 ++++ ip6tables.c.selinux | 2300 ++++++++++++++++++++++++++ iptables-config | 37 - iptables-restore.c.counters | 384 +++++ iptables.c.selinux | 2298 ++++++++++++++++++++++++++ iptables.init | 320 ---- iptables.spec | 371 ----- libipq/libipq.c.netlink | 373 +++++ libiptc/.cvsignore | 1 - libiptc2/.#libiptc.c.1.40 | 1972 +++++++++++++++++++++++ 17 files changed, 12855 insertions(+), 738 deletions(-) create mode 100644 .#iptables.c.1.54 create mode 100644 .#iptables.c.1.55 delete mode 100644 .cvsignore create mode 100644 Makefile.nolibnsl create mode 100644 extensions/.#libipt_ECN.c.1.7 create mode 100644 extensions/.#libipt_recent.c.1.6 delete mode 100644 extensions/.cvsignore create mode 100644 extensions/libipt_icmp.c.print_type create mode 100644 ip6tables.c.selinux delete mode 100644 iptables-config create mode 100644 iptables-restore.c.counters create mode 100644 iptables.c.selinux delete mode 100755 iptables.init delete mode 100644 iptables.spec create mode 100644 libipq/libipq.c.netlink delete mode 100644 libiptc/.cvsignore create mode 100644 libiptc2/.#libiptc.c.1.40 diff --git a/.#iptables.c.1.54 b/.#iptables.c.1.54 new file mode 100644 index 0000000..a838e56 --- /dev/null +++ b/.#iptables.c.1.54 @@ -0,0 +1,2298 @@ +/* Code to take an iptables-style command line and do it. */ + +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_CHECK 0x0800U +#define CMD_RENAME_CHAIN 0x1000U +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E' }; + +#define OPTION_OFFSET 256 + +#define OPT_NONE 0x00000U +#define OPT_NUMERIC 0x00001U +#define OPT_SOURCE 0x00002U +#define OPT_DESTINATION 0x00004U +#define OPT_PROTOCOL 0x00008U +#define OPT_JUMP 0x00010U +#define OPT_VERBOSE 0x00020U +#define OPT_EXPANDED 0x00040U +#define OPT_VIANAMEIN 0x00080U +#define OPT_VIANAMEOUT 0x00100U +#define OPT_FRAGMENT 0x00200U +#define OPT_LINENUMBERS 0x00400U +#define OPT_COUNTERS 0x00800U +#define NUMBER_OF_OPT 12 +static const char optflags[NUMBER_OF_OPT] += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'}; + +static struct option original_opts[] = { + { "append", 1, 0, 'A' }, + { "delete", 1, 0, 'D' }, + { "insert", 1, 0, 'I' }, + { "replace", 1, 0, 'R' }, + { "list", 2, 0, 'L' }, + { "flush", 2, 0, 'F' }, + { "zero", 2, 0, 'Z' }, + { "new-chain", 1, 0, 'N' }, + { "delete-chain", 2, 0, 'X' }, + { "rename-chain", 1, 0, 'E' }, + { "policy", 1, 0, 'P' }, + { "source", 1, 0, 's' }, + { "destination", 1, 0, 'd' }, + { "src", 1, 0, 's' }, /* synonym */ + { "dst", 1, 0, 'd' }, /* synonym */ + { "protocol", 1, 0, 'p' }, + { "in-interface", 1, 0, 'i' }, + { "jump", 1, 0, 'j' }, + { "table", 1, 0, 't' }, + { "match", 1, 0, 'm' }, + { "numeric", 0, 0, 'n' }, + { "out-interface", 1, 0, 'o' }, + { "verbose", 0, 0, 'v' }, + { "exact", 0, 0, 'x' }, + { "fragments", 0, 0, 'f' }, + { "version", 0, 0, 'V' }, + { "help", 2, 0, 'h' }, + { "line-numbers", 0, 0, '0' }, + { "modprobe", 1, 0, 'M' }, + { "set-counters", 1, 0, 'c' }, + { 0 } +}; + +/* we need this for iptables-restore. iptables-restore.c sets line to the + * current line of the input file, in order to give a more precise error + * message. iptables itself doesn't need this, so it is initialized to the + * magic number of -1 */ +int line = -1; + +#ifndef __OPTIMIZE__ +struct ipt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (void *)e + e->target_offset; +} +#endif + +static struct option *opts = original_opts; +static unsigned int global_option_offset = 0; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o -f --line */ +/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' '}, +/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ',' ','x'}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'} +}; + +static int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ IPT_INV_SRCIP, +/* -d */ IPT_INV_DSTIP, +/* -p */ IPT_INV_PROTO, +/* -j */ 0, +/* -v */ 0, +/* -x */ 0, +/* -i */ IPT_INV_VIA_IN, +/* -o */ IPT_INV_VIA_OUT, +/* -f */ IPT_INV_FRAG, +/*--line*/ 0 +}; + +const char *program_version; +const char *program_name; + +/* Keeping track of external matches and targets: linked lists. */ +struct iptables_match *iptables_matches = NULL; +struct iptables_target *iptables_targets = NULL; + +/* Extra debugging from libiptc */ +extern void dump_entries(const iptc_handle_t handle); + +/* A few hardcoded protocols for 'all' and in case the user has no + /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +static const struct pprot chain_protos[] = { + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "icmp", IPPROTO_ICMP }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "all", 0 }, +}; + +static char * +proto_to_name(u_int8_t proto, int nolookup) +{ + unsigned int i; + + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + + for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) + if (chain_protos[i].num == proto) + return chain_protos[i].name; + + return NULL; +} + +struct in_addr * +dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof(buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +static struct in_addr * +network_to_addr(const char *name) +{ + struct netent *net; + static struct in_addr addr; + + if ((net = getnetbyname(name)) != NULL) { + if (net->n_addrtype != AF_INET) + return (struct in_addr *) NULL; + addr.s_addr = htonl((unsigned long) net->n_net); + return &addr; + } + + return (struct in_addr *) NULL; +} + +static void +inaddrcpy(struct in_addr *dst, struct in_addr *src) +{ + /* memcpy(dst, src, sizeof(struct in_addr)); */ + dst->s_addr = src->s_addr; +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", program_name, program_version); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +void +exit_tryhelp(int status) +{ + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + program_name, program_name ); + exit(status); +} + +void +exit_printhelp(void) +{ + struct iptables_match *m = NULL; + struct iptables_target *t = NULL; + + printf("%s v%s\n\n" +"Usage: %s -[AD] chain rule-specification [options]\n" +" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LFZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + program_name, program_version, program_name, program_name, + program_name, program_name, program_name, program_name, + program_name, program_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain] List the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain] Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n" +" --source -s [!] address[/mask]\n" +" source specification\n" +" --destination -d [!] address[/mask]\n" +" destination specification\n" +" --in-interface -i [!] input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +" --out-interface -o [!] output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +"[!] --fragment -f match second or further fragments only\n" +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + /* Print out any special helps. A user might like to be able + to add a --help to the commandline, and see expected + results. So we call help for all matches & targets */ + for (t=iptables_targets;t;t=t->next) { + printf("\n"); + t->help(); + } + for (m=iptables_matches;m;m=m->next) { + printf("\n"); + m->help(); + } + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(int *cmd, const int newcmd, const int othercmds, int invert) +{ + if (invert) + exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +int +check_inverse(const char option[], int *invert, int *optind, int argc) +{ + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (optind) { + *optind = *optind+1; + if (argc && *optind > argc) + exit_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static void * +fw_malloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) { + perror("iptables: malloc failed"); + exit(1); + } + return p; +} + +static struct in_addr * +host_to_addr(const char *name, unsigned int *naddr) +{ + struct hostent *host; + struct in_addr *addr; + unsigned int i; + + *naddr = 0; + if ((host = gethostbyname(name)) != NULL) { + if (host->h_addrtype != AF_INET || + host->h_length != sizeof(struct in_addr)) + return (struct in_addr *) NULL; + + while (host->h_addr_list[*naddr] != (char *) NULL) + (*naddr)++; + addr = fw_calloc(*naddr, sizeof(struct in_addr)); + for (i = 0; i < *naddr; i++) + inaddrcpy(&(addr[i]), + (struct in_addr *) host->h_addr_list[i]); + return addr; + } + + return (struct in_addr *) NULL; +} + +static char * +addr_to_host(const struct in_addr *addr) +{ + struct hostent *host; + + if ((host = gethostbyaddr((char *) addr, + sizeof(struct in_addr), AF_INET)) != NULL) + return (char *) host->h_name; + + return (char *) NULL; +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +static struct in_addr * +parse_hostnetwork(const char *name, unsigned int *naddrs) +{ + struct in_addr *addrp, *addrptmp; + + if ((addrptmp = dotted_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = fw_malloc(sizeof(struct in_addr)); + inaddrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static struct in_addr * +parse_mask(char *mask) +{ + static struct in_addr maskaddr; + struct in_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 32 bits */ + maskaddr.s_addr = 0xFFFFFFFF; + return &maskaddr; + } + if ((addrp = dotted_to_addr(mask)) != NULL) + /* dotted_to_addr already returns a network byte order addr */ + return addrp; + if (string_to_number(mask, 0, 32, &bits) == -1) + exit_error(PARAMETER_PROBLEM, + "invalid mask `%s' specified", mask); + if (bits != 0) { + maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); + return &maskaddr; + } + + maskaddr.s_addr = 0L; + return &maskaddr; +} + +void +parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs) +{ + struct in_addr *addrp; + char buf[256]; + char *p; + int i, j, k, n; + + strncpy(buf, name, sizeof(buf) - 1); + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_mask(p + 1); + } else + addrp = parse_mask(NULL); + inaddrcpy(maskp, addrp); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (maskp->s_addr == 0L) + strcpy(buf, "0.0.0.0"); + + addrp = *addrpp = parse_hostnetwork(buf, naddrs); + n = *naddrs; + for (i = 0, j = 0; i < n; i++) { + addrp[j++].s_addr &= maskp->s_addr; + for (k = 0; k < j - 1; k++) { + if (addrp[k].s_addr == addrp[j - 1].s_addr) { + (*naddrs)--; + j--; + break; + } + } + } +} + +struct iptables_match * +find_match(const char *name, enum ipt_tryload tryload) +{ + struct iptables_match *ptr; + + for (ptr = iptables_matches; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified target as match. */ + ptr = find_match(name, DONT_LOAD); + + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find match `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ +static struct iptables_match * +find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) +{ + unsigned int proto; + + if (string_to_number(pname, 0, 255, &proto) != -1) { + char *protoname = proto_to_name(proto, nolookup); + + if (protoname) + return find_match(protoname, tryload); + } else + return find_match(pname, tryload); + + return NULL; +} + +u_int16_t +parse_protocol(const char *s) +{ + unsigned int proto; + + if (string_to_number(s, 0, 255, &proto) == -1) { + struct protoent *pent; + + if ((pent = getprotobyname(s))) + proto = pent->p_proto; + else { + unsigned int i; + for (i = 0; + i < sizeof(chain_protos)/sizeof(struct pprot); + i++) { + if (strcmp(s, chain_protos[i].name) == 0) { + proto = chain_protos[i].num; + break; + } + } + if (i == sizeof(chain_protos)/sizeof(struct pprot)) + exit_error(PARAMETER_PROBLEM, + "unknown protocol `%s' specified", + s); + } + } + + return (u_int16_t)proto; +} + +static void +parse_interface(const char *arg, char *vianame, unsigned char *mask) +{ + int vialen = strlen(arg); + unsigned int i; + + memset(mask, 0, IFNAMSIZ); + memset(vianame, 0, IFNAMSIZ); + + if (vialen + 1 > IFNAMSIZ) + exit_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than IFNAMSIZ" + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); + if (vialen == 0) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' + && vianame[i] != '.') { + printf("Warning: wierd character in interface" + " `%s' (No aliases, :, ! or *).\n", + vianame); + break; + } + } + } +} + +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + exit_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%i chars max)", + targetname, sizeof(ipt_chainlabel)-1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +static char * +addr_to_network(const struct in_addr *addr) +{ + struct netent *net; + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) + return (char *) net->n_name; + + return (char *) NULL; +} + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +char * +addr_to_anyname(const struct in_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL || + (name = addr_to_network(addr)) != NULL) + return name; + + return addr_to_dotted(addr); +} + +char * +mask_to_dotted(const struct in_addr *mask) +{ + int i; + static char buf[20]; + u_int32_t maskaddr, bits; + + maskaddr = ntohl(mask->s_addr); + + if (maskaddr == 0xFFFFFFFFL) + /* we don't want to see "/32" */ + return ""; + + i = 32; + bits = 0xFFFFFFFEL; + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + if (i >= 0) + sprintf(buf, "/%d", i); + else + /* mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%s", addr_to_dotted(mask)); + + return buf; +} + +int +string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +static void +set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, + int invert) +{ + if (*options & option) + exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + exit_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +struct iptables_target * +find_target(const char *name, enum ipt_tryload tryload) +{ + struct iptables_target *ptr; + + /* Standard target? */ + if (strcmp(name, "") == 0 + || strcmp(name, IPTC_LABEL_ACCEPT) == 0 + || strcmp(name, IPTC_LABEL_DROP) == 0 + || strcmp(name, IPTC_LABEL_QUEUE) == 0 + || strcmp(name, IPTC_LABEL_RETURN) == 0) + name = "standard"; + + for (ptr = iptables_targets; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified match as a target. */ + ptr = find_target(name, DONT_LOAD); + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find target `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *option_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + + return merge; +} + +void +register_match(struct iptables_match *me) +{ + struct iptables_match **i; + + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_match(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: match `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: match `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Append to list. */ + for (i = &iptables_matches; *i; i = &(*i)->next); + me->next = NULL; + *i = me; + + me->m = NULL; + me->mflags = 0; +} + +void +register_target(struct iptables_target *me) +{ + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_target(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: target `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: target `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Prepend to list. */ + me->next = iptables_targets; + iptables_targets = me; + me->t = NULL; + me->tflags = 0; +} + +static void +print_num(u_int64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), number); + } + else printf(FMT("%4lluG ","%lluG "), number); + } + else printf(FMT("%4lluM ","%lluM "), number); + } else + printf(FMT("%4lluK ","%lluK "), number); + } else + printf(FMT("%5llu ","%llu "), number); + } else + printf(FMT("%8llu ","%llu "), number); +} + + +static void +print_header(unsigned int format, const char *chain, iptc_handle_t *handle) +{ + struct ipt_counters counters; + const char *pol = iptc_get_policy(chain, &counters, handle); + printf("Chain %s", chain); + if (pol) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + print_num(counters.pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + print_num(counters.bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else { + unsigned int refs; + if (!iptc_get_references(&refs, chain, handle)) + printf(" (ERROR obtaining refs)\n"); + else + printf(" (%u references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + + +static int +print_match(const struct ipt_entry_match *m, + const struct ipt_ip *ip, + int numeric) +{ + struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); + + if (match) { + if (match->print) + match->print(ip, m, numeric); + else + printf("%s ", match->name); + } else { + if (m->u.user.name[0]) + printf("UNKNOWN match `%s' ", m->u.user.name); + } + /* Don't stop iterating. */ + return 0; +} + +/* e is called `fw' here for hysterical raisins */ +static void +print_firewall(const struct ipt_entry *fw, + const char *targname, + unsigned int num, + unsigned int format, + const iptc_handle_t handle) +{ + struct iptables_target *target = NULL; + const struct ipt_entry_target *t; + u_int8_t flags; + char buf[BUFSIZ]; + + if (!iptc_is_chain(targname, handle)) + target = find_target(targname, TRY_LOAD); + else + target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); + + t = ipt_get_target((struct ipt_entry *)fw); + flags = fw->ip.flags; + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num+1); + + if (!(format & FMT_NOCOUNTS)) { + print_num(fw->counters.pcnt, format); + print_num(fw->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname); + + fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout); + { + char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), fw->ip.proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + + if (fw->ip.invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.iniface[0] != '\0') { + strcat(iface, fw->ip.iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (fw->ip.invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.outiface[0] != '\0') { + strcat(iface, fw->ip.outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } + + fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src))); + strcat(buf, mask_to_dotted(&(fw->ip.smsk))); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst))); + strcat(buf, mask_to_dotted(&(fw->ip.dmsk))); + printf(FMT("%-19s","-> %s"), buf); + } + + if (format & FMT_NOTABLE) + fputs(" ", stdout); + + IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); + + if (target) { + if (target->print) + /* Print the target information. */ + target->print(&fw->ip, t, format & FMT_NUMERIC); + } else if (t->u.target_size != sizeof(*t)) + printf("[%u bytes of unknown target data] ", + t->u.target_size - sizeof(*t)); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +static void +print_firewall_line(const struct ipt_entry *fw, + const iptc_handle_t h) +{ + struct ipt_entry_target *t; + + t = ipt_get_target((struct ipt_entry *)fw); + print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); +} + +static int +append_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_append_entry(chain, fw, handle); + } + } + + return ret; +} + +static int +replace_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + const struct in_addr *saddr, + const struct in_addr *daddr, + int verbose, + iptc_handle_t *handle) +{ + fw->ip.src.s_addr = saddr->s_addr; + fw->ip.dst.s_addr = daddr->s_addr; + + if (verbose) + print_firewall_line(fw, *handle); + return iptc_replace_entry(chain, fw, rulenum, handle); +} + +static int +insert_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_insert_entry(chain, fw, rulenum, handle); + } + } + + return ret; +} + +static unsigned char * +make_delete_mask(struct ipt_entry *fw) +{ + /* Establish mask for comparison */ + unsigned int size; + struct iptables_match *m; + unsigned char *mask, *mptr; + + size = sizeof(struct ipt_entry); + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + mask = fw_calloc(1, size + + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->size); + + memset(mask, 0xFF, sizeof(struct ipt_entry)); + mptr = mask + sizeof(struct ipt_entry); + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->userspacesize); + mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->userspacesize); + + return mask; +} + +static int +delete_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + unsigned char *mask; + + mask = make_delete_mask(fw); + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_delete_entry(chain, fw, mask, handle); + } + } + return ret; +} + +int +for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), + int verbose, int builtinstoo, iptc_handle_t *handle) +{ + int ret = 1; + const char *chain; + char *chains; + unsigned int i, chaincount = 0; + + chain = iptc_first_chain(handle); + while (chain) { + chaincount++; + chain = iptc_next_chain(handle); + } + + chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount); + i = 0; + chain = iptc_first_chain(handle); + while (chain) { + strcpy(chains + i*sizeof(ipt_chainlabel), chain); + i++; + chain = iptc_next_chain(handle); + } + + for (i = 0; i < chaincount; i++) { + if (!builtinstoo + && iptc_builtin(chains + i*sizeof(ipt_chainlabel), + *handle)) + continue; + ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle); + } + + free(chains); + return ret; +} + +int +flush_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(flush_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Flushing chain `%s'\n", chain); + return iptc_flush_entries(chain, handle); +} + +static int +zero_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(zero_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Zeroing chain `%s'\n", chain); + return iptc_zero_entries(chain, handle); +} + +int +delete_chain(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(delete_chain, verbose, 0, handle); + + if (verbose) + fprintf(stdout, "Deleting chain `%s'\n", chain); + return iptc_delete_chain(chain, handle); +} + +static int +list_entries(const ipt_chainlabel chain, int verbose, int numeric, + int expanded, int linenumbers, iptc_handle_t *handle) +{ + int found = 0; + unsigned int format; + const char *this; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + for (this = iptc_first_chain(handle); + this; + this = iptc_next_chain(handle)) { + const struct ipt_entry *i; + unsigned int num; + + if (chain && strcmp(chain, this) != 0) + continue; + + if (found) printf("\n"); + + print_header(format, this, handle); + i = iptc_first_rule(this, handle); + + num = 0; + while (i) { + print_firewall(i, + iptc_get_target(i, handle), + num++, + format, + *handle); + i = iptc_next_rule(i, handle); + } + found = 1; + } + + errno = ENOENT; + return found; +} + +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + switch (read(procfile, ret, 1024)) { + case -1: goto fail; + case 1024: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +int iptables_insmod(const char *modname, const char *modprobe) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of kernel */ + if (!modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + modprobe = buf; + } + + switch (fork()) { + case 0: + argv[0] = (char *)modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* not usually reached */ + exit(0); + case -1: + return -1; + + default: /* parent */ + wait(NULL); + } + + free(buf); + return 0; +} + +static struct ipt_entry * +generate_entry(const struct ipt_entry *fw, + struct iptables_match *matches, + struct ipt_entry_target *target) +{ + unsigned int size; + struct iptables_match *m; + struct ipt_entry *e; + + size = sizeof(struct ipt_entry); + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + size += m->m->u.match_size; + } + + e = fw_malloc(size + target->u.target_size); + *e = *fw; + e->target_offset = size; + e->next_offset = size + target->u.target_size; + + size = 0; + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + memcpy(e->elems + size, m->m, m->m->u.match_size); + size += m->m->u.match_size; + } + memcpy(e->elems + size, target, target->u.target_size); + + return e; +} + +int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) +{ + struct ipt_entry fw, *e = NULL; + int invert = 0; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in_addr *saddrs = NULL, *daddrs = NULL; + + int c, verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, options = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct iptables_match *m; + struct iptables_target *target = NULL; + struct iptables_target *t; + const char *jumpto = ""; + char *protocol = NULL; + const char *modprobe = NULL; + int proto_used = 0; + + memset(&fw, 0, sizeof(fw)); + + opts = original_opts; + global_option_offset = 0; + + /* re-set optind to 0 in case do_command gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = iptables_matches; m; m = m->next) { + m->mflags = 0; + m->used = 0; + } + + for (t = iptables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((c = getopt_long(argc, argv, + "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:", + opts, NULL)) != -1) { + switch (c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, CMD_ZERO, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'N': + if (optarg && *optarg == '-') + exit_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `-'\n"); + if (find_target(optarg, TRY_LOAD)) + exit_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + /* iptables -p icmp -h */ + if (!iptables_matches && protocol) + find_match(protocol, TRY_LOAD); + + exit_printhelp(); + + /* + * Option selection + */ + case 'p': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_PROTOCOL, &fw.ip.invflags, + invert); + + /* Canonicalize into lower case */ + for (protocol = argv[optind-1]; *protocol; protocol++) + *protocol = tolower(*protocol); + + protocol = argv[optind-1]; + fw.ip.proto = parse_protocol(protocol); + + if (fw.ip.proto == 0 + && (fw.ip.invflags & IPT_INV_PROTO)) + exit_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + fw.nfcache |= NFC_IP_PROTO; + break; + + case 's': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_SOURCE, &fw.ip.invflags, + invert); + shostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_SRC; + break; + + case 'd': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_DESTINATION, &fw.ip.invflags, + invert); + dhostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_DST; + break; + + case 'j': + set_option(&options, OPT_JUMP, &fw.ip.invflags, + invert); + jumpto = parse_target(optarg); + /* TRY_LOAD (may be chain name) */ + target = find_target(jumpto, TRY_LOAD); + + if (target) { + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + target->size; + + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + opts = merge_options(opts, target->extra_opts, &target->option_offset); + } + break; + + + case 'i': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.iniface, + fw.ip.iniface_mask); + fw.nfcache |= NFC_IP_IF_IN; + break; + + case 'o': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.outiface, + fw.ip.outiface_mask); + fw.nfcache |= NFC_IP_IF_OUT; + break; + + case 'f': + set_option(&options, OPT_FRAGMENT, &fw.ip.invflags, + invert); + fw.ip.flags |= IPT_F_FRAG; + fw.nfcache |= NFC_IP_FRAG; + break; + + case 'v': + if (!verbose) + set_option(&options, OPT_VERBOSE, + &fw.ip.invflags, invert); + verbose++; + break; + + case 'm': { + size_t size; + + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = find_match(optarg, LOAD_MUST_SUCCEED); + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + opts = merge_options(opts, m->extra_opts, &m->option_offset); + } + break; + + case 'n': + set_option(&options, OPT_NUMERIC, &fw.ip.invflags, + invert); + break; + + case 't': + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = argv[optind-1]; + break; + + case 'x': + set_option(&options, OPT_EXPANDED, &fw.ip.invflags, + invert); + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", program_version); + else + printf("%s v%s\n", + program_name, program_version); + exit(0); + + case '0': + set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags, + invert); + break; + + case 'M': + modprobe = optarg; + break; + + case 'c': + + set_option(&options, OPT_COUNTERS, &fw.ip.invflags, + invert); + pcnt = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + + if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + + break; + + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + exit_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = TRUE; + optarg[0] = '\0'; + continue; + } + printf("Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + /* FIXME: This scheme doesn't allow two of the same + matches --RR */ + if (!target + || !(target->parse(c - target->option_offset, + argv, invert, + &target->tflags, + &fw, &target->t))) { + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + if (m->parse(c - m->option_offset, + argv, invert, + &m->mflags, + &fw, + &fw.nfcache, + &m->m)) + break; + } + + /* If you listen carefully, you can + actually hear this code suck. */ + + /* some explanations (after four different bugs + * in 3 different releases): If we encountere a + * parameter, that has not been parsed yet, + * it's not an option of an explicitly loaded + * match or a target. However, we support + * implicit loading of the protocol match + * extension. '-p tcp' means 'l4 proto 6' and + * at the same time 'load tcp protocol match on + * demand if we specify --dport'. + * + * To make this work, we need to make sure: + * - the parameter has not been parsed by + * a match (m above) + * - a protocol has been specified + * - the protocol extension has not been + * loaded yet, or is loaded and unused + * [think of iptables-restore!] + * - the protocol extension can be successively + * loaded + */ + if (m == NULL + && protocol + && (!find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + || (find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + && (proto_used == 0)) + ) + && (m = find_proto(protocol, TRY_LOAD, + options&OPT_NUMERIC))) { + /* Try loading protocol */ + size_t size; + + proto_used = 1; + + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + + opts = merge_options(opts, + m->extra_opts, &m->option_offset); + + optind--; + continue; + } + if (!m) + exit_error(PARAMETER_PROBLEM, + "Unknown arg `%s'", + argv[optind-1]); + } + } + invert = FALSE; + } + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + m->final_check(m->mflags); + } + + if (target) + target->final_check(target->tflags); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + exit_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + exit_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + exit_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { + if (!(options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + parse_hostnetworkmask(shostnetworkmask, &saddrs, + &(fw.ip.smsk), &nsaddrs); + + if (dhostnetworkmask) + parse_hostnetworkmask(dhostnetworkmask, &daddrs, + &(fw.ip.dmsk), &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + exit_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, options); + + if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN) + exit_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %i chars)", + chain, IPT_FUNCTION_MAXNAMELEN); + + /* only allocate handle if we weren't called with a handle */ + if (!*handle) + *handle = iptc_init(*table); + + if (!*handle) { + /* try to insmod the module if iptc_init failed */ + iptables_insmod("ip_tables", modprobe); + *handle = iptc_init(*table); + } + + if (!*handle) + exit_error(VERSION_PROBLEM, + "can't initialize iptables table `%s': %s", + *table, iptc_strerror(errno)); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (options & OPT_VIANAMEOUT) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (options & OPT_VIANAMEIN) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + if (target && iptc_is_chain(jumpto, *handle)) { + printf("Warning: using chain %s, not extension\n", + jumpto); + + target = NULL; + } + + /* If they didn't specify a target, or it's a chain + name, use standard. */ + if (!target + && (strlen(jumpto) == 0 + || iptc_is_chain(jumpto, *handle))) { + size_t size; + + target = find_target(IPT_STANDARD_TARGET, + LOAD_MUST_SUCCEED); + + size = sizeof(struct ipt_entry_target) + + target->size; + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + } + + if (!target) { + /* it is no chain, and we can't load a plugin. + * We cannot know if the plugin is corrupt, non + * existant OR if the user just misspelled a + * chain. */ + find_target(jumpto, LOAD_MUST_SUCCEED); + } else { + e = generate_entry(&fw, iptables_matches, target->t); + } + } + + switch (command) { + case CMD_APPEND: + ret = append_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE: + ret = delete_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE_NUM: + ret = iptc_delete_num_entry(chain, rulenum - 1, handle); + break; + case CMD_REPLACE: + ret = replace_entry(chain, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, + handle); + break; + case CMD_INSERT: + ret = insert_entry(chain, e, rulenum - 1, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_LIST: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + break; + case CMD_FLUSH: + ret = flush_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_ZERO: + ret = zero_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_LIST|CMD_ZERO: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + if (ret) + ret = zero_entries(chain, + options&OPT_VERBOSE, handle); + break; + case CMD_NEW_CHAIN: + ret = iptc_create_chain(chain, handle); + break; + case CMD_DELETE_CHAIN: + ret = delete_chain(chain, options&OPT_VERBOSE, handle); + break; + case CMD_RENAME_CHAIN: + ret = iptc_rename_chain(chain, newname, handle); + break; + case CMD_SET_POLICY: + ret = iptc_set_policy(chain, policy, NULL, handle); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + + if (verbose > 1) + dump_entries(*handle); + + return ret; +} diff --git a/.#iptables.c.1.55 b/.#iptables.c.1.55 new file mode 100644 index 0000000..b8087f1 --- /dev/null +++ b/.#iptables.c.1.55 @@ -0,0 +1,2303 @@ +/* Code to take an iptables-style command line and do it. */ + +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_CHECK 0x0800U +#define CMD_RENAME_CHAIN 0x1000U +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E' }; + +#define OPTION_OFFSET 256 + +#define OPT_NONE 0x00000U +#define OPT_NUMERIC 0x00001U +#define OPT_SOURCE 0x00002U +#define OPT_DESTINATION 0x00004U +#define OPT_PROTOCOL 0x00008U +#define OPT_JUMP 0x00010U +#define OPT_VERBOSE 0x00020U +#define OPT_EXPANDED 0x00040U +#define OPT_VIANAMEIN 0x00080U +#define OPT_VIANAMEOUT 0x00100U +#define OPT_FRAGMENT 0x00200U +#define OPT_LINENUMBERS 0x00400U +#define OPT_COUNTERS 0x00800U +#define NUMBER_OF_OPT 12 +static const char optflags[NUMBER_OF_OPT] += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'}; + +static struct option original_opts[] = { + { "append", 1, 0, 'A' }, + { "delete", 1, 0, 'D' }, + { "insert", 1, 0, 'I' }, + { "replace", 1, 0, 'R' }, + { "list", 2, 0, 'L' }, + { "flush", 2, 0, 'F' }, + { "zero", 2, 0, 'Z' }, + { "new-chain", 1, 0, 'N' }, + { "delete-chain", 2, 0, 'X' }, + { "rename-chain", 1, 0, 'E' }, + { "policy", 1, 0, 'P' }, + { "source", 1, 0, 's' }, + { "destination", 1, 0, 'd' }, + { "src", 1, 0, 's' }, /* synonym */ + { "dst", 1, 0, 'd' }, /* synonym */ + { "protocol", 1, 0, 'p' }, + { "in-interface", 1, 0, 'i' }, + { "jump", 1, 0, 'j' }, + { "table", 1, 0, 't' }, + { "match", 1, 0, 'm' }, + { "numeric", 0, 0, 'n' }, + { "out-interface", 1, 0, 'o' }, + { "verbose", 0, 0, 'v' }, + { "exact", 0, 0, 'x' }, + { "fragments", 0, 0, 'f' }, + { "version", 0, 0, 'V' }, + { "help", 2, 0, 'h' }, + { "line-numbers", 0, 0, '0' }, + { "modprobe", 1, 0, 'M' }, + { "set-counters", 1, 0, 'c' }, + { 0 } +}; + +/* we need this for iptables-restore. iptables-restore.c sets line to the + * current line of the input file, in order to give a more precise error + * message. iptables itself doesn't need this, so it is initialized to the + * magic number of -1 */ +int line = -1; + +#ifndef __OPTIMIZE__ +struct ipt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (void *)e + e->target_offset; +} +#endif + +static struct option *opts = original_opts; +static unsigned int global_option_offset = 0; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o -f --line */ +/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' '}, +/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ',' ','x'}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'} +}; + +static int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ IPT_INV_SRCIP, +/* -d */ IPT_INV_DSTIP, +/* -p */ IPT_INV_PROTO, +/* -j */ 0, +/* -v */ 0, +/* -x */ 0, +/* -i */ IPT_INV_VIA_IN, +/* -o */ IPT_INV_VIA_OUT, +/* -f */ IPT_INV_FRAG, +/*--line*/ 0 +}; + +const char *program_version; +const char *program_name; + +/* Keeping track of external matches and targets: linked lists. */ +struct iptables_match *iptables_matches = NULL; +struct iptables_target *iptables_targets = NULL; + +/* Extra debugging from libiptc */ +extern void dump_entries(const iptc_handle_t handle); + +/* A few hardcoded protocols for 'all' and in case the user has no + /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +static const struct pprot chain_protos[] = { + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "icmp", IPPROTO_ICMP }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "all", 0 }, +}; + +static char * +proto_to_name(u_int8_t proto, int nolookup) +{ + unsigned int i; + + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + + for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) + if (chain_protos[i].num == proto) + return chain_protos[i].name; + + return NULL; +} + +struct in_addr * +dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof(buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +static struct in_addr * +network_to_addr(const char *name) +{ + struct netent *net; + static struct in_addr addr; + + if ((net = getnetbyname(name)) != NULL) { + if (net->n_addrtype != AF_INET) + return (struct in_addr *) NULL; + addr.s_addr = htonl((unsigned long) net->n_net); + return &addr; + } + + return (struct in_addr *) NULL; +} + +static void +inaddrcpy(struct in_addr *dst, struct in_addr *src) +{ + /* memcpy(dst, src, sizeof(struct in_addr)); */ + dst->s_addr = src->s_addr; +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", program_name, program_version); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +void +exit_tryhelp(int status) +{ +<<<<<<< iptables.c + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); +======= + if (line != -1) + fprintf(stderr, "Error occured at line: %d\n", line); +>>>>>>> 1.55 + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + program_name, program_name ); + exit(status); +} + +void +exit_printhelp(void) +{ + struct iptables_match *m = NULL; + struct iptables_target *t = NULL; + + printf("%s v%s\n\n" +"Usage: %s -[AD] chain rule-specification [options]\n" +" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LFZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + program_name, program_version, program_name, program_name, + program_name, program_name, program_name, program_name, + program_name, program_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain] List the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain] Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n" +" --source -s [!] address[/mask]\n" +" source specification\n" +" --destination -d [!] address[/mask]\n" +" destination specification\n" +" --in-interface -i [!] input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +" --out-interface -o [!] output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +"[!] --fragment -f match second or further fragments only\n" +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + /* Print out any special helps. A user might like to be able + to add a --help to the commandline, and see expected + results. So we call help for all matches & targets */ + for (t=iptables_targets;t;t=t->next) { + printf("\n"); + t->help(); + } + for (m=iptables_matches;m;m=m->next) { + printf("\n"); + m->help(); + } + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(int *cmd, const int newcmd, const int othercmds, int invert) +{ + if (invert) + exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +int +check_inverse(const char option[], int *invert, int *optind, int argc) +{ + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (optind) { + *optind = *optind+1; + if (argc && *optind > argc) + exit_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static void * +fw_malloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) { + perror("iptables: malloc failed"); + exit(1); + } + return p; +} + +static struct in_addr * +host_to_addr(const char *name, unsigned int *naddr) +{ + struct hostent *host; + struct in_addr *addr; + unsigned int i; + + *naddr = 0; + if ((host = gethostbyname(name)) != NULL) { + if (host->h_addrtype != AF_INET || + host->h_length != sizeof(struct in_addr)) + return (struct in_addr *) NULL; + + while (host->h_addr_list[*naddr] != (char *) NULL) + (*naddr)++; + addr = fw_calloc(*naddr, sizeof(struct in_addr)); + for (i = 0; i < *naddr; i++) + inaddrcpy(&(addr[i]), + (struct in_addr *) host->h_addr_list[i]); + return addr; + } + + return (struct in_addr *) NULL; +} + +static char * +addr_to_host(const struct in_addr *addr) +{ + struct hostent *host; + + if ((host = gethostbyaddr((char *) addr, + sizeof(struct in_addr), AF_INET)) != NULL) + return (char *) host->h_name; + + return (char *) NULL; +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +static struct in_addr * +parse_hostnetwork(const char *name, unsigned int *naddrs) +{ + struct in_addr *addrp, *addrptmp; + + if ((addrptmp = dotted_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = fw_malloc(sizeof(struct in_addr)); + inaddrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static struct in_addr * +parse_mask(char *mask) +{ + static struct in_addr maskaddr; + struct in_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 32 bits */ + maskaddr.s_addr = 0xFFFFFFFF; + return &maskaddr; + } + if ((addrp = dotted_to_addr(mask)) != NULL) + /* dotted_to_addr already returns a network byte order addr */ + return addrp; + if (string_to_number(mask, 0, 32, &bits) == -1) + exit_error(PARAMETER_PROBLEM, + "invalid mask `%s' specified", mask); + if (bits != 0) { + maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); + return &maskaddr; + } + + maskaddr.s_addr = 0L; + return &maskaddr; +} + +void +parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs) +{ + struct in_addr *addrp; + char buf[256]; + char *p; + int i, j, k, n; + + strncpy(buf, name, sizeof(buf) - 1); + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_mask(p + 1); + } else + addrp = parse_mask(NULL); + inaddrcpy(maskp, addrp); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (maskp->s_addr == 0L) + strcpy(buf, "0.0.0.0"); + + addrp = *addrpp = parse_hostnetwork(buf, naddrs); + n = *naddrs; + for (i = 0, j = 0; i < n; i++) { + addrp[j++].s_addr &= maskp->s_addr; + for (k = 0; k < j - 1; k++) { + if (addrp[k].s_addr == addrp[j - 1].s_addr) { + (*naddrs)--; + j--; + break; + } + } + } +} + +struct iptables_match * +find_match(const char *name, enum ipt_tryload tryload) +{ + struct iptables_match *ptr; + + for (ptr = iptables_matches; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified target as match. */ + ptr = find_match(name, DONT_LOAD); + + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find match `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ +static struct iptables_match * +find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) +{ + unsigned int proto; + + if (string_to_number(pname, 0, 255, &proto) != -1) { + char *protoname = proto_to_name(proto, nolookup); + + if (protoname) + return find_match(protoname, tryload); + } else + return find_match(pname, tryload); + + return NULL; +} + +u_int16_t +parse_protocol(const char *s) +{ + unsigned int proto; + + if (string_to_number(s, 0, 255, &proto) == -1) { + struct protoent *pent; + + if ((pent = getprotobyname(s))) + proto = pent->p_proto; + else { + unsigned int i; + for (i = 0; + i < sizeof(chain_protos)/sizeof(struct pprot); + i++) { + if (strcmp(s, chain_protos[i].name) == 0) { + proto = chain_protos[i].num; + break; + } + } + if (i == sizeof(chain_protos)/sizeof(struct pprot)) + exit_error(PARAMETER_PROBLEM, + "unknown protocol `%s' specified", + s); + } + } + + return (u_int16_t)proto; +} + +static void +parse_interface(const char *arg, char *vianame, unsigned char *mask) +{ + int vialen = strlen(arg); + unsigned int i; + + memset(mask, 0, IFNAMSIZ); + memset(vianame, 0, IFNAMSIZ); + + if (vialen + 1 > IFNAMSIZ) + exit_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than IFNAMSIZ" + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); + if (vialen == 0) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' + && vianame[i] != '.') { + printf("Warning: wierd character in interface" + " `%s' (No aliases, :, ! or *).\n", + vianame); + break; + } + } + } +} + +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + exit_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%i chars max)", + targetname, sizeof(ipt_chainlabel)-1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +static char * +addr_to_network(const struct in_addr *addr) +{ + struct netent *net; + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) + return (char *) net->n_name; + + return (char *) NULL; +} + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +char * +addr_to_anyname(const struct in_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL || + (name = addr_to_network(addr)) != NULL) + return name; + + return addr_to_dotted(addr); +} + +char * +mask_to_dotted(const struct in_addr *mask) +{ + int i; + static char buf[20]; + u_int32_t maskaddr, bits; + + maskaddr = ntohl(mask->s_addr); + + if (maskaddr == 0xFFFFFFFFL) + /* we don't want to see "/32" */ + return ""; + + i = 32; + bits = 0xFFFFFFFEL; + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + if (i >= 0) + sprintf(buf, "/%d", i); + else + /* mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%s", addr_to_dotted(mask)); + + return buf; +} + +int +string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +static void +set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, + int invert) +{ + if (*options & option) + exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + exit_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +struct iptables_target * +find_target(const char *name, enum ipt_tryload tryload) +{ + struct iptables_target *ptr; + + /* Standard target? */ + if (strcmp(name, "") == 0 + || strcmp(name, IPTC_LABEL_ACCEPT) == 0 + || strcmp(name, IPTC_LABEL_DROP) == 0 + || strcmp(name, IPTC_LABEL_QUEUE) == 0 + || strcmp(name, IPTC_LABEL_RETURN) == 0) + name = "standard"; + + for (ptr = iptables_targets; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified match as a target. */ + ptr = find_target(name, DONT_LOAD); + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find target `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *option_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + + return merge; +} + +void +register_match(struct iptables_match *me) +{ + struct iptables_match **i; + + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_match(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: match `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: match `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Append to list. */ + for (i = &iptables_matches; *i; i = &(*i)->next); + me->next = NULL; + *i = me; + + me->m = NULL; + me->mflags = 0; +} + +void +register_target(struct iptables_target *me) +{ + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_target(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: target `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: target `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Prepend to list. */ + me->next = iptables_targets; + iptables_targets = me; + me->t = NULL; + me->tflags = 0; +} + +static void +print_num(u_int64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), number); + } + else printf(FMT("%4lluG ","%lluG "), number); + } + else printf(FMT("%4lluM ","%lluM "), number); + } else + printf(FMT("%4lluK ","%lluK "), number); + } else + printf(FMT("%5llu ","%llu "), number); + } else + printf(FMT("%8llu ","%llu "), number); +} + + +static void +print_header(unsigned int format, const char *chain, iptc_handle_t *handle) +{ + struct ipt_counters counters; + const char *pol = iptc_get_policy(chain, &counters, handle); + printf("Chain %s", chain); + if (pol) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + print_num(counters.pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + print_num(counters.bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else { + unsigned int refs; + if (!iptc_get_references(&refs, chain, handle)) + printf(" (ERROR obtaining refs)\n"); + else + printf(" (%u references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + + +static int +print_match(const struct ipt_entry_match *m, + const struct ipt_ip *ip, + int numeric) +{ + struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); + + if (match) { + if (match->print) + match->print(ip, m, numeric); + else + printf("%s ", match->name); + } else { + if (m->u.user.name[0]) + printf("UNKNOWN match `%s' ", m->u.user.name); + } + /* Don't stop iterating. */ + return 0; +} + +/* e is called `fw' here for hysterical raisins */ +static void +print_firewall(const struct ipt_entry *fw, + const char *targname, + unsigned int num, + unsigned int format, + const iptc_handle_t handle) +{ + struct iptables_target *target = NULL; + const struct ipt_entry_target *t; + u_int8_t flags; + char buf[BUFSIZ]; + + if (!iptc_is_chain(targname, handle)) + target = find_target(targname, TRY_LOAD); + else + target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); + + t = ipt_get_target((struct ipt_entry *)fw); + flags = fw->ip.flags; + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num+1); + + if (!(format & FMT_NOCOUNTS)) { + print_num(fw->counters.pcnt, format); + print_num(fw->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname); + + fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout); + { + char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), fw->ip.proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + + if (fw->ip.invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.iniface[0] != '\0') { + strcat(iface, fw->ip.iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (fw->ip.invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.outiface[0] != '\0') { + strcat(iface, fw->ip.outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } + + fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src))); + strcat(buf, mask_to_dotted(&(fw->ip.smsk))); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst))); + strcat(buf, mask_to_dotted(&(fw->ip.dmsk))); + printf(FMT("%-19s","-> %s"), buf); + } + + if (format & FMT_NOTABLE) + fputs(" ", stdout); + + IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); + + if (target) { + if (target->print) + /* Print the target information. */ + target->print(&fw->ip, t, format & FMT_NUMERIC); + } else if (t->u.target_size != sizeof(*t)) + printf("[%u bytes of unknown target data] ", + t->u.target_size - sizeof(*t)); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +static void +print_firewall_line(const struct ipt_entry *fw, + const iptc_handle_t h) +{ + struct ipt_entry_target *t; + + t = ipt_get_target((struct ipt_entry *)fw); + print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); +} + +static int +append_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_append_entry(chain, fw, handle); + } + } + + return ret; +} + +static int +replace_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + const struct in_addr *saddr, + const struct in_addr *daddr, + int verbose, + iptc_handle_t *handle) +{ + fw->ip.src.s_addr = saddr->s_addr; + fw->ip.dst.s_addr = daddr->s_addr; + + if (verbose) + print_firewall_line(fw, *handle); + return iptc_replace_entry(chain, fw, rulenum, handle); +} + +static int +insert_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_insert_entry(chain, fw, rulenum, handle); + } + } + + return ret; +} + +static unsigned char * +make_delete_mask(struct ipt_entry *fw) +{ + /* Establish mask for comparison */ + unsigned int size; + struct iptables_match *m; + unsigned char *mask, *mptr; + + size = sizeof(struct ipt_entry); + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + mask = fw_calloc(1, size + + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->size); + + memset(mask, 0xFF, sizeof(struct ipt_entry)); + mptr = mask + sizeof(struct ipt_entry); + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->userspacesize); + mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->userspacesize); + + return mask; +} + +static int +delete_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + unsigned char *mask; + + mask = make_delete_mask(fw); + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_delete_entry(chain, fw, mask, handle); + } + } + return ret; +} + +int +for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), + int verbose, int builtinstoo, iptc_handle_t *handle) +{ + int ret = 1; + const char *chain; + char *chains; + unsigned int i, chaincount = 0; + + chain = iptc_first_chain(handle); + while (chain) { + chaincount++; + chain = iptc_next_chain(handle); + } + + chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount); + i = 0; + chain = iptc_first_chain(handle); + while (chain) { + strcpy(chains + i*sizeof(ipt_chainlabel), chain); + i++; + chain = iptc_next_chain(handle); + } + + for (i = 0; i < chaincount; i++) { + if (!builtinstoo + && iptc_builtin(chains + i*sizeof(ipt_chainlabel), + *handle)) + continue; + ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle); + } + + free(chains); + return ret; +} + +int +flush_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(flush_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Flushing chain `%s'\n", chain); + return iptc_flush_entries(chain, handle); +} + +static int +zero_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(zero_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Zeroing chain `%s'\n", chain); + return iptc_zero_entries(chain, handle); +} + +int +delete_chain(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(delete_chain, verbose, 0, handle); + + if (verbose) + fprintf(stdout, "Deleting chain `%s'\n", chain); + return iptc_delete_chain(chain, handle); +} + +static int +list_entries(const ipt_chainlabel chain, int verbose, int numeric, + int expanded, int linenumbers, iptc_handle_t *handle) +{ + int found = 0; + unsigned int format; + const char *this; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + for (this = iptc_first_chain(handle); + this; + this = iptc_next_chain(handle)) { + const struct ipt_entry *i; + unsigned int num; + + if (chain && strcmp(chain, this) != 0) + continue; + + if (found) printf("\n"); + + print_header(format, this, handle); + i = iptc_first_rule(this, handle); + + num = 0; + while (i) { + print_firewall(i, + iptc_get_target(i, handle), + num++, + format, + *handle); + i = iptc_next_rule(i, handle); + } + found = 1; + } + + errno = ENOENT; + return found; +} + +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + switch (read(procfile, ret, 1024)) { + case -1: goto fail; + case 1024: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +int iptables_insmod(const char *modname, const char *modprobe) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of kernel */ + if (!modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + modprobe = buf; + } + + switch (fork()) { + case 0: + argv[0] = (char *)modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* not usually reached */ + exit(0); + case -1: + return -1; + + default: /* parent */ + wait(NULL); + } + + free(buf); + return 0; +} + +static struct ipt_entry * +generate_entry(const struct ipt_entry *fw, + struct iptables_match *matches, + struct ipt_entry_target *target) +{ + unsigned int size; + struct iptables_match *m; + struct ipt_entry *e; + + size = sizeof(struct ipt_entry); + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + size += m->m->u.match_size; + } + + e = fw_malloc(size + target->u.target_size); + *e = *fw; + e->target_offset = size; + e->next_offset = size + target->u.target_size; + + size = 0; + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + memcpy(e->elems + size, m->m, m->m->u.match_size); + size += m->m->u.match_size; + } + memcpy(e->elems + size, target, target->u.target_size); + + return e; +} + +int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) +{ + struct ipt_entry fw, *e = NULL; + int invert = 0; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in_addr *saddrs = NULL, *daddrs = NULL; + + int c, verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, options = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct iptables_match *m; + struct iptables_target *target = NULL; + struct iptables_target *t; + const char *jumpto = ""; + char *protocol = NULL; + const char *modprobe = NULL; + int proto_used = 0; + + memset(&fw, 0, sizeof(fw)); + + opts = original_opts; + global_option_offset = 0; + + /* re-set optind to 0 in case do_command gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = iptables_matches; m; m = m->next) { + m->mflags = 0; + m->used = 0; + } + + for (t = iptables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((c = getopt_long(argc, argv, + "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:", + opts, NULL)) != -1) { + switch (c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, CMD_ZERO, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'N': + if (optarg && *optarg == '-') + exit_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `-'\n"); + if (find_target(optarg, TRY_LOAD)) + exit_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + /* iptables -p icmp -h */ + if (!iptables_matches && protocol) + find_match(protocol, TRY_LOAD); + + exit_printhelp(); + + /* + * Option selection + */ + case 'p': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_PROTOCOL, &fw.ip.invflags, + invert); + + /* Canonicalize into lower case */ + for (protocol = argv[optind-1]; *protocol; protocol++) + *protocol = tolower(*protocol); + + protocol = argv[optind-1]; + fw.ip.proto = parse_protocol(protocol); + + if (fw.ip.proto == 0 + && (fw.ip.invflags & IPT_INV_PROTO)) + exit_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + fw.nfcache |= NFC_IP_PROTO; + break; + + case 's': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_SOURCE, &fw.ip.invflags, + invert); + shostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_SRC; + break; + + case 'd': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_DESTINATION, &fw.ip.invflags, + invert); + dhostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_DST; + break; + + case 'j': + set_option(&options, OPT_JUMP, &fw.ip.invflags, + invert); + jumpto = parse_target(optarg); + /* TRY_LOAD (may be chain name) */ + target = find_target(jumpto, TRY_LOAD); + + if (target) { + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + target->size; + + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + opts = merge_options(opts, target->extra_opts, &target->option_offset); + } + break; + + + case 'i': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.iniface, + fw.ip.iniface_mask); + fw.nfcache |= NFC_IP_IF_IN; + break; + + case 'o': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.outiface, + fw.ip.outiface_mask); + fw.nfcache |= NFC_IP_IF_OUT; + break; + + case 'f': + set_option(&options, OPT_FRAGMENT, &fw.ip.invflags, + invert); + fw.ip.flags |= IPT_F_FRAG; + fw.nfcache |= NFC_IP_FRAG; + break; + + case 'v': + if (!verbose) + set_option(&options, OPT_VERBOSE, + &fw.ip.invflags, invert); + verbose++; + break; + + case 'm': { + size_t size; + + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = find_match(optarg, LOAD_MUST_SUCCEED); + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + opts = merge_options(opts, m->extra_opts, &m->option_offset); + } + break; + + case 'n': + set_option(&options, OPT_NUMERIC, &fw.ip.invflags, + invert); + break; + + case 't': + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = argv[optind-1]; + break; + + case 'x': + set_option(&options, OPT_EXPANDED, &fw.ip.invflags, + invert); + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", program_version); + else + printf("%s v%s\n", + program_name, program_version); + exit(0); + + case '0': + set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags, + invert); + break; + + case 'M': + modprobe = optarg; + break; + + case 'c': + + set_option(&options, OPT_COUNTERS, &fw.ip.invflags, + invert); + pcnt = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + + if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + + break; + + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + exit_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = TRUE; + optarg[0] = '\0'; + continue; + } + printf("Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + /* FIXME: This scheme doesn't allow two of the same + matches --RR */ + if (!target + || !(target->parse(c - target->option_offset, + argv, invert, + &target->tflags, + &fw, &target->t))) { + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + if (m->parse(c - m->option_offset, + argv, invert, + &m->mflags, + &fw, + &fw.nfcache, + &m->m)) + break; + } + + /* If you listen carefully, you can + actually hear this code suck. */ + + /* some explanations (after four different bugs + * in 3 different releases): If we encountere a + * parameter, that has not been parsed yet, + * it's not an option of an explicitly loaded + * match or a target. However, we support + * implicit loading of the protocol match + * extension. '-p tcp' means 'l4 proto 6' and + * at the same time 'load tcp protocol match on + * demand if we specify --dport'. + * + * To make this work, we need to make sure: + * - the parameter has not been parsed by + * a match (m above) + * - a protocol has been specified + * - the protocol extension has not been + * loaded yet, or is loaded and unused + * [think of iptables-restore!] + * - the protocol extension can be successively + * loaded + */ + if (m == NULL + && protocol + && (!find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + || (find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + && (proto_used == 0)) + ) + && (m = find_proto(protocol, TRY_LOAD, + options&OPT_NUMERIC))) { + /* Try loading protocol */ + size_t size; + + proto_used = 1; + + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + + opts = merge_options(opts, + m->extra_opts, &m->option_offset); + + optind--; + continue; + } + if (!m) + exit_error(PARAMETER_PROBLEM, + "Unknown arg `%s'", + argv[optind-1]); + } + } + invert = FALSE; + } + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + m->final_check(m->mflags); + } + + if (target) + target->final_check(target->tflags); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + exit_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + exit_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + exit_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { + if (!(options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + parse_hostnetworkmask(shostnetworkmask, &saddrs, + &(fw.ip.smsk), &nsaddrs); + + if (dhostnetworkmask) + parse_hostnetworkmask(dhostnetworkmask, &daddrs, + &(fw.ip.dmsk), &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + exit_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, options); + + if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN) + exit_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %i chars)", + chain, IPT_FUNCTION_MAXNAMELEN); + + /* only allocate handle if we weren't called with a handle */ + if (!*handle) + *handle = iptc_init(*table); + + if (!*handle) { + /* try to insmod the module if iptc_init failed */ + iptables_insmod("ip_tables", modprobe); + *handle = iptc_init(*table); + } + + if (!*handle) + exit_error(VERSION_PROBLEM, + "can't initialize iptables table `%s': %s", + *table, iptc_strerror(errno)); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (options & OPT_VIANAMEOUT) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (options & OPT_VIANAMEIN) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + if (target && iptc_is_chain(jumpto, *handle)) { + printf("Warning: using chain %s, not extension\n", + jumpto); + + target = NULL; + } + + /* If they didn't specify a target, or it's a chain + name, use standard. */ + if (!target + && (strlen(jumpto) == 0 + || iptc_is_chain(jumpto, *handle))) { + size_t size; + + target = find_target(IPT_STANDARD_TARGET, + LOAD_MUST_SUCCEED); + + size = sizeof(struct ipt_entry_target) + + target->size; + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + } + + if (!target) { + /* it is no chain, and we can't load a plugin. + * We cannot know if the plugin is corrupt, non + * existant OR if the user just misspelled a + * chain. */ + find_target(jumpto, LOAD_MUST_SUCCEED); + } else { + e = generate_entry(&fw, iptables_matches, target->t); + } + } + + switch (command) { + case CMD_APPEND: + ret = append_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE: + ret = delete_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE_NUM: + ret = iptc_delete_num_entry(chain, rulenum - 1, handle); + break; + case CMD_REPLACE: + ret = replace_entry(chain, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, + handle); + break; + case CMD_INSERT: + ret = insert_entry(chain, e, rulenum - 1, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_LIST: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + break; + case CMD_FLUSH: + ret = flush_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_ZERO: + ret = zero_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_LIST|CMD_ZERO: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + if (ret) + ret = zero_entries(chain, + options&OPT_VERBOSE, handle); + break; + case CMD_NEW_CHAIN: + ret = iptc_create_chain(chain, handle); + break; + case CMD_DELETE_CHAIN: + ret = delete_chain(chain, options&OPT_VERBOSE, handle); + break; + case CMD_RENAME_CHAIN: + ret = iptc_rename_chain(chain, newname, handle); + break; + case CMD_SET_POLICY: + ret = iptc_set_policy(chain, policy, NULL, handle); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + + if (verbose > 1) + dump_entries(*handle); + + return ret; +} diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 7bdbcc3..0000000 --- a/.cvsignore +++ /dev/null @@ -1,8 +0,0 @@ -.makefirst -ip6tables -ip6tables-restore -ip6tables-save -iptables -iptables-restore -iptables-save -*.d diff --git a/Makefile.nolibnsl b/Makefile.nolibnsl new file mode 100644 index 0000000..622a680 --- /dev/null +++ b/Makefile.nolibnsl @@ -0,0 +1,208 @@ +# uncomment this to get a fully statically linked version +# NO_SHARED_LIBS = 1 + +# uncomment this to disable IPv6 support +# DO_IPV6 = 0 + +###################################################################### +# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE +###################################################################### + +# Standard part of Makefile for topdir. +TOPLEVEL_INCLUDED=YES + +ifndef KERNEL_DIR +KERNEL_DIR=/usr/src/linux +endif +IPTABLES_VERSION:=1.2.9 +OLD_IPTABLES_VERSION:=1.2.8 + +PREFIX:=/usr +LIBDIR:=$(PREFIX)/lib +BINDIR:=$(PREFIX)/sbin +MANDIR:=$(PREFIX)/man +INCDIR:=$(PREFIX)/include + +# directory for new iptables releases +RELEASE_DIR:=/tmp + +# Need libc6 for this. FIXME: Should covert to autoconf. +ifeq ($(shell [ -f /usr/include/netinet/ip6.h ] && echo YES), YES) +DO_IPV6:=1 +endif + +COPT_FLAGS:=-O2 +CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -Iinclude/ -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DIPTC_DEBUG + +ifdef NO_SHARED_LIBS +CFLAGS += -DNO_SHARED_LIBS=1 +endif + +ifndef NO_SHARED_LIBS +DEPFILES = $(SHARED_LIBS:%.so=%.d) +SH_CFLAGS:=$(CFLAGS) -fPIC +STATIC_LIBS = +STATIC6_LIBS = +LDFLAGS = -rdynamic +LDLIBS = -ldl -lnsl +else +DEPFILES = $(EXT_OBJS:%.o=%.d) +STATIC_LIBS = extensions/libext.a +STATIC6_LIBS = extensions/libext6.a +LDFLAGS = -static +LDLIBS = +endif + +EXTRAS+=iptables iptables.o +EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8 + +# No longer experimental. +EXTRAS+=iptables-save iptables-restore +EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 + +ifeq ($(DO_IPV6), 1) +EXTRAS+=ip6tables ip6tables.o +EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ip6tables $(DESTDIR)$(MANDIR)/man8/ip6tables.8 +EXTRAS_EXP+=ip6tables-save ip6tables-restore +EXTRA_INSTALLS_EXP+=$(DESTDIR)$(BINDIR)/ip6tables-save $(DESTDIR)$(BINDIR)/ip6tables-restore # $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-restore.8 +endif + +# Sparc64 hack +ifeq ($(shell uname -m),sparc64) +# The kernel is 64-bit, even though userspace is 32. +CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32 +endif + +# HPPA hack +ifeq ($(shell uname -m),parisc64) +# The kernel is 64-bit, even though userspace is 32. +CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32 +endif + +ifndef IPT_LIBDIR +IPT_LIBDIR:=$(LIBDIR)/iptables +endif + +.PHONY: default +default: print-extensions all + +.PHONY: print-extensions +print-extensions: + @[ -n "$(OPTIONALS)" ] && echo Extensions found: $(OPTIONALS) + +iptables.o: iptables.c + $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $< + +iptables: iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/iptables: iptables + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +iptables-save: iptables-save.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/iptables-save: iptables-save + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +iptables-restore: iptables-restore.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +ip6tables.o: ip6tables.c + $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $< + +ip6tables: ip6tables-standalone.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/ip6tables: ip6tables + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +ip6tables-save: ip6tables-save.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/ip6tables-save: ip6tables-save + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +ip6tables-restore: ip6tables-restore.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a + $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(DESTDIR)$(BINDIR)/ip6tables-restore: ip6tables-restore + @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR) + cp $< $@ + +$(DESTDIR)$(MANDIR)/man8/%.8: %.8 + @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8 + cp $< $@ + +EXTRA_DEPENDS+=iptables-standalone.d iptables.d + +iptables-standalone.d iptables.d: %.d: %.c + @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@ + + +# Development Targets +.PHONY: install-devel-man3 +install-devel-man3: $(DEVEL_MAN3) + @[ -d $(DESTDIR)$(MANDIR)/man3 ] || mkdir -p $(DESTDIR)$(MANDIR)/man3 + @cp -v $(DEVEL_MAN3) $(DESTDIR)$(MANDIR)/man3 + +.PHONY: install-devel-headers +install-devel-headers: $(DEVEL_HEADERS) + @[ -d $(DESTDIR)$(INCDIR) ] || mkdir -p $(DESTDIR)$(INCDIR) + @cp -v $(DEVEL_HEADERS) $(DESTDIR)$(INCDIR) + +.PHONY: install-devel-libs +install-devel-libs: $(DEVEL_LIBS) + @[ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR) + @cp -v $(DEVEL_LIBS) $(DESTDIR)$(LIBDIR) + +.PHONY: install-devel +install-devel: all install-devel-man3 install-devel-headers install-devel-libs + +.PHONY: distclean +distclean: clean + @rm -f TAGS `find . -name '*~' -o -name '.*~'` `find . -name '*.rej'` `find . -name '*.d'` .makefirst + +# Rusty's distro magic. +.PHONY: distrib +distrib: check distclean delrelease $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 diff md5sums # nowhitespace + +# Makefile must not define: +# -g -pg -DIPTC_DEBUG +.PHONY: check +check: + @if echo $(CFLAGS) | egrep -e '-g|-pg|IPTC_DEBUG' >/dev/null; then echo Remove debugging flags; exit 1; else exit 0; fi + +.PHONY: nowhitespace +nowhitespace: + @if grep -n '[ ]$$' `find . -name 'Makefile' -o -name '*.[ch]'`; then exit 1; else exit 0; fi + +.PHONY: delrelease +delrelease: + rm -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 + +$(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2: + cd .. && ln -sf userspace iptables-$(IPTABLES_VERSION) && tar cvf - --exclude CVS iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION) + +.PHONY: diff +diff: $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 + @mkdir /tmp/diffdir + @cd /tmp/diffdir && tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 + @set -e; cd /tmp/diffdir; tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(OLD_IPTABLES_VERSION).tar.bz2; echo Creating patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2; diff -urN iptables-$(OLD_IPTABLES_VERSION) iptables-$(IPTABLES_VERSION) | bzip2 -9 > $(RELEASE_DIR)/patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2 + @rm -rf /tmp/diffdir + +.PHONY: md5sums +md5sums: + cd $(RELEASE_DIR)/ && md5sum patch-iptables-*-$(IPTABLES_VERSION).bz2 iptables-$(IPTABLES_VERSION).tar.bz2 + +# $(wildcard) fails wierdly with make v.3.78.1. +include $(shell echo */Makefile) +include Rules.make diff --git a/extensions/.#libipt_ECN.c.1.7 b/extensions/.#libipt_ECN.c.1.7 new file mode 100644 index 0000000..7bb0044 --- /dev/null +++ b/extensions/.#libipt_ECN.c.1.7 @@ -0,0 +1,180 @@ +/* Shared library add-on to iptables for ECN, $Version$ + * + * (C) 2002 by Harald Welte + * + * This program is distributed under the terms of GNU GPL v2, 1991 + * + * libipt_ECN.c borrowed heavily from libipt_DSCP.c + * + * $Id: libipt_ECN.c,v 1.7 2002/05/29 15:11:36 laforge Exp $ + */ +#include +#include +#include +#include + +#include +#include +#include + +static void init(struct ipt_entry_target *t, unsigned int *nfcache) +{ +} + +static void help(void) +{ + printf( +"ECN target v%s options\n" +" --ecn-tcp-remove Remove all ECN bits from TCP header\n" +"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n" +" --ecn-ip-ect Set the IPv4 ECT codepoint (0 to 3)\n" +" --ecn-tcp-cwr Set the IPv4 CWR bit (0 or 1)\n" +" --ecn-tcp-ece Set the IPv4 CWR bit (0 or 1)\n", + IPTABLES_VERSION, IPTABLES_VERSION +); +} + +static struct option opts[] = { + { "ecn-tcp-remove", 0, 0, 'F' }, + { "ecn-tcp-cwr", 1, 0, 'G' }, + { "ecn-tcp-ece", 1, 0, 'H' }, + { "ecn-ip-ect", 1, 0, '9' }, + { 0 } +}; + +static int +parse(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + struct ipt_entry_target **target) +{ + unsigned int result; + struct ipt_ECN_info *einfo + = (struct ipt_ECN_info *)(*target)->data; + + switch (c) { + case 'F': + if (*flags) + exit_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-remove ONCE!"); + einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR; + einfo->proto.tcp.ece = 0; + einfo->proto.tcp.cwr = 0; + *flags = 1; + break; + case 'G': + if (*flags & IPT_ECN_OP_SET_CWR) + exit_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-cwr ONCE!"); + if (string_to_number(optarg, 0, 1, &result)) + exit_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_CWR; + einfo->proto.tcp.cwr = result; + *flags |= IPT_ECN_OP_SET_CWR; + break; + case 'H': + if (*flags & IPT_ECN_OP_SET_ECE) + exit_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-tcp-ece ONCE!"); + if (string_to_number(optarg, 0, 1, &result)) + exit_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_ECE; + einfo->proto.tcp.ece = result; + *flags |= IPT_ECN_OP_SET_ECE; + break; + case '9': + if (*flags & IPT_ECN_OP_SET_IP) + exit_error(PARAMETER_PROBLEM, + "ECN target: Only use --ecn-ip-ect ONCE!"); + if (string_to_number(optarg, 0, 3, &result)) + exit_error(PARAMETER_PROBLEM, + "ECN target: Value out of range"); + einfo->operation |= IPT_ECN_OP_SET_IP; + einfo->ip_ect = (result << IPT_ECN_SHIFT); + default: + return 0; + } + + return 1; +} + +static void +final_check(unsigned int flags) +{ + if (!flags) + exit_error(PARAMETER_PROBLEM, + "ECN target: Parameter --ecn-remove is required"); +} + +/* Prints out the targinfo. */ +static void +print(const struct ipt_ip *ip, + const struct ipt_entry_target *target, + int numeric) +{ + const struct ipt_ECN_info *einfo = + (const struct ipt_ECN_info *)target->data; + + printf("ECN "); + + if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) + && einfo->proto.tcp.ece == 0 + && einfo->proto.tcp.cwr == 0) + printf("TCP remove "); + else { + if (einfo->operation & IPT_ECN_OP_SET_ECE) + printf("ECE=%u ", einfo->proto.tcp.ece); + + if (einfo->operation & IPT_ECN_OP_SET_CWR) + printf("CWR=%u ", einfo->proto.tcp.cwr); + + if (einfo->operation & IPT_ECN_OP_SET_IP) + printf("ECT codepoint=%u ", einfo->ip_ect); + } +} + +/* Saves the union ipt_targinfo in parsable form to stdout. */ +static void +save(const struct ipt_ip *ip, const struct ipt_entry_target *target) +{ + const struct ipt_ECN_info *einfo = + (const struct ipt_ECN_info *)target->data; + + if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) + && einfo->proto.tcp.ece == 0 + && einfo->proto.tcp.cwr == 0) + printf("--ecn-tcp-remove "); + else { + + if (einfo->operation & IPT_ECN_OP_SET_ECE) + printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece); + + if (einfo->operation & IPT_ECN_OP_SET_CWR) + printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr); + + if (einfo->operation & IPT_ECN_OP_SET_IP) + printf("--ecn-ip-ect %d ", einfo->ip_ect); + } +} + +static +struct iptables_target ecn += { NULL, + "ECN", + IPTABLES_VERSION, + IPT_ALIGN(sizeof(struct ipt_ECN_info)), + IPT_ALIGN(sizeof(struct ipt_ECN_info)), + &help, + &init, + &parse, + &final_check, + &print, + &save, + opts +}; + +void _init(void) +{ + register_target(&ecn); +} diff --git a/extensions/.#libipt_recent.c.1.6 b/extensions/.#libipt_recent.c.1.6 new file mode 100644 index 0000000..cb86597 --- /dev/null +++ b/extensions/.#libipt_recent.c.1.6 @@ -0,0 +1,229 @@ +/* Shared library add-on to iptables to add recent matching support. */ +#include +#include +#include +#include +#include + +#include +#include + +/* Function which prints out usage message. */ +static void +help(void) +{ + printf( +"recent v%s options:\n" +"[!] --set Add source address to list, always matches.\n" +"[!] --rcheck Match if source address in list.\n" +"[!] --update Match if source address in list, also update last-seen time.\n" +"[!] --remove Match if source address in list, also removes that address from list.\n" +" --seconds seconds For check and update commands above.\n" +" Specifies that the match will only occur if source address last seen within\n" +" the last 'seconds' seconds.\n" +" --hitcount hits For check and update commands above.\n" +" Specifies that the match will only occur if source address seen hits times.\n" +" May be used in conjunction with the seconds option.\n" +" --rttl For check and update commands above.\n" +" Specifies that the match will only occur if the source address and the TTL\n" +" match between this packet and the one which was set.\n" +" Useful if you have problems with people spoofing their source address in order\n" +" to DoS you via this module.\n" +" --name name Name of the recent list to be used. DEFAULT used if none given.\n" +" --rsource Save the source address of each packet in the recent list table (default).\n" +" --rdest Save the destination address of each packet in the recent list table.\n" +, +IPTABLES_VERSION); + +} + +static struct option opts[] = { + { "set", 0, 0, 201 }, + { "rcheck", 0, 0, 202 }, + { "update", 0, 0, 203 }, + { "seconds", 1, 0, 204 }, + { "hitcount", 1, 0, 205 }, + { "remove",0, 0, 206 }, + { "rttl",0, 0, 207 }, + { "name", 1, 0, 208 }, + { "rsource", 0, 0, 209 }, + { "rdest", 0, 0, 210 }, + {0} +}; + +/* Initialize the match. */ +static void +init(struct ipt_entry_match *match, unsigned int *nfcache) +{ + struct ipt_recent_info *info = (struct ipt_recent_info *)(match)->data; + + *nfcache |= NFC_UNKNOWN; + + strncpy(info->name, "DEFAULT", 200); + info->side = IPT_RECENT_SOURCE; +} + +/* Function which parses command options; returns true if it + ate an option */ +static int +parse(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + unsigned int *nfcache, + struct ipt_entry_match **match) +{ + struct ipt_recent_info *info = (struct ipt_recent_info *)(*match)->data; + switch (c) { + case 201: + if (*flags) + exit_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--check' " + "`--update' or `--remove' may be set"); + check_inverse(optarg, &invert, &optind, 0); + info->check_set |= IPT_RECENT_SET; + if (invert) + info->invert = 1; + *flags = 1; + break; + + case 202: + if (*flags) + exit_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--check' " + "`--update' or `--remove' may be set"); + check_inverse(optarg, &invert, &optind, 0); + info->check_set |= IPT_RECENT_CHECK; + if (invert) + info->invert = 1; + *flags = 1; + break; + + case 203: + if (*flags) + exit_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--check' " + "`--update' or `--remove' may be set"); + check_inverse(optarg, &invert, &optind, 0); + info->check_set |= IPT_RECENT_UPDATE; + if (invert) + info->invert = 1; + *flags = 1; + break; + + case 206: + if (*flags) + exit_error(PARAMETER_PROBLEM, + "recent: only one of `--set', `--check' " + "`--update' or `--remove' may be set"); + check_inverse(optarg, &invert, &optind, 0); + info->check_set |= IPT_RECENT_REMOVE; + if (invert) + info->invert = 1; + *flags = 1; + break; + + case 204: + info->seconds = atoi(optarg); + break; + + case 205: + info->hit_count = atoi(optarg); + break; + + case 207: + info->check_set |= IPT_RECENT_TTL; + break; + + case 208: + strncpy(info->name, optarg, 200); + break; + + case 209: + info->side = IPT_RECENT_SOURCE; + break; + + case 210: + info->side = IPT_RECENT_DEST; + break; + + default: + return 0; + } + + return 1; +} + +/* Final check; must have specified a specific option. */ +static void +final_check(unsigned int flags) +{ + + if (!flags) + exit_error(PARAMETER_PROBLEM, + "recent: you must specify one of `--set', `--check' " + "`--update' or `--remove'"); +} + +/* Prints out the matchinfo. */ +static void +print(const struct ipt_ip *ip, + const struct ipt_entry_match *match, + int numeric) +{ + struct ipt_recent_info *info = (struct ipt_recent_info *)match->data; + + if (info->invert) fputc('!', stdout); + + printf("recent: "); + if(info->check_set & IPT_RECENT_SET) printf("SET "); + if(info->check_set & IPT_RECENT_CHECK) printf("CHECK "); + if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE "); + if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE "); + if(info->seconds) printf("seconds: %d ", info->seconds); + if(info->hit_count) printf("hit_count: %d ", info->hit_count); + if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match "); + if(info->name) printf("name: %s ", info->name); + if(info->side == IPT_RECENT_SOURCE) printf("side: source "); + if(info->side == IPT_RECENT_DEST) printf("side: dest"); +} + +/* Saves the union ipt_matchinfo in parsable form to stdout. */ +static void +save(const struct ipt_ip *ip, const struct ipt_entry_match *match) +{ + struct ipt_recent_info *info = (struct ipt_recent_info *)match; + + if (info->invert) fputc('!', stdout); + + printf("recent: "); + if(info->check_set & IPT_RECENT_SET) printf("SET "); + if(info->check_set & IPT_RECENT_CHECK) printf("CHECK "); + if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE "); + if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE "); + if(info->seconds) printf("seconds: %d ",info->seconds); + if(info->hit_count) printf("hit_count: %d ",info->hit_count); + if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match "); + if(info->name) printf("name: %s ",info->name); + if(info->side == IPT_RECENT_SOURCE) printf("side: source "); + if(info->side == IPT_RECENT_DEST) printf("side: dest"); +} + +static +struct iptables_match recent += { NULL, + "recent", + IPTABLES_VERSION, + IPT_ALIGN(sizeof(struct ipt_recent_info)), + IPT_ALIGN(sizeof(struct ipt_recent_info)), + &help, + &init, + &parse, + &final_check, + &print, + &save, + opts +}; + +void _init(void) +{ + register_match(&recent); +} diff --git a/extensions/.cvsignore b/extensions/.cvsignore deleted file mode 100644 index a438335..0000000 --- a/extensions/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/extensions/libipt_icmp.c.print_type b/extensions/libipt_icmp.c.print_type new file mode 100644 index 0000000..6c7b1c3 --- /dev/null +++ b/extensions/libipt_icmp.c.print_type @@ -0,0 +1,310 @@ +/* Shared library add-on to iptables to add ICMP support. */ +#include +#include +#include +#include +#include +#include +#include + +/* special hack for icmp-type 'any': + * Up to kernel <=2.4.20 the problem was: + * '-p icmp ' matches all icmp packets + * '-p icmp -m icmp' matches _only_ ICMP type 0 :( + * This is now fixed by initializing the field * to icmp type 0xFF + * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37 + */ + +struct icmp_names { + const char *name; + u_int8_t type; + u_int8_t code_min, code_max; +}; + +static const struct icmp_names icmp_codes[] = { + { "any", 0xFF, 0, 0xFF }, + { "echo-reply", 0, 0, 0xFF }, + /* Alias */ { "pong", 0, 0, 0xFF }, + + { "destination-unreachable", 3, 0, 0xFF }, + { "network-unreachable", 3, 0, 0 }, + { "host-unreachable", 3, 1, 1 }, + { "protocol-unreachable", 3, 2, 2 }, + { "port-unreachable", 3, 3, 3 }, + { "fragmentation-needed", 3, 4, 4 }, + { "source-route-failed", 3, 5, 5 }, + { "network-unknown", 3, 6, 6 }, + { "host-unknown", 3, 7, 7 }, + { "network-prohibited", 3, 9, 9 }, + { "host-prohibited", 3, 10, 10 }, + { "TOS-network-unreachable", 3, 11, 11 }, + { "TOS-host-unreachable", 3, 12, 12 }, + { "communication-prohibited", 3, 13, 13 }, + { "host-precedence-violation", 3, 14, 14 }, + { "precedence-cutoff", 3, 15, 15 }, + + { "source-quench", 4, 0, 0xFF }, + + { "redirect", 5, 0, 0xFF }, + { "network-redirect", 5, 0, 0 }, + { "host-redirect", 5, 1, 1 }, + { "TOS-network-redirect", 5, 2, 2 }, + { "TOS-host-redirect", 5, 3, 3 }, + + { "echo-request", 8, 0, 0xFF }, + /* Alias */ { "ping", 8, 0, 0xFF }, + + { "router-advertisement", 9, 0, 0xFF }, + + { "router-solicitation", 10, 0, 0xFF }, + + { "time-exceeded", 11, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, + { "ttl-zero-during-transit", 11, 0, 0 }, + { "ttl-zero-during-reassembly", 11, 1, 1 }, + + { "parameter-problem", 12, 0, 0xFF }, + { "ip-header-bad", 12, 0, 0 }, + { "required-option-missing", 12, 1, 1 }, + + { "timestamp-request", 13, 0, 0xFF }, + + { "timestamp-reply", 14, 0, 0xFF }, + + { "address-mask-request", 17, 0, 0xFF }, + + { "address-mask-reply", 18, 0, 0xFF } +}; + +static void +print_icmptypes() +{ + unsigned int i; + printf("Valid ICMP Types:"); + + for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) { + if (i && icmp_codes[i].type == icmp_codes[i-1].type) { + if (icmp_codes[i].code_min == icmp_codes[i-1].code_min + && (icmp_codes[i].code_max + == icmp_codes[i-1].code_max)) + printf(" (%s)", icmp_codes[i].name); + else + printf("\n %s", icmp_codes[i].name); + } + else + printf("\n%s", icmp_codes[i].name); + } + printf("\n"); +} + +/* Function which prints out usage message. */ +static void +help(void) +{ + printf( +"ICMP v%s options:\n" +" --icmp-type [!] typename match icmp type\n" +" (or numeric type or type/code)\n" +"\n", IPTABLES_VERSION); + print_icmptypes(); +} + +static struct option opts[] = { + { "icmp-type", 1, 0, '1' }, + {0} +}; + +static unsigned int +parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[]) +{ + unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names); + unsigned int match = limit; + unsigned int i; + + for (i = 0; i < limit; i++) { + if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)) + == 0) { + if (match != limit) + exit_error(PARAMETER_PROBLEM, + "Ambiguous ICMP type `%s':" + " `%s' or `%s'?", + icmptype, + icmp_codes[match].name, + icmp_codes[i].name); + match = i; + } + } + + if (match != limit) { + *type = icmp_codes[match].type; + code[0] = icmp_codes[match].code_min; + code[1] = icmp_codes[match].code_max; + } else { + char *slash; + char buffer[strlen(icmptype) + 1]; + unsigned int number; + + strcpy(buffer, icmptype); + slash = strchr(buffer, '/'); + + if (slash) + *slash = '\0'; + + if (string_to_number(buffer, 0, 255, &number) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid ICMP type `%s'\n", buffer); + *type = number; + if (slash) { + if (string_to_number(slash+1, 0, 255, &number) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid ICMP code `%s'\n", + slash+1); + code[0] = code[1] = number; + } else { + code[0] = 0; + code[1] = 0xFF; + } + } + + if (code[0] == 0 && code[1] == 0xFF) + return NFC_IP_SRC_PT; + else return NFC_IP_SRC_PT | NFC_IP_DST_PT; +} + +/* Initialize the match. */ +static void +init(struct ipt_entry_match *m, unsigned int *nfcache) +{ + struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data; + + icmpinfo->type = 0xFF; + icmpinfo->code[1] = 0xFF; +} + +/* Function which parses command options; returns true if it + ate an option */ +static int +parse(int c, char **argv, int invert, unsigned int *flags, + const struct ipt_entry *entry, + unsigned int *nfcache, + struct ipt_entry_match **match) +{ + struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data; + + switch (c) { + case '1': + check_inverse(optarg, &invert, &optind, 0); + *nfcache |= parse_icmp(argv[optind-1], + &icmpinfo->type, + icmpinfo->code); + if (invert) + icmpinfo->invflags |= IPT_ICMP_INV; + break; + + default: + return 0; + } + + return 1; +} + +static void print_icmptype(u_int8_t type, + u_int8_t code_min, u_int8_t code_max, + int invert, + int numeric) +{ + if (!numeric) { + unsigned int i; + + for (i = 0; + i < sizeof(icmp_codes)/sizeof(struct icmp_names); + i++) { + if (icmp_codes[i].type == type + && icmp_codes[i].code_min == code_min + && icmp_codes[i].code_max == code_max) + break; + } + + if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) { + printf("%s%s ", + invert ? "!" : "", + icmp_codes[i].name); + return; + } + } + + if (invert) + printf("!"); + + printf("type %u", type); + if (code_min == 0 && code_max == 0xFF) + printf(" "); + else if (code_min == code_max) + printf(" code %u ", code_min); + else + printf(" codes %u-%u ", code_min, code_max); +} + +/* Prints out the union ipt_matchinfo. */ +static void +print(const struct ipt_ip *ip, + const struct ipt_entry_match *match, + int numeric) +{ + const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; + + printf("icmp "); + print_icmptype(icmp->type, icmp->code[0], icmp->code[1], + icmp->invflags & IPT_ICMP_INV, + numeric); + + if (icmp->invflags & ~IPT_ICMP_INV) + printf("Unknown invflags: 0x%X ", + icmp->invflags & ~IPT_ICMP_INV); +} + +/* Saves the match in parsable form to stdout. */ +static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match) +{ + const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data; + + if (icmp->invflags & IPT_ICMP_INV) + printf("! "); + + /* special hack for 'any' case */ + if (icmp->type == 0xFF) { + print_icmptype(icmp->type, icmp->code[0], icmp->code[1], + icmp->invflags & IPT_ICMP_INV, 0); + } else { + printf("--icmp-type %u", icmp->type); + if (icmp->code[0] != 0 || icmp->code[1] != 0xFF) + printf("/%u", icmp->code[0]); + printf(" "); + } +} + +/* Final check; we don't care. */ +static void final_check(unsigned int flags) +{ +} + +static +struct iptables_match icmp += { NULL, + "icmp", + IPTABLES_VERSION, + IPT_ALIGN(sizeof(struct ipt_icmp)), + IPT_ALIGN(sizeof(struct ipt_icmp)), + &help, + &init, + &parse, + &final_check, + &print, + &save, + opts +}; + +void _init(void) +{ + register_match(&icmp); +} diff --git a/ip6tables.c.selinux b/ip6tables.c.selinux new file mode 100644 index 0000000..fc4e284 --- /dev/null +++ b/ip6tables.c.selinux @@ -0,0 +1,2300 @@ +/* Code to take an iptables-style command line and do it. */ + +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IP6T_LIB_DIR +#define IP6T_LIB_DIR "/usr/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_CHECK 0x0800U +#define CMD_RENAME_CHAIN 0x1000U +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E' }; + +#define OPTION_OFFSET 256 + +#define OPT_NONE 0x00000U +#define OPT_NUMERIC 0x00001U +#define OPT_SOURCE 0x00002U +#define OPT_DESTINATION 0x00004U +#define OPT_PROTOCOL 0x00008U +#define OPT_JUMP 0x00010U +#define OPT_VERBOSE 0x00020U +#define OPT_EXPANDED 0x00040U +#define OPT_VIANAMEIN 0x00080U +#define OPT_VIANAMEOUT 0x00100U +#define OPT_LINENUMBERS 0x00200U +#define OPT_COUNTERS 0x00400U +#define NUMBER_OF_OPT 11 +static const char optflags[NUMBER_OF_OPT] += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '3', 'c'}; + +static struct option original_opts[] = { + { "append", 1, 0, 'A' }, + { "delete", 1, 0, 'D' }, + { "insert", 1, 0, 'I' }, + { "replace", 1, 0, 'R' }, + { "list", 2, 0, 'L' }, + { "flush", 2, 0, 'F' }, + { "zero", 2, 0, 'Z' }, + { "new-chain", 1, 0, 'N' }, + { "delete-chain", 2, 0, 'X' }, + { "rename-chain", 1, 0, 'E' }, + { "policy", 1, 0, 'P' }, + { "source", 1, 0, 's' }, + { "destination", 1, 0, 'd' }, + { "src", 1, 0, 's' }, /* synonym */ + { "dst", 1, 0, 'd' }, /* synonym */ + { "protocol", 1, 0, 'p' }, + { "in-interface", 1, 0, 'i' }, + { "jump", 1, 0, 'j' }, + { "table", 1, 0, 't' }, + { "match", 1, 0, 'm' }, + { "numeric", 0, 0, 'n' }, + { "out-interface", 1, 0, 'o' }, + { "verbose", 0, 0, 'v' }, + { "exact", 0, 0, 'x' }, + { "version", 0, 0, 'V' }, + { "help", 2, 0, 'h' }, + { "line-numbers", 0, 0, '0' }, + { "modprobe", 1, 0, 'M' }, + { "set-counters", 1, 0, 'c' }, + { 0 } +}; + +/* we need this for ip6tables-restore. ip6tables-restore.c sets line to the + * current line of the input file, in order to give a more precise error + * message. ip6tables itself doesn't need this, so it is initialized to the + * magic number of -1 */ +int line = -1; + +#ifndef __OPTIMIZE__ +struct ip6t_entry_target * +ip6t_get_target(struct ip6t_entry *e) +{ + return (void *)e + e->target_offset; +} +#endif + +static struct option *opts = original_opts; +static unsigned int global_option_offset = 0; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o --line */ +/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'}, +/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'}, +/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x'}, +/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'}, +/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ','x'}, +/*LIST*/ {' ','x','x','x','x',' ',' ','x','x',' '}, +/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x'}, +/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x'}, +/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'}, +/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'}, +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x'}, +/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ','x'}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x'} +}; + +static int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ IP6T_INV_SRCIP, +/* -d */ IP6T_INV_DSTIP, +/* -p */ IP6T_INV_PROTO, +/* -j */ 0, +/* -v */ 0, +/* -x */ 0, +/* -i */ IP6T_INV_VIA_IN, +/* -o */ IP6T_INV_VIA_OUT, +/*--line*/ 0 +}; + +const char *program_version; +const char *program_name; + +/* Keeping track of external matches and targets: linked lists. */ +struct ip6tables_match *ip6tables_matches = NULL; +struct ip6tables_target *ip6tables_targets = NULL; + +/* Extra debugging from libiptc */ +extern void dump_entries6(const ip6tc_handle_t handle); + +/* A few hardcoded protocols for 'all' and in case the user has no + /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +static const struct pprot chain_protos[] = { + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "icmpv6", IPPROTO_ICMPV6 }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "all", 0 }, +}; + +static char * +proto_to_name(u_int8_t proto, int nolookup) +{ + unsigned int i; + + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + + for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) + if (chain_protos[i].num == proto) + return chain_protos[i].name; + + return NULL; +} + +static void +in6addrcpy(struct in6_addr *dst, struct in6_addr *src) +{ + memcpy(dst, src, sizeof(struct in6_addr)); + /* dst->s6_addr = src->s6_addr; */ +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", program_name, program_version); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +void +exit_tryhelp(int status) +{ + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + program_name, program_name ); + exit(status); +} + +void +exit_printhelp(void) +{ + struct ip6tables_match *m = NULL; + struct ip6tables_target *t = NULL; + + printf("%s v%s\n\n" +"Usage: %s -[AD] chain rule-specification [options]\n" +" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LFZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + program_name, program_version, program_name, program_name, + program_name, program_name, program_name, program_name, + program_name, program_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain] List the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain] Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n" +" --source -s [!] address[/mask]\n" +" source specification\n" +" --destination -d [!] address[/mask]\n" +" destination specification\n" +" --in-interface -i [!] input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +" --out-interface -o [!] output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +/*"[!] --fragment -f match second or further fragments only\n"*/ +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + /* Print out any special helps. A user might like to be able to add a --help + to the commandline, and see expected results. So we call help for all + matches & targets */ + for (t=ip6tables_targets;t;t=t->next) { + printf("\n"); + t->help(); + } + for (m=ip6tables_matches;m;m=m->next) { + printf("\n"); + m->help(); + } + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(int *cmd, const int newcmd, const int othercmds, int invert) +{ + if (invert) + exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +int +check_inverse(const char option[], int *invert, int *optind, int argc) +{ + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (optind) { + *optind = *optind+1; + if (argc && *optind > argc) + exit_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = calloc(count, size)) == NULL) { + perror("ip6tables: calloc failed"); + exit(1); + } + return p; +} + +static void * +fw_malloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) { + perror("ip6tables: malloc failed"); + exit(1); + } + return p; +} + +static char * +addr_to_numeric(const struct in6_addr *addrp) +{ + /* 0000:0000:0000:0000:0000:000.000.000.000 + * 0000:0000:0000:0000:0000:0000:0000:0000 */ + static char buf[50+1]; + return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); +} + +static struct in6_addr * +numeric_to_addr(const char *num) +{ + static struct in6_addr ap; + int err; + if ((err=inet_pton(AF_INET6, num, &ap)) == 1) + return ≈ +#ifdef DEBUG + fprintf(stderr, "\nnumeric2addr: %d\n", err); +#endif + return (struct in6_addr *)NULL; +} + + +static struct in6_addr * +host_to_addr(const char *name, unsigned int *naddr) +{ + struct addrinfo hints; + struct addrinfo *res; + static struct in6_addr *addr; + int err; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags=AI_CANONNAME; + hints.ai_family=AF_INET6; + hints.ai_socktype=SOCK_RAW; + hints.ai_protocol=41; + hints.ai_next=NULL; + + *naddr = 0; + if ( (err=getaddrinfo(name, NULL, &hints, &res)) != 0 ){ +#ifdef DEBUG + fprintf(stderr,"Name2IP: %s\n",gai_strerror(err)); +#endif + return (struct in6_addr *) NULL; + } else { + if (res->ai_family != AF_INET6 || + res->ai_addrlen != sizeof(struct sockaddr_in6)) + return (struct in6_addr *) NULL; + +#ifdef DEBUG + fprintf(stderr, "resolved: len=%d %s ", res->ai_addrlen, + addr_to_numeric(&(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr))); +#endif + /* Get the first element of the address-chain */ + addr = fw_calloc(1, sizeof(struct in6_addr)); + in6addrcpy(addr, (struct in6_addr *) + &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr); + freeaddrinfo(res); + *naddr = 1; + return addr; + } + + return (struct in6_addr *) NULL; +} + +static char * +addr_to_host(const struct in6_addr *addr) +{ + struct sockaddr_in6 saddr; + int err; + static char hostname[NI_MAXHOST]; + + memset(&saddr, 0, sizeof(struct sockaddr_in6)); + in6addrcpy(&(saddr.sin6_addr),(struct in6_addr *)addr); + saddr.sin6_family = AF_INET6; + + if ( (err=getnameinfo((struct sockaddr *)&saddr, + sizeof(struct sockaddr_in6), + hostname, sizeof(hostname)-1, + NULL, 0, 0)) != 0 ){ +#ifdef DEBUG + fprintf(stderr,"IP2Name: %s\n",gai_strerror(err)); +#endif + return (char *) NULL; + } else { +#ifdef DEBUG + fprintf (stderr, "\naddr2host: %s\n", hostname); +#endif + + return hostname; + } + + return (char *) NULL; +} + +static char * +mask_to_numeric(const struct in6_addr *addrp) +{ + static char buf[50+2]; + int l = ipv6_prefix_length(addrp); + if (l == -1) { + strcpy(buf, "/"); + strcat(buf, addr_to_numeric(addrp)); + return buf; + } + sprintf(buf, "/%d", l); + return buf; +} + +static struct in6_addr * +network_to_addr(const char *name) +{ + /* abort();*/ + /* TODO: not implemented yet, but the exception breaks the + * name resolvation */ + return (struct in6_addr *)NULL; +} + +static char * +addr_to_anyname(const struct in6_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL) + return name; + + return addr_to_numeric(addr); +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +static struct in6_addr * +parse_hostnetwork(const char *name, unsigned int *naddrs) +{ + struct in6_addr *addrp, *addrptmp; + + if ((addrptmp = numeric_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = fw_malloc(sizeof(struct in6_addr)); + in6addrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static struct in6_addr * +parse_mask(char *mask) +{ + static struct in6_addr maskaddr; + struct in6_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 128 bits */ + memset(&maskaddr, 0xff, sizeof maskaddr); + return &maskaddr; + } + if ((addrp = numeric_to_addr(mask)) != NULL) + return addrp; + if (string_to_number(mask, 0, 128, &bits) == -1) + exit_error(PARAMETER_PROBLEM, + "invalid mask `%s' specified", mask); + if (bits != 0) { + char *p = (char *)&maskaddr; + memset(p, 0xff, bits / 8); + memset(p + (bits / 8) + 1, 0, (128 - bits) / 8); + p[bits / 8] = 0xff << (8 - (bits & 7)); + return &maskaddr; + } + + memset(&maskaddr, 0, sizeof maskaddr); + return &maskaddr; +} + +void +parse_hostnetworkmask(const char *name, struct in6_addr **addrpp, + struct in6_addr *maskp, unsigned int *naddrs) +{ + struct in6_addr *addrp; + char buf[256]; + char *p; + int i, j, n; + + strncpy(buf, name, sizeof(buf) - 1); + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_mask(p + 1); + } else + addrp = parse_mask(NULL); + in6addrcpy(maskp, addrp); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (!memcmp(maskp, &in6addr_any, sizeof(in6addr_any))) + strcpy(buf, "::"); + + addrp = *addrpp = parse_hostnetwork(buf, naddrs); + n = *naddrs; + for (i = 0, j = 0; i < n; i++) { + int k; + for (k = 0; k < 4; k++) + addrp[j].in6_u.u6_addr32[k] &= maskp->in6_u.u6_addr32[k]; + j++; + for (k = 0; k < j - 1; k++) { + if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) { + (*naddrs)--; + j--; + break; + } + } + } +} + +struct ip6tables_match * +find_match(const char *name, enum ip6t_tryload tryload) +{ + struct ip6tables_match *ptr; + int icmphack = 0; + + /* This is ugly as hell. Nonetheless, there is no way of changing + * this without hurting backwards compatibility */ + if ( (strcmp(name,"icmpv6") == 0) || + (strcmp(name,"ipv6-icmp") == 0) || + (strcmp(name,"icmp6") == 0) ) icmphack = 1; + + if (!icmphack) { + for (ptr = ip6tables_matches; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + } else { + for (ptr = ip6tables_matches; ptr; ptr = ptr->next) { + if (strcmp("icmp6", ptr->name) == 0) + break; + } + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so") + + strlen(name)]; + if (!icmphack) + sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name); + else + sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", "icmpv6"); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified target as match. */ + ptr = find_match(name, DONT_LOAD); + + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find match `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ +static struct ip6tables_match * +find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup) +{ + unsigned int proto; + + if (string_to_number(pname, 0, 255, &proto) != -1) { + char *protoname = proto_to_name(proto, nolookup); + + if (protoname) + return find_match(protoname, tryload); + } else + return find_match(pname, tryload); + + return NULL; +} + +u_int16_t +parse_protocol(const char *s) +{ + unsigned int proto; + + if (string_to_number(s, 0, 255, &proto) == -1) { + struct protoent *pent; + + if ((pent = getprotobyname(s))) + proto = pent->p_proto; + else { + unsigned int i; + for (i = 0; + i < sizeof(chain_protos)/sizeof(struct pprot); + i++) { + if (strcmp(s, chain_protos[i].name) == 0) { + proto = chain_protos[i].num; + break; + } + } + if (i == sizeof(chain_protos)/sizeof(struct pprot)) + exit_error(PARAMETER_PROBLEM, + "unknown protocol `%s' specified", + s); + } + } + + return (u_int16_t)proto; +} + +static void +parse_interface(const char *arg, char *vianame, unsigned char *mask) +{ + int vialen = strlen(arg); + unsigned int i; + + memset(mask, 0, IFNAMSIZ); + memset(vianame, 0, IFNAMSIZ); + + if (vialen + 1 > IFNAMSIZ) + exit_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than IFNAMSIZ" + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); + if (vialen == 0) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' + && vianame[i] != '.') { + printf("Warning: wierd character in interface" + " `%s' (No aliases, :, ! or *).\n", + vianame); + break; + } + } + } +} + +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + exit_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname)+1 > sizeof(ip6t_chainlabel)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%i chars max)", + targetname, sizeof(ip6t_chainlabel)-1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +int +string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +static void +set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, + int invert) +{ + if (*options & option) + exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + exit_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +struct ip6tables_target * +find_target(const char *name, enum ip6t_tryload tryload) +{ + struct ip6tables_target *ptr; + + /* Standard target? */ + if (strcmp(name, "") == 0 + || strcmp(name, IP6TC_LABEL_ACCEPT) == 0 + || strcmp(name, IP6TC_LABEL_DROP) == 0 + || strcmp(name, IP6TC_LABEL_QUEUE) == 0 + || strcmp(name, IP6TC_LABEL_RETURN) == 0) + name = "standard"; + + for (ptr = ip6tables_targets; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so") + + strlen(name)]; + sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified match as a target. */ + ptr = find_target(name, DONT_LOAD); + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find target `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *option_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + + return merge; +} + +void +register_match6(struct ip6tables_match *me) +{ + struct ip6tables_match **i; + + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_match(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: match `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IP6T_ALIGN(me->size)) { + fprintf(stderr, "%s: match `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Append to list. */ + for (i = &ip6tables_matches; *i; i = &(*i)->next); + me->next = NULL; + *i = me; + + me->m = NULL; + me->mflags = 0; +} + +void +register_target6(struct ip6tables_target *me) +{ + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_target(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: target `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IP6T_ALIGN(me->size)) { + fprintf(stderr, "%s: target `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Prepend to list. */ + me->next = ip6tables_targets; + ip6tables_targets = me; + me->t = NULL; + me->tflags = 0; +} + +static void +print_num(u_int64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), number); + } + else printf(FMT("%4lluG ","%lluG "), number); + } + else printf(FMT("%4lluM ","%lluM "), number); + } else + printf(FMT("%4lluK ","%lluK "), number); + } else + printf(FMT("%5llu ","%llu "), number); + } else + printf(FMT("%8llu ","%llu "), number); +} + + +static void +print_header(unsigned int format, const char *chain, ip6tc_handle_t *handle) +{ + struct ip6t_counters counters; + const char *pol = ip6tc_get_policy(chain, &counters, handle); + printf("Chain %s", chain); + if (pol) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + print_num(counters.pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + print_num(counters.bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else { + unsigned int refs; + if (!ip6tc_get_references(&refs, chain, handle)) + printf(" (ERROR obtaining refs)\n"); + else + printf(" (%u references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + + +static int +print_match(const struct ip6t_entry_match *m, + const struct ip6t_ip6 *ip, + int numeric) +{ + struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD); + + if (match) { + if (match->print) + match->print(ip, m, numeric); + else + printf("%s ", match->name); + } else { + if (m->u.user.name[0]) + printf("UNKNOWN match `%s' ", m->u.user.name); + } + /* Don't stop iterating. */ + return 0; +} + +/* e is called `fw' here for hysterical raisins */ +static void +print_firewall(const struct ip6t_entry *fw, + const char *targname, + unsigned int num, + unsigned int format, + const ip6tc_handle_t handle) +{ + struct ip6tables_target *target = NULL; + const struct ip6t_entry_target *t; + u_int8_t flags; + char buf[BUFSIZ]; + + if (!ip6tc_is_chain(targname, handle)) + target = find_target(targname, TRY_LOAD); + else + target = find_target(IP6T_STANDARD_TARGET, LOAD_MUST_SUCCEED); + + t = ip6t_get_target((struct ip6t_entry *)fw); + flags = fw->ipv6.flags; + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num+1); + + if (!(format & FMT_NOCOUNTS)) { + print_num(fw->counters.pcnt, format); + print_num(fw->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname); + + fputc(fw->ipv6.invflags & IP6T_INV_PROTO ? '!' : ' ', stdout); + { + char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), fw->ipv6.proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(' ', stdout); /* Invert flag of FRAG */ + fputc(' ', stdout); /* -f */ + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + + if (fw->ipv6.invflags & IP6T_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ipv6.iniface[0] != '\0') { + strcat(iface, fw->ipv6.iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (fw->ipv6.invflags & IP6T_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ipv6.outiface[0] != '\0') { + strcat(iface, fw->ipv6.outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } + + fputc(fw->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout); + if (!memcmp(&fw->ipv6.smsk, &in6addr_any, sizeof in6addr_any) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.src))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.src))); + strcat(buf, mask_to_numeric(&(fw->ipv6.smsk))); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(fw->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout); + if (!memcmp(&fw->ipv6.dmsk, &in6addr_any, sizeof in6addr_any) + && !(format & FMT_NUMERIC)) + printf(FMT("%-19s","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.dst))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.dst))); + strcat(buf, mask_to_numeric(&(fw->ipv6.dmsk))); + printf(FMT("%-19s","-> %s"), buf); + } + + if (format & FMT_NOTABLE) + fputs(" ", stdout); + + IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC); + + if (target) { + if (target->print) + /* Print the target information. */ + target->print(&fw->ipv6, t, format & FMT_NUMERIC); + } else if (t->u.target_size != sizeof(*t)) + printf("[%u bytes of unknown target data] ", + t->u.target_size - sizeof(*t)); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +static void +print_firewall_line(const struct ip6t_entry *fw, + const ip6tc_handle_t h) +{ + struct ip6t_entry_target *t; + + t = ip6t_get_target((struct ip6t_entry *)fw); + print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); +} + +static int +append_entry(const ip6t_chainlabel chain, + struct ip6t_entry *fw, + unsigned int nsaddrs, + const struct in6_addr saddrs[], + unsigned int ndaddrs, + const struct in6_addr daddrs[], + int verbose, + ip6tc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ipv6.src = saddrs[i]; + for (j = 0; j < ndaddrs; j++) { + fw->ipv6.dst = daddrs[j]; + if (verbose) + print_firewall_line(fw, *handle); + ret &= ip6tc_append_entry(chain, fw, handle); + } + } + + return ret; +} + +static int +replace_entry(const ip6t_chainlabel chain, + struct ip6t_entry *fw, + unsigned int rulenum, + const struct in6_addr *saddr, + const struct in6_addr *daddr, + int verbose, + ip6tc_handle_t *handle) +{ + fw->ipv6.src = *saddr; + fw->ipv6.dst = *daddr; + + if (verbose) + print_firewall_line(fw, *handle); + return ip6tc_replace_entry(chain, fw, rulenum, handle); +} + +static int +insert_entry(const ip6t_chainlabel chain, + struct ip6t_entry *fw, + unsigned int rulenum, + unsigned int nsaddrs, + const struct in6_addr saddrs[], + unsigned int ndaddrs, + const struct in6_addr daddrs[], + int verbose, + ip6tc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ipv6.src = saddrs[i]; + for (j = 0; j < ndaddrs; j++) { + fw->ipv6.dst = daddrs[j]; + if (verbose) + print_firewall_line(fw, *handle); + ret &= ip6tc_insert_entry(chain, fw, rulenum, handle); + } + } + + return ret; +} + +static unsigned char * +make_delete_mask(struct ip6t_entry *fw) +{ + /* Establish mask for comparison */ + unsigned int size; + struct ip6tables_match *m; + unsigned char *mask, *mptr; + + size = sizeof(struct ip6t_entry); + for (m = ip6tables_matches; m; m = m->next) { + if (!m->used) + continue; + + size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size; + } + + mask = fw_calloc(1, size + + IP6T_ALIGN(sizeof(struct ip6t_entry_target)) + + ip6tables_targets->size); + + memset(mask, 0xFF, sizeof(struct ip6t_entry)); + mptr = mask + sizeof(struct ip6t_entry); + + for (m = ip6tables_matches; m; m = m->next) { + if (!m->used) + continue; + + memset(mptr, 0xFF, + IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + + m->userspacesize); + mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size; + } + + memset(mptr, 0xFF, + IP6T_ALIGN(sizeof(struct ip6t_entry_target)) + + ip6tables_targets->userspacesize); + + return mask; +} + +static int +delete_entry(const ip6t_chainlabel chain, + struct ip6t_entry *fw, + unsigned int nsaddrs, + const struct in6_addr saddrs[], + unsigned int ndaddrs, + const struct in6_addr daddrs[], + int verbose, + ip6tc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + unsigned char *mask; + + mask = make_delete_mask(fw); + for (i = 0; i < nsaddrs; i++) { + fw->ipv6.src = saddrs[i]; + for (j = 0; j < ndaddrs; j++) { + fw->ipv6.dst = daddrs[j]; + if (verbose) + print_firewall_line(fw, *handle); + ret &= ip6tc_delete_entry(chain, fw, mask, handle); + } + } + return ret; +} + +int +for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), + int verbose, int builtinstoo, ip6tc_handle_t *handle) +{ + int ret = 1; + const char *chain; + char *chains; + unsigned int i, chaincount = 0; + + chain = ip6tc_first_chain(handle); + while (chain) { + chaincount++; + chain = ip6tc_next_chain(handle); + } + + chains = fw_malloc(sizeof(ip6t_chainlabel) * chaincount); + i = 0; + chain = ip6tc_first_chain(handle); + while (chain) { + strcpy(chains + i*sizeof(ip6t_chainlabel), chain); + i++; + chain = ip6tc_next_chain(handle); + } + + for (i = 0; i < chaincount; i++) { + if (!builtinstoo + && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel), + *handle)) + continue; + ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle); + } + + free(chains); + return ret; +} + +int +flush_entries(const ip6t_chainlabel chain, int verbose, + ip6tc_handle_t *handle) +{ + if (!chain) + return for_each_chain(flush_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Flushing chain `%s'\n", chain); + return ip6tc_flush_entries(chain, handle); +} + +static int +zero_entries(const ip6t_chainlabel chain, int verbose, + ip6tc_handle_t *handle) +{ + if (!chain) + return for_each_chain(zero_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Zeroing chain `%s'\n", chain); + return ip6tc_zero_entries(chain, handle); +} + +int +delete_chain(const ip6t_chainlabel chain, int verbose, + ip6tc_handle_t *handle) +{ + if (!chain) + return for_each_chain(delete_chain, verbose, 0, handle); + + if (verbose) + fprintf(stdout, "Deleting chain `%s'\n", chain); + return ip6tc_delete_chain(chain, handle); +} + +static int +list_entries(const ip6t_chainlabel chain, int verbose, int numeric, + int expanded, int linenumbers, ip6tc_handle_t *handle) +{ + int found = 0; + unsigned int format; + const char *this; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + for (this = ip6tc_first_chain(handle); + this; + this = ip6tc_next_chain(handle)) { + const struct ip6t_entry *i; + unsigned int num; + + if (chain && strcmp(chain, this) != 0) + continue; + + if (found) printf("\n"); + + print_header(format, this, handle); + i = ip6tc_first_rule(this, handle); + + num = 0; + while (i) { + print_firewall(i, + ip6tc_get_target(i, handle), + num++, + format, + *handle); + i = ip6tc_next_rule(i, handle); + } + found = 1; + } + + errno = ENOENT; + return found; +} + +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + switch (read(procfile, ret, 1024)) { + case -1: goto fail; + case 1024: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +int ip6tables_insmod(const char *modname, const char *modprobe) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of kernel */ + if (!modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + modprobe = buf; + } + + switch (fork()) { + case 0: + argv[0] = (char *)modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* not usually reached */ + exit(0); + case -1: + return -1; + + default: /* parent */ + wait(NULL); + } + + free(buf); + return 0; +} + +static struct ip6t_entry * +generate_entry(const struct ip6t_entry *fw, + struct ip6tables_match *matches, + struct ip6t_entry_target *target) +{ + unsigned int size; + struct ip6tables_match *m; + struct ip6t_entry *e; + + size = sizeof(struct ip6t_entry); + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + size += m->m->u.match_size; + } + + e = fw_malloc(size + target->u.target_size); + *e = *fw; + e->target_offset = size; + e->next_offset = size + target->u.target_size; + + size = 0; + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + memcpy(e->elems + size, m->m, m->m->u.match_size); + size += m->m->u.match_size; + } + memcpy(e->elems + size, target, target->u.target_size); + + return e; +} + +int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle) +{ + struct ip6t_entry fw, *e = NULL; + int invert = 0; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in6_addr *saddrs = NULL, *daddrs = NULL; + + int c, verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, options = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct ip6tables_match *m; + struct ip6tables_target *target = NULL; + struct ip6tables_target *t; + const char *jumpto = ""; + char *protocol = NULL; + const char *modprobe = NULL; + int proto_used = 0; + char icmp6p[] = "icmpv6"; + + memset(&fw, 0, sizeof(fw)); + + opts = original_opts; + global_option_offset = 0; + + /* re-set optind to 0 in case do_command gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = ip6tables_matches; m; m = m->next) { + m->mflags = 0; + m->used = 0; + } + + for (t = ip6tables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((c = getopt_long(argc, argv, + "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:", + opts, NULL)) != -1) { + switch (c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, CMD_ZERO, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'N': + if (optarg && *optarg == '-') + exit_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `-'\n"); + if (find_target(optarg, TRY_LOAD)) + exit_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + /* iptables -p icmp -h */ + if (!ip6tables_matches && protocol) + find_match(protocol, TRY_LOAD); + + exit_printhelp(); + + /* + * Option selection + */ + case 'p': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_PROTOCOL, &fw.ipv6.invflags, + invert); + + /* Canonicalize into lower case */ + for (protocol = argv[optind-1]; *protocol; protocol++) + *protocol = tolower(*protocol); + + protocol = argv[optind-1]; + if ( strcmp(protocol,"ipv6-icmp") == 0) + protocol = icmp6p; + fw.ipv6.proto = parse_protocol(protocol); + fw.ipv6.flags |= IP6T_F_PROTO; + + if (fw.ipv6.proto == 0 + && (fw.ipv6.invflags & IP6T_INV_PROTO)) + exit_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + fw.nfcache |= NFC_IP6_PROTO; + break; + + case 's': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_SOURCE, &fw.ipv6.invflags, + invert); + shostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP6_SRC; + break; + + case 'd': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_DESTINATION, &fw.ipv6.invflags, + invert); + dhostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP6_DST; + break; + + case 'j': + set_option(&options, OPT_JUMP, &fw.ipv6.invflags, + invert); + jumpto = parse_target(optarg); + /* TRY_LOAD (may be chain name) */ + target = find_target(jumpto, TRY_LOAD); + + if (target) { + size_t size; + + size = IP6T_ALIGN(sizeof(struct ip6t_entry_target)) + + target->size; + + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + opts = merge_options(opts, target->extra_opts, &target->option_offset); + } + break; + + + case 'i': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEIN, &fw.ipv6.invflags, + invert); + parse_interface(argv[optind-1], + fw.ipv6.iniface, + fw.ipv6.iniface_mask); + fw.nfcache |= NFC_IP6_IF_IN; + break; + + case 'o': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEOUT, &fw.ipv6.invflags, + invert); + parse_interface(argv[optind-1], + fw.ipv6.outiface, + fw.ipv6.outiface_mask); + fw.nfcache |= NFC_IP6_IF_OUT; + break; + + case 'v': + if (!verbose) + set_option(&options, OPT_VERBOSE, + &fw.ipv6.invflags, invert); + verbose++; + break; + + case 'm': { + size_t size; + + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = find_match(optarg, LOAD_MUST_SUCCEED); + size = IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + + m->size; + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + opts = merge_options(opts, m->extra_opts, &m->option_offset); + } + break; + + case 'n': + set_option(&options, OPT_NUMERIC, &fw.ipv6.invflags, + invert); + break; + + case 't': + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = argv[optind-1]; + break; + + case 'x': + set_option(&options, OPT_EXPANDED, &fw.ipv6.invflags, + invert); + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", program_version); + else + printf("%s v%s\n", + program_name, program_version); + exit(0); + + case '0': + set_option(&options, OPT_LINENUMBERS, &fw.ipv6.invflags, + invert); + break; + + case 'M': + modprobe = optarg; + break; + + case 'c': + + set_option(&options, OPT_COUNTERS, &fw.ipv6.invflags, + invert); + pcnt = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + + if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + + break; + + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + exit_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = TRUE; + optarg[0] = '\0'; + continue; + } + printf("Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + /* FIXME: This scheme doesn't allow two of the same + matches --RR */ + if (!target + || !(target->parse(c - target->option_offset, + argv, invert, + &target->tflags, + &fw, &target->t))) { + for (m = ip6tables_matches; m; m = m->next) { + if (!m->used) + continue; + + if (m->parse(c - m->option_offset, + argv, invert, + &m->mflags, + &fw, + &fw.nfcache, + &m->m)) + break; + } + + /* If you listen carefully, you can + actually hear this code suck. */ + + /* some explanations (after four different bugs + * in 3 different releases): If we encountere a + * parameter, that has not been parsed yet, + * it's not an option of an explicitly loaded + * match or a target. However, we support + * implicit loading of the protocol match + * extension. '-p tcp' means 'l4 proto 6' and + * at the same time 'load tcp protocol match on + * demand if we specify --dport'. + * + * To make this work, we need to make sure: + * - the parameter has not been parsed by + * a match (m above) + * - a protocol has been specified + * - the protocol extension has not been + * loaded yet, or is loaded and unused + * [think of iptables-restore!] + * - the protocol extension can be successively + * loaded + */ + if (m == NULL + && protocol + && (!find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + || (find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + && (proto_used == 0)) + ) + && (m = find_proto(protocol, TRY_LOAD, + options&OPT_NUMERIC))) { + /* Try loading protocol */ + size_t size; + + proto_used = 1; + + size = IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + + m->size; + + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + + opts = merge_options(opts, + m->extra_opts, &m->option_offset); + + optind--; + continue; + } + + if (!m) + exit_error(PARAMETER_PROBLEM, + "Unknown arg `%s'", + argv[optind-1]); + } + } + invert = FALSE; + } + + for (m = ip6tables_matches; m; m = m->next) { + if (!m->used) + continue; + + m->final_check(m->mflags); + } + + if (target) + target->final_check(target->tflags); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + exit_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + exit_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + exit_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { + if (!(options & OPT_DESTINATION)) + dhostnetworkmask = "::0/0"; + if (!(options & OPT_SOURCE)) + shostnetworkmask = "::0/0"; + } + + if (shostnetworkmask) + parse_hostnetworkmask(shostnetworkmask, &saddrs, + &(fw.ipv6.smsk), &nsaddrs); + + if (dhostnetworkmask) + parse_hostnetworkmask(dhostnetworkmask, &daddrs, + &(fw.ipv6.dmsk), &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (fw.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP))) + exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + exit_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, options); + + if (chain && strlen(chain) > IP6T_FUNCTION_MAXNAMELEN) + exit_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %i chars)", + chain, IP6T_FUNCTION_MAXNAMELEN); + + /* only allocate handle if we weren't called with a handle */ + if (!*handle) + *handle = ip6tc_init(*table); + + if (!*handle) { + /* try to insmod the module if iptc_init failed */ + ip6tables_insmod("ip6_tables", modprobe); + *handle = ip6tc_init(*table); + } + + if (!*handle) + exit_error(VERSION_PROBLEM, + "can't initialize ip6tables table `%s': %s", + *table, ip6tc_strerror(errno)); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (options & OPT_VIANAMEOUT) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (options & OPT_VIANAMEIN) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + if (target && ip6tc_is_chain(jumpto, *handle)) { + printf("Warning: using chain %s, not extension\n", + jumpto); + + target = NULL; + } + + /* If they didn't specify a target, or it's a chain + name, use standard. */ + if (!target + && (strlen(jumpto) == 0 + || ip6tc_is_chain(jumpto, *handle))) { + size_t size; + + target = find_target(IP6T_STANDARD_TARGET, + LOAD_MUST_SUCCEED); + + size = sizeof(struct ip6t_entry_target) + + target->size; + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + } + + if (!target) { + /* it is no chain, and we can't load a plugin. + * We cannot know if the plugin is corrupt, non + * existant OR if the user just misspelled a + * chain. */ + find_target(jumpto, LOAD_MUST_SUCCEED); + } else { + e = generate_entry(&fw, ip6tables_matches, target->t); + } + } + + switch (command) { + case CMD_APPEND: + ret = append_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE: + ret = delete_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE_NUM: + ret = ip6tc_delete_num_entry(chain, rulenum - 1, handle); + break; + case CMD_REPLACE: + ret = replace_entry(chain, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, + handle); + break; + case CMD_INSERT: + ret = insert_entry(chain, e, rulenum - 1, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_LIST: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + break; + case CMD_FLUSH: + ret = flush_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_ZERO: + ret = zero_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_LIST|CMD_ZERO: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + if (ret) + ret = zero_entries(chain, + options&OPT_VERBOSE, handle); + break; + case CMD_NEW_CHAIN: + ret = ip6tc_create_chain(chain, handle); + break; + case CMD_DELETE_CHAIN: + ret = delete_chain(chain, options&OPT_VERBOSE, handle); + break; + case CMD_RENAME_CHAIN: + ret = ip6tc_rename_chain(chain, newname, handle); + break; + case CMD_SET_POLICY: + ret = ip6tc_set_policy(chain, policy, NULL, handle); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + + if (verbose > 1) + dump_entries6(*handle); + + return ret; +} diff --git a/iptables-config b/iptables-config deleted file mode 100644 index 80e37fb..0000000 --- a/iptables-config +++ /dev/null @@ -1,37 +0,0 @@ -# Load additional iptables modules (nat helpers) -# Default: -none- -# Space separated list of nat helpers (e.g. 'ip_nat_ftp ip_nat_irc'), which -# are loaded after the firewall rules are applied. Options for the helpers are -# stored in /etc/modules.conf. -#IPTABLES_MODULES="" - -# Unload modules on restart and stop -# Value: yes|no, default: yes -# This option has to be 'yes' to get to a sane state for a firewall -# restart or stop. Only set to 'no' if there are problems unloading netfilter -# modules. -#IPTABLES_MODULES_UNLOAD="yes" - -# Save current firewall rules on stop. -# Value: yes|no, default: no -# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets stopped -# (e.g. on system shutdown). -#IPTABLES_SAVE_ON_STOP="no" - -# Save current firewall rules on restart. -# Value: yes|no, default: no -# Saves all firewall rules to /etc/sysconfig/iptables if firewall gets -# restarted. -#IPTABLES_SAVE_ON_RESTART="no" - -# Save (and restore) rule and chain counter. -# Value: yes|no, default: no -# Save counters for rules and chains to /etc/sysconfig/iptables if -# 'service iptables save' is called or on stop or restart if SAVE_ON_STOP or -# SAVE_ON_RESTART is enabled. -#IPTABLES_SAVE_COUNTER="no" - -# Numeric status output -# Value: yes|no, default: no -# Print IP addresses and port numbers in numeric format in the status output. -#IPTABLES_STATUS_NUMERIC="no" diff --git a/iptables-restore.c.counters b/iptables-restore.c.counters new file mode 100644 index 0000000..b40630c --- /dev/null +++ b/iptables-restore.c.counters @@ -0,0 +1,384 @@ +/* Code to restore the iptables state, from file by iptables-save. + * (C) 2000-2002 by Harald Welte + * based on previous code from Rusty Russell + * + * This code is distributed under the terms of GNU GPL v2 + * + * $Id: iptables-restore.c,v 1.26 2003/05/02 15:30:11 laforge Exp $ + */ + +#include +#include +#include +#include +#include +#include "iptables.h" +#include "libiptc/libiptc.h" + +#ifdef DEBUG +#define DEBUGP(x, args...) fprintf(stderr, x, ## args) +#else +#define DEBUGP(x, args...) +#endif + +static int binary = 0, counters = 0, verbose = 0, noflush = 0; + +/* Keeping track of external matches and targets. */ +static struct option options[] = { + { "binary", 0, 0, 'b' }, + { "counters", 0, 0, 'c' }, + { "verbose", 1, 0, 'v' }, + { "help", 0, 0, 'h' }, + { "noflush", 0, 0, 'n'}, + { "modprobe", 1, 0, 'M'}, + { 0 } +}; + +static void print_usage(const char *name, const char *version) __attribute__((noreturn)); + +static void print_usage(const char *name, const char *version) +{ + fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n" + " [ --binary ]\n" + " [ --counters ]\n" + " [ --verbose ]\n" + " [ --help ]\n" + " [ --noflush ]\n" + " [ --modprobe=]\n", name); + + exit(1); +} + +iptc_handle_t create_handle(const char *tablename, const char* modprobe ) +{ + iptc_handle_t handle; + + handle = iptc_init(tablename); + + if (!handle) { + /* try to insmod the module if iptc_init failed */ + iptables_insmod("ip_tables", modprobe); + handle = iptc_init(tablename); + } + + if (!handle) { + exit_error(PARAMETER_PROBLEM, "%s: unable to initialize" + "table '%s'\n", program_name, tablename); + exit(1); + } + return handle; +} + +int parse_counters(char *string, struct ipt_counters *ctr) +{ + return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt) == 2); +} + +/* global new argv and argc */ +static char *newargv[255]; +static int newargc; + +/* function adding one argument to newargv, updating newargc + * returns true if argument added, false otherwise */ +static int add_argv(char *what) { + DEBUGP("add_argv: %s\n", what); + if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) { + newargv[newargc] = strdup(what); + newargc++; + return 1; + } else + return 0; +} + +static void free_argv(void) { + int i; + + for (i = 0; i < newargc; i++) + free(newargv[i]); +} + +int main(int argc, char *argv[]) +{ + iptc_handle_t handle = NULL; + char buffer[10240]; + int c; + char curtable[IPT_TABLE_MAXNAMELEN + 1]; + FILE *in; + const char *modprobe = 0; + int in_table = 0; + + program_name = "iptables-restore"; + program_version = IPTABLES_VERSION; + line = 0; + +#ifdef NO_SHARED_LIBS + init_extensions(); +#endif + + while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) { + switch (c) { + case 'b': + binary = 1; + break; + case 'c': + counters = 1; + break; + case 'v': + verbose = 1; + break; + case 'h': + print_usage("iptables-restore", + IPTABLES_VERSION); + break; + case 'n': + noflush = 1; + break; + case 'M': + modprobe = optarg; + break; + } + } + + if (optind == argc - 1) { + in = fopen(argv[optind], "r"); + if (!in) { + fprintf(stderr, "Can't open %s: %s", argv[optind], + strerror(errno)); + exit(1); + } + } + else if (optind < argc) { + fprintf(stderr, "Unknown arguments found on commandline"); + exit(1); + } + else in = stdin; + + /* Grab standard input. */ + while (fgets(buffer, sizeof(buffer), in)) { + int ret = 0; + + line++; + if (buffer[0] == '\n') continue; + else if (buffer[0] == '#') { + if (verbose) fputs(buffer, stdout); + continue; + } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) { + DEBUGP("Calling commit\n"); + ret = iptc_commit(&handle); + in_table = 0; + } else if ((buffer[0] == '*') && (!in_table)) { + /* New table */ + char *table; + + table = strtok(buffer+1, " \t\n"); + DEBUGP("line %u, table '%s'\n", line, table); + if (!table) { + exit_error(PARAMETER_PROBLEM, + "%s: line %u table name invalid\n", + program_name, line); + exit(1); + } + strncpy(curtable, table, IPT_TABLE_MAXNAMELEN); + + if (handle) + iptc_free(&handle); + + handle = create_handle(table, modprobe); + if (noflush == 0) { + DEBUGP("Cleaning all chains of table '%s'\n", + table); + for_each_chain(flush_entries, verbose, 1, + &handle); + + DEBUGP("Deleting all user-defined chains " + "of table '%s'\n", table); + for_each_chain(delete_chain, verbose, 0, + &handle) ; + } + + ret = 1; + in_table = 1; + + } else if ((buffer[0] == ':') && (in_table)) { + /* New chain. */ + char *policy, *chain; + + chain = strtok(buffer+1, " \t\n"); + DEBUGP("line %u, chain '%s'\n", line, chain); + if (!chain) { + exit_error(PARAMETER_PROBLEM, + "%s: line %u chain name invalid\n", + program_name, line); + exit(1); + } + + if (!iptc_builtin(chain, handle)) { + DEBUGP("Creating new chain '%s'\n", chain); + if (!iptc_create_chain(chain, &handle)) + exit_error(PARAMETER_PROBLEM, + "error creating chain " + "'%s':%s\n", chain, + strerror(errno)); + } + + policy = strtok(NULL, " \t\n"); + DEBUGP("line %u, policy '%s'\n", line, policy); + if (!policy) { + exit_error(PARAMETER_PROBLEM, + "%s: line %u policy invalid\n", + program_name, line); + exit(1); + } + + if (strcmp(policy, "-") != 0) { + struct ipt_counters count; + + if (counters) { + char *ctrs; + ctrs = strtok(NULL, " \t\n"); + + parse_counters(ctrs, &count); + + } else { + memset(&count, 0, + sizeof(struct ipt_counters)); + } + + DEBUGP("Setting policy of chain %s to %s\n", + chain, policy); + + if (!iptc_set_policy(chain, policy, &count, + &handle)) + exit_error(OTHER_PROBLEM, + "Can't set policy `%s'" + " on `%s' line %u: %s\n", + chain, policy, line, + iptc_strerror(errno)); + } + + ret = 1; + + } else if (in_table) { + int a; + char *ptr = buffer; + char *pcnt = NULL; + char *bcnt = NULL; + char *parsestart; + + /* the parser */ + char *param_start, *curchar; + int quote_open; + + /* reset the newargv */ + newargc = 0; + + if (buffer[0] == '[') { + /* we have counters in our input */ + ptr = strchr(buffer, ']'); + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Bad line %u: need ]\n", + line); + + pcnt = strtok(buffer+1, ":"); + if (!pcnt) + exit_error(PARAMETER_PROBLEM, + "Bad line %u: need :\n", + line); + + bcnt = strtok(NULL, "]"); + if (!bcnt) + exit_error(PARAMETER_PROBLEM, + "Bad line %u: need ]\n", + line); + + /* start command parsing after counter */ + parsestart = ptr + 1; + } else { + /* start command parsing at start of line */ + parsestart = buffer; + } + + add_argv(argv[0]); + add_argv("-t"); + add_argv((char *) &curtable); + + if (counters && pcnt && bcnt) { + add_argv("--set-counters"); + add_argv((char *) pcnt); + add_argv((char *) bcnt); + } + + /* After fighting with strtok enough, here's now + * a 'real' parser. According to Rusty I'm now no + * longer a real hacker, but I can live with that */ + + quote_open = 0; + param_start = parsestart; + + for (curchar = parsestart; *curchar; curchar++) { + if (*curchar == '"') { + if (quote_open) { + quote_open = 0; + *curchar = ' '; + } else { + quote_open = 1; + param_start++; + } + } + if (*curchar == ' ' + || *curchar == '\t' + || * curchar == '\n') { + char param_buffer[1024]; + int param_len = curchar-param_start; + + if (quote_open) + continue; + + if (!param_len) { + /* two spaces? */ + param_start++; + continue; + } + + /* end of one parameter */ + strncpy(param_buffer, param_start, + param_len); + *(param_buffer+param_len) = '\0'; + + /* check if table name specified */ + if (!strncmp(param_buffer, "-t", 3) + || !strncmp(param_buffer, "--table", 8)) { + exit_error(PARAMETER_PROBLEM, + "Line %u seems to have a " + "-t table option.\n", line); + exit(1); + } + + add_argv(param_buffer); + param_start += param_len + 1; + } else { + /* regular character, skip */ + } + } + + DEBUGP("calling do_command(%u, argv, &%s, handle):\n", + newargc, curtable); + + for (a = 0; a < newargc; a++) + DEBUGP("argv[%u]: %s\n", a, newargv[a]); + + ret = do_command(newargc, newargv, + &newargv[2], &handle); + + free_argv(); + } + if (!ret) { + fprintf(stderr, "%s: line %u failed\n", + program_name, line); + exit(1); + } + } + + return 0; +} diff --git a/iptables.c.selinux b/iptables.c.selinux new file mode 100644 index 0000000..187fd86 --- /dev/null +++ b/iptables.c.selinux @@ -0,0 +1,2298 @@ +/* Code to take an iptables-style command line and do it. */ + +/* + * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au + * + * (C) 2000-2002 by the netfilter coreteam : + * Paul 'Rusty' Russell + * Marc Boucher + * James Morris + * Harald Welte + * Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/lib/iptables" +#endif + +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif + +#define FMT_NUMERIC 0x0001 +#define FMT_NOCOUNTS 0x0002 +#define FMT_KILOMEGAGIGA 0x0004 +#define FMT_OPTIONS 0x0008 +#define FMT_NOTABLE 0x0010 +#define FMT_NOTARGET 0x0020 +#define FMT_VIA 0x0040 +#define FMT_NONEWLINE 0x0080 +#define FMT_LINENUMBERS 0x0100 + +#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ + | FMT_NUMERIC | FMT_NOTABLE) +#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) + + +#define CMD_NONE 0x0000U +#define CMD_INSERT 0x0001U +#define CMD_DELETE 0x0002U +#define CMD_DELETE_NUM 0x0004U +#define CMD_REPLACE 0x0008U +#define CMD_APPEND 0x0010U +#define CMD_LIST 0x0020U +#define CMD_FLUSH 0x0040U +#define CMD_ZERO 0x0080U +#define CMD_NEW_CHAIN 0x0100U +#define CMD_DELETE_CHAIN 0x0200U +#define CMD_SET_POLICY 0x0400U +#define CMD_CHECK 0x0800U +#define CMD_RENAME_CHAIN 0x1000U +#define NUMBER_OF_CMD 13 +static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z', + 'N', 'X', 'P', 'E' }; + +#define OPTION_OFFSET 256 + +#define OPT_NONE 0x00000U +#define OPT_NUMERIC 0x00001U +#define OPT_SOURCE 0x00002U +#define OPT_DESTINATION 0x00004U +#define OPT_PROTOCOL 0x00008U +#define OPT_JUMP 0x00010U +#define OPT_VERBOSE 0x00020U +#define OPT_EXPANDED 0x00040U +#define OPT_VIANAMEIN 0x00080U +#define OPT_VIANAMEOUT 0x00100U +#define OPT_FRAGMENT 0x00200U +#define OPT_LINENUMBERS 0x00400U +#define OPT_COUNTERS 0x00800U +#define NUMBER_OF_OPT 12 +static const char optflags[NUMBER_OF_OPT] += { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'}; + +static struct option original_opts[] = { + { "append", 1, 0, 'A' }, + { "delete", 1, 0, 'D' }, + { "insert", 1, 0, 'I' }, + { "replace", 1, 0, 'R' }, + { "list", 2, 0, 'L' }, + { "flush", 2, 0, 'F' }, + { "zero", 2, 0, 'Z' }, + { "new-chain", 1, 0, 'N' }, + { "delete-chain", 2, 0, 'X' }, + { "rename-chain", 1, 0, 'E' }, + { "policy", 1, 0, 'P' }, + { "source", 1, 0, 's' }, + { "destination", 1, 0, 'd' }, + { "src", 1, 0, 's' }, /* synonym */ + { "dst", 1, 0, 'd' }, /* synonym */ + { "protocol", 1, 0, 'p' }, + { "in-interface", 1, 0, 'i' }, + { "jump", 1, 0, 'j' }, + { "table", 1, 0, 't' }, + { "match", 1, 0, 'm' }, + { "numeric", 0, 0, 'n' }, + { "out-interface", 1, 0, 'o' }, + { "verbose", 0, 0, 'v' }, + { "exact", 0, 0, 'x' }, + { "fragments", 0, 0, 'f' }, + { "version", 0, 0, 'V' }, + { "help", 2, 0, 'h' }, + { "line-numbers", 0, 0, '0' }, + { "modprobe", 1, 0, 'M' }, + { "set-counters", 1, 0, 'c' }, + { 0 } +}; + +/* we need this for iptables-restore. iptables-restore.c sets line to the + * current line of the input file, in order to give a more precise error + * message. iptables itself doesn't need this, so it is initialized to the + * magic number of -1 */ +int line = -1; + +#ifndef __OPTIMIZE__ +struct ipt_entry_target * +ipt_get_target(struct ipt_entry *e) +{ + return (void *)e + e->target_offset; +} +#endif + +static struct option *opts = original_opts; +static unsigned int global_option_offset = 0; + +/* Table of legal combinations of commands and options. If any of the + * given commands make an option legal, that option is legal (applies to + * CMD_LIST and CMD_ZERO only). + * Key: + * + compulsory + * x illegal + * optional + */ + +static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] = +/* Well, it's better than "Re: Linux vs FreeBSD" */ +{ + /* -n -s -d -p -j -v -x -i -o -f --line */ +/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'}, +/*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' '}, +/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'}, +/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'}, +/*CHECK*/ {'x','+','+','+','x',' ','x',' ',' ',' ','x'}, +/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'} +}; + +static int inverse_for_options[NUMBER_OF_OPT] = +{ +/* -n */ 0, +/* -s */ IPT_INV_SRCIP, +/* -d */ IPT_INV_DSTIP, +/* -p */ IPT_INV_PROTO, +/* -j */ 0, +/* -v */ 0, +/* -x */ 0, +/* -i */ IPT_INV_VIA_IN, +/* -o */ IPT_INV_VIA_OUT, +/* -f */ IPT_INV_FRAG, +/*--line*/ 0 +}; + +const char *program_version; +const char *program_name; + +/* Keeping track of external matches and targets: linked lists. */ +struct iptables_match *iptables_matches = NULL; +struct iptables_target *iptables_targets = NULL; + +/* Extra debugging from libiptc */ +extern void dump_entries(const iptc_handle_t handle); + +/* A few hardcoded protocols for 'all' and in case the user has no + /etc/protocols */ +struct pprot { + char *name; + u_int8_t num; +}; + +/* Primitive headers... */ +/* defined in netinet/in.h */ +#if 0 +#ifndef IPPROTO_ESP +#define IPPROTO_ESP 50 +#endif +#ifndef IPPROTO_AH +#define IPPROTO_AH 51 +#endif +#endif + +static const struct pprot chain_protos[] = { + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "icmp", IPPROTO_ICMP }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "all", 0 }, +}; + +static char * +proto_to_name(u_int8_t proto, int nolookup) +{ + unsigned int i; + + if (proto && !nolookup) { + struct protoent *pent = getprotobynumber(proto); + if (pent) + return pent->p_name; + } + + for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) + if (chain_protos[i].num == proto) + return chain_protos[i].name; + + return NULL; +} + +struct in_addr * +dotted_to_addr(const char *dotted) +{ + static struct in_addr addr; + unsigned char *addrp; + char *p, *q; + unsigned int onebyte; + int i; + char buf[20]; + + /* copy dotted string, because we need to modify it */ + strncpy(buf, dotted, sizeof(buf) - 1); + addrp = (unsigned char *) &(addr.s_addr); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return (struct in_addr *) NULL; + + *q = '\0'; + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[i] = (unsigned char) onebyte; + p = q + 1; + } + + /* we've checked 3 bytes, now we check the last one */ + if (string_to_number(p, 0, 255, &onebyte) == -1) + return (struct in_addr *) NULL; + + addrp[3] = (unsigned char) onebyte; + + return &addr; +} + +static struct in_addr * +network_to_addr(const char *name) +{ + struct netent *net; + static struct in_addr addr; + + if ((net = getnetbyname(name)) != NULL) { + if (net->n_addrtype != AF_INET) + return (struct in_addr *) NULL; + addr.s_addr = htonl((unsigned long) net->n_net); + return &addr; + } + + return (struct in_addr *) NULL; +} + +static void +inaddrcpy(struct in_addr *dst, struct in_addr *src) +{ + /* memcpy(dst, src, sizeof(struct in_addr)); */ + dst->s_addr = src->s_addr; +} + +void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + va_start(args, msg); + fprintf(stderr, "%s v%s: ", program_name, program_version); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + if (status == VERSION_PROBLEM) + fprintf(stderr, + "Perhaps iptables or your kernel needs to be upgraded.\n"); + exit(status); +} + +void +exit_tryhelp(int status) +{ + if (line != -1) + fprintf(stderr, "Error occurred at line: %d\n", line); + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + program_name, program_name ); + exit(status); +} + +void +exit_printhelp(void) +{ + struct iptables_match *m = NULL; + struct iptables_target *t = NULL; + + printf("%s v%s\n\n" +"Usage: %s -[AD] chain rule-specification [options]\n" +" %s -[RI] chain rulenum rule-specification [options]\n" +" %s -D chain rulenum [options]\n" +" %s -[LFZ] [chain] [options]\n" +" %s -[NX] chain\n" +" %s -E old-chain-name new-chain-name\n" +" %s -P chain target [options]\n" +" %s -h (print this help information)\n\n", + program_name, program_version, program_name, program_name, + program_name, program_name, program_name, program_name, + program_name, program_name); + + printf( +"Commands:\n" +"Either long or short options are allowed.\n" +" --append -A chain Append to chain\n" +" --delete -D chain Delete matching rule from chain\n" +" --delete -D chain rulenum\n" +" Delete rule rulenum (1 = first) from chain\n" +" --insert -I chain [rulenum]\n" +" Insert in chain as rulenum (default 1=first)\n" +" --replace -R chain rulenum\n" +" Replace rule rulenum (1 = first) in chain\n" +" --list -L [chain] List the rules in a chain or all chains\n" +" --flush -F [chain] Delete all rules in chain or all chains\n" +" --zero -Z [chain] Zero counters in chain or all chains\n" +" --new -N chain Create a new user-defined chain\n" +" --delete-chain\n" +" -X [chain] Delete a user-defined chain\n" +" --policy -P chain target\n" +" Change policy on chain to target\n" +" --rename-chain\n" +" -E old-chain new-chain\n" +" Change chain name, (moving any references)\n" + +"Options:\n" +" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n" +" --source -s [!] address[/mask]\n" +" source specification\n" +" --destination -d [!] address[/mask]\n" +" destination specification\n" +" --in-interface -i [!] input name[+]\n" +" network interface name ([+] for wildcard)\n" +" --jump -j target\n" +" target for rule (may load target extension)\n" +" --match -m match\n" +" extended match (may load extension)\n" +" --numeric -n numeric output of addresses and ports\n" +" --out-interface -o [!] output name[+]\n" +" network interface name ([+] for wildcard)\n" +" --table -t table table to manipulate (default: `filter')\n" +" --verbose -v verbose mode\n" +" --line-numbers print line numbers when listing\n" +" --exact -x expand numbers (display exact values)\n" +"[!] --fragment -f match second or further fragments only\n" +" --modprobe= try to insert modules using this command\n" +" --set-counters PKTS BYTES set the counter during insert/append\n" +"[!] --version -V print package version.\n"); + + /* Print out any special helps. A user might like to be able + to add a --help to the commandline, and see expected + results. So we call help for all matches & targets */ + for (t=iptables_targets;t;t=t->next) { + printf("\n"); + t->help(); + } + for (m=iptables_matches;m;m=m->next) { + printf("\n"); + m->help(); + } + exit(0); +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1< 1; option >>= 1, ptr++); + + return *ptr; +} + +static char +cmd2char(int option) +{ + const char *ptr; + for (ptr = cmdflags; option > 1; option >>= 1, ptr++); + + return *ptr; +} + +static void +add_command(int *cmd, const int newcmd, const int othercmds, int invert) +{ + if (invert) + exit_error(PARAMETER_PROBLEM, "unexpected ! flag"); + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n", + cmd2char(newcmd), cmd2char(*cmd & (~othercmds))); + *cmd |= newcmd; +} + +int +check_inverse(const char option[], int *invert, int *optind, int argc) +{ + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); + *invert = TRUE; + if (optind) { + *optind = *optind+1; + if (argc && *optind > argc) + exit_error(PARAMETER_PROBLEM, + "no argument following `!'"); + } + + return TRUE; + } + return FALSE; +} + +static void * +fw_calloc(size_t count, size_t size) +{ + void *p; + + if ((p = calloc(count, size)) == NULL) { + perror("iptables: calloc failed"); + exit(1); + } + return p; +} + +static void * +fw_malloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) { + perror("iptables: malloc failed"); + exit(1); + } + return p; +} + +static struct in_addr * +host_to_addr(const char *name, unsigned int *naddr) +{ + struct hostent *host; + struct in_addr *addr; + unsigned int i; + + *naddr = 0; + if ((host = gethostbyname(name)) != NULL) { + if (host->h_addrtype != AF_INET || + host->h_length != sizeof(struct in_addr)) + return (struct in_addr *) NULL; + + while (host->h_addr_list[*naddr] != (char *) NULL) + (*naddr)++; + addr = fw_calloc(*naddr, sizeof(struct in_addr)); + for (i = 0; i < *naddr; i++) + inaddrcpy(&(addr[i]), + (struct in_addr *) host->h_addr_list[i]); + return addr; + } + + return (struct in_addr *) NULL; +} + +static char * +addr_to_host(const struct in_addr *addr) +{ + struct hostent *host; + + if ((host = gethostbyaddr((char *) addr, + sizeof(struct in_addr), AF_INET)) != NULL) + return (char *) host->h_name; + + return (char *) NULL; +} + +/* + * All functions starting with "parse" should succeed, otherwise + * the program fails. + * Most routines return pointers to static data that may change + * between calls to the same or other routines with a few exceptions: + * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask" + * return global static data. +*/ + +static struct in_addr * +parse_hostnetwork(const char *name, unsigned int *naddrs) +{ + struct in_addr *addrp, *addrptmp; + + if ((addrptmp = dotted_to_addr(name)) != NULL || + (addrptmp = network_to_addr(name)) != NULL) { + addrp = fw_malloc(sizeof(struct in_addr)); + inaddrcpy(addrp, addrptmp); + *naddrs = 1; + return addrp; + } + if ((addrp = host_to_addr(name, naddrs)) != NULL) + return addrp; + + exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name); +} + +static struct in_addr * +parse_mask(char *mask) +{ + static struct in_addr maskaddr; + struct in_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 32 bits */ + maskaddr.s_addr = 0xFFFFFFFF; + return &maskaddr; + } + if ((addrp = dotted_to_addr(mask)) != NULL) + /* dotted_to_addr already returns a network byte order addr */ + return addrp; + if (string_to_number(mask, 0, 32, &bits) == -1) + exit_error(PARAMETER_PROBLEM, + "invalid mask `%s' specified", mask); + if (bits != 0) { + maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits)); + return &maskaddr; + } + + maskaddr.s_addr = 0L; + return &maskaddr; +} + +void +parse_hostnetworkmask(const char *name, struct in_addr **addrpp, + struct in_addr *maskp, unsigned int *naddrs) +{ + struct in_addr *addrp; + char buf[256]; + char *p; + int i, j, k, n; + + strncpy(buf, name, sizeof(buf) - 1); + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + addrp = parse_mask(p + 1); + } else + addrp = parse_mask(NULL); + inaddrcpy(maskp, addrp); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (maskp->s_addr == 0L) + strcpy(buf, "0.0.0.0"); + + addrp = *addrpp = parse_hostnetwork(buf, naddrs); + n = *naddrs; + for (i = 0, j = 0; i < n; i++) { + addrp[j++].s_addr &= maskp->s_addr; + for (k = 0; k < j - 1; k++) { + if (addrp[k].s_addr == addrp[j - 1].s_addr) { + (*naddrs)--; + j--; + break; + } + } + } +} + +struct iptables_match * +find_match(const char *name, enum ipt_tryload tryload) +{ + struct iptables_match *ptr; + + for (ptr = iptables_matches; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified target as match. */ + ptr = find_match(name, DONT_LOAD); + + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load match `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find match `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ +static struct iptables_match * +find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) +{ + unsigned int proto; + + if (string_to_number(pname, 0, 255, &proto) != -1) { + char *protoname = proto_to_name(proto, nolookup); + + if (protoname) + return find_match(protoname, tryload); + } else + return find_match(pname, tryload); + + return NULL; +} + +u_int16_t +parse_protocol(const char *s) +{ + unsigned int proto; + + if (string_to_number(s, 0, 255, &proto) == -1) { + struct protoent *pent; + + if ((pent = getprotobyname(s))) + proto = pent->p_proto; + else { + unsigned int i; + for (i = 0; + i < sizeof(chain_protos)/sizeof(struct pprot); + i++) { + if (strcmp(s, chain_protos[i].name) == 0) { + proto = chain_protos[i].num; + break; + } + } + if (i == sizeof(chain_protos)/sizeof(struct pprot)) + exit_error(PARAMETER_PROBLEM, + "unknown protocol `%s' specified", + s); + } + } + + return (u_int16_t)proto; +} + +static void +parse_interface(const char *arg, char *vianame, unsigned char *mask) +{ + int vialen = strlen(arg); + unsigned int i; + + memset(mask, 0, IFNAMSIZ); + memset(vianame, 0, IFNAMSIZ); + + if (vialen + 1 > IFNAMSIZ) + exit_error(PARAMETER_PROBLEM, + "interface name `%s' must be shorter than IFNAMSIZ" + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); + if (vialen == 0) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); + /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' + && vianame[i] != '.') { + printf("Warning: wierd character in interface" + " `%s' (No aliases, :, ! or *).\n", + vianame); + break; + } + } + } +} + +/* Can't be zero. */ +static int +parse_rulenumber(const char *rule) +{ + unsigned int rulenum; + + if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1) + exit_error(PARAMETER_PROBLEM, + "Invalid rule number `%s'", rule); + + return rulenum; +} + +static const char * +parse_target(const char *targetname) +{ + const char *ptr; + + if (strlen(targetname) < 1) + exit_error(PARAMETER_PROBLEM, + "Invalid target name (too short)"); + + if (strlen(targetname)+1 > sizeof(ipt_chainlabel)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s' (%i chars max)", + targetname, sizeof(ipt_chainlabel)-1); + + for (ptr = targetname; *ptr; ptr++) + if (isspace(*ptr)) + exit_error(PARAMETER_PROBLEM, + "Invalid target name `%s'", targetname); + return targetname; +} + +static char * +addr_to_network(const struct in_addr *addr) +{ + struct netent *net; + + if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) + return (char *) net->n_name; + + return (char *) NULL; +} + +char * +addr_to_dotted(const struct in_addr *addrp) +{ + static char buf[20]; + const unsigned char *bytep; + + bytep = (const unsigned char *) &(addrp->s_addr); + sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); + return buf; +} + +char * +addr_to_anyname(const struct in_addr *addr) +{ + char *name; + + if ((name = addr_to_host(addr)) != NULL || + (name = addr_to_network(addr)) != NULL) + return name; + + return addr_to_dotted(addr); +} + +char * +mask_to_dotted(const struct in_addr *mask) +{ + int i; + static char buf[20]; + u_int32_t maskaddr, bits; + + maskaddr = ntohl(mask->s_addr); + + if (maskaddr == 0xFFFFFFFFL) + /* we don't want to see "/32" */ + return ""; + + i = 32; + bits = 0xFFFFFFFEL; + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + if (i >= 0) + sprintf(buf, "/%d", i); + else + /* mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%s", addr_to_dotted(mask)); + + return buf; +} + +int +string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +static void +set_option(unsigned int *options, unsigned int option, u_int8_t *invflg, + int invert) +{ + if (*options & option) + exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed", + opt2char(option)); + *options |= option; + + if (invert) { + unsigned int i; + for (i = 0; 1 << i != option; i++); + + if (!inverse_for_options[i]) + exit_error(PARAMETER_PROBLEM, + "cannot have ! before -%c", + opt2char(option)); + *invflg |= inverse_for_options[i]; + } +} + +struct iptables_target * +find_target(const char *name, enum ipt_tryload tryload) +{ + struct iptables_target *ptr; + + /* Standard target? */ + if (strcmp(name, "") == 0 + || strcmp(name, IPTC_LABEL_ACCEPT) == 0 + || strcmp(name, IPTC_LABEL_DROP) == 0 + || strcmp(name, IPTC_LABEL_QUEUE) == 0 + || strcmp(name, IPTC_LABEL_RETURN) == 0) + name = "standard"; + + for (ptr = iptables_targets; ptr; ptr = ptr->next) { + if (strcmp(name, ptr->name) == 0) + break; + } + +#ifndef NO_SHARED_LIBS + if (!ptr && tryload != DONT_LOAD) { + char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") + + strlen(name)]; + sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified match as a target. */ + ptr = find_target(name, DONT_LOAD); + if (!ptr) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s'\n", + name); + } else if (tryload == LOAD_MUST_SUCCEED) + exit_error(PARAMETER_PROBLEM, + "Couldn't load target `%s':%s\n", + name, dlerror()); + } +#else + if (ptr && !ptr->loaded) { + if (tryload != DONT_LOAD) + ptr->loaded = 1; + else + ptr = NULL; + } + if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { + exit_error(PARAMETER_PROBLEM, + "Couldn't find target `%s'\n", name); + } +#endif + + if (ptr) + ptr->used = 1; + + return ptr; +} + +static struct option * +merge_options(struct option *oldopts, const struct option *newopts, + unsigned int *option_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *option_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *option_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + + return merge; +} + +void +register_match(struct iptables_match *me) +{ + struct iptables_match **i; + + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_match(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: match `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: match `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Append to list. */ + for (i = &iptables_matches; *i; i = &(*i)->next); + me->next = NULL; + *i = me; + + me->m = NULL; + me->mflags = 0; +} + +void +register_target(struct iptables_target *me) +{ + if (strcmp(me->version, program_version) != 0) { + fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", + program_name, me->name, me->version, program_version); + exit(1); + } + + if (find_target(me->name, DONT_LOAD)) { + fprintf(stderr, "%s: target `%s' already registered.\n", + program_name, me->name); + exit(1); + } + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: target `%s' has invalid size %u.\n", + program_name, me->name, me->size); + exit(1); + } + + /* Prepend to list. */ + me->next = iptables_targets; + iptables_targets = me; + me->t = NULL; + me->tflags = 0; +} + +static void +print_num(u_int64_t number, unsigned int format) +{ + if (format & FMT_KILOMEGAGIGA) { + if (number > 99999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; + printf(FMT("%4lluT ","%lluT "), number); + } + else printf(FMT("%4lluG ","%lluG "), number); + } + else printf(FMT("%4lluM ","%lluM "), number); + } else + printf(FMT("%4lluK ","%lluK "), number); + } else + printf(FMT("%5llu ","%llu "), number); + } else + printf(FMT("%8llu ","%llu "), number); +} + + +static void +print_header(unsigned int format, const char *chain, iptc_handle_t *handle) +{ + struct ipt_counters counters; + const char *pol = iptc_get_policy(chain, &counters, handle); + printf("Chain %s", chain); + if (pol) { + printf(" (policy %s", pol); + if (!(format & FMT_NOCOUNTS)) { + fputc(' ', stdout); + print_num(counters.pcnt, (format|FMT_NOTABLE)); + fputs("packets, ", stdout); + print_num(counters.bcnt, (format|FMT_NOTABLE)); + fputs("bytes", stdout); + } + printf(")\n"); + } else { + unsigned int refs; + if (!iptc_get_references(&refs, chain, handle)) + printf(" (ERROR obtaining refs)\n"); + else + printf(" (%u references)\n", refs); + } + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4s ", "%s "), "num"); + if (!(format & FMT_NOCOUNTS)) { + if (format & FMT_KILOMEGAGIGA) { + printf(FMT("%5s ","%s "), "pkts"); + printf(FMT("%5s ","%s "), "bytes"); + } else { + printf(FMT("%8s ","%s "), "pkts"); + printf(FMT("%10s ","%s "), "bytes"); + } + } + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ","%s "), "target"); + fputs(" prot ", stdout); + if (format & FMT_OPTIONS) + fputs("opt", stdout); + if (format & FMT_VIA) { + printf(FMT(" %-6s ","%s "), "in"); + printf(FMT("%-6s ","%s "), "out"); + } + printf(FMT(" %-19s ","%s "), "source"); + printf(FMT(" %-19s "," %s "), "destination"); + printf("\n"); +} + + +static int +print_match(const struct ipt_entry_match *m, + const struct ipt_ip *ip, + int numeric) +{ + struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); + + if (match) { + if (match->print) + match->print(ip, m, numeric); + else + printf("%s ", match->name); + } else { + if (m->u.user.name[0]) + printf("UNKNOWN match `%s' ", m->u.user.name); + } + /* Don't stop iterating. */ + return 0; +} + +/* e is called `fw' here for hysterical raisins */ +static void +print_firewall(const struct ipt_entry *fw, + const char *targname, + unsigned int num, + unsigned int format, + const iptc_handle_t handle) +{ + struct iptables_target *target = NULL; + const struct ipt_entry_target *t; + u_int8_t flags; + char buf[BUFSIZ]; + + if (!iptc_is_chain(targname, handle)) + target = find_target(targname, TRY_LOAD); + else + target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); + + t = ipt_get_target((struct ipt_entry *)fw); + flags = fw->ip.flags; + + if (format & FMT_LINENUMBERS) + printf(FMT("%-4u ", "%u "), num+1); + + if (!(format & FMT_NOCOUNTS)) { + print_num(fw->counters.pcnt, format); + print_num(fw->counters.bcnt, format); + } + + if (!(format & FMT_NOTARGET)) + printf(FMT("%-9s ", "%s "), targname); + + fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout); + { + char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); + if (pname) + printf(FMT("%-5s", "%s "), pname); + else + printf(FMT("%-5hu", "%hu "), fw->ip.proto); + } + + if (format & FMT_OPTIONS) { + if (format & FMT_NOTABLE) + fputs("opt ", stdout); + fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); + fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); + fputc(' ', stdout); + } + + if (format & FMT_VIA) { + char iface[IFNAMSIZ+2]; + + if (fw->ip.invflags & IPT_INV_VIA_IN) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.iniface[0] != '\0') { + strcat(iface, fw->ip.iniface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT(" %-6s ","in %s "), iface); + + if (fw->ip.invflags & IPT_INV_VIA_OUT) { + iface[0] = '!'; + iface[1] = '\0'; + } + else iface[0] = '\0'; + + if (fw->ip.outiface[0] != '\0') { + strcat(iface, fw->ip.outiface); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); + printf(FMT("%-6s ","out %s "), iface); + } + + fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); + if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","%s "), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src))); + strcat(buf, mask_to_dotted(&(fw->ip.smsk))); + printf(FMT("%-19s ","%s "), buf); + } + + fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); + if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) + printf(FMT("%-19s ","-> %s"), "anywhere"); + else { + if (format & FMT_NUMERIC) + sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst))); + else + sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst))); + strcat(buf, mask_to_dotted(&(fw->ip.dmsk))); + printf(FMT("%-19s ","-> %s"), buf); + } + + if (format & FMT_NOTABLE) + fputs(" ", stdout); + + IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); + + if (target) { + if (target->print) + /* Print the target information. */ + target->print(&fw->ip, t, format & FMT_NUMERIC); + } else if (t->u.target_size != sizeof(*t)) + printf("[%u bytes of unknown target data] ", + t->u.target_size - sizeof(*t)); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +} + +static void +print_firewall_line(const struct ipt_entry *fw, + const iptc_handle_t h) +{ + struct ipt_entry_target *t; + + t = ipt_get_target((struct ipt_entry *)fw); + print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); +} + +static int +append_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_append_entry(chain, fw, handle); + } + } + + return ret; +} + +static int +replace_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + const struct in_addr *saddr, + const struct in_addr *daddr, + int verbose, + iptc_handle_t *handle) +{ + fw->ip.src.s_addr = saddr->s_addr; + fw->ip.dst.s_addr = daddr->s_addr; + + if (verbose) + print_firewall_line(fw, *handle); + return iptc_replace_entry(chain, fw, rulenum, handle); +} + +static int +insert_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int rulenum, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_insert_entry(chain, fw, rulenum, handle); + } + } + + return ret; +} + +static unsigned char * +make_delete_mask(struct ipt_entry *fw) +{ + /* Establish mask for comparison */ + unsigned int size; + struct iptables_match *m; + unsigned char *mask, *mptr; + + size = sizeof(struct ipt_entry); + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + mask = fw_calloc(1, size + + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->size); + + memset(mask, 0xFF, sizeof(struct ipt_entry)); + mptr = mask + sizeof(struct ipt_entry); + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->userspacesize); + mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + } + + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_target)) + + iptables_targets->userspacesize); + + return mask; +} + +static int +delete_entry(const ipt_chainlabel chain, + struct ipt_entry *fw, + unsigned int nsaddrs, + const struct in_addr saddrs[], + unsigned int ndaddrs, + const struct in_addr daddrs[], + int verbose, + iptc_handle_t *handle) +{ + unsigned int i, j; + int ret = 1; + unsigned char *mask; + + mask = make_delete_mask(fw); + for (i = 0; i < nsaddrs; i++) { + fw->ip.src.s_addr = saddrs[i].s_addr; + for (j = 0; j < ndaddrs; j++) { + fw->ip.dst.s_addr = daddrs[j].s_addr; + if (verbose) + print_firewall_line(fw, *handle); + ret &= iptc_delete_entry(chain, fw, mask, handle); + } + } + return ret; +} + +int +for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *), + int verbose, int builtinstoo, iptc_handle_t *handle) +{ + int ret = 1; + const char *chain; + char *chains; + unsigned int i, chaincount = 0; + + chain = iptc_first_chain(handle); + while (chain) { + chaincount++; + chain = iptc_next_chain(handle); + } + + chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount); + i = 0; + chain = iptc_first_chain(handle); + while (chain) { + strcpy(chains + i*sizeof(ipt_chainlabel), chain); + i++; + chain = iptc_next_chain(handle); + } + + for (i = 0; i < chaincount; i++) { + if (!builtinstoo + && iptc_builtin(chains + i*sizeof(ipt_chainlabel), + *handle)) + continue; + ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle); + } + + free(chains); + return ret; +} + +int +flush_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(flush_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Flushing chain `%s'\n", chain); + return iptc_flush_entries(chain, handle); +} + +static int +zero_entries(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(zero_entries, verbose, 1, handle); + + if (verbose) + fprintf(stdout, "Zeroing chain `%s'\n", chain); + return iptc_zero_entries(chain, handle); +} + +int +delete_chain(const ipt_chainlabel chain, int verbose, + iptc_handle_t *handle) +{ + if (!chain) + return for_each_chain(delete_chain, verbose, 0, handle); + + if (verbose) + fprintf(stdout, "Deleting chain `%s'\n", chain); + return iptc_delete_chain(chain, handle); +} + +static int +list_entries(const ipt_chainlabel chain, int verbose, int numeric, + int expanded, int linenumbers, iptc_handle_t *handle) +{ + int found = 0; + unsigned int format; + const char *this; + + format = FMT_OPTIONS; + if (!verbose) + format |= FMT_NOCOUNTS; + else + format |= FMT_VIA; + + if (numeric) + format |= FMT_NUMERIC; + + if (!expanded) + format |= FMT_KILOMEGAGIGA; + + if (linenumbers) + format |= FMT_LINENUMBERS; + + for (this = iptc_first_chain(handle); + this; + this = iptc_next_chain(handle)) { + const struct ipt_entry *i; + unsigned int num; + + if (chain && strcmp(chain, this) != 0) + continue; + + if (found) printf("\n"); + + print_header(format, this, handle); + i = iptc_first_rule(this, handle); + + num = 0; + while (i) { + print_firewall(i, + iptc_get_target(i, handle), + num++, + format, + *handle); + i = iptc_next_rule(i, handle); + } + found = 1; + } + + errno = ENOENT; + return found; +} + +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + switch (read(procfile, ret, 1024)) { + case -1: goto fail; + case 1024: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +int iptables_insmod(const char *modname, const char *modprobe) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of kernel */ + if (!modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + modprobe = buf; + } + + switch (fork()) { + case 0: + argv[0] = (char *)modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* not usually reached */ + exit(0); + case -1: + return -1; + + default: /* parent */ + wait(NULL); + } + + free(buf); + return 0; +} + +static struct ipt_entry * +generate_entry(const struct ipt_entry *fw, + struct iptables_match *matches, + struct ipt_entry_target *target) +{ + unsigned int size; + struct iptables_match *m; + struct ipt_entry *e; + + size = sizeof(struct ipt_entry); + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + size += m->m->u.match_size; + } + + e = fw_malloc(size + target->u.target_size); + *e = *fw; + e->target_offset = size; + e->next_offset = size + target->u.target_size; + + size = 0; + for (m = matches; m; m = m->next) { + if (!m->used) + continue; + + memcpy(e->elems + size, m->m, m->m->u.match_size); + size += m->m->u.match_size; + } + memcpy(e->elems + size, target, target->u.target_size); + + return e; +} + +int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle) +{ + struct ipt_entry fw, *e = NULL; + int invert = 0; + unsigned int nsaddrs = 0, ndaddrs = 0; + struct in_addr *saddrs = NULL, *daddrs = NULL; + + int c, verbose = 0; + const char *chain = NULL; + const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL; + const char *policy = NULL, *newname = NULL; + unsigned int rulenum = 0, options = 0, command = 0; + const char *pcnt = NULL, *bcnt = NULL; + int ret = 1; + struct iptables_match *m; + struct iptables_target *target = NULL; + struct iptables_target *t; + const char *jumpto = ""; + char *protocol = NULL; + const char *modprobe = NULL; + int proto_used = 0; + + memset(&fw, 0, sizeof(fw)); + + opts = original_opts; + global_option_offset = 0; + + /* re-set optind to 0 in case do_command gets called + * a second time */ + optind = 0; + + /* clear mflags in case do_command gets called a second time + * (we clear the global list of all matches for security)*/ + for (m = iptables_matches; m; m = m->next) { + m->mflags = 0; + m->used = 0; + } + + for (t = iptables_targets; t; t = t->next) { + t->tflags = 0; + t->used = 0; + } + + /* Suppress error messages: we may add new options if we + demand-load a protocol. */ + opterr = 0; + + while ((c = getopt_long(argc, argv, + "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:", + opts, NULL)) != -1) { + switch (c) { + /* + * Command selection + */ + case 'A': + add_command(&command, CMD_APPEND, CMD_NONE, + invert); + chain = optarg; + break; + + case 'D': + add_command(&command, CMD_DELETE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') { + rulenum = parse_rulenumber(argv[optind++]); + command = CMD_DELETE_NUM; + } + break; + + case 'R': + add_command(&command, CMD_REPLACE, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a rule number", + cmd2char(CMD_REPLACE)); + break; + + case 'I': + add_command(&command, CMD_INSERT, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + rulenum = parse_rulenumber(argv[optind++]); + else rulenum = 1; + break; + + case 'L': + add_command(&command, CMD_LIST, CMD_ZERO, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'F': + add_command(&command, CMD_FLUSH, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'Z': + add_command(&command, CMD_ZERO, CMD_LIST, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'N': + if (optarg && *optarg == '-') + exit_error(PARAMETER_PROBLEM, + "chain name not allowed to start " + "with `-'\n"); + if (find_target(optarg, TRY_LOAD)) + exit_error(PARAMETER_PROBLEM, + "chain name may not clash " + "with target name\n"); + add_command(&command, CMD_NEW_CHAIN, CMD_NONE, + invert); + chain = optarg; + break; + + case 'X': + add_command(&command, CMD_DELETE_CHAIN, CMD_NONE, + invert); + if (optarg) chain = optarg; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + chain = argv[optind++]; + break; + + case 'E': + add_command(&command, CMD_RENAME_CHAIN, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + newname = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires old-chain-name and " + "new-chain-name", + cmd2char(CMD_RENAME_CHAIN)); + break; + + case 'P': + add_command(&command, CMD_SET_POLICY, CMD_NONE, + invert); + chain = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + policy = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires a chain and a policy", + cmd2char(CMD_SET_POLICY)); + break; + + case 'h': + if (!optarg) + optarg = argv[optind]; + + /* iptables -p icmp -h */ + if (!iptables_matches && protocol) + find_match(protocol, TRY_LOAD); + + exit_printhelp(); + + /* + * Option selection + */ + case 'p': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_PROTOCOL, &fw.ip.invflags, + invert); + + /* Canonicalize into lower case */ + for (protocol = argv[optind-1]; *protocol; protocol++) + *protocol = tolower(*protocol); + + protocol = argv[optind-1]; + fw.ip.proto = parse_protocol(protocol); + + if (fw.ip.proto == 0 + && (fw.ip.invflags & IPT_INV_PROTO)) + exit_error(PARAMETER_PROBLEM, + "rule would never match protocol"); + fw.nfcache |= NFC_IP_PROTO; + break; + + case 's': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_SOURCE, &fw.ip.invflags, + invert); + shostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_SRC; + break; + + case 'd': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_DESTINATION, &fw.ip.invflags, + invert); + dhostnetworkmask = argv[optind-1]; + fw.nfcache |= NFC_IP_DST; + break; + + case 'j': + set_option(&options, OPT_JUMP, &fw.ip.invflags, + invert); + jumpto = parse_target(optarg); + /* TRY_LOAD (may be chain name) */ + target = find_target(jumpto, TRY_LOAD); + + if (target) { + size_t size; + + size = IPT_ALIGN(sizeof(struct ipt_entry_target)) + + target->size; + + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + opts = merge_options(opts, target->extra_opts, &target->option_offset); + } + break; + + + case 'i': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.iniface, + fw.ip.iniface_mask); + fw.nfcache |= NFC_IP_IF_IN; + break; + + case 'o': + check_inverse(optarg, &invert, &optind, argc); + set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags, + invert); + parse_interface(argv[optind-1], + fw.ip.outiface, + fw.ip.outiface_mask); + fw.nfcache |= NFC_IP_IF_OUT; + break; + + case 'f': + set_option(&options, OPT_FRAGMENT, &fw.ip.invflags, + invert); + fw.ip.flags |= IPT_F_FRAG; + fw.nfcache |= NFC_IP_FRAG; + break; + + case 'v': + if (!verbose) + set_option(&options, OPT_VERBOSE, + &fw.ip.invflags, invert); + verbose++; + break; + + case 'm': { + size_t size; + + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --match"); + + m = find_match(optarg, LOAD_MUST_SUCCEED); + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + opts = merge_options(opts, m->extra_opts, &m->option_offset); + } + break; + + case 'n': + set_option(&options, OPT_NUMERIC, &fw.ip.invflags, + invert); + break; + + case 't': + if (invert) + exit_error(PARAMETER_PROBLEM, + "unexpected ! flag before --table"); + *table = argv[optind-1]; + break; + + case 'x': + set_option(&options, OPT_EXPANDED, &fw.ip.invflags, + invert); + break; + + case 'V': + if (invert) + printf("Not %s ;-)\n", program_version); + else + printf("%s v%s\n", + program_name, program_version); + exit(0); + + case '0': + set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags, + invert); + break; + + case 'M': + modprobe = optarg; + break; + + case 'c': + + set_option(&options, OPT_COUNTERS, &fw.ip.invflags, + invert); + pcnt = optarg; + if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + bcnt = argv[optind++]; + else + exit_error(PARAMETER_PROBLEM, + "-%c requires packet and byte counter", + opt2char(OPT_COUNTERS)); + + if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c packet counter not numeric", + opt2char(OPT_COUNTERS)); + + if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1) + exit_error(PARAMETER_PROBLEM, + "-%c byte counter not numeric", + opt2char(OPT_COUNTERS)); + + break; + + + case 1: /* non option */ + if (optarg[0] == '!' && optarg[1] == '\0') { + if (invert) + exit_error(PARAMETER_PROBLEM, + "multiple consecutive ! not" + " allowed"); + invert = TRUE; + optarg[0] = '\0'; + continue; + } + printf("Bad argument `%s'\n", optarg); + exit_tryhelp(2); + + default: + /* FIXME: This scheme doesn't allow two of the same + matches --RR */ + if (!target + || !(target->parse(c - target->option_offset, + argv, invert, + &target->tflags, + &fw, &target->t))) { + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + if (m->parse(c - m->option_offset, + argv, invert, + &m->mflags, + &fw, + &fw.nfcache, + &m->m)) + break; + } + + /* If you listen carefully, you can + actually hear this code suck. */ + + /* some explanations (after four different bugs + * in 3 different releases): If we encountere a + * parameter, that has not been parsed yet, + * it's not an option of an explicitly loaded + * match or a target. However, we support + * implicit loading of the protocol match + * extension. '-p tcp' means 'l4 proto 6' and + * at the same time 'load tcp protocol match on + * demand if we specify --dport'. + * + * To make this work, we need to make sure: + * - the parameter has not been parsed by + * a match (m above) + * - a protocol has been specified + * - the protocol extension has not been + * loaded yet, or is loaded and unused + * [think of iptables-restore!] + * - the protocol extension can be successively + * loaded + */ + if (m == NULL + && protocol + && (!find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + || (find_proto(protocol, DONT_LOAD, + options&OPT_NUMERIC) + && (proto_used == 0)) + ) + && (m = find_proto(protocol, TRY_LOAD, + options&OPT_NUMERIC))) { + /* Try loading protocol */ + size_t size; + + proto_used = 1; + + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + + m->size; + + m->m = fw_calloc(1, size); + m->m->u.match_size = size; + strcpy(m->m->u.user.name, m->name); + m->init(m->m, &fw.nfcache); + + opts = merge_options(opts, + m->extra_opts, &m->option_offset); + + optind--; + continue; + } + if (!m) + exit_error(PARAMETER_PROBLEM, + "Unknown arg `%s'", + argv[optind-1]); + } + } + invert = FALSE; + } + + for (m = iptables_matches; m; m = m->next) { + if (!m->used) + continue; + + m->final_check(m->mflags); + } + + if (target) + target->final_check(target->tflags); + + /* Fix me: must put inverse options checking here --MN */ + + if (optind < argc) + exit_error(PARAMETER_PROBLEM, + "unknown arguments found on commandline"); + if (!command) + exit_error(PARAMETER_PROBLEM, "no command specified"); + if (invert) + exit_error(PARAMETER_PROBLEM, + "nothing appropriate following !"); + + if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) { + if (!(options & OPT_DESTINATION)) + dhostnetworkmask = "0.0.0.0/0"; + if (!(options & OPT_SOURCE)) + shostnetworkmask = "0.0.0.0/0"; + } + + if (shostnetworkmask) + parse_hostnetworkmask(shostnetworkmask, &saddrs, + &(fw.ip.smsk), &nsaddrs); + + if (dhostnetworkmask) + parse_hostnetworkmask(dhostnetworkmask, &daddrs, + &(fw.ip.dmsk), &ndaddrs); + + if ((nsaddrs > 1 || ndaddrs > 1) && + (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP))) + exit_error(PARAMETER_PROBLEM, "! not allowed with multiple" + " source or destination IP addresses"); + + if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1)) + exit_error(PARAMETER_PROBLEM, "Replacement rule does not " + "specify a unique address"); + + generic_opt_check(command, options); + + if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN) + exit_error(PARAMETER_PROBLEM, + "chain name `%s' too long (must be under %i chars)", + chain, IPT_FUNCTION_MAXNAMELEN); + + /* only allocate handle if we weren't called with a handle */ + if (!*handle) + *handle = iptc_init(*table); + + if (!*handle) { + /* try to insmod the module if iptc_init failed */ + iptables_insmod("ip_tables", modprobe); + *handle = iptc_init(*table); + } + + if (!*handle) + exit_error(VERSION_PROBLEM, + "can't initialize iptables table `%s': %s", + *table, iptc_strerror(errno)); + + if (command == CMD_APPEND + || command == CMD_DELETE + || command == CMD_INSERT + || command == CMD_REPLACE) { + if (strcmp(chain, "PREROUTING") == 0 + || strcmp(chain, "INPUT") == 0) { + /* -o not valid with incoming packets. */ + if (options & OPT_VIANAMEOUT) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEOUT), + chain); + } + + if (strcmp(chain, "POSTROUTING") == 0 + || strcmp(chain, "OUTPUT") == 0) { + /* -i not valid with outgoing packets */ + if (options & OPT_VIANAMEIN) + exit_error(PARAMETER_PROBLEM, + "Can't use -%c with %s\n", + opt2char(OPT_VIANAMEIN), + chain); + } + + if (target && iptc_is_chain(jumpto, *handle)) { + printf("Warning: using chain %s, not extension\n", + jumpto); + + target = NULL; + } + + /* If they didn't specify a target, or it's a chain + name, use standard. */ + if (!target + && (strlen(jumpto) == 0 + || iptc_is_chain(jumpto, *handle))) { + size_t size; + + target = find_target(IPT_STANDARD_TARGET, + LOAD_MUST_SUCCEED); + + size = sizeof(struct ipt_entry_target) + + target->size; + target->t = fw_calloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, jumpto); + target->init(target->t, &fw.nfcache); + } + + if (!target) { + /* it is no chain, and we can't load a plugin. + * We cannot know if the plugin is corrupt, non + * existant OR if the user just misspelled a + * chain. */ + find_target(jumpto, LOAD_MUST_SUCCEED); + } else { + e = generate_entry(&fw, iptables_matches, target->t); + } + } + + switch (command) { + case CMD_APPEND: + ret = append_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE: + ret = delete_entry(chain, e, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_DELETE_NUM: + ret = iptc_delete_num_entry(chain, rulenum - 1, handle); + break; + case CMD_REPLACE: + ret = replace_entry(chain, e, rulenum - 1, + saddrs, daddrs, options&OPT_VERBOSE, + handle); + break; + case CMD_INSERT: + ret = insert_entry(chain, e, rulenum - 1, + nsaddrs, saddrs, ndaddrs, daddrs, + options&OPT_VERBOSE, + handle); + break; + case CMD_LIST: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + break; + case CMD_FLUSH: + ret = flush_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_ZERO: + ret = zero_entries(chain, options&OPT_VERBOSE, handle); + break; + case CMD_LIST|CMD_ZERO: + ret = list_entries(chain, + options&OPT_VERBOSE, + options&OPT_NUMERIC, + options&OPT_EXPANDED, + options&OPT_LINENUMBERS, + handle); + if (ret) + ret = zero_entries(chain, + options&OPT_VERBOSE, handle); + break; + case CMD_NEW_CHAIN: + ret = iptc_create_chain(chain, handle); + break; + case CMD_DELETE_CHAIN: + ret = delete_chain(chain, options&OPT_VERBOSE, handle); + break; + case CMD_RENAME_CHAIN: + ret = iptc_rename_chain(chain, newname, handle); + break; + case CMD_SET_POLICY: + ret = iptc_set_policy(chain, policy, NULL, handle); + break; + default: + /* We should never reach this... */ + exit_tryhelp(2); + } + + if (verbose > 1) + dump_entries(*handle); + + return ret; +} diff --git a/iptables.init b/iptables.init deleted file mode 100755 index 5c99246..0000000 --- a/iptables.init +++ /dev/null @@ -1,320 +0,0 @@ -#!/bin/sh -# -# iptables Start iptables firewall -# -# chkconfig: 2345 08 92 -# description: Starts, stops and saves iptables firewall -# -# config: /etc/sysconfig/iptables -# config: /etc/sysconfig/iptables-config - -# Source function library. -. /etc/init.d/functions - -IPTABLES=iptables -IPTABLES_DATA=/etc/sysconfig/$IPTABLES -IPTABLES_CONFIG=/etc/sysconfig/${IPTABLES}-config -IPV=${IPTABLES%tables} # ip for ipv4 | ip6 for ipv6 -PROC_IPTABLES_NAMES=/proc/net/${IPV}_tables_names -VAR_SUBSYS_IPTABLES=/var/lock/subsys/$IPTABLES - -if [ ! -x /sbin/$IPTABLES ]; then - echo -n $"/sbin/$IPTABLES does not exist."; warning; echo - exit 0 -fi - -if lsmod 2>/dev/null | grep -q ipchains ; then - echo -n $"ipchains and $IPTABLES can not be used together."; warning; echo - exit 0 -fi - -# Old or new modutils -/sbin/modprobe --version 2>&1 | grep -q module-init-tools \ - && NEW_MODUTILS=1 \ - || NEW_MODUTILS=0 - -# Default firewall configuration: -IPTABLES_MODULES="" -IPTABLES_MODULES_UNLOAD="yes" -IPTABLES_SAVE_ON_STOP="no" -IPTABLES_SAVE_ON_RESTART="no" -IPTABLES_SAVE_COUNTER="no" -IPTABLES_STATUS_NUMERIC="no" - -# Load firewall configuration. -[ -f "$IPTABLES_CONFIG" ] && . "$IPTABLES_CONFIG" - -rmmod_r() { - # Unload module with all referring modules. - # At first all referring modules will be unloaded, then the module itself. - local mod=$1 - local ret=0 - local ref= - - # Get referring modules. - # New modutils have another output format. - [ $NEW_MODUTILS = 1 ] \ - && ref=`lsmod | awk "/^${mod}/ { print \\\$4; }" | tr ',' ' '` \ - || ref=`lsmod | grep ^${mod} | cut -d "[" -s -f 2 | cut -d "]" -s -f 1` - - # recursive call for all referring modules - for i in $ref; do - rmmod_r $i - let ret+=$?; - done - - # Unload module. - # The extra test is for 2.6: The module might have autocleaned, - # after all referring modules are unloaded. - if grep -q "^${mod}" /proc/modules ; then - modprobe -r $mod > /dev/null 2>&1 - let ret+=$?; - fi - - return $ret -} - -flush_n_delete() { - # Flush firewall rules and delete chains. - [ -e "$PROC_IPTABLES_NAMES" ] || return 1 - - # Check if firewall is configured (has tables) - tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null` - [ -z "$tables" ] && return 1 - - echo -n $"Flushing firewall rules: " - ret=0 - # For all tables - for i in $tables; do - # Flush firewall rules. - $IPTABLES -t $i -F; - let ret+=$?; - - # Delete firewall chains. - $IPTABLES -t $i -X; - let ret+=$?; - - # Set counter to zero. - $IPTABLES -t $i -Z; - let ret+=$?; - done - - [ $ret -eq 0 ] && success || failure - echo - return $ret -} - -set_policy() { - # Set policy for configured tables. - policy=$1 - - # Check if iptable module is loaded - [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1 - - # Check if firewall is configured (has tables) - tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null` - [ -z "$tables" ] && return 1 - - echo -n $"Setting chains to policy $policy: " - ret=0 - for i in $tables; do - echo -n "$i " - case "$i" in - filter) - $IPTABLES -t filter -P INPUT $policy \ - && $IPTABLES -t filter -P OUTPUT $policy \ - && $IPTABLES -t filter -P FORWARD $policy \ - || let ret+=1 - ;; - nat) - $IPTABLES -t nat -P PREROUTING $policy \ - && $IPTABLES -t nat -P POSTROUTING $policy \ - && $IPTABLES -t nat -P OUTPUT $policy \ - || let ret+=1 - ;; - mangle) - $IPTABLES -t mangle -P PREROUTING $policy \ - && $IPTABLES -t mangle -P POSTROUTING $policy \ - && $IPTABLES -t mangle -P INPUT $policy \ - && $IPTABLES -t mangle -P OUTPUT $policy \ - && $IPTABLES -t mangle -P FORWARD $policy \ - || let ret+=1 - ;; - *) - let ret+=1 - ;; - esac - done - - [ $ret -eq 0 ] && success || failure - echo - return $ret -} - -start() { - # Do not start if there is no config file. - [ -f "$IPTABLES_DATA" ] || return 1 - - echo -n $"Applying $IPTABLES firewall rules: " - - OPT= - [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c" - - $IPTABLES-restore $OPT $IPTABLES_DATA - if [ $? -eq 0 ]; then - success; echo - else - failure; echo; return 1 - fi - - # Load additional modules (helpers) - if [ -n "$IPTABLES_MODULES" ]; then - echo -n $"Loading additional $IPTABLES modules: " - ret=0 - for mod in $IPTABLES_MODULES; do - echo -n "$mod " - modprobe $mod > /dev/null 2>&1 - let ret+=$?; - done - [ $ret -eq 0 ] && success || failure - echo - fi - - touch $VAR_SUBSYS_IPTABLES - return $ret -} - -stop() { - # Do not stop if iptables module is not loaded. - [ -e "$PROC_IPTABLES_NAMES" ] || return 1 - - flush_n_delete - set_policy ACCEPT - - if [ "x$IPTABLES_MODULES_UNLOAD" = "xyes" ]; then - echo -n $"Unloading $IPTABLES modules: " - ret=0 - rmmod_r ${IPV}_tables - let ret+=$?; - rmmod_r ${IPV}_conntrack - let ret+=$?; - [ $ret -eq 0 ] && success || failure - echo - fi - - rm -f $VAR_SUBSYS_IPTABLES - return $ret -} - -save() { - # Check if iptable module is loaded - [ ! -e "$PROC_IPTABLES_NAMES" ] && return 1 - - # Check if firewall is configured (has tables) - tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null` - [ -z "$tables" ] && return 1 - - echo -n $"Saving firewall rules to $IPTABLES_DATA: " - - OPT= - [ "x$IPTABLES_SAVE_COUNTER" = "xyes" ] && OPT="-c" - - ret=0 - TMP_FILE=`/bin/mktemp -q /tmp/$IPTABLES.XXXXXX` \ - && chmod 600 "$TMP_FILE" \ - && $IPTABLES-save $OPT > $TMP_FILE 2>/dev/null \ - && size=`stat -c '%s' $TMP_FILE` && [ $size -gt 0 ] \ - || ret=1 - if [ $ret -eq 0 ]; then - if [ -e $IPTABLES_DATA ]; then - cp -f $IPTABLES_DATA $IPTABLES_DATA.save \ - && chmod 600 $IPTABLES_DATA.save \ - || ret=1 - fi - if [ $ret -eq 0 ]; then - cp -f $TMP_FILE $IPTABLES_DATA \ - && chmod 600 $IPTABLES_DATA \ - || ret=1 - fi - fi - [ $ret -eq 0 ] && success || failure - echo - rm -f $TMP_FILE - return $ret -} - -status() { - # Do not print status if lockfile is missing and iptables modules are not - # loaded. - # Check if iptable module is loaded - if [ ! -f "$VAR_SUBSYS_IPTABLES" ]; then - echo $"Firewall is stopped." - return 1 - fi - - # Check if firewall is configured (has tables) - if [ ! -e "$PROC_IPTABLES_NAMES" ]; then - echo $"Firewall is not configured. " - return 1 - fi - tables=`cat $PROC_IPTABLES_NAMES 2>/dev/null` - if [ -z "$tables" ]; then - echo $"Firewall is not configured. " - return 1 - fi - - NUM= - [ "x$IPTABLES_STATUS_NUMERIC" = "xyes" ] && NUM="-n" - - for table in $tables; do - echo $"Table: $table" - $IPTABLES -t $table --list $NUM && echo - done - - return 0 -} - -restart() { - [ "x$IPTABLES_SAVE_ON_RESTART" = "xyes" ] && save - stop - start -} - -case "$1" in - start) - stop - start - RETVAL=$? - ;; - stop) - [ "x$IPTABLES_SAVE_ON_STOP" = "xyes" ] && save - stop - RETVAL=$? - ;; - restart) - restart - RETVAL=$? - ;; - condrestart) - [ -e "$VAR_SUBSYS_IPTABLES" ] && restart - ;; - status) - status - RETVAL=$? - ;; - panic) - flush_n_delete - set_policy DROP - RETVAL=$? - ;; - save) - save - RETVAL=$? - ;; - *) - echo $"Usage: $0 {start|stop|restart|condrestart|status|panic|save}" - exit 1 - ;; -esac - -exit $RETVAL diff --git a/iptables.spec b/iptables.spec deleted file mode 100644 index a6ddcef..0000000 --- a/iptables.spec +++ /dev/null @@ -1,371 +0,0 @@ -%define name iptables -%define version 1.2.9 -%define release 2.3.1.1.planetlab%{?date:.%{date}} - -Vendor: PlanetLab -Packager: PlanetLab Central -Distribution: PlanetLab 3.0 -URL: http://cvs.planet-lab.org/cvs/iptables - -%define build_devel 1 -%define linux_header 0 - -Name: %{name} -Summary: Tools for managing Linux kernel packet filtering capabilities. -Version: %{version} -Release: %{release} -Source: http://www.netfilter.org/%{name}-%{version}.tar.bz2 -%define SOURCE1 iptables.init -%define SOURCE2 iptables-config -%if %{linux_header} -Source3: netfilter-2.4.20.tar.gz -%endif -Patch2: iptables-1.2.8-nolibnsl.patch -Patch3: iptables-1.2.8-print_type.patch -Patch4: iptables-1.2.9-netlink.patch -Patch5: iptables-1.2.9-selinux.patch -Patch6: iptables-1.2.9-counters.patch -Group: System Environment/Base -#URL: http://www.netfilter.org/ -BuildRoot: %{_tmppath}/%{name}-buildroot -License: GPL -BuildPrereq: /usr/bin/perl -Requires: kernel >= 2.4.20 -Requires(post,postun): chkconfig -Prefix: %{_prefix} - -%package ipv6 -Summary: IPv6 support for iptables. -Group: System Environment/Base -Requires: %{name} = %{version} - -%if %{build_devel} -%package devel -Summary: Development package for iptables. -Group: System Environment/Base -Requires: %{name} = %{version} -%endif - -%description -The iptables utility controls the network packet filtering code in the -Linux kernel. If you need to set up firewalls and/or IP masquerading, -you should install this package. - -%description ipv6 -The iptables package contains IPv6 (the next version of the IP -protocol) support for iptables. Iptables controls the Linux kernel -network packet filtering code, allowing you to set up firewalls and IP -masquerading. - -Install iptables-ipv6 if you need to set up firewalling for your -network and you are using ipv6. - -%if %{build_devel} -%description devel -The iptables utility controls the network packet filtering code in the -Linux kernel. If you need to set up firewalls and/or IP masquerading, -you should install this package. -%endif - -%prep -rm -rf %{buildroot} - -%setup -q - -# Put it to a reasonable place -find . -type f -exec perl -pi -e "s,/usr,%{prefix},g" {} \; - -%build -TOPDIR=`pwd` -OPT="$RPM_OPT_FLAGS -I$TOPDIR/include" -# bootstrap to avoid BuildRequires of kernel-source -for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do - if [ -f $KERNEL_DIR/include/linux/version.h ] ; then - break - fi -done -make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} -make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} iptables-save iptables-restore -make COPT_FLAGS="$OPT" KERNEL_DIR=/usr LIBDIR=/%{_lib} ip6tables-save ip6tables-restore - -%install -# bootstrap to avoid BuildRequires of kernel-source -for KERNEL_DIR in $RPM_BUILD_DIR/linux-* /lib/modules/`uname -r`/build /usr ; do - if [ -f $KERNEL_DIR/include/linux/version.h ] ; then - break - fi -done -make install DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=/%{_lib} MANDIR=%{_mandir} -%if %{build_devel} -make install-devel DESTDIR=%{buildroot} KERNEL_DIR=/usr BINDIR=/sbin LIBDIR=%{_libdir} MANDIR=%{_mandir} -%endif -cp ip{6,}tables-{save,restore} $RPM_BUILD_ROOT/sbin -cp iptables-*.8 $RPM_BUILD_ROOT%{_mandir}/man8 -mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d -install -c -m755 %{SOURCE1} $RPM_BUILD_ROOT/etc/rc.d/init.d/iptables -sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE1} > ip6tables.init -install -c -m755 ip6tables.init $RPM_BUILD_ROOT/etc/rc.d/init.d/ip6tables -mkdir -p $RPM_BUILD_ROOT/etc/sysconfig -install -c -m755 %{SOURCE2} $RPM_BUILD_ROOT/etc/sysconfig/iptables-config -sed -e 's;iptables;ip6tables;g' -e 's;IPTABLES;IP6TABLES;g' < %{SOURCE2} > ip6tables-config -install -c -m755 ip6tables-config $RPM_BUILD_ROOT/etc/sysconfig/ip6tables-config - -%clean -rm -rf $RPM_BUILD_ROOT - -%post -/sbin/chkconfig --add iptables - -%preun -if [ "$1" = 0 ]; then - /sbin/chkconfig --del iptables -fi - -%post ipv6 -/sbin/chkconfig --add ip6tables - -%preun ipv6 -if [ "$1" = 0 ]; then - /sbin/chkconfig --del ip6tables -fi - -%files -%defattr(-,root,root,0755) -%doc COPYING INSTALL INCOMPATIBILITIES -%config %attr(0755,root,root) /etc/rc.d/init.d/iptables -%config(noreplace) %attr(0600,root,root) /etc/sysconfig/iptables-config -/sbin/iptables* -%{_mandir}/man8/iptables* -%dir /%{_lib}/iptables -/%{_lib}/iptables/libipt* - -%files ipv6 -%defattr(-,root,root,0755) -%config %attr(0755,root,root) /etc/rc.d/init.d/ip6tables -%config(noreplace) %attr(0600,root,root) /etc/sysconfig/ip6tables-config -/sbin/ip6tables* -%{_mandir}/man8/ip6tables* -/%{_lib}/iptables/libip6t* - -%if %{build_devel} -%files devel -%defattr(-,root,root,0755) -%{_includedir}/libipq.h -%{_libdir}/libipq.a -%{_libdir}/libiptc.a -%{_mandir}/man3/* -%endif - -%changelog -* Tue Mar 02 2004 Elliot Lee -- rebuilt - -* Thu Feb 26 2004 Thomas Woerner 1.2.9-2.3 -- fixed iptables-restore -c fault if there are no counters (#116421) - -* Fri Feb 13 2004 Elliot Lee -- rebuilt - -* Sun Jan 25 2004 Dan Walsh 1.2.9-1.2 -- Close File descriptors to prevent SELinux error message - -* Wed Jan 7 2004 Thomas Woerner 1.2.9-1.1 -- rebuild - -* Wed Dec 17 2003 Thomas Woerner 1.2.9-1 -- vew version 1.2.9 -- new config options in ipXtables-config: - IPTABLES_MODULES_UNLOAD -- more documentation in ipXtables-config -- fix for netlink security issue in libipq (devel package) -- print fix for libipt_icmp (#109546) - -* Thu Oct 23 2003 Thomas Woerner 1.2.8-13 -- marked all messages in iptables init script for translation (#107462) -- enabled devel package (#105884, #106101) -- bumped build for fedora for libipt_recent.so (#106002) - -* Tue Sep 23 2003 Thomas Woerner 1.2.8-12.1 -- fixed lost udp port range in ip6tables-save (#104484) -- fixed non numeric multiport port output in ipXtables-savs - -* Mon Sep 22 2003 Florian La Roche 1.2.8-11 -- do not link against -lnsl - -* Wed Sep 17 2003 Thomas Woerner 1.2.8-10 -- made variables in rmmod_r local - -* Tue Jul 22 2003 Thomas Woerner 1.2.8-9 -- fixed permission for init script - -* Sat Jul 19 2003 Thomas Woerner 1.2.8-8 -- fixed save when iptables file is missing and iptables-config permissions - -* Tue Jul 8 2003 Thomas Woerner 1.2.8-7 -- fixes for ip6tables: module unloading, setting policy only for existing - tables - -* Thu Jul 3 2003 Thomas Woerner 1.2.8-6 -- IPTABLES_SAVE_COUNTER defaults to no, now -- install config file in /etc/sysconfig -- exchange unload of ip_tables and ip_conntrack -- fixed start function - -* Wed Jul 2 2003 Thomas Woerner 1.2.8-5 -- new config option IPTABLES_SAVE_ON_RESTART -- init script: new status, save and restart -- fixes #44905, #65389, #80785, #82860, #91040, #91560 and #91374 - -* Mon Jun 30 2003 Thomas Woerner 1.2.8-4 -- new config option IPTABLES_STATUS_NUMERIC -- cleared IPTABLES_MODULES in iptables-config - -* Mon Jun 30 2003 Thomas Woerner 1.2.8-3 -- new init scripts - -* Sat Jun 28 2003 Florian La Roche -- remove check for very old kernel versions in init scripts -- sync up both init scripts and remove some further ugly things -- add some docu into rpm - -* Thu Jun 26 2003 Thomas Woerner 1.2.8-2 -- rebuild - -* Mon Jun 16 2003 Thomas Woerner 1.2.8-1 -- update to 1.2.8 - -* Wed Jan 22 2003 Tim Powers -- rebuilt - -* Mon Jan 13 2003 Bill Nottingham 1.2.7a-1 -- update to 1.2.7a -- add a plethora of bugfixes courtesy Michael Schwendt - -* Fri Dec 13 2002 Elliot Lee 1.2.6a-3 -- Fix multilib - -* Wed Aug 07 2002 Karsten Hopp -- fixed iptables and ip6tables initscript output, based on #70511 -- check return status of all iptables calls, not just the last one - in a 'for' loop. - -* Mon Jul 29 2002 Bernhard Rosenkraenzer 1.2.6a-1 -- 1.2.6a (bugfix release, #69747) - -* Fri Jun 21 2002 Tim Powers -- automated rebuild - -* Thu May 23 2002 Tim Powers -- automated rebuild - -* Mon Mar 4 2002 Bernhard Rosenkraenzer 1.2.5-3 -- Add some fixes from CVS, fixing bug #60465 - -* Tue Feb 12 2002 Bernhard Rosenkraenzer 1.2.5-2 -- Merge ip6tables improvements from Ian Prowell - #59402 -- Update URL (#59354) -- Use /sbin/chkconfig rather than chkconfig in %postun script - -* Fri Jan 11 2002 Bernhard Rosenkraenzer 1.2.5-1 -- 1.2.5 - -* Wed Jan 09 2002 Tim Powers -- automated rebuild - -* Mon Nov 5 2001 Bernhard Rosenkraenzer 1.2.4-2 -- Fix %preun script - -* Tue Oct 30 2001 Bernhard Rosenkraenzer 1.2.4-1 -- Update to 1.2.4 (various fixes, including security fixes; among others: - #42990, #50500, #53325, #54280) -- Fix init script (#31133) - -* Mon Sep 3 2001 Bernhard Rosenkraenzer 1.2.3-1 -- 1.2.3 (5 security fixes, some other fixes) -- Fix updating (#53032) - -* Mon Aug 27 2001 Bernhard Rosenkraenzer 1.2.2-4 -- Fix #50990 -- Add some fixes from current CVS; should fix #52620 - -* Mon Jul 16 2001 Bernhard Rosenkraenzer 1.2.2-3 -- Add some fixes from the current CVS tree; fixes #49154 and some IPv6 - issues - -* Tue Jun 26 2001 Bernhard Rosenkraenzer 1.2.2-2 -- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt - - -* Tue May 8 2001 Bernhard Rosenkraenzer 1.2.2-1 -- 1.2.2 - -* Wed Mar 21 2001 Bernhard Rosenkraenzer -- 1.2.1a, fixes #28412, #31136, #31460, #31133 - -* Thu Mar 1 2001 Bernhard Rosenkraenzer -- Yet another initscript fix (#30173) -- Fix the fixes; they fixed some issues but broke more important - stuff :/ (#30176) - -* Tue Feb 27 2001 Bernhard Rosenkraenzer -- Fix up initscript (#27962) -- Add fixes from CVS to iptables-{restore,save}, fixing #28412 - -* Fri Feb 09 2001 Karsten Hopp -- create /etc/sysconfig/iptables mode 600 (same problem as #24245) - -* Mon Feb 05 2001 Karsten Hopp -- fix bugzilla #25986 (initscript not marked as config file) -- fix bugzilla #25962 (iptables-restore) -- mv chkconfig --del from postun to preun - -* Thu Feb 1 2001 Trond Eivind Glomsrød -- Fix check for ipchains - -* Mon Jan 29 2001 Bernhard Rosenkraenzer -- Some fixes to init scripts - -* Wed Jan 24 2001 Bernhard Rosenkraenzer -- Add some fixes from CVS, fixes among other things Bug #24732 - -* Wed Jan 17 2001 Bernhard Rosenkraenzer -- Add missing man pages, fix up init script (Bug #17676) - -* Mon Jan 15 2001 Bill Nottingham -- add init script - -* Mon Jan 15 2001 Bernhard Rosenkraenzer -- 1.2 -- fix up ipv6 split -- add init script -- Move the plugins from /usr/lib/iptables to /lib/iptables. - This needs to work before /usr is mounted... -- Use -O1 on alpha (compiler bug) - -* Sat Jan 6 2001 Bernhard Rosenkraenzer -- 1.1.2 -- Add IPv6 support (in separate package) - -* Thu Aug 17 2000 Bill Nottingham -- build everywhere - -* Tue Jul 25 2000 Bernhard Rosenkraenzer -- 1.1.1 - -* Thu Jul 13 2000 Prospector -- automatic rebuild - -* Tue Jun 27 2000 Preston Brown -- move iptables to /sbin. -- excludearch alpha for now, not building there because of compiler bug(?) - -* Fri Jun 9 2000 Bill Nottingham -- don't obsolete ipchains either -- update to 1.1.0 - -* Mon Jun 4 2000 Bill Nottingham -- remove explicit kernel requirement - -* Tue May 2 2000 Bernhard Rosenkränzer -- initial package diff --git a/libipq/libipq.c.netlink b/libipq/libipq.c.netlink new file mode 100644 index 0000000..709c8a2 --- /dev/null +++ b/libipq/libipq.c.netlink @@ -0,0 +1,373 @@ +/* + * libipq.c + * + * IPQ userspace library. + * + * Please note that this library is still developmental, and there may + * be some API changes. + * + * Author: James Morris + * + * 07-11-2001 Modified by Fernando Anton to add support for IPv6. + * + * Copyright (c) 2000-2001 Netfilter Core Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * + * Private interface + * + ****************************************************************************/ + +enum { + IPQ_ERR_NONE = 0, + IPQ_ERR_IMPL, + IPQ_ERR_HANDLE, + IPQ_ERR_SOCKET, + IPQ_ERR_BIND, + IPQ_ERR_BUFFER, + IPQ_ERR_RECV, + IPQ_ERR_NLEOF, + IPQ_ERR_ADDRLEN, + IPQ_ERR_STRUNC, + IPQ_ERR_RTRUNC, + IPQ_ERR_NLRECV, + IPQ_ERR_SEND, + IPQ_ERR_SUPP, + IPQ_ERR_RECVBUF, + IPQ_ERR_TIMEOUT, + IPQ_ERR_PROTOCOL +}; +#define IPQ_MAXERR IPQ_ERR_PROTOCOL + +struct ipq_errmap_t { + int errcode; + char *message; +} ipq_errmap[] = { + { IPQ_ERR_NONE, "Unknown error" }, + { IPQ_ERR_IMPL, "Implementation error" }, + { IPQ_ERR_HANDLE, "Unable to create netlink handle" }, + { IPQ_ERR_SOCKET, "Unable to create netlink socket" }, + { IPQ_ERR_BIND, "Unable to bind netlink socket" }, + { IPQ_ERR_BUFFER, "Unable to allocate buffer" }, + { IPQ_ERR_RECV, "Failed to receive netlink message" }, + { IPQ_ERR_NLEOF, "Received EOF on netlink socket" }, + { IPQ_ERR_ADDRLEN, "Invalid peer address length" }, + { IPQ_ERR_STRUNC, "Sent message truncated" }, + { IPQ_ERR_RTRUNC, "Received message truncated" }, + { IPQ_ERR_NLRECV, "Received error from netlink" }, + { IPQ_ERR_SEND, "Failed to send netlink message" }, + { IPQ_ERR_SUPP, "Operation not supported" }, + { IPQ_ERR_RECVBUF, "Receive buffer size invalid" }, + { IPQ_ERR_TIMEOUT, "Timeout"}, + { IPQ_ERR_PROTOCOL, "Invalid protocol specified" } +}; + +static int ipq_errno = IPQ_ERR_NONE; + +static ssize_t ipq_netlink_sendto(const struct ipq_handle *h, + const void *msg, size_t len); + +static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h, + unsigned char *buf, size_t len, + int timeout); + +static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h, + const struct msghdr *msg, + unsigned int flags); + +static char *ipq_strerror(int errcode); + +static ssize_t ipq_netlink_sendto(const struct ipq_handle *h, + const void *msg, size_t len) +{ + int status = sendto(h->fd, msg, len, 0, + (struct sockaddr *)&h->peer, sizeof(h->peer)); + if (status < 0) + ipq_errno = IPQ_ERR_SEND; + return status; +} + +static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h, + const struct msghdr *msg, + unsigned int flags) +{ + int status = sendmsg(h->fd, msg, flags); + if (status < 0) + ipq_errno = IPQ_ERR_SEND; + return status; +} + +static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h, + unsigned char *buf, size_t len, + int timeout) +{ + int addrlen, status; + struct nlmsghdr *nlh; + + if (len < sizeof(struct nlmsgerr)) { + ipq_errno = IPQ_ERR_RECVBUF; + return -1; + } + addrlen = sizeof(h->peer); + + if (timeout != 0) { + int ret; + struct timeval tv; + fd_set read_fds; + + if (timeout < 0) { + /* non-block non-timeout */ + tv.tv_sec = 0; + tv.tv_usec = 0; + } else { + tv.tv_sec = timeout / 1000000; + tv.tv_usec = timeout % 1000000; + } + + FD_ZERO(&read_fds); + FD_SET(h->fd, &read_fds); + ret = select(h->fd+1, &read_fds, NULL, NULL, &tv); + if (ret < 0) { + if (errno == EINTR) { + return 0; + } else { + ipq_errno = IPQ_ERR_RECV; + return -1; + } + } + if (!FD_ISSET(h->fd, &read_fds)) { + ipq_errno = IPQ_ERR_TIMEOUT; + return 0; + } + } + status = recvfrom(h->fd, buf, len, 0, + (struct sockaddr *)&h->peer, &addrlen); + if (status < 0) { + ipq_errno = IPQ_ERR_RECV; + return status; + } + if (addrlen != sizeof(h->peer)) { + ipq_errno = IPQ_ERR_RECV; + return -1; + } + if (status == 0) { + ipq_errno = IPQ_ERR_NLEOF; + return -1; + } + nlh = (struct nlmsghdr *)buf; + if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) { + ipq_errno = IPQ_ERR_RTRUNC; + return -1; + } + return status; +} + +static char *ipq_strerror(int errcode) +{ + if (errcode < 0 || errcode > IPQ_MAXERR) + errcode = IPQ_ERR_IMPL; + return ipq_errmap[errcode].message; +} + +/**************************************************************************** + * + * Public interface + * + ****************************************************************************/ + +/* + * Create and initialise an ipq handle. + */ +struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol) +{ + int status; + struct ipq_handle *h; + + h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle)); + if (h == NULL) { + ipq_errno = IPQ_ERR_HANDLE; + return NULL; + } + + memset(h, 0, sizeof(struct ipq_handle)); + + if (protocol == PF_INET) + h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL); + else if (protocol == PF_INET6) + h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW); + else { + ipq_errno = IPQ_ERR_PROTOCOL; + free(h); + return NULL; + } + + if (h->fd == -1) { + ipq_errno = IPQ_ERR_SOCKET; + close(h->fd); + free(h); + return NULL; + } + memset(&h->local, 0, sizeof(struct sockaddr_nl)); + h->local.nl_family = AF_NETLINK; + h->local.nl_pid = getpid(); + h->local.nl_groups = 0; + status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local)); + if (status == -1) { + ipq_errno = IPQ_ERR_BIND; + close(h->fd); + free(h); + return NULL; + } + memset(&h->peer, 0, sizeof(struct sockaddr_nl)); + h->peer.nl_family = AF_NETLINK; + h->peer.nl_pid = 0; + h->peer.nl_groups = 0; + return h; +} + +/* + * No error condition is checked here at this stage, but it may happen + * if/when reliable messaging is implemented. + */ +int ipq_destroy_handle(struct ipq_handle *h) +{ + if (h) { + close(h->fd); + free(h); + } + return 0; +} + +int ipq_set_mode(const struct ipq_handle *h, + u_int8_t mode, size_t range) +{ + struct { + struct nlmsghdr nlh; + ipq_peer_msg_t pm; + } req; + + memset(&req, 0, sizeof(req)); + req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req)); + req.nlh.nlmsg_flags = NLM_F_REQUEST; + req.nlh.nlmsg_type = IPQM_MODE; + req.nlh.nlmsg_pid = h->local.nl_pid; + req.pm.msg.mode.value = mode; + req.pm.msg.mode.range = range; + return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len); +} + +/* + * timeout is in microseconds (1 second is 1000000 (1 million) microseconds) + * + */ +ssize_t ipq_read(const struct ipq_handle *h, + unsigned char *buf, size_t len, int timeout) +{ + return ipq_netlink_recvfrom(h, buf, len, timeout); +} + +int ipq_message_type(const unsigned char *buf) +{ + return ((struct nlmsghdr*)buf)->nlmsg_type; +} + +int ipq_get_msgerr(const unsigned char *buf) +{ + struct nlmsghdr *h = (struct nlmsghdr *)buf; + struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); + return -err->error; +} + +ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf) +{ + return NLMSG_DATA((struct nlmsghdr *)(buf)); +} + +int ipq_set_verdict(const struct ipq_handle *h, + ipq_id_t id, + unsigned int verdict, + size_t data_len, + unsigned char *buf) +{ + unsigned char nvecs; + size_t tlen; + struct nlmsghdr nlh; + ipq_peer_msg_t pm; + struct iovec iov[3]; + struct msghdr msg; + + memset(&nlh, 0, sizeof(nlh)); + nlh.nlmsg_flags = NLM_F_REQUEST; + nlh.nlmsg_type = IPQM_VERDICT; + nlh.nlmsg_pid = h->local.nl_pid; + memset(&pm, 0, sizeof(pm)); + pm.msg.verdict.value = verdict; + pm.msg.verdict.id = id; + pm.msg.verdict.data_len = data_len; + iov[0].iov_base = &nlh; + iov[0].iov_len = sizeof(nlh); + iov[1].iov_base = ± + iov[1].iov_len = sizeof(pm); + tlen = sizeof(nlh) + sizeof(pm); + nvecs = 2; + if (data_len && buf) { + iov[2].iov_base = buf; + iov[2].iov_len = data_len; + tlen += data_len; + nvecs++; + } + msg.msg_name = (void *)&h->peer; + msg.msg_namelen = sizeof(h->peer); + msg.msg_iov = iov; + msg.msg_iovlen = nvecs; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + nlh.nlmsg_len = tlen; + return ipq_netlink_sendmsg(h, &msg, 0); +} + +/* Not implemented yet */ +int ipq_ctl(const struct ipq_handle *h, int request, ...) +{ + return 1; +} + +char *ipq_errstr(void) +{ + return ipq_strerror(ipq_errno); +} + +void ipq_perror(const char *s) +{ + if (s) + fputs(s, stderr); + else + fputs("ERROR", stderr); + if (ipq_errno) + fprintf(stderr, ": %s", ipq_errstr()); + if (errno) + fprintf(stderr, ": %s", strerror(errno)); + fputc('\n', stderr); +} diff --git a/libiptc/.cvsignore b/libiptc/.cvsignore deleted file mode 100644 index a438335..0000000 --- a/libiptc/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -*.d diff --git a/libiptc2/.#libiptc.c.1.40 b/libiptc2/.#libiptc.c.1.40 new file mode 100644 index 0000000..ce4af9b --- /dev/null +++ b/libiptc2/.#libiptc.c.1.40 @@ -0,0 +1,1972 @@ +/* Library which manipulates firewall rules. Version $Revision: 1.40 $ */ + +/* Architecture of firewall rules is as follows: + * + * Chains go INPUT, FORWARD, OUTPUT then user chains. + * Each user chain starts with an ERROR node. + * Every chain ends with an unconditional jump: a RETURN for user chains, + * and a POLICY for built-ins. + */ + +/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See + * COPYING for details). + * (C) 2000-2003 by the Netfilter Core Team + * + * 2003-Jun-20: Harald Welte : + * - Reimplementation of chain cache to use offsets instead of entries + * 2003-Jun-23: Harald Welte : + * - speed optimization, sponsored by Astaro AG (http://www.astaro.com/) + * don't rebuild the chain cache after every operation, instead fix it + * up after a ruleset change. + * 2003-Jun-30: Harald Welte : + * - total reimplementation + */ +#include "linux_listhelp.h" + +#ifndef IPT_LIB_DIR +#define IPT_LIB_DIR "/usr/lib/iptables" +#endif + +static int sockfd = -1; +static void *iptc_fn = NULL; + +static const char *hooknames[] += { [HOOK_PRE_ROUTING] "PREROUTING", + [HOOK_LOCAL_IN] "INPUT", + [HOOK_FORWARD] "FORWARD", + [HOOK_LOCAL_OUT] "OUTPUT", + [HOOK_POST_ROUTING] "POSTROUTING", +#ifdef HOOK_DROPPING + [HOOK_DROPPING] "DROPPING" +#endif +}; + +struct counter_map +{ + enum { + COUNTER_MAP_NOMAP, + COUNTER_MAP_NORMAL_MAP, + COUNTER_MAP_ZEROED, + COUNTER_MAP_SET + } maptype; + unsigned int mappos; +}; + +/* Convenience structures */ +struct ipt_error_target +{ + STRUCT_ENTRY_TARGET t; + char error[TABLE_MAXNAMELEN]; +}; + +struct rule_head +{ + struct list_head list; /* list of rules in chain */ + + struct chain_head *chain; /* we're part of this chain */ + struct rule_head *target; /* target of this rule, in case + it is a jump rule */ + + + unsigned int size; /* size of rule */ + STRUCT_ENTRY *entry_blob; /* pointer to entry in blob */ + STRUCT_ENTRY entry[0]; +}; + +struct chain_head +{ + struct list_head list; + + char name[TABLE_MAXNAMELEN]; + unsigned int hooknum; + struct list_head rules; +}; + +STRUCT_TC_HANDLE +{ + /* Have changes been made? */ + int changed; + + /* linked list of chains in this table */ + struct list_head chains; + + /* current position of first_chain() / next_chain() */ + struct chain_head *chain_iterator_cur; + + /* current position of first_rule() / next_rule() */ + struct rule_head *rule_iterator_cur; + + struct counter_map *counter_map; + + /* the structure we receive from getsockopt() */ + STRUCT_GETINFO info; + + /* Array of hook names */ + const char **hooknames; +#if 0 + /* Size in here reflects original state. */ + + + /* Cached position of chain heads (NULL = no cache). */ + unsigned int cache_num_chains; + unsigned int cache_num_builtins; + struct chain_cache *cache_chain_heads; + + /* Chain iterator: current chain cache entry. */ + struct chain_cache *cache_chain_iteration; + + /* Rule iterator: terminal rule */ + STRUCT_ENTRY *cache_rule_end; + + /* Number in here reflects current state. */ + unsigned int new_number; +#endif + STRUCT_GET_ENTRIES entries; +}; + +static void +set_changed(TC_HANDLE_T h) +{ + h->changed = 1; +} + +#ifdef IPTC_DEBUG +static void do_check(TC_HANDLE_T h, unsigned int line); +#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0) +#else +#define CHECK(h) +#endif + +static struct rule_head *ruleh_alloc(unsigned int size) +{ + struct rule_head *ruleh = malloc(sizeof(*ruleh)+size); + if (!ruleh) + return NULL; + + memset(ruleh, 0, sizeof(*ruleh)+size); + ruleh->size = size; + + return ruleh; +} + +static void ruleh_free(struct rule_head *ruleh) +{ + list_del(&ruleh->list); + free(ruleh); +} + +static struct rule_head *ruleh_get_n(struct chain_head *chain, int num) +{ + return NULL; +} + +static struct chain_head *chainh_alloc(TC_HANDLE_T h, const char *name) +{ + struct chain_head *chainh = malloc(sizeof(*chainh)); + if (!chainh) + return NULL; + + memset(chainh, 0, sizeof(*chainh)); + strncpy(chainh->name, name, sizeof(&chainh->name)); + list_append(&chainh->list, &h->chains); + + return chainh; +} + +static void +chainh_free(struct chain_head *chainh) +{ + /* FIXME */ + struct list_head *cur_item, *item2; + + list_for_each_safe(cur_item, item2, &chainh->rules) { + struct rule_head *ruleh = list_entry(cur_item, + struct rule_head, + list); + ruleh_free(ruleh); + } + + list_del(&chainh->list); +} + +#if 0 +static inline int +get_number(const STRUCT_ENTRY *i, + const STRUCT_ENTRY *seek, + unsigned int *pos) +{ + if (i == seek) + return 1; + (*pos)++; + return 0; +} +#endif + +static struct chain_head * +chainh_find(TC_HANDLE_T h, const IPT_CHAINLABEL name) +{ + struct list_head *cur; + + list_for_each(cur, &h->chains) { + struct chain_head *ch = list_entry(cur, struct chain_head, + list); + if (!strcmp(name, ch->name)) + return ch; + } + return NULL; +} + +static inline unsigned long +entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e) +{ + return (void *)e - (void *)h->entries.entrytable; +} + +#if 0 +static unsigned int +entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek) +{ + unsigned int pos = 0; + + if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, + get_number, seek, &pos) == 0) { + fprintf(stderr, "ERROR: offset %i not an entry!\n", + (char *)seek - (char *)h->entries.entrytable); + abort(); + } + return pos; +} + +static inline int +get_entry_n(STRUCT_ENTRY *i, + unsigned int number, + unsigned int *pos, + STRUCT_ENTRY **pe) +{ + if (*pos == number) { + *pe = i; + return 1; + } + (*pos)++; + return 0; +} + +static STRUCT_ENTRY * +index2entry(TC_HANDLE_T h, unsigned int index) +{ + unsigned int pos = 0; + STRUCT_ENTRY *ret = NULL; + + ENTRY_ITERATE(h->entries.entrytable, h->entries.size, + get_entry_n, index, &pos, &ret); + + return ret; +} + +static inline STRUCT_ENTRY * +get_entry(TC_HANDLE_T h, unsigned int offset) +{ + return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset); +} + + +static inline unsigned long +index2offset(TC_HANDLE_T h, unsigned int index) +{ + return entry2offset(h, index2entry(h, index)); +} + +static inline STRUCT_ENTRY * +offset2entry(TC_HANDLE_T h, unsigned int offset) +{ + return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset); +} + +static inline unsigned int +offset2index(const TC_HANDLE_T h, unsigned int offset) +{ + return entry2index(h, offset2entry(h, offset)); +} + + +static const char * +get_errorlabel(TC_HANDLE_T h, unsigned int offset) +{ + STRUCT_ENTRY *e; + + e = get_entry(h, offset); + if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) { + fprintf(stderr, "ERROR: offset %u not an error node!\n", + offset); + abort(); + } + + return (const char *)GET_TARGET(e)->data; +} +#endif + +/* Allocate handle of given size */ +static TC_HANDLE_T +alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules) +{ + size_t len; + TC_HANDLE_T h; + + len = sizeof(STRUCT_TC_HANDLE) + + size + + num_rules * sizeof(struct counter_map); + + if ((h = malloc(len)) == NULL) { + errno = ENOMEM; + return NULL; + } + + h->changed = 0; + + h->counter_map = (void *)h + + sizeof(STRUCT_TC_HANDLE) + + size; + strcpy(h->info.name, tablename); + strcpy(h->entries.name, tablename); + INIT_LIST_HEAD(&h->chains); + + return h; +} + +struct rule_head * +append_entrycopy(STRUCT_ENTRY *e, struct chain_head *chain) +{ + struct rule_head *ruleh = ruleh_alloc(e->next_offset); + if (!ruleh) + return NULL; + + memcpy(&ruleh->entry, e, e->next_offset); + ruleh->chain = chain; + ruleh->entry_blob = e; + list_append(&ruleh->list, &chain->rules); + + return ruleh; +} + + +/* have to return 0 on success, bcf ENTRY_ITERATE */ +static inline int +parse_entry(STRUCT_ENTRY *e, TC_HANDLE_T h, struct chain_head **curchain) +{ + int i; + union tgt_u { + STRUCT_ENTRY_TARGET ent; + STRUCT_STANDARD_TARGET std; + struct ipt_error_target err; + } *tgt; + + tgt = (union tgt_u *) GET_TARGET(e); + + if (e->target_offset == sizeof(STRUCT_ENTRY) + && (strcmp(tgt->ent.u.user.name, IPT_STANDARD_TARGET) == 0)) { + /* jump to somewhere else */ + append_entrycopy(e, *curchain); + /* FIXME: */ + } else if (e->target_offset == sizeof(STRUCT_ENTRY) + && e->next_offset == sizeof(STRUCT_ENTRY) + + ALIGN(sizeof(struct ipt_error_target)) + && !strcmp(tgt->ent.u.user.name, ERROR_TARGET)) { + /* chain head */ + *curchain = chainh_find(h, tgt->err.error); + if (*curchain) { + /* FIXME: error handling */ + return 1; + } + + *curchain = chainh_alloc(h, tgt->err.error); + /* FIXME: error handling */ + append_entrycopy(e, *curchain); + } else if (e->target_offset == sizeof(STRUCT_ENTRY) + && e->next_offset == sizeof(STRUCT_ENTRY) + + ALIGN(sizeof(STRUCT_STANDARD_TARGET)) + && tgt->std.verdict == RETURN) { + /* chain end */ + append_entrycopy(e, *curchain); + *curchain = NULL; + } else { + /* normal rule */ + append_entrycopy(e, *curchain); + } + + /* iterate over hook_entries, needed to connect builtin + * chains with hook numbers */ + for (i = 0; i < NUMHOOKS; i++) { + if (!(h->info.valid_hooks & (1 << i))) + continue; + if (h->info.hook_entry[i] == entry2offset(h, e)) { + /* found hook entry point */ + if (*curchain) + (*curchain)->hooknum = i; + } + if (h->info.underflow[i] == entry2offset(h, e)) { + /* found underflow point */ + } + } + + return 0; +} + +static int parse_ruleset(TC_HANDLE_T h) +{ + struct chain_head *curchain; + + /* iterate over ruleset; create linked list of rule_head/chain_head */ + if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size, + parse_entry, h, &curchain)) { + /* some error happened while iterating */ + return 0; + } + + return 1; +} + +TC_HANDLE_T +TC_INIT(const char *tablename) +{ + TC_HANDLE_T h; + STRUCT_GETINFO info; + unsigned int i; + int tmp; + socklen_t s; + + iptc_fn = TC_INIT; + + if (sockfd != -1) { + close(sockfd); + sockfd = -1; + } + + if (strlen(tablename) >= TABLE_MAXNAMELEN) { + errno = EINVAL; + return NULL; + } + + sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW); + if (sockfd < 0) + return NULL; + + s = sizeof(info); + + strcpy(info.name, tablename); + if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) + return NULL; + + if ((h = alloc_handle(info.name, info.size, info.num_entries)) + == NULL) { + close(sockfd); + sockfd = -1; + return NULL; + } + +/* Too hard --RR */ +#if 0 + sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name); + dynlib = dlopen(pathname, RTLD_NOW); + if (!dynlib) { + errno = ENOENT; + return NULL; + } + h->hooknames = dlsym(dynlib, "hooknames"); + if (!h->hooknames) { + errno = ENOENT; + return NULL; + } +#else + h->hooknames = hooknames; +#endif + + /* Initialize current state */ + h->info = info; + //h->new_number = h->info.num_entries; + for (i = 0; i < h->info.num_entries; i++) + h->counter_map[i] + = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i}); + + h->entries.size = h->info.size; + + tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size; + + if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries, + &tmp) < 0) { + close(sockfd); + sockfd = -1; + free(h); + return NULL; + } + + CHECK(h); + parse_ruleset(h); + + return h; +} + +void +TC_FREE(TC_HANDLE_T *h) +{ + struct list_head *cur_item, *item2; + + close(sockfd); + sockfd = -1; + + /* free all chains */ + list_for_each_safe(cur_item, item2, (*h)->chains.next) { + struct chain_head *chead = list_entry(cur_item, + struct chain_head, + list); + chainh_free(chead); + } + + /* FIXME: free all other ressources we might be using */ + + free(*h); + *h = NULL; +} + +static inline int +print_match(const STRUCT_ENTRY_MATCH *m) +{ + printf("Match name: `%s'\n", m->u.user.name); + return 0; +} + +static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle); + +#if 0 +void +TC_DUMP_ENTRIES(const TC_HANDLE_T handle) +{ + CHECK(handle); + + printf("libiptc v%s. %u entries, %u bytes.\n", + IPTABLES_VERSION, + handle->new_number, handle->entries.size); + printf("Table `%s'\n", handle->info.name); + printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n", + handle->info.hook_entry[HOOK_PRE_ROUTING], + handle->info.hook_entry[HOOK_LOCAL_IN], + handle->info.hook_entry[HOOK_FORWARD], + handle->info.hook_entry[HOOK_LOCAL_OUT], + handle->info.hook_entry[HOOK_POST_ROUTING]); + printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n", + handle->info.underflow[HOOK_PRE_ROUTING], + handle->info.underflow[HOOK_LOCAL_IN], + handle->info.underflow[HOOK_FORWARD], + handle->info.underflow[HOOK_LOCAL_OUT], + handle->info.underflow[HOOK_POST_ROUTING]); + + ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size, + dump_entry, handle); +} + +/* Returns 0 if not hook entry, else hooknumber + 1 */ +static inline unsigned int +is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h) +{ + unsigned int i; + + for (i = 0; i < NUMHOOKS; i++) { + if ((h->info.valid_hooks & (1 << i)) + && get_entry(h, h->info.hook_entry[i]) == e) + return i+1; + } + return 0; +} + + +static int alphasort(const void *a, const void *b) +{ + return strcmp(((struct chain_cache *)a)->name, + ((struct chain_cache *)b)->name); +} +#endif + +/* Returns chain head if found, otherwise NULL. */ +static struct chain_head * +find_label(const char *name, TC_HANDLE_T handle) +{ + struct list_head *pos; + + if (list_empty(&handle->chains)) + return NULL; + + list_for_each(pos, &handle->chains) { + struct chain_head *c = list_entry(pos, struct chain_head, list); + if (!strcmp(c->name, name)) + return c; + } + + return NULL; +} + +/* Does this chain exist? */ +int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle) +{ + return find_label(chain, handle) != NULL; +} + +#if 0 +/* Returns the position of the final (ie. unconditional) element. */ +static unsigned int +get_chain_end(const TC_HANDLE_T handle, unsigned int start) +{ + unsigned int last_off, off; + STRUCT_ENTRY *e; + + last_off = start; + e = get_entry(handle, start); + + /* Terminate when we meet a error label or a hook entry. */ + for (off = start + e->next_offset; + off < handle->entries.size; + last_off = off, off += e->next_offset) { + STRUCT_ENTRY_TARGET *t; + unsigned int i; + + e = get_entry(handle, off); + + /* We hit an entry point. */ + for (i = 0; i < NUMHOOKS; i++) { + if ((handle->info.valid_hooks & (1 << i)) + && off == handle->info.hook_entry[i]) + return last_off; + } + + /* We hit a user chain label */ + t = GET_TARGET(e); + if (strcmp(t->u.user.name, ERROR_TARGET) == 0) + return last_off; + } + /* SHOULD NEVER HAPPEN */ + fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n", + handle->entries.size, off); + abort(); +} +#endif + +/* Iterator functions to run through the chains. */ +const char * +TC_FIRST_CHAIN(TC_HANDLE_T *handle) +{ + struct chain_head *firsthead = list_entry((*handle)->chains.next, + struct chain_head, list); + (*handle)->chain_iterator_cur = firsthead; + + return firsthead->name; +} + +/* Iterator functions to run through the chains. Returns NULL at end. */ +const char * +TC_NEXT_CHAIN(TC_HANDLE_T *handle) +{ + struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list); + (*handle)->chain_iterator_cur = next; + + if (&next->list == &(*handle)->chains) + return NULL; + + return next->name; +} + +/* Get first rule in the given chain: NULL for empty chain. */ +const STRUCT_ENTRY * +TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle) +{ + struct chain_head *c; + struct rule_head *r; + + c = find_label(chain, *handle); + if (!c) { + errno = ENOENT; + return NULL; + } + + /* Empty chain: single return/policy rule */ + if (list_empty(&c->rules)) + return NULL; + + r = list_entry(c->rules.next, struct rule_head, list); + (*handle)->rule_iterator_cur = r; + + return r->entry; +} + +/* Returns NULL when rules run out. */ +const STRUCT_ENTRY * +TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle) +{ + struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list); + + if (&r->list == &r->chain->rules) + return NULL; + + /* NOTE: prev is without any influence ! */ + return r->entry; +} + +#if 0 +/* How many rules in this chain? */ +unsigned int +TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle) +{ + unsigned int off = 0; + STRUCT_ENTRY *start, *end; + + CHECK(*handle); + if (!find_label(&off, chain, *handle)) { + errno = ENOENT; + return (unsigned int)-1; + } + + start = get_entry(*handle, off); + end = get_entry(*handle, get_chain_end(*handle, off)); + + return entry2index(*handle, end) - entry2index(*handle, start); +} + +/* Get n'th rule in this chain. */ +const STRUCT_ENTRY *TC_GET_RULE(const char *chain, + unsigned int n, + TC_HANDLE_T *handle) +{ + unsigned int pos = 0, chainindex; + + CHECK(*handle); + if (!find_label(&pos, chain, *handle)) { + errno = ENOENT; + return NULL; + } + + chainindex = entry2index(*handle, get_entry(*handle, pos)); + + return index2entry(*handle, chainindex + n); +} +#endif + +static const char * +target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce) +{ + int spos; + unsigned int labelidx; + STRUCT_ENTRY *jumpto; + + /* To avoid const warnings */ + STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce; + + if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) + return GET_TARGET(e)->u.user.name; + + /* Standard target: evaluate */ + spos = *(int *)GET_TARGET(e)->data; + if (spos < 0) { + if (spos == RETURN) + return LABEL_RETURN; + else if (spos == -NF_ACCEPT-1) + return LABEL_ACCEPT; + else if (spos == -NF_DROP-1) + return LABEL_DROP; + else if (spos == -NF_QUEUE-1) + return LABEL_QUEUE; + + fprintf(stderr, "ERROR: off %lu/%u not a valid target (%d)\n", + entry2offset(handle, e), handle->entries.size, + spos); + abort(); + } + + jumpto = get_entry(handle, spos); + + /* Fall through rule */ + if (jumpto == (void *)e + e->next_offset) + return ""; + + /* Must point to head of a chain: ie. after error rule */ + /* FIXME: this needs to deal with internal jump targets */ + labelidx = entry2index(handle, jumpto) - 1; + return get_errorlabel(handle, index2offset(handle, labelidx)); +} + +/* Returns a pointer to the target name of this position. */ +const char *TC_GET_TARGET(const STRUCT_ENTRY *e, + TC_HANDLE_T *handle) +{ + return target_name(*handle, e); +} + +/* Is this a built-in chain? Actually returns hook + 1. */ +int +TC_BUILTIN(const char *chain, const TC_HANDLE_T handle) +{ + unsigned int i; + + for (i = 0; i < NUMHOOKS; i++) { + if ((handle->info.valid_hooks & (1 << i)) + && handle->hooknames[i] + && strcmp(handle->hooknames[i], chain) == 0) + return i+1; + } + return 0; +} + +/* Get the policy of a given built-in chain */ +const char * +TC_GET_POLICY(const char *chain, + STRUCT_COUNTERS *counters, + TC_HANDLE_T *handle) +{ + unsigned int start; + STRUCT_ENTRY *e; + int hook; + + hook = TC_BUILTIN(chain, *handle); + if (hook != 0) + start = (*handle)->info.hook_entry[hook-1]; + else + return NULL; + + e = get_entry(*handle, get_chain_end(*handle, start)); + *counters = e->counters; + + return target_name(*handle, e); +} + +static int +correct_verdict(STRUCT_ENTRY *e, + char *base, + unsigned int offset, int delta_offset) +{ + STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e); + unsigned int curr = (char *)e - base; + + /* Trap: insert of fall-through rule. Don't change fall-through + verdict to jump-over-next-rule. */ + if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0 + && t->verdict > (int)offset + && !(curr == offset && + t->verdict == curr + e->next_offset)) { + t->verdict += delta_offset; + } + + return 0; +} + +/* Adjusts standard verdict jump positions after an insertion/deletion. */ +static int +set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle) +{ + ENTRY_ITERATE((*handle)->entries.entrytable, + (*handle)->entries.size, + correct_verdict, (char *)(*handle)->entries.entrytable, + offset, delta_offset); + + set_changed(*handle); + return 1; +} + +/* If prepend is set, then we are prepending to a chain: if the + * insertion position is an entry point, keep the entry point. */ +static int +insert_rules(unsigned int num_rules, unsigned int rules_size, + const STRUCT_ENTRY *insert, + unsigned int offset, unsigned int num_rules_offset, + int prepend, + TC_HANDLE_T *handle) +{ + TC_HANDLE_T newh; + STRUCT_GETINFO newinfo; + unsigned int i; + + if (offset >= (*handle)->entries.size) { + errno = EINVAL; + return 0; + } + + newinfo = (*handle)->info; + + /* Fix up entry points. */ + for (i = 0; i < NUMHOOKS; i++) { + /* Entry points to START of chain, so keep same if + inserting on at that point. */ + if ((*handle)->info.hook_entry[i] > offset) + newinfo.hook_entry[i] += rules_size; + + /* Underflow always points to END of chain (policy), + so if something is inserted at same point, it + should be advanced. */ + if ((*handle)->info.underflow[i] >= offset) + newinfo.underflow[i] += rules_size; + } + + newh = alloc_handle((*handle)->info.name, + (*handle)->entries.size + rules_size, + (*handle)->new_number + num_rules); + if (!newh) + return 0; + newh->info = newinfo; + + /* Copy pre... */ + memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset); + /* ... Insert new ... */ + memcpy((char *)newh->entries.entrytable + offset, insert, rules_size); + /* ... copy post */ + memcpy((char *)newh->entries.entrytable + offset + rules_size, + (char *)(*handle)->entries.entrytable + offset, + (*handle)->entries.size - offset); + + /* Move counter map. */ + /* Copy pre... */ + memcpy(newh->counter_map, (*handle)->counter_map, + sizeof(struct counter_map) * num_rules_offset); + /* ... copy post */ + memcpy(newh->counter_map + num_rules_offset + num_rules, + (*handle)->counter_map + num_rules_offset, + sizeof(struct counter_map) * ((*handle)->new_number + - num_rules_offset)); + /* Set intermediates to no counter copy */ + for (i = 0; i < num_rules; i++) + newh->counter_map[num_rules_offset+i] + = ((struct counter_map){ COUNTER_MAP_SET, 0 }); + + newh->new_number = (*handle)->new_number + num_rules; + newh->entries.size = (*handle)->entries.size + rules_size; + newh->hooknames = (*handle)->hooknames; + + if ((*handle)->cache_chain_heads) + free((*handle)->cache_chain_heads); + free(*handle); + *handle = newh; + + return set_verdict(offset, rules_size, handle); +} + +static int +delete_rules(unsigned int num_rules, unsigned int rules_size, + unsigned int offset, unsigned int num_rules_offset, + TC_HANDLE_T *handle) +{ + unsigned int i; + + if (offset + rules_size > (*handle)->entries.size) { + errno = EINVAL; + return 0; + } + + /* Fix up entry points. */ + for (i = 0; i < NUMHOOKS; i++) { + /* In practice, we never delete up to a hook entry, + since the built-in chains are always first, + so these two are never equal */ + if ((*handle)->info.hook_entry[i] >= offset + rules_size) + (*handle)->info.hook_entry[i] -= rules_size; + else if ((*handle)->info.hook_entry[i] > offset) { + fprintf(stderr, "ERROR: Deleting entry %u %u %u\n", + i, (*handle)->info.hook_entry[i], offset); + abort(); + } + + /* Underflow points to policy (terminal) rule in + built-in, so sequality is valid here (when deleting + the last rule). */ + if ((*handle)->info.underflow[i] >= offset + rules_size) + (*handle)->info.underflow[i] -= rules_size; + else if ((*handle)->info.underflow[i] > offset) { + fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n", + i, (*handle)->info.underflow[i], offset); + abort(); + } + } + + /* Move the rules down. */ + memmove((char *)(*handle)->entries.entrytable + offset, + (char *)(*handle)->entries.entrytable + offset + rules_size, + (*handle)->entries.size - (offset + rules_size)); + + /* Move the counter map down. */ + memmove(&(*handle)->counter_map[num_rules_offset], + &(*handle)->counter_map[num_rules_offset + num_rules], + sizeof(struct counter_map) + * ((*handle)->new_number - (num_rules + num_rules_offset))); + + /* Fix numbers */ + (*handle)->new_number -= num_rules; + (*handle)->entries.size -= rules_size; + + return set_verdict(offset, -(int)rules_size, handle); +} + +static int +standard_map(STRUCT_ENTRY *e, int verdict) +{ + STRUCT_STANDARD_TARGET *t; + + t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); + + if (t->target.u.target_size + != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) { + errno = EINVAL; + return 0; + } + /* memset for memcmp convenience on delete/replace */ + memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN); + strcpy(t->target.u.user.name, STANDARD_TARGET); + t->verdict = verdict; + + return 1; +} + +static int +map_target(const TC_HANDLE_T handle, + STRUCT_ENTRY *e, + unsigned int offset, + STRUCT_ENTRY_TARGET *old) +{ + STRUCT_ENTRY_TARGET *t = GET_TARGET(e); + + /* Save old target (except data, which we don't change, except for + standard case, where we don't care). */ + *old = *t; + + /* Maybe it's empty (=> fall through) */ + if (strcmp(t->u.user.name, "") == 0) + return standard_map(e, offset + e->next_offset); + /* Maybe it's a standard target name... */ + else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0) + return standard_map(e, -NF_ACCEPT - 1); + else if (strcmp(t->u.user.name, LABEL_DROP) == 0) + return standard_map(e, -NF_DROP - 1); + else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0) + return standard_map(e, -NF_QUEUE - 1); + else if (strcmp(t->u.user.name, LABEL_RETURN) == 0) + return standard_map(e, RETURN); + else if (TC_BUILTIN(t->u.user.name, handle)) { + /* Can't jump to builtins. */ + errno = EINVAL; + return 0; + } else { + /* Maybe it's an existing chain name. */ + struct chain_cache *c; + + c = find_label(t->u.user.name, handle); + if (c) + return standard_map(e, c->start_off); + } + + /* Must be a module? If not, kernel will reject... */ + /* memset to all 0 for your memcmp convenience. */ + memset(t->u.user.name + strlen(t->u.user.name), + 0, + FUNCTION_MAXNAMELEN - strlen(t->u.user.name)); + return 1; +} + +static void +unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old) +{ + STRUCT_ENTRY_TARGET *t = GET_TARGET(e); + + /* Save old target (except data, which we don't change, except for + standard case, where we don't care). */ + *t = *old; +} + +/* Insert the entry `fw' in chain `chain' into position `rulenum'. */ +int +TC_INSERT_ENTRY(const IPT_CHAINLABEL chain, + const STRUCT_ENTRY *e, + unsigned int rulenum, + TC_HANDLE_T *handle) +{ + unsigned int chainindex, offset; + STRUCT_ENTRY_TARGET old; + struct chain_cache *c; + STRUCT_ENTRY *tmp; + int ret; + + iptc_fn = TC_INSERT_ENTRY; + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + chainindex = offset2index(*handle, c->start_off); + + tmp = index2entry(*handle, chainindex + rulenum); + if (!tmp || tmp > offset2entry(*handle, c->end_off)) { + errno = E2BIG; + return 0; + } + offset = index2offset(*handle, chainindex + rulenum); + + /* Mapping target actually alters entry, but that's + transparent to the caller. */ + if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old)) + return 0; + + ret = insert_rules(1, e->next_offset, e, offset, + chainindex + rulenum, rulenum == 0, handle); + unmap_target((STRUCT_ENTRY *)e, &old); + return ret; +} + +/* Atomically replace rule `rulenum' in `chain' with `fw'. */ +int +TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain, + const STRUCT_ENTRY *e, + unsigned int rulenum, + TC_HANDLE_T *handle) +{ + unsigned int chainindex, offset; + STRUCT_ENTRY_TARGET old; + struct chain_cache *c; + STRUCT_ENTRY *tmp; + int ret; + + iptc_fn = TC_REPLACE_ENTRY; + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + chainindex = offset2index(*handle, c->start_off); + + tmp = index2entry(*handle, chainindex + rulenum); + if (!tmp || tmp >= offset2entry(*handle, c->end_off)) { + errno = E2BIG; + return 0; + } + + offset = index2offset(*handle, chainindex + rulenum); + /* Replace = delete and insert. */ + if (!delete_rules(1, get_entry(*handle, offset)->next_offset, + offset, chainindex + rulenum, handle)) + return 0; + + if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old)) + return 0; + + ret = insert_rules(1, e->next_offset, e, offset, + chainindex + rulenum, 1, handle); + unmap_target((STRUCT_ENTRY *)e, &old); + return ret; +} + +/* Append entry `fw' to chain `chain'. Equivalent to insert with + rulenum = length of chain. */ +int +TC_APPEND_ENTRY(const IPT_CHAINLABEL chain, + const STRUCT_ENTRY *e, + TC_HANDLE_T *handle) +{ + struct chain_cache *c; + STRUCT_ENTRY_TARGET old; + int ret; + + iptc_fn = TC_APPEND_ENTRY; + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + if (!map_target(*handle, (STRUCT_ENTRY *)e, + c->end_off, &old)) + return 0; + + ret = insert_rules(1, e->next_offset, e, c->end_off, + offset2index(*handle, c->end_off), 0, handle); + unmap_target((STRUCT_ENTRY *)e, &old); + return ret; +} + +static inline int +match_different(const STRUCT_ENTRY_MATCH *a, + const unsigned char *a_elems, + const unsigned char *b_elems, + unsigned char **maskptr) +{ + const STRUCT_ENTRY_MATCH *b; + unsigned int i; + + /* Offset of b is the same as a. */ + b = (void *)b_elems + ((unsigned char *)a - a_elems); + + if (a->u.match_size != b->u.match_size) + return 1; + + if (strcmp(a->u.user.name, b->u.user.name) != 0) + return 1; + + *maskptr += ALIGN(sizeof(*a)); + + for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++) + if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0) + return 1; + *maskptr += i; + return 0; +} + +static inline int +target_different(const unsigned char *a_targdata, + const unsigned char *b_targdata, + unsigned int tdatasize, + const unsigned char *mask) +{ + unsigned int i; + for (i = 0; i < tdatasize; i++) + if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0) + return 1; + + return 0; +} + +static int +is_same(const STRUCT_ENTRY *a, + const STRUCT_ENTRY *b, + unsigned char *matchmask); + +/* Delete the first rule in `chain' which matches `fw'. */ +int +TC_DELETE_ENTRY(const IPT_CHAINLABEL chain, + const STRUCT_ENTRY *origfw, + unsigned char *matchmask, + TC_HANDLE_T *handle) +{ + unsigned int offset; + struct chain_cache *c; + STRUCT_ENTRY *e, *fw; + + iptc_fn = TC_DELETE_ENTRY; + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + fw = malloc(origfw->next_offset); + if (fw == NULL) { + errno = ENOMEM; + return 0; + } + + for (offset = c->start_off; offset < c->end_off; + offset += e->next_offset) { + STRUCT_ENTRY_TARGET discard; + + memcpy(fw, origfw, origfw->next_offset); + + /* FIXME: handle this in is_same --RR */ + if (!map_target(*handle, fw, offset, &discard)) { + free(fw); + return 0; + } + e = get_entry(*handle, offset); + +#if 0 + printf("Deleting:\n"); + dump_entry(newe); +#endif + if (is_same(e, fw, matchmask)) { + int ret; + ret = delete_rules(1, e->next_offset, + offset, entry2index(*handle, e), + handle); + free(fw); + return ret; + } + } + + free(fw); + errno = ENOENT; + return 0; +} + +/* Delete the rule in position `rulenum' in `chain'. */ +int +TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain, + unsigned int rulenum, + TC_HANDLE_T *handle) +{ + unsigned int index; + int ret; + STRUCT_ENTRY *e; + struct chain_cache *c; + + iptc_fn = TC_DELETE_NUM_ENTRY; + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + index = offset2index(*handle, c->start_off) + rulenum; + + if (index >= offset2index(*handle, c->end_off)) { + errno = E2BIG; + return 0; + } + + e = index2entry(*handle, index); + if (e == NULL) { + errno = EINVAL; + return 0; + } + + ret = delete_rules(1, e->next_offset, entry2offset(*handle, e), + index, handle); + return ret; +} + +/* Check the packet `fw' on chain `chain'. Returns the verdict, or + NULL and sets errno. */ +const char * +TC_CHECK_PACKET(const IPT_CHAINLABEL chain, + STRUCT_ENTRY *entry, + TC_HANDLE_T *handle) +{ + errno = ENOSYS; + return NULL; +} + +/* Flushes the entries in the given chain (ie. empties chain). */ +int +TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) +{ + unsigned int startindex, endindex; + STRUCT_ENTRY *startentry, *endentry; + struct chain_cache *c; + int ret; + + iptc_fn = TC_FLUSH_ENTRIES; + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + startindex = offset2index(*handle, c->start_off); + endindex = offset2index(*handle, c->end_off); + startentry = offset2entry(*handle, c->start_off); + endentry = offset2entry(*handle, c->end_off); + + ret = delete_rules(endindex - startindex, + (char *)endentry - (char *)startentry, + c->start_off, startindex, + handle); + return ret; +} + +/* Zeroes the counters in a chain. */ +int +TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) +{ + unsigned int i, end; + struct chain_cache *c; + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + i = offset2index(*handle, c->start_off); + end = offset2index(*handle, c->end_off); + + for (; i <= end; i++) { + if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP) + (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED; + } + set_changed(*handle); + + return 1; +} + +STRUCT_COUNTERS * +TC_READ_COUNTER(const IPT_CHAINLABEL chain, + unsigned int rulenum, + TC_HANDLE_T *handle) +{ + STRUCT_ENTRY *e; + struct chain_cache *c; + unsigned int chainindex, end; + + iptc_fn = TC_READ_COUNTER; + CHECK(*handle); + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return NULL; + } + + chainindex = offset2index(*handle, c->start_off); + end = offset2index(*handle, c->end_off); + + if (chainindex + rulenum > end) { + errno = E2BIG; + return NULL; + } + + e = index2entry(*handle, chainindex + rulenum); + + return &e->counters; +} + +int +TC_ZERO_COUNTER(const IPT_CHAINLABEL chain, + unsigned int rulenum, + TC_HANDLE_T *handle) +{ + STRUCT_ENTRY *e; + struct chain_cache *c; + unsigned int chainindex, end; + + iptc_fn = TC_ZERO_COUNTER; + CHECK(*handle); + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + chainindex = offset2index(*handle, c->start_off); + end = offset2index(*handle, c->end_off); + + if (chainindex + rulenum > end) { + errno = E2BIG; + return 0; + } + + e = index2entry(*handle, chainindex + rulenum); + + if ((*handle)->counter_map[chainindex + rulenum].maptype + == COUNTER_MAP_NORMAL_MAP) { + (*handle)->counter_map[chainindex + rulenum].maptype + = COUNTER_MAP_ZEROED; + } + + set_changed(*handle); + + return 1; +} + +int +TC_SET_COUNTER(const IPT_CHAINLABEL chain, + unsigned int rulenum, + STRUCT_COUNTERS *counters, + TC_HANDLE_T *handle) +{ + STRUCT_ENTRY *e; + struct chain_cache *c; + unsigned int chainindex, end; + + iptc_fn = TC_SET_COUNTER; + CHECK(*handle); + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + chainindex = offset2index(*handle, c->start_off); + end = offset2index(*handle, c->end_off); + + if (chainindex + rulenum > end) { + errno = E2BIG; + return 0; + } + + e = index2entry(*handle, chainindex + rulenum); + + (*handle)->counter_map[chainindex + rulenum].maptype + = COUNTER_MAP_SET; + + memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS)); + + set_changed(*handle); + + return 1; +} + +/* Creates a new chain. */ +/* To create a chain, create two rules: error node and unconditional + * return. */ +int +TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) +{ + int ret; + struct chainstart { + STRUCT_ENTRY head; + struct ipt_error_target name; + } *newc1; + struct chainend { + STRUCT_ENTRY ret; + STRUCT_STANDARD_TARGET target; + } *newc2; + struct chain_head *chead; + + iptc_fn = TC_CREATE_CHAIN; + + /* find_label doesn't cover built-in targets: DROP, ACCEPT, + QUEUE, RETURN. */ + if (find_label(chain, *handle) + || strcmp(chain, LABEL_DROP) == 0 + || strcmp(chain, LABEL_ACCEPT) == 0 + || strcmp(chain, LABEL_QUEUE) == 0 + || strcmp(chain, LABEL_RETURN) == 0) { + errno = EEXIST; + return 0; + } + + if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) { + errno = EINVAL; + return 0; + } + + chead = chainh_alloc(*handle, chain); + if (!chead) { + errno = ENOMEM; + return 0; + } + + newc1 = ruleh_alloc(sizeof(*newc1)); + if (!newc1) { + chainh_free(chead); + return 0; + } + + newc2 = ruleh_alloc(sizeof(*newc2)); + if (!newc2) { + chainh_free(chead); + ruleh_free(newc1); + return 0; + } + + newc1->head.target_offset = sizeof(STRUCT_ENTRY); + newc1->head.next_offset + = sizeof(STRUCT_ENTRY) + + ALIGN(sizeof(struct ipt_error_target)); + strcpy(newc1->name.t.u.user.name, ERROR_TARGET); + newc1->name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target)); + strcpy(newc1->name.error, chain); + + newc2->ret.target_offset = sizeof(STRUCT_ENTRY); + newc2->ret.next_offset + = sizeof(STRUCT_ENTRY) + + ALIGN(sizeof(STRUCT_STANDARD_TARGET)); + strcpy(newc2->target.target.u.user.name, STANDARD_TARGET); + newc->target.target.u.target_size + = ALIGN(sizeof(STRUCT_STANDARD_TARGET)); + newc->target.verdict = RETURN; + + list_prepend(newc1, &chead->rules); + list_append(newc2, &chead->rules); + + return 1; +} + +static int +count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref) +{ + STRUCT_STANDARD_TARGET *t; + + if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) { + t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); + + if (t->verdict == offset) + (*ref)++; + } + + return 0; +} + +/* Get the number of references to this chain. */ +int +TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain, + TC_HANDLE_T *handle) +{ + struct chain_cache *c; + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + *ref = 0; + ENTRY_ITERATE((*handle)->entries.entrytable, + (*handle)->entries.size, + count_ref, c->start_off, ref); + return 1; +} + +/* Deletes a chain. */ +int +TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) +{ + unsigned int labelidx, labeloff; + unsigned int references; + struct chain_cache *c; + int ret; + STRUCT_ENTRY *start; + + if (!TC_GET_REFERENCES(&references, chain, handle)) + return 0; + + iptc_fn = TC_DELETE_CHAIN; + + if (TC_BUILTIN(chain, *handle)) { + errno = EINVAL; + return 0; + } + + if (references > 0) { + errno = EMLINK; + return 0; + } + + if (!(c = find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + + if (c->start_off != c->end_off) { + errno = ENOTEMPTY; + return 0; + } + + /* Need label index: preceeds chain start */ + labelidx = offset2index(*handle, c->start_off) - 1; + labeloff = index2offset(*handle, labelidx); + + start = offset2entry(*handle, c->start_off); + + ret = delete_rules(2, + get_entry(*handle, labeloff)->next_offset + + start->next_offset, + labeloff, labelidx, handle); + return ret; +} + +/* Renames a chain. */ +int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname, + const IPT_CHAINLABEL newname, + TC_HANDLE_T *handle) +{ + unsigned int labeloff, labelidx; + struct chain_cache *c; + struct ipt_error_target *t; + + iptc_fn = TC_RENAME_CHAIN; + + /* find_label doesn't cover built-in targets: DROP, ACCEPT, + QUEUE, RETURN. */ + if (find_label(newname, *handle) + || strcmp(newname, LABEL_DROP) == 0 + || strcmp(newname, LABEL_ACCEPT) == 0 + || strcmp(newname, LABEL_QUEUE) == 0 + || strcmp(newname, LABEL_RETURN) == 0) { + errno = EEXIST; + return 0; + } + + if (!(c = find_label(oldname, *handle)) + || TC_BUILTIN(oldname, *handle)) { + errno = ENOENT; + return 0; + } + + if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) { + errno = EINVAL; + return 0; + } + + /* Need label index: preceeds chain start */ + labelidx = offset2index(*handle, c->start_off) - 1; + labeloff = index2offset(*handle, labelidx); + + t = (struct ipt_error_target *) + GET_TARGET(get_entry(*handle, labeloff)); + + memset(t->error, 0, sizeof(t->error)); + strcpy(t->error, newname); + set_changed(*handle); + + return 1; +} + +/* Sets the policy on a built-in chain. */ +int +TC_SET_POLICY(const IPT_CHAINLABEL chain, + const IPT_CHAINLABEL policy, + STRUCT_COUNTERS *counters, + TC_HANDLE_T *handle) +{ + unsigned int hook; + unsigned int policyoff, ctrindex; + STRUCT_ENTRY *e; + STRUCT_STANDARD_TARGET *t; + + iptc_fn = TC_SET_POLICY; + /* Figure out which chain. */ + hook = TC_BUILTIN(chain, *handle); + if (hook == 0) { + errno = ENOENT; + return 0; + } else + hook--; + + policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]); + if (policyoff != (*handle)->info.underflow[hook]) { + printf("ERROR: Policy for `%s' offset %u != underflow %u\n", + chain, policyoff, (*handle)->info.underflow[hook]); + return 0; + } + + e = get_entry(*handle, policyoff); + t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); + + if (strcmp(policy, LABEL_ACCEPT) == 0) + t->verdict = -NF_ACCEPT - 1; + else if (strcmp(policy, LABEL_DROP) == 0) + t->verdict = -NF_DROP - 1; + else { + errno = EINVAL; + return 0; + } + + ctrindex = entry2index(*handle, e); + + if (counters) { + /* set byte and packet counters */ + memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS)); + + (*handle)->counter_map[ctrindex].maptype + = COUNTER_MAP_SET; + + } else { + (*handle)->counter_map[ctrindex] + = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 }); + } + + set_changed(*handle); + + return 1; +} + +/* Without this, on gcc 2.7.2.3, we get: + libiptc.c: In function `TC_COMMIT': + libiptc.c:833: fixed or forbidden register was spilled. + This may be due to a compiler bug or to impossible asm + statements or clauses. +*/ +static void +subtract_counters(STRUCT_COUNTERS *answer, + const STRUCT_COUNTERS *a, + const STRUCT_COUNTERS *b) +{ + answer->pcnt = a->pcnt - b->pcnt; + answer->bcnt = a->bcnt - b->bcnt; +} + +int +TC_COMMIT(TC_HANDLE_T *handle) +{ + /* Replace, then map back the counters. */ + STRUCT_REPLACE *repl; + STRUCT_COUNTERS_INFO *newcounters; + unsigned int i; + size_t counterlen; + + CHECK(*handle); + + counterlen = sizeof(STRUCT_COUNTERS_INFO) + + sizeof(STRUCT_COUNTERS) * (*handle)->new_number; + +#if 0 + TC_DUMP_ENTRIES(*handle); +#endif + + /* Don't commit if nothing changed. */ + if (!(*handle)->changed) + goto finished; + + repl = malloc(sizeof(*repl) + (*handle)->entries.size); + if (!repl) { + errno = ENOMEM; + return 0; + } + + /* These are the old counters we will get from kernel */ + repl->counters = malloc(sizeof(STRUCT_COUNTERS) + * (*handle)->info.num_entries); + if (!repl->counters) { + free(repl); + errno = ENOMEM; + return 0; + } + + /* These are the counters we're going to put back, later. */ + newcounters = malloc(counterlen); + if (!newcounters) { + free(repl->counters); + free(repl); + errno = ENOMEM; + return 0; + } + + strcpy(repl->name, (*handle)->info.name); + repl->num_entries = (*handle)->new_number; + repl->size = (*handle)->entries.size; + memcpy(repl->hook_entry, (*handle)->info.hook_entry, + sizeof(repl->hook_entry)); + memcpy(repl->underflow, (*handle)->info.underflow, + sizeof(repl->underflow)); + repl->num_counters = (*handle)->info.num_entries; + repl->valid_hooks = (*handle)->info.valid_hooks; + memcpy(repl->entries, (*handle)->entries.entrytable, + (*handle)->entries.size); + + if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl, + sizeof(*repl) + (*handle)->entries.size) < 0) { + free(repl->counters); + free(repl); + free(newcounters); + return 0; + } + + /* Put counters back. */ + strcpy(newcounters->name, (*handle)->info.name); + newcounters->num_counters = (*handle)->new_number; + for (i = 0; i < (*handle)->new_number; i++) { + unsigned int mappos = (*handle)->counter_map[i].mappos; + switch ((*handle)->counter_map[i].maptype) { + case COUNTER_MAP_NOMAP: + newcounters->counters[i] + = ((STRUCT_COUNTERS){ 0, 0 }); + break; + + case COUNTER_MAP_NORMAL_MAP: + /* Original read: X. + * Atomic read on replacement: X + Y. + * Currently in kernel: Z. + * Want in kernel: X + Y + Z. + * => Add in X + Y + * => Add in replacement read. + */ + newcounters->counters[i] = repl->counters[mappos]; + break; + + case COUNTER_MAP_ZEROED: + /* Original read: X. + * Atomic read on replacement: X + Y. + * Currently in kernel: Z. + * Want in kernel: Y + Z. + * => Add in Y. + * => Add in (replacement read - original read). + */ + subtract_counters(&newcounters->counters[i], + &repl->counters[mappos], + &index2entry(*handle, i)->counters); + break; + + case COUNTER_MAP_SET: + /* Want to set counter (iptables-restore) */ + + memcpy(&newcounters->counters[i], + &index2entry(*handle, i)->counters, + sizeof(STRUCT_COUNTERS)); + + break; + } + } + +#ifdef KERNEL_64_USERSPACE_32 + { + /* Kernel will think that pointer should be 64-bits, and get + padding. So we accomodate here (assumption: alignment of + `counters' is on 64-bit boundary). */ + u_int64_t *kernptr = (u_int64_t *)&newcounters->counters; + if ((unsigned long)&newcounters->counters % 8 != 0) { + fprintf(stderr, + "counters alignment incorrect! Mail rusty!\n"); + abort(); + } + *kernptr = newcounters->counters; + } +#endif /* KERNEL_64_USERSPACE_32 */ + + if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS, + newcounters, counterlen) < 0) { + free(repl->counters); + free(repl); + free(newcounters); + return 0; + } + + free(repl->counters); + free(repl); + free(newcounters); + + finished: + TC_FREE(handle); + return 1; +} + +/* Get raw socket. */ +int +TC_GET_RAW_SOCKET() +{ + return sockfd; +} + +/* Translates errno numbers into more human-readable form than strerror. */ +const char * +TC_STRERROR(int err) +{ + unsigned int i; + struct table_struct { + void *fn; + int err; + const char *message; + } table [] = + { { TC_INIT, EPERM, "Permission denied (you must be root)" }, + { TC_INIT, EINVAL, "Module is wrong version" }, + { TC_INIT, ENOENT, + "Table does not exist (do you need to insmod?)" }, + { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" }, + { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" }, + { TC_DELETE_CHAIN, EMLINK, + "Can't delete chain with references left" }, + { TC_CREATE_CHAIN, EEXIST, "Chain already exists" }, + { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" }, + { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" }, + { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" }, + { TC_READ_COUNTER, E2BIG, "Index of counter too big" }, + { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" }, + { TC_INSERT_ENTRY, ELOOP, "Loop found in table" }, + { TC_INSERT_ENTRY, EINVAL, "Target problem" }, + /* EINVAL for CHECK probably means bad interface. */ + { TC_CHECK_PACKET, EINVAL, + "Bad arguments (does that interface exist?)" }, + { TC_CHECK_PACKET, ENOSYS, + "Checking will most likely never get implemented" }, + /* ENOENT for DELETE probably means no matching rule */ + { TC_DELETE_ENTRY, ENOENT, + "Bad rule (does a matching rule exist in that chain?)" }, + { TC_SET_POLICY, ENOENT, + "Bad built-in chain name" }, + { TC_SET_POLICY, EINVAL, + "Bad policy name" }, + + { NULL, 0, "Incompatible with this kernel" }, + { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" }, + { NULL, ENOSYS, "Will be implemented real soon. I promise ;)" }, + { NULL, ENOMEM, "Memory allocation problem" }, + { NULL, ENOENT, "No chain/target/match by that name" }, + }; + + for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) { + if ((!table[i].fn || table[i].fn == iptc_fn) + && table[i].err == err) + return table[i].message; + } + + return strerror(err); +} -- 2.43.0