'planetlab-3_2-branch'.
--- /dev/null
+.makefirst
+ip6tables
+ip6tables-restore
+ip6tables-save
+iptables
+iptables-restore
+iptables-save
+*.d
--- /dev/null
+#!/bin/bash
+
+if test -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_addrtype.h; then
+ echo "addrtype"
+fi
--- /dev/null
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ip_pool.h ] && echo pool POOL
--- /dev/null
+/* Shared library add-on to iptables to add IP pool mangling target. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#define ip_pool_init ip_POOL_init
+#define ip_pool_get_index ip_POOL_get_index
+#define ip_pool_get_name ip_POOL_get_name
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"POOL v%s options:\n"
+" --add-srcip <pool>\n"
+" --del-srcip <pool>\n"
+" --add-dstip <pool>\n"
+" --del-dstip <pool>\n"
+" add/del src/dst IP from pool.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+ { "add-srcip", 1, 0, '1' },
+ { "del-srcip", 1, 0, '2' },
+ { "add-dstip", 1, 0, '3' },
+ { "del-dstip", 1, 0, '4' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *target, unsigned int *nfcache)
+{
+ struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+ ipi->src = ipi->dst = IP_POOL_NONE;
+ ipi->flags = 0;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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,
+ struct ipt_entry_target **target)
+{
+ struct ipt_pool_info *ipi = (struct ipt_pool_info *) (*target)->data;
+ switch (c) {
+ case '1': /* --add-srcip <pool> */
+ ipi->src = ip_pool_get_index(optarg);
+ ipi->flags &= ~IPT_POOL_DEL_SRC;
+ break;
+ case '2': /* --del-srcip <pool> */
+ ipi->src = ip_pool_get_index(optarg);
+ ipi->flags |= IPT_POOL_DEL_SRC;
+ break;
+ case '3': /* --add-dstip <pool> */
+ ipi->dst = ip_pool_get_index(optarg);
+ ipi->flags &= ~IPT_POOL_DEL_DST;
+ break;
+ case '4': /* --del-dstip <pool> */
+ ipi->dst = ip_pool_get_index(optarg);
+ ipi->flags |= IPT_POOL_DEL_DST;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ char buf[256];
+ struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+ printf("POOL");
+ if (ipi->src != IP_POOL_NONE) {
+ printf(" --%s-srcip %s",
+ (ipi->flags & IPT_POOL_DEL_SRC) ? "del" : "add",
+ ip_pool_get_name(buf, sizeof(buf), ipi->src, numeric));
+ }
+ if (ipi->dst != IP_POOL_NONE) {
+ printf(" --%s-dstip %s",
+ (ipi->flags & IPT_POOL_DEL_DST) ? "del" : "add",
+ ip_pool_get_name(buf, sizeof(buf), ipi->dst, numeric));
+ }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ char buf[256];
+ struct ipt_pool_info *ipi = (struct ipt_pool_info *) target->data;
+
+ printf("-j POOL");
+ if (ipi->src != IP_POOL_NONE) {
+ printf(" --%s-srcip %s",
+ (ipi->flags & IPT_POOL_DEL_SRC) ? "del" : "add",
+ ip_pool_get_name(buf, sizeof(buf), ipi->src, 0));
+ }
+ if (ipi->dst != IP_POOL_NONE) {
+ printf(" --%s-dstip %s",
+ (ipi->flags & IPT_POOL_DEL_DST) ? "del" : "add",
+ ip_pool_get_name(buf, sizeof(buf), ipi->dst, 0));
+ }
+}
+
+static
+struct iptables_target ipt_pool_target
+= { NULL,
+ "POOL",
+ IPTABLES_VERSION,
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&ipt_pool_target);
+}
--- /dev/null
+/* Shared library add-on to iptables to add IP address pool matching. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"pool v%s options:\n"
+" [!] --srcpool NAME|INDEX\n"
+" [!] --dstpool NAME|INDEX\n"
+" Pool index (or name from %s) to match\n"
+"\n", IPTABLES_VERSION, IPPOOL_CONF);
+}
+
+static struct option opts[] = {
+ { "srcpool", 1, 0, '1' },
+ { "dstpool", 1, 0, '2' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ info->src = IP_POOL_NONE;
+ info->dst = IP_POOL_NONE;
+ info->flags = 0;
+ /* Can't cache this - XXX */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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_pool_info *info =
+ (struct ipt_pool_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ check_inverse(optarg, &invert, &optind, 0);
+ info->src = ip_pool_get_index(argv[optind-1]);
+ if (invert) info->flags |= IPT_POOL_INV_SRC;
+ *flags = 1;
+ break;
+ case '2':
+ check_inverse(optarg, &invert, &optind, 0);
+ info->dst = ip_pool_get_index(argv[optind-1]);
+ if (invert) info->flags |= IPT_POOL_INV_DST;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; must have specified --srcpool or --dstpool. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM, "You must specify either `--srcpool or --dstpool'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ char buf[256];
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ if (info->src != IP_POOL_NONE)
+ printf("%ssrcpool %s ",
+ (info->flags & IPT_POOL_INV_SRC) ? "!" : "",
+ ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+ if (info->dst != IP_POOL_NONE)
+ printf("%sdstpool %s ",
+ (info->flags & IPT_POOL_INV_DST) ? "!" : "",
+ ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ char buf[256];
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ if (info->src != IP_POOL_NONE)
+ printf("%s--srcpool %s ",
+ (info->flags & IPT_POOL_INV_SRC) ? "! " : "",
+ ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+ if (info->dst != IP_POOL_NONE)
+ printf("%s--dstpool %s ",
+ (info->flags & IPT_POOL_INV_DST) ? "! " : "",
+ ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+static
+struct iptables_match pool
+= { NULL,
+ "pool",
+ IPTABLES_VERSION,
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&pool);
+}
--- /dev/null
+/* Shared library add-on to iptables to add IP address pool matching. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_pool.h>
+
+#include <libippool/ip_pool_support.h>
+
+/* FIXME --RR */
+#include "../ippool/libippool.c"
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"pool v%s options:\n"
+" [!] --srcpool NAME|INDEX\n"
+" [!] --dstpool NAME|INDEX\n"
+" Pool index (or name from %s) to match\n"
+"\n", IPTABLES_VERSION, IPPOOL_CONF);
+}
+
+static struct option opts[] = {
+ { "srcpool", 1, 0, '1' },
+ { "dstpool", 1, 0, '2' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ info->src = IP_POOL_NONE;
+ info->dst = IP_POOL_NONE;
+ info->flags = 0;
+ /* Can't cache this - XXX */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* 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_pool_info *info =
+ (struct ipt_pool_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ check_inverse(optarg, &invert, &optind, 0);
+ info->src = ip_pool_get_index(argv[optind-1]);
+ if (invert) info->flags |= IPT_POOL_INV_SRC;
+ *flags = 1;
+ break;
+ case '2':
+ check_inverse(optarg, &invert, &optind, 0);
+ info->dst = ip_pool_get_index(argv[optind-1]);
+ if (invert) info->flags |= IPT_POOL_INV_DST;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; must have specified --srcpool or --dstpool. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM, "You must specify either `--srcpool or --dstpool'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ char buf[256];
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ if (info->src != IP_POOL_NONE)
+ printf("%ssrcpool %s ",
+ (info->flags & IPT_POOL_INV_SRC) ? "!" : "",
+ ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+ if (info->dst != IP_POOL_NONE)
+ printf("%sdstpool %s ",
+ (info->flags & IPT_POOL_INV_DST) ? "!" : "",
+ ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ char buf[256];
+ struct ipt_pool_info *info =
+ (struct ipt_pool_info *)match->data;
+
+ if (info->src != IP_POOL_NONE)
+ printf("%s--srcpool %s",
+ (info->flags & IPT_POOL_INV_SRC) ? "! " : "",
+ ip_pool_get_name(buf, sizeof(buf), info->src, 0));
+ if (info->dst != IP_POOL_NONE)
+ printf("%s--dstpool %s",
+ (info->flags & IPT_POOL_INV_DST) ? "! " : "",
+ ip_pool_get_name(buf, sizeof(buf), info->dst, 0));
+}
+
+static
+struct iptables_match pool
+= { NULL,
+ "pool",
+ IPTABLES_VERSION,
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ IPT_ALIGN(sizeof(struct ipt_pool_info)),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&pool);
+}
--- /dev/null
+/* support function prototypes for IP pool management (config file, mostly) */
+#ifndef _IP_POOL_SUPPORT_H
+#define _IP_POOL_SUPPORT_H
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+
+#ifndef IPPOOL_CONF
+#define IPPOOL_CONF "/etc/ippool.conf"
+#endif
+
+/* called just to draw in this support .o */
+void ip_pool_init(void);
+
+/* given a pool name (or number), return pool index, possibly reading .conf */
+ip_pool_t ip_pool_get_index(char *name);
+
+/* given a pool index, and a buffer to store a name, search for the index
+ * in the .conf file, and give the textual name, if present; if not, the
+ * numeric index is returned. If numeric_flag == 1, the numeric index is
+ * always returned
+ */
+char *ip_pool_get_name(char *buf, int size, ip_pool_t index, int numeric_flag);
+
+#endif /*_IP_POOL_SUPPORT_H*/
--- /dev/null
+.TH IP6TABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" Man page written by Andras Kis-Szabo <kisza@sch.bme.hu>
+.\" It is based on iptables man page.
+.\"
+.\" iptables page by Herve Eychenne <rv@wallfire.org>
+.\" It is based on ipchains man page.
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\" 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.
+.\"
+.\"
+.SH NAME
+ip6tables \- IPv6 packet filter administration
+.SH SYNOPSIS
+.BR "ip6tables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "ip6tables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "ip6tables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "ip6tables [-t table] -N " "chain"
+.br
+.BR "ip6tables [-t table] -X " "[chain]"
+.br
+.BR "ip6tables [-t table] -P " "chain target [options]"
+.br
+.BR "ip6tables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Ip6tables
+is used to set up, maintain, and inspect the tables of IPv6 packet
+filter rules in the Linux kernel. Several different tables
+may be defined. Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets. Each
+rule specifies what to do with a packet that matches. This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target. If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace (if supported by the kernel).
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain. If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently two independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present), as nat table has not been implemented yet.
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on. If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed). It contains
+the built-in chains
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration. Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B ip6tables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform. Only one of them
+can be specified on the command line unless otherwise specified
+below. For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B ip6tables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain. There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.B "-I, --insert"
+Insert one or more rules in the selected chain as the given rule
+number. So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain. This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain. If the source and/or
+destination names resolve to multiple addresses, the command will
+fail. Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain. If no chain is selected, all
+chains are listed. As every other iptables command, it applies to the
+specified table (filter is the default), so mangle rules get listed by
+.nf
+ ip6tables -t mangle -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed. The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ ip6tables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains. It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name. There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified. There must be no references
+to the chain. If there are, you must delete or replace the referring
+rules before the chain can be deleted. If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target. See the section
+.B TARGETS
+for the legal targets. Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name. This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR ipv6-icmp|icmpv6 ,
+or
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one. A protocol name from /etc/protocols is also allowed.
+A "!" argument before the protocol inverts the
+test. The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IPv6 address (with /mask), or a plain IPv6 address.
+(the network name isn't supported now).
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 64
+is equivalent to
+.IR ffff:ffff:ffff:ffff:0000:0000:0000:0000 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification.
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it. The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below). If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). When the "!" argument is used before the interface name, the
+sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD
+and
+.B OUTPUT
+chains). When the "!" argument is used before the interface name, the
+sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, any interface name will match.
+.TP
+.\" Currently not supported (header-based)
+.\"
+.\" .B "[!] " "-f, --fragment"
+.\" This means that the rule only refers to second and further fragments
+.\" of fragmented packets. Since there is no way to tell the source or
+.\" destination ports of such a packet (or ICMP type), such a packet will
+.\" not match any rules which specify them. When the "!" argument
+.\" precedes the "-f" flag, the rule will only match head fragments, or
+.\" unfragmented packets.
+.\" .TP
+.B "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output. This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks. The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M). This option is
+only relevant for the
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+ip6tables can use extended packet matching modules. These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module. You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.SS tcp
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification. The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified. The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set. Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ ip6tables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and RST bits
+cleared. Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.SS udp
+These extensions are loaded if `--protocol udp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
+.SS ipv6-icmp
+This extension is loaded if `--protocol ipv6-icmp' or `--protocol icmpv6' is
+specified. It provides the following option:
+.TP
+.BR "--icmpv6-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+IPv6-ICMP type, or one of the IPv6-ICMP type names shown by the command
+.nf
+ ip6tables -p ipv6-icmp -h
+.fi
+.SS mac
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address. It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used). It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
+.SS multiport
+This module matches a set of source or destination ports. Up to 15
+ports can be specified. It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the source port is one of the given ports. The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the destination port is one of the given ports. The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
+.SS mark
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a mask is
+specified, this is logically ANDed with the mask before the
+comparison).
+.SS owner
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets. It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match. This is regarded as experimental.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.\" .SS state
+.\" This module, when combined with connection tracking, allows access to
+.\" the connection tracking state for this packet.
+.\" .TP
+.\" .BI "--state " "state"
+.\" Where state is a comma separated list of the connection states to
+.\" match. Possible states are
+.\" .B INVALID
+.\" meaning that the packet is associated with no known connection,
+.\" .B ESTABLISHED
+.\" meaning that the packet is associated with a connection which has seen
+.\" packets in both directions,
+.\" .B NEW
+.\" meaning that the packet has started a new connection, or otherwise
+.\" associated with a connection which has not seen packets in both
+.\" directions, and
+.\" .B RELATED
+.\" meaning that the packet is starting a new connection, but is
+.\" associated with an existing connection, such as an FTP data transfer,
+.\" or an ICMP error.
+.\" .SS unclean
+.\" This module takes no options, but attempts to match packets which seem
+.\" malformed or unusual. This is regarded as experimental.
+.\" .SS tos
+.\" This module matches the 8 bits of Type of Service field in the IP
+.\" header (ie. including the precedence bits).
+.\" .TP
+.\" .BI "--tos " "tos"
+.\" The argument is either a standard name, (use
+.\" .br
+.\" iptables -m tos -h
+.\" .br
+.\" to see the list), or a numeric value to match.
+.SH TARGET EXTENSIONS
+ip6tables can use extended target modules: the following are included
+in the standard distribution.
+.SS LOG
+Turn on kernel logging of matching packets. When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IPv6 IPv6-header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule. So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IPv6 packet header.
+.SS MARK
+This is used to set the netfilter mark value associated with the
+packet. It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-mark " "mark"
+.SS REJECT
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains. The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp6-no-route"
+.B " no-route"
+.B " icmp6-adm-prohibited"
+.B " adm-prohibited"
+.B " icmp6-addr-unreachable"
+.B " addr-unreach"
+.B " icmp6-port-unreachable"
+.B " port-unreach"
+.fi
+which return the appropriate IPv6-ICMP error message (\fBport-unreach\fP is
+the default). Finally, the option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back. This is mainly useful for blocking
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.\" .SS TOS
+.\" This is used to set the 8-bit Type of Service field in the IP header.
+.\" It is only valid in the
+.\" .B mangle
+.\" table.
+.\" .TP
+.\" .BI "--set-tos " "tos"
+.\" You can use a numeric TOS values, or use
+.\" .br
+.\" iptables -j TOS -h
+.\" .br
+.\" to see the list of valid TOS names.
+.\" .SS MIRROR
+.\" This is an experimental demonstration target which inverts the source
+.\" and destination fields in the IP header and retransmits the packet.
+.\" It is only valid in the
+.\" .BR INPUT ,
+.\" .B FORWARD
+.\" and
+.\" .B PREROUTING
+.\" chains, and user-defined chains which are only called from those
+.\" chains. Note that the outgoing packets are
+.\" .B NOT
+.\" seen by any packet filtering chains, connection tracking or NAT, to
+.\" avoid loops and other problems.
+.\" .SS SNAT
+.\" This target is only valid in the
+.\" .B nat
+.\" table, in the
+.\" .B POSTROUTING
+.\" chain. It specifies that the source address of the packet should be
+.\" modified (and all future packets in this connection will also be
+.\" mangled), and rules should cease being examined. It takes one option:
+.\" .TP
+.\" .BR "--to-source " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+.\" which can specify a single new source IP address, an inclusive range
+.\" of IP addresses, and optionally, a port range (which is only valid if
+.\" the rule also specifies
+.\" .B "-p tcp"
+.\" or
+.\" .BR "-p udp" ).
+.\" If no port range is specified, then source ports below 512 will be
+.\" mapped to other ports below 512: those between 512 and 1023 inclusive
+.\" will be mapped to ports below 1024, and other ports will be mapped to
+.\" 1024 or above. Where possible, no port alteration will occur.
+.\" .SS DNAT
+.\" This target is only valid in the
+.\" .B nat
+.\" table, in the
+.\" .B PREROUTING
+.\" and
+.\" .B OUTPUT
+.\" chains, and user-defined chains which are only called from those
+.\" chains. It specifies that the destination address of the packet
+.\" should be modified (and all future packets in this connection will
+.\" also be mangled), and rules should cease being examined. It takes one
+.\" option:
+.\" .TP
+.\" .BR "--to-destination " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+.\" which can specify a single new destination IP address, an inclusive
+.\" range of IP addresses, and optionally, a port range (which is only
+.\" valid if the rule also specifies
+.\" .B "-p tcp"
+.\" or
+.\" .BR "-p udp" ).
+.\" If no port range is specified, then the destination port will never be
+.\" modified.
+.\" .SS MASQUERADE
+.\" This target is only valid in the
+.\" .B nat
+.\" table, in the
+.\" .B POSTROUTING
+.\" chain. It should only be used with dynamically assigned IP (dialup)
+.\" connections: if you have a static IP address, you should use the SNAT
+.\" target. Masquerading is equivalent to specifying a mapping to the IP
+.\" address of the interface the packet is going out, but also has the
+.\" effect that connections are
+.\" .I forgotten
+.\" when the interface goes down. This is the correct behavior when the
+.\" next dialup is unlikely to have the same interface address (and hence
+.\" any established connections are lost anyway). It takes one option:
+.\" .TP
+.\" .BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+.\" This specifies a range of source ports to use, overriding the default
+.\" .B SNAT
+.\" source port-selection heuristics (see above). This is only valid
+.\" if the rule also specifies
+.\" .B "-p tcp"
+.\" or
+.\" .BR "-p udp" .
+.\" .SS REDIRECT
+.\" This target is only valid in the
+.\" .B nat
+.\" table, in the
+.\" .B PREROUTING
+.\" and
+.\" .B OUTPUT
+.\" chains, and user-defined chains which are only called from those
+.\" chains. It alters the destination IP address to send the packet to
+.\" the machine itself (locally-generated packets are mapped to the
+.\" 127.0.0.1 address). It takes one option:
+.\" .TP
+.\" .BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+.\" This specifies a destination port or range of ports to use: without
+.\" this, the destination port is never altered. This is only valid
+.\" if the rule also specifies
+.\" .B "-p tcp"
+.\" or
+.\" .BR "-p udp" .
+.SH DIAGNOSTICS
+Various error messages are printed to standard error. The exit code
+is 0 for correct functioning. Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs? What's this? ;-)
+Well... the counters are not reliable on sparc64.
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B ip6tables
+is very similar to ipchains by Rusty Russell. The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively. Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.\" .PP The various forms of NAT have been separated out;
+.\" .B iptables
+.\" is a pure packet filter when using the default `filter' table, with
+.\" optional extension modules. This should simplify much of the previous
+.\" confusion over the combination of IP masquerading and packet filtering
+.\" seen previously. So the following options are handled differently:
+.\" .br
+.\" -j MASQ
+.\" .br
+.\" -M -S
+.\" .br
+.\" -M -L
+.\" .br
+There are several other changes in ip6tables.
+.SH SEE ALSO
+.BR ip6tables-save (8),
+.BR ip6tables-restore(8),
+.BR iptables (8),
+.BR iptables-save (8),
+.BR iptables-restore (8).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG target, TTL match+target and libipulog.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik,
+James Morris, Harald Welte and Rusty Russell.
+.PP
+ip6tables man page created by Andras Kis-Szabo, based on
+iptables man page written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
--- /dev/null
+EXTRAS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_pool.h ] && echo ippool/libippool.a ippool/ippool)
+
+ifndef TOPLEVEL_INCLUDED
+local:
+ cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=$(shell [ -f $(KERNEL_DIR)/include/linux/netfilter_ipv4/ip_pool.h ] && echo ippool/libippool.d)
+
+ippool/libippool.a: ippool/libippool.a(ippool/libippool.o)
+
+ippool/libippool.d: %.d: %.c
+ @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.a($*.o):@' > $@
+
+ippool/ippool.o: ippool/ippool.c
+ippool/ippool: ippool/ippool.o ippool/libippool.a
+ $(CC) $(CFLAGS) -o $@ $^
+endif
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include <linux/netfilter_ipv4/ip_pool.h>
+#include <libippool/ip_pool_support.h>
+
+/* translate errnos, as returned by our *sockopt() functions */
+static char *errno2msg(int op, int err)
+{
+ switch(err) {
+ case ERANGE:
+ return "Address out of pool range";
+ }
+ return strerror(err);
+}
+
+static void usage(char *msg)
+{
+ if (msg) fprintf(stderr, "ERROR: %s\n", msg);
+ fprintf(stderr,
+"Usage: ippool [-LADCNF] [POOL] [args]\n"
+"The POOL argument is either a number, or a name from %s\n"
+, IPPOOL_CONF);
+ fprintf(stderr,
+"ippool [-n] [-l|-L POOL] [-u] [-v]\n"
+ "\tLists all (-l), or a single (-L) pool.\n"
+ "\tWith -u, summarized usage counts are listed.\n"
+ "\tWith -v, each pool membership is shown, one per line.\n"
+"ippool [-n] [-f|-F [POOL]]\n"
+ "\tflushes POOL (or all pools.)\n"
+"ippool [-n] [-x|-X [POOL]]\n"
+ "\tremoves POOL (or all pools.)\n"
+"ippool [-n] -B\n"
+ "\tcreates all fully specified pools found in the config file.\n"
+"ippool [-n] -N POOL [-t type] [FIRST LAST]\n"
+ "\tcreates POOL of IP addresses FIRST to LAST (inclusive). If a\n"
+ "\tpool name from the config file %s is used, type and\n"
+ "\taddress information can be defined there. The -t argument\n"
+ "\tgives the type (default bitmap).\n"
+"ippool [-n] -A POOL ADDR\n"
+ "\tadds ADDR to POOL\n"
+"ippool [-n] -D POOL ADDR\n"
+ "\tremoves ADDR from POOL\n"
+"ippool [-n] -C POOL ADDR\n"
+ "\ttests ADDR against membership in POOL\n"
+, IPPOOL_CONF);
+ exit(1);
+}
+
+/* config file parsing */
+
+#define IP_POOL_T_NONE 0
+#define IP_POOL_T_BITMAP 1
+
+static int conf_type = IP_POOL_T_NONE;
+static unsigned long conf_addr = 0;
+static unsigned long conf_addr2 = 0;
+
+#define SCAN_EOF (IP_POOL_NONE-1)
+
+static ip_pool_t get_index_line(
+ FILE *fp,
+ char **namep,
+ char **typep,
+ char **argp
+) {
+ char *p;
+ ip_pool_t index;
+ static char buf[256];
+
+ if (namep) *namep = 0;
+ if (typep) *typep = 0;
+ if (argp) *argp = 0;
+
+ if (!fgets(buf, sizeof(buf), fp)) return SCAN_EOF;
+
+ p = strtok(buf, " \t\n");
+ if (!p || *p == '#') return IP_POOL_NONE;
+ index = atoi(p);
+
+ p = strtok(0, " \t\n");
+ if (!p || *p == '#') return index;
+ if (namep) *namep = p;
+
+ p = strtok(0, " \t\n");
+ if (!p || *p == '#') return index;
+ if (typep) *typep = p;
+
+ p = strtok(0, "#\n");
+ if (argp) *argp = p;
+
+ return index;
+}
+
+static ip_pool_t get_index(char *name)
+{
+ FILE *fp;
+ char *poolname, *type, *arg, *p;
+ ip_pool_t res;
+
+ if (isdigit(*name))
+ return atoi(name);
+ fp = fopen(IPPOOL_CONF, "r");
+ if (!fp) {
+ fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+ exit(1);
+ }
+ while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+ if (poolname && 0 == strcmp(poolname, name)) {
+ if (!type || (0 == strcmp(type, "bitmap"))) {
+ conf_type = IP_POOL_T_BITMAP;
+ p = strtok(arg, " \t");
+ if (p) {
+ conf_addr = inet_addr(p);
+ p = strtok(0, " \t");
+ if (p)
+ conf_addr2 = inet_addr(p);
+ else
+ conf_addr = 0;
+ }
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ if (res == SCAN_EOF) {
+ fprintf(stderr, "pool '%s' not found in %s\n",
+ name, IPPOOL_CONF);
+ exit(1);
+ }
+ return res;
+}
+
+static char *get_name(ip_pool_t index)
+{
+ FILE *fp;
+ static char sbuf[256];
+ int ok = 0;
+
+ fp = fopen(IPPOOL_CONF, "r");
+ if (fp) {
+ while (fgets(sbuf, sizeof(sbuf), fp)) {
+ char *p = strtok(sbuf, " \t\n");
+
+ if (!p || *p == '#') continue;
+ if (index != atoi(p)) continue;
+ p = strtok(0, " \t\n");
+ if (!p || *p == '#') continue;
+ memmove(sbuf, p, strlen(p)+1);
+ ok = 1;
+ break;
+ }
+ fclose(fp);
+ }
+ if (!ok) sprintf(sbuf, "%d", index);
+ return sbuf;
+}
+
+/* user/kernel interaction */
+
+static int fd = -1;
+static int flag_n = 0; /* do not do anything; just brag about it */
+static int flag_v = 0; /* be verbose (list members) */
+static int flag_q = 0; /* be quiet */
+static int flag_u = 0; /* show usage counts in listings */
+static char *flag_t = "bitmap"; /* pool type */
+
+static ip_pool_t high_nr(void)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+
+ req.op = IP_POOL_HIGH_NR;
+ if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ fprintf(stderr,
+ "IP_POOL_HIGH_NR failed: %s\n",
+ errno2msg(IP_POOL_HIGH_NR, errno));
+ exit(1);
+ }
+ return req.index;
+}
+
+static void do_list(ip_pool_t pool)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+ u_int32_t first_ip;
+ u_int32_t last_ip;
+
+ req.op = IP_POOL_LOOKUP;
+ req.index = pool;
+ req.addr = req.addr2 = 0;
+ reqlen = sizeof(req);
+ if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ struct in_addr ina;
+ ina.s_addr = req.addr;
+ printf("%s %s", get_name(req.index), inet_ntoa(ina));
+ ina.s_addr = req.addr2;
+ printf(" %s", inet_ntoa(ina));
+ first_ip = ntohl(req.addr);
+ last_ip = ntohl(req.addr2);
+ if (flag_u) {
+ req.op = IP_POOL_USAGE;
+ req.index = pool;
+ reqlen = sizeof(req);
+ if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+ &req, &reqlen)) {
+ printf(" %d %d", req.addr, req.addr2);
+ } else {
+ printf(" - -");
+ }
+ }
+ printf("\n");
+ if (flag_v) {
+ while (first_ip <= last_ip) {
+ req.op = IP_POOL_TEST_ADDR;
+ req.index = pool;
+ ina.s_addr = req.addr = htonl(first_ip);
+ reqlen = sizeof(req);
+ if (0 == getsockopt(fd, SOL_IP, SO_IP_POOL,
+ &req, &reqlen)) {
+ if (req.addr) printf("\t%s\n",
+ inet_ntoa(ina));
+ }
+ first_ip++;
+ }
+ }
+ }
+}
+
+static void do_list_all(void)
+{
+ ip_pool_t i, highest = high_nr();
+
+ for (i=0; i<=highest; i++)
+ do_list(i);
+}
+
+static void do_flush(ip_pool_t pool)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+
+ req.op = IP_POOL_FLUSH;
+ req.index = pool;
+ if (flag_n) {
+ printf("ippool -F %d\n", req.index);
+ return;
+ }
+ if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ int err = errno;
+ fprintf(stderr,
+ "IP_POOL_FLUSH %s failed: %s\n",
+ get_name(pool), errno2msg(IP_POOL_FLUSH, err));
+ exit(1);
+ }
+}
+
+static void do_flush_all(void)
+{
+ ip_pool_t i, highest = high_nr();
+
+ for (i=0; i<=highest; i++)
+ do_flush(i);
+}
+
+static void do_destroy(ip_pool_t pool)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+
+ req.op = IP_POOL_DESTROY;
+ req.index = pool;
+ if (flag_n) {
+ printf("ippool -X %d\n", req.index);
+ return;
+ }
+ if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ int err = errno;
+ fprintf(stderr,
+ "IP_POOL_DESTROY %s failed: %s\n",
+ get_name(pool), errno2msg(IP_POOL_DESTROY, err));
+ exit(1);
+ }
+}
+
+static void do_destroy_all(void)
+{
+ ip_pool_t i, highest = high_nr();
+
+ for (i=0; i<=highest; i++)
+ do_destroy(i);
+}
+
+static int do_adddel(ip_pool_t pool, char *straddr, int op)
+{
+ struct ip_pool_request req;
+ int res;
+ int reqlen = sizeof(req);
+
+ req.op = op;
+ req.index = pool;
+ req.addr = inet_addr(straddr);
+ req.addr2 = 0;
+ if (flag_n) {
+ printf("ippool -%c %s %s\n",
+ (op == IP_POOL_ADD_ADDR) ? 'A' : 'D',
+ get_name(req.index), straddr);
+ return 0;
+ }
+ res = getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen);
+ if (0 > res) {
+ int err = errno;
+ fprintf(stderr,
+ "IP_POOL_ADD/DEL %s in %s failed: %s\n",
+ straddr, get_name(pool), errno2msg(op, err));
+ exit(1);
+ }
+ if (!flag_q)
+ printf("%s %s %d %d\n", get_name(pool), straddr, req.addr,
+ op == IP_POOL_ADD_ADDR ? 1 : 0);
+ return req.addr;
+}
+
+static int do_check(ip_pool_t pool, char *straddr)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+
+ req.op = IP_POOL_TEST_ADDR;
+ req.index = pool;
+ req.addr = inet_addr(straddr);
+ if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ int err = errno;
+ fprintf(stderr,
+ "IP_POOL_TEST_ADDR %s in %s failed: %s\n",
+ straddr, get_name(pool),
+ errno2msg(IP_POOL_TEST_ADDR, err));
+ exit(1);
+ }
+ if (!flag_q)
+ printf("%s %s %d\n", get_name(req.index), straddr, req.addr);
+ return !req.addr;
+}
+
+static void do_new(ip_pool_t pool, int argc, char **argv)
+{
+ struct ip_pool_request req;
+ int reqlen = sizeof(req);
+
+ req.op = IP_POOL_INIT;
+ req.index = pool;
+ if (argc >= 2) {
+ conf_type = IP_POOL_T_BITMAP;
+ conf_addr = inet_addr(argv[0]);
+ conf_addr2 = inet_addr(argv[1]);
+ }
+ if (conf_type != IP_POOL_T_BITMAP || conf_addr == 0 || conf_addr2 == 0)
+ usage("bad pool specification");
+ req.addr = conf_addr;
+ req.addr2 = conf_addr2;
+ if (flag_n) {
+ printf("ippool -N %s [-T %s] ...\n", get_name(pool),
+ conf_type == IP_POOL_T_BITMAP
+ ? "bitmap"
+ : "???");
+ return;
+ }
+ if (0 > getsockopt(fd, SOL_IP, SO_IP_POOL, &req, &reqlen)) {
+ struct in_addr ina;
+ int err = errno;
+ ina.s_addr = conf_addr;
+ fprintf(stderr,
+ "IP_POOL_INIT %s [%s-",
+ get_name(pool), inet_ntoa(ina));
+ ina.s_addr = conf_addr2;
+ fprintf(stderr,
+ "%s] failed: %s\n",
+ inet_ntoa(ina), errno2msg(IP_POOL_INIT, err));
+ exit(1);
+ }
+}
+
+static void do_new_all(void)
+{
+ FILE *fp;
+ char *poolname, *type, *arg, *p;
+ int res;
+
+ fp = fopen(IPPOOL_CONF, "r");
+ if (!fp) {
+ fprintf(stderr, "cannot open %s - no pool names", IPPOOL_CONF);
+ exit(1);
+ }
+ while (SCAN_EOF != (res=get_index_line(fp, &poolname, &type, &arg))) {
+ if (poolname && type && (0 == strcmp(type, "bitmap"))) {
+ conf_type = IP_POOL_T_BITMAP;
+ p = strtok(arg, " \t");
+ if (p) {
+ conf_addr = inet_addr(p);
+ p = strtok(0, " \t");
+ if (p)
+ conf_addr2 = inet_addr(p);
+ else
+ conf_addr = 0;
+ }
+ if (conf_addr != 0) {
+ if (flag_v)
+ printf("ippool -N %s (%s) [%s]\n",
+ poolname, type, arg);
+ do_new(res, 0, 0);
+ }
+ }
+ }
+ fclose(fp);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int op;
+#define OP_NONE 0
+#define OP_LIST 1
+#define OP_LIST_ALL 2
+#define OP_FLUSH 3
+#define OP_FLUSH_ALL 4
+#define OP_DESTROY 5
+#define OP_DESTROY_ALL 6
+#define OP_ADD 7
+#define OP_DEL 8
+#define OP_CHECK 9
+#define OP_NEW 10
+#define OP_NEW_ALL 11
+#define OP_HIGHEST 12
+ char *op_pool;
+
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "cannot get DGRAM socket: %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ op_pool = 0;
+ op = OP_NONE;
+ /* GRRR. I thought getopt() would allow an "L*" specifier, for an -L
+ * taking an optional argument. Does not work. Bad.
+ * Adding -l for -L without argument, also -f/-F and -x/-X.
+ */
+ while (EOF != (opt=getopt( argc, argv, "HhnvuqA:D:C:N:t:L:F:X:lfxB")))
+ switch(opt) {
+ case 'l':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_LIST_ALL;
+ break;
+ case 'L':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_LIST;
+ op_pool = optarg;
+ break;
+ case 'f':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_FLUSH_ALL;
+ break;
+ case 'F':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_FLUSH;
+ op_pool = optarg;
+ break;
+ case 'x':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_DESTROY_ALL;
+ break;
+ case 'X':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_DESTROY;
+ op_pool = optarg;
+ break;
+ case 'A':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_ADD;
+ op_pool = optarg;
+ break;
+ case 'D':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_DEL;
+ op_pool = optarg;
+ break;
+ case 'C':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_CHECK;
+ op_pool = optarg;
+ break;
+ case 'B':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_NEW_ALL;
+ break;
+ case 'N':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_NEW;
+ op_pool = optarg;
+ break;
+ case 'H':
+ if (op != OP_NONE) usage("conflicting operations");
+ op = OP_HIGHEST;
+ break;
+ case 't':
+ flag_t = optarg;
+ break;
+ case 'n':
+ flag_n = 1;
+ break;
+ case 'v':
+ flag_v = 1;
+ break;
+ case 'u':
+ flag_u = 1;
+ break;
+ case 'q':
+ flag_q = 1;
+ break;
+ case 'h':
+ usage(0);
+ default:
+ usage("bad option");
+ }
+ if (op == OP_NONE)
+ usage("no operation specified");
+ if (op == OP_LIST_ALL) {
+ do_list_all();
+ return 0;
+ }
+ if (op == OP_LIST) {
+ do_list(get_index(op_pool));
+ return 0;
+ }
+ if (op == OP_FLUSH_ALL) {
+ do_flush_all();
+ return 0;
+ }
+ if (op == OP_FLUSH) {
+ do_flush(get_index(op_pool));
+ return 0;
+ }
+ if (op == OP_DESTROY_ALL) {
+ do_destroy_all();
+ return 0;
+ }
+ if (op == OP_DESTROY) {
+ do_destroy(get_index(op_pool));
+ return 0;
+ }
+ if (op == OP_CHECK) {
+ if (optind >= argc)
+ usage("missing address to check");
+ return do_check(get_index(op_pool), argv[optind]);
+ }
+ if (op == OP_NEW_ALL) {
+ do_new_all();
+ return 0;
+ }
+ if (op == OP_NEW) {
+ do_new(get_index(op_pool), argc-optind, argv+optind);
+ return 0;
+ }
+ if (op == OP_ADD) {
+ if (optind >= argc)
+ usage("missing address to add");
+ return do_adddel(get_index(op_pool),
+ argv[optind], IP_POOL_ADD_ADDR);
+ }
+ if (op == OP_DEL) {
+ if (optind >= argc)
+ usage("missing address to delete");
+ return do_adddel(get_index(op_pool),
+ argv[optind], IP_POOL_DEL_ADDR);
+ }
+ if (op == OP_HIGHEST) {
+ printf("%d\n", high_nr());
+ return 0;
+ }
+ usage("no operation specified");
+ return 0;
+}
--- /dev/null
+/* support functions for ip_pool modules */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include <libippool/ip_pool_support.h>
+
+void ip_pool_init(void)
+{
+}
+
+ip_pool_t ip_pool_get_index(char *name)
+{
+ FILE *fp;
+ char buf[256];
+
+ if (isdigit(*name))
+ return atoi(name);
+ fp = fopen(IPPOOL_CONF, "r");
+ if (!fp) exit_error(PARAMETER_PROBLEM,
+ "cannot open %s - no pool names", IPPOOL_CONF);
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p = strtok(buf, " \t\n");
+ ip_pool_t index;
+
+ if (!p || *p == '#') continue;
+ index = atoi(p);
+ p = strtok(0, " \t\n");
+ if (p && 0 == strcmp(p, name)) {
+ fclose(fp);
+ return index;
+ }
+ }
+ exit_error(PARAMETER_PROBLEM,
+ "pool '%s' not found in %s\n", name, IPPOOL_CONF);
+}
+
+char *ip_pool_get_name(char *buf, int size, ip_pool_t index, int numeric)
+{
+ FILE *fp;
+ int ok = 0;
+
+ /* make sure we have enough room, at least for a %d */
+ if (size < 16)
+ exit_error(PARAMETER_PROBLEM,
+ "ip_pool_support:get_name buf too small (%d vs. 16)\n",
+ size);
+ if (numeric)
+ goto do_numeric;
+ fp = fopen(IPPOOL_CONF, "r");
+ if (fp) {
+ while (fgets(buf, size, fp)) {
+ char *p = strtok(buf, " \t\n");
+
+ if (!p || *p == '#') continue;
+ if (index != atoi(p)) continue;
+ p = strtok(0, " \t\n");
+ if (!p || *p == '#') continue;
+ memmove(buf, p, strlen(p)+1);
+ ok = 1;
+ break;
+ }
+ fclose(fp);
+ }
+ if (!ok) {
+do_numeric:
+ sprintf(buf, "%d", index);
+ }
+ return buf;
+}
--- /dev/null
+# 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"
--- /dev/null
+.TH IPTABLES 8 "Mar 09, 2002" "" ""
+.\"
+.\" Man page written by Herve Eychenne <rv@wallfire.org> (May 1999)
+.\" It is based on ipchains page.
+.\" TODO: add a word for protocol helpers (FTP, IRC, SNMP-ALG)
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl>
+.\"
+.\" 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.
+.\"
+.\"
+.SH NAME
+iptables \- administration tool for IPv4 packet filtering and NAT
+.SH SYNOPSIS
+.BR "iptables [-t table] -[AD] " "chain rule-specification [options]"
+.br
+.BR "iptables [-t table] -I " "chain [rulenum] rule-specification [options]"
+.br
+.BR "iptables [-t table] -R " "chain rulenum rule-specification [options]"
+.br
+.BR "iptables [-t table] -D " "chain rulenum [options]"
+.br
+.BR "iptables [-t table] -[LFZ] " "[chain] [options]"
+.br
+.BR "iptables [-t table] -N " "chain"
+.br
+.BR "iptables [-t table] -X " "[chain]"
+.br
+.BR "iptables [-t table] -P " "chain target [options]"
+.br
+.BR "iptables [-t table] -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Iptables
+is used to set up, maintain, and inspect the tables of IP packet
+filter rules in the Linux kernel. Several different tables
+may be defined. Each table contains a number of built-in
+chains and may also contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets. Each
+rule specifies what to do with a packet that matches. This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target. If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain or one of the
+special values
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace (if supported by the kernel).
+.I RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain. If the end of a built-in chain is reached
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are currently three independent tables (which tables are present
+at any time depends on the kernel configuration options and which
+modules are present).
+.TP
+.BI "-t, --table " "table"
+This option specifies the packet matching table which the command
+should operate on. If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.RS
+.TP .4i
+.BR "filter" :
+This is the default table (if no -t option is passed). It contains
+the built-in chains
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for packets being routed through the box), and
+.B OUTPUT
+(for locally-generated packets).
+.TP
+.BR "nat" :
+This table is consulted when a packet that creates a new
+connection is encountered. It consists of three built-ins:
+.B PREROUTING
+(for altering packets as soon as they come in),
+.B OUTPUT
+(for altering locally-generated packets before routing), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.TP
+.BR "mangle" :
+This table is used for specialized packet alteration. Until kernel
+2.4.17 it had two built-in chains:
+.B PREROUTING
+(for altering incoming packets before routing) and
+.B OUTPUT
+(for altering locally-generated packets before routing).
+Since kernel 2.4.18, three other built-in chains are also supported:
+.B INPUT
+(for packets coming into the box itself),
+.B FORWARD
+(for altering packets being routed through the box), and
+.B POSTROUTING
+(for altering packets as they are about to go out).
+.RE
+.SH OPTIONS
+The options that are recognized by
+.B iptables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform. Only one of them
+can be specified on the command line unless otherwise specified
+below. For all the long versions of the command and option names, you
+need to use only enough letters to ensure that
+.B iptables
+can differentiate it from all other options.
+.TP
+.BI "-A, --append " "chain rule-specification"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BI "-D, --delete " "chain rule-specification"
+.ns
+.TP
+.BI "-D, --delete " "chain rulenum"
+Delete one or more rules from the selected chain. There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.BR "-I, --insert " "\fIchain\fP [\fIrulenum\fP] \fIrule-specification\fP"
+Insert one or more rules in the selected chain as the given rule
+number. So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain. This is also the default if no rule number
+is specified.
+.TP
+.BI "-R, --replace " "chain rulenum rule-specification"
+Replace a rule in the selected chain. If the source and/or
+destination names resolve to multiple addresses, the command will
+fail. Rules are numbered starting at 1.
+.TP
+.BR "-L, --list " "[\fIchain\fP]"
+List all rules in the selected chain. If no chain is selected, all
+chains are listed. As every other iptables command, it applies to the
+specified table (filter is the default), so NAT rules get listed by
+.nf
+ iptables -t nat -n -L
+.fi
+Please note that it is often used with the
+.B -n
+option, in order to avoid long reverse DNS lookups.
+It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed. The exact output is affected by the other
+arguments given. The exact rules are suppressed until you use
+.nf
+ iptables -L -v
+.fi
+.TP
+.BR "-F, --flush " "[\fIchain\fP]"
+Flush the selected chain (all the chains in the table if none is given).
+This is equivalent to deleting all the rules one by one.
+.TP
+.BR "-Z, --zero " "[\fIchain\fP]"
+Zero the packet and byte counters in all chains. It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared. (See above.)
+.TP
+.BI "-N, --new-chain " "chain"
+Create a new user-defined chain by the given name. There must be no
+target of that name already.
+.TP
+.BR "-X, --delete-chain " "[\fIchain\fP]"
+Delete the optional user-defined chain specified. There must be no references
+to the chain. If there are, you must delete or replace the referring
+rules before the chain can be deleted. If no argument is given, it
+will attempt to delete every non-builtin chain in the table.
+.TP
+.BI "-P, --policy " "chain target"
+Set the policy for the chain to the given target. See the section
+.B TARGETS
+for the legal targets. Only built-in (non-user-defined) chains can have
+policies, and neither built-in nor user-defined chains can be policy
+targets.
+.TP
+.BI "-E, --rename-chain " "old-chain new-chain"
+Rename the user specified chain to the user supplied name. This is
+cosmetic, and has no effect on the structure of the table.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, insert, replace and append commands).
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmp ,
+or
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one. A protocol name from /etc/protocols is also allowed.
+A "!" argument before the protocol inverts the
+test. The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a network name, a hostname (please note that specifying
+any name to be resolved with a remote query such as DNS is a really bad idea),
+a network IP address (with /mask), or a plain IP address.
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 24
+is equivalent to
+.IR 255.255.255.0 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification.
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; i.e., what to do if the packet
+matches it. The target can be a user-defined chain (other than the
+one this rule is in), one of the special builtin targets which decide
+the fate of the packet immediately, or an extension (see
+.B EXTENSIONS
+below). If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). When the "!" argument is used before the interface name, the
+sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, any interface name will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+Name of an interface via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains). When the "!" argument is used before the interface name, the
+sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, any interface name will match.
+.TP
+.B "[!] " "-f, --fragment"
+This means that the rule only refers to second and further fragments
+of fragmented packets. Since there is no way to tell the source or
+destination ports of such a packet (or ICMP type), such a packet will
+not match any rules which specify them. When the "!" argument
+precedes the "-f" flag, the rule will only match head fragments, or
+unfragmented packets.
+.TP
+.BI "-c, --set-counters " "PKTS BYTES"
+This enables the administrator to initialize the packet and byte
+counters of a rule (during
+.B INSERT,
+.B APPEND,
+.B REPLACE
+operations).
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output. This option makes the list command show the interface
+name, the rule options (if any), and the TOS masks. The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M). This option is
+only relevant for the
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.TP
+.B "--modprobe=command"
+When adding or inserting rules into a chain, use
+.B command
+to load any necessary modules (targets, match extensions, etc).
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules. These are loaded
+in two ways: implicitly, when
+.B -p
+or
+.B --protocol
+is specified, or with the
+.B -m
+or
+.B --match
+options, followed by the matching module name; after these, various
+extra command line options become available, depending on the specific
+module. You can specify multiple extended match modules in one line,
+and you can use the
+.B -h
+or
+.B --help
+options after the module has been specified to receive help specific
+to that module.
+
+The following are included in the base package, and most of these can
+be preceded by a
+.B !
+to invert the sense of the match.
+.SS ah
+This module matches the SPIs in AH header of IPSec packets.
+.TP
+.BR "--ahspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS conntrack
+This module, when combined with connection tracking, allows access to
+more connection tracking information than the "state" match.
+(this module is present only if iptables was compiled under a kernel
+supporting this feature)
+.TP
+.BI "--ctstate " "state"
+Where state is a comma separated list of the connection states to
+match. Possible states are
+.B INVALID
+meaning that the packet is associated with no known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.B SNAT
+A virtual state, matching if the original source address differs from
+the reply destination.
+.B DNAT
+A virtual state, matching if the original destination differs from the
+reply source.
+.TP
+.BI "--ctproto " "proto"
+Protocol to match (by number or name)
+.TP
+.BI "--ctorigsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original source address
+.TP
+.BI "--ctorigdst " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against original destination address
+.TP
+.BI "--ctreplsrc " "[!] \fIaddress\fP[/\fImask\fP]"
+Match against reply source address
+.TP
+.BI "--ctrepldst " "[!] \fIaddress\fB[/\fImask\fP]"
+Match against reply destination address
+.TP
+.BI "--ctstatus " "[\fINONE|EXPECTED|SEEN_REPLY|ASSURED\fP][,...]"
+Match against internal conntrack states
+.TP
+.BI "--ctexpire " "\fItime\fP[\fI:time\fP]"
+Match remaining lifetime in seconds against given value
+or range of values (inclusive)
+.SS dscp
+This module matches the 6 bit DSCP field within the TOS field in the
+IP header. DSCP has superseded TOS within the IETF.
+.TP
+.BI "--dscp " "value"
+Match against a numeric (decimal or hex) value [0-32].
+.TP
+.BI "--dscp-class " "\fIDiffServ Class\fP"
+Match the DiffServ class. This value may be any of the
+BE, EF, AFxx or CSx classes. It will then be converted
+into it's according numeric value.
+.SS esp
+This module matches the SPIs in ESP header of IPSec packets.
+.TP
+.BR "--espspi " "[!] \fIspi\fP[:\fIspi\fP]"
+.SS helper
+This module matches packets related to a specific conntrack-helper.
+.TP
+.BI "--helper " "string"
+Matches packets related to the specified conntrack-helper.
+.RS
+.PP
+string can be "ftp" for packets related to a ftp-session on default port.
+For other ports append -portnr to the value, ie. "ftp-2121".
+.PP
+Same rules apply for other conntrack-helpers.
+.RE
+.SS icmp
+This extension is loaded if `--protocol icmp' is specified. It
+provides the following option:
+.TP
+.BR "--icmp-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, or one of the ICMP type names shown by the command
+.nf
+ iptables -p icmp -h
+.fi
+.SS length
+This module matches the length of a packet against a specific value
+or range of values.
+.TP
+.BR "--length " "\fIlength\fP[:\fIlength\fP]"
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached
+(unless the `!' flag is used). It can be used in combination with the
+.B LOG
+target to give limited logging, for example.
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+Maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
+.SS mac
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address. It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets coming from an Ethernet device
+and entering the
+.BR PREROUTING ,
+.B FORWARD
+or
+.B INPUT
+chains.
+.SS mark
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BR "--mark " "\fIvalue\fP[/\fImask\fP]"
+Matches packets with the given unsigned mark value (if a mask is
+specified, this is logically ANDed with the mask before the
+comparison).
+.SS multiport
+This module matches a set of source or destination ports. Up to 15
+ports can be specified. It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the source port is one of the given ports. The flag
+.B --sports
+is a convenient alias for this option.
+.TP
+.BR "--destination-ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the destination port is one of the given ports. The flag
+.B --dports
+is a convenient alias for this option.
+.TP
+.BR "--ports " "\fIport\fP[,\fIport\fP[,\fIport\fP...]]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
+.SS owner
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets. It is only valid in the
+.B OUTPUT
+chain, and even this some packets (such as ICMP ping responses) may
+have no owner, and hence never match.
+.TP
+.BI "--uid-owner " "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner " "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner " "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner " "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.TP
+.BI "--cmd-owner " "name"
+Matches if the packet was created by a process with the given command name.
+(this option is present only if iptables was compiled under a kernel
+supporting this feature)
+.SS physdev
+This module matches on the bridge port input and output devices enslaved
+to a bridge device. This module is a part of the infrastructure that enables
+a transparent bridging IP firewall and is only useful for kernel versions
+above version 2.5.44.
+.TP
+.B --physdev-in name
+Name of a bridge port via which a packet is received (only for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. If the packet didn't arrive
+through a bridge device, this packet won't match this option, unless '!' is used.
+.TP
+.B --physdev-out name
+Name of a bridge port via which a packet is going to be sent (for packets
+entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains). If the interface name ends in a "+", then any
+interface which begins with this name will match. Note that in the
+.BR nat " and " mangle
+.B OUTPUT
+chains one cannot match on the bridge output port, however one can in the
+.B "filter OUTPUT"
+chain. If the packet won't leave by a bridge device or it is yet unknown what
+the output device will be, then the packet won't match this option, unless
+'!' is used.
+.TP
+.B --physdev-is-in
+Matches if the packet has entered through a bridge interface.
+.TP
+.B --physdev-is-out
+Matches if the packet will leave through a bridge interface.
+.TP
+.B --physdev-is-bridged
+Matches if the packet is being bridged and therefore is not being routed.
+This is only useful in the FORWARD and POSTROUTING chains.
+.SS pkttype
+This module matches the link-layer packet type.
+.TP
+.BI "--pkt-type " "[\fIunicast\fP|\fIbroadcast\fP|\fImulticast\fP]"
+.SS state
+This module, when combined with connection tracking, allows access to
+the connection tracking state for this packet.
+.TP
+.BI "--state " "state"
+Where state is a comma separated list of the connection states to
+match. Possible states are
+.B INVALID
+meaning that the packet could not be identified for some reason which
+includes running out of memory and ICMP errors which don't correspond to any
+known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.SS tcp
+These extensions are loaded if `--protocol tcp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is a convenient alias for this option.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification. The flag
+.B --dport
+is a convenient alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified. The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set. Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.fi
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and RST bits
+cleared. Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.TP
+.BR "--mss " "\fIvalue\fP[:\fIvalue\fP]"
+Match TCP SYN or SYN/ACK packets with the specified MSS value (or range),
+which control the maximum packet size for that connection.
+.SS tos
+This module matches the 8 bits of Type of Service field in the IP
+header (ie. including the precedence bits).
+.TP
+.BI "--tos " "tos"
+The argument is either a standard name, (use
+.br
+ iptables -m tos -h
+.br
+to see the list), or a numeric value to match.
+.SS ttl
+This module matches the time to live field in the IP header.
+.TP
+.BI "--ttl " "ttl"
+Matches the given TTL value.
+.SS udp
+These extensions are loaded if `--protocol udp' is specified. It
+provides the following options:
+.TP
+.BR "--source-port " "[!] \fIport\fP[:\fIport\fP]"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] \fIport\fP[:\fIport\fP]"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
+.SS unclean
+This module takes no options, but attempts to match packets which seem
+malformed or unusual. This is regarded as experimental.
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.SS DNAT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains. It specifies that the destination address of the packet
+should be modified (and all future packets in this connection will
+also be mangled), and rules should cease being examined. It takes one
+type of option:
+.TP
+.BR "--to-destination " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new destination IP address, an inclusive
+range of IP addresses, and optionally, a port range (which is only
+valid if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then the destination port will never be
+modified.
+.RS
+.PP
+You can add several --to-destination options. If you specify more
+than one destination address, either via an address range or multiple
+--to-destination options, a simple round-robin (one after another in
+cycle) load balancing takes place between these adresses.
+.SS DSCP
+This target allows to alter the value of the DSCP bits within the TOS
+header of the IPv4 packet. As this manipulates a packet, it can only
+be used in the mangle table.
+.TP
+.BI "--set-dscp " "value"
+Set the DSCP field to a numerical value (can be decimal or hex)
+.TP
+.BI "--set-dscp-class " "class"
+Set the DSCP field to a DiffServ class.
+.SS ECN
+This target allows to selectively work around known ECN blackholes.
+It can only be used in the mangle table.
+.TP
+.BI "--ecn-tcp-remove"
+Remove all ECN bits from the TCP header. Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+.SS LOG
+Turn on kernel logging of matching packets. When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IP header fields) via the kernel log
+(where it can be read with
+.I dmesg
+or
+.IR syslogd (8)).
+This is a "non-terminating target", i.e. rule traversal continues at
+the next rule. So if you want to LOG the packets you refuse, use two
+separate rules with the same matching criteria, first using target LOG
+then DROP (or REJECT).
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 29 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IP packet header.
+.SS MARK
+This is used to set the netfilter mark value associated with the
+packet. It is only valid in the
+.B mangle
+table. It can for example be used in conjunction with iproute2.
+.TP
+.BI "--set-mark " "mark"
+.SS MASQUERADE
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain. It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target. Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down. This is the correct behavior when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway). It takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a range of source ports to use, overriding the default
+.B SNAT
+source port-selection heuristics (see above). This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.SS MIRROR
+This is an experimental demonstration target which inverts the source
+and destination fields in the IP header and retransmits the packet.
+It is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains, and user-defined chains which are only called from those
+chains. Note that the outgoing packets are
+.B NOT
+seen by any packet filtering chains, connection tracking or NAT, to
+avoid loops and other problems.
+.SS REDIRECT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains. It alters the destination IP address to send the packet to
+the machine itself (locally-generated packets are mapped to the
+127.0.0.1 address). It takes one option:
+.TP
+.BR "--to-ports " "\fIport\fP[-\fIport\fP]"
+This specifies a destination port or range of ports to use: without
+this, the destination port is never altered. This is only valid
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" .
+.SS REJECT
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.B DROP
+so it is a terminating TARGET, ending rule traversal.
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains, and user-defined chains which are only called from those
+chains. The following option controls the nature of the error packet
+returned:
+.TP
+.BI "--reject-with " "type"
+The type given can be
+.nf
+.B " icmp-net-unreachable"
+.B " icmp-host-unreachable"
+.B " icmp-port-unreachable"
+.B " icmp-proto-unreachable"
+.B " icmp-net-prohibited"
+.B " icmp-host-prohibited or"
+.B " icmp-admin-prohibited (*)"
+.fi
+which return the appropriate ICMP error message (\fBport-unreachable\fP is
+the default). The option
+.B tcp-reset
+can be used on rules which only match the TCP protocol: this causes a
+TCP RST packet to be sent back. This is mainly useful for blocking
+.I ident
+(113/tcp) probes which frequently occur when sending mail to broken mail
+hosts (which won't accept your mail otherwise).
+.TP
+(*) Using icmp-admin-prohibited with kernels that do not support it will result in a plain DROP instead of REJECT
+.SS SNAT
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain. It specifies that the source address of the packet should be
+modified (and all future packets in this connection will also be
+mangled), and rules should cease being examined. It takes one type
+of option:
+.TP
+.BR "--to-source " "\fIipaddr\fP[-\fIipaddr\fP][:\fIport\fP-\fIport\fP]"
+which can specify a single new source IP address, an inclusive range
+of IP addresses, and optionally, a port range (which is only valid if
+the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then source ports below 512 will be
+mapped to other ports below 512: those between 512 and 1023 inclusive
+will be mapped to ports below 1024, and other ports will be mapped to
+1024 or above. Where possible, no port alteration will occur.
+.RS
+.PP
+You can add several --to-source options. If you specify more
+than one source address, either via an address range or multiple
+--to-source options, a simple round-robin (one after another in
+cycle) takes place between these adresses.
+.SS TCPMSS
+This target allows to alter the MSS value of TCP SYN packets, to control
+the maximum size for that connection (usually limiting it to your
+outgoing interface's MTU minus 40). Of course, it can only be used
+in conjunction with
+.BR "-p tcp" .
+.br
+This target is used to overcome criminally braindead ISPs or servers
+which block ICMP Fragmentation Needed packets. The symptoms of this
+problem are that everything works fine from your Linux
+firewall/router, but machines behind it can never exchange large
+packets:
+.PD 0
+.RS 0.1i
+.TP 0.3i
+1)
+Web browsers connect, then hang with no data received.
+.TP
+2)
+Small mail works fine, but large emails hang.
+.TP
+3)
+ssh works fine, but scp hangs after initial handshaking.
+.RE
+.PD
+Workaround: activate this option and add a rule to your firewall
+configuration like:
+.nf
+ iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \\
+ -j TCPMSS --clamp-mss-to-pmtu
+.fi
+.TP
+.BI "--set-mss " "value"
+Explicitly set MSS option to specified value.
+.TP
+.B "--clamp-mss-to-pmtu"
+Automatically clamp MSS value to (path_MTU - 40).
+.TP
+These options are mutually exclusive.
+.SS TOS
+This is used to set the 8-bit Type of Service field in the IP header.
+It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-tos " "tos"
+You can use a numeric TOS values, or use
+.nf
+ iptables -j TOS -h
+.fi
+to see the list of valid TOS names.
+.SS ULOG
+This target provides userspace logging of matching packets. When this
+target is set for a rule, the Linux kernel will multicast this packet
+through a
+.IR netlink
+socket. One or more userspace processes may then subscribe to various
+multicast groups and receive the packets.
+Like LOG, this is a "non-terminating target", i.e. rule traversal
+continues at the next rule.
+.TP
+.BI "--ulog-nlgroup " "nlgroup"
+This specifies the netlink group (1-32) to which the packet is sent.
+Default value is 1.
+.TP
+.BI "--ulog-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 32 characters
+long, and useful for distinguishing messages in the logs.
+.TP
+.BI "--ulog-cprange " "size"
+Number of bytes to be copied to userspace. A value of 0 always copies
+the entire packet, regardless of its size. Default is 0.
+.TP
+.BI "--ulog-qthreshold " "size"
+Number of packet to queue inside kernel. Setting this value to, e.g. 10
+accumulates ten packets inside the kernel and transmits them as one
+netlink multipart message to userspace. Default is 1 (for backwards
+compatibility).
+.br
+.SH DIAGNOSTICS
+Various error messages are printed to standard error. The exit code
+is 0 for correct functioning. Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Bugs? What's this? ;-)
+Well... the counters are not reliable on sparc64.
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B iptables
+is very similar to ipchains by Rusty Russell. The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively. Hence every packet only
+passes through one of the three chains (except loopback traffic, which
+involves both INPUT and OUTPUT chains); previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.PP The various forms of NAT have been separated out;
+.B iptables
+is a pure packet filter when using the default `filter' table, with
+optional extension modules. This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously. So the following options are handled differently:
+.nf
+ -j MASQ
+ -M -S
+ -M -L
+.fi
+There are several other changes in iptables.
+.SH SEE ALSO
+.BR iptables-save (8),
+.BR iptables-restore (8),
+.BR ip6tables (8),
+.BR ip6tables-save (8),
+.BR ip6tables-restore (8).
+.P
+The packet-filtering-HOWTO details iptables usage for
+packet filtering, the NAT-HOWTO details NAT,
+the netfilter-extensions-HOWTO details the extensions that are
+not in the standard distribution,
+and the netfilter-hacking-HOWTO details the netfilter internals.
+.br
+See
+.BR "http://www.netfilter.org/" .
+.SH AUTHORS
+Rusty Russell wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic packet
+selection framework in iptables, then wrote the mangle table, the owner match,
+the mark stuff, and ran around doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+Harald Welte wrote the ULOG target, TTL, DSCP, ECN matches and targets.
+.PP
+The Netfilter Core Team is: Marc Boucher, Martin Josefsson, Jozsef Kadlecsik,
+James Morris, Harald Welte and Rusty Russell.
+.PP
+Man page written by Herve Eychenne <rv@wallfire.org>.
+.\" .. and did I mention that we are incredibly cool people?
+.\" .. sexy, too ..
+.\" .. witty, charming, powerful ..
+.\" .. and most of all, modest ..
--- /dev/null
+#!/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
--- /dev/null
+%define name iptables
+%define version 1.2.9
+%define release 2.3.1.1.planetlab%{?date:.%{date}}
+
+Vendor: PlanetLab
+Packager: PlanetLab Central <support@planet-lab.org>
+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 <sopwith@redhat.com>
+- rebuilt
+
+* Thu Feb 26 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-2.3
+- fixed iptables-restore -c fault if there are no counters (#116421)
+
+* Fri Feb 13 2004 Elliot Lee <sopwith@redhat.com>
+- rebuilt
+
+* Sun Jan 25 2004 Dan Walsh <dwalsh@redhat.com> 1.2.9-1.2
+- Close File descriptors to prevent SELinux error message
+
+* Wed Jan 7 2004 Thomas Woerner <twoerner@redhat.com> 1.2.9-1.1
+- rebuild
+
+* Wed Dec 17 2003 Thomas Woerner <twoerner@redhat.com> 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 <twoerner@redhat.com> 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 <twoerner@redhat.com> 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 <Florian.LaRoche@redhat.de> 1.2.8-11
+- do not link against -lnsl
+
+* Wed Sep 17 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-10
+- made variables in rmmod_r local
+
+* Tue Jul 22 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-9
+- fixed permission for init script
+
+* Sat Jul 19 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-8
+- fixed save when iptables file is missing and iptables-config permissions
+
+* Tue Jul 8 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-7
+- fixes for ip6tables: module unloading, setting policy only for existing
+ tables
+
+* Thu Jul 3 2003 Thomas Woerner <twoerner@redhat.com> 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 <twoerner@redhat.com> 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 <twoerner@redhat.com> 1.2.8-4
+- new config option IPTABLES_STATUS_NUMERIC
+- cleared IPTABLES_MODULES in iptables-config
+
+* Mon Jun 30 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-3
+- new init scripts
+
+* Sat Jun 28 2003 Florian La Roche <Florian.LaRoche@redhat.de>
+- 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 <twoerner@redhat.com> 1.2.8-2
+- rebuild
+
+* Mon Jun 16 2003 Thomas Woerner <twoerner@redhat.com> 1.2.8-1
+- update to 1.2.8
+
+* Wed Jan 22 2003 Tim Powers <timp@redhat.com>
+- rebuilt
+
+* Mon Jan 13 2003 Bill Nottingham <notting@redhat.com> 1.2.7a-1
+- update to 1.2.7a
+- add a plethora of bugfixes courtesy Michael Schwendt <mschewndt@yahoo.com>
+
+* Fri Dec 13 2002 Elliot Lee <sopwith@redhat.com> 1.2.6a-3
+- Fix multilib
+
+* Wed Aug 07 2002 Karsten Hopp <karsten@redhat.de>
+- 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 <bero@redhat.com> 1.2.6a-1
+- 1.2.6a (bugfix release, #69747)
+
+* Fri Jun 21 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Thu May 23 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Mar 4 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-3
+- Add some fixes from CVS, fixing bug #60465
+
+* Tue Feb 12 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-2
+- Merge ip6tables improvements from Ian Prowell <iprowell@prowell.org>
+ #59402
+- Update URL (#59354)
+- Use /sbin/chkconfig rather than chkconfig in %postun script
+
+* Fri Jan 11 2002 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.5-1
+- 1.2.5
+
+* Wed Jan 09 2002 Tim Powers <timp@redhat.com>
+- automated rebuild
+
+* Mon Nov 5 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.4-2
+- Fix %preun script
+
+* Tue Oct 30 2001 Bernhard Rosenkraenzer <bero@redhat.com> 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 <bero@redhat.com> 1.2.3-1
+- 1.2.3 (5 security fixes, some other fixes)
+- Fix updating (#53032)
+
+* Mon Aug 27 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-4
+- Fix #50990
+- Add some fixes from current CVS; should fix #52620
+
+* Mon Jul 16 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-3
+- Add some fixes from the current CVS tree; fixes #49154 and some IPv6
+ issues
+
+* Tue Jun 26 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-2
+- Fix iptables-save reject-with (#45632), Patch from Michael Schwendt
+ <mschwendt@yahoo.com>
+
+* Tue May 8 2001 Bernhard Rosenkraenzer <bero@redhat.com> 1.2.2-1
+- 1.2.2
+
+* Wed Mar 21 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.2.1a, fixes #28412, #31136, #31460, #31133
+
+* Thu Mar 1 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Yet another initscript fix (#30173)
+- Fix the fixes; they fixed some issues but broke more important
+ stuff :/ (#30176)
+
+* Tue Feb 27 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Fix up initscript (#27962)
+- Add fixes from CVS to iptables-{restore,save}, fixing #28412
+
+* Fri Feb 09 2001 Karsten Hopp <karsten@redhat.de>
+- create /etc/sysconfig/iptables mode 600 (same problem as #24245)
+
+* Mon Feb 05 2001 Karsten Hopp <karsten@redhat.de>
+- 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 <teg@redhat.com>
+- Fix check for ipchains
+
+* Mon Jan 29 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Some fixes to init scripts
+
+* Wed Jan 24 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add some fixes from CVS, fixes among other things Bug #24732
+
+* Wed Jan 17 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- Add missing man pages, fix up init script (Bug #17676)
+
+* Mon Jan 15 2001 Bill Nottingham <notting@redhat.com>
+- add init script
+
+* Mon Jan 15 2001 Bernhard Rosenkraenzer <bero@redhat.com>
+- 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 <bero@redhat.com>
+- 1.1.2
+- Add IPv6 support (in separate package)
+
+* Thu Aug 17 2000 Bill Nottingham <notting@redhat.com>
+- build everywhere
+
+* Tue Jul 25 2000 Bernhard Rosenkraenzer <bero@redhat.com>
+- 1.1.1
+
+* Thu Jul 13 2000 Prospector <bugzilla@redhat.com>
+- automatic rebuild
+
+* Tue Jun 27 2000 Preston Brown <pbrown@redhat.com>
+- move iptables to /sbin.
+- excludearch alpha for now, not building there because of compiler bug(?)
+
+* Fri Jun 9 2000 Bill Nottingham <notting@redhat.com>
+- don't obsolete ipchains either
+- update to 1.1.0
+
+* Mon Jun 4 2000 Bill Nottingham <notting@redhat.com>
+- remove explicit kernel requirement
+
+* Tue May 2 2000 Bernhard Rosenkränzer <bero@redhat.com>
+- initial package
--- /dev/null
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* 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). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN NF_IP6_LOCAL_IN
+#define HOOK_FORWARD NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET struct ip6t_entry_target
+#define STRUCT_ENTRY struct ip6t_entry
+#define STRUCT_ENTRY_MATCH struct ip6t_entry_match
+#define STRUCT_GETINFO struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES struct ip6t_get_entries
+#define STRUCT_COUNTERS struct ip6t_counters
+#define STRUCT_COUNTERS_INFO struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE struct ip6t_replace
+
+#define STRUCT_TC_HANDLE struct ip6tc_handle
+#define TC_HANDLE_T ip6tc_handle_t
+
+#define ENTRY_ITERATE IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET ip6t_get_target
+
+#define ERROR_TARGET IP6T_ERROR_TARGET
+#define NUMHOOKS NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES dump_entries6
+#define TC_IS_CHAIN ip6tc_is_chain
+#define TC_FIRST_CHAIN ip6tc_first_chain
+#define TC_NEXT_CHAIN ip6tc_next_chain
+#define TC_FIRST_RULE ip6tc_first_rule
+#define TC_NEXT_RULE ip6tc_next_rule
+#define TC_GET_TARGET ip6tc_get_target
+#define TC_BUILTIN ip6tc_builtin
+#define TC_GET_POLICY ip6tc_get_policy
+#define TC_INSERT_ENTRY ip6tc_insert_entry
+#define TC_REPLACE_ENTRY ip6tc_replace_entry
+#define TC_APPEND_ENTRY ip6tc_append_entry
+#define TC_DELETE_ENTRY ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY ip6tc_delete_num_entry
+#define TC_CHECK_PACKET ip6tc_check_packet
+#define TC_FLUSH_ENTRIES ip6tc_flush_entries
+#define TC_ZERO_ENTRIES ip6tc_zero_entries
+#define TC_ZERO_COUNTER ip6tc_zero_counter
+#define TC_READ_COUNTER ip6tc_read_counter
+#define TC_SET_COUNTER ip6tc_set_counter
+#define TC_CREATE_CHAIN ip6tc_create_chain
+#define TC_GET_REFERENCES ip6tc_get_references
+#define TC_DELETE_CHAIN ip6tc_delete_chain
+#define TC_RENAME_CHAIN ip6tc_rename_chain
+#define TC_SET_POLICY ip6tc_set_policy
+#define TC_GET_RAW_SOCKET ip6tc_get_raw_socket
+#define TC_INIT ip6tc_init
+#define TC_FREE ip6tc_free
+#define TC_COMMIT ip6tc_commit
+#define TC_STRERROR ip6tc_strerror
+
+#define TC_AF AF_INET6
+#define TC_IPPROTO IPPROTO_IPV6
+
+#define SO_SET_REPLACE IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET IP6T_STANDARD_TARGET
+#define LABEL_RETURN IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT IP6TC_LABEL_ACCEPT
+#define LABEL_DROP IP6TC_LABEL_DROP
+#define LABEL_QUEUE IP6TC_LABEL_QUEUE
+
+#define ALIGN IP6T_ALIGN
+#define RETURN IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+ int l, i;
+ for (l = 0; l < 128; l++) {
+ if (BIT6(a, l) == 0)
+ break;
+ }
+ for (i = l + 1; i < 128; i++) {
+ if (BIT6(a, i) == 1)
+ return -1;
+ }
+ return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+ size_t i;
+ char buf[40];
+ int len;
+ struct ip6t_entry_target *t;
+
+ printf("Entry %u (%lu):\n", entry2index(handle, e),
+ entry2offset(handle, e));
+ puts("SRC IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.smsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ puts("DST IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.dmsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ printf("Interface: `%s'/", e->ipv6.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ipv6.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ipv6.proto);
+ if (e->ipv6.flags & IP6T_F_TOS)
+ printf("TOS: %u\n", e->ipv6.tos);
+ printf("Flags: %02X\n", e->ipv6.flags);
+ printf("Invflags: %02X\n", e->ipv6.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ e->counters.pcnt, e->counters.bcnt);
+ printf("Cache: %08X ", e->nfcache);
+ if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+ if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+ if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
+ if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
+ if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
+ if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
+ if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
+ if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
+ if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
+ if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
+ if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
+ if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
+ if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
+ printf("\n");
+
+ IP6T_MATCH_ITERATE(e, print_match);
+
+ t = ip6t_get_target(e);
+ printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+ if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == IP6T_RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+ unsigned char *matchmask)
+{
+ unsigned int i;
+ STRUCT_ENTRY_TARGET *ta, *tb;
+ unsigned char *mptr;
+
+ /* Always compare head structures: ignore mask here. */
+ if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+ || a->ipv6.proto != b->ipv6.proto
+ || a->ipv6.tos != b->ipv6.tos
+ || a->ipv6.flags != b->ipv6.flags
+ || a->ipv6.invflags != b->ipv6.invflags)
+ return 0;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+ return 0;
+ if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+ != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+ return 0;
+ if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+ return 0;
+ if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+ != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+ return 0;
+ }
+
+ if (a->nfcache != b->nfcache
+ || a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return 0;
+
+ mptr = matchmask + sizeof(STRUCT_ENTRY);
+ if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+ return 0;
+
+ ta = GET_TARGET((STRUCT_ENTRY *)a);
+ tb = GET_TARGET((STRUCT_ENTRY *)b);
+ if (ta->u.target_size != tb->u.target_size)
+ return 0;
+ if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+ return 0;
+ mptr += sizeof(*ta);
+
+ if (target_different(ta->data, tb->data,
+ ta->u.target_size - sizeof(*ta), mptr))
+ return 0;
+
+ return 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ipv6); i++)
+ if (((char *)ipv6)[i])
+ break;
+
+ return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ n = get_chain_end(h, n);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+ }
+
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ /* This code is getting ugly because linux < 2.4.18-pre6 had
+ * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+ * */
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ /* Hooks should be first five */
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ n = get_chain_end(h, n);
+ }
+
+ if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+ n = get_chain_end(h, n);
+ }
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+ }
+ } else {
+ fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+ abort();
+ }
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NUMHOOKS; i++) {
+ STRUCT_ENTRY *e;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ipv6));
+ assert(e->target_offset == sizeof(*e));
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ printf("target_size=%u, align=%u\n",
+ t->target.u.target_size, ALIGN(sizeof(*t)));
+ assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+ assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+ assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+ +sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(STRUCT_ENTRY)
+ + sizeof(STRUCT_STANDARD_TARGET))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+
+#if 0
+ /* Check all the entries. */
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+ ->u.user.name,
+ ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
--- /dev/null
+--- libiptc.c 2003-06-30 18:26:59.000000000 +0200
++++ libiptc.c 2003-06-30 18:27:24.000000000 +0200
+@@ -64,19 +61,35 @@
+ char error[TABLE_MAXNAMELEN];
+ };
+
+-struct chain_cache
++struct rule_head
+ {
++ struct list_head list;
++
++ struct chain_head *chain;
++
++ unsigned int size;
++ STRUCT_ENTRY entry[0];
++}
++
++struct chain_head
++{
++ struct list_head list;
++
+ char name[TABLE_MAXNAMELEN];
+- /* This is the first rule in chain. */
+- unsigned int start_off;
+- /* Last rule in chain */
+- unsigned int end_off;
++ unsigned int hooknum;
++ struct list_head rules;
+ };
+
+ STRUCT_TC_HANDLE
+ {
+ /* Have changes been made? */
+ int changed;
++
++ struct list_head chains;
++
++ struct chain_head *chain_iterator_cur;
++
++#if 0
+ /* Size in here reflects original state. */
+ STRUCT_GETINFO info;
+
+@@ -98,6 +111,7 @@
+ /* Number in here reflects current state. */
+ unsigned int new_number;
+ STRUCT_GET_ENTRIES entries;
++#endif
+ };
+
+ static void
+@@ -375,173 +389,25 @@
+ }
+ return 0;
+ }
+-
+-static inline int
+-add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+-{
+- unsigned int builtin;
+-
+- /* Last entry. End it. */
+- if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+- /* This is the ERROR node at end of the table */
+- h->cache_chain_heads[h->cache_num_chains-1].end_off =
+- entry2offset(h, *prev);
+- return 0;
+- }
+-
+- /* We know this is the start of a new chain if it's an ERROR
+- target, or a hook entry point */
+- if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+- /* prev was last entry in previous chain */
+- h->cache_chain_heads[h->cache_num_chains-1].end_off
+- = entry2offset(h, *prev);
+-
+- strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+- (const char *)GET_TARGET(e)->data);
+- h->cache_chain_heads[h->cache_num_chains].start_off
+- = entry2offset(h, (void *)e + e->next_offset);
+- h->cache_num_chains++;
+- } else if ((builtin = is_hook_entry(e, h)) != 0) {
+- if (h->cache_num_chains > 0)
+- /* prev was last entry in previous chain */
+- h->cache_chain_heads[h->cache_num_chains-1].end_off
+- = entry2offset(h, *prev);
+-
+- strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+- h->hooknames[builtin-1]);
+- h->cache_chain_heads[h->cache_num_chains].start_off
+- = entry2offset(h, (void *)e);
+- h->cache_num_chains++;
+- }
+-
+- *prev = e;
+- return 0;
+-}
+-
+ static int alphasort(const void *a, const void *b)
+ {
+ return strcmp(((struct chain_cache *)a)->name,
+ ((struct chain_cache *)b)->name);
+ }
+
+-static int populate_cache(TC_HANDLE_T h)
+-{
+- unsigned int i;
+- STRUCT_ENTRY *prev;
+-
+- /* # chains < # rules / 2 + num builtins - 1 */
+- h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+- * sizeof(struct chain_cache));
+- if (!h->cache_chain_heads) {
+- errno = ENOMEM;
+- return 0;
+- }
+-
+- h->cache_num_chains = 0;
+- h->cache_num_builtins = 0;
+-
+- /* Count builtins */
+- for (i = 0; i < NUMHOOKS; i++) {
+- if (h->info.valid_hooks & (1 << i))
+- h->cache_num_builtins++;
+- }
+-
+- prev = NULL;
+- ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+- add_chain, h, &prev);
+-
+- qsort(h->cache_chain_heads + h->cache_num_builtins,
+- h->cache_num_chains - h->cache_num_builtins,
+- sizeof(struct chain_cache), alphasort);
+-
+- return 1;
+-}
+-
+-static int
+-correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
+-{
+- int i; /* needs to be signed because deleting first
+- chain can make it drop to -1 */
+-
+- if (!delta)
+- return 1;
+-
+- for (i = 0; i < h->cache_num_chains; i++) {
+- struct chain_cache *cc = &h->cache_chain_heads[i];
+-
+- if (delta < 0) {
+- /* take care about deleted chains */
+- if (cc->start_off >= offset+delta
+- && cc->end_off <= offset) {
+- /* this chain is within the deleted range,
+- * let's remove it from the cache */
+- void *start;
+- unsigned int size;
+-
+- h->cache_num_chains--;
+- if (i+1 >= h->cache_num_chains)
+- continue;
+- start = &h->cache_chain_heads[i+1];
+- size = (h->cache_num_chains-i)
+- * sizeof(struct chain_cache);
+- memmove(cc, start, size);
+-
+- /* iterate over same index again, since
+- * it is now a different chain */
+- i--;
+- continue;
+- }
+- }
+-
+- if (cc->start_off > offset)
+- cc->start_off += delta;
+-
+- if (cc->end_off >= offset)
+- cc->end_off += delta;
+- }
+- /* HW_FIXME: sorting might be needed, but just in case a new chain was
+- * added */
+-
+- return 1;
+-}
+-
+-static int
+-add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
+- unsigned int end_off)
+-{
+- struct chain_cache *ccs = realloc(h->cache_chain_heads,
+- (h->new_number / 2 + 4 + 1)
+- * sizeof(struct chain_cache));
+- struct chain_cache *newcc;
+-
+- if (!ccs)
+- return 0;
+-
+- h->cache_chain_heads = ccs;
+- newcc = &h->cache_chain_heads[h->cache_num_chains];
+- h->cache_num_chains++;
+-
+- strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
+- newcc->start_off = start_off;
+- newcc->end_off = end_off;
+-
+- return 1;
+-}
+-
+-/* Returns cache ptr if found, otherwise NULL. */
+-static struct chain_cache *
++/* Returns chain head if found, otherwise NULL. */
++static struct chain_head *
+ find_label(const char *name, TC_HANDLE_T handle)
+ {
+- unsigned int i;
++ struct list_head *pos;
+
+- if (handle->cache_chain_heads == NULL
+- && !populate_cache(handle))
++ if (!handle->chains)
+ return NULL;
+
+- /* FIXME: Linear search through builtins, then binary --RR */
+- for (i = 0; i < handle->cache_num_chains; i++) {
+- if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+- return &handle->cache_chain_heads[i];
++ 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;
+@@ -594,34 +460,30 @@
+ const char *
+ TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+ {
+- if ((*handle)->cache_chain_heads == NULL
+- && !populate_cache(*handle))
+- return NULL;
++ (*handle)->chain_iterator_cur = (*handle)->chains;
+
+- (*handle)->cache_chain_iteration
+- = &(*handle)->cache_chain_heads[0];
+-
+- return (*handle)->cache_chain_iteration->name;
++ return (*handle)->chains.name;
+ }
+
+ /* Iterator functions to run through the chains. Returns NULL at end. */
+ const char *
+ TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+ {
+- (*handle)->cache_chain_iteration++;
++ struct chain_head *next = list_entry(&(*handle)->chain_iterator_cur->list.next, struct chain_head, list);
++ (*handle)->chain_iterator_cur = next;
+
+- if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+- == (*handle)->cache_num_chains)
++ if (next == (*handle)->chains)
+ return NULL;
+
+- return (*handle)->cache_chain_iteration->name;
++ 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_cache *c;
++ struct chain_head *c;
++ struct rule_head *r;
+
+ c = find_label(chain, *handle);
+ if (!c) {
+@@ -630,22 +492,26 @@
+ }
+
+ /* Empty chain: single return/policy rule */
+- if (c->start_off == c->end_off)
++ if (list_empty(c->rules))
+ return NULL;
+
+- (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+- return offset2entry(*handle, c->start_off);
++ 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)
+ {
+- if ((void *)prev + prev->next_offset
+- == (void *)(*handle)->cache_rule_end)
++ struct rule_head *r = list_entry((*handle)->rule_iterator_cur->list.next, struct rule_head, list);
++
++ if (r == r->chain)
+ return NULL;
+
+- return (void *)prev + prev->next_offset;
++ /* NOTE: prev is without any influence ! */
++ return r->entry;
+ }
+
+ #if 0
+@@ -773,7 +639,7 @@
+ return target_name(*handle, e);
+ }
+
+-static inline int
++static int
+ correct_verdict(STRUCT_ENTRY *e,
+ char *base,
+ unsigned int offset, int delta_offset)
+@@ -874,16 +740,8 @@
+ newh->entries.size = (*handle)->entries.size + rules_size;
+ newh->hooknames = (*handle)->hooknames;
+
+- newh->cache_chain_heads = (*handle)->cache_chain_heads;
+- newh->cache_num_builtins = (*handle)->cache_num_builtins;
+- newh->cache_num_chains = (*handle)->cache_num_chains;
+- newh->cache_rule_end = (*handle)->cache_rule_end;
+- newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
+- if (!correct_cache(newh, offset, rules_size)) {
+- free(newh);
+- return 0;
+- }
+-
++ if ((*handle)->cache_chain_heads)
++ free((*handle)->cache_chain_heads);
+ free(*handle);
+ *handle = newh;
+
+@@ -942,10 +800,6 @@
+ (*handle)->new_number -= num_rules;
+ (*handle)->entries.size -= rules_size;
+
+- /* Fix the chain cache */
+- if (!correct_cache(*handle, offset, -(int)rules_size))
+- return 0;
+-
+ return set_verdict(offset, -(int)rules_size, handle);
+ }
+
+@@ -1449,7 +1303,6 @@
+ STRUCT_ENTRY ret;
+ STRUCT_STANDARD_TARGET target;
+ } newc;
+- unsigned int destination;
+
+ iptc_fn = TC_CREATE_CHAIN;
+
+@@ -1487,21 +1340,11 @@
+ = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ newc.target.verdict = RETURN;
+
+- destination = index2offset(*handle, (*handle)->new_number -1);
+-
+ /* Add just before terminal entry */
+ ret = insert_rules(2, sizeof(newc), &newc.head,
+- destination,
++ index2offset(*handle, (*handle)->new_number - 1),
+ (*handle)->new_number - 1,
+ 0, handle);
+-
+- set_changed(*handle);
+-
+- /* add chain cache info for this chain */
+- add_chain_cache(*handle, chain,
+- destination+newc.head.next_offset,
+- destination+newc.head.next_offset);
+-
+ return ret;
+ }
+
+@@ -1629,11 +1472,6 @@
+
+ memset(t->error, 0, sizeof(t->error));
+ strcpy(t->error, newname);
+-
+- /* update chain cache */
+- memset(c->name, 0, sizeof(c->name));
+- strcpy(c->name, newname);
+-
+ set_changed(*handle);
+
+ return 1;
--- /dev/null
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* 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). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION 4
+#define IP_OFFSET 0x1FFF
+
+#define HOOK_PRE_ROUTING NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN NF_IP_LOCAL_IN
+#define HOOK_FORWARD NF_IP_FORWARD
+#define HOOK_LOCAL_OUT NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET struct ipt_entry_target
+#define STRUCT_ENTRY struct ipt_entry
+#define STRUCT_ENTRY_MATCH struct ipt_entry_match
+#define STRUCT_GETINFO struct ipt_getinfo
+#define STRUCT_GET_ENTRIES struct ipt_get_entries
+#define STRUCT_COUNTERS struct ipt_counters
+#define STRUCT_COUNTERS_INFO struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE struct ipt_replace
+
+#define STRUCT_TC_HANDLE struct iptc_handle
+#define TC_HANDLE_T iptc_handle_t
+
+#define ENTRY_ITERATE IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET ipt_get_target
+
+#define ERROR_TARGET IPT_ERROR_TARGET
+#define NUMHOOKS NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL ipt_chainlabel
+
+#define TC_DUMP_ENTRIES dump_entries
+#define TC_IS_CHAIN iptc_is_chain
+#define TC_FIRST_CHAIN iptc_first_chain
+#define TC_NEXT_CHAIN iptc_next_chain
+#define TC_FIRST_RULE iptc_first_rule
+#define TC_NEXT_RULE iptc_next_rule
+#define TC_GET_TARGET iptc_get_target
+#define TC_BUILTIN iptc_builtin
+#define TC_GET_POLICY iptc_get_policy
+#define TC_INSERT_ENTRY iptc_insert_entry
+#define TC_REPLACE_ENTRY iptc_replace_entry
+#define TC_APPEND_ENTRY iptc_append_entry
+#define TC_DELETE_ENTRY iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY iptc_delete_num_entry
+#define TC_CHECK_PACKET iptc_check_packet
+#define TC_FLUSH_ENTRIES iptc_flush_entries
+#define TC_ZERO_ENTRIES iptc_zero_entries
+#define TC_READ_COUNTER iptc_read_counter
+#define TC_ZERO_COUNTER iptc_zero_counter
+#define TC_SET_COUNTER iptc_set_counter
+#define TC_CREATE_CHAIN iptc_create_chain
+#define TC_GET_REFERENCES iptc_get_references
+#define TC_DELETE_CHAIN iptc_delete_chain
+#define TC_RENAME_CHAIN iptc_rename_chain
+#define TC_SET_POLICY iptc_set_policy
+#define TC_GET_RAW_SOCKET iptc_get_raw_socket
+#define TC_INIT iptc_init
+#define TC_FREE iptc_free
+#define TC_COMMIT iptc_commit
+#define TC_STRERROR iptc_strerror
+
+#define TC_AF AF_INET
+#define TC_IPPROTO IPPROTO_IP
+
+#define SO_SET_REPLACE IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO IPT_SO_GET_INFO
+#define SO_GET_ENTRIES IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET IPT_STANDARD_TARGET
+#define LABEL_RETURN IPTC_LABEL_RETURN
+#define LABEL_ACCEPT IPTC_LABEL_ACCEPT
+#define LABEL_DROP IPTC_LABEL_DROP
+#define LABEL_QUEUE IPTC_LABEL_QUEUE
+
+#define ALIGN IPT_ALIGN
+#define RETURN IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+ size_t i;
+ STRUCT_ENTRY_TARGET *t;
+
+ printf("Entry %u (%lu):\n", entry2index(handle, e),
+ entry2offset(handle, e));
+ printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+ printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+ printf("Interface: `%s'/", e->ip.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ip.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ip.proto);
+ printf("Flags: %02X\n", e->ip.flags);
+ printf("Invflags: %02X\n", e->ip.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ e->counters.pcnt, e->counters.bcnt);
+ printf("Cache: %08X ", e->nfcache);
+ if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+ if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+ if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
+ if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
+ if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
+ if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
+ if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
+ if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
+ if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
+ if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
+ if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
+ if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
+ if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
+ printf("\n");
+
+ IPT_MATCH_ITERATE(e, print_match);
+
+ t = GET_TARGET(e);
+ printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+ if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+ : pos == RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+ unsigned int i;
+ STRUCT_ENTRY_TARGET *ta, *tb;
+ unsigned char *mptr;
+
+ /* Always compare head structures: ignore mask here. */
+ if (a->ip.src.s_addr != b->ip.src.s_addr
+ || a->ip.dst.s_addr != b->ip.dst.s_addr
+ || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+ || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+ || a->ip.proto != b->ip.proto
+ || a->ip.flags != b->ip.flags
+ || a->ip.invflags != b->ip.invflags)
+ return 0;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+ return 0;
+ if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+ != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+ return 0;
+ if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+ return 0;
+ if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+ != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+ return 0;
+ }
+
+ if (a->nfcache != b->nfcache
+ || a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return 0;
+
+ mptr = matchmask + sizeof(STRUCT_ENTRY);
+ if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+ return 0;
+
+ ta = GET_TARGET((STRUCT_ENTRY *)a);
+ tb = GET_TARGET((STRUCT_ENTRY *)b);
+ if (ta->u.target_size != tb->u.target_size)
+ return 0;
+ if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+ return 0;
+
+ mptr += sizeof(*ta);
+ if (target_different(ta->data, tb->data,
+ ta->u.target_size - sizeof(*ta), mptr))
+ return 0;
+
+ return 1;
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+ if (((u_int32_t *)ip)[i])
+ return 0;
+
+ return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+ assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+ assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+ (*off) += m->u.match_size;
+ return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+ unsigned int user_offset, int *was_return,
+ TC_HANDLE_T h)
+{
+ unsigned int toff;
+ STRUCT_STANDARD_TARGET *t;
+
+ assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+ assert(e->next_offset >= e->target_offset
+ + sizeof(STRUCT_ENTRY_TARGET));
+ toff = sizeof(STRUCT_ENTRY);
+ IPT_MATCH_ITERATE(e, check_match, &toff);
+
+ assert(toff == e->target_offset);
+
+ t = (STRUCT_STANDARD_TARGET *)
+ GET_TARGET((STRUCT_ENTRY *)e);
+ /* next_offset will have to be multiple of entry alignment. */
+ assert(e->next_offset == ALIGN(e->next_offset));
+ assert(e->target_offset == ALIGN(e->target_offset));
+ assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+ assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+ if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+ assert(t->target.u.target_size
+ == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(t->verdict == -NF_DROP-1
+ || t->verdict == -NF_ACCEPT-1
+ || t->verdict == RETURN
+ || t->verdict < (int)h->entries.size);
+
+ if (t->verdict >= 0) {
+ STRUCT_ENTRY *te = get_entry(h, t->verdict);
+ int idx;
+
+ idx = entry2index(h, te);
+ assert(strcmp(GET_TARGET(te)->u.user.name,
+ IPT_ERROR_TARGET)
+ != 0);
+ assert(te != e);
+
+ /* Prior node must be error node, or this node. */
+ assert(t->verdict == entry2offset(h, e)+e->next_offset
+ || strcmp(GET_TARGET(index2entry(h, idx-1))
+ ->u.user.name, IPT_ERROR_TARGET)
+ == 0);
+ }
+
+ if (t->verdict == RETURN
+ && unconditional(&e->ip)
+ && e->target_offset == sizeof(*e))
+ *was_return = 1;
+ else
+ *was_return = 0;
+ } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+ assert(t->target.u.target_size
+ == ALIGN(sizeof(struct ipt_error_target)));
+
+ /* If this is in user area, previous must have been return */
+ if (*off > user_offset)
+ assert(*was_return);
+
+ *was_return = 0;
+ }
+ else *was_return = 0;
+
+ if (*off == user_offset)
+ assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+ (*off) += e->next_offset;
+ (*i)++;
+ return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_FORWARD
+ | 1 << NF_IP_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert((h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_POST_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_POST_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)));
+
+ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+ n = get_chain_end(h, n);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+ }
+
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ /* This code is getting ugly because linux < 2.4.18-pre6 had
+ * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+ * */
+ assert((h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_FORWARD
+ | 1 << NF_IP_LOCAL_OUT
+ | 1 << NF_IP_POST_ROUTING)));
+
+ /* Hooks should be first five */
+ assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+ n = get_chain_end(h, n);
+ }
+
+ if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+ n = get_chain_end(h, n);
+ }
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+ user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+ }
+
+#ifdef NF_IP_DROPPING
+ } else if (strcmp(h->info.name, "drop") == 0) {
+ assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+ /* Hook should be first */
+ assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+ user_offset = 0;
+#endif
+ } else {
+ fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+ abort();
+ }
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NUMHOOKS; i++) {
+ STRUCT_ENTRY *e;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ip));
+ assert(e->target_offset == sizeof(*e));
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+ assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+ assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+ +sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(STRUCT_ENTRY)
+ + sizeof(STRUCT_STANDARD_TARGET))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+ /* Check all the entries. */
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+ ->u.user.name,
+ ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
--- /dev/null
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* 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). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN NF_IP6_LOCAL_IN
+#define HOOK_FORWARD NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET struct ip6t_entry_target
+#define STRUCT_ENTRY struct ip6t_entry
+#define STRUCT_ENTRY_MATCH struct ip6t_entry_match
+#define STRUCT_GETINFO struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES struct ip6t_get_entries
+#define STRUCT_COUNTERS struct ip6t_counters
+#define STRUCT_COUNTERS_INFO struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE struct ip6t_replace
+
+#define STRUCT_TC_HANDLE struct ip6tc_handle
+#define TC_HANDLE_T ip6tc_handle_t
+
+#define ENTRY_ITERATE IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET ip6t_get_target
+
+#define ERROR_TARGET IP6T_ERROR_TARGET
+#define NUMHOOKS NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES dump_entries6
+#define TC_IS_CHAIN ip6tc_is_chain
+#define TC_FIRST_CHAIN ip6tc_first_chain
+#define TC_NEXT_CHAIN ip6tc_next_chain
+#define TC_FIRST_RULE ip6tc_first_rule
+#define TC_NEXT_RULE ip6tc_next_rule
+#define TC_GET_TARGET ip6tc_get_target
+#define TC_BUILTIN ip6tc_builtin
+#define TC_GET_POLICY ip6tc_get_policy
+#define TC_INSERT_ENTRY ip6tc_insert_entry
+#define TC_REPLACE_ENTRY ip6tc_replace_entry
+#define TC_APPEND_ENTRY ip6tc_append_entry
+#define TC_DELETE_ENTRY ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY ip6tc_delete_num_entry
+#define TC_CHECK_PACKET ip6tc_check_packet
+#define TC_FLUSH_ENTRIES ip6tc_flush_entries
+#define TC_ZERO_ENTRIES ip6tc_zero_entries
+#define TC_ZERO_COUNTER ip6tc_zero_counter
+#define TC_READ_COUNTER ip6tc_read_counter
+#define TC_SET_COUNTER ip6tc_set_counter
+#define TC_CREATE_CHAIN ip6tc_create_chain
+#define TC_GET_REFERENCES ip6tc_get_references
+#define TC_DELETE_CHAIN ip6tc_delete_chain
+#define TC_RENAME_CHAIN ip6tc_rename_chain
+#define TC_SET_POLICY ip6tc_set_policy
+#define TC_GET_RAW_SOCKET ip6tc_get_raw_socket
+#define TC_INIT ip6tc_init
+#define TC_FREE ip6tc_free
+#define TC_COMMIT ip6tc_commit
+#define TC_STRERROR ip6tc_strerror
+
+#define TC_AF AF_INET6
+#define TC_IPPROTO IPPROTO_IPV6
+
+#define SO_SET_REPLACE IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET IP6T_STANDARD_TARGET
+#define LABEL_RETURN IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT IP6TC_LABEL_ACCEPT
+#define LABEL_DROP IP6TC_LABEL_DROP
+#define LABEL_QUEUE IP6TC_LABEL_QUEUE
+
+#define ALIGN IP6T_ALIGN
+#define RETURN IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+ int l, i;
+ for (l = 0; l < 128; l++) {
+ if (BIT6(a, l) == 0)
+ break;
+ }
+ for (i = l + 1; i < 128; i++) {
+ if (BIT6(a, i) == 1)
+ return -1;
+ }
+ return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+ size_t i;
+ char buf[40];
+ int len;
+ struct ip6t_entry_target *t;
+
+ printf("Entry %u (%lu):\n", entry2index(handle, e),
+ entry2offset(handle, e));
+ puts("SRC IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.smsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ puts("DST IP: ");
+ inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+ puts(buf);
+ putchar('/');
+ len = ipv6_prefix_length(&e->ipv6.dmsk);
+ if (len != -1)
+ printf("%d", len);
+ else {
+ inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+ puts(buf);
+ }
+ putchar('\n');
+
+ printf("Interface: `%s'/", e->ipv6.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ipv6.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ipv6.proto);
+ if (e->ipv6.flags & IP6T_F_TOS)
+ printf("TOS: %u\n", e->ipv6.tos);
+ printf("Flags: %02X\n", e->ipv6.flags);
+ printf("Invflags: %02X\n", e->ipv6.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ e->counters.pcnt, e->counters.bcnt);
+ printf("Cache: %08X ", e->nfcache);
+ if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+ if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+ if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
+ if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
+ if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
+ if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
+ if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
+ if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
+ if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
+ if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
+ if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
+ if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
+ if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
+ printf("\n");
+
+ IP6T_MATCH_ITERATE(e, print_match);
+
+ t = ip6t_get_target(e);
+ printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+ if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == IP6T_RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+ unsigned char *matchmask)
+{
+ unsigned int i;
+ STRUCT_ENTRY_TARGET *ta, *tb;
+ unsigned char *mptr;
+
+ /* Always compare head structures: ignore mask here. */
+ if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+ || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+ || a->ipv6.proto != b->ipv6.proto
+ || a->ipv6.tos != b->ipv6.tos
+ || a->ipv6.flags != b->ipv6.flags
+ || a->ipv6.invflags != b->ipv6.invflags)
+ return 0;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+ return 0;
+ if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+ != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+ return 0;
+ if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+ return 0;
+ if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+ != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+ return 0;
+ }
+
+ if (a->nfcache != b->nfcache
+ || a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return 0;
+
+ mptr = matchmask + sizeof(STRUCT_ENTRY);
+ if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+ return 0;
+
+ ta = GET_TARGET((STRUCT_ENTRY *)a);
+ tb = GET_TARGET((STRUCT_ENTRY *)b);
+ if (ta->u.target_size != tb->u.target_size)
+ return 0;
+ if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+ return 0;
+ mptr += sizeof(*ta);
+
+ if (target_different(ta->data, tb->data,
+ ta->u.target_size - sizeof(*ta), mptr))
+ return 0;
+
+ return 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ipv6); i++)
+ if (((char *)ipv6)[i])
+ break;
+
+ return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ n = get_chain_end(h, n);
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+ }
+
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ /* This code is getting ugly because linux < 2.4.18-pre6 had
+ * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+ * */
+ assert((h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_OUT)) ||
+ (h->info.valid_hooks
+ == (1 << NF_IP6_PRE_ROUTING
+ | 1 << NF_IP6_LOCAL_IN
+ | 1 << NF_IP6_FORWARD
+ | 1 << NF_IP6_LOCAL_OUT
+ | 1 << NF_IP6_POST_ROUTING)));
+
+ /* Hooks should be first five */
+ assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+ n = get_chain_end(h, 0);
+
+ if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+ n = get_chain_end(h, n);
+ }
+
+ if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+ n = get_chain_end(h, n);
+ }
+
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+ user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+ if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+ user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+ }
+ } else {
+ fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+ abort();
+ }
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NUMHOOKS; i++) {
+ STRUCT_ENTRY *e;
+ STRUCT_STANDARD_TARGET *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ipv6));
+ assert(e->target_offset == sizeof(*e));
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+ printf("target_size=%u, align=%u\n",
+ t->target.u.target_size, ALIGN(sizeof(*t)));
+ assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+ assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+ assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+ +sizeof(STRUCT_STANDARD_TARGET)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(STRUCT_ENTRY)
+ + sizeof(STRUCT_STANDARD_TARGET))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+
+#if 0
+ /* Check all the entries. */
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+ ->u.user.name,
+ ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
--- /dev/null
+/* Library which manipulates firewall rules. Version $Revision: 1.41 $ */
+
+/* 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 <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ * - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ * - 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 <laforge@netfilter.org>:
+ * - reimplementation from scratch. *sigh*. I hope nobody has to touch
+ * this code ever again.
+ */
+#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 chain_head *jumpto; /* target of this rule, in case
+ it is a jump rule */
+
+ struct counter_map counter_map;
+
+ 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 rule_head *firstrule; /* first (ERROR) rule */
+ struct rule_head *lastrule; /* last (RETURN) rule */
+};
+
+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;
+
+ /* 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 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_clean(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);
+ }
+}
+
+static void
+chainh_free(struct chain_head *chainh)
+{
+ chainh_clean(chainh);
+ list_del(&chainh->list);
+}
+
+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;
+}
+
+/* Returns chain head if found, otherwise NULL. */
+static struct chain_head *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+ return chainh_find(handle, name);
+}
+
+
+/*
+ * functions that directly operate on the blob
+ */
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+ return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+ return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+/* needed by entry2index */
+static inline int
+get_number(const STRUCT_ENTRY *i,
+ const STRUCT_ENTRY *seek,
+ unsigned int *pos)
+{
+ if (i == seek)
+ return 1;
+ (*pos)++;
+ return 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 unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+ return entry2offset(h, index2entry(h, index));
+}
+
+static 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 (char *)GET_TARGET(e)->data;
+}
+
+#if 0
+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));
+}
+
+
+#endif
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_tc_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;
+
+ strcpy(h->info.name, tablename);
+ strcpy(h->entries.name, tablename);
+ INIT_LIST_HEAD(&h->chains);
+
+ return h;
+}
+
+/* get the name of the chain that we jump to */
+static char *
+parse_jumptarget(const STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+ STRUCT_ENTRY *jumpto;
+ int spos, labelidx;
+
+ if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0) {
+ /* called for non-standard target */
+ return "__FIXME";
+ }
+ /* Standard target: evaluate */
+ spos = *(int *)GET_TARGET(e)->data;
+ if (spos < 0) {
+ return "__FIXME";
+ }
+
+ jumpto = get_entry(h, 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(h, jumpto) - 1;
+ return get_errorlabel(h, index2offset(h, labelidx));
+}
+
+/* parser functions */
+
+struct rule_head *
+append_entrycopy(const STRUCT_ENTRY *e, struct rule_head *prev)
+{
+ struct rule_head *ruleh = ruleh_alloc(e->next_offset);
+ if (!ruleh)
+ return NULL;
+
+ memcpy(&ruleh->entry, e, e->next_offset);
+ ruleh->chain = prev->chain;
+ ruleh->entry_blob = e;
+ list_append(&ruleh->list, &prev->list);
+
+ return ruleh;
+}
+
+/* have to return 0 on success, bcf ENTRY_ITERATE */
+static inline int
+parse_entry(const 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;
+
+ struct rule_head *lastrule = list_entry((*curchain)->rules.prev,
+ struct rule_head, list);
+ struct rule_head *newrule;
+
+ 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 */
+ char *targname;
+ struct chain_head *chainh;
+
+ newrule = append_entrycopy(e, lastrule);
+
+ targname = parse_jumptarget(e, h);
+ if (!(chainh = find_label(targname, h))) {
+ chainh = chainh_alloc(h, targname);
+ }
+ if (!chainh) {
+ errno = ENOMEM;
+ return 1;
+ }
+ newrule->jumpto = chainh;
+
+ } 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)) {
+ *curchain = chainh_alloc(h, tgt->err.error);
+ /* FIXME: error handling */
+ }
+ newrule = append_entrycopy(e, lastrule);
+ (*curchain)->firstrule = newrule;
+
+ } 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 */
+ newrule = append_entrycopy(e, lastrule);
+ (*curchain)->lastrule = newrule;
+ *curchain = NULL;
+ } else {
+ /* normal rule */
+ newrule = append_entrycopy(e, lastrule);
+ }
+
+ /* create counter map entry */
+ newrule->counter_map.maptype = COUNTER_MAP_NORMAL_MAP;
+ newrule->counter_map.mappos = entry2index(h, e);
+
+ /* 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;
+ 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_tc_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;
+ //
+ 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) {
+ 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
+
+/* 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;
+
+ /* 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: entry %p not a valid target (%d)\n",
+ e, spos);
+ abort();
+ }
+
+#if 0
+// 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));
+#endif
+ return "";
+}
+
+/* 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)
+{
+ STRUCT_ENTRY *e;
+ struct chain_head *chainh;
+ struct rule_head *ruleh;
+ int hook;
+
+ hook = TC_BUILTIN(chain, *handle);
+ if (hook == 0)
+ return NULL;
+
+ chainh = find_label(chain, *handle);
+ if (!chainh) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ ruleh = chainh->lastrule;
+
+ e = ruleh->entry;
+ *counters = e->counters;
+
+ return target_name(*handle, e);
+}
+
+#if 0
+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;
+}
+#endif
+
+
+
+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 = (STRUCT_ENTRY_TARGET *)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_head *c;
+
+#if 0
+ /* FIXME */
+ c = find_label(t->u.user.name, handle);
+ if (c)
+ return standard_map(e, c->start_off);
+#endif
+ }
+
+ /* 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;
+}
+
+static struct rule_head *
+ruleh_get_n(struct chain_head *chead, int rulenum)
+{
+ int i = 0;
+ struct list_head *list;
+
+
+ list_for_each(list, &chead->rules) {
+ struct rule_head *rhead = list_entry(list, struct rule_head,
+ list);
+ i++;
+ if (i == rulenum)
+ return rhead;
+ }
+ return NULL;
+}
+
+/* Insert the entry `e' 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)
+{
+ struct chain_head *c;
+ struct rule_head *prev;
+
+ iptc_fn = TC_INSERT_ENTRY;
+ if (!(c = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ prev = ruleh_get_n(c, rulenum-1);
+ if (!prev) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ if (append_entrycopy(e, prev))
+ return 1;
+
+ return 0;
+}
+
+/* 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)
+{
+ struct chain_head *c;
+ struct rule_head *repl;
+
+ iptc_fn = TC_REPLACE_ENTRY;
+
+ if (!(c = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ repl = ruleh_get_n(c, rulenum);
+ if (!repl) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ if (!append_entrycopy(e, repl)) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ ruleh_free(repl);
+ return 1;
+}
+
+/* Append entry `e' 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_head *c;
+ struct rule_head *rhead;
+
+ iptc_fn = TC_APPEND_ENTRY;
+
+ if (!(c = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ rhead = list_entry(c->rules.prev, struct rule_head, list);
+ if(append_entrycopy(e, rhead))
+ return 1;
+
+ return 0;
+}
+
+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 `origfw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+ const STRUCT_ENTRY *origfw,
+ unsigned char *matchmask,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct list_head *cur, *cur2;
+
+ iptc_fn = TC_DELETE_ENTRY;
+ if (!(c = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ list_for_each_safe(cur, cur2, &c->rules) {
+ struct rule_head *rhead = list_entry(cur, struct rule_head,
+ list);
+ if (is_same(rhead->entry, origfw, matchmask)) {
+ ruleh_free(rhead);
+ return 1;
+ }
+ }
+
+ 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)
+{
+ struct chain_head *chainh;
+ struct rule_head *rhead;
+
+ iptc_fn = TC_DELETE_NUM_ENTRY;
+
+ if (!(chainh = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ rhead = ruleh_get_n(chainh, rulenum);
+ if (!rhead) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ ruleh_free(rhead);
+
+ return 1;
+}
+
+/* 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)
+{
+ struct list_head *cur, *cur2;
+ struct chain_head *chainh;
+
+ if (!(chainh = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ list_for_each_safe(cur, cur2, &chainh->rules) {
+ struct rule_head *ruleh = list_entry(cur, struct rule_head,
+ list);
+ /* don't free the entry and policy/return entries */
+ if (ruleh != chainh->firstrule && ruleh != chainh->lastrule)
+ ruleh_free(ruleh);
+ }
+ return 1;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct list_head *cur;
+
+ if (!(c = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ list_for_each(cur, c->rules.next) {
+ struct rule_head *r = list_entry(cur, struct rule_head, list);
+ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+ r->counter_map.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_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_READ_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = find_label(chain, *handle) )
+ || !(r = ruleh_get_n(c, rulenum))) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ return &r->entry->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+ unsigned int rulenum,
+ TC_HANDLE_T *handle)
+{
+ STRUCT_ENTRY *e;
+ struct chain_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_ZERO_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = find_label(chain, *handle))
+ || !(r = ruleh_get_n(c, rulenum))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP)
+ r->counter_map.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_head *c;
+ struct rule_head *r;
+
+ iptc_fn = TC_SET_COUNTER;
+ CHECK(*handle);
+
+ if (!(c = find_label(chain, *handle))
+ || !(r = ruleh_get_n(c, rulenum))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ r->counter_map.maptype = COUNTER_MAP_SET;
+ memcpy(&r->entry->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 rule_head *newr1, *newr2;
+ 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;
+ }
+
+ newr1 = ruleh_alloc(sizeof(*newc1));
+ if (!newr1) {
+ chainh_free(chead);
+ return 0;
+ }
+ newc1 = (struct chainstart *) newr1->entry;
+
+ newr2 = ruleh_alloc(sizeof(*newc2));
+ if (!newr2) {
+ chainh_free(chead);
+ ruleh_free(newr1);
+ return 0;
+ }
+ newc2 = (struct chainend *) newr2->entry;
+
+ 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);
+ newc2->target.target.u.target_size
+ = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ newc2->target.verdict = RETURN;
+
+ list_prepend(&newr1->list, &chead->rules);
+ chead->firstrule = newr1;
+ list_append(&newr2->list, &chead->rules);
+ chead->lastrule = newr2;
+
+ return 1;
+}
+
+#if 0
+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;
+}
+#endif
+
+static unsigned int
+count_rules(struct chain_head *chainh)
+{
+ unsigned int numrules = 0;
+ struct list_head *cur;
+
+ list_for_each(cur, &chainh->rules) {
+ numrules++;
+ }
+
+ if (numrules <=2)
+ return 0;
+ else
+ return numrules-2;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+ unsigned int references;
+ struct chain_head *chainh;
+
+#if 0
+ 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;
+ }
+#endif
+
+ if (!(chainh = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (!(count_rules(chainh) == 0)) {
+ errno = ENOTEMPTY;
+ return 0;
+ }
+
+ chainh_free(chainh);
+ return 1;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+ const IPT_CHAINLABEL newname,
+ TC_HANDLE_T *handle)
+{
+ struct chain_head *c;
+ struct rule_head *ruleh;
+ 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;
+ }
+
+ ruleh = list_entry(&c->rules.next, struct rule_head, list);
+
+ t = (struct ipt_error_target *)
+ GET_TARGET(ruleh->entry);
+
+ memset(t->error, 0, sizeof(t->error));
+ strcpy(t->error, newname);
+
+ 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)
+{
+ int ctrindex;
+ unsigned int hook;
+ struct chain_head *chainh;
+ struct rule_head *policyrh;
+ 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--;
+
+ if (!(chainh = find_label(chain, *handle))) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ policyrh = chainh->lastrule;
+ if (policyrh) {
+ printf("ERROR: Policy for `%s' non-existant", chain);
+ return 0;
+ }
+
+ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(policyrh->entry);
+
+ 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));
+
+ policyrh->counter_map.maptype = COUNTER_MAP_SET;
+
+ } else {
+ policyrh->counter_map.maptype = COUNTER_MAP_NOMAP;
+ policyrh->counter_map.mappos = 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);
+}
--- /dev/null
+/* 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 <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ * - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ * - performance 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.
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+ return (void *)e + e->target_offset;
+}
+#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 chain_cache
+{
+ char name[TABLE_MAXNAMELEN];
+ /* This is the first rule in chain. */
+ unsigned int start_off;
+ /* Last rule in chain */
+ unsigned int end_off;
+};
+
+STRUCT_TC_HANDLE
+{
+ /* Have changes been made? */
+ int changed;
+ /* Size in here reflects original state. */
+ STRUCT_GETINFO info;
+
+ struct counter_map *counter_map;
+ /* Array of hook names */
+ const char **hooknames;
+
+ /* 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;
+ 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 inline int
+get_number(const STRUCT_ENTRY *i,
+ const STRUCT_ENTRY *seek,
+ unsigned int *pos)
+{
+ if (i == seek)
+ return 1;
+ (*pos)++;
+ return 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
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+ return (void *)e - (void *)h->entries.entrytable;
+}
+
+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;
+}
+
+/* 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->cache_num_chains = 0;
+ h->cache_chain_heads = NULL;
+ h->counter_map = (void *)h
+ + sizeof(STRUCT_TC_HANDLE)
+ + size;
+ strcpy(h->info.name, tablename);
+ strcpy(h->entries.name, tablename);
+
+ return h;
+}
+
+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);
+ return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+ close(sockfd);
+ sockfd = -1;
+ if ((*h)->cache_chain_heads)
+ free((*h)->cache_chain_heads);
+ 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);
+
+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 inline int
+add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+{
+ unsigned int builtin;
+
+ /* Last entry. End it. */
+ if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+ /* This is the ERROR node at end of the table */
+ h->cache_chain_heads[h->cache_num_chains-1].end_off =
+ entry2offset(h, *prev);
+ return 0;
+ }
+
+ /* We know this is the start of a new chain if it's an ERROR
+ target, or a hook entry point */
+ if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+ /* prev was last entry in previous chain */
+ h->cache_chain_heads[h->cache_num_chains-1].end_off
+ = entry2offset(h, *prev);
+
+ strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+ (const char *)GET_TARGET(e)->data);
+ h->cache_chain_heads[h->cache_num_chains].start_off
+ = entry2offset(h, (void *)e + e->next_offset);
+ h->cache_num_chains++;
+ } else if ((builtin = is_hook_entry(e, h)) != 0) {
+ if (h->cache_num_chains > 0)
+ /* prev was last entry in previous chain */
+ h->cache_chain_heads[h->cache_num_chains-1].end_off
+ = entry2offset(h, *prev);
+
+ strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+ h->hooknames[builtin-1]);
+ h->cache_chain_heads[h->cache_num_chains].start_off
+ = entry2offset(h, (void *)e);
+ h->cache_num_chains++;
+ }
+
+ *prev = e;
+ return 0;
+}
+
+static int alphasort(const void *a, const void *b)
+{
+ return strcmp(((struct chain_cache *)a)->name,
+ ((struct chain_cache *)b)->name);
+}
+
+static int populate_cache(TC_HANDLE_T h)
+{
+ unsigned int i;
+ STRUCT_ENTRY *prev;
+
+ /* # chains < # rules / 2 + num builtins - 1 */
+ h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+ * sizeof(struct chain_cache));
+ if (!h->cache_chain_heads) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ h->cache_num_chains = 0;
+ h->cache_num_builtins = 0;
+
+ /* Count builtins */
+ for (i = 0; i < NUMHOOKS; i++) {
+ if (h->info.valid_hooks & (1 << i))
+ h->cache_num_builtins++;
+ }
+
+ prev = NULL;
+ ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+ add_chain, h, &prev);
+
+ qsort(h->cache_chain_heads + h->cache_num_builtins,
+ h->cache_num_chains - h->cache_num_builtins,
+ sizeof(struct chain_cache), alphasort);
+
+ return 1;
+}
+
+static int
+correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
+{
+ int i; /* needs to be signed because deleting first
+ chain can make it drop to -1 */
+
+ if (!delta)
+ return 1;
+
+ for (i = 0; i < h->cache_num_chains; i++) {
+ struct chain_cache *cc = &h->cache_chain_heads[i];
+
+ if (delta < 0) {
+ /* take care about deleted chains */
+ if (cc->start_off >= offset+delta
+ && cc->end_off <= offset) {
+ /* this chain is within the deleted range,
+ * let's remove it from the cache */
+ void *start;
+ unsigned int size;
+
+ h->cache_num_chains--;
+ if (i+1 >= h->cache_num_chains)
+ continue;
+ start = &h->cache_chain_heads[i+1];
+ size = (h->cache_num_chains-i)
+ * sizeof(struct chain_cache);
+ memmove(cc, start, size);
+
+ /* iterate over same index again, since
+ * it is now a different chain */
+ i--;
+ continue;
+ }
+ }
+
+ if (cc->start_off > offset)
+ cc->start_off += delta;
+
+ if (cc->end_off >= offset)
+ cc->end_off += delta;
+ }
+ /* HW_FIXME: sorting might be needed, but just in case a new chain was
+ * added */
+
+ return 1;
+}
+
+static int
+add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
+ unsigned int end_off)
+{
+ struct chain_cache *ccs = realloc(h->cache_chain_heads,
+ (h->new_number / 2 + 4 + 1)
+ * sizeof(struct chain_cache));
+ struct chain_cache *newcc;
+
+ if (!ccs)
+ return 0;
+
+ h->cache_chain_heads = ccs;
+ newcc = &h->cache_chain_heads[h->cache_num_chains];
+ h->cache_num_chains++;
+
+ strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
+ newcc->start_off = start_off;
+ newcc->end_off = end_off;
+
+ return 1;
+}
+
+/* Returns cache ptr if found, otherwise NULL. */
+static struct chain_cache *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+ unsigned int i;
+
+ if (handle->cache_chain_heads == NULL
+ && !populate_cache(handle))
+ return NULL;
+
+ /* FIXME: Linear search through builtins, then binary --RR */
+ for (i = 0; i < handle->cache_num_chains; i++) {
+ if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+ return &handle->cache_chain_heads[i];
+ }
+
+ return NULL;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+ return find_label(chain, handle) != NULL;
+}
+
+/* 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();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+ if ((*handle)->cache_chain_heads == NULL
+ && !populate_cache(*handle))
+ return NULL;
+
+ (*handle)->cache_chain_iteration
+ = &(*handle)->cache_chain_heads[0];
+
+ return (*handle)->cache_chain_iteration->name;
+}
+
+/* Iterator functions to run through the chains. Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+ (*handle)->cache_chain_iteration++;
+
+ if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+ == (*handle)->cache_num_chains)
+ return NULL;
+
+ return (*handle)->cache_chain_iteration->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_cache *c;
+
+ c = find_label(chain, *handle);
+ if (!c) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ /* Empty chain: single return/policy rule */
+ if (c->start_off == c->end_off)
+ return NULL;
+
+ (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+ return offset2entry(*handle, c->start_off);
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+ if ((void *)prev + prev->next_offset
+ == (void *)(*handle)->cache_rule_end)
+ return NULL;
+
+ return (void *)prev + prev->next_offset;
+}
+
+#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 (%i)\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 */
+ 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 inline 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;
+
+ newh->cache_chain_heads = (*handle)->cache_chain_heads;
+ newh->cache_num_builtins = (*handle)->cache_num_builtins;
+ newh->cache_num_chains = (*handle)->cache_num_chains;
+ newh->cache_rule_end = (*handle)->cache_rule_end;
+ newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
+ if (!correct_cache(newh, offset, rules_size)) {
+ free(newh);
+ return 0;
+ }
+
+ 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;
+
+ /* Fix the chain cache */
+ if (!correct_cache(*handle, offset, -(int)rules_size))
+ return 0;
+
+ 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 {
+ STRUCT_ENTRY head;
+ struct ipt_error_target name;
+ STRUCT_ENTRY ret;
+ STRUCT_STANDARD_TARGET target;
+ } newc;
+ unsigned int destination;
+
+ 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;
+ }
+
+ memset(&newc, 0, sizeof(newc));
+ newc.head.target_offset = sizeof(STRUCT_ENTRY);
+ newc.head.next_offset
+ = sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(struct ipt_error_target));
+ strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+ newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+ strcpy(newc.name.error, chain);
+
+ newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+ newc.ret.next_offset
+ = sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+ newc.target.target.u.target_size
+ = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ newc.target.verdict = RETURN;
+
+ destination = index2offset(*handle, (*handle)->new_number -1);
+
+ /* Add just before terminal entry */
+ ret = insert_rules(2, sizeof(newc), &newc.head,
+ destination,
+ (*handle)->new_number - 1,
+ 0, handle);
+
+ set_changed(*handle);
+
+ /* add chain cache info for this chain */
+ add_chain_cache(*handle, chain,
+ destination+newc.head.next_offset,
+ destination+newc.head.next_offset);
+
+ return ret;
+}
+
+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);
+
+ /* update chain cache */
+ memset(c->name, 0, sizeof(c->name));
+ strcpy(c->name, 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);
+}
--- /dev/null
+/* Library which manipulates firewall rules. Version $Revision: 1.39 $ */
+
+/* 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 <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org:
+ * - Reimplementation of chain cache to use offsets instead of entries
+ *
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+ return (void *)e + e->target_offset;
+}
+#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;
+
+ struct chain_head *chain;
+
+ unsigned int size;
+ 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;
+
+ struct list_head chains;
+
+ struct chain_head *chain_iterator_cur;
+
+#if 0
+ /* Size in here reflects original state. */
+ STRUCT_GETINFO info;
+
+ struct counter_map *counter_map;
+ /* Array of hook names */
+ const char **hooknames;
+
+ /* 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;
+ STRUCT_GET_ENTRIES entries;
+#endif
+};
+
+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 inline int
+get_number(const STRUCT_ENTRY *i,
+ const STRUCT_ENTRY *seek,
+ unsigned int *pos)
+{
+ if (i == seek)
+ return 1;
+ (*pos)++;
+ return 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
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+ return (void *)e - (void *)h->entries.entrytable;
+}
+
+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;
+}
+
+/* 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->cache_num_chains = 0;
+ h->cache_chain_heads = NULL;
+ h->counter_map = (void *)h
+ + sizeof(STRUCT_TC_HANDLE)
+ + size;
+ strcpy(h->info.name, tablename);
+ strcpy(h->entries.name, tablename);
+
+ return h;
+}
+
+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);
+ return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+ close(sockfd);
+ sockfd = -1;
+ if ((*h)->cache_chain_heads)
+ free((*h)->cache_chain_heads);
+ 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);
+
+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);
+}
+
+/* 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 (!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;
+}
+
+/* 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();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+ (*handle)->chain_iterator_cur = (*handle)->chains;
+
+ return (*handle)->chains.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 == (*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 == r->chain)
+ 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 (%i)\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 */
+ 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 {
+ STRUCT_ENTRY head;
+ struct ipt_error_target name;
+ STRUCT_ENTRY ret;
+ STRUCT_STANDARD_TARGET target;
+ } newc;
+
+ 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;
+ }
+
+ memset(&newc, 0, sizeof(newc));
+ newc.head.target_offset = sizeof(STRUCT_ENTRY);
+ newc.head.next_offset
+ = sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(struct ipt_error_target));
+ strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+ newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+ strcpy(newc.name.error, chain);
+
+ newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+ newc.ret.next_offset
+ = sizeof(STRUCT_ENTRY)
+ + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+ newc.target.target.u.target_size
+ = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+ newc.target.verdict = RETURN;
+
+ /* Add just before terminal entry */
+ ret = insert_rules(2, sizeof(newc), &newc.head,
+ index2offset(*handle, (*handle)->new_number - 1),
+ (*handle)->new_number - 1,
+ 0, handle);
+ return ret;
+}
+
+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);
+}
--- /dev/null
+#ifndef _LINUXLIST_H
+#define _LINUXLIST_H
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+#endif
--- /dev/null
+#ifndef _LINUX_LISTHELP_H
+#define _LINUX_LISTHELP_H
+#include <stdlib.h>
+#include <string.h>
+#include "linux_list.h"
+
+/* Header to do more comprehensive job than linux/list.h; assume list
+ is first entry in structure. */
+
+/* Return pointer to first true entry, if any, or NULL. A macro
+ required to allow inlining of cmpfn. */
+#define LIST_FIND(head, cmpfn, type, args...) \
+({ \
+ const struct list_head *__i = (head); \
+ \
+ do { \
+ __i = __i->next; \
+ if (__i == (head)) { \
+ __i = NULL; \
+ break; \
+ } \
+ } while (!cmpfn((const type)__i , ## args)); \
+ (type)__i; \
+})
+
+#define LIST_FIND_W(head, cmpfn, type, args...) \
+({ \
+ const struct list_head *__i = (head); \
+ \
+ do { \
+ __i = __i->next; \
+ if (__i == (head)) { \
+ __i = NULL; \
+ break; \
+ } \
+ } while (!cmpfn((type)__i , ## args)); \
+ (type)__i; \
+})
+
+static inline int
+__list_cmp_same(const void *p1, const void *p2) { return p1 == p2; }
+
+/* Is this entry in the list? */
+static inline int
+list_inlist(struct list_head *head, const void *entry)
+{
+ return LIST_FIND(head, __list_cmp_same, void *, entry) != NULL;
+}
+
+/* Delete from list. */
+#define LIST_DELETE(head, oldentry) list_del((struct list_head *)oldentry)
+
+/* Append. */
+static inline void
+list_append(struct list_head *head, void *new)
+{
+ list_add((new), (head)->prev);
+}
+
+/* Prepend. */
+static inline void
+list_prepend(struct list_head *head, void *new)
+{
+ list_add(new, head);
+}
+
+/* Insert according to ordering function; insert before first true. */
+#define LIST_INSERT(head, new, cmpfn) \
+do { \
+ struct list_head *__i; \
+ for (__i = (head)->next; \
+ !cmpfn((new), (typeof (new))__i) && __i != (head); \
+ __i = __i->next); \
+ list_add((struct list_head *)(new), __i->prev); \
+} while(0)
+
+/* If the field after the list_head is a nul-terminated string, you
+ can use these functions. */
+static inline int __list_cmp_name(const void *i, const char *name)
+{
+ return strcmp(name, i+sizeof(struct list_head)) == 0;
+}
+
+/* Returns false if same name already in list, otherwise does insert. */
+static inline int
+list_named_insert(struct list_head *head, void *new)
+{
+ if (LIST_FIND(head, __list_cmp_name, void *,
+ new + sizeof(struct list_head)))
+ return 0;
+ list_prepend(head, new);
+ return 1;
+}
+
+/* Find this named element in the list. */
+#define list_named_find(head, name) \
+LIST_FIND(head, __list_cmp_name, void *, name)
+
+#endif /*_LISTHELP_H*/