--- ipac-ng-1.31.orig/agents/ipchains/libipfwc.c +++ ipac-ng-1.31/agents/ipchains/libipfwc.c @@ -501,6 +501,7 @@ " transparent proxying?)"); break; default: + break; } return message; } --- ipac-ng-1.31.orig/agents/iptables/iptables.c +++ ipac-ng-1.31/agents/iptables/iptables.c @@ -1108,6 +1108,7 @@ rule = new_rule(); chain = new_rule(); + chain->pkts = 1; strncpy(rule->name, cp+1, MAX_RULE_NAME_LENGTH); strncpy(chain->name, nextline->line, cp-nextline->line); --- ipac-ng-1.31.orig/debian/README.Debian +++ ipac-ng-1.31/debian/README.Debian @@ -0,0 +1,41 @@ +ipac-ng for Debian +------------------ + +Some hints about using ipac-ng: + +1.) +You have to restart the Accounting if your IP changes. +For example if you use ipac-ng on your ISDN dialup host +you have to add "/etc/init.d/ipac-ng restart" to your +interface up script. + +--8<-- +2.) +On live system if the ipmasq is restarted then the ipac-ng (also with the +old ipac package) finish to count any traffic. +The solution is simple: any ipmasq restart need an ipac-ng restart +The automatic solution that I adopt is to create the following very last +ipmasq rule that I call ZZZ|L_ipac.rul: + +# /etc/ipmasq/rules/ZZZ|L_ipac.rul +# +# Autor: Guido Bozzetto +# Date : 20011205 + +# +# Work only if it is the very last rule of the ipmasq rules. +# To speed-up the system start-up don't check at boot time. + +[ ! $runlevel ] && +for INITSCRIPT in /etc/init.d/ipac /etc/init.d/ipac-ng;do + if [ -s $INITSCRIPT ];then + if [ "$SHOWRULES" == "yes" ]; then + echo "#: Reload the IPAC(-NG) chains" + echo $INITSCRIPT force-reload + fi + if [ "$NOACT" != "yes" ]; then + $INITSCRIPT force-reload >/dev/null 2>&1 + fi + fi +done + --- ipac-ng-1.31.orig/debian/changelog +++ ipac-ng-1.31/debian/changelog @@ -0,0 +1,365 @@ +ipac-ng (1.31-3ubuntu1) feisty; urgency=low + + * Merge from Debian unstable, remaining changes: + - Change retained from 1.31-2ubuntu1: + + Fix up build deps + - Changes retained from 1.31-1ubuntu[1-3]: + + Copy rules.conf and ipac.conf from BTS #327344 + + Fix dpatch rules + + Include patches/00list + + Integrate dpatch into the build system. + + Add ipac-ng-1.31-iptables-1.3.1.dpatch, fixes ipac-ng with + iptables-1.3.1, taken from + https://sourceforge.net/tracker/index.php?func=detail&aid=1193721&group_id=40604&atid=428516 + + -- Padmanabhan Palanivelu Wed, 20 Dec 2006 11:30:50 +0000 + +ipac-ng (1.31-3) unstable; urgency=low + + * Changed email address. + + -- Daniel Baumann Tue, 12 Dec 2006 01:50:00 +0100 + +ipac-ng (1.31-2ubuntu1) edgy; urgency=low + + * Merge from debian unstable. + * Fix up build deps + + -- Andy Price Tue, 23 Aug 2006 00:02:29 +0100 + +ipac-ng (1.31-2) unstable; urgency=low + + * Cleanup rules a bit. + * Removed unnecessary build-depends (which are already in build-essential). + + -- Daniel Baumann Wed, 1 Mar 2006 13:21:00 +0100 + +ipac-ng (1.31-1ubuntu3) dapper; urgency=low + + * Copy rules.conf and ipac.conf from BTS #327344 + - Closes Malone: #28635 + + -- Barry deFreese Fri, 19 May 2006 18:39:54 -0400 + +ipac-ng (1.31-1ubuntu2) breezy; urgency=low + + * Fix dpatch rules + * Include patches/00list + + -- Trent Lloyd Fri, 9 Sep 2005 23:18:40 +0800 + +ipac-ng (1.31-1ubuntu1) breezy; urgency=low + + * Integrate dpatch into the build system. + * Add ipac-ng-1.31-iptables-1.3.1.dpatch, fixes ipac-ng + with iptables-1.3.1, taken from + https://sourceforge.net/tracker/index.php?func=detail&aid=1193721&group_id=40604&atid=428516 + + -- Trent Lloyd Wed, 7 Sep 2005 07:41:44 +0800 + +ipac-ng (1.31-1) unstable; urgency=high + + * New maintainer (Closes: #316896). + * New upstream release (Closes: #264823): + - note that the webfrontend is no longer part of ipac-ng. + * Added patch to fix problem with fetchipac (Needs testing, belongs to + #315351). + * Fixed spelling mistake in control (Closes: #293866). + * Fixed FTBS with gcc4 (Closes: #318352). + * Updated to new policy. + + -- Daniel Baumann Fri, 22 Jul 2005 13:53:00 +0200 + +ipac-ng (1.27-5.1) unstable; urgency=medium + + * NMU. + * Remove broken "sanity check" in gdbm.c to make gdbm storage work + on 64bit architectures (Closes: #299207). + + -- Alexander Wirt Sun, 8 May 2005 13:19:46 +0200 + +ipac-ng (1.27-5) unstable; urgency=low + + * compiled gdbm into ipac-ng but text is still default + (closes: Bug#241664) + + -- Noèl Köthe Sat, 08 May 2004 10:23:40 +0200 + +ipac-ng (1.27-4) unstable; urgency=low + + * gdbm storage has some problems and I heared nothing from + upstream.:( + woody has "plain-file" and I changed the default storage + back to "plain-file" to get the package working again without + big problems. Will test gdbm in coming upstream release more + intensive + (closes: Bug#202533) + (closes: Bug#202441) + * without gdbm there will be no verbose DELETE output (it mails + you the cleanup every month.) + (closes: Bug#209114) + * fixed unsecure htpasswd example + (closes: Bug#205201) + * ntpdate is started behind ipac-ng (ipac-ng 19, ntpdate 53) + (closes: Bug#204596) + * updated Standards-Version + + -- Noèl Köthe Mon, 29 Mar 2004 21:22:06 +0200 + +ipac-ng (1.27-3) unstable; urgency=low + + * corrected gdbm data collection + (closes: Bug#191004) + * fixed "make install" doesn't install ipac-convert + (closes: Bug#195736) + * removed test option, so the data will be store to /var/lib/ipac + again. + (closes: Bug#195735) + + -- Noel Koethe Tue, 1 Jul 2003 13:12:00 +0200 + +ipac-ng (1.27-2) unstable; urgency=low + + * rebuild for libgdbm + * updated Standards-Version: 3.5.10 + + -- Noel Koethe Fri, 13 Jun 2003 13:36:00 +0200 + +ipac-ng (1.27-1) unstable; urgency=low + + * new upstream from 2003-04-13 + * updated Standards-Version + * added project URL to description + * added hostname in subject in monthly traffic report + * gdbm is now default storage method + (closes: Bug#176743) + * added README to package and requested a merging of README and + README-NG so it doesn't confuse to have 2 files + (closes: Bug#187048) + + -- Noel Koethe Fri, 25 Apr 2003 22:13:00 +0100 + +ipac-ng (1.25-1) unstable; urgency=low + + * new upstream from 2002-12-29 + (closes: Bug#173980) + * ipac-ng starts and stops now with S19 so when shutdown the + machine it will not collide with firewall scripts with S20 + (closes: Bug#168433) + + -- Noel Koethe Fri, 10 Jan 2003 16:52:00 +0100 + +ipac-ng (1.24-2) unstable; urgency=low + + * corrected upstream file name to .orig... + + -- Noel Koethe Tue, 5 Nov 2002 10:42:00 +0100 + +ipac-ng (1.24-1) unstable; urgency=low + + * new upstream from 2002-09-28 + (closes: Bug#166695) + * fixed build problem (thanks Matt for your help) + (closes: Bug#165302) + I reported it upstream so it get fixed in the next release + https://sourceforge.net/tracker/index.php?func=detail&aid=633712&group_id=28513&atid=393575 + + -- Noel Koethe Tue, 5 Nov 2002 10:22:00 +0100 + +ipac-ng (1.23-4) unstable; urgency=low + + * added missing rules.conf and corrected ipac.conf + (closes: Bug#160595) + * you can choose your storage method now via + /etc/ipac-ng/ipac.conf; added gdbm + (closes: Bug#149836) + * corrected init.d script because of the new rule names + + -- Noel Koethe Wed, 2 Oct 2002 13:22:00 +0200 + +ipac-ng (1.23-3) unstable; urgency=low + + * upstream changed config file syntax. updated default + ipac.conf + (closes: Bug#160595) + + -- Noel Koethe Sat, 14 Sep 2002 15:26:00 +0200 + +ipac-ng (1.23-2) unstable; urgency=low + + * added flex to build deps because buildd fails + + -- Noel Koethe Tue, 10 Sep 2002 23:58:00 +0200 + +ipac-ng (1.23-1) unstable; urgency=low + + * new upstream 1.23 from 2002-08-27 + (closes: Bug#160402) + * corrected typo in init.d script + (closes: Bug#148532) + * corrected links in examples + (closes: Bug#155932) + * updated Standards-Version from 3.5.2 to 3.5.7 + + -- Noel Koethe Tue, 10 Sep 2002 21:43:00 +0200 + +ipac-ng (1.21-2) unstable; urgency=high + + * added libc6-dev | libc6.1-dev to build-deps + (closes: Bug#144855) + + -- Noel Koethe Sun, 28 Apr 2002 23:10:00 +0200 + +ipac-ng (1.21-1) unstable; urgency=low + + * new upstream version from 2002-02-27 + (closes: Bug#136082) + * upstream added + "Ipacsum switch for omitting zero bytes lines was added" + (closes: Bug#134238) + + -- Noel Koethe Wed, 27 Feb 2002 19:39:00 +0100 + +ipac-ng (1.20-2) unstable; urgency=low + + * corrected nntp port of default configuration + (closes: Bug#134228) + * getting accounting data before flushing the iptables rules + when stopping/restarting ipac-ng + (closes: Bug#134230) + * added nttps, rsync, telnets, pop3s to the defaul config (commented out) + (closes: Bug#134231) + + -- Noel Koethe Sat, 16 Feb 2002 14:42:00 +0100 + +ipac-ng (1.20-1) unstable; urgency=low + + * new upstream release from 2002-02-13 + + -- Noel Koethe Thu, 14 Feb 2002 18:48:00 +0100 + +ipac-ng (1.18-2) unstable; urgency=low + + * added "Recommends: libgd-perl" to debian/control + without this lib you cannot build png graphics + from your traffic + (closes: Bug#131806) + + -- Noel Koethe Fri, 1 Feb 2002 19:57:00 +0100 + +ipac-ng (1.18-1) unstable; urgency=low + + * new upstream release from 2002-01-27 + (closes: Bug#131131) + + -- Noel Koethe Sun, 27 Jan 2002 16:41:00 +0100 + +ipac-ng (1.17-1) unstable; urgency=low + + * new upstream release from 2002-01-26 + (closes: Bug#130919) + + -- Noel Koethe Sat, 26 Jan 2002 15:22:00 +0100 + +ipac-ng (1.16-1) unstable; urgency=low + + * new upstream release from 2002-01-23 + (closes: Bug#130649) + * upstream wrote me he fixed the "Index of counter + to big" problem. + (closes: Bug#121602) + (closes: Bug#124331) + * provide .diff.gz and .orig.tar.gz because its + not a debian native package (was wrong in 1.15-*) + (closes: Bug#130648) + * added debian/docs for the docs README-NG, TODO + + -- Noel Koethe Thu, 24 Jan 2002 12:36:00 +0100 + +ipac-ng (1.15-2) unstable; urgency=low + + * added example for multi IPs on one host + * changed the replacement from daily to monthly to get better + graphs with a better resolution + + -- Noel Koethe Fri, 18 Jan 2002 15:37:00 +0100 + +ipac-ng (1.15-1) unstable; urgency=low + + * new upstream version 1.15 from 2002-01-03 + (closes: Bug#127642) + + -- Noel Koethe Thu, 3 Jan 2002 17:49:00 +0100 + +ipac-ng (1.14-1) unstable; urgency=low + + * new upstream version 1.14 from 2001-12-13 + + -- Noel Koethe Sun, 16 Dec 2001 16:22:00 +0100 + +ipac-ng (1.13-1) unstable; urgency=low + + * new upstream version 1.13 from 2001-12-04 + * added 2 notes to README.Debian about ipmasq + and about interface restarts + (closes: Bug#121850) + (closes: Bug#122512) + * changed the default config file with more IP Accounting + * use port numbers instead of service names + (closes: Bug#116103) + + -- Noel Koethe Wed, 09 Dec 2001 20:29:00 +0100 + +ipac-ng (1.12-1) unstable; urgency=low + + * new upstream version 1.12 from 2001-11-18 + + -- Noel Koethe Sun, 18 Nov 2001 23:10:00 +0100 + +ipac-ng (1.11-1) unstable; urgency=low + + * new version 1.11 + * corrected manpage. ipacset(8) does not exist in ipac-ng + (closes: Bug#114039) + (closes: Bug#114009) + * fixed cron.monthly. mail wasn't send out (closes: Bug#114068) + * upstream added cgi to view Accounting (closes: Bug#113101) + + -- Noel Koethe Mon, 05 Oct 2001 23:40:00 +0200 + +ipac-ng (1.10-3) unstable; urgency=high + + * init.d and the cron jobs are now checking if netfilter is in + the kernel or loaded as module. If not it will load the module + returns an error. cron also test this so there shouldn't be an + error mail every 10 minutes from the Accounting collecting job + (closes: Bug#113703) + * corrected data directory (closes: Bug#113565) + * removed shellutils in depends (was insert because I use nice) to + make package lintian clean + + -- Noel Koethe Sat, 29 Sep 2001 12:19:00 +0200 + +ipac-ng (1.10-2) unstable; urgency=high + + * corrected debian/cron.d test of config file (closes: Bug#113288) + * corrected ./configure because config file (/etc/ipac-ng/ipac.conf) + was broken. It uses "files" in the local directory (closes: Bug#113104 + and Bug#113103) + * added "debianutils, mawk, bison, iptables" to the Build-Depends: in + debian/control (closes: Bug#113063) + * s/"usr/share/doc/ipacng"/"usr/share/doc/ipac-ng"/ in debian/dirs + (closes: Bug#113100) + + -- Noel Koethe Sun, 23 Sep 2001 17:07:11 +0200 + +ipac-ng (1.10-1) unstable; urgency=low + + * Initial Release (closes: Bug#111155) + + -- Noel Koethe Sun, 15 Sep 2001 14:16:56 +0200 + +Local variables: +mode: debian-changelog +End: + --- ipac-ng-1.31.orig/debian/compat +++ ipac-ng-1.31/debian/compat @@ -0,0 +1 @@ +4 --- ipac-ng-1.31.orig/debian/control +++ ipac-ng-1.31/debian/control @@ -0,0 +1,21 @@ +Source: ipac-ng +Section: net +Priority: optional +Maintainer: Daniel Baumann +Build-Depends: debhelper (>> 4.0.0), libc6-dev (>= 2.2.4-1) | libc6.1-dev (>= 2.2.4-1), debianutils (>= 2.14.1), mawk, bison, iptables, flex, libgdbm-dev, dpatch +Build-Conflicts: libpgsql-dev +Standards-Version: 3.6.2 + +Package: ipac-ng +Architecture: any +Depends: ${shlibs:Depends}, netbase, iptables, cron (>= 3.0pl1-42), perl +Recommends: libgd-perl +Suggests: modutils +Conflicts: ipac +Replaces: ipac +Description: IP Accounting for iptables (kernel >=2.4) + Inserts iptables rules to classify network traffic and monitors these + rules, writing the data to a file at a certain interval. It will then + allow one to calculate IP accounting data and statistics. + . + https://sf.net/projects/ipac-ng/ --- ipac-ng-1.31.orig/debian/copyright +++ ipac-ng-1.31/debian/copyright @@ -0,0 +1,13 @@ +This package was debianized by +Noèl Köthe on Sun, 15 Jul 2001 14:16:56 +0200. + +It was downloaded from https://sourceforge.net/projects/ipac-ng/ + +Upstream Author: kaiser13@mail2000.ru + +Copyright: + +Released under the terms of the GPL; see +/usr/share/common-licenses/GPL. + +initial written by Moritz Both, 1997, and enhanced by Al Zaharov, 2001. --- ipac-ng-1.31.orig/debian/cron.d +++ ipac-ng-1.31/debian/cron.d @@ -0,0 +1,12 @@ +# Run the ipac-ng fetch program every 10 minutes +# +#+--------------------- minute [0-59;*/10 means every 10 minutes (0,10,20,30,40,50)] +#| +----------------- hour [0-23] +#| | +--------------- day of month [1-31] +#| | | +------------- month [1-12] +#| | | | +----------- day of week [0-7; 0 or 7 is Sunday] +#| | | | | +-------- user +#| | | | | | +-- command +#| | | | | | | +*/10 * * * * root test -f /proc/net/ip_tables_names && test -f /etc/ipac-ng/ipac.conf && test -f /usr/sbin/fetchipac && test -d /var/lib/ipac/ && /usr/bin/nice /usr/sbin/fetchipac + --- ipac-ng-1.31.orig/debian/cron.monthly +++ ipac-ng-1.31/debian/cron.monthly @@ -0,0 +1,5 @@ +#!/bin/sh +test -f /proc/net/ip_tables_names && test -f /etc/ipac-ng/ipac.conf && test -f /usr/sbin/fetchipac && test -f /usr/sbin/ipacsum|| exit 0 + +# traffic use from last month mailed to root +/usr/bin/nice /usr/sbin/ipacsum --fixed-quantity K --replace --timeframe "last month"|mail -s "monthly trafficreport in kb, `date -d month+ago +%Y-%B`" root --- ipac-ng-1.31.orig/debian/dirs +++ ipac-ng-1.31/debian/dirs @@ -0,0 +1,10 @@ +etc/ipac-ng +etc/cron.d +etc/cron.monthly +etc/init.d +usr/sbin +usr/share/man/man8 +usr/share/doc/ipac-ng +var/lib/ipac +var/lock +var/run --- ipac-ng-1.31.orig/debian/docs +++ ipac-ng-1.31/debian/docs @@ -0,0 +1,2 @@ +README +TODO --- ipac-ng-1.31.orig/debian/init.d +++ ipac-ng-1.31/debian/init.d @@ -0,0 +1,68 @@ +#! /bin/sh +# +# ipac-ng init.d startup script +# noel@koethe.net, 2001-08-22 + +PATH=/sbin:/bin:/usr/sbin:/usr/bin +NAME=ipac-ng +DESC="IP Accounting" +CONFIG=/etc/ipac-ng/ipac.conf +DAEMON=/usr/sbin/fetchipac +PROC=/proc/net/ip_tables_names +IPTABLES=/sbin/iptables + +# if program and/or configfile are not present stop startup +test -f $DAEMON && test -f $CONFIG || exit 0 + +check() { + if ! [ -f $PROC ]; then + echo " module ip_tables not loaded, loading ..." + /sbin/modprobe ip_tables + if ! [ -f $PROC ]; then + echo " IP filter missing in kernel! You need to compile in CONFIG_IP_NF_IPTABLES" + exit 0 + fi + fi + + if ! [ -f $IPTABLES ]; then + echo " iptables is missing." + exit 0 + fi +} + +case "$1" in + start) + echo -n "Starting $DESC: " + check + $DAEMON -S + echo "$NAME." + ;; + stop) + echo -n "Stopping $DESC: " + # fetch accounting before removing the rules + $DAEMON + echo -n "flushing IP accounting rules ..." + $IPTABLES --flush ipac~o + $IPTABLES --flush ipac~i + $IPTABLES --flush ipac~fi + $IPTABLES --flush ipac~fo + # here should the ipac_* rule be removed from INPUT OUTPUT and FORWARD but --flush is + # evil because it would remove all rules in these chains. + echo "$NAME stopped." + ;; + restart | force-reload) + echo -n "Restarting $DESC: " + check + # get accounting datas + $DAEMON + # write iptables accounting rules again + $DAEMON -S + echo "$NAME." + ;; + *) + echo "Usage: /etc/init.d/ipac {start|stop|restart|force-reload}" + exit 1 + ;; +esac + +exit 0 --- ipac-ng-1.31.orig/debian/ipac.conf +++ ipac-ng-1.31/debian/ipac.conf @@ -0,0 +1,27 @@ +# This is the main ipac-ng configuration file. It contains the +# configuration directives that give the ipac-ng its instructions. +# Install as /etc/ipac-ng/ipac.conf + +## accouting agent. iptables and ipchains available now. +account agent = iptables + +## storage. gdbm, postgre and files supported. (files is not recommended) +#=20050909 storage = postgre +#=20050909 storage = gdbm +storage = plain-file + +## rules file +rules file = /etc/ipac-ng/rules.conf + +# dont store lines contains only zeroes to speedup processing and to save space +drop zero lines = yes + +## This parameters controls database location +## 'db host', 'db port' can be left blank for a local database +## as now, both databasess (access and storage) configured by these parameters +#db host = localhost +#db port = 5432 +## ATTENTION: no underscore '_' in the following parameters allowed! +db name = ipac +db user = ipac +db pass = "XXXXXXXX" --- ipac-ng-1.31.orig/debian/patches/00list +++ ipac-ng-1.31/debian/patches/00list @@ -0,0 +1 @@ +ipac-ng-1.31-iptables-1.3.1.dpatch --- ipac-ng-1.31.orig/debian/patches/ipac-ng-1.31-iptables-1.3.1.dpatch +++ ipac-ng-1.31/debian/patches/ipac-ng-1.31-iptables-1.3.1.dpatch @@ -0,0 +1,6571 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## ipac-ng-1.31-iptables-1.3.1.dpatch by Trent Lloyd +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: No description. + +@DPATCH@ +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/iptables.c /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/iptables.c +--- ./agents/iptables/iptables.c 2005-09-07 07:19:45.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/iptables.c 2005-09-07 07:40:07.000000000 +0800 +@@ -62,10 +62,6 @@ + #define FALSE 0 + #endif + +-#ifndef IPT_LIB_DIR +-#define IPT_LIB_DIR "/lib/iptables" +-#endif +- + #define FMT_NUMERIC 0x0001 + #define FMT_NOCOUNTS 0x0002 + #define FMT_KILOMEGAGIGA 0x0004 +@@ -91,7 +87,6 @@ + static struct option *opts = original_opts; + static unsigned int global_option_offset = 0; + +-extern char *authhost; + + /* - T.Mohan 5/7/2001 + * interface structure to pass to append rule +@@ -105,6 +100,13 @@ + + typedef struct iface_struct s_iface; + ++struct iptables_rule_match ++{ ++ struct iptables_rule_match *next; ++ ++ struct iptables_match *match; ++}; ++ + + /* Include file for additions: new matches and targets. */ + struct iptables_match +@@ -113,6 +115,9 @@ + + ipt_chainlabel name; + ++ /* Revision of match (0 by default). */ ++ u_int8_t revision; ++ + const char *version; + + /* Size of match data. */ +@@ -152,7 +157,6 @@ + unsigned int option_offset; + struct ipt_entry_match *m; + unsigned int mflags; +- unsigned int used; + }; + + struct iptables_target +@@ -161,6 +165,9 @@ + + ipt_chainlabel name; + ++ /* Revision of target (0 by default). */ ++ u_int8_t revision; ++ + const char *version; + + /* Size of target data. */ +@@ -202,6 +209,7 @@ + unsigned int used; + }; + ++ + enum ipt_tryload { + DONT_LOAD, + TRY_LOAD, +@@ -246,6 +254,9 @@ + * compiler warning. + */ + ++char *lib_dir = "/lib/iptables"; ++ ++ + void + exit_error(enum exittype status, char *msg, ...) + { +@@ -367,7 +378,7 @@ + * iptables-1.2.2 file:iptables.c + */ + +-void ++static void + parse_interface(const char *arg, char *vianame, unsigned char *mask) + { + int vialen = strlen(arg); +@@ -382,23 +393,25 @@ + " (%i)", arg, IFNAMSIZ-1); + + strcpy(vianame, arg); +- if (vialen == 0) ++ if ((vialen == 0) || (vialen == 1 && vianame[0] == '+')) + memset(mask, 0, IFNAMSIZ); + else if (vianame[vialen - 1] == '+') { + memset(mask, 0xFF, vialen - 1); + memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); ++ /* Don't remove `+' here! -HW */ + } else { + /* Include nul-terminator in match */ + memset(mask, 0xFF, vialen + 1); + memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); +- } + for (i = 0; vianame[i]; i++) { + if (!isalnum(vianame[i]) + && vianame[i] != '_' +- && vianame[i] != '+' + && vianame[i] != '.') { +- exit_error(PARAMETER_PROBLEM, "Warning: weird character in interface" +- " `%s' (No aliases, :, ! or *).\n", vianame); ++ printf("Warning: wierd character in interface" ++ " `%s' (No aliases, :, ! or *).\n", ++ vianame); ++ break; ++ } + } + } + } +@@ -429,20 +442,27 @@ + } + + int +-check_inverse(const char option[], int *invert) ++check_inverse(const char option[], int *invert, int *optind, int argc) + { + if (option && strcmp(option, "!") == 0) { + if (*invert) + exit_error(PARAMETER_PROBLEM, + "Multiple `!' flags not allowed"); +- + *invert = TRUE; ++ if (optind) { ++ *optind = *optind+1; ++ if (argc && *optind > argc) ++ exit_error(PARAMETER_PROBLEM, ++ "no argument following `!'"); ++ } ++ + return TRUE; + } + return FALSE; + } + + // --------------------------------------------------------------------- ++/* code copied from iptables 1.3.1 */ + // --------------------------------------------------------------------- + + static char * +@@ -509,7 +529,7 @@ + return addr_to_dotted(addr); + } + +-static char * ++char * + mask_to_dotted(const struct in_addr *mask) + { + int i; +@@ -535,22 +555,19 @@ + return buf; + } + ++ + static struct ipt_entry * + generate_entry(const struct ipt_entry *fw, +- struct iptables_match *matches, ++ struct iptables_rule_match *matches, + struct ipt_entry_target *target) + { + unsigned int size; +- struct iptables_match *m; ++ struct iptables_rule_match *matchp; + struct ipt_entry *e; + + size = sizeof(struct ipt_entry); +- for (m = matches; m; m = m->next) { +- if (!m->used) +- continue; +- +- size += m->m->u.match_size; +- } ++ for (matchp = matches; matchp; matchp = matchp->next) ++ size += matchp->match->m->u.match_size; + + e = xmalloc(size + target->u.target_size); + *e = *fw; +@@ -558,12 +575,9 @@ + e->next_offset = size + target->u.target_size; + + size = 0; +- for (m = matches; m; m = m->next) { +- if (!m->used) +- continue; +- +- memcpy(e->elems + size, m->m, m->m->u.match_size); +- size += m->m->u.match_size; ++ for (matchp = matches; matchp; matchp = matchp->next) { ++ memcpy(e->elems + size, matchp->match->m, matchp->match->m->u.match_size); ++ size += matchp->match->m->u.match_size; + } + memcpy(e->elems + size, target, target->u.target_size); + +@@ -575,15 +589,17 @@ + int procfile; + char *ret; + ++#define PROCFILE_BUFSIZ 1024 + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + +- ret = malloc(1024); ++ ret = (char *) malloc(PROCFILE_BUFSIZ); + if (ret) { +- switch (read(procfile, ret, 1024)) { ++ memset(ret, 0, PROCFILE_BUFSIZ); ++ switch (read(procfile, ret, PROCFILE_BUFSIZ)) { + case -1: goto fail; +- case 1024: goto fail; /* Partial read. Wierd */ ++ case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */ + } + if (ret[strlen(ret)-1]=='\n') + ret[strlen(ret)-1]=0; +@@ -618,22 +634,22 @@ + } + + if (!ptr && tryload != DONT_LOAD) { +- char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") ++ char path[strlen(lib_dir) + sizeof("/libipt_.so") + + strlen(name)]; +- sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); ++ sprintf(path, "%s/libipt_%s.so", lib_dir, name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified match as a target. */ + ptr = find_target(name, DONT_LOAD); + if (!ptr) { +- fprintf(stderr, "Couldn't load target `%s'\n", ++ exit_error(PARAMETER_PROBLEM, ++ "Couldn't load target `%s'\n", + name); +- exit(1); + } + } else if (tryload == LOAD_MUST_SUCCEED) { +- fprintf(stderr, "Couldn't load target `%s':%s\n", ++ exit_error(PARAMETER_PROBLEM, ++ "Couldn't load target `%s':%s\n", + name, dlerror()); +- exit(1); + } + } + +@@ -647,8 +663,9 @@ + { + char *buf = NULL; + char *argv[3]; ++ int status; + +-// If they don't explicitly set it, read out of kernel ++ /* If they don't explicitly set it, read out of kernel */ + if (!modprobe) { + buf = get_modprobe(); + if (!buf) +@@ -664,16 +681,18 @@ + execv(argv[0], argv); + + // not usually reached +- exit(0); ++ exit(1); + case -1: + return -1; + + default: // parent +- wait(NULL); ++ wait(&status); + } + + free(buf); ++ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + return 0; ++ return -1; + } + + void +@@ -687,7 +706,7 @@ + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: target `%s' has invalid size %u.\n", +- "fddfgdsse", me->name, me->size); ++ "fddfgdsse", me->name, (unsigned int)me->size); + exit(1); + } + +@@ -698,20 +717,17 @@ + me->tflags = 0; + } + +-unsigned char * make_delete_mask(struct ipt_entry *fw) ++static unsigned char * ++make_delete_mask(struct ipt_entry *fw, struct iptables_rule_match *matches) + { + /* Establish mask for comparison */ + unsigned int size; +- struct iptables_match *m; ++ struct iptables_rule_match *matchp; + unsigned char *mask, *mptr; + + size = sizeof(struct ipt_entry); +- for (m = iptables_matches; m; m = m->next) { +- if (!m->used) +- continue; +- +- size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; +- } ++ for (matchp = matches; matchp; matchp = matchp->next) ++ size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size; + + mask = xcalloc(1, size + + IPT_ALIGN(sizeof(struct ipt_entry_target)) +@@ -720,14 +736,11 @@ + memset(mask, 0xFF, sizeof(struct ipt_entry)); + mptr = mask + sizeof(struct ipt_entry); + +- for (m = iptables_matches; m; m = m->next) { +- if (!m->used) +- continue; +- ++ for (matchp = matches; matchp; matchp = matchp->next) { + memset(mptr, 0xFF, + IPT_ALIGN(sizeof(struct ipt_entry_match)) +- + m->userspacesize); +- mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; ++ + matchp->match->userspacesize); ++ mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + matchp->match->size; + } + + memset(mptr, 0xFF, +@@ -738,7 +751,7 @@ + } + + struct iptables_match * +-find_match(const char *name, enum ipt_tryload tryload) ++find_match(const char *name, enum ipt_tryload tryload, struct iptables_rule_match **matches) + { + struct iptables_match *ptr; + +@@ -748,28 +761,37 @@ + } + + if (!ptr && tryload != DONT_LOAD) { +- char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") ++ char path[strlen(lib_dir) + sizeof("/libipt_.so") + + strlen(name)]; +- sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); ++ sprintf(path, "%s/libipt_%s.so", lib_dir, name); + if (dlopen(path, RTLD_NOW)) { + /* Found library. If it didn't register itself, + maybe they specified target as match. */ +- ptr = find_match(name, DONT_LOAD); ++ ptr = find_match(name, DONT_LOAD, NULL); + + if (!ptr) { +- fprintf(stderr, "Couldn't load match `%s'\n", ++ exit_error(PARAMETER_PROBLEM, ++ "Couldn't load match `%s'\n", + name); +- exit(1); + } + } else if (tryload == LOAD_MUST_SUCCEED) { +- fprintf(stderr, "Couldn't load match `%s':%s\n", ++ exit_error(PARAMETER_PROBLEM, ++ "Couldn't load match `%s':%s\n", + name, dlerror()); +- exit(1); + } + } + +- if (ptr) +- ptr->used = 1; ++ if (ptr && matches) { ++ struct iptables_rule_match **i; ++ struct iptables_rule_match *newentry; ++ ++ newentry = xmalloc(sizeof(struct iptables_rule_match)); ++ ++ for (i = matches; *i; i = &(*i)->next); ++ newentry->match = ptr; ++ newentry->next = NULL; ++ *i = newentry; ++ } + + return ptr; + } +@@ -779,7 +801,7 @@ + { + struct iptables_match **i; + +- if (find_match(me->name, DONT_LOAD)) { ++ if (find_match(me->name, DONT_LOAD, NULL)) { + fprintf(stderr, "%s: match `%s' already registered.\n", + "fetchipac??", me->name); + exit(1); +@@ -787,7 +809,7 @@ + + if (me->size != IPT_ALIGN(me->size)) { + fprintf(stderr, "%s: match `%s' has invalid size %u.\n", +- "fetchipac??", me->name, me->size); ++ "fetchipac??", me->name, (unsigned int)me->size); + exit(1); + } + +@@ -801,16 +823,21 @@ + } + + ++/* Christophe Burki wants `-p 6' to imply `-m tcp'. */ + static struct iptables_match * +-find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) ++find_proto(const char *pname, enum ipt_tryload tryload, int nolookup, struct iptables_rule_match **matches) + { +- int proto; ++ unsigned int proto; + +- proto = string_to_number(pname, 0, 255); +- if (proto != -1) +- return find_match(proto_to_name(proto, nolookup), tryload); ++ if ((proto = string_to_number(pname, 0, 255)) != -1) { ++ char *protoname = proto_to_name(proto, nolookup); + +- return find_match(pname, tryload); ++ if (protoname) ++ return find_match(protoname, tryload, matches); ++ } else ++ return find_match(pname, tryload, matches); ++ ++ return NULL; + } + + static void +@@ -823,15 +850,19 @@ + number = (number + 500) / 1000; + if (number > 9999) { + number = (number + 500) / 1000; +- printf(FMT("%4lluG ","%lluG "),number); ++ if (number > 9999) { ++ number = (number + 500) / 1000; ++ printf(FMT("%4lluT ","%lluT "), (unsigned long long)number); + } +- else printf(FMT("%4lluM ","%lluM "), number); ++ else printf(FMT("%4lluG ","%lluG "), (unsigned long long)number); ++ } ++ else printf(FMT("%4lluM ","%lluM "), (unsigned long long)number); + } else +- printf(FMT("%4lluK ","%lluK "), number); ++ printf(FMT("%4lluK ","%lluK "), (unsigned long long)number); + } else +- printf(FMT("%5llu ","%llu "), number); ++ printf(FMT("%5llu ","%llu "), (unsigned long long)number); + } else +- printf(FMT("%8llu ","%llu "), number); ++ printf(FMT("%8llu ","%llu "), (unsigned long long)number); + } + + static int +@@ -839,7 +870,7 @@ + const struct ipt_ip *ip, + int numeric) + { +- struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); ++ struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD, NULL); + + if (match) { + if (match->print) +@@ -867,9 +898,6 @@ + u_int8_t flags; + char buf[BUFSIZ]; + +- /* User creates a chain called "REJECT": this overrides the +- `REJECT' target module. Keep feeding them rope until the +- revolution... Bwahahahahah */ + if (!iptc_is_chain(targname, handle)) + target = find_target(targname, TRY_LOAD); + else +@@ -917,10 +945,6 @@ + + if (fw->ip.iniface[0] != '\0') { + strcat(iface, fw->ip.iniface); +- /* If it doesn't compare the nul-term, it's a +- wildcard. */ +- if (fw->ip.iniface_mask[strlen(fw->ip.iniface)] == 0) +- strcat(iface, "+"); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); +@@ -934,10 +958,6 @@ + + if (fw->ip.outiface[0] != '\0') { + strcat(iface, fw->ip.outiface); +- /* If it doesn't compare the nul-term, it's a +- wildcard. */ +- if (fw->ip.outiface_mask[strlen(fw->ip.outiface)] == 0) +- strcat(iface, "+"); + } + else if (format & FMT_NUMERIC) strcat(iface, "*"); + else strcat(iface, "any"); +@@ -979,7 +999,7 @@ + target->print(&fw->ip, t, format & FMT_NUMERIC); + } else if (t->u.target_size != sizeof(*t)) + printf("[%u bytes of unknown target data] ", +- t->u.target_size - sizeof(*t)); ++ (unsigned int)(t->u.target_size - sizeof(*t))); + + if (!(format & FMT_NONEWLINE)) + fputc('\n', stdout); +@@ -996,6 +1016,15 @@ + } + + ++static void set_revision(char *name, u_int8_t revision) ++{ ++ /* Old kernel sources don't have ".revision" field, ++ but we stole a byte from name. */ ++ name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; ++ name[IPT_FUNCTION_MAXNAMELEN - 1] = revision; ++} ++ ++ + // --------------------------------------------------------------------- + + +@@ -1130,8 +1159,7 @@ + chain->pkts++; + continue; + } +- +- counters = iptc_read_counter(chain->name, chain->pkts, &handle); ++ counters = iptc_read_counter(chain->name, chain->pkts, &handle); // ???? why chain->pkts + if (counters) { + iptc_zero_counter(chain->name, chain->pkts, &handle); + chain->pkts++; +@@ -1193,7 +1221,7 @@ + * + */ + static int +-prepare_entry (raw_rule_type *d, struct ipt_entry **e) ++prepare_entry (raw_rule_type *d, struct ipt_entry **e, struct iptables_rule_match **matches) + { + struct ipt_entry fw; + unsigned int naddrs = 0; +@@ -1201,10 +1229,14 @@ + struct iptables_match *m; + struct iptables_target *target = NULL; + struct iptables_target *t; ++ ++ struct iptables_rule_match *matchp; ++ + size_t size; + int inverse; + int c,argc; + int invert = 0; ++ int proto_used = 0; + + bzero(&fw, sizeof(fw)); + +@@ -1234,7 +1266,6 @@ + + for (m = iptables_matches; m; m = m->next) { + m->mflags = 0; +- m->used = 0; + } + + for (t = iptables_targets; t; t = t->next) { +@@ -1280,6 +1311,8 @@ + target->t = xcalloc(1, size); + target->t->u.target_size = size; + strcpy(target->t->u.user.name, d->target); ++ set_revision(target->t->u.user.name, target->revision); ++ if (target->init != NULL) + target->init(target->t, &fw.nfcache); + + if(check_inverse_type(d->protocol)) +@@ -1291,7 +1324,7 @@ + } + + if (d->protocol[0] != '\0' && d->protocol[0] != 'i') { +- m = find_proto(d->protocol, LOAD_MUST_SUCCEED, 0); ++ m = find_proto(d->protocol, LOAD_MUST_SUCCEED, 0, matches); + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + m->m = xcalloc(size, 1); + m->m->u.match_size = size; +@@ -1339,7 +1372,7 @@ + while ((c = getopt_long(argc, d->extension,"-m:", opts, NULL))!= -1) { + switch (c) { + case 'm': +- m = find_match(optarg, LOAD_MUST_SUCCEED); ++ m = find_match(optarg, LOAD_MUST_SUCCEED, matches); + size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; + m->m = xcalloc(1, size); + m->m->u.match_size = size; +@@ -1362,32 +1395,80 @@ + exit(1); + + default: +- for (m = iptables_matches; m; m = m->next) { +- if (!m->used) +- continue; +- if (m->parse(c - m->option_offset, ++ ++ /* FIXME: This scheme doesn't allow two of the same ++ matches --RR */ ++ if (!target ++ || !(target->parse(c - target->option_offset, ++ d->extension, invert, ++ &target->tflags, ++ &fw, &target->t))) { ++ for (matchp = *matches; matchp; matchp = matchp->next) { ++ if (matchp->match->parse(c - matchp->match->option_offset, + d->extension, invert, +- &m->mflags, +- &fw, &fw.nfcache, &m->m)) ++ &matchp->match->mflags, ++ &fw, ++ &fw.nfcache, ++ &matchp->match->m)) + break; + } +- break; ++ ++ if (m == NULL ++ && d->protocol ++ && (!find_proto(d->protocol, DONT_LOAD, ++ 0, NULL) ++ || (find_proto(d->protocol, DONT_LOAD, ++ 0, NULL) ++ && (proto_used == 0)) ++ ) ++ && (m = find_proto(d->protocol, TRY_LOAD, ++ 0, matches))) { ++ /* Try loading protocol */ ++ size_t size; ++ ++ proto_used = 1; ++ ++ size = IPT_ALIGN(sizeof(struct ipt_entry_match)) ++ + m->size; ++ ++ m->m = xcalloc(1, size); ++ m->m->u.match_size = size; ++ strcpy(m->m->u.user.name, m->name); ++ set_revision(m->m->u.user.name, ++ m->revision); ++ if (m->init != NULL) ++ m->init(m->m, &fw.nfcache); ++ ++ opts = merge_options(opts, ++ m->extra_opts, &m->option_offset); ++ ++ optind--; ++ continue; ++ } ++ ++ m = matchp ? matchp->match : NULL; ++ if (!m) ++ exit_error(PARAMETER_PROBLEM, ++ "Unknown arg `%s'", ++ d->extension); + } + } + } +- for (m = iptables_matches; m; m = m->next) { +- if (!m->used) +- continue; +- m->final_check(m->mflags); + } + ++ for (matchp = *matches; matchp; matchp = matchp->next) ++ matchp->match->final_check(matchp->match->mflags); ++ ++ if (target) + target->final_check(target->tflags); +- *e = generate_entry(&fw, iptables_matches, target->t); ++ ++ *e = generate_entry(&fw, *matches, target->t); ++ free(target->t); ++ + + if (!handle) if (!(handle = iptc_init("filter"))) + exit_error(PARAMETER_PROBLEM, + "iptables: %s\n", iptc_strerror(errno)); +- + return 0; + } + +@@ -1400,9 +1481,11 @@ + insert_rule(raw_rule_type *d, int rule_num) + { + struct ipt_entry *e = NULL; ++ struct iptables_rule_match *matches = NULL; ++ + int ret=1; + +- if (prepare_entry(d, &e)!=0) ++ if (prepare_entry(d, &e, &matches)!=0) + return (1); + if (verbose>1) { + printf("Inserting rule\n"); +@@ -1422,9 +1505,10 @@ + replace_rule (raw_rule_type *d, int rule_num) + { + struct ipt_entry *e = NULL; ++ struct iptables_rule_match *matches = NULL; + int ret=1; + +- if (prepare_entry(d, &e)!=0) ++ if (prepare_entry(d, &e, &matches)!=0) + return (1); + + if (verbose>1) { +@@ -1450,8 +1534,9 @@ + append_rule (raw_rule_type *d) + { + struct ipt_entry *e = NULL; ++ struct iptables_rule_match *matches = NULL; + +- if (prepare_entry(d, &e)!=0) ++ if (prepare_entry(d, &e, &matches)!=0) + return (1); + + if (verbose>1) { +@@ -1473,9 +1558,11 @@ + { + struct ipt_entry *e = NULL; + unsigned char *mask = NULL; ++ struct iptables_rule_match *matches = NULL; ++ + int ret=1; + +- if (prepare_entry(d, &e)!=0) ++ if (prepare_entry(d, &e, &matches)!=0) + return (1); + + if (verbose>1) { +@@ -1483,7 +1570,7 @@ + print_firewall_line(e, handle); + } + +- mask = make_delete_mask(e); ++ mask = make_delete_mask(e, matches); + ret &= iptc_delete_entry(d->dest, e, mask, &handle); + free(e); + return ret; +@@ -1493,10 +1580,8 @@ + delete_num_rule (char *chain, int num) + { + struct ipt_entry *e = NULL; +- unsigned char *mask = NULL; + int ret = 1; + +- mask = make_delete_mask(e); + ret &= iptc_delete_num_entry(chain, num, &handle); + free(e); + return ret; +@@ -1589,11 +1674,9 @@ + if (!handle) + handle = iptc_init("filter"); + +- if (!handle) { +-// try to insmod the module if iptc_init failed +- iptables_insmod("ip_tables", modprobe); ++ /* try to insmod the module if iptc_init failed */ ++ if (!handle && iptables_insmod("ip_tables", modprobe) != -1) + handle = iptc_init("filter"); +- } + + if (!handle) { + fprintf(stderr, "ipac-ng: can't initialize iptables table `filter'\n" +@@ -1618,7 +1701,6 @@ + setup_rules(void) + { + raw_rule_type *d, *d1; +- char targ[MAX_RULE_NAME_LENGTH+2]; + char chain[MAX_RULE_NAME_LENGTH+2]; + FILE *frunfile; + +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/iptables.c.orig /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/iptables.c.orig +--- ./agents/iptables/iptables.c.orig 1970-01-01 08:00:00.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/iptables.c.orig 2005-09-07 07:19:45.000000000 +0800 +@@ -0,0 +1,1786 @@ ++/* ++ * ++ * $Id: iptables.c,v 1.7 2004/06/27 22:08:54 friedl Exp $ ++ * ++ * postgresql backend to ipac ++ * Copyright (C) 2001-2003 Al Zakharov ++ * ++ * 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. ++ * ++ * The author can be reached via email: kaiser13@mail2000.ru, or by ++ * fido: Al_Zaharov@p88.f58.n5005.z2.fidonet.org ++ * ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* - T.Mohan 5/7/2001 ++ * #include for isalnum() in parse_interface() ++ */ ++#include ++#include ++#include "ipac.h" ++#include "libiptc.h" ++#include "../../lib/libnet.h" ++ ++ ++#ifndef PROC_SYS_MODPROBE ++#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" ++#endif ++ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++#ifndef FALSE ++#define FALSE 0 ++#endif ++ ++#ifndef IPT_LIB_DIR ++#define IPT_LIB_DIR "/lib/iptables" ++#endif ++ ++#define FMT_NUMERIC 0x0001 ++#define FMT_NOCOUNTS 0x0002 ++#define FMT_KILOMEGAGIGA 0x0004 ++#define FMT_OPTIONS 0x0008 ++#define FMT_NOTABLE 0x0010 ++#define FMT_NOTARGET 0x0020 ++#define FMT_VIA 0x0040 ++#define FMT_NONEWLINE 0x0080 ++#define FMT_LINENUMBERS 0x0100 ++ ++#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ ++ | FMT_NUMERIC | FMT_NOTABLE) ++#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab)) ++ ++#define OPTION_OFFSET 256 ++ ++static struct option original_opts[] = { ++ { "match", 1, 0, 'm' }, ++ { 0 } ++}; ++ ++ ++static struct option *opts = original_opts; ++static unsigned int global_option_offset = 0; ++ ++extern char *authhost; ++ ++/* - T.Mohan 5/7/2001 ++ * interface structure to pass to append rule ++ */ ++ ++struct iface_struct { ++ char* ifaceIn; ++ char* ifaceOut; ++ int invflags; ++}; ++ ++typedef struct iface_struct s_iface; ++ ++ ++/* Include file for additions: new matches and targets. */ ++struct iptables_match ++{ ++ struct iptables_match *next; ++ ++ ipt_chainlabel name; ++ ++ const char *version; ++ ++ /* Size of match data. */ ++ size_t size; ++ ++ /* Size of match data relevent for userspace comparison purposes */ ++ size_t userspacesize; ++ ++ /* Function which prints out usage message. */ ++ void (*help)(void); ++ ++ /* Initialize the match. */ ++ void (*init)(struct ipt_entry_match *m, unsigned int *nfcache); ++ ++ /* Function which parses command options; returns true if it ++ ate an option */ ++ int (*parse)(int c, char **argv, int invert, unsigned int *flags, ++ const struct ipt_entry *entry, ++ unsigned int *nfcache, ++ struct ipt_entry_match **match); ++ ++ /* Final check; exit if not ok. */ ++ void (*final_check)(unsigned int flags); ++ ++ /* Prints out the match iff non-NULL: put space at end */ ++ void (*print)(const struct ipt_ip *ip, ++ const struct ipt_entry_match *match, int numeric); ++ ++ /* Saves the match info in parsable form to stdout. */ ++ void (*save)(const struct ipt_ip *ip, ++ const struct ipt_entry_match *match); ++ ++ /* Pointer to list of extra command-line options */ ++ const struct option *extra_opts; ++ ++ /* Ignore these men behind the curtain: */ ++ unsigned int option_offset; ++ struct ipt_entry_match *m; ++ unsigned int mflags; ++ unsigned int used; ++}; ++ ++struct iptables_target ++{ ++ struct iptables_target *next; ++ ++ ipt_chainlabel name; ++ ++ const char *version; ++ ++ /* Size of target data. */ ++ size_t size; ++ ++ /* Size of target data relevent for userspace comparison purposes */ ++ size_t userspacesize; ++ ++ /* Function which prints out usage message. */ ++ void (*help)(void); ++ ++ /* Initialize the target. */ ++ void (*init)(struct ipt_entry_target *t, unsigned int *nfcache); ++ ++ /* Function which parses command options; returns true if it ++ ate an option */ ++ int (*parse)(int c, char **argv, int invert, unsigned int *flags, ++ const struct ipt_entry *entry, ++ struct ipt_entry_target **target); ++ ++ /* Final check; exit if not ok. */ ++ void (*final_check)(unsigned int flags); ++ ++ /* Prints out the target iff non-NULL: put space at end */ ++ void (*print)(const struct ipt_ip *ip, ++ const struct ipt_entry_target *target, int numeric); ++ ++ /* Saves the targinfo in parsable form to stdout. */ ++ void (*save)(const struct ipt_ip *ip, ++ const struct ipt_entry_target *target); ++ ++ /* Pointer to list of extra command-line options */ ++ struct option *extra_opts; ++ ++ /* Ignore these men behind the curtain: */ ++ unsigned int option_offset; ++ struct ipt_entry_target *t; ++ unsigned int tflags; ++ unsigned int used; ++}; ++ ++enum ipt_tryload { ++ DONT_LOAD, ++ TRY_LOAD, ++ LOAD_MUST_SUCCEED ++}; ++ ++ ++/** structure for run file (rule file) */ ++struct runfile_line_type { ++ char *line; ++ struct runfile_line_type *next; ++}; ++ ++static iptc_handle_t handle; ++ ++struct iptables_match *iptables_matches = NULL; ++struct iptables_target *iptables_targets = NULL; ++ ++extern void register_match(struct iptables_match *me); ++extern void register_target(struct iptables_target *me); ++ ++/* plain file ipac interface entries */ ++int iptables_ipac_init(int flag); ++int iptables_ipac_set(rule_type **firstrule, int first); ++int iptables_ipac_read(rule_type **firstrule); ++int iptables_ipac_check(void); ++ ++static const acc_agent_t interface_entry = { ++ "iptables", ++ iptables_ipac_init, ++ iptables_ipac_set, ++ iptables_ipac_read, ++ iptables_ipac_check, ++}; ++ ++const acc_agent_t *ipac_ag_interface_iptables() { ++ return &interface_entry; ++} ++ ++/* - T.Mohan 5/7/2001 ++ * exit_error(). moved here to avoid impicit declaration ++ * compiler warning. ++ */ ++ ++void ++exit_error(enum exittype status, char *msg, ...) ++{ ++ va_list args; ++ ++ va_start(args, msg); ++// fprintf(stderr, "%s v%s: ", program_name, program_version); ++ vfprintf(stderr, msg, args); ++ va_end(args); ++ fprintf(stderr, "\n"); ++// if (status == PARAMETER_PROBLEM) ++// exit_tryhelp(status); ++ if (status == VERSION_PROBLEM) ++ fprintf(stderr, "Perhaps iptables or your " ++ "kernel needs to be upgraded.\n"); ++ exit(status); ++} ++ ++//=================================================== ++static int ++tcp_service_to_port(const char *name) ++{ ++ struct servent *service; ++ ++ if ((service = getservbyname(name, "tcp")) != NULL) ++ return ntohs((unsigned short) service->s_port); ++ ++ return -1; ++} ++ ++static u_int16_t ++parse_tcp_port(const char *port) ++{ ++ int portnum; ++ ++ if ((portnum = string_to_number(port, 0, 65535)) != -1 || ++ (portnum = tcp_service_to_port(port)) != -1) ++ return (u_int16_t)portnum; ++ ++ fprintf(stderr, "invalid TCP port/service `%s' specified\n", port); ++ exit(1); ++} ++ ++/* - T.Mohan 5/7/2001 ++ * parse_tcp_ports() function source from ++ * iptables-1.2.2 file:extensions/ibip6t_tcp.c ++ */ ++ ++static void ++parse_tcp_ports(const char *portstring, u_int16_t *ports) ++{ ++ char *buffer; ++ char *cp; ++ ++ buffer = strdup(portstring); ++ if ((cp = strchr(buffer, ':')) == NULL) ++ ports[0] = ports[1] = parse_tcp_port(buffer); ++ else { ++ *cp = '\0'; ++ cp++; ++ ++ ports[0] = buffer[0] ? parse_tcp_port(buffer) : 0; ++ ports[1] = cp[0] ? parse_tcp_port(cp) : 0xFFFF; ++ } ++ free(buffer); ++} ++ ++static int ++udp_service_to_port(const char *name) ++{ ++ struct servent *service; ++ ++ if ((service = getservbyname(name, "udp")) != NULL) ++ return ntohs((unsigned short) service->s_port); ++ ++ return -1; ++} ++ ++static u_int16_t ++parse_udp_port(const char *port) ++{ ++ int portnum; ++ ++ if ((portnum = string_to_number(port, 0, 65535)) != -1 || ++ (portnum = udp_service_to_port(port)) != -1) ++ return (u_int16_t)portnum; ++ ++ fprintf(stderr, "invalid UDP port/service `%s' specified\n", port); ++ exit(1); ++} ++ ++/* - T.Mohan 5/7/2001 ++ * parse_udp_ports() function source from ++ * iptables-1.2.2 file:extensions/ibip6t_udp.c ++ */ ++ ++static void ++parse_udp_ports(const char *portstring, u_int16_t *ports) ++{ ++ char *buffer; ++ char *cp; ++ ++ buffer = strdup(portstring); ++ if ((cp = strchr(buffer, ':')) == NULL) ++ ports[0] = ports[1] = parse_udp_port(buffer); ++ else { ++ *cp = '\0'; ++ cp++; ++ ++ ports[0] = buffer[0] ? parse_udp_port(buffer) : 0; ++ ports[1] = cp[0] ? parse_udp_port(cp) : 0xFFFF; ++ } ++ free(buffer); ++} ++ ++ ++/* - T.Mohan 5/7/2001 ++ * parse_interface() function source modified from ++ * iptables-1.2.2 file:iptables.c ++ */ ++ ++void ++parse_interface(const char *arg, char *vianame, unsigned char *mask) ++{ ++ int vialen = strlen(arg); ++ unsigned int i; ++ ++ memset(mask, 0, IFNAMSIZ); ++ memset(vianame, 0, IFNAMSIZ); ++ ++ if (vialen + 1 > IFNAMSIZ) ++ exit_error(PARAMETER_PROBLEM, ++ "interface name `%s' must be shorter than IFNAMSIZ" ++ " (%i)", arg, IFNAMSIZ-1); ++ ++ strcpy(vianame, arg); ++ if (vialen == 0) ++ memset(mask, 0, IFNAMSIZ); ++ else if (vianame[vialen - 1] == '+') { ++ memset(mask, 0xFF, vialen - 1); ++ memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); ++ } else { ++ /* Include nul-terminator in match */ ++ memset(mask, 0xFF, vialen + 1); ++ memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); ++ } ++ for (i = 0; vianame[i]; i++) { ++ if (!isalnum(vianame[i]) ++ && vianame[i] != '_' ++ && vianame[i] != '+' ++ && vianame[i] != '.') { ++ exit_error(PARAMETER_PROBLEM, "Warning: weird character in interface" ++ " `%s' (No aliases, :, ! or *).\n", vianame); ++ } ++ } ++} ++ ++int ++check_inverse_type(char* src) ++{ ++ if (src) { ++ if (memcmp(src, "!", 1) == 0) { ++ int slen = strlen(src); ++ ++ //strip the "!" ++ memcpy(src, src+1, slen); ++ ++ //if all there was, was a `!' after doing the strip, ++ // return no inverse and don't complain about it. ++ if (slen == 1) ++ return 0; ++ ++ if (memcmp(src, "!", 1) == 0) ++ exit_error(PARAMETER_PROBLEM, ++ "Multiple `!' flags not allowed"); ++ ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++int ++check_inverse(const char option[], int *invert) ++{ ++ if (option && strcmp(option, "!") == 0) { ++ if (*invert) ++ exit_error(PARAMETER_PROBLEM, ++ "Multiple `!' flags not allowed"); ++ ++ *invert = TRUE; ++ return TRUE; ++ } ++ return FALSE; ++} ++ ++// --------------------------------------------------------------------- ++// --------------------------------------------------------------------- ++ ++static char * ++proto_to_name(u_int8_t proto, int nolookup) ++{ ++ unsigned int i; ++ ++ if (proto && !nolookup) { ++ struct protoent *pent = getprotobynumber(proto); ++ if (pent) ++ return pent->p_name; ++ } ++ ++ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++) ++ if (chain_protos[i].num == proto) ++ return chain_protos[i].name; ++ ++ return NULL; ++} ++ ++static char * ++addr_to_network(const struct in_addr *addr) ++{ ++ struct netent *net; ++ ++ if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL) ++ return (char *) net->n_name; ++ ++ return (char *) NULL; ++} ++ ++char * ++addr_to_dotted(const struct in_addr *addrp) ++{ ++ static char buf[20]; ++ const unsigned char *bytep; ++ ++ bytep = (const unsigned char *) &(addrp->s_addr); ++ sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]); ++ return buf; ++} ++ ++static char * ++addr_to_host(const struct in_addr *addr) ++{ ++ struct hostent *host; ++ ++ if ((host = gethostbyaddr((char *) addr, ++ sizeof(struct in_addr), AF_INET)) != NULL) ++ return (char *) host->h_name; ++ ++ return (char *) NULL; ++} ++ ++static char * ++addr_to_anyname(const struct in_addr *addr) ++{ ++ char *name; ++ ++ if ((name = addr_to_host(addr)) != NULL || ++ (name = addr_to_network(addr)) != NULL) ++ return name; ++ ++ return addr_to_dotted(addr); ++} ++ ++static char * ++mask_to_dotted(const struct in_addr *mask) ++{ ++ int i; ++ static char buf[20]; ++ u_int32_t maskaddr, bits; ++ ++ maskaddr = ntohl(mask->s_addr); ++ ++ if (maskaddr == 0xFFFFFFFFL) ++ /* we don't want to see "/32" */ ++ return ""; ++ ++ i = 32; ++ bits = 0xFFFFFFFEL; ++ while (--i >= 0 && maskaddr != bits) ++ bits <<= 1; ++ if (i >= 0) ++ sprintf(buf, "/%d", i); ++ else ++ /* mask was not a decent combination of 1's and 0's */ ++ sprintf(buf, "/%s", addr_to_dotted(mask)); ++ ++ return buf; ++} ++ ++static struct ipt_entry * ++generate_entry(const struct ipt_entry *fw, ++ struct iptables_match *matches, ++ struct ipt_entry_target *target) ++{ ++ unsigned int size; ++ struct iptables_match *m; ++ struct ipt_entry *e; ++ ++ size = sizeof(struct ipt_entry); ++ for (m = matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ ++ size += m->m->u.match_size; ++ } ++ ++ e = xmalloc(size + target->u.target_size); ++ *e = *fw; ++ e->target_offset = size; ++ e->next_offset = size + target->u.target_size; ++ ++ size = 0; ++ for (m = matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ ++ memcpy(e->elems + size, m->m, m->m->u.match_size); ++ size += m->m->u.match_size; ++ } ++ memcpy(e->elems + size, target, target->u.target_size); ++ ++ return e; ++} ++ ++static char *get_modprobe(void) ++{ ++ int procfile; ++ char *ret; ++ ++ procfile = open(PROC_SYS_MODPROBE, O_RDONLY); ++ if (procfile < 0) ++ return NULL; ++ ++ ret = malloc(1024); ++ if (ret) { ++ switch (read(procfile, ret, 1024)) { ++ case -1: goto fail; ++ case 1024: goto fail; /* Partial read. Wierd */ ++ } ++ if (ret[strlen(ret)-1]=='\n') ++ ret[strlen(ret)-1]=0; ++ close(procfile); ++ return ret; ++ } ++ fail: ++ free(ret); ++ close(procfile); ++ return NULL; ++} ++ ++struct iptables_target * ++find_target(const char *name, enum ipt_tryload tryload) ++{ ++ struct iptables_target *ptr; ++ ++ /* Standard target? */ ++ if (strcmp(name, "") == 0 ++ || strcmp(name, IPTC_LABEL_ACCEPT) == 0 ++ || strcmp(name, IPTC_LABEL_DROP) == 0 ++ || strcmp(name, IPTC_LABEL_QUEUE) == 0 ++ || strcmp(name, IPTC_LABEL_RETURN) == 0) ++ name = "standard"; ++ ++ ++ for (ptr = iptables_targets; ptr; ptr = ptr->next) { ++ if (strcmp(name, ptr->name) == 0) ++ { ++ break; ++ } ++ } ++ ++ if (!ptr && tryload != DONT_LOAD) { ++ char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") ++ + strlen(name)]; ++ sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); ++ if (dlopen(path, RTLD_NOW)) { ++ /* Found library. If it didn't register itself, ++ maybe they specified match as a target. */ ++ ptr = find_target(name, DONT_LOAD); ++ if (!ptr) { ++ fprintf(stderr, "Couldn't load target `%s'\n", ++ name); ++ exit(1); ++ } ++ } else if (tryload == LOAD_MUST_SUCCEED) { ++ fprintf(stderr, "Couldn't load target `%s':%s\n", ++ name, dlerror()); ++ exit(1); ++ } ++ } ++ ++ if (ptr) ++ ptr->used = 1; ++ ++ return ptr; ++} ++ ++static int iptables_insmod(const char *modname, const char *modprobe) ++{ ++ char *buf = NULL; ++ char *argv[3]; ++ ++// If they don't explicitly set it, read out of kernel ++ if (!modprobe) { ++ buf = get_modprobe(); ++ if (!buf) ++ return -1; ++ modprobe = buf; ++ } ++ ++ switch (fork()) { ++ case 0: ++ argv[0] = (char *)modprobe; ++ argv[1] = (char *)modname; ++ argv[2] = NULL; ++ execv(argv[0], argv); ++ ++// not usually reached ++ exit(0); ++ case -1: ++ return -1; ++ ++ default: // parent ++ wait(NULL); ++ } ++ ++ free(buf); ++ return 0; ++} ++ ++void ++register_target(struct iptables_target *me) ++{ ++ if (find_target(me->name, DONT_LOAD)) { ++ fprintf(stderr, "%s: target `%s' already registered.\n", ++ "fddfewev", me->name); ++ exit(1); ++ } ++ ++ if (me->size != IPT_ALIGN(me->size)) { ++ fprintf(stderr, "%s: target `%s' has invalid size %u.\n", ++ "fddfgdsse", me->name, me->size); ++ exit(1); ++ } ++ ++// Prepend to list. ++ me->next = iptables_targets; ++ iptables_targets = me; ++ me->t = NULL; ++ me->tflags = 0; ++} ++ ++unsigned char * make_delete_mask(struct ipt_entry *fw) ++{ ++ /* Establish mask for comparison */ ++ unsigned int size; ++ struct iptables_match *m; ++ unsigned char *mask, *mptr; ++ ++ size = sizeof(struct ipt_entry); ++ for (m = iptables_matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ ++ size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; ++ } ++ ++ mask = xcalloc(1, size ++ + IPT_ALIGN(sizeof(struct ipt_entry_target)) ++ + iptables_targets->size); ++ ++ memset(mask, 0xFF, sizeof(struct ipt_entry)); ++ mptr = mask + sizeof(struct ipt_entry); ++ ++ for (m = iptables_matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ ++ memset(mptr, 0xFF, ++ IPT_ALIGN(sizeof(struct ipt_entry_match)) ++ + m->userspacesize); ++ mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; ++ } ++ ++ memset(mptr, 0xFF, ++ IPT_ALIGN(sizeof(struct ipt_entry_target)) ++ + iptables_targets->userspacesize); ++ ++ return mask; ++} ++ ++struct iptables_match * ++find_match(const char *name, enum ipt_tryload tryload) ++{ ++ struct iptables_match *ptr; ++ ++ for (ptr = iptables_matches; ptr; ptr = ptr->next) { ++ if (strcmp(name, ptr->name) == 0) ++ break; ++ } ++ ++ if (!ptr && tryload != DONT_LOAD) { ++ char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so") ++ + strlen(name)]; ++ sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name); ++ if (dlopen(path, RTLD_NOW)) { ++ /* Found library. If it didn't register itself, ++ maybe they specified target as match. */ ++ ptr = find_match(name, DONT_LOAD); ++ ++ if (!ptr) { ++ fprintf(stderr, "Couldn't load match `%s'\n", ++ name); ++ exit(1); ++ } ++ } else if (tryload == LOAD_MUST_SUCCEED) { ++ fprintf(stderr, "Couldn't load match `%s':%s\n", ++ name, dlerror()); ++ exit(1); ++ } ++ } ++ ++ if (ptr) ++ ptr->used = 1; ++ ++ return ptr; ++} ++ ++void ++register_match(struct iptables_match *me) ++{ ++ struct iptables_match **i; ++ ++ if (find_match(me->name, DONT_LOAD)) { ++ fprintf(stderr, "%s: match `%s' already registered.\n", ++ "fetchipac??", me->name); ++ exit(1); ++ } ++ ++ if (me->size != IPT_ALIGN(me->size)) { ++ fprintf(stderr, "%s: match `%s' has invalid size %u.\n", ++ "fetchipac??", me->name, me->size); ++ exit(1); ++ } ++ ++ /* Append to list. */ ++ for (i = &iptables_matches; *i; i = &(*i)->next); ++ me->next = NULL; ++ *i = me; ++ ++ me->m = NULL; ++ me->mflags = 0; ++} ++ ++ ++static struct iptables_match * ++find_proto(const char *pname, enum ipt_tryload tryload, int nolookup) ++{ ++ int proto; ++ ++ proto = string_to_number(pname, 0, 255); ++ if (proto != -1) ++ return find_match(proto_to_name(proto, nolookup), tryload); ++ ++ return find_match(pname, tryload); ++} ++ ++static void ++print_num(u_int64_t number, unsigned int format) ++{ ++ if (format & FMT_KILOMEGAGIGA) { ++ if (number > 99999) { ++ number = (number + 500) / 1000; ++ if (number > 9999) { ++ number = (number + 500) / 1000; ++ if (number > 9999) { ++ number = (number + 500) / 1000; ++ printf(FMT("%4lluG ","%lluG "),number); ++ } ++ else printf(FMT("%4lluM ","%lluM "), number); ++ } else ++ printf(FMT("%4lluK ","%lluK "), number); ++ } else ++ printf(FMT("%5llu ","%llu "), number); ++ } else ++ printf(FMT("%8llu ","%llu "), number); ++} ++ ++static int ++print_match(const struct ipt_entry_match *m, ++ const struct ipt_ip *ip, ++ int numeric) ++{ ++ struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD); ++ ++ if (match) { ++ if (match->print) ++ match->print(ip, m, numeric); ++ else ++ printf("%s ", match->name); ++ } else { ++ if (m->u.user.name[0]) ++ printf("UNKNOWN match `%s' ", m->u.user.name); ++ } ++ /* Don't stop iterating. */ ++ return 0; ++} ++ ++/* e is called `fw' here for hysterical raisins */ ++static void ++print_firewall(const struct ipt_entry *fw, ++ const char *targname, ++ unsigned int num, ++ unsigned int format, ++ const iptc_handle_t handle) ++{ ++ struct iptables_target *target = NULL; ++ const struct ipt_entry_target *t; ++ u_int8_t flags; ++ char buf[BUFSIZ]; ++ ++ /* User creates a chain called "REJECT": this overrides the ++ `REJECT' target module. Keep feeding them rope until the ++ revolution... Bwahahahahah */ ++ if (!iptc_is_chain(targname, handle)) ++ target = find_target(targname, TRY_LOAD); ++ else ++ target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); ++ ++ t = ipt_get_target((struct ipt_entry *)fw); ++ flags = fw->ip.flags; ++ ++ if (format & FMT_LINENUMBERS) ++ printf(FMT("%-4u ", "%u "), num+1); ++ ++ if (!(format & FMT_NOCOUNTS)) { ++ print_num(fw->counters.pcnt, format); ++ print_num(fw->counters.bcnt, format); ++ } ++ ++ if (!(format & FMT_NOTARGET)) ++ printf(FMT("%-9s ", "%s "), targname); ++ ++ fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout); ++ { ++ char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC); ++ if (pname) ++ printf(FMT("%-5s", "%s "), pname); ++ else ++ printf(FMT("%-5hu", "%hu "), fw->ip.proto); ++ } ++ ++ if (format & FMT_OPTIONS) { ++ if (format & FMT_NOTABLE) ++ fputs("opt ", stdout); ++ fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout); ++ fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout); ++ fputc(' ', stdout); ++ } ++ ++ if (format & FMT_VIA) { ++ char iface[IFNAMSIZ+2]; ++ ++ if (fw->ip.invflags & IPT_INV_VIA_IN) { ++ iface[0] = '!'; ++ iface[1] = '\0'; ++ } ++ else iface[0] = '\0'; ++ ++ if (fw->ip.iniface[0] != '\0') { ++ strcat(iface, fw->ip.iniface); ++ /* If it doesn't compare the nul-term, it's a ++ wildcard. */ ++ if (fw->ip.iniface_mask[strlen(fw->ip.iniface)] == 0) ++ strcat(iface, "+"); ++ } ++ else if (format & FMT_NUMERIC) strcat(iface, "*"); ++ else strcat(iface, "any"); ++ printf(FMT(" %-6s ","in %s "), iface); ++ ++ if (fw->ip.invflags & IPT_INV_VIA_OUT) { ++ iface[0] = '!'; ++ iface[1] = '\0'; ++ } ++ else iface[0] = '\0'; ++ ++ if (fw->ip.outiface[0] != '\0') { ++ strcat(iface, fw->ip.outiface); ++ /* If it doesn't compare the nul-term, it's a ++ wildcard. */ ++ if (fw->ip.outiface_mask[strlen(fw->ip.outiface)] == 0) ++ strcat(iface, "+"); ++ } ++ else if (format & FMT_NUMERIC) strcat(iface, "*"); ++ else strcat(iface, "any"); ++ printf(FMT("%-6s ","out %s "), iface); ++ } ++ ++ fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout); ++ if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC)) ++ printf(FMT("%-19s ","%s "), "anywhere"); ++ else { ++ if (format & FMT_NUMERIC) ++ sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src))); ++ else ++ sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src))); ++ strcat(buf, mask_to_dotted(&(fw->ip.smsk))); ++ printf(FMT("%-19s ","%s "), buf); ++ } ++ ++ fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout); ++ if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC)) ++ printf(FMT("%-19s","-> %s"), "anywhere"); ++ else { ++ if (format & FMT_NUMERIC) ++ sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst))); ++ else ++ sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst))); ++ strcat(buf, mask_to_dotted(&(fw->ip.dmsk))); ++ printf(FMT("%-19s","-> %s"), buf); ++ } ++ ++ if (format & FMT_NOTABLE) ++ fputs(" ", stdout); ++ ++ IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC); ++ ++ if (target) { ++ if (target->print) ++ /* Print the target information. */ ++ target->print(&fw->ip, t, format & FMT_NUMERIC); ++ } else if (t->u.target_size != sizeof(*t)) ++ printf("[%u bytes of unknown target data] ", ++ t->u.target_size - sizeof(*t)); ++ ++ if (!(format & FMT_NONEWLINE)) ++ fputc('\n', stdout); ++} ++ ++static void ++print_firewall_line(const struct ipt_entry *fw, ++ const iptc_handle_t h) ++{ ++ struct ipt_entry_target *t; ++ ++ t = ipt_get_target((struct ipt_entry *)fw); ++ print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h); ++} ++ ++ ++// --------------------------------------------------------------------- ++ ++ ++/** delete run file (rule file) from memory, freeing dynamically ++ * allocated memory ++ */ ++static void ++destroy_runfile_lines(struct runfile_line_type *lines) ++{ ++ struct runfile_line_type *next; ++ while(lines != NULL) ++ { ++ if (lines->line != NULL) ++ free(lines->line); ++ next = lines->next; ++ free(lines); ++ lines = next; ++ } ++} ++ ++//------------------------------------------------------------------ ++/** read run file (rule file) and store it in memory, using a singly ++ * linked list of instances of struct runfile_line_type ++ * return the list read or NULL in case of error ++ */ ++static struct runfile_line_type ++*read_runfile() ++{ ++ FILE *frunfile; ++ char runfile_line[MAX_RULE_NAME_LENGTH*2], *cp; ++ struct runfile_line_type *result, *lastline, *cur; ++ ++ int tmp=0; ++ ++ frunfile = fopen(RUNFILE, "r"); ++ if (frunfile == NULL) { ++ fprintf(stderr, "%s: cant open run file \"%s\": %s " ++ "(fetchipac -S not ran?)\n", ++ me, RUNFILE, strerror(errno)); ++ return NULL; ++ } ++ ++ result = NULL; ++ lastline = NULL; ++ while(fgets(runfile_line, MAX_RULE_NAME_LENGTH*2, frunfile) != NULL) { ++ tmp++; ++ cp = strchr(runfile_line, '\n'); ++ if (cp) ++ *cp = '\0'; ++ if (*runfile_line == '#') ++ continue; ++ ++ cur = (struct runfile_line_type *) ++ xmalloc(sizeof(struct runfile_line_type)); ++ cur->line = xstrdup(runfile_line); ++ cur->next = NULL; ++ if (result == NULL) ++ result = cur; ++ else ++ lastline->next = cur; ++ lastline = cur; ++ } ++ if (!feof(frunfile)) { ++ fprintf(stderr, "%s: reading \"%s\": %s\n", ++ me, RUNFILE, strerror(errno)); ++ fclose(frunfile); ++ destroy_runfile_lines(result); ++ result = NULL; ++ } ++ fclose(frunfile); ++ return result; ++} ++ ++/** read kernel accounting data in iptables system ++ * read from stream f ++ * create records with data (packet and data counters) and ++ * rule names and store them into instances of rule_type (declared ++ * in ipac.h) using rule names from runfile ++ * if a rule name is equal to a previously used rule name, add the ++ * counters and re-use the old record ++ * complain if something is wrong with the data. ++ * return 0 for success, 1 otherwise ++ */ ++static int ++read_iptables(struct runfile_line_type *runfile, rule_type **firstrule) ++{ ++ char *cp="\0"; ++ rule_type *rule, *lastrule; ++ rule_type *chain, *lastchain, *firstchain; ++ struct runfile_line_type *nextline; ++ void *node; ++ void *ruletree = NULL; ++ void *chaintree = NULL; ++ struct ipt_counters *counters = NULL; ++ ++ if (handle) ++ iptc_commit(&handle); // we need fresh snapshot of the rules ++ ++ iptables_ipac_init(0); // init after commit is a must ++ ++ /* create the rule_type records in correct order as from ++ * run file. ++ */ ++ lastrule = *firstrule = NULL; ++ chain = lastchain = firstchain = NULL; ++ for (nextline=runfile; nextline!=NULL; nextline=nextline->next) { ++ cp = strchr(nextline->line, '|'); ++ if (cp == 0) ++ continue; /* bad entry */ ++ rule = new_rule(); ++ ++ chain = new_rule(); ++ chain->pkts = 1; ++ ++ strncpy(rule->name, cp+1, MAX_RULE_NAME_LENGTH); ++ strncpy(chain->name, nextline->line, cp-nextline->line); ++ chain->name[cp-nextline->line]='\0'; ++ ++ node = tsearch(chain, &chaintree, rule_compare); ++ if (*(rule_type **)node != chain) { ++ free(chain); ++ chain=*(rule_type **)node; // chain is already there ++ } else { ++ if (lastchain != NULL) ++ lastchain->next = chain; ++ lastchain = chain; ++ if (firstchain == NULL) ++ firstchain = chain; ++ } ++ ++ if (rule->name[0] == '%') { ++ chain->pkts++; ++ continue; ++ } ++ ++ counters = iptc_read_counter(chain->name, chain->pkts, &handle); ++ if (counters) { ++ iptc_zero_counter(chain->name, chain->pkts, &handle); ++ chain->pkts++; ++ } ++ ++ /* use a binary tree to find rules with same name */ ++ node = tsearch(rule, &ruletree, rule_compare); ++ if (*(rule_type **)node != rule) { ++ free(rule); ++ rule=*(rule_type **)node; ++ } else { ++ if (lastrule != NULL) ++ lastrule->next = rule; ++ lastrule = rule; ++ if (*firstrule == NULL) ++ *firstrule = rule; ++ } ++ if (counters) { ++ rule->pkts += counters->pcnt; ++ rule->bytes += counters->bcnt; ++ } else { ++ rule->pkts = 0; ++ rule->bytes = 0; ++ } ++ } ++ iptc_commit(&handle); ++ iptables_ipac_init(0); ++ free_tree(&ruletree); ++ free_tree(&chaintree); ++ return 0; ++} ++ ++static struct option * ++merge_options(struct option *oldopts, const struct option *newopts, ++ unsigned int *option_offset) ++{ ++ unsigned int num_old, num_new, i; ++ struct option *merge; ++ ++ for (num_old = 0; oldopts[num_old].name; num_old++); ++ for (num_new = 0; newopts[num_new].name; num_new++); ++ ++ global_option_offset += OPTION_OFFSET; ++ *option_offset = global_option_offset; ++ ++ merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); ++ memcpy(merge, oldopts, num_old * sizeof(struct option)); ++ for (i = 0; i < num_new; i++) { ++ merge[num_old + i] = newopts[i]; ++ merge[num_old + i].val += *option_offset; ++ } ++ memset(merge + num_old + num_new, 0, sizeof(struct option)); ++ ++ return merge; ++} ++ ++/* ++ * Prepare ipt entry for such a funcs as insert, delete, append, replace rule ++ * ++ */ ++static int ++prepare_entry (raw_rule_type *d, struct ipt_entry **e) ++{ ++ struct ipt_entry fw; ++ unsigned int naddrs = 0; ++ struct in_addr *addrs = NULL; ++ struct iptables_match *m; ++ struct iptables_target *target = NULL; ++ struct iptables_target *t; ++ size_t size; ++ int inverse; ++ int c,argc; ++ int invert = 0; ++ ++ bzero(&fw, sizeof(fw)); ++ ++ if (verbose>2) ++ printf("preparing entry for '%s' chain\n", d->dest); ++ ++ if (!strcmp(d->protocol, "all")) ++ d->protocol[0]='\0'; ++ ++ if (d->iface && strlen(d->iface)>1) { ++ if ((!strncmp(d->dest+strlen(d->dest)-2, "~o", 2)) || ++ (!strncmp(d->dest+strlen(d->dest)-3, "~fi", 3)) || ++ (!strncmp(d->dest+strlen(d->dest)-4, "~c_o", 4)) || ++ (!strncmp(d->dest+strlen(d->dest)-5, "~c_fi", 5))) { ++ inverse = check_inverse_type(d->iface); ++ parse_interface(d->iface, fw.ip.iniface, fw.ip.iniface_mask); ++ fw.ip.invflags |= (inverse ? IPT_INV_VIA_IN : 0); ++ fw.nfcache = NFC_IP_IF_IN; ++ } else { ++ inverse = check_inverse_type(d->iface); ++ parse_interface(d->iface, fw.ip.outiface, fw.ip.outiface_mask); ++ fw.ip.invflags |= (inverse ? IPT_INV_VIA_OUT : 0); ++ fw.nfcache = NFC_IP_IF_OUT; ++ } ++ } else ++ fw.ip.invflags = 0; ++ ++ for (m = iptables_matches; m; m = m->next) { ++ m->mflags = 0; ++ m->used = 0; ++ } ++ ++ for (t = iptables_targets; t; t = t->next) { ++ t->tflags = 0; ++ t->used = 0; ++ } ++ ++ if (!iptc_is_chain(d->dest, handle)) { ++ fprintf(stderr, "%s is not a chain\n", d->dest); ++ return (1); ++ } ++ ++ if (strlen(d->snet)>2) { ++ if (check_inverse_type(d->snet)) ++ fw.ip.invflags |= IPT_INV_SRCIP; ++ parse_hostnetworkmask(d->snet, &addrs, &(fw.ip.smsk), &naddrs); ++ if (naddrs>1) ++ exit_error(PARAMETER_PROBLEM, ++ "Incorrect rule: more than 1 source address\n"); ++ fw.ip.src.s_addr = addrs[0].s_addr; ++ fw.nfcache |= NFC_IP_SRC; ++ } ++ ++ if (strlen(d->dnet)>2) { ++ if (check_inverse_type(d->dnet)) ++ fw.ip.invflags |= IPT_INV_DSTIP; ++ parse_hostnetworkmask(d->dnet, &addrs, &(fw.ip.dmsk), &naddrs); ++ if (naddrs>1) ++ exit_error(PARAMETER_PROBLEM, "Incorrect rule: more than 1 " ++ "destination address\n"); ++ fw.ip.dst.s_addr = addrs[0].s_addr; ++ fw.nfcache |= NFC_IP_DST; ++ } ++ ++ if ((d->sport[0]!='\0' || d->dport[0]!='\0') && d->protocol[0]=='\0') ++ exit_error(PARAMETER_PROBLEM, "Incorrect rule: source or " ++ "destination port specified while protocol is not. sport='%s', dport='%s'\n", ++ d->sport, d->dport); ++ ++ /* Loading target /if any/ */ ++ target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED); ++ size = sizeof(struct ipt_entry_target) + target->size; ++ target->t = xcalloc(1, size); ++ target->t->u.target_size = size; ++ strcpy(target->t->u.user.name, d->target); ++ target->init(target->t, &fw.nfcache); ++ ++ if(check_inverse_type(d->protocol)) ++ fw.ip.invflags |= IPT_INV_PROTO; ++ ++ if (d->protocol[0] != '\0') { ++ fw.ip.proto = parse_protocol(d->protocol); ++ fw.nfcache |= NFC_IP_PROTO; ++ } ++ ++ if (d->protocol[0] != '\0' && d->protocol[0] != 'i') { ++ m = find_proto(d->protocol, LOAD_MUST_SUCCEED, 0); ++ size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; ++ m->m = xcalloc(size, 1); ++ m->m->u.match_size = size; ++ strcpy(m->m->u.user.name, m->name); ++ m->init(m->m, &fw.nfcache); ++ ++ if (d->sport[0]!='\0' || d->dport[0]!='\0') { ++ if (!strcmp(d->protocol, "tcp")) { ++ struct ipt_tcp *tcpinfo = (struct ipt_tcp *)m->m->data; ++ ++ if (d->sport[0]!='\0') ++ /* - T.Mohan 5/7/2001 */ ++ parse_tcp_ports(d->sport, tcpinfo->spts); ++ ++ if (d->dport[0]!='\0') ++ /* - T.Mohan 5/7/2001 */ ++ parse_tcp_ports(d->dport, tcpinfo->dpts); ++ } ++ ++ if (!strcmp(d->protocol, "udp")) { ++ struct ipt_udp *udpinfo = ++ (struct ipt_udp *)m->m->data; ++ if (d->sport[0]!='\0') ++ /* - T.Mohan 5/7/2001 */ ++ parse_udp_ports(d->sport, udpinfo->spts); ++ ++ if (d->dport[0]!='\0') ++ /* - T.Mohan 5/7/2001 */ ++ parse_udp_ports(d->dport, udpinfo->dpts); ++ } ++ } ++ } ++ ++ if (d->extension[2]) { ++ opts = original_opts; ++ optind = 0; ++ global_option_offset = 0; ++ ++ d->extension[0] = xstrdup("fetchipac"); ++ d->extension[1] = xstrdup("-m"); ++ ++ for (argc=0;d->extension[argc];argc++); ++ ++ // parse extension ++ while ((c = getopt_long(argc, d->extension,"-m:", opts, NULL))!= -1) { ++ switch (c) { ++ case 'm': ++ m = find_match(optarg, LOAD_MUST_SUCCEED); ++ size = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size; ++ m->m = xcalloc(1, size); ++ m->m->u.match_size = size; ++ strcpy(m->m->u.user.name, m->name); ++ m->init(m->m, &fw.nfcache); ++ opts = merge_options(opts, m->extra_opts, ++ &m->option_offset); ++ break; ++ case 1: ++ if (optarg[0] == '!' && optarg[1] == '\0') { ++ if (invert) ++ exit_error(PARAMETER_PROBLEM, ++ "multiple consecutive ! " ++ "not allowed"); ++ invert = TRUE; ++ optarg[0] = '\0'; ++ continue; ++ } ++ printf("Bad argument `%s'\n", optarg); ++ exit(1); ++ ++ default: ++ for (m = iptables_matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ if (m->parse(c - m->option_offset, ++ d->extension, invert, ++ &m->mflags, ++ &fw, &fw.nfcache, &m->m)) ++ break; ++ } ++ break; ++ } ++ } ++ } ++ for (m = iptables_matches; m; m = m->next) { ++ if (!m->used) ++ continue; ++ m->final_check(m->mflags); ++ } ++ ++ target->final_check(target->tflags); ++ *e = generate_entry(&fw, iptables_matches, target->t); ++ ++ if (!handle) if (!(handle = iptc_init("filter"))) ++ exit_error(PARAMETER_PROBLEM, ++ "iptables: %s\n", iptc_strerror(errno)); ++ ++ return 0; ++} ++ ++/* ++ * Try to insert rule into kernel return 0 in case all right, 1 otherwise ++ */ ++static int ++//insert_rule (char *chain, char *saddr, char *sport, char *daddr, char *dport, ++// char *proto, char *targ, int rule_num, char *iface) ++insert_rule(raw_rule_type *d, int rule_num) ++{ ++ struct ipt_entry *e = NULL; ++ int ret=1; ++ ++ if (prepare_entry(d, &e)!=0) ++ return (1); ++ if (verbose>1) { ++ printf("Inserting rule\n"); ++ print_firewall_line(e, handle); ++ } ++ ret &= iptc_insert_entry(d->dest, e, rule_num, &handle); ++ free(e); ++ return ret; ++} ++ ++/* ++ * Try to atomically replace rule in kernel return 0 in case all right, 1 otherwice ++ */ ++static int ++//replace_rule (char *chain, char *saddr, char *sport, char *daddr, char *dport, ++// char *proto, char *targ, int rule_num, char *iface) ++replace_rule (raw_rule_type *d, int rule_num) ++{ ++ struct ipt_entry *e = NULL; ++ int ret=1; ++ ++ if (prepare_entry(d, &e)!=0) ++ return (1); ++ ++ if (verbose>1) { ++ printf("Replacing rule %d in '%s'\n", rule_num, d->dest); ++ print_firewall_line(e, handle); ++ } ++ ret &= iptc_replace_entry(d->dest, e, rule_num, &handle); ++ free(e); ++ return ret; ++} ++ ++/* ++ * Try to append rule into kernel return 0 in case all right, 1 otherwice ++ */ ++ ++/* - T.Mohan 5/7/2001 ++ * append_rule () modified to pass struct iface_struct ++ * parse_tcp_ports() & parse_udp_ports, correctly parses xxxx:xxxx style port rules ++ */ ++static int ++//append_rule (char *chain, char *saddr, char *sport, char *daddr, char *dport, ++// char *proto, char *targ, char *iface) ++append_rule (raw_rule_type *d) ++{ ++ struct ipt_entry *e = NULL; ++ ++ if (prepare_entry(d, &e)!=0) ++ return (1); ++ ++ if (verbose>1) { ++ printf("Appending rule to chain '%s'\n", d->dest); ++ print_firewall_line(e, handle); ++ } ++ if (!iptc_append_entry(d->dest, e, &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ free(e); ++ return 0; ++} ++ ++static int ++//delete_rule (char *chain, char *saddr, char *sport, char *daddr, char *dport, ++// char *proto, char *targ, char *iface) ++delete_rule (raw_rule_type *d) ++{ ++ struct ipt_entry *e = NULL; ++ unsigned char *mask = NULL; ++ int ret=1; ++ ++ if (prepare_entry(d, &e)!=0) ++ return (1); ++ ++ if (verbose>1) { ++ printf("Deleting rule\n"); ++ print_firewall_line(e, handle); ++ } ++ ++ mask = make_delete_mask(e); ++ ret &= iptc_delete_entry(d->dest, e, mask, &handle); ++ free(e); ++ return ret; ++} ++ ++static int ++delete_num_rule (char *chain, int num) ++{ ++ struct ipt_entry *e = NULL; ++ unsigned char *mask = NULL; ++ int ret = 1; ++ ++ mask = make_delete_mask(e); ++ ret &= iptc_delete_num_entry(chain, num, &handle); ++ free(e); ++ return ret; ++} ++ ++ ++ ++/** Setup chains if they doesn't exist ++ * ++ */ ++static int ++setup_tables(void) ++{ ++ if (verbose) ++ fprintf(stderr, "Setup tables..\n"); ++ if (!iptc_is_chain("ipac~fi", handle)) ++ if (!iptc_create_chain("ipac~fi", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (!iptc_is_chain("ipac~fo", handle)) ++ if (!iptc_create_chain("ipac~fo", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (!iptc_is_chain("ipac~i", handle)) ++ if (!iptc_create_chain("ipac~i", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (!iptc_is_chain("ipac~o", handle)) ++ if (!iptc_create_chain("ipac~o", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ return 0; ++}; ++ ++static int ++flush_acc_tables(void) ++{ ++ raw_rule_type *d, *d1; ++ ++ if (iptc_is_chain("ipac~fi", handle)) ++ if (!iptc_flush_entries("ipac~fi", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (iptc_is_chain("ipac~fo", handle)) ++ if (!iptc_flush_entries("ipac~fo", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (iptc_is_chain("ipac~i", handle)) ++ if (!iptc_flush_entries("ipac~i", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++ if (iptc_is_chain("ipac~o", handle)) ++ if (!iptc_flush_entries("ipac~o", &handle)) { ++ fprintf(stderr, "iptables: %s\n", iptc_strerror(errno)); ++ return (1); ++ } ++// Try to flush our old chains ++ if (access_agent->get_raw_list("iptables", "", &d)) { ++ fprintf(stderr, "access error\n"); ++ return 1; ++ } ++ d1=d; ++ while(d) { ++ if (strlen(d->name)>7) ++ if (!memcmp(d->name, "%chain%", 7)) ++ if (iptc_is_chain(d->name+8, handle)) { ++ iptc_flush_entries(d->name+8, &handle); ++ if (verbose>1) ++ fprintf(stderr, "flushing chain '%s'\n", d->name+8); ++ } ++ ++ d=d->next; ++ } ++ free_raw_list(d1); ++ return 0; ++} ++ ++ ++int iptables_ipac_init(int flag) ++{ ++ char *modprobe = NULL; ++ ++ if (!handle) ++ handle = iptc_init("filter"); ++ ++ if (!handle) { ++// try to insmod the module if iptc_init failed ++ iptables_insmod("ip_tables", modprobe); ++ handle = iptc_init("filter"); ++ } ++ ++ if (!handle) { ++ fprintf(stderr, "ipac-ng: can't initialize iptables table `filter'\n" ++ "\tis \"Network packet filtering (replaces ipchains)\"\n" ++ "\tin Networking options of your kernel enabled?\n" ++ "\tif so then you *have* to enable \"IP tables support\" in\n" ++ "\t\"IP: Netfilter Configuration\" *and* \"Packet filtering\"\n" ++ "\tsome lines below in your kernel configuration.\n" ++ "\tPlease don't send bug reports\n" ++ "\tif you failed to enable these features! \n" ++ "\t You have to check that /usr/include/linux points to the\n" ++ "\tright kernel's headers (somewhere in /usr/src/linux/include/linux?).\n" ++ "\tIf it's not then you have to correct this and recompile ipac-ng\n" ++ "---\nwbw, kaiser.\n\n" ++ "\tiptables reported that %s\n", iptc_strerror(errno)); ++ exit (1); ++ } ++ return 0; ++}; ++ ++static int ++setup_rules(void) ++{ ++ raw_rule_type *d, *d1; ++ char targ[MAX_RULE_NAME_LENGTH+2]; ++ char chain[MAX_RULE_NAME_LENGTH+2]; ++ FILE *frunfile; ++ ++ if(access_agent->get_raw_list("iptables", "", &d)) { ++ fprintf(stderr, "access error\n"); ++ return 1; ++ } ++ ++ frunfile = fopen(RUNFILE, "w"); //needs to make some error handling later ++ if (!frunfile) { ++ fprintf(stderr, "%s: opening runfile \"%s\": %s\n", ++ me, RUNFILE, strerror(errno)); ++ return 1; ++ } ++ d1 = d; ++ while(d) { ++ /* Trying to implement hierarchic rules */ ++ strcpy(d->target, ""); /* no target per default */ ++ strcpy(chain, d->dest); // %-) ++ ++ /* Are we dealing with new chain? if so create it */ ++ if (d->name[0]=='%') { ++ if (!strncmp(d->name, "%chain%", 7)) { ++ if (strlen(d->name)<8) { ++ fprintf(stderr, "error: new " ++ "chain name missing\n"); ++ return 1; ++ } ++ /* set target to this new chain */ ++ strcpy(d->target, d->name+8); ++ if (verbose>1) ++ fprintf(stderr, "creating chain '%s'\n", d->target); ++ iptc_create_chain(d->target, &handle); ++ fprintf(frunfile, "%s|%%%s%%\n", chain, d->target); ++ } else { ++ fprintf(stderr, "error: incorrect symbol %% " ++ "in rule name\n"); ++ return 1; ++ } ++ } else ++ if (!(((strlen(chain)>4) && ++ (!memcmp(chain+strlen(chain)-4, "~c", 2))) || ++ ((strlen(chain)>5) && ++ (!memcmp(chain+strlen(chain)-5, "~c", 2))))) ++ fprintf(frunfile,"%s|%s\n", chain, d->name); ++ ++ strcpy(d->dest, chain); ++ append_rule(d); ++ d=d->next; ++ } ++ fclose(frunfile); ++ free_raw_list(d1); ++ return 0; ++} ++ ++/** ++ Setup all possible rules in iptables ++ */ ++int iptables_ipac_set(rule_type **firstrule, int first) ++{ ++ unsigned int ref=0; ++ raw_rule_type d; ++ bzero((void *) &d, sizeof(raw_rule_type)); ++ ++ if (verbose) ++ fprintf(stderr, "Flushing accounting chains..\n"); ++ flush_acc_tables(); ++ if (verbose) ++ fprintf(stderr, "Setting up acc chains..\n"); ++ if (first==1) ++ setup_tables(); ++ if (verbose) ++ fprintf(stderr, "Setting up accounting rules..\n"); ++ setup_rules(); ++ ++ if (first==1) { ++ iptc_get_references(&ref, "ipac~fi", &handle); ++ if (ref!=0) { ++ strcpy(d.dest, "OUTPUT"); strcpy(d.snet, "0/0"); strcpy(d.dnet, "0/0"); ++ strcpy(d.target, "ipac~i"); ++ delete_rule(&d); ++ strcpy(d.dest, "FORWARD"); strcpy(d.target, "ipac~fi"); ++ delete_rule(&d); ++ } ++ iptc_get_references(&ref, "ipac~fo", &handle); ++ if ((ref!=0) && (first==1)) { ++ strcpy(d.dest, "INPUT"); strcpy(d.snet, "0/0"); strcpy(d.dnet, "0/0"); ++ strcpy(d.target, "ipac~o"); ++ delete_rule(&d); ++ strcpy(d.dest, "FORWARD"); strcpy(d.target, "ipac~fo"); ++ delete_rule(&d); ++ } ++ strcpy(d.dest, "OUTPUT"); strcpy(d.snet, "0/0"); strcpy(d.dnet, "0/0"); ++ strcpy(d.target, "ipac~i"); ++ insert_rule(&d, 0); ++ strcpy(d.dest, "INPUT"); strcpy(d.target, "ipac~o"); ++ insert_rule(&d, 0); ++ strcpy(d.dest, "FORWARD"); strcpy(d.target, "ipac~fo"); ++ insert_rule(&d, 0); ++ strcpy(d.dest, "FORWARD"); strcpy(d.target, "ipac~fi"); ++ insert_rule(&d, 0); ++ } ++ iptc_commit(&handle); ++ iptables_ipac_init(0); ++ return 0; ++} ++ ++int ++iptables_ipac_read(rule_type **firstrule) ++{ ++ struct runfile_line_type *runfile; ++ ++ runfile = read_runfile(); ++ if (runfile == NULL) ++ return 1; ++ ++ return read_iptables(runfile, firstrule); ++} ++ ++int ++iptables_ipac_check(void){ ++ int tmp=0; ++ ++ if ((iptc_is_chain("ipac~fi", handle) + ++ iptc_is_chain("ipac~fo", handle))!=2) ++ return 1; ++ iptc_get_references(&tmp, "ipac~fo", &handle); ++ if (tmp==0) ++ return 1; ++ iptc_get_references(&tmp, "ipac~fi", &handle); ++ if (tmp==0) ++ return 1; ++ return 0; ++} ++ ++void ++flush_remove_chain(char *ch_name) ++{ ++ if (!handle) ++ iptables_ipac_init(0); ++ if (iptc_is_chain(ch_name, handle)) { ++ iptc_flush_entries(ch_name, &handle); ++ iptc_delete_chain(ch_name, &handle); ++ iptc_commit(&handle); ++ iptables_ipac_init(0); // always do init after commit! ++ } ++} ++ ++/* ++ * Deny packets from any to any. ++ * Make by inserting drop rule to the forward chain ++ */ ++int ++iptables_ipac_alarm(void) ++{ ++ int ret; ++ raw_rule_type d; ++ bzero((void *)&d, sizeof(raw_rule_type)); ++ if (!handle) ++ iptables_ipac_init(0); ++ strcpy(d.dest, "FORWARD"); strcpy(d.target, "DROP"); ++ ret = insert_rule(&d, 0); ++ iptc_commit(&handle); ++ return ret; ++} +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/libip4tc.c /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libip4tc.c +--- ./agents/iptables/libip4tc.c 2003-07-06 18:33:23.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libip4tc.c 2005-09-07 07:39:56.000000000 +0800 +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include + + #ifdef DEBUG_CONNTRACK + #define inline +@@ -90,6 +91,7 @@ + #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 + +@@ -127,8 +129,8 @@ + size_t i; + STRUCT_ENTRY_TARGET *t; + +- printf("Entry %u (%lu):\n", entry2index(handle, e), +- entry2offset(handle, e)); ++ printf("Entry %u (%lu):\n", iptcb_entry2index(handle, e), ++ iptcb_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", +@@ -143,21 +145,10 @@ + 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); ++ (unsigned long long)e->counters.pcnt, (unsigned long long)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); +@@ -182,60 +173,48 @@ + return 0; + } + +-static int ++static unsigned char * + 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.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; ++ return NULL; + + for (i = 0; i < IFNAMSIZ; i++) { + if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i]) +- return 0; ++ return NULL; + if ((a->ip.iniface[i] & a->ip.iniface_mask[i]) + != (b->ip.iniface[i] & b->ip.iniface_mask[i])) +- return 0; ++ return NULL; + if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i]) +- return 0; ++ return NULL; + if ((a->ip.outiface[i] & a->ip.outiface_mask[i]) + != (b->ip.outiface[i] & b->ip.outiface_mask[i])) +- return 0; ++ return NULL; + } + + if (a->nfcache != b->nfcache + || a->target_offset != b->target_offset + || a->next_offset != b->next_offset) +- return 0; ++ return NULL; + + 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 NULL; + +- return 1; ++ return mptr; + } + ++#if 0 + /***************************** DEBUGGING ********************************/ + static inline int + unconditional(const struct ipt_ip *ip) +@@ -290,20 +269,20 @@ + assert(t->verdict == -NF_DROP-1 + || t->verdict == -NF_ACCEPT-1 + || t->verdict == RETURN +- || t->verdict < (int)h->entries.size); ++ || t->verdict < (int)h->entries->size); + + if (t->verdict >= 0) { + STRUCT_ENTRY *te = get_entry(h, t->verdict); + int idx; + +- idx = entry2index(h, te); ++ idx = iptcb_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 ++ assert(t->verdict == iptcb_entry2offset(h, e)+e->next_offset + || strcmp(GET_TARGET(index2entry(h, idx-1)) + ->u.user.name, IPT_ERROR_TARGET) + == 0); +@@ -335,7 +314,7 @@ + return 0; + } + +-#ifndef NDEBUG ++#ifdef IPTC_DEBUG + /* Do every conceivable sanity check on the handle */ + static void + do_check(TC_HANDLE_T h, unsigned int line) +@@ -364,35 +343,90 @@ + + user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT]; + } else if (strcmp(h->info.name, "nat") == 0) { +- assert(h->info.valid_hooks ++ assert((h->info.valid_hooks + == (1 << NF_IP_PRE_ROUTING + | 1 << NF_IP_POST_ROUTING +- | 1 << NF_IP_LOCAL_OUT)); ++ | 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]; ++ } ++ } else if (strcmp(h->info.name, "raw") == 0) { + assert(h->info.valid_hooks + == (1 << NF_IP_PRE_ROUTING + | 1 << NF_IP_LOCAL_OUT)); + +- /* Hooks should be first two */ ++ /* Hooks should be first three */ + assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0); + +- n = get_chain_end(h, 0); ++ 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]; ++ + #ifdef NF_IP_DROPPING + } else if (strcmp(h->info.name, "drop") == 0) { + assert(h->info.valid_hooks == (1 << NF_IP_DROPPING)); +@@ -425,8 +459,8 @@ + assert(unconditional(&e->ip)); + assert(e->target_offset == sizeof(*e)); + t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); +- assert(t->target.u.target_size == IPT_ALIGN(sizeof(*t))); +- assert(e->next_offset == sizeof(*e) + IPT_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); +@@ -458,6 +492,8 @@ + /* Final entry must be error node */ + assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1)) + ->u.user.name, +- IPT_ERROR_TARGET) == 0); ++ ERROR_TARGET) == 0); + } +-#endif /*NDEBUG*/ ++#endif /*IPTC_DEBUG*/ ++ ++#endif +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/libiptc.c /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libiptc.c +--- ./agents/iptables/libiptc.c 2003-07-06 19:34:52.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libiptc.c 2005-09-07 07:39:56.000000000 +0800 +@@ -1,4 +1,4 @@ +-/* Library which manipulates firewall rules. Version $Revision: 1.2 $ */ ++/* Library which manipulates firewall rules. Version $Revision: 3756 $ */ + + /* Architecture of firewall rules is as follows: + * +@@ -9,21 +9,43 @@ + */ + + /* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See +- COPYING for details). */ ++ * COPYING for details). ++ * (C) 2000-2004 by the Netfilter Core Team ++ * ++ * 2003-Jun-20: Harald Welte : ++ * - Reimplementation of chain cache to use offsets instead of entries ++ * 2003-Jun-23: Harald Welte : ++ * - 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. ++ * 2004-Aug-18: Harald Welte : ++ * - futher performance work: total reimplementation of libiptc. ++ * - libiptc now has a real internal (linked-list) represntation of the ++ * ruleset and a parser/compiler from/to this internal representation ++ * - again sponsored by Astaro AG (http://www.astaro.com/) ++ */ ++#include ++#include + +-#ifndef IPT_LIB_DIR +-#define IPT_LIB_DIR "/lib/iptables" ++#include "linux_list.h" ++ ++//#define IPTC_DEBUG2 1 ++ ++#ifdef IPTC_DEBUG2 ++#include ++#define DEBUGP(x, args...) fprintf(stderr, "%s: " x, __FUNCTION__, ## args) ++#define DEBUGP_C(x, args...) fprintf(stderr, x, ## args) ++#else ++#define DEBUGP(x, args...) ++#define DEBUGP_C(x, args...) + #endif + +-#ifndef __OPTIMIZE__ +-STRUCT_ENTRY_TARGET * +-GET_TARGET(STRUCT_ENTRY *e) +-{ +- return (void *)e + e->target_offset; +-} ++#ifndef IPT_LIB_DIR ++#define IPT_LIB_DIR "/usr/local/lib/iptables" + #endif + + static int sockfd = -1; ++static int sockfd_use = 0; + static void *iptc_fn = NULL; + + static const char *hooknames[] +@@ -37,6 +59,16 @@ + #endif + }; + ++/* Convenience structures */ ++struct ipt_error_target ++{ ++ STRUCT_ENTRY_TARGET t; ++ char error[TABLE_MAXNAMELEN]; ++}; ++ ++struct chain_head; ++struct rule_head; ++ + struct counter_map + { + enum { +@@ -48,59 +80,95 @@ + unsigned int mappos; + }; + +-/* Convenience structures */ +-struct ipt_error_target ++enum iptcc_rule_type { ++ IPTCC_R_STANDARD, /* standard target (ACCEPT, ...) */ ++ IPTCC_R_MODULE, /* extension module (SNAT, ...) */ ++ IPTCC_R_FALLTHROUGH, /* fallthrough rule */ ++ IPTCC_R_JUMP, /* jump to other chain */ ++}; ++ ++struct rule_head + { +- STRUCT_ENTRY_TARGET t; +- char error[TABLE_MAXNAMELEN]; ++ struct list_head list; ++ struct chain_head *chain; ++ struct counter_map counter_map; ++ ++ unsigned int index; /* index (needed for counter_map) */ ++ unsigned int offset; /* offset in rule blob */ ++ ++ enum iptcc_rule_type type; ++ struct chain_head *jump; /* jump target, if IPTCC_R_JUMP */ ++ ++ unsigned int size; /* size of entry data */ ++ STRUCT_ENTRY entry[0]; + }; + +-struct chain_cache ++struct chain_head + { ++ struct list_head list; + char name[TABLE_MAXNAMELEN]; +- /* This is the first rule in chain. */ +- STRUCT_ENTRY *start; +- /* Last rule in chain */ +- STRUCT_ENTRY *end; ++ unsigned int hooknum; /* hook number+1 if builtin */ ++ unsigned int references; /* how many jumps reference us */ ++ int verdict; /* verdict if builtin */ ++ ++ STRUCT_COUNTERS counters; /* per-chain counters */ ++ struct counter_map counter_map; ++ ++ unsigned int num_rules; /* number of rules in list */ ++ struct list_head rules; /* list of rules */ ++ ++ unsigned int index; /* index (needed for jump resolval) */ ++ unsigned int head_offset; /* offset in rule blob */ ++ unsigned int foot_index; /* index (needed for counter_map) */ ++ unsigned int foot_offset; /* offset in rule blob */ + }; + + STRUCT_TC_HANDLE + { +- /* Have changes been made? */ +- int changed; +- /* Size in here reflects original state. */ ++ int changed; /* Have changes been made? */ ++ ++ struct list_head chains; ++ ++ struct chain_head *chain_iterator_cur; ++ struct rule_head *rule_iterator_cur; ++ + STRUCT_GETINFO info; ++ STRUCT_GET_ENTRIES *entries; ++}; + +- struct counter_map *counter_map; +- /* Array of hook names */ +- const char **hooknames; ++/* allocate a new chain head for the cache */ ++static struct chain_head *iptcc_alloc_chain_head(const char *name, int hooknum) ++{ ++ struct chain_head *c = malloc(sizeof(*c)); ++ if (!c) ++ return NULL; ++ memset(c, 0, sizeof(*c)); + +- /* Cached position of chain heads (NULL = no cache). */ +- unsigned int cache_num_chains; +- unsigned int cache_num_builtins; +- struct chain_cache *cache_chain_heads; ++ strncpy(c->name, name, TABLE_MAXNAMELEN); ++ c->hooknum = hooknum; ++ INIT_LIST_HEAD(&c->rules); + +- /* Chain iterator: current chain cache entry. */ +- struct chain_cache *cache_chain_iteration; ++ return c; ++} + +- /* Rule iterator: terminal rule */ +- STRUCT_ENTRY *cache_rule_end; ++/* allocate and initialize a new rule for the cache */ ++static struct rule_head *iptcc_alloc_rule(struct chain_head *c, unsigned int size) ++{ ++ struct rule_head *r = malloc(sizeof(*r)+size); ++ if (!r) ++ return NULL; ++ memset(r, 0, sizeof(*r)); + +- /* Number in here reflects current state. */ +- unsigned int new_number; +- STRUCT_GET_ENTRIES entries; +-}; ++ r->chain = c; ++ r->size = size; + ++ return r; ++} ++ ++/* notify us that the ruleset has been modified by the user */ + static void + set_changed(TC_HANDLE_T h) + { +- if (h->cache_chain_heads) { +- free(h->cache_chain_heads); +- h->cache_chain_heads = NULL; +- h->cache_num_chains = 0; +- h->cache_chain_iteration = NULL; +- h->cache_rule_end = NULL; +- } + h->changed = 1; + } + +@@ -111,8 +179,13 @@ + #define CHECK(h) + #endif + ++ ++/********************************************************************** ++ * iptc blob utility functions (iptcb_*) ++ **********************************************************************/ ++ + static inline int +-get_number(const STRUCT_ENTRY *i, ++iptcb_get_number(const STRUCT_ENTRY *i, + const STRUCT_ENTRY *seek, + unsigned int *pos) + { +@@ -122,22 +195,8 @@ + 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, ++iptcb_get_entry_n(STRUCT_ENTRY *i, + unsigned int number, + unsigned int *pos, + STRUCT_ENTRY **pe) +@@ -150,51 +209,556 @@ + return 0; + } + +-static STRUCT_ENTRY * +-index2entry(TC_HANDLE_T h, unsigned int index) ++static inline STRUCT_ENTRY * ++iptcb_get_entry(TC_HANDLE_T h, unsigned int offset) + { +- unsigned int pos = 0; +- STRUCT_ENTRY *ret = NULL; ++ return (STRUCT_ENTRY *)((char *)h->entries->entrytable + offset); ++} + +- ENTRY_ITERATE(h->entries.entrytable, h->entries.size, +- get_entry_n, index, &pos, &ret); ++static unsigned int ++iptcb_entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek) ++{ ++ unsigned int pos = 0; + +- return ret; ++ if (ENTRY_ITERATE(h->entries->entrytable, h->entries->size, ++ iptcb_get_number, seek, &pos) == 0) { ++ fprintf(stderr, "ERROR: offset %u not an entry!\n", ++ (unsigned int)((char *)seek - (char *)h->entries->entrytable)); ++ abort(); ++ } ++ return pos; + } + + static inline STRUCT_ENTRY * +-get_entry(TC_HANDLE_T h, unsigned int offset) ++iptcb_offset2entry(TC_HANDLE_T h, unsigned int offset) + { +- return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset); ++ return (STRUCT_ENTRY *) ((void *)h->entries->entrytable+offset); + } + ++ + static inline unsigned long +-entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e) ++iptcb_entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e) + { +- return (char *)e - (char *)h->entries.entrytable; ++ return (void *)e - (void *)h->entries->entrytable; + } + +-static unsigned long +-index2offset(TC_HANDLE_T h, unsigned int index) ++static inline unsigned int ++iptcb_offset2index(const TC_HANDLE_T h, unsigned int offset) + { +- return entry2offset(h, index2entry(h, index)); ++ return iptcb_entry2index(h, iptcb_offset2entry(h, offset)); + } + +-static const char * +-get_errorlabel(TC_HANDLE_T h, unsigned int offset) ++/* Returns 0 if not hook entry, else hooknumber + 1 */ ++static inline unsigned int ++iptcb_ent_is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h) + { +- STRUCT_ENTRY *e; ++ unsigned int i; + +- 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(); ++ for (i = 0; i < NUMHOOKS; i++) { ++ if ((h->info.valid_hooks & (1 << i)) ++ && iptcb_get_entry(h, h->info.hook_entry[i]) == e) ++ return i+1; + } ++ return 0; ++} + +- return (const char *)GET_TARGET(e)->data; ++ ++/********************************************************************** ++ * iptc cache utility functions (iptcc_*) ++ **********************************************************************/ ++ ++/* Is the given chain builtin (1) or user-defined (0) */ ++static unsigned int iptcc_is_builtin(struct chain_head *c) ++{ ++ return (c->hooknum ? 1 : 0); ++} ++ ++/* Get a specific rule within a chain */ ++static struct rule_head *iptcc_get_rule_num(struct chain_head *c, ++ unsigned int rulenum) ++{ ++ struct rule_head *r; ++ unsigned int num = 0; ++ ++ list_for_each_entry(r, &c->rules, list) { ++ num++; ++ if (num == rulenum) ++ return r; ++ } ++ return NULL; ++} ++ ++/* Get a specific rule within a chain backwards */ ++static struct rule_head *iptcc_get_rule_num_reverse(struct chain_head *c, ++ unsigned int rulenum) ++{ ++ struct rule_head *r; ++ unsigned int num = 0; ++ ++ list_for_each_entry_reverse(r, &c->rules, list) { ++ num++; ++ if (num == rulenum) ++ return r; ++ } ++ return NULL; ++} ++ ++/* Returns chain head if found, otherwise NULL. */ ++static struct chain_head * ++iptcc_find_chain_by_offset(TC_HANDLE_T handle, unsigned int offset) ++{ ++ struct list_head *pos; ++ ++ if (list_empty(&handle->chains)) ++ return NULL; ++ ++ list_for_each(pos, &handle->chains) { ++ struct chain_head *c = list_entry(pos, struct chain_head, list); ++ if (offset >= c->head_offset && offset <= c->foot_offset) ++ return c; ++ } ++ ++ return NULL; ++} ++/* Returns chain head if found, otherwise NULL. */ ++static struct chain_head * ++iptcc_find_label(const char *name, TC_HANDLE_T handle) ++{ ++ struct list_head *pos; ++ ++ if (list_empty(&handle->chains)) ++ return NULL; ++ ++ list_for_each(pos, &handle->chains) { ++ struct chain_head *c = list_entry(pos, struct chain_head, list); ++ if (!strcmp(c->name, name)) ++ return c; ++ } ++ ++ return NULL; + } + ++/* called when rule is to be removed from cache */ ++static void iptcc_delete_rule(struct rule_head *r) ++{ ++ DEBUGP("deleting rule %p (offset %u)\n", r, r->offset); ++ /* clean up reference count of called chain */ ++ if (r->type == IPTCC_R_JUMP ++ && r->jump) ++ r->jump->references--; ++ ++ list_del(&r->list); ++ free(r); ++} ++ ++ ++/********************************************************************** ++ * RULESET PARSER (blob -> cache) ++ **********************************************************************/ ++ ++/* Delete policy rule of previous chain, since cache doesn't contain ++ * chain policy rules. ++ * WARNING: This function has ugly design and relies on a lot of context, only ++ * to be called from specific places within the parser */ ++static int __iptcc_p_del_policy(TC_HANDLE_T h, unsigned int num) ++{ ++ if (h->chain_iterator_cur) { ++ /* policy rule is last rule */ ++ struct rule_head *pr = (struct rule_head *) ++ h->chain_iterator_cur->rules.prev; ++ ++ /* save verdict */ ++ h->chain_iterator_cur->verdict = ++ *(int *)GET_TARGET(pr->entry)->data; ++ ++ /* save counter and counter_map information */ ++ h->chain_iterator_cur->counter_map.maptype = ++ COUNTER_MAP_NORMAL_MAP; ++ h->chain_iterator_cur->counter_map.mappos = num-1; ++ memcpy(&h->chain_iterator_cur->counters, &pr->entry->counters, ++ sizeof(h->chain_iterator_cur->counters)); ++ ++ /* foot_offset points to verdict rule */ ++ h->chain_iterator_cur->foot_index = num; ++ h->chain_iterator_cur->foot_offset = pr->offset; ++ ++ /* delete rule from cache */ ++ iptcc_delete_rule(pr); ++ h->chain_iterator_cur->num_rules--; ++ ++ return 1; ++ } ++ return 0; ++} ++ ++/* alphabetically insert a chain into the list */ ++static inline void iptc_insert_chain(TC_HANDLE_T h, struct chain_head *c) ++{ ++ struct chain_head *tmp; ++ ++ /* sort only user defined chains */ ++ if (!c->hooknum) { ++ list_for_each_entry(tmp, &h->chains, list) { ++ if (strcmp(c->name, tmp->name) <= 0) { ++ list_add(&c->list, tmp->list.prev); ++ return; ++ } ++ } ++ } ++ ++ /* survived till end of list: add at tail */ ++ list_add_tail(&c->list, &h->chains); ++} ++ ++/* Another ugly helper function split out of cache_add_entry to make it less ++ * spaghetti code */ ++static void __iptcc_p_add_chain(TC_HANDLE_T h, struct chain_head *c, ++ unsigned int offset, unsigned int *num) ++{ ++ __iptcc_p_del_policy(h, *num); ++ ++ c->head_offset = offset; ++ c->index = *num; ++ ++ iptc_insert_chain(h, c); ++ ++ h->chain_iterator_cur = c; ++} ++ ++/* main parser function: add an entry from the blob to the cache */ ++static int cache_add_entry(STRUCT_ENTRY *e, ++ TC_HANDLE_T h, ++ STRUCT_ENTRY **prev, ++ unsigned int *num) ++{ ++ unsigned int builtin; ++ unsigned int offset = (char *)e - (char *)h->entries->entrytable; ++ ++ DEBUGP("entering..."); ++ ++ /* Last entry ("policy rule"). End it.*/ ++ if (iptcb_entry2offset(h,e) + e->next_offset == h->entries->size) { ++ /* This is the ERROR node at the end of the chain */ ++ DEBUGP_C("%u:%u: end of table:\n", *num, offset); ++ ++ __iptcc_p_del_policy(h, *num); ++ ++ h->chain_iterator_cur = NULL; ++ goto out_inc; ++ } ++ ++ /* 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) { ++ struct chain_head *c = ++ iptcc_alloc_chain_head((const char *)GET_TARGET(e)->data, 0); ++ DEBUGP_C("%u:%u:new userdefined chain %s: %p\n", *num, offset, ++ (char *)c->name, c); ++ if (!c) { ++ errno = -ENOMEM; ++ return -1; ++ } ++ ++ __iptcc_p_add_chain(h, c, offset, num); ++ ++ } else if ((builtin = iptcb_ent_is_hook_entry(e, h)) != 0) { ++ struct chain_head *c = ++ iptcc_alloc_chain_head((char *)hooknames[builtin-1], ++ builtin); ++ DEBUGP_C("%u:%u new builtin chain: %p (rules=%p)\n", ++ *num, offset, c, &c->rules); ++ if (!c) { ++ errno = -ENOMEM; ++ return -1; ++ } ++ ++ c->hooknum = builtin; ++ ++ __iptcc_p_add_chain(h, c, offset, num); ++ ++ /* FIXME: this is ugly. */ ++ goto new_rule; ++ } else { ++ /* has to be normal rule */ ++ struct rule_head *r; ++new_rule: ++ ++ if (!(r = iptcc_alloc_rule(h->chain_iterator_cur, ++ e->next_offset))) { ++ errno = ENOMEM; ++ return -1; ++ } ++ DEBUGP_C("%u:%u normal rule: %p: ", *num, offset, r); ++ ++ r->index = *num; ++ r->offset = offset; ++ memcpy(r->entry, e, e->next_offset); ++ r->counter_map.maptype = COUNTER_MAP_NORMAL_MAP; ++ r->counter_map.mappos = r->index; ++ ++ /* handling of jumps, etc. */ ++ if (!strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET)) { ++ 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 -1; ++ } ++ ++ if (t->verdict < 0) { ++ DEBUGP_C("standard, verdict=%d\n", t->verdict); ++ r->type = IPTCC_R_STANDARD; ++ } else if (t->verdict == r->offset+e->next_offset) { ++ DEBUGP_C("fallthrough\n"); ++ r->type = IPTCC_R_FALLTHROUGH; ++ } else { ++ DEBUGP_C("jump, target=%u\n", t->verdict); ++ r->type = IPTCC_R_JUMP; ++ /* Jump target fixup has to be deferred ++ * until second pass, since we migh not ++ * yet have parsed the target */ ++ } ++ } else { ++ DEBUGP_C("module, target=%s\n", GET_TARGET(e)->u.user.name); ++ r->type = IPTCC_R_MODULE; ++ } ++ ++ list_add_tail(&r->list, &h->chain_iterator_cur->rules); ++ h->chain_iterator_cur->num_rules++; ++ } ++out_inc: ++ (*num)++; ++ return 0; ++} ++ ++ ++/* parse an iptables blob into it's pieces */ ++static int parse_table(TC_HANDLE_T h) ++{ ++ STRUCT_ENTRY *prev; ++ unsigned int num = 0; ++ struct chain_head *c; ++ ++ /* First pass: over ruleset blob */ ++ ENTRY_ITERATE(h->entries->entrytable, h->entries->size, ++ cache_add_entry, h, &prev, &num); ++ ++ /* Second pass: fixup parsed data from first pass */ ++ list_for_each_entry(c, &h->chains, list) { ++ struct rule_head *r; ++ list_for_each_entry(r, &c->rules, list) { ++ struct chain_head *c; ++ STRUCT_STANDARD_TARGET *t; ++ ++ if (r->type != IPTCC_R_JUMP) ++ continue; ++ ++ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry); ++ c = iptcc_find_chain_by_offset(h, t->verdict); ++ if (!c) ++ return -1; ++ r->jump = c; ++ c->references++; ++ } ++ } ++ ++ /* FIXME: sort chains */ ++ ++ return 1; ++} ++ ++ ++/********************************************************************** ++ * RULESET COMPILATION (cache -> blob) ++ **********************************************************************/ ++ ++/* Convenience structures */ ++struct iptcb_chain_start{ ++ STRUCT_ENTRY e; ++ struct ipt_error_target name; ++}; ++#define IPTCB_CHAIN_START_SIZE (sizeof(STRUCT_ENTRY) + \ ++ ALIGN(sizeof(struct ipt_error_target))) ++ ++struct iptcb_chain_foot { ++ STRUCT_ENTRY e; ++ STRUCT_STANDARD_TARGET target; ++}; ++#define IPTCB_CHAIN_FOOT_SIZE (sizeof(STRUCT_ENTRY) + \ ++ ALIGN(sizeof(STRUCT_STANDARD_TARGET))) ++ ++struct iptcb_chain_error { ++ STRUCT_ENTRY entry; ++ struct ipt_error_target target; ++}; ++#define IPTCB_CHAIN_ERROR_SIZE (sizeof(STRUCT_ENTRY) + \ ++ ALIGN(sizeof(struct ipt_error_target))) ++ ++ ++ ++/* compile rule from cache into blob */ ++static inline int iptcc_compile_rule (TC_HANDLE_T h, STRUCT_REPLACE *repl, struct rule_head *r) ++{ ++ /* handle jumps */ ++ if (r->type == IPTCC_R_JUMP) { ++ STRUCT_STANDARD_TARGET *t; ++ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry); ++ /* memset for memcmp convenience on delete/replace */ ++ memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN); ++ strcpy(t->target.u.user.name, STANDARD_TARGET); ++ /* Jumps can only happen to builtin chains, so we ++ * can safely assume that they always have a header */ ++ t->verdict = r->jump->head_offset + IPTCB_CHAIN_START_SIZE; ++ } else if (r->type == IPTCC_R_FALLTHROUGH) { ++ STRUCT_STANDARD_TARGET *t; ++ t = (STRUCT_STANDARD_TARGET *)GET_TARGET(r->entry); ++ t->verdict = r->offset + r->size; ++ } ++ ++ /* copy entry from cache to blob */ ++ memcpy((char *)repl->entries+r->offset, r->entry, r->size); ++ ++ return 1; ++} ++ ++/* compile chain from cache into blob */ ++static int iptcc_compile_chain(TC_HANDLE_T h, STRUCT_REPLACE *repl, struct chain_head *c) ++{ ++ int ret; ++ struct rule_head *r; ++ struct iptcb_chain_start *head; ++ struct iptcb_chain_foot *foot; ++ ++ /* only user-defined chains have heaer */ ++ if (!iptcc_is_builtin(c)) { ++ /* put chain header in place */ ++ head = (void *)repl->entries + c->head_offset; ++ head->e.target_offset = sizeof(STRUCT_ENTRY); ++ head->e.next_offset = IPTCB_CHAIN_START_SIZE; ++ strcpy(head->name.t.u.user.name, ERROR_TARGET); ++ head->name.t.u.target_size = ++ ALIGN(sizeof(struct ipt_error_target)); ++ strcpy(head->name.error, c->name); ++ } else { ++ repl->hook_entry[c->hooknum-1] = c->head_offset; ++ repl->underflow[c->hooknum-1] = c->foot_offset; ++ } ++ ++ /* iterate over rules */ ++ list_for_each_entry(r, &c->rules, list) { ++ ret = iptcc_compile_rule(h, repl, r); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* put chain footer in place */ ++ foot = (void *)repl->entries + c->foot_offset; ++ foot->e.target_offset = sizeof(STRUCT_ENTRY); ++ foot->e.next_offset = IPTCB_CHAIN_FOOT_SIZE; ++ strcpy(foot->target.target.u.user.name, STANDARD_TARGET); ++ foot->target.target.u.target_size = ++ ALIGN(sizeof(STRUCT_STANDARD_TARGET)); ++ /* builtin targets have verdict, others return */ ++ if (iptcc_is_builtin(c)) ++ foot->target.verdict = c->verdict; ++ else ++ foot->target.verdict = RETURN; ++ /* set policy-counters */ ++ memcpy(&foot->e.counters, &c->counters, sizeof(STRUCT_COUNTERS)); ++ ++ return 0; ++} ++ ++/* calculate offset and number for every rule in the cache */ ++static int iptcc_compile_chain_offsets(TC_HANDLE_T h, struct chain_head *c, ++ int *offset, int *num) ++{ ++ struct rule_head *r; ++ ++ c->head_offset = *offset; ++ DEBUGP("%s: chain_head %u, offset=%u\n", c->name, *num, *offset); ++ ++ if (!iptcc_is_builtin(c)) { ++ /* Chain has header */ ++ *offset += sizeof(STRUCT_ENTRY) ++ + ALIGN(sizeof(struct ipt_error_target)); ++ (*num)++; ++ } ++ ++ list_for_each_entry(r, &c->rules, list) { ++ DEBUGP("rule %u, offset=%u, index=%u\n", *num, *offset, *num); ++ r->offset = *offset; ++ r->index = *num; ++ *offset += r->size; ++ (*num)++; ++ } ++ ++ DEBUGP("%s; chain_foot %u, offset=%u, index=%u\n", c->name, *num, ++ *offset, *num); ++ c->foot_offset = *offset; ++ c->foot_index = *num; ++ *offset += sizeof(STRUCT_ENTRY) ++ + ALIGN(sizeof(STRUCT_STANDARD_TARGET)); ++ (*num)++; ++ ++ return 1; ++} ++ ++/* put the pieces back together again */ ++static int iptcc_compile_table_prep(TC_HANDLE_T h, unsigned int *size) ++{ ++ struct chain_head *c; ++ unsigned int offset = 0, num = 0; ++ int ret = 0; ++ ++ /* First pass: calculate offset for every rule */ ++ list_for_each_entry(c, &h->chains, list) { ++ ret = iptcc_compile_chain_offsets(h, c, &offset, &num); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Append one error rule at end of chain */ ++ num++; ++ offset += sizeof(STRUCT_ENTRY) ++ + ALIGN(sizeof(struct ipt_error_target)); ++ ++ /* ruleset size is now in offset */ ++ *size = offset; ++ return num; ++} ++ ++static int iptcc_compile_table(TC_HANDLE_T h, STRUCT_REPLACE *repl) ++{ ++ struct chain_head *c; ++ struct iptcb_chain_error *error; ++ ++ /* Second pass: copy from cache to offsets, fill in jumps */ ++ list_for_each_entry(c, &h->chains, list) { ++ int ret = iptcc_compile_chain(h, repl, c); ++ if (ret < 0) ++ return ret; ++ } ++ ++ /* Append error rule at end of chain */ ++ error = (void *)repl->entries + repl->size - IPTCB_CHAIN_ERROR_SIZE; ++ error->entry.target_offset = sizeof(STRUCT_ENTRY); ++ error->entry.next_offset = IPTCB_CHAIN_ERROR_SIZE; ++ error->target.t.u.user.target_size = ++ ALIGN(sizeof(struct ipt_error_target)); ++ strcpy((char *)&error->target.t.u.user.name, ERROR_TARGET); ++ strcpy((char *)&error->target.error, "ERROR"); ++ ++ return 1; ++} ++ ++/********************************************************************** ++ * EXTERNAL API (operates on cache only) ++ **********************************************************************/ ++ + /* Allocate handle of given size */ + static TC_HANDLE_T + alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules) +@@ -202,94 +766,139 @@ + size_t len; + TC_HANDLE_T h; + +- len = sizeof(STRUCT_TC_HANDLE) +- + size +- + num_rules * sizeof(struct counter_map); ++ len = sizeof(STRUCT_TC_HANDLE) + size; + +- if ((h = malloc(len)) == NULL) { ++ h = malloc(sizeof(STRUCT_TC_HANDLE)); ++ if (!h) { + 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; ++ memset(h, 0, sizeof(*h)); ++ INIT_LIST_HEAD(&h->chains); + strcpy(h->info.name, tablename); +- strcpy(h->entries.name, tablename); ++ ++ h->entries = malloc(sizeof(STRUCT_GET_ENTRIES) + size); ++ if (!h->entries) ++ goto out_free_handle; ++ ++ strcpy(h->entries->name, tablename); ++ h->entries->size = size; + + return h; ++ ++out_free_handle: ++ free(h); ++ ++ return NULL; + } + ++ + 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); ++ if (strlen(tablename) >= TABLE_MAXNAMELEN) { ++ errno = EINVAL; ++ return NULL; ++ } + ++ if (sockfd_use == 0) { + sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW); + if (sockfd < 0) + return NULL; ++ } ++ sockfd_use++; + + s = sizeof(info); +- if (strlen(tablename) >= TABLE_MAXNAMELEN) { +- errno = EINVAL; +- return NULL; +- } ++ + strcpy(info.name, tablename); +- if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) ++ if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0) { ++ if (--sockfd_use == 0) { ++ close(sockfd); ++ sockfd = -1; ++ } + return NULL; ++ } + +- if ((h = alloc_handle(info.name, info.size, info.num_entries)) +- == NULL) +- return NULL; ++ DEBUGP("valid_hooks=0x%08x, num_entries=%u, size=%u\n", ++ info.valid_hooks, info.num_entries, info.size); + +-/* 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; ++ if ((h = alloc_handle(info.name, info.size, info.num_entries)) ++ == NULL) { ++ if (--sockfd_use == 0) { ++ close(sockfd); ++ sockfd = -1; + } +- 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; ++ 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) { +- free(h); +- return NULL; ++ if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, h->entries, ++ &tmp) < 0) ++ goto error; ++ ++#ifdef IPTC_DEBUG2 ++ { ++ int fd = open("/tmp/libiptc-so_get_entries.blob", ++ O_CREAT|O_WRONLY); ++ if (fd >= 0) { ++ write(fd, h->entries, tmp); ++ close(fd); + } ++ } ++#endif ++ ++ if (parse_table(h) < 0) ++ goto error; + + CHECK(h); + return h; ++error: ++ if (--sockfd_use == 0) { ++ close(sockfd); ++ sockfd = -1; ++ } ++ TC_FREE(&h); ++ return NULL; ++} ++ ++void ++TC_FREE(TC_HANDLE_T *h) ++{ ++ struct chain_head *c, *tmp; ++ ++ iptc_fn = TC_FREE; ++ if (--sockfd_use == 0) { ++ close(sockfd); ++ sockfd = -1; ++ } ++ ++ list_for_each_entry_safe(c, tmp, &(*h)->chains, list) { ++ struct rule_head *r, *rtmp; ++ ++ list_for_each_entry_safe(r, rtmp, &c->rules, list) { ++ free(r); ++ } ++ ++ free(c); ++ } ++ ++ free((*h)->entries); ++ free(*h); ++ ++ *h = NULL; + } + + static inline int +@@ -304,11 +913,11 @@ + void + TC_DUMP_ENTRIES(const TC_HANDLE_T handle) + { ++ iptc_fn = TC_DUMP_ENTRIES; + CHECK(handle); +- +- printf("libiptc v%s. %u entries, %u bytes.\n", +- NETFILTER_VERSION, +- handle->new_number, handle->entries.size); ++#if 0 ++ printf("libiptc v%s. %u bytes.\n", ++ IPTABLES_VERSION, 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], +@@ -323,516 +932,277 @@ + handle->info.underflow[HOOK_LOCAL_OUT], + handle->info.underflow[HOOK_POST_ROUTING]); + +- ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size, ++ 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 = *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 +- = *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 +- = (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 +- = *prev; +- +- strcpy(h->cache_chain_heads[h->cache_num_chains].name, +- h->hooknames[builtin-1]); +- h->cache_chain_heads[h->cache_num_chains].start +- = (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; +-} +- +-/* 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; ++#endif + } + + /* Does this chain exist? */ + int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle) + { +- return find_label(chain, handle) != NULL; ++ iptc_fn = TC_IS_CHAIN; ++ return iptcc_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) ++static void iptcc_chain_iterator_advance(TC_HANDLE_T handle) + { +- 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; +- } ++ struct chain_head *c = handle->chain_iterator_cur; + +- /* 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(); ++ if (c->list.next == &handle->chains) ++ handle->chain_iterator_cur = NULL; ++ else ++ handle->chain_iterator_cur = ++ list_entry(c->list.next, struct chain_head, list); + } + + /* 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)) ++ struct chain_head *c = list_entry((*handle)->chains.next, ++ struct chain_head, list); ++ ++ iptc_fn = TC_FIRST_CHAIN; ++ ++ ++ if (list_empty(&(*handle)->chains)) { ++ DEBUGP(": no chains\n"); + return NULL; ++ } + +- (*handle)->cache_chain_iteration +- = &(*handle)->cache_chain_heads[0]; ++ (*handle)->chain_iterator_cur = c; ++ iptcc_chain_iterator_advance(*handle); + +- return (*handle)->cache_chain_iteration->name; ++ DEBUGP(": returning `%s'\n", c->name); ++ return c->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 *c = (*handle)->chain_iterator_cur; + +- if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads +- == (*handle)->cache_num_chains) ++ iptc_fn = TC_NEXT_CHAIN; ++ ++ if (!c) { ++ DEBUGP(": no more chains\n"); + return NULL; ++ } + +- return (*handle)->cache_chain_iteration->name; ++ iptcc_chain_iterator_advance(*handle); ++ ++ DEBUGP(": returning `%s'\n", c->name); ++ return c->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); ++ iptc_fn = TC_FIRST_RULE; ++ ++ DEBUGP("first rule(%s): ", chain); ++ ++ c = iptcc_find_label(chain, *handle); + if (!c) { + errno = ENOENT; + return NULL; + } + + /* Empty chain: single return/policy rule */ +- if (c->start == c->end) ++ if (list_empty(&c->rules)) { ++ DEBUGP_C("no rules, returning NULL\n"); + return NULL; ++ } + +- (*handle)->cache_rule_end = c->end; +- return c->start; ++ r = list_entry(c->rules.next, struct rule_head, list); ++ (*handle)->rule_iterator_cur = r; ++ DEBUGP_C("%p\n", 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; ++ ++ iptc_fn = TC_NEXT_RULE; ++ DEBUGP("rule_iterator_cur=%p...", (*handle)->rule_iterator_cur); ++ ++ if (!(*handle)->rule_iterator_cur) { ++ DEBUGP_C("returning NULL\n"); + return NULL; ++ } ++ ++ r = list_entry((*handle)->rule_iterator_cur->list.next, ++ struct rule_head, list); + +- return (void *)prev + prev->next_offset; ++ iptc_fn = TC_NEXT_RULE; ++ ++ DEBUGP_C("next=%p, head=%p...", &r->list, ++ &(*handle)->rule_iterator_cur->chain->rules); ++ ++ if (&r->list == &(*handle)->rule_iterator_cur->chain->rules) { ++ (*handle)->rule_iterator_cur = NULL; ++ DEBUGP_C("finished, returning NULL\n"); ++ return NULL; ++ } ++ ++ (*handle)->rule_iterator_cur = r; ++ ++ /* NOTE: prev is without any influence ! */ ++ DEBUGP_C("returning rule %p\n", r); ++ 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; +- ++ struct chain_head *c; ++ iptc_fn = TC_NUM_RULES; + CHECK(*handle); +- if (!find_label(&off, chain, *handle)) { ++ ++ c = iptcc_find_label(chain, *handle); ++ if (!c) { + 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); ++ return c->num_rules; + } + +-/* 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; ++ struct chain_head *c; ++ struct rule_head *r; ++ ++ iptc_fn = TC_GET_RULE; + + CHECK(*handle); +- if (!find_label(&pos, chain, *handle)) { ++ ++ c = iptcc_find_label(chain, *handle); ++ if (!c) { + errno = ENOENT; + return NULL; + } + +- chainindex = entry2index(*handle, get_entry(*handle, pos)); +- +- return index2entry(*handle, chainindex + n); ++ r = iptcc_get_rule_num(c, n); ++ if (!r) ++ return NULL; ++ return r->entry; + } +-#endif + +-static const char * +-target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce) ++/* Returns a pointer to the target name of this position. */ ++const char *standard_target_map(int verdict) + { +- 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) ++ switch (verdict) { ++ case RETURN: + return LABEL_RETURN; +- else if (spos == -NF_ACCEPT-1) ++ break; ++ case -NF_ACCEPT-1: + return LABEL_ACCEPT; +- else if (spos == -NF_DROP-1) ++ break; ++ case -NF_DROP-1: + return LABEL_DROP; +- else if (spos == -NF_QUEUE-1) ++ break; ++ case -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); ++ break; ++ default: ++ fprintf(stderr, "ERROR: %d not a valid target)\n", ++ verdict); + abort(); ++ break; + } +- +- 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)); ++ /* not reached */ ++ return NULL; + } + + /* Returns a pointer to the target name of this position. */ +-const char *TC_GET_TARGET(const STRUCT_ENTRY *e, ++const char *TC_GET_TARGET(const STRUCT_ENTRY *ce, + TC_HANDLE_T *handle) + { +- return target_name(*handle, e); +-} ++ STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce; ++ struct rule_head *r = container_of(e, struct rule_head, entry[0]); + +-/* Is this a built-in chain? Actually returns hook + 1. */ +-int +-TC_BUILTIN(const char *chain, const TC_HANDLE_T handle) +-{ +- unsigned int i; ++ iptc_fn = TC_GET_TARGET; + +- 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; ++ switch(r->type) { ++ int spos; ++ case IPTCC_R_FALLTHROUGH: ++ return ""; ++ break; ++ case IPTCC_R_JUMP: ++ DEBUGP("r=%p, jump=%p, name=`%s'\n", r, r->jump, r->jump->name); ++ return r->jump->name; ++ break; ++ case IPTCC_R_STANDARD: ++ spos = *(int *)GET_TARGET(e)->data; ++ DEBUGP("r=%p, spos=%d'\n", r, spos); ++ return standard_target_map(spos); ++ break; ++ case IPTCC_R_MODULE: ++ return GET_TARGET(e)->u.user.name; ++ break; + } +- +-/* 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) ++/* Is this a built-in chain? Actually returns hook + 1. */ ++int ++TC_BUILTIN(const char *chain, const TC_HANDLE_T handle) + { +- STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e); +- unsigned int curr = (char *)e - base; ++ struct chain_head *c; + +- /* 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; +- } ++ iptc_fn = TC_BUILTIN; + ++ c = iptcc_find_label(chain, handle); ++ if (!c) { ++ errno = ENOENT; + 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; ++ return iptcc_is_builtin(c); + } + +-/* 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, ++/* Get the policy of a given built-in chain */ ++const char * ++TC_GET_POLICY(const char *chain, ++ STRUCT_COUNTERS *counters, + 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); +-} ++ struct chain_head *c; + +-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; ++ iptc_fn = TC_GET_POLICY; + +- if (offset + rules_size > (*handle)->entries.size) { +- errno = EINVAL; +- return 0; +- } ++ DEBUGP("called for chain %s\n", chain); + +- /* 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(); ++ c = iptcc_find_label(chain, *handle); ++ if (!c) { ++ errno = ENOENT; ++ return NULL; + } + +- /* 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))); ++ if (!iptcc_is_builtin(c)) ++ return NULL; + +- /* Fix numbers */ +- (*handle)->new_number -= num_rules; +- (*handle)->entries.size -= rules_size; ++ *counters = c->counters; + +- return set_verdict(offset, -(int)rules_size, handle); ++ return standard_target_map(c->verdict); + } + + static int +-standard_map(STRUCT_ENTRY *e, int verdict) ++iptcc_standard_map(struct rule_head *r, int verdict) + { ++ STRUCT_ENTRY *e = r->entry; + STRUCT_STANDARD_TARGET *t; + + t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e); +@@ -847,64 +1217,62 @@ + strcpy(t->target.u.user.name, STANDARD_TARGET); + t->verdict = verdict; + ++ r->type = IPTCC_R_STANDARD; ++ + return 1; + } + + static int +-map_target(const TC_HANDLE_T handle, +- STRUCT_ENTRY *e, +- unsigned int offset, +- STRUCT_ENTRY_TARGET *old) ++iptcc_map_target(const TC_HANDLE_T handle, ++ struct rule_head *r) + { ++ STRUCT_ENTRY *e = r->entry; + 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); ++ if (strcmp(t->u.user.name, "") == 0) { ++ r->type = IPTCC_R_FALLTHROUGH; ++ return 1; ++ } + /* Maybe it's a standard target name... */ + else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0) +- return standard_map(e, -NF_ACCEPT - 1); ++ return iptcc_standard_map(r, -NF_ACCEPT - 1); + else if (strcmp(t->u.user.name, LABEL_DROP) == 0) +- return standard_map(e, -NF_DROP - 1); ++ return iptcc_standard_map(r, -NF_DROP - 1); + else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0) +- return standard_map(e, -NF_QUEUE - 1); ++ return iptcc_standard_map(r, -NF_QUEUE - 1); + else if (strcmp(t->u.user.name, LABEL_RETURN) == 0) +- return standard_map(e, RETURN); ++ return iptcc_standard_map(r, 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; ++ struct chain_head *c; ++ DEBUGP("trying to find chain `%s': ", t->u.user.name); + +- c = find_label(t->u.user.name, handle); +- if (c) +- return standard_map(e, entry2offset(handle, c->start)); ++ c = iptcc_find_label(t->u.user.name, handle); ++ if (c) { ++ DEBUGP_C("found!\n"); ++ r->type = IPTCC_R_JUMP; ++ r->jump = c; ++ c->references++; ++ return 1; ++} ++ DEBUGP_C("not found :(\n"); + } + + /* Must be a module? If not, kernel will reject... */ +- /* memset to all 0 for your memcmp convenience. */ ++ /* memset to all 0 for your memcmp convenience: don't clear version */ + memset(t->u.user.name + strlen(t->u.user.name), + 0, +- FUNCTION_MAXNAMELEN - strlen(t->u.user.name)); ++ FUNCTION_MAXNAMELEN - 1 - strlen(t->u.user.name)); ++ r->type = IPTCC_R_MODULE; ++ set_changed(handle); + 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, +@@ -912,36 +1280,56 @@ + unsigned int rulenum, + TC_HANDLE_T *handle) + { +- unsigned int chainindex, offset; +- STRUCT_ENTRY_TARGET old; +- struct chain_cache *c; +- STRUCT_ENTRY *tmp; +- int ret; ++ struct chain_head *c; ++ struct rule_head *r; ++ struct list_head *prev; + + iptc_fn = TC_INSERT_ENTRY; +- if (!(c = find_label(chain, *handle))) { ++ ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- chainindex = entry2index(*handle, c->start); +- +- tmp = index2entry(*handle, chainindex + rulenum); +- if (!tmp || tmp > c->end) { ++ /* first rulenum index = 0 ++ first c->num_rules index = 1 */ ++ if (rulenum > c->num_rules) { + 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)) ++ /* If we are inserting at the end just take advantage of the ++ double linked list, insert will happen before the entry ++ prev points to. */ ++ if (rulenum == c->num_rules) { ++ prev = &c->rules; ++ } else if (rulenum + 1 <= c->num_rules/2) { ++ r = iptcc_get_rule_num(c, rulenum + 1); ++ prev = &r->list; ++ } else { ++ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum); ++ prev = &r->list; ++ } ++ ++ if (!(r = iptcc_alloc_rule(c, e->next_offset))) { ++ errno = ENOMEM; + return 0; ++ } + +- ret = insert_rules(1, e->next_offset, e, offset, +- chainindex + rulenum, rulenum == 0, handle); +- unmap_target((STRUCT_ENTRY *)e, &old); +- return ret; ++ memcpy(r->entry, e, e->next_offset); ++ r->counter_map.maptype = COUNTER_MAP_SET; ++ ++ if (!iptcc_map_target(*handle, r)) { ++ free(r); ++ return 0; ++ } ++ ++ list_add_tail(&r->list, prev); ++ c->num_rules++; ++ ++ set_changed(*handle); ++ ++ return 1; + } + + /* Atomically replace rule `rulenum' in `chain' with `fw'. */ +@@ -951,40 +1339,47 @@ + unsigned int rulenum, + TC_HANDLE_T *handle) + { +- unsigned int chainindex, offset; +- STRUCT_ENTRY_TARGET old; +- struct chain_cache *c; +- STRUCT_ENTRY *tmp; +- int ret; ++ struct chain_head *c; ++ struct rule_head *r, *old; + + iptc_fn = TC_REPLACE_ENTRY; + +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- chainindex = entry2index(*handle, c->start); +- +- tmp = index2entry(*handle, chainindex + rulenum); +- if (!tmp || tmp >= c->end) { ++ if (rulenum >= c->num_rules) { + 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)) ++ /* Take advantage of the double linked list if possible. */ ++ if (rulenum + 1 <= c->num_rules/2) { ++ old = iptcc_get_rule_num(c, rulenum + 1); ++ } else { ++ old = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum); ++ } ++ ++ if (!(r = iptcc_alloc_rule(c, e->next_offset))) { ++ errno = ENOMEM; + return 0; ++ } + +- if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old)) ++ memcpy(r->entry, e, e->next_offset); ++ r->counter_map.maptype = COUNTER_MAP_SET; ++ ++ if (!iptcc_map_target(*handle, r)) { ++ free(r); + return 0; ++ } + +- ret = insert_rules(1, e->next_offset, e, offset, +- chainindex + rulenum, 1, handle); +- unmap_target((STRUCT_ENTRY *)e, &old); +- return ret; ++ list_add(&r->list, &old->list); ++ iptcc_delete_rule(old); ++ ++ set_changed(*handle); ++ ++ return 1; + } + + /* Append entry `fw' to chain `chain'. Equivalent to insert with +@@ -994,26 +1389,37 @@ + const STRUCT_ENTRY *e, + TC_HANDLE_T *handle) + { +- struct chain_cache *c; +- STRUCT_ENTRY_TARGET old; +- int ret; ++ struct chain_head *c; ++ struct rule_head *r; + + iptc_fn = TC_APPEND_ENTRY; +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { ++ DEBUGP("unable to find chain `%s'\n", chain); + errno = ENOENT; + return 0; + } + +- if (!map_target(*handle, (STRUCT_ENTRY *)e, +- entry2offset(*handle, c->end), &old)) ++ if (!(r = iptcc_alloc_rule(c, e->next_offset))) { ++ DEBUGP("unable to allocate rule for chain `%s'\n", chain); ++ errno = ENOMEM; + return 0; ++ } + +- ret = insert_rules(1, e->next_offset, e, +- entry2offset(*handle, c->end), +- entry2index(*handle, c->end), +- 0, handle); +- unmap_target((STRUCT_ENTRY *)e, &old); +- return ret; ++ memcpy(r->entry, e, e->next_offset); ++ r->counter_map.maptype = COUNTER_MAP_SET; ++ ++ if (!iptcc_map_target(*handle, r)) { ++ DEBUGP("unable to map target of rule for chain `%s'\n", chain); ++ free(r); ++ return 0; ++ } ++ ++ list_add_tail(&r->list, &c->rules); ++ c->num_rules++; ++ ++ set_changed(*handle); ++ ++ return 1; + } + + static inline int +@@ -1044,20 +1450,42 @@ + } + + static inline int +-target_different(const unsigned char *a_targdata, +- const unsigned char *b_targdata, +- unsigned int tdatasize, +- const unsigned char *mask) ++target_same(struct rule_head *a, struct rule_head *b,const unsigned char *mask) + { + unsigned int i; +- for (i = 0; i < tdatasize; i++) +- if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0) ++ STRUCT_ENTRY_TARGET *ta, *tb; ++ ++ if (a->type != b->type) ++ return 0; ++ ++ ta = GET_TARGET(a->entry); ++ tb = GET_TARGET(b->entry); ++ ++ switch (a->type) { ++ case IPTCC_R_FALLTHROUGH: + return 1; ++ case IPTCC_R_JUMP: ++ return a->jump == b->jump; ++ case IPTCC_R_STANDARD: ++ return ((STRUCT_STANDARD_TARGET *)ta)->verdict ++ == ((STRUCT_STANDARD_TARGET *)tb)->verdict; ++ case IPTCC_R_MODULE: ++ if (ta->u.target_size != tb->u.target_size) ++ return 0; ++ if (strcmp(ta->u.user.name, tb->u.user.name) != 0) ++ return 0; + ++ for (i = 0; i < ta->u.target_size - sizeof(*ta); i++) ++ if (((ta->data[i] ^ tb->data[i]) & mask[i]) != 0) + return 0; ++ return 1; ++ default: ++ fprintf(stderr, "ERROR: bad type %i\n", a->type); ++ abort(); ++ } + } + +-static int ++static unsigned char * + is_same(const STRUCT_ENTRY *a, + const STRUCT_ENTRY *b, + unsigned char *matchmask); +@@ -1069,88 +1497,106 @@ + unsigned char *matchmask, + TC_HANDLE_T *handle) + { +- unsigned int offset; +- struct chain_cache *c; +- STRUCT_ENTRY *e, *fw; ++ struct chain_head *c; ++ struct rule_head *r, *i; + + iptc_fn = TC_DELETE_ENTRY; +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- fw = malloc(origfw->next_offset); +- if (fw == NULL) { ++ /* Create a rule_head from origfw. */ ++ r = iptcc_alloc_rule(c, origfw->next_offset); ++ if (!r) { + errno = ENOMEM; + return 0; + } + +- for (offset = entry2offset(*handle, c->start); +- offset < entry2offset(*handle, c->end); +- 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); ++ memcpy(r->entry, origfw, origfw->next_offset); ++ r->counter_map.maptype = COUNTER_MAP_NOMAP; ++ if (!iptcc_map_target(*handle, r)) { ++ DEBUGP("unable to map target of rule for chain `%s'\n", chain); ++ free(r); + 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; ++ list_for_each_entry(i, &c->rules, list) { ++ unsigned char *mask; ++ ++ mask = is_same(r->entry, i->entry, matchmask); ++ if (!mask) ++ continue; ++ ++ if (!target_same(r, i, mask)) ++ continue; ++ ++ /* If we are about to delete the rule that is the ++ * current iterator, move rule iterator back. next ++ * pointer will then point to real next node */ ++ if (i == (*handle)->rule_iterator_cur) { ++ (*handle)->rule_iterator_cur = ++ list_entry((*handle)->rule_iterator_cur->list.prev, ++ struct rule_head, list); + } ++ ++ c->num_rules--; ++ iptcc_delete_rule(i); ++ ++ set_changed(*handle); ++ free(r); ++ return 1; + } + +- free(fw); ++ free(r); + 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; ++ struct chain_head *c; ++ struct rule_head *r; + + iptc_fn = TC_DELETE_NUM_ENTRY; +- if (!(c = find_label(chain, *handle))) { ++ ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- index = entry2index(*handle, c->start) + rulenum; +- +- if (index >= entry2index(*handle, c->end)) { ++ if (rulenum >= c->num_rules) { + errno = E2BIG; + return 0; + } + +- e = index2entry(*handle, index); +- if (e == NULL) { +- errno = EINVAL; +- return 0; ++ /* Take advantage of the double linked list if possible. */ ++ if (rulenum + 1 <= c->num_rules/2) { ++ r = iptcc_get_rule_num(c, rulenum + 1); ++ } else { ++ r = iptcc_get_rule_num_reverse(c, c->num_rules - rulenum); + } + +- ret = delete_rules(1, e->next_offset, entry2offset(*handle, e), +- index, handle); +- return ret; ++ /* If we are about to delete the rule that is the current ++ * iterator, move rule iterator back. next pointer will then ++ * point to real next node */ ++ if (r == (*handle)->rule_iterator_cur) { ++ (*handle)->rule_iterator_cur = ++ list_entry((*handle)->rule_iterator_cur->list.prev, ++ struct rule_head, list); ++ } ++ ++ c->num_rules--; ++ iptcc_delete_rule(r); ++ ++ set_changed(*handle); ++ ++ return 1; + } + + /* Check the packet `fw' on chain `chain'. Returns the verdict, or +@@ -1160,6 +1606,7 @@ + STRUCT_ENTRY *entry, + TC_HANDLE_T *handle) + { ++ iptc_fn = TC_CHECK_PACKET; + errno = ENOSYS; + return NULL; + } +@@ -1168,44 +1615,44 @@ + int + TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle) + { +- unsigned int startindex, endindex; +- struct chain_cache *c; +- int ret; ++ struct chain_head *c; ++ struct rule_head *r, *tmp; + + iptc_fn = TC_FLUSH_ENTRIES; +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } +- startindex = entry2index(*handle, c->start); +- endindex = entry2index(*handle, c->end); + +- ret = delete_rules(endindex - startindex, +- (char *)c->end - (char *)c->start, +- entry2offset(*handle, c->start), startindex, +- handle); +- return ret; ++ list_for_each_entry_safe(r, tmp, &c->rules, list) { ++ iptcc_delete_rule(r); ++ } ++ ++ c->num_rules = 0; ++ ++ set_changed(*handle); ++ ++ return 1; + } + + /* 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; ++ struct chain_head *c; ++ struct rule_head *r; + +- if (!(c = find_label(chain, *handle))) { ++ iptc_fn = TC_ZERO_ENTRIES; ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- i = entry2index(*handle, c->start); +- end = entry2index(*handle, c->end); +- +- for (; i <= end; i++) { +- if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP) +- (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED; ++ list_for_each_entry(r, &c->rules, list) { ++ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP) ++ r->counter_map.maptype = COUNTER_MAP_ZEROED; + } ++ + set_changed(*handle); + + return 1; +@@ -1216,29 +1663,23 @@ + unsigned int rulenum, + TC_HANDLE_T *handle) + { +- STRUCT_ENTRY *e; +- struct chain_cache *c; +- unsigned int chainindex, end; ++ struct chain_head *c; ++ struct rule_head *r; + + iptc_fn = TC_READ_COUNTER; + CHECK(*handle); + +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return NULL; + } + +- chainindex = entry2index(*handle, c->start); +- end = entry2index(*handle, c->end); +- +- if (chainindex + rulenum > end) { ++ if (!(r = iptcc_get_rule_num(c, rulenum))) { + errno = E2BIG; + return NULL; + } + +- e = index2entry(*handle, chainindex + rulenum); +- +- return &e->counters; ++ return &r->entry[0].counters; + } + + int +@@ -1246,33 +1687,24 @@ + unsigned int rulenum, + TC_HANDLE_T *handle) + { +- STRUCT_ENTRY *e; +- struct chain_cache *c; +- unsigned int chainindex, end; ++ struct chain_head *c; ++ struct rule_head *r; + + iptc_fn = TC_ZERO_COUNTER; + CHECK(*handle); + +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- chainindex = entry2index(*handle, c->start); +- end = entry2index(*handle, c->end); +- +- if (chainindex + rulenum > end) { ++ if (!(r = iptcc_get_rule_num(c, rulenum))) { + 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; +-// } ++ if (r->counter_map.maptype == COUNTER_MAP_NORMAL_MAP) ++ r->counter_map.maptype = COUNTER_MAP_ZEROED; + + set_changed(*handle); + +@@ -1285,30 +1717,25 @@ + STRUCT_COUNTERS *counters, + TC_HANDLE_T *handle) + { ++ struct chain_head *c; ++ struct rule_head *r; + STRUCT_ENTRY *e; +- struct chain_cache *c; +- unsigned int chainindex, end; + + iptc_fn = TC_SET_COUNTER; + CHECK(*handle); + +- if (!(c = find_label(chain, *handle))) { ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- chainindex = entry2index(*handle, c->start); +- end = entry2index(*handle, c->end); +- +- if (chainindex + rulenum > end) { ++ if (!(r = iptcc_get_rule_num(c, rulenum))) { + errno = E2BIG; + return 0; + } + +- e = index2entry(*handle, chainindex + rulenum); +- +- (*handle)->counter_map[chainindex + rulenum].maptype +- = COUNTER_MAP_SET; ++ e = r->entry; ++ r->counter_map.maptype = COUNTER_MAP_SET; + + memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS)); + +@@ -1323,71 +1750,42 @@ + 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; ++ static struct chain_head *c; + + iptc_fn = TC_CREATE_CHAIN; + + /* find_label doesn't cover built-in targets: DROP, ACCEPT, + QUEUE, RETURN. */ +- if (find_label(chain, *handle) ++ if (iptcc_find_label(chain, *handle) + || strcmp(chain, LABEL_DROP) == 0 + || strcmp(chain, LABEL_ACCEPT) == 0 + || strcmp(chain, LABEL_QUEUE) == 0 + || strcmp(chain, LABEL_RETURN) == 0) { ++ DEBUGP("Chain `%s' already exists\n", chain); + errno = EEXIST; + return 0; + } + + if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) { ++ DEBUGP("Chain name `%s' too long\n", chain); + 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; +-} ++ c = iptcc_alloc_chain_head(chain, 0); ++ if (!c) { ++ DEBUGP("Cannot allocate memory for chain `%s'\n", chain); ++ errno = ENOMEM; ++ return 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); ++ DEBUGP("Creating chain `%s'\n", chain); ++ list_add_tail(&c->list, &(*handle)->chains); + +- if (t->verdict == offset) +- (*ref)++; +- } ++ set_changed(*handle); + +- return 0; ++ return 1; + } + + /* Get the number of references to this chain. */ +@@ -1395,17 +1793,16 @@ + TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain, + TC_HANDLE_T *handle) + { +- struct chain_cache *c; ++ struct chain_head *c; + +- if (!(c = find_label(chain, *handle))) { ++ iptc_fn = TC_GET_REFERENCES; ++ if (!(c = iptcc_find_label(chain, *handle))) { + errno = ENOENT; + return 0; + } + +- *ref = 0; +- ENTRY_ITERATE((*handle)->entries.entrytable, +- (*handle)->entries.size, +- count_ref, entry2offset(*handle, c->start), ref); ++ *ref = c->references; ++ + return 1; + } + +@@ -1413,45 +1810,53 @@ + 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; +- +- if (!TC_GET_REFERENCES(&references, chain, handle)) +- return 0; ++ struct chain_head *c; + + iptc_fn = TC_DELETE_CHAIN; + ++ if (!(c = iptcc_find_label(chain, *handle))) { ++ DEBUGP("cannot find chain `%s'\n", chain); ++ errno = ENOENT; ++ return 0; ++ } ++ + if (TC_BUILTIN(chain, *handle)) { ++ DEBUGP("cannot remove builtin chain `%s'\n", chain); + errno = EINVAL; + return 0; + } + +- if (references > 0) { +- errno = EMLINK; ++ if (!TC_GET_REFERENCES(&references, chain, handle)) { ++ DEBUGP("cannot get references on chain `%s'\n", chain); + return 0; + } + +- if (!(c = find_label(chain, *handle))) { +- errno = ENOENT; ++ if (references > 0) { ++ DEBUGP("chain `%s' still has references\n", chain); ++ errno = EMLINK; + return 0; + } + +- if ((void *)c->start != c->end) { ++ if (c->num_rules) { ++ DEBUGP("chain `%s' is not empty\n", chain); + errno = ENOTEMPTY; + return 0; + } + +- /* Need label index: preceeds chain start */ +- labelidx = entry2index(*handle, c->start) - 1; +- labeloff = index2offset(*handle, labelidx); ++ /* If we are about to delete the chain that is the current ++ * iterator, move chain iterator firward. */ ++ if (c == (*handle)->chain_iterator_cur) ++ iptcc_chain_iterator_advance(*handle); + +- ret = delete_rules(2, +- get_entry(*handle, labeloff)->next_offset +- + c->start->next_offset, +- labeloff, labelidx, handle); +- return ret; ++ list_del(&c->list); ++ free(c); ++ ++ DEBUGP("chain `%s' deleted\n", chain); ++ ++ set_changed(*handle); ++ ++ return 1; + } + + /* Renames a chain. */ +@@ -1459,15 +1864,12 @@ + const IPT_CHAINLABEL newname, + TC_HANDLE_T *handle) + { +- unsigned int labeloff, labelidx; +- struct chain_cache *c; +- struct ipt_error_target *t; +- ++ struct chain_head *c; + iptc_fn = TC_RENAME_CHAIN; + + /* find_label doesn't cover built-in targets: DROP, ACCEPT, + QUEUE, RETURN. */ +- if (find_label(newname, *handle) ++ if (iptcc_find_label(newname, *handle) + || strcmp(newname, LABEL_DROP) == 0 + || strcmp(newname, LABEL_ACCEPT) == 0 + || strcmp(newname, LABEL_QUEUE) == 0 +@@ -1476,7 +1878,7 @@ + return 0; + } + +- if (!(c = find_label(oldname, *handle)) ++ if (!(c = iptcc_find_label(oldname, *handle)) + || TC_BUILTIN(oldname, *handle)) { + errno = ENOENT; + return 0; +@@ -1487,15 +1889,8 @@ + return 0; + } + +- /* Need label index: preceeds chain start */ +- labelidx = entry2index(*handle, c->start) - 1; +- labeloff = index2offset(*handle, labelidx); +- +- t = (struct ipt_error_target *) +- GET_TARGET(get_entry(*handle, labeloff)); ++ strncpy(c->name, newname, sizeof(IPT_CHAINLABEL)); + +- memset(t->error, 0, sizeof(t->error)); +- strcpy(t->error, newname); + set_changed(*handle); + + return 1; +@@ -1508,51 +1903,37 @@ + STRUCT_COUNTERS *counters, + TC_HANDLE_T *handle) + { +- unsigned int hook; +- unsigned int policyoff, ctrindex; +- STRUCT_ENTRY *e; +- STRUCT_STANDARD_TARGET *t; ++ struct chain_head *c; + + iptc_fn = TC_SET_POLICY; +- /* Figure out which chain. */ +- hook = TC_BUILTIN(chain, *handle); +- if (hook == 0) { ++ ++ if (!(c = iptcc_find_label(chain, *handle))) { ++ DEBUGP("cannot find chain `%s'\n", chain); + 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]); ++ if (!iptcc_is_builtin(c)) { ++ DEBUGP("cannot set policy of userdefinedchain `%s'\n", chain); ++ errno = ENOENT; + 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; ++ c->verdict = -NF_ACCEPT - 1; + else if (strcmp(policy, LABEL_DROP) == 0) +- t->verdict = -NF_DROP - 1; ++ c->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; +- ++ memcpy(&c->counters, counters, sizeof(STRUCT_COUNTERS)); ++ c->counter_map.maptype = COUNTER_MAP_SET; + } else { +- (*handle)->counter_map[ctrindex] +- = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 }); ++ c->counter_map.maptype = COUNTER_MAP_NOMAP; + } + + set_changed(*handle); +@@ -1575,31 +1956,100 @@ + answer->bcnt = a->bcnt - b->bcnt; + } + ++ ++static void counters_nomap(STRUCT_COUNTERS_INFO *newcounters, ++ unsigned int index) ++{ ++ newcounters->counters[index] = ((STRUCT_COUNTERS) { 0, 0}); ++ DEBUGP_C("NOMAP => zero\n"); ++} ++ ++static void counters_normal_map(STRUCT_COUNTERS_INFO *newcounters, ++ STRUCT_REPLACE *repl, ++ unsigned int index, ++ unsigned int mappos) ++{ ++ /* 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[index] = repl->counters[mappos]; ++ DEBUGP_C("NORMAL_MAP => mappos %u \n", mappos); ++} ++ ++static void counters_map_zeroed(STRUCT_COUNTERS_INFO *newcounters, ++ STRUCT_REPLACE *repl, ++ unsigned int index, ++ unsigned int mappos, ++ STRUCT_COUNTERS *counters) ++{ ++ /* 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[index], ++ &repl->counters[mappos], ++ counters); ++ DEBUGP_C("ZEROED => mappos %u\n", mappos); ++} ++ ++static void counters_map_set(STRUCT_COUNTERS_INFO *newcounters, ++ unsigned int index, ++ STRUCT_COUNTERS *counters) ++{ ++ /* Want to set counter (iptables-restore) */ ++ ++ memcpy(&newcounters->counters[index], counters, ++ sizeof(STRUCT_COUNTERS)); ++ ++ DEBUGP_C("SET\n"); ++} ++ ++ + 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 +- = sizeof(STRUCT_COUNTERS_INFO) +- + sizeof(STRUCT_COUNTERS) * (*handle)->new_number; ++ struct chain_head *c; ++ int ret; ++ size_t counterlen; ++ int new_number; ++ unsigned int new_size; + ++ iptc_fn = TC_COMMIT; + CHECK(*handle); +-#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); ++ new_number = iptcc_compile_table_prep(*handle, &new_size); ++ if (new_number < 0) { ++ errno = ENOMEM; ++ return 0; ++ } ++ ++ repl = malloc(sizeof(*repl) + new_size); + if (!repl) { + errno = ENOMEM; + return 0; + } ++ memset(repl, 0, sizeof(*repl) + new_size); ++ ++#if 0 ++ TC_DUMP_ENTRIES(*handle); ++#endif ++ ++ counterlen = sizeof(STRUCT_COUNTERS_INFO) ++ + sizeof(STRUCT_COUNTERS) * new_number; + + /* These are the old counters we will get from kernel */ + repl->counters = malloc(sizeof(STRUCT_COUNTERS) +@@ -1609,7 +2059,6 @@ + errno = ENOMEM; + return 0; + } +- + /* These are the counters we're going to put back, later. */ + newcounters = malloc(counterlen); + if (!newcounters) { +@@ -1618,21 +2067,40 @@ + errno = ENOMEM; + return 0; + } ++ memset(newcounters, 0, counterlen); + + 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_entries = new_number; ++ repl->size = new_size; ++ + repl->num_counters = (*handle)->info.num_entries; + repl->valid_hooks = (*handle)->info.valid_hooks; +- memcpy(repl->entries, (*handle)->entries.entrytable, +- (*handle)->entries.size); ++ ++ DEBUGP("num_entries=%u, size=%u, num_counters=%u\n", ++ repl->num_entries, repl->size, repl->num_counters); ++ ++ ret = iptcc_compile_table(*handle, repl); ++ if (ret < 0) { ++ errno = ret; ++ free(repl->counters); ++ free(repl); ++ return 0; ++ } ++ ++ ++#ifdef IPTC_DEBUG2 ++ { ++ int fd = open("/tmp/libiptc-so_set_replace.blob", ++ O_CREAT|O_WRONLY); ++ if (fd >= 0) { ++ write(fd, repl, sizeof(*repl) + repl->size); ++ close(fd); ++ } ++ } ++#endif + + if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl, +- sizeof(*repl) + (*handle)->entries.size) < 0) { ++ sizeof(*repl) + repl->size) < 0) { + free(repl->counters); + free(repl); + free(newcounters); +@@ -1641,49 +2109,64 @@ + + /* 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) { ++ newcounters->num_counters = new_number; ++ ++ list_for_each_entry(c, &(*handle)->chains, list) { ++ struct rule_head *r; ++ ++ /* Builtin chains have their own counters */ ++ if (iptcc_is_builtin(c)) { ++ DEBUGP("counter for chain-index %u: ", c->foot_index); ++ switch(c->counter_map.maptype) { ++ case COUNTER_MAP_NOMAP: ++ counters_nomap(newcounters, c->foot_index); ++ break; ++ case COUNTER_MAP_NORMAL_MAP: ++ counters_normal_map(newcounters, repl, ++ c->foot_index, ++ c->counter_map.mappos); ++ break; ++ case COUNTER_MAP_ZEROED: ++ counters_map_zeroed(newcounters, repl, ++ c->foot_index, ++ c->counter_map.mappos, ++ &c->counters); ++ break; ++ case COUNTER_MAP_SET: ++ counters_map_set(newcounters, c->foot_index, ++ &c->counters); ++ break; ++ } ++ } ++ ++ list_for_each_entry(r, &c->rules, list) { ++ DEBUGP("counter for index %u: ", r->index); ++ switch (r->counter_map.maptype) { + case COUNTER_MAP_NOMAP: +- newcounters->counters[i] +- = ((STRUCT_COUNTERS){ 0, 0 }); ++ counters_nomap(newcounters, r->index); + 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]; ++ counters_normal_map(newcounters, repl, ++ r->index, ++ r->counter_map.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); ++ counters_map_zeroed(newcounters, repl, ++ r->index, ++ r->counter_map.mappos, ++ &r->entry->counters); + break; + + case COUNTER_MAP_SET: +- /* Want to set counter (iptables-restore) */ +- +- memcpy(&newcounters->counters[i], +- &index2entry(*handle, i)->counters, +- sizeof(STRUCT_COUNTERS)); +- ++ counters_map_set(newcounters, r->index, ++ &r->entry->counters); + break; + } + } ++ } ++ + + #ifdef KERNEL_64_USERSPACE_32 + { +@@ -1696,10 +2179,21 @@ + "counters alignment incorrect! Mail rusty!\n"); + abort(); + } +- *kernptr = &newcounters->counters; ++ *kernptr = newcounters->counters; + } + #endif /* KERNEL_64_USERSPACE_32 */ + ++#ifdef IPTC_DEBUG2 ++ { ++ int fd = open("/tmp/libiptc-so_set_add_counters.blob", ++ O_CREAT|O_WRONLY); ++ if (fd >= 0) { ++ write(fd, newcounters, counterlen); ++ close(fd); ++ } ++ } ++#endif ++ + if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS, + newcounters, counterlen) < 0) { + free(repl->counters); +@@ -1713,10 +2207,7 @@ + free(newcounters); + + finished: +- if ((*handle)->cache_chain_heads) +- free((*handle)->cache_chain_heads); +- free(*handle); +- *handle = NULL; ++ TC_FREE(handle); + return 1; + } + +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/libiptc.h /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libiptc.h +--- ./agents/iptables/libiptc.h 2003-07-06 18:33:17.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/libiptc.h 2005-09-07 07:39:56.000000000 +0800 +@@ -1,7 +1,3 @@ +-#ifndef NETFILTER_VERSION +-#define NETFILTER_VERSION "1.2.5" +-#endif +- + #ifndef _LIBIPTC_H + #define _LIBIPTC_H + /* Library which manipulates filtering rules. */ +@@ -38,6 +34,9 @@ + /* Take a snapshot of the rules. Returns NULL on error. */ + iptc_handle_t iptc_init(const char *tablename); + ++/* Cleanup after iptc_init(). */ ++void iptc_free(iptc_handle_t *h); ++ + /* Iterator functions to run through the chains. Returns NULL at end. */ + const char *iptc_first_chain(iptc_handle_t *handle); + const char *iptc_next_chain(iptc_handle_t *handle); +diff -urNad --exclude=CVS --exclude=.svn ./agents/iptables/linux_list.h /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/linux_list.h +--- ./agents/iptables/linux_list.h 1970-01-01 08:00:00.000000000 +0800 ++++ /tmp/dpep-work.j5GgPv/ipac-ng-1.31/agents/iptables/linux_list.h 2005-09-07 07:39:56.000000000 +0800 +@@ -0,0 +1,723 @@ ++#ifndef _LINUX_LIST_H ++#define _LINUX_LIST_H ++ ++#undef offsetof ++#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) ++ ++/** ++ * container_of - cast a member of a structure out to the containing structure ++ * ++ * @ptr: the pointer to the member. ++ * @type: the type of the container struct this is embedded in. ++ * @member: the name of the member within the struct. ++ * ++ */ ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++ ++/* ++ * Check at compile time that something is of a particular type. ++ * Always evaluates to 1 so you may use it easily in comparisons. ++ */ ++#define typecheck(type,x) \ ++({ type __dummy; \ ++ typeof(x) __dummy2; \ ++ (void)(&__dummy == &__dummy2); \ ++ 1; \ ++}) ++ ++#define prefetch(x) 1 ++ ++/* empty define to make this work in userspace -HW */ ++#define smp_wmb() ++ ++/* ++ * These are non-NULL pointers that will result in page faults ++ * under normal circumstances, used to verify that nobody uses ++ * non-initialized list entries. ++ */ ++#define LIST_POISON1 ((void *) 0x00100100) ++#define LIST_POISON2 ((void *) 0x00200200) ++ ++/* ++ * 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); ++} ++ ++/* ++ * 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_rcu(struct list_head * new, ++ struct list_head * prev, struct list_head * next) ++{ ++ new->next = next; ++ new->prev = prev; ++ smp_wmb(); ++ next->prev = new; ++ prev->next = new; ++} ++ ++/** ++ * list_add_rcu - add a new entry to rcu-protected list ++ * @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. ++ * ++ * The caller must take whatever precautions are necessary ++ * (such as holding appropriate locks) to avoid racing ++ * with another list-mutation primitive, such as list_add_rcu() ++ * or list_del_rcu(), running on this same list. ++ * However, it is perfectly legal to run concurrently with ++ * the _rcu list-traversal primitives, such as ++ * list_for_each_entry_rcu(). ++ */ ++static inline void list_add_rcu(struct list_head *new, struct list_head *head) ++{ ++ __list_add_rcu(new, head, head->next); ++} ++ ++/** ++ * list_add_tail_rcu - add a new entry to rcu-protected list ++ * @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. ++ * ++ * The caller must take whatever precautions are necessary ++ * (such as holding appropriate locks) to avoid racing ++ * with another list-mutation primitive, such as list_add_tail_rcu() ++ * or list_del_rcu(), running on this same list. ++ * However, it is perfectly legal to run concurrently with ++ * the _rcu list-traversal primitives, such as ++ * list_for_each_entry_rcu(). ++ */ ++static inline void list_add_tail_rcu(struct list_head *new, ++ struct list_head *head) ++{ ++ __list_add_rcu(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); ++ entry->next = LIST_POISON1; ++ entry->prev = LIST_POISON2; ++} ++ ++/** ++ * list_del_rcu - deletes entry from list without re-initialization ++ * @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. It is useful for RCU based ++ * lockfree traversal. ++ * ++ * In particular, it means that we can not poison the forward ++ * pointers that may still be used for walking the list. ++ * ++ * The caller must take whatever precautions are necessary ++ * (such as holding appropriate locks) to avoid racing ++ * with another list-mutation primitive, such as list_del_rcu() ++ * or list_add_rcu(), running on this same list. ++ * However, it is perfectly legal to run concurrently with ++ * the _rcu list-traversal primitives, such as ++ * list_for_each_entry_rcu(). ++ * ++ * Note that the caller is not permitted to immediately free ++ * the newly deleted entry. Instead, either synchronize_kernel() ++ * or call_rcu() must be used to defer freeing until an RCU ++ * grace period has elapsed. ++ */ ++static inline void list_del_rcu(struct list_head *entry) ++{ ++ __list_del(entry->prev, entry->next); ++ entry->prev = LIST_POISON2; ++} ++ ++/** ++ * 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_move - delete from one list and add as another's head ++ * @list: the entry to move ++ * @head: the head that will precede our entry ++ */ ++static inline void list_move(struct list_head *list, struct list_head *head) ++{ ++ __list_del(list->prev, list->next); ++ list_add(list, head); ++} ++ ++/** ++ * list_move_tail - delete from one list and add as another's tail ++ * @list: the entry to move ++ * @head: the head that will follow our entry ++ */ ++static inline void list_move_tail(struct list_head *list, ++ struct list_head *head) ++{ ++ __list_del(list->prev, list->next); ++ list_add_tail(list, head); ++} ++ ++/** ++ * list_empty - tests whether a list is empty ++ * @head: the list to test. ++ */ ++static inline int list_empty(const struct list_head *head) ++{ ++ return head->next == head; ++} ++ ++/** ++ * list_empty_careful - tests whether a list is ++ * empty _and_ checks that no other CPU might be ++ * in the process of still modifying either member ++ * ++ * NOTE: using list_empty_careful() without synchronization ++ * can only be safe if the only activity that can happen ++ * to the list entry is list_del_init(). Eg. it cannot be used ++ * if another CPU could re-list_add() it. ++ * ++ * @head: the list to test. ++ */ ++static inline int list_empty_careful(const struct list_head *head) ++{ ++ struct list_head *next = head->next; ++ return (next == head) && (next == head->prev); ++} ++ ++static inline void __list_splice(struct list_head *list, ++ struct list_head *head) ++{ ++ struct list_head *first = list->next; ++ 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_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) ++{ ++ if (!list_empty(list)) ++ __list_splice(list, head); ++} ++ ++/** ++ * list_splice_init - join two lists and reinitialise the emptied list. ++ * @list: the new list to add. ++ * @head: the place to add it in the first list. ++ * ++ * The list at @list is reinitialised ++ */ ++static inline void list_splice_init(struct list_head *list, ++ struct list_head *head) ++{ ++ if (!list_empty(list)) { ++ __list_splice(list, head); ++ INIT_LIST_HEAD(list); ++ } ++} ++ ++/** ++ * 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) \ ++ container_of(ptr, type, 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, prefetch(pos->next); pos != (head); \ ++ pos = pos->next, prefetch(pos->next)) ++ ++/** ++ * __list_for_each - iterate over a list ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ * ++ * This variant differs from list_for_each() in that it's the ++ * simplest possible list iteration code, no prefetching is done. ++ * Use this for code that knows the list to be very short (empty ++ * or 1 entry) most of the time. ++ */ ++#define __list_for_each(pos, head) \ ++ for (pos = (head)->next; pos != (head); pos = pos->next) ++ ++/** ++ * list_for_each_prev - iterate over a list backwards ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ */ ++#define list_for_each_prev(pos, head) \ ++ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \ ++ pos = pos->prev, prefetch(pos->prev)) ++ ++/** ++ * 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) ++ ++/** ++ * list_for_each_entry - iterate over list of given type ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry(pos, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member), \ ++ prefetch(pos->member.next); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.next, typeof(*pos), member), \ ++ prefetch(pos->member.next)) ++ ++/** ++ * list_for_each_entry_reverse - iterate backwards over list of given type. ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry_reverse(pos, head, member) \ ++ for (pos = list_entry((head)->prev, typeof(*pos), member), \ ++ prefetch(pos->member.prev); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.prev, typeof(*pos), member), \ ++ prefetch(pos->member.prev)) ++ ++/** ++ * list_prepare_entry - prepare a pos entry for use as a start point in ++ * list_for_each_entry_continue ++ * @pos: the type * to use as a start point ++ * @head: the head of the list ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_prepare_entry(pos, head, member) \ ++ ((pos) ? : list_entry(head, typeof(*pos), member)) ++ ++/** ++ * list_for_each_entry_continue - iterate over list of given type ++ * continuing after existing point ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry_continue(pos, head, member) \ ++ for (pos = list_entry(pos->member.next, typeof(*pos), member), \ ++ prefetch(pos->member.next); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.next, typeof(*pos), member), \ ++ prefetch(pos->member.next)) ++ ++/** ++ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry ++ * @pos: the type * to use as a loop counter. ++ * @n: another type * to use as temporary storage ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ */ ++#define list_for_each_entry_safe(pos, n, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member), \ ++ n = list_entry(pos->member.next, typeof(*pos), member); \ ++ &pos->member != (head); \ ++ pos = n, n = list_entry(n->member.next, typeof(*n), member)) ++ ++/** ++ * list_for_each_rcu - iterate over an rcu-protected list ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as list_add_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define list_for_each_rcu(pos, head) \ ++ for (pos = (head)->next, prefetch(pos->next); pos != (head); \ ++ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next)) ++ ++#define __list_for_each_rcu(pos, head) \ ++ for (pos = (head)->next; pos != (head); \ ++ pos = pos->next, ({ smp_read_barrier_depends(); 0;})) ++ ++/** ++ * list_for_each_safe_rcu - iterate over an rcu-protected 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. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as list_add_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define list_for_each_safe_rcu(pos, n, head) \ ++ for (pos = (head)->next, n = pos->next; pos != (head); \ ++ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next) ++ ++/** ++ * list_for_each_entry_rcu - iterate over rcu list of given type ++ * @pos: the type * to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the list_struct within the struct. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as list_add_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define list_for_each_entry_rcu(pos, head, member) \ ++ for (pos = list_entry((head)->next, typeof(*pos), member), \ ++ prefetch(pos->member.next); \ ++ &pos->member != (head); \ ++ pos = list_entry(pos->member.next, typeof(*pos), member), \ ++ ({ smp_read_barrier_depends(); 0;}), \ ++ prefetch(pos->member.next)) ++ ++ ++/** ++ * list_for_each_continue_rcu - iterate over an rcu-protected list ++ * continuing after existing point. ++ * @pos: the &struct list_head to use as a loop counter. ++ * @head: the head for your list. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as list_add_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define list_for_each_continue_rcu(pos, head) \ ++ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \ ++ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next)) ++ ++/* ++ * Double linked lists with a single pointer list head. ++ * Mostly useful for hash tables where the two pointer list head is ++ * too wasteful. ++ * You lose the ability to access the tail in O(1). ++ */ ++ ++struct hlist_head { ++ struct hlist_node *first; ++}; ++ ++struct hlist_node { ++ struct hlist_node *next, **pprev; ++}; ++ ++#define HLIST_HEAD_INIT { .first = NULL } ++#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } ++#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) ++#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) ++ ++static inline int hlist_unhashed(const struct hlist_node *h) ++{ ++ return !h->pprev; ++} ++ ++static inline int hlist_empty(const struct hlist_head *h) ++{ ++ return !h->first; ++} ++ ++static inline void __hlist_del(struct hlist_node *n) ++{ ++ struct hlist_node *next = n->next; ++ struct hlist_node **pprev = n->pprev; ++ *pprev = next; ++ if (next) ++ next->pprev = pprev; ++} ++ ++static inline void hlist_del(struct hlist_node *n) ++{ ++ __hlist_del(n); ++ n->next = LIST_POISON1; ++ n->pprev = LIST_POISON2; ++} ++ ++/** ++ * hlist_del_rcu - deletes entry from hash list without re-initialization ++ * @n: the element to delete from the hash list. ++ * ++ * Note: list_unhashed() on entry does not return true after this, ++ * the entry is in an undefined state. It is useful for RCU based ++ * lockfree traversal. ++ * ++ * In particular, it means that we can not poison the forward ++ * pointers that may still be used for walking the hash list. ++ * ++ * The caller must take whatever precautions are necessary ++ * (such as holding appropriate locks) to avoid racing ++ * with another list-mutation primitive, such as hlist_add_head_rcu() ++ * or hlist_del_rcu(), running on this same list. ++ * However, it is perfectly legal to run concurrently with ++ * the _rcu list-traversal primitives, such as ++ * hlist_for_each_entry(). ++ */ ++static inline void hlist_del_rcu(struct hlist_node *n) ++{ ++ __hlist_del(n); ++ n->pprev = LIST_POISON2; ++} ++ ++static inline void hlist_del_init(struct hlist_node *n) ++{ ++ if (n->pprev) { ++ __hlist_del(n); ++ INIT_HLIST_NODE(n); ++ } ++} ++ ++#define hlist_del_rcu_init hlist_del_init ++ ++static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) ++{ ++ struct hlist_node *first = h->first; ++ n->next = first; ++ if (first) ++ first->pprev = &n->next; ++ h->first = n; ++ n->pprev = &h->first; ++} ++ ++ ++/** ++ * hlist_add_head_rcu - adds the specified element to the specified hlist, ++ * while permitting racing traversals. ++ * @n: the element to add to the hash list. ++ * @h: the list to add to. ++ * ++ * The caller must take whatever precautions are necessary ++ * (such as holding appropriate locks) to avoid racing ++ * with another list-mutation primitive, such as hlist_add_head_rcu() ++ * or hlist_del_rcu(), running on this same list. ++ * However, it is perfectly legal to run concurrently with ++ * the _rcu list-traversal primitives, such as ++ * hlist_for_each_entry(), but only if smp_read_barrier_depends() ++ * is used to prevent memory-consistency problems on Alpha CPUs. ++ * Regardless of the type of CPU, the list-traversal primitive ++ * must be guarded by rcu_read_lock(). ++ * ++ * OK, so why don't we have an hlist_for_each_entry_rcu()??? ++ */ ++static inline void hlist_add_head_rcu(struct hlist_node *n, ++ struct hlist_head *h) ++{ ++ struct hlist_node *first = h->first; ++ n->next = first; ++ n->pprev = &h->first; ++ smp_wmb(); ++ if (first) ++ first->pprev = &n->next; ++ h->first = n; ++} ++ ++/* next must be != NULL */ ++static inline void hlist_add_before(struct hlist_node *n, ++ struct hlist_node *next) ++{ ++ n->pprev = next->pprev; ++ n->next = next; ++ next->pprev = &n->next; ++ *(n->pprev) = n; ++} ++ ++static inline void hlist_add_after(struct hlist_node *n, ++ struct hlist_node *next) ++{ ++ next->next = n->next; ++ n->next = next; ++ next->pprev = &n->next; ++ ++ if(next->next) ++ next->next->pprev = &next->next; ++} ++ ++#define hlist_entry(ptr, type, member) container_of(ptr,type,member) ++ ++#define hlist_for_each(pos, head) \ ++ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ ++ pos = pos->next) ++ ++#define hlist_for_each_safe(pos, n, head) \ ++ for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ ++ pos = n) ++ ++/** ++ * hlist_for_each_entry - iterate over list of given type ++ * @tpos: the type * to use as a loop counter. ++ * @pos: the &struct hlist_node to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry(tpos, pos, head, member) \ ++ for (pos = (head)->first; \ ++ pos && ({ prefetch(pos->next); 1;}) && \ ++ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ ++ pos = pos->next) ++ ++/** ++ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point ++ * @tpos: the type * to use as a loop counter. ++ * @pos: the &struct hlist_node to use as a loop counter. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry_continue(tpos, pos, member) \ ++ for (pos = (pos)->next; \ ++ pos && ({ prefetch(pos->next); 1;}) && \ ++ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ ++ pos = pos->next) ++ ++/** ++ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point ++ * @tpos: the type * to use as a loop counter. ++ * @pos: the &struct hlist_node to use as a loop counter. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry_from(tpos, pos, member) \ ++ for (; pos && ({ prefetch(pos->next); 1;}) && \ ++ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ ++ pos = pos->next) ++ ++/** ++ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry ++ * @tpos: the type * to use as a loop counter. ++ * @pos: the &struct hlist_node to use as a loop counter. ++ * @n: another &struct hlist_node to use as temporary storage ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ */ ++#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ ++ for (pos = (head)->first; \ ++ pos && ({ n = pos->next; 1; }) && \ ++ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ ++ pos = n) ++ ++/** ++ * hlist_for_each_entry_rcu - iterate over rcu list of given type ++ * @pos: the type * to use as a loop counter. ++ * @pos: the &struct hlist_node to use as a loop counter. ++ * @head: the head for your list. ++ * @member: the name of the hlist_node within the struct. ++ * ++ * This list-traversal primitive may safely run concurrently with ++ * the _rcu list-mutation primitives such as hlist_add_rcu() ++ * as long as the traversal is guarded by rcu_read_lock(). ++ */ ++#define hlist_for_each_entry_rcu(tpos, pos, head, member) \ ++ for (pos = (head)->first; \ ++ pos && ({ prefetch(pos->next); 1;}) && \ ++ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ ++ pos = pos->next, ({ smp_read_barrier_depends(); 0; }) ) ++ ++#endif --- ipac-ng-1.31.orig/debian/rules +++ ipac-ng-1.31/debian/rules @@ -0,0 +1,88 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +# export DH_VERBOSE=1 + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + ./configure --prefix=/usr \ + --mandir=/usr/share/man \ + --datadir=/var/lib/ipac \ + --enable-classic=yes \ + --enable-default-storage=plain-file \ + --enable-default-access=files \ + --enable-default-agent=iptables + +# --enable-auth-server=localhost \ +# --enable-classic=yes \ + + touch configure-stamp + +build: configure-stamp build-stamp +build-stamp: patch + dh_testdir + + # Add here commands to compile the package. + $(MAKE) + + touch build-stamp + +clean: unpatch + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) distclean + -rm -f storage/gdbm/libstorgdbm.a storage/plain-file/libstorplain-file.a + + dh_clean + +install: DH_OPTIONS= +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/ipac-ng. + $(MAKE) install DESTDIR=$(CURDIR)/debian/ipac-ng + +# dh_movefiles --sourcedir=debian/ipac-ng + +binary-indep: build install + +binary-arch: build install + dh_testdir -a + dh_testroot -a + dh_installdebconf -a + # copy example config + cp debian/ipac.conf `pwd`/debian/ipac-ng/etc/ipac-ng/ + cp debian/rules.conf `pwd`/debian/ipac-ng/etc/ipac-ng/ + dh_installdocs -a + dh_installexamples --all contrib/ + dh_installinit --update-rcd-params='start 19 2 3 4 5 . stop 19 0 1 6 .' + dh_installcron -a + dh_installchangelogs CHANGELOG -a + dh_strip -a + dh_link -a + dh_compress -a + dh_fixperms -a + dh_installdeb -a + dh_shlibdeps -a + dh_gencontrol -a + dh_md5sums -a + dh_builddeb -a + +patch: patch-stamp +patch-stamp: + dpatch apply-all + +unpatch: + dpatch deapply-all + rm -rf patch-stamp debian/patched + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure patch unpatch --- ipac-ng-1.31.orig/debian/rules.conf +++ ipac-ng-1.31/debian/rules.conf @@ -0,0 +1,32 @@ +# Example config file with accounting rules for iptables +# Install as /etc/ipac-ng/rules.conf +# +# Format: +# Name of rule|direction|interface|protocol|source|destination|extension| +# WARNING!!!! spaces are not allowed before and after '|'. +# +# where +# Name of rule Any string to identify this rule +# direction ipac~fi - forward in +# ipac~fo - forward out +# ipac~i - outgoing from machine with ipac-ng to other host(/net) +# (or incoming to otherhost) +# ipac~o - incoming to machine with ipac-ng +# (or outgoing from otherhost) +# +# interface interface name, '+' means all interfaces (dont try to use ip numbers here!) +# protocol tcp | udp | icmp | all +# source \ +# destination both as described in ipfwadm(8), or empty +# + +# +# W A R N I N G ! ! ! +# +# Don't use symbols other than '0-9A-z[space]' in rules names. You may encounter +# some strange troubles. + +Incoming Total System|ipac~o|eth0|all|||| +Incoming Total System|ipac~fi|eth0|all|||| +Outgoing Total System|ipac~i|eth0|all|||| +Outgoing Total System|ipac~fo|eth0|all|||| --- ipac-ng-1.31.orig/fetchipac.8 +++ ipac-ng-1.31/fetchipac.8 @@ -67,7 +67,7 @@ .I gdbm mysql sqlite plain-file postgre . The default storage method used is .\" =()<.IR @@.>()= -.IR gdbm. +.IR plain-file. .B fetchipac uses a directory to store data. The default directory for that is