summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Boucher <marc@mbsi.ca>2000-03-20 06:03:29 +0000
committerMarc Boucher <marc@mbsi.ca>2000-03-20 06:03:29 +0000
commite6869a8f59d779ff4d5a0984c86d80db70784962 (patch)
treecbaf2a4e3f8249de3967b959a214c27ff5fdee2a
reorganized tree after kernel merge
-rw-r--r--COPYING339
-rw-r--r--Makefile64
-rw-r--r--Rules.make56
-rw-r--r--extensions/Makefile14
-rw-r--r--extensions/libipt_DNAT.c244
-rw-r--r--extensions/libipt_LOG.c260
-rw-r--r--extensions/libipt_MARK.c120
-rw-r--r--extensions/libipt_MASQUERADE.c166
-rw-r--r--extensions/libipt_REDIRECT.c166
-rw-r--r--extensions/libipt_REJECT.c159
-rw-r--r--extensions/libipt_SNAT.c244
-rw-r--r--extensions/libipt_TOS.c173
-rw-r--r--extensions/libipt_icmp.c295
-rw-r--r--extensions/libipt_limit.c196
-rw-r--r--extensions/libipt_mac.c144
-rw-r--r--extensions/libipt_mark.c128
-rw-r--r--extensions/libipt_multiport.c262
-rw-r--r--extensions/libipt_owner.c219
-rw-r--r--extensions/libipt_standard.c67
-rw-r--r--extensions/libipt_state.c162
-rw-r--r--extensions/libipt_tcp.c439
-rw-r--r--extensions/libipt_tos.c171
-rw-r--r--extensions/libipt_udp.c252
-rw-r--r--extensions/libipt_unclean.c66
-rw-r--r--include/iptables.h122
-rw-r--r--include/libipq/libipq.h77
-rw-r--r--include/libiptc/ipt_kernel_headers.h22
-rw-r--r--include/libiptc/libiptc.h131
-rw-r--r--iptables-restore.c154
-rw-r--r--iptables-save.c260
-rw-r--r--iptables-standalone.c50
-rw-r--r--iptables.8708
-rw-r--r--iptables.c1963
-rw-r--r--libipq/IPQ.notes.txt118
-rw-r--r--libipq/Makefile11
-rw-r--r--libipq/libipq.c310
-rw-r--r--libiptc/Makefile16
-rw-r--r--libiptc/libiptc.c1828
38 files changed, 10176 insertions, 0 deletions
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..a43ea212
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..6090e4f8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+# Standard part of Makefile for topdir.
+TOPLEVEL_INCLUDED=YES
+
+ifndef KERNEL_DIR
+KERNEL_DIR=/usr/src/linux
+endif
+NETFILTER_VERSION:=1.0.0alpha
+
+LIBDIR:=/usr/local/lib
+BINDIR:=/usr/local/bin
+MANDIR:=/usr/local/man
+
+COPT_FLAGS:=-O
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -Iinclude/ -I$(KERNEL_DIR)/include -DNETFILTER_VERSION=\"$(NETFILTER_VERSION)\" -g
+
+DEPFILES := $(SHARED_LIBS:%.so=%.d)
+SH_CFLAGS:=$(CFLAGS) -fPIC
+DEPFILES := $(SHARED_LIBS:%.so=%.d)
+
+EXTRAS+=iptables iptables.o #iptables-save iptables-restore
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8 #$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore
+
+ifndef IPT_LIBDIR
+IPT_LIBDIR:=$(LIBDIR)/iptables
+endif
+
+default: all
+
+iptables.o: iptables.c
+ $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+iptables: iptables-standalone.c iptables.o libiptc/libiptc.a
+ $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -rdynamic -o $@ $^ -ldl
+
+$(DESTDIR)$(BINDIR)/iptables: iptables
+ @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp $< $@
+
+iptables-save: iptables-save.c iptables.o libiptc/libiptc.a
+ $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -rdynamic -o $@ $^ -ldl
+
+$(DESTDIR)$(BINDIR)/iptables-save: iptables-save
+ @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp $< $@
+
+iptables-restore: iptables-restore.c iptables.o libiptc/libiptc.a
+ $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -rdynamic -o $@ $^ -ldl
+
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore
+ @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+ cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/iptables.8: iptables.8
+ @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+ cp $< $@
+
+EXTRA_DEPENDS+=iptables-standalone.d iptables.d
+
+iptables-standalone.d iptables.d: %.d: %.c
+ @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
+
+# $(wildcard) fails wierdly with make v.3.78.1.
+include $(shell echo */Makefile)
+include Rules.make
diff --git a/Rules.make b/Rules.make
new file mode 100644
index 00000000..b90866d5
--- /dev/null
+++ b/Rules.make
@@ -0,0 +1,56 @@
+#! /usr/bin/make
+
+all: $(SHARED_LIBS) $(EXTRAS)
+
+clean: $(EXTRA_CLEANS)
+ rm -f $(SHARED_LIBS) $(EXTRAS) $(SHARED_LIBS:%.so=%_sh.o)
+
+install: all $(EXTRA_INSTALLS)
+
+TAGS:
+ @rm -f $@
+ find . -name '*.[ch]' | xargs etags -a
+
+dep: $(DEPFILES) $(EXTRA_DEPENDS)
+ @echo Dependencies will be generated on next make.
+ @rm -f $(DEPFILES) $(EXTRA_DEPENDS) .makefirst
+
+$(SHARED_LIBS:%.so=%.d): %.d: %.c
+ @-$(CC) -M -MG $(CFLAGS) $< | \
+ sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
+
+$(SHARED_LIBS): %.so : %_sh.o
+ $(LD) -shared -o $@ $<
+
+%_sh.o : %.c
+ $(CC) $(SH_CFLAGS) -o $@ -c $<
+
+distrib: nowhitespace distclean delrelease /home/public/netfilter/netfilter-$(NETFILTER_VERSION).tar.bz2 #diff md5sums
+
+delrelease:
+ rm -f /home/public/netfilter/netfilter-$(NETFILTER_VERSION).tar.bz2
+
+distclean: clean
+ @rm -f TAGS `find . -name '*~' -o -name '*.[do]' -o -name '*.rej'` .makefirst
+
+nowhitespace:
+ @if grep -n '[ ]$$' `find . -name 'Makefile' -o -name '*.[ch]'`; then exit 1; else exit 0; fi
+
+/home/public/netfilter/netfilter-$(NETFILTER_VERSION).tar.bz2:
+ cd .. && ln -sfn netfilter netfilter-$(NETFILTER_VERSION) && tar cvf - --exclude install-kernel --exclude transfer --exclude netfilter-$(NETFILTER_VERSION)/bugs --exclude CVS --exclude .depend --exclude netfilter-$(NETFILTER_VERSION)/./NAT/userspace/.depend --exclude netfilter-$(NETFILTER_VERSION)/linux-netfilter netfilter-$(NETFILTER_VERSION)/. | bzip2 -9 > $@ && rm netfilter-$(NETFILTER_VERSION)
+
+diff: /home/public/netfilter/netfilter-$(NETFILTER_VERSION).tar.bz2
+ @mkdir /tmp/diffdir
+ @cd /tmp/diffdir && tar xfI /home/public/netfilter/netfilter-$(NETFILTER_VERSION).tar.bz2
+ @set -e; cd /tmp/diffdir; tar xfI /home/public/netfilter/netfilter-$(OLD_NETFILTER_VERSION).tar.bz2; echo Creating patch-netfilter-$(OLD_NETFILTER_VERSION)-$(NETFILTER_VERSION).bz2; diff -urN netfilter-$(OLD_NETFILTER_VERSION) netfilter-$(NETFILTER_VERSION) | bzip2 -9 > /home/public/netfilter/patch-netfilter-$(OLD_NETFILTER_VERSION)-$(NETFILTER_VERSION).bz2
+ @rm -rf /tmp/diffdir
+
+md5sums:
+ cd /home/public/netfilter/ && md5sum patch-netfilter-*-$(NETFILTER_VERSION).bz2 netfilter-$(NETFILTER_VERSION).tar.bz2
+
+.makefirst:
+ @echo Making dependencies: please wait...
+ @touch .makefirst
+
+-include $(DEPFILES) $(EXTRA_DEPENDS)
+-include .makefirst
diff --git a/extensions/Makefile b/extensions/Makefile
new file mode 100644
index 00000000..8e0dec1c
--- /dev/null
+++ b/extensions/Makefile
@@ -0,0 +1,14 @@
+#! /usr/bin/make
+
+PF_EXT_SLIB:=tcp udp icmp mac limit standard REJECT LOG unclean state multiport tos TOS mark MARK owner SNAT DNAT MASQUERADE REDIRECT
+SHARED_LIBS+=$(foreach T,$(PF_EXT_SLIB),extensions/libipt_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF_EXT_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libipt_$(T).so)
+
+ifndef TOPLEVEL_INCLUDED
+local:
+ cd .. && $(MAKE) $(SHARED_LIBS)
+endif
+
+$(DESTDIR)$(LIBDIR)/iptables/libipt_%.so: extensions/libipt_%.so
+ @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
+ cp $< $@
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
new file mode 100644
index 00000000..e3c37222
--- /dev/null
+++ b/extensions/libipt_DNAT.c
@@ -0,0 +1,244 @@
+/* Shared library add-on to iptables to add destination-NAT support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+
+/* Dest NAT data consists of a multi-range, indicating where to map
+ to. */
+struct ipt_natinfo
+{
+ struct ipt_entry_target t;
+ struct ip_nat_multi_range mr;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"DNAT v%s options:\n"
+" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
+" Address to map destination to.\n"
+" (You can use this more than once)\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "to-destination", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
+{
+ unsigned int size;
+
+ /* One rangesize already in struct ipt_natinfo */
+ size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+ info = realloc(info, size);
+ if (!info)
+ exit_error(OTHER_PROBLEM, "Out of memory\n");
+
+ info->t.target_size = size;
+ info->mr.range[info->mr.rangesize] = *range;
+ info->mr.rangesize++;
+
+ return info;
+}
+
+/* Ranges expected in network order. */
+static struct ipt_entry_target *
+parse_to(char *arg, int portok, struct ipt_natinfo *info)
+{
+ struct ip_nat_range range;
+ char *colon, *dash;
+ struct in_addr *ip;
+
+ memset(&range, 0, sizeof(range));
+ colon = strchr(arg, ':');
+
+ if (colon) {
+ int port;
+
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+ port = atoi(colon+1);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", colon+1);
+
+ dash = strchr(colon, '-');
+ if (!dash) {
+ range.min.tcp.port
+ = range.max.tcp.port
+ = htons(port);
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ /* People are stupid. */
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", colon+1);
+ range.min.tcp.port = htons(port);
+ range.max.tcp.port = htons(maxport);
+ }
+ /* Starts with a colon? No IP info...*/
+ if (colon == arg)
+ return &(append_range(info, &range)->t);
+ *colon = '\0';
+ }
+
+ range.flags |= IP_NAT_RANGE_MAP_IPS;
+ dash = strchr(arg, '-');
+ if (colon && dash && dash > colon)
+ dash = NULL;
+
+ if (dash)
+ *dash = '\0';
+
+ ip = dotted_to_addr(arg);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ arg);
+ range.min_ip = ip->s_addr;
+ if (dash) {
+ ip = dotted_to_addr(dash+1);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ dash+1);
+ range.max_ip = ip->s_addr;
+ } else
+ range.max_ip = range.min_ip;
+
+ return &(append_range(info, &range)->t);
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_natinfo *info = (void *)*target;
+ int portok;
+
+ if (entry->ip.proto == IPPROTO_TCP
+ || entry->ip.proto == IPPROTO_UDP)
+ portok = 1;
+ else
+ portok = 0;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --to-destination");
+
+ *target = parse_to(optarg, portok, info);
+ *flags = 1;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Final check; must have specfied --to-source. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "You must specify --to-destination");
+}
+
+static void print_range(const struct ip_nat_range *r)
+{
+ if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+ struct in_addr a;
+
+ a.s_addr = r->min_ip;
+ printf("%s", addr_to_dotted(&a));
+ if (r->max_ip != r->min_ip) {
+ a.s_addr = r->max_ip;
+ printf("-%s", addr_to_dotted(&a));
+ }
+ }
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf(":");
+ printf("%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", ntohs(r->max.tcp.port));
+ }
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ struct ipt_natinfo *info = (void *)target;
+ unsigned int i = 0;
+
+ printf("to:");
+ for (i = 0; i < info->mr.rangesize; i++) {
+ print_range(&info->mr.range[i]);
+ printf(" ");
+ }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ struct ipt_natinfo *info = (void *)target;
+ unsigned int i = 0;
+
+ for (i = 0; i < info->mr.rangesize; i++) {
+ printf("--to-destination ");
+ print_range(&info->mr.range[i]);
+ printf(" ");
+ }
+}
+
+struct iptables_target dnat
+= { NULL,
+ "DNAT",
+ NETFILTER_VERSION,
+ sizeof(struct ip_nat_multi_range),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&dnat);
+}
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
new file mode 100644
index 00000000..cab5739d
--- /dev/null
+++ b/extensions/libipt_LOG.c
@@ -0,0 +1,260 @@
+/* Shared library add-on to iptables to add LOG support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_LOG.h>
+
+#define LOG_DEFAULT_LEVEL LOG_WARNING
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"LOG v%s options:\n"
+" --log-level level Level of logging (numeric or see syslog.conf)\n"
+" --log-prefix prefix Prefix log messages with this prefix.\n\n"
+" --log-tcp-sequence Log TCP sequence numbers.\n\n"
+" --log-tcp-options Log TCP options.\n\n"
+" --log-ip-options Log IP options.\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "log-level", 1, 0, '!' },
+ { "log-prefix", 1, 0, '#' },
+ { "log-tcp-sequence", 0, 0, '1' },
+ { "log-tcp-options", 0, 0, '2' },
+ { "log-ip-options", 0, 0, '3' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ struct ipt_log_info *loginfo = (struct ipt_log_info *)t->data;
+
+ loginfo->level = LOG_DEFAULT_LEVEL;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+struct ipt_log_names {
+ const char *name;
+ unsigned int level;
+};
+
+static struct ipt_log_names ipt_log_names[]
+= { { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "debug", LOG_DEBUG },
+ { "emerg", LOG_EMERG },
+ { "error", LOG_ERR }, /* DEPRECATED */
+ { "info", LOG_INFO },
+ { "notice", LOG_NOTICE },
+ { "panic", LOG_EMERG }, /* DEPRECATED */
+ { "warning", LOG_WARNING }
+};
+
+static u_int8_t
+parse_level(const char *level)
+{
+ int lev;
+
+ lev = string_to_number(level, 0, 7);
+ if (lev == -1) {
+ unsigned int i = 0;
+
+ for (i = 0;
+ i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
+ i++) {
+ if (strncasecmp(level, ipt_log_names[i].name,
+ strlen(level)) == 0) {
+ if (lev != -1)
+ exit_error(PARAMETER_PROBLEM,
+ "log-level `%s' ambiguous",
+ level);
+ lev = ipt_log_names[i].level;
+ }
+ }
+
+ if (lev == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "log-level `%s' unknown", level);
+ }
+
+ return (u_int8_t)lev;
+}
+
+#define IPT_LOG_OPT_LEVEL 0x01
+#define IPT_LOG_OPT_PREFIX 0x02
+#define IPT_LOG_OPT_TCPSEQ 0x04
+#define IPT_LOG_OPT_TCPOPT 0x08
+#define IPT_LOG_OPT_IPOPT 0x10
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_log_info *loginfo = (struct ipt_log_info *)(*target)->data;
+
+ switch (c) {
+ case '!':
+ if (*flags & IPT_LOG_OPT_LEVEL)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify --log-level twice");
+
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log-level");
+
+ loginfo->level = parse_level(optarg);
+ *flags |= IPT_LOG_OPT_LEVEL;
+ break;
+
+ case '#':
+ if (*flags & IPT_LOG_OPT_PREFIX)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify --log-prefix twice");
+
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --log-prefix");
+
+ if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+ exit_error(PARAMETER_PROBLEM,
+ "Maximum prefix length %u for --log-prefix",
+ sizeof(loginfo->prefix) - 1);
+
+ strcpy(loginfo->prefix, optarg);
+ *flags |= IPT_LOG_OPT_PREFIX;
+ break;
+
+ case '1':
+ if (*flags & IPT_LOG_OPT_TCPSEQ)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify --log-tcp-sequence "
+ "twice");
+
+ loginfo->logflags |= IPT_LOG_TCPSEQ;
+ *flags |= IPT_LOG_OPT_TCPSEQ;
+ break;
+
+ case '2':
+ if (*flags & IPT_LOG_OPT_TCPOPT)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify --log-tcp-options twice");
+
+ loginfo->logflags |= IPT_LOG_TCPOPT;
+ *flags |= IPT_LOG_OPT_TCPOPT;
+ break;
+
+ case '3':
+ if (*flags & IPT_LOG_OPT_IPOPT)
+ exit_error(PARAMETER_PROBLEM,
+ "Can't specify --log-ip-options twice");
+
+ loginfo->logflags |= IPT_LOG_IPOPT;
+ *flags |= IPT_LOG_OPT_IPOPT;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ const struct ipt_log_info *loginfo
+ = (const struct ipt_log_info *)target->data;
+ unsigned int i = 0;
+
+ printf("LOG ");
+ if (numeric)
+ printf("flags %u level %u ",
+ loginfo->logflags, loginfo->level);
+ else {
+ for (i = 0;
+ i < sizeof(ipt_log_names) / sizeof(struct ipt_log_names);
+ i++) {
+ if (loginfo->level == ipt_log_names[i].level) {
+ printf("level %s ", ipt_log_names[i].name);
+ break;
+ }
+ }
+ if (i == sizeof(ipt_log_names) / sizeof(struct ipt_log_names))
+ printf("UNKNOWN level %u ", loginfo->level);
+ if (loginfo->logflags & IPT_LOG_TCPSEQ)
+ printf("tcp-sequence ");
+ if (loginfo->logflags & IPT_LOG_TCPOPT)
+ printf("tcp-options ");
+ if (loginfo->logflags & IPT_LOG_IPOPT)
+ printf("ip-options ");
+ if (loginfo->logflags & ~(IPT_LOG_MASK))
+ printf("unknown-flags ");
+ }
+
+ if (strcmp(loginfo->prefix, "") != 0)
+ printf("prefix `%s' ", loginfo->prefix);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ const struct ipt_log_info *loginfo
+ = (const struct ipt_log_info *)target->data;
+
+ if (strcmp(loginfo->prefix, "") != 0)
+ printf("--log-prefix %s ", loginfo->prefix);
+
+ if (loginfo->level != LOG_DEFAULT_LEVEL)
+ printf("--log-level %u ", loginfo->level);
+
+ if (loginfo->logflags & IPT_LOG_TCPSEQ)
+ printf("--log-tcp-sequence ");
+ if (loginfo->logflags & IPT_LOG_TCPOPT)
+ printf("--log-tcp-options ");
+ if (loginfo->logflags & IPT_LOG_IPOPT)
+ printf("--log-ip-options ");
+}
+
+struct iptables_target log
+= { NULL,
+ "LOG",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_log_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&log);
+}
diff --git a/extensions/libipt_MARK.c b/extensions/libipt_MARK.c
new file mode 100644
index 00000000..ef5a60d5
--- /dev/null
+++ b/extensions/libipt_MARK.c
@@ -0,0 +1,120 @@
+/* Shared library add-on to iptables to add MARK target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_MARK.h>
+
+struct markinfo {
+ struct ipt_entry_target t;
+ struct ipt_mark_target_info mark;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"MARK target v%s options:\n"
+" --set-mark value Set nfmark value\n"
+"\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "set-mark", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_mark_target_info *markinfo
+ = (struct ipt_mark_target_info *)(*target)->data;
+
+ switch (c) {
+ char *end;
+ case '1':
+ markinfo->mark = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+ if (*flags)
+ exit_error(PARAMETER_PROBLEM,
+ "MARK target: Can't specify --set-mark twice");
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "MARK target: Parameter --set-mark is required");
+}
+
+static void
+print_mark(unsigned long mark, int numeric)
+{
+ printf("0x%lx ", mark);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ const struct ipt_mark_target_info *markinfo =
+ (const struct ipt_mark_target_info *)target->data;
+ printf("MARK set ");
+ print_mark(markinfo->mark, numeric);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ const struct ipt_mark_target_info *markinfo =
+ (const struct ipt_mark_target_info *)target->data;
+
+ printf("--set-mark 0x%lx ", markinfo->mark);
+}
+
+struct iptables_target mark
+= { NULL,
+ "MARK",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_mark_target_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&mark);
+}
diff --git a/extensions/libipt_MASQUERADE.c b/extensions/libipt_MASQUERADE.c
new file mode 100644
index 00000000..a1151bbc
--- /dev/null
+++ b/extensions/libipt_MASQUERADE.c
@@ -0,0 +1,166 @@
+/* Shared library add-on to iptables to add masquerade support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"MASQUERADE v%s options:\n"
+" --to-ports <port>[-<port>]\n"
+" Port (range) to map to.\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "to-ports", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
+
+ /* Actually, it's 0, but it's ignored at the moment. */
+ mr->rangesize = 1;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct ip_nat_multi_range *mr)
+{
+ const char *dash;
+ int port;
+
+ mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+ port = atoi(arg);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM, "Port `%s' not valid\n", arg);
+
+ dash = strchr(arg, '-');
+ if (!dash) {
+ mr->range[0].min.tcp.port
+ = mr->range[0].max.tcp.port
+ = port;
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ /* People are stupid. Present reader excepted. */
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", arg);
+ mr->range[0].min.tcp.port = port;
+ mr->range[0].max.tcp.port = maxport;
+ }
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ int portok;
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)(*target)->data;
+
+ if (entry->ip.proto == IPPROTO_TCP
+ || entry->ip.proto == IPPROTO_UDP)
+ portok = 1;
+ else
+ portok = 0;
+
+ switch (c) {
+ case '1':
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --to-ports");
+
+ parse_ports(optarg, mr);
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)target->data;
+ struct ip_nat_range *r = &mr->range[0];
+
+ printf("MASQUERADE ");
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf("%hu", r->min.tcp.port);
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", r->max.tcp.port);
+ printf(" ");
+ }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)target->data;
+ struct ip_nat_range *r = &mr->range[0];
+
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf("%hu", r->min.tcp.port);
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", r->max.tcp.port);
+ printf(" ");
+ }
+}
+
+struct iptables_target masq
+= { NULL,
+ "MASQUERADE",
+ NETFILTER_VERSION,
+ sizeof(struct ip_nat_multi_range),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&masq);
+}
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
new file mode 100644
index 00000000..aaafaaf1
--- /dev/null
+++ b/extensions/libipt_REDIRECT.c
@@ -0,0 +1,166 @@
+/* Shared library add-on to iptables to add redirect support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"REDIRECT v%s options:\n"
+" --to-ports <port>[-<port>]\n"
+" Port (range) to map to.\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "to-ports", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *)t->data;
+
+ /* Actually, it's 0, but it's ignored at the moment. */
+ mr->rangesize = 1;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Parses ports */
+static void
+parse_ports(const char *arg, struct ip_nat_multi_range *mr)
+{
+ const char *dash;
+ int port;
+
+ mr->range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+ port = atoi(arg);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM, "Port `%s' not valid\n", arg);
+
+ dash = strchr(arg, '-');
+ if (!dash) {
+ mr->range[0].min.tcp.port
+ = mr->range[0].max.tcp.port
+ = port;
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ /* People are stupid. */
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", arg);
+ mr->range[0].min.tcp.port = port;
+ mr->range[0].max.tcp.port = maxport;
+ }
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)(*target)->data;
+ int portok;
+
+ if (entry->ip.proto == IPPROTO_TCP
+ || entry->ip.proto == IPPROTO_UDP)
+ portok = 1;
+ else
+ portok = 0;
+
+ switch (c) {
+ case '1':
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --to-ports");
+
+ parse_ports(optarg, mr);
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)target->data;
+ struct ip_nat_range *r = &mr->range[0];
+
+ printf("REDIRECT ");
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf("%hu", r->min.tcp.port);
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", r->max.tcp.port);
+ printf(" ");
+ }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ struct ip_nat_multi_range *mr
+ = (struct ip_nat_multi_range *)target->data;
+ struct ip_nat_range *r = &mr->range[0];
+
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf("%hu", r->min.tcp.port);
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", r->max.tcp.port);
+ printf(" ");
+ }
+}
+
+struct iptables_target redir
+= { NULL,
+ "REDIRECT",
+ NETFILTER_VERSION,
+ sizeof(struct ip_nat_multi_range),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&redir);
+}
diff --git a/extensions/libipt_REJECT.c b/extensions/libipt_REJECT.c
new file mode 100644
index 00000000..e3365873
--- /dev/null
+++ b/extensions/libipt_REJECT.c
@@ -0,0 +1,159 @@
+/* Shared library add-on to iptables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_REJECT.h>
+
+struct reject_names {
+ const char *name;
+ const char *alias;
+ enum ipt_reject_with with;
+ const char *desc;
+};
+
+static const struct reject_names reject_table[] = {
+ {"icmp-net-unreachable", "net-unreach",
+ IPT_ICMP_NET_UNREACHABLE, "ICMP network unreachable"},
+ {"icmp-host-unreachable", "host-unreach",
+ IPT_ICMP_HOST_UNREACHABLE, "ICMP host unreachable"},
+ {"icmp-port-unreachable", "port-unreach",
+ IPT_ICMP_PORT_UNREACHABLE, "ICMP port unreachable (default)"},
+ {"icmp-proto-unreachable", "proto-unreach",
+ IPT_ICMP_PROT_UNREACHABLE, "ICMP protocol unreachable"},
+ {"tcp-reset", "rst",
+ IPT_TCP_RESET, "for TCP only: faked TCP RST"},
+ {"echo-reply", "echoreply",
+ IPT_ICMP_ECHOREPLY, "for ICMP echo only: faked ICMP echo reply"},
+};
+
+static void
+print_reject_types()
+{
+ unsigned int i;
+
+ printf("Valid reject types:\n");
+
+ for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+ printf(" %-25s\t%s\n", reject_table[i].name, reject_table[i].desc);
+ printf(" %-25s\talias\n", reject_table[i].alias);
+ }
+ printf("\n");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"REJECT options:\n"
+"--reject-with type drop input packet and send back\n"
+" a reply packet according to type:\n");
+
+ print_reject_types();
+}
+
+static struct option opts[] = {
+ { "reject-with", 1, 0, '1' },
+ { 0 }
+};
+
+/* Allocate and initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ struct ipt_reject_info *reject = (struct ipt_reject_info *)t->data;
+
+ /* default */
+ reject->with = IPT_ICMP_PORT_UNREACHABLE;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_reject_info *reject = (struct ipt_reject_info *)(*target)->data;
+ unsigned int limit = sizeof(reject_table)/sizeof(struct reject_names);
+ unsigned int i;
+
+ switch(c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --reject-with");
+ for (i = 0; i < limit; i++) {
+ if ((strncasecmp(reject_table[i].name, optarg, strlen(optarg)) == 0)
+ || (strncasecmp(reject_table[i].alias, optarg, strlen(optarg)) == 0)) {
+ reject->with = reject_table[i].with;
+ return 1;
+ }
+ }
+ exit_error(PARAMETER_PROBLEM, "unknown reject type `%s'",optarg);
+ default:
+ /* Fall through */
+ }
+ return 0;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out ipt_reject_info. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ const struct ipt_reject_info *reject
+ = (const struct ipt_reject_info *)target->data;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(reject_table)/sizeof(struct reject_names); i++) {
+ if (reject_table[i].with == reject->with)
+ break;
+ }
+ printf("reject-with %s ", reject_table[i].name);
+}
+
+/* Saves ipt_reject in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ const struct ipt_reject_info *reject
+ = (const struct ipt_reject_info *)target->data;
+
+ printf("--reject-with %s ", reject_table[reject->with].name);
+}
+
+struct iptables_target reject
+= { NULL,
+ "REJECT",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_reject_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&reject);
+}
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
new file mode 100644
index 00000000..86ba39b9
--- /dev/null
+++ b/extensions/libipt_SNAT.c
@@ -0,0 +1,244 @@
+/* Shared library add-on to iptables to add source-NAT support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ip_nat_rule.h>
+
+/* Source NAT data consists of a multi-range, indicating where to map
+ to. */
+struct ipt_natinfo
+{
+ struct ipt_entry_target t;
+ struct ip_nat_multi_range mr;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"SNAT v%s options:\n"
+" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
+" Address to map source to.\n"
+" (You can use this more than once)\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "to-source", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+static struct ipt_natinfo *
+append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
+{
+ unsigned int size;
+
+ /* One rangesize already in struct ipt_natinfo */
+ size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
+
+ info = realloc(info, size);
+ if (!info)
+ exit_error(OTHER_PROBLEM, "Out of memory\n");
+
+ info->t.target_size = size;
+ info->mr.range[info->mr.rangesize] = *range;
+ info->mr.rangesize++;
+
+ return info;
+}
+
+/* Ranges expected in network order. */
+static struct ipt_entry_target *
+parse_to(char *arg, int portok, struct ipt_natinfo *info)
+{
+ struct ip_nat_range range;
+ char *colon, *dash;
+ struct in_addr *ip;
+
+ memset(&range, 0, sizeof(range));
+ colon = strchr(arg, ':');
+
+ if (colon) {
+ int port;
+
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
+
+ port = atoi(colon+1);
+ if (port == 0 || port > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", colon+1);
+
+ dash = strchr(colon, '-');
+ if (!dash) {
+ range.min.tcp.port
+ = range.max.tcp.port
+ = htons(port);
+ } else {
+ int maxport;
+
+ maxport = atoi(dash + 1);
+ if (maxport == 0 || maxport > 65535)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid\n", dash+1);
+ if (maxport < port)
+ /* People are stupid. */
+ exit_error(PARAMETER_PROBLEM,
+ "Port range `%s' funky\n", colon+1);
+ range.min.tcp.port = htons(port);
+ range.max.tcp.port = htons(maxport);
+ }
+ /* Starts with a colon? No IP info...*/
+ if (colon == arg)
+ return &(append_range(info, &range)->t);
+ *colon = '\0';
+ }
+
+ range.flags |= IP_NAT_RANGE_MAP_IPS;
+ dash = strchr(arg, '-');
+ if (colon && dash && dash > colon)
+ dash = NULL;
+
+ if (dash)
+ *dash = '\0';
+
+ ip = dotted_to_addr(arg);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ arg);
+ range.min_ip = ip->s_addr;
+ if (dash) {
+ ip = dotted_to_addr(dash+1);
+ if (!ip)
+ exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+ dash+1);
+ range.max_ip = ip->s_addr;
+ } else
+ range.max_ip = range.min_ip;
+
+ return &(append_range(info, &range)->t);
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_natinfo *info = (void *)*target;
+ int portok;
+
+ if (entry->ip.proto == IPPROTO_TCP
+ || entry->ip.proto == IPPROTO_UDP)
+ portok = 1;
+ else
+ portok = 0;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --to-source");
+
+ *target = parse_to(optarg, portok, info);
+ *flags = 1;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Final check; must have specfied --to-source. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "You must specify --to-source");
+}
+
+static void print_range(const struct ip_nat_range *r)
+{
+ if (r->flags & IP_NAT_RANGE_MAP_IPS) {
+ struct in_addr a;
+
+ a.s_addr = r->min_ip;
+ printf("%s", addr_to_dotted(&a));
+ if (r->max_ip != r->min_ip) {
+ a.s_addr = r->max_ip;
+ printf("-%s", addr_to_dotted(&a));
+ }
+ }
+ if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+ printf(":");
+ printf("%hu", ntohs(r->min.tcp.port));
+ if (r->max.tcp.port != r->min.tcp.port)
+ printf("-%hu", ntohs(r->max.tcp.port));
+ }
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ struct ipt_natinfo *info = (void *)target;
+ unsigned int i = 0;
+
+ printf("to:");
+ for (i = 0; i < info->mr.rangesize; i++) {
+ print_range(&info->mr.range[i]);
+ printf(" ");
+ }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ struct ipt_natinfo *info = (void *)target;
+ unsigned int i = 0;
+
+ for (i = 0; i < info->mr.rangesize; i++) {
+ printf("--to-source ");
+ print_range(&info->mr.range[i]);
+ printf(" ");
+ }
+}
+
+struct iptables_target snat
+= { NULL,
+ "SNAT",
+ NETFILTER_VERSION,
+ sizeof(struct ip_nat_multi_range),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&snat);
+}
diff --git a/extensions/libipt_TOS.c b/extensions/libipt_TOS.c
new file mode 100644
index 00000000..4a8e91ba
--- /dev/null
+++ b/extensions/libipt_TOS.c
@@ -0,0 +1,173 @@
+/* Shared library add-on to iptables to add TOS target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_TOS.h>
+
+struct tosinfo {
+ struct ipt_entry_target t;
+ struct ipt_tos_target_info tos;
+};
+
+/* TOS names and values. */
+struct TOS_value
+{
+ unsigned char TOS;
+ const char *name;
+} TOS_values[] = {
+ { IPTOS_LOWDELAY, "Minimize-Delay" },
+ { IPTOS_THROUGHPUT, "Maximize-Throughput" },
+ { IPTOS_RELIABILITY, "Maximize-Reliability" },
+ { IPTOS_MINCOST, "Minimize-Cost" },
+ { IPTOS_NORMALSVC, "Normal-Service" },
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ unsigned int i;
+
+ printf(
+"TOS target v%s options:\n"
+" --set-tos value Set Type of Service field to one of the\n"
+" following numeric or descriptive values:\n",
+NETFILTER_VERSION);
+
+ for (i = 0; i < sizeof(TOS_values)/sizeof(struct TOS_value);i++)
+ printf(" %s %u (0x%02x)\n",
+ TOS_values[i].name,
+ TOS_values[i].TOS,
+ TOS_values[i].TOS);
+ fputc('\n', stdout);
+}
+
+static struct option opts[] = {
+ { "set-tos", 1, 0, '1' },
+ { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+static void
+parse_tos(const unsigned char *s, struct ipt_tos_target_info *info)
+{
+ unsigned int i;
+ int tos = string_to_number(s, 0, 255);
+
+ if (tos != -1) {
+ if (tos == IPTOS_LOWDELAY
+ || tos == IPTOS_THROUGHPUT
+ || tos == IPTOS_RELIABILITY
+ || tos == IPTOS_MINCOST
+ || tos == IPTOS_NORMALSVC) {
+ info->tos = (u_int8_t )tos;
+ return;
+ }
+ } else {
+ for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+ if (strcasecmp(s,TOS_values[i].name) == 0) {
+ info->tos = TOS_values[i].TOS;
+ return;
+ }
+ }
+ exit_error(PARAMETER_PROBLEM, "Bad TOS value `%s'", s);
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ struct ipt_tos_target_info *tosinfo
+ = (struct ipt_tos_target_info *)(*target)->data;
+
+ switch (c) {
+ case '1':
+ if (*flags)
+ exit_error(PARAMETER_PROBLEM,
+ "TOS target: Cant specify --set-tos twice");
+ parse_tos(optarg, tosinfo);
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "TOS target: Parameter --set-tos is required");
+}
+
+static void
+print_tos(u_int8_t tos, int numeric)
+{
+ unsigned int i;
+
+ if (!numeric) {
+ for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+ if (TOS_values[i].TOS == tos) {
+ printf("%s ", TOS_values[i].name);
+ return;
+ }
+ }
+ printf("0x%02x ", tos);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_target *target,
+ int numeric)
+{
+ const struct ipt_tos_target_info *tosinfo =
+ (const struct ipt_tos_target_info *)target->data;
+ printf("TOS set ");
+ print_tos(tosinfo->tos, numeric);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+ const struct ipt_tos_target_info *tosinfo =
+ (const struct ipt_tos_target_info *)target->data;
+
+ printf("--set-tos 0x%02x ", tosinfo->tos);
+}
+
+struct iptables_target tos
+= { NULL,
+ "TOS",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_tos_target_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&tos);
+}
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
new file mode 100644
index 00000000..65857ce3
--- /dev/null
+++ b/extensions/libipt_icmp.c
@@ -0,0 +1,295 @@
+/* Shared library add-on to iptables to add ICMP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+struct icmp_names {
+ const char *name;
+ u_int8_t type;
+ u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+ { "echo-reply", 0, 0, 0xFF },
+ /* Alias */ { "pong", 0, 0, 0xFF },
+
+ { "destination-unreachable", 3, 0, 0xFF },
+ { "network-unreachable", 3, 0, 0 },
+ { "host-unreachable", 3, 1, 1 },
+ { "protocol-unreachable", 3, 2, 2 },
+ { "port-unreachable", 3, 3, 3 },
+ { "fragmentation-needed", 3, 4, 4 },
+ { "source-route-failed", 3, 5, 5 },
+ { "network-unknown", 3, 6, 6 },
+ { "host-unknown", 3, 7, 7 },
+ { "network-prohibited", 3, 9, 9 },
+ { "host-prohibited", 3, 10, 10 },
+ { "TOS-network-unreachable", 3, 11, 11 },
+ { "TOS-host-unreachable", 3, 12, 12 },
+ { "communication-prohibited", 3, 13, 13 },
+ { "host-precedence-violation", 3, 14, 14 },
+ { "precedence-cutoff", 3, 15, 15 },
+
+ { "source-quench", 4, 0, 0xFF },
+
+ { "redirect", 5, 0, 0xFF },
+ { "network-redirect", 5, 0, 0 },
+ { "host-redirect", 5, 1, 1 },
+ { "TOS-network-redirect", 5, 2, 2 },
+ { "TOS-host-redirect", 5, 3, 3 },
+
+ { "echo-request", 8, 0, 0xFF },
+ /* Alias */ { "ping", 8, 0, 0xFF },
+
+ { "router-advertisement", 9, 0, 0xFF },
+
+ { "router-solicitation", 10, 0, 0xFF },
+
+ { "time-exceeded", 11, 0, 0xFF },
+ /* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+ { "ttl-zero-during-transit", 11, 0, 0 },
+ { "ttl-zero-during-reassembly", 11, 1, 1 },
+
+ { "parameter-problem", 12, 0, 0xFF },
+ { "ip-header-bad", 12, 0, 0 },
+ { "required-option-missing", 12, 1, 1 },
+
+ { "timestamp-request", 13, 0, 0xFF },
+
+ { "timestamp-reply", 14, 0, 0xFF },
+
+ { "address-mask-request", 17, 0, 0xFF },
+
+ { "address-mask-reply", 18, 0, 0xFF }
+};
+
+static void
+print_icmptypes()
+{
+ unsigned int i;
+ printf("Valid ICMP Types:");
+
+ for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+ if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+ if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+ && (icmp_codes[i].code_max
+ == icmp_codes[i-1].code_max))
+ printf(" (%s)", icmp_codes[i].name);
+ else
+ printf("\n %s", icmp_codes[i].name);
+ }
+ else
+ printf("\n%s", icmp_codes[i].name);
+ }
+ printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"ICMP v%s options:\n"
+" --icmp-type [!] typename match icmp type\n"
+" (or numeric type or type/code)\n"
+"\n", NETFILTER_VERSION);
+ print_icmptypes();
+}
+
+static struct option opts[] = {
+ { "icmp-type", 1, 0, '1' },
+ {0}
+};
+
+static unsigned int
+parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
+{
+ unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+ unsigned int match = limit;
+ unsigned int i;
+
+ for (i = 0; i < limit; i++) {
+ if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+ == 0) {
+ if (match != limit)
+ exit_error(PARAMETER_PROBLEM,
+ "Ambiguous ICMP type `%s':"
+ " `%s' or `%s'?",
+ icmptype,
+ icmp_codes[match].name,
+ icmp_codes[i].name);
+ match = i;
+ }
+ }
+
+ if (match != limit) {
+ *type = icmp_codes[match].type;
+ code[0] = icmp_codes[match].code_min;
+ code[1] = icmp_codes[match].code_max;
+ } else {
+ char *slash;
+ char buffer[strlen(icmptype) + 1];
+ int number;
+
+ strcpy(buffer, icmptype);
+ slash = strchr(buffer, '/');
+
+ if (slash)
+ *slash = '\0';
+
+ number = string_to_number(buffer, 0, 255);
+ if (number == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid ICMP type `%s'\n", buffer);
+ *type = number;
+ if (slash) {
+ number = string_to_number(slash+1, 0, 255);
+ if (number == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid ICMP code `%s'\n",
+ slash+1);
+ code[0] = code[1] = number;
+ } else {
+ code[0] = 0;
+ code[1] = 0xFF;
+ }
+ }
+
+ if (code[0] == 0 && code[1] == 0xFF)
+ return NFC_IP_SRC_PT;
+ else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
+
+ icmpinfo->code[1] = 0xFF;
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ *nfcache |= parse_icmp(argv[optind-1],
+ &icmpinfo->type,
+ icmpinfo->code);
+ if (invert)
+ icmpinfo->invflags |= IPT_ICMP_INV;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void print_icmptype(u_int8_t type,
+ u_int8_t code_min, u_int8_t code_max,
+ int invert,
+ int numeric)
+{
+ if (!numeric) {
+ unsigned int i;
+
+ for (i = 0;
+ i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+ i++) {
+ if (icmp_codes[i].type == type
+ && icmp_codes[i].code_min == code_min
+ && icmp_codes[i].code_max == code_max)
+ break;
+ }
+
+ if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+ printf("%s%s ",
+ invert ? "!" : "",
+ icmp_codes[i].name);
+ return;
+ }
+ }
+
+ if (invert)
+ printf("!");
+
+ printf("type %u", type);
+ if (code_min == 0 && code_max == 0xFF)
+ printf(" ");
+ else if (code_min == code_max)
+ printf(" code %u ", code_min);
+ else
+ printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+ printf("icmp ");
+ print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+ icmp->invflags & IPT_ICMP_INV,
+ numeric);
+
+ if (icmp->invflags & ~IPT_ICMP_INV)
+ printf("Unknown invflags: 0x%X ",
+ icmp->invflags & ~IPT_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+ if (icmp->invflags & IPT_ICMP_INV)
+ printf("! ");
+
+ printf("--icmp-type %u", icmp->type);
+ if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+ printf("/%u", icmp->code[0]);
+ printf(" ");
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+struct iptables_match icmp
+= { NULL,
+ "icmp",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_icmp),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&icmp);
+}
diff --git a/extensions/libipt_limit.c b/extensions/libipt_limit.c
new file mode 100644
index 00000000..aed63059
--- /dev/null
+++ b/extensions/libipt_limit.c
@@ -0,0 +1,196 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * Jérôme de Vivie <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne <eychenne@info.enserb.u-bordeaux.fr>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_limit.h>
+
+#define IPT_LIMIT_AVG "3/hour"
+#define IPT_LIMIT_BURST 5
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"limit v%s options:\n"
+"--limit avg max average match rate: default "IPT_LIMIT_AVG"\n"
+" [Packets per second unless followed by \n"
+" /sec /minute /hour /day postfixes]\n"
+"--limit-burst number number to match in a burst, default %u\n"
+"\n", NETFILTER_VERSION, IPT_LIMIT_BURST);
+}
+
+static struct option opts[] = {
+ { "limit", 1, 0, '%' },
+ { "limit-burst", 1, 0, '$' },
+ { 0 }
+};
+
+static
+int parse_rate(const char *rate, u_int32_t *val)
+{
+ const char *delim;
+ u_int32_t r;
+ u_int32_t mult = 1; /* Seconds by default. */
+
+ delim = strchr(rate, '/');
+ if (delim) {
+ if (strlen(delim+1) == 0)
+ return 0;
+
+ if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0)
+ mult = 1;
+ else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0)
+ mult = 60;
+ else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0)
+ mult = 60*60;
+ else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0)
+ mult = 24*60*60;
+ else
+ return 0;
+ }
+ r = atoi(rate);
+ if (!r)
+ return 0;
+
+ /* This would get mapped to infinite (1/day is minimum they
+ can specify, so we're ok at that end). */
+ if (r / mult > IPT_LIMIT_SCALE)
+ exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+ *val = IPT_LIMIT_SCALE * mult / r;
+ return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ struct ipt_rateinfo *r = (struct ipt_rateinfo *)m->data;
+
+ parse_rate(IPT_LIMIT_AVG, &r->avg);
+ r->burst = IPT_LIMIT_BURST;
+
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* FIXME: handle overflow:
+ if (r->avg*r->burst/r->burst != r->avg)
+ exit_error(PARAMETER_PROBLEM,
+ "Sorry: burst too large for that avg rate.\n");
+*/
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_rateinfo *r = (struct ipt_rateinfo *)(*match)->data;
+ int num;
+
+ switch(c) {
+ case '%':
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --limit");
+ if (!parse_rate(optarg, &r->avg))
+ exit_error(PARAMETER_PROBLEM,
+ "bad rate `%s'", optarg);
+ break;
+
+ case '$':
+ if (check_inverse(optarg, &invert))
+ exit_error(PARAMETER_PROBLEM,
+ "Unexpected `!' after --limit-burst");
+
+ num = string_to_number(optarg, 0, 10000);
+ if (num <= 0)
+ exit_error(PARAMETER_PROBLEM,
+ "bad --limit-burst `%s'", optarg);
+ r->burst = num;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+static struct rates
+{
+ const char *name;
+ u_int32_t mult;
+} rates[] = { { "day", IPT_LIMIT_SCALE*24*60*60 },
+ { "hour", IPT_LIMIT_SCALE*60*60 },
+ { "min", IPT_LIMIT_SCALE*60 },
+ { "sec", IPT_LIMIT_SCALE } };
+
+static void print_rate(u_int32_t period)
+{
+ unsigned int i;
+
+ for (i = 1; i < sizeof(rates)/sizeof(struct rates); i++) {
+ if (period > rates[i].mult
+ || rates[i].mult % period != 0)
+ break;
+ }
+
+ printf("%u/%s ", rates[i-1].mult / period, rates[i-1].name);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
+ printf("limit: avg "); print_rate(r->avg);
+ printf("burst %u ", r->burst);
+}
+
+/* FIXME: Make minimalist: only print rate if not default --RR */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ struct ipt_rateinfo *r = (struct ipt_rateinfo *)match->data;
+
+ printf("--limit "); print_rate(r->avg);
+ if (r->burst != IPT_LIMIT_BURST)
+ printf("--limit-burst %u ", r->burst);
+}
+
+struct iptables_match limit
+= { NULL,
+ "limit",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_rateinfo),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&limit);
+}
diff --git a/extensions/libipt_mac.c b/extensions/libipt_mac.c
new file mode 100644
index 00000000..36d89692
--- /dev/null
+++ b/extensions/libipt_mac.c
@@ -0,0 +1,144 @@
+/* Shared library add-on to iptables to add MAC address support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_mac.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"MAC v%s options:\n"
+" --mac-source [!] XX:XX:XX:XX:XX:XX\n"
+" Match source MAC address\n"
+"\n", NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "mac-source", 1, 0, '1' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+static void
+parse_mac(const char *mac, struct ipt_mac_info *info)
+{
+ unsigned int i = 0;
+
+ if (strlen(mac) != ETH_ALEN*3-1)
+ exit_error(PARAMETER_PROBLEM, "Bad mac address `%s'", mac);
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ long number;
+ char *end;
+
+ number = strtol(mac + i*3, &end, 16);
+
+ if (end == mac + i*3 + 2
+ && number >= 0
+ && number <= 255)
+ info->srcaddr[i] = number;
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "Bad mac address `%s'", mac);
+ }
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_mac_info *macinfo = (struct ipt_mac_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_mac(argv[optind-1], macinfo);
+ if (invert)
+ macinfo->invert = 1;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void print_mac(unsigned char macaddress[ETH_ALEN], int invert)
+{
+ unsigned int i;
+
+ printf("%s%02X", invert ? "!" : "", macaddress[0]);
+ for (i = 1; i < ETH_ALEN; i++)
+ printf(":%02X", macaddress[i]);
+ printf(" ");
+}
+
+/* Final check; must have specified --mac. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "You must specify `--mac-source'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ printf("MAC ");
+ print_mac(((struct ipt_mac_info *)match->data)->srcaddr,
+ ((struct ipt_mac_info *)match->data)->invert);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ printf("--mac ");
+ print_mac(((struct ipt_mac_info *)match->data)->srcaddr,
+ ((struct ipt_mac_info *)match->data)->invert);
+}
+
+struct iptables_match mac
+= { NULL,
+ "mac",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_mac_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&mac);
+}
diff --git a/extensions/libipt_mark.c b/extensions/libipt_mark.c
new file mode 100644
index 00000000..318cd94d
--- /dev/null
+++ b/extensions/libipt_mark.c
@@ -0,0 +1,128 @@
+/* Shared library add-on to iptables to add NFMARK matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_mark.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"MARK match v%s options:\n"
+"[!] --mark value[/mask] Match nfmark value with optional mask\n"
+"\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "mark", 1, 0, '1' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ /* Can't cache this. */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_mark_info *markinfo = (struct ipt_mark_info *)(*match)->data;
+
+ switch (c) {
+ char *end;
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ markinfo->mark = strtoul(optarg, &end, 0);
+ if (*end == '/') {
+ markinfo->mask = strtoul(end+1, &end, 0);
+ } else
+ markinfo->mask = 0xffffffff;
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad MARK value `%s'", optarg);
+ if (invert)
+ markinfo->invert = 1;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+print_mark(unsigned long mark, unsigned long mask, int invert, int numeric)
+{
+ if (invert)
+ fputc('!', stdout);
+
+ if(mask != 0xffffffff)
+ printf("0x%lx/0x%lx ", mark, mask);
+ else
+ printf("0x%lx ", mark);
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "MARK match: You must specify `--mark'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ printf("MARK match ");
+ print_mark(((struct ipt_mark_info *)match->data)->mark,
+ ((struct ipt_mark_info *)match->data)->mask,
+ ((struct ipt_mark_info *)match->data)->invert, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ printf("--mark ");
+ print_mark(((struct ipt_mark_info *)match->data)->mark,
+ ((struct ipt_mark_info *)match->data)->mask,
+ ((struct ipt_mark_info *)match->data)->invert, 0);
+}
+
+struct iptables_match mark
+= { NULL,
+ "mark",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_mark_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&mark);
+}
diff --git a/extensions/libipt_multiport.c b/extensions/libipt_multiport.c
new file mode 100644
index 00000000..727f95fc
--- /dev/null
+++ b/extensions/libipt_multiport.c
@@ -0,0 +1,262 @@
+/* Shared library add-on to iptables to add multiple TCP port support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_multiport.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"multiport v%s options:\n"
+" --source-ports port[,port,port...]\n"
+" --sports ...\n"
+" match source port(s)\n"
+" --destination-ports port[,port,port...]\n"
+" --dports ...\n"
+" match destination port(s)\n"
+" --ports port[,port,port]\n"
+" match both source and destination port(s)\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "source-ports", 1, 0, '1' },
+ { "sports", 1, 0, '1' }, /* synonym */
+ { "destination-ports", 1, 0, '2' },
+ { "dports", 1, 0, '2' }, /* synonym */
+ { "ports", 1, 0, '3' },
+ {0}
+};
+
+static int
+service_to_port(const char *name, const char *proto)
+{
+ struct servent *service;
+
+ if ((service = getservbyname(name, proto)) != NULL)
+ return ntohs((unsigned short) service->s_port);
+
+ return -1;
+}
+
+static u_int16_t
+parse_port(const char *port, const char *proto)
+{
+ int portnum;
+ if ((portnum = string_to_number(port, 0, 65535)) != -1 ||
+ (portnum = service_to_port(port, proto)) != -1)
+ return (u_int16_t)portnum;
+
+ exit_error(PARAMETER_PROBLEM,
+ "invalid port/service `%s' specified", port);
+}
+
+static unsigned int
+parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
+{
+ char *buffer, *cp, *next;
+ unsigned int i;
+
+ buffer = strdup(portstring);
+ if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+ for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
+ {
+ next=strchr(cp, ',');
+ if (next) *next++='\0';
+ ports[i] = parse_port(cp, proto);
+ }
+ if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+ free(buffer);
+ return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static const char *
+check_proto(const struct ipt_entry *entry)
+{
+ if (entry->ip.proto == IPPROTO_TCP)
+ return "tcp";
+ else if (entry->ip.proto == IPPROTO_UDP)
+ return "udp";
+ else if (!entry->ip.proto)
+ exit_error(PARAMETER_PROBLEM,
+ "multiport needs `-p tcp' or `-p udp'");
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "multiport only works with TCP or UDP");
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ const char *proto;
+ struct ipt_multiport *multiinfo
+ = (struct ipt_multiport *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ proto = check_proto(entry);
+ multiinfo->count = parse_multi_ports(argv[optind-1],
+ multiinfo->ports, proto);
+ multiinfo->flags = IPT_MULTIPORT_SOURCE;
+ *nfcache |= NFC_IP_SRC_PT;
+ break;
+
+ case '2':
+ proto = check_proto(entry);
+ multiinfo->count = parse_multi_ports(argv[optind-1],
+ multiinfo->ports, proto);
+ multiinfo->flags = IPT_MULTIPORT_DESTINATION;
+ *nfcache |= NFC_IP_DST_PT;
+ break;
+
+ case '3':
+ proto = check_proto(entry);
+ multiinfo->count = parse_multi_ports(argv[optind-1],
+ multiinfo->ports, proto);
+ multiinfo->flags = IPT_MULTIPORT_EITHER;
+ *nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (*flags)
+ exit_error(PARAMETER_PROBLEM,
+ "multiport can only have one option");
+ *flags = 1;
+ return 1;
+}
+
+/* Final check; must specify something. */
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM, "multiport expection an option");
+}
+
+static char *
+port_to_service(int port, u_int8_t proto)
+{
+ struct servent *service;
+
+ if ((service = getservbyport(htons(port),
+ proto == IPPROTO_TCP ? "tcp" : "udp")))
+ return service->s_name;
+
+ return NULL;
+}
+
+static void
+print_port(u_int16_t port, u_int8_t protocol, int numeric)
+{
+ char *service;
+
+ if (numeric || (service = port_to_service(port, protocol)) == NULL)
+ printf("%u", port);
+ else
+ printf("%s", service);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ const struct ipt_multiport *multiinfo
+ = (const struct ipt_multiport *)match->data;
+ unsigned int i;
+
+ printf("multiport ");
+
+ switch (multiinfo->flags) {
+ case IPT_MULTIPORT_SOURCE:
+ printf("sports ");
+ break;
+
+ case IPT_MULTIPORT_DESTINATION:
+ printf("dports ");
+ break;
+
+ case IPT_MULTIPORT_EITHER:
+ printf("ports ");
+ break;
+
+ default:
+ printf("ERROR ");
+ break;
+ }
+
+ for (i=0; i < multiinfo->count; i++) {
+ printf("%s", i ? "," : "");
+ print_port(multiinfo->ports[i], ip->proto, numeric);
+ }
+ printf(" ");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct ipt_multiport *multiinfo
+ = (const struct ipt_multiport *)match->data;
+ unsigned int i;
+
+ switch (multiinfo->flags) {
+ case IPT_MULTIPORT_SOURCE:
+ printf("--sports ");
+ break;
+
+ case IPT_MULTIPORT_DESTINATION:
+ printf("--dports ");
+ break;
+
+ case IPT_MULTIPORT_EITHER:
+ printf("--ports ");
+ break;
+ }
+
+ for (i=0; i < multiinfo->count; i++) {
+ printf("%s", i ? "," : "");
+ print_port(multiinfo->ports[i], ip->proto, 0);
+ }
+ printf(" ");
+}
+
+struct iptables_match multiport
+= { NULL,
+ "multiport",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_multiport),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void
+_init(void)
+{
+ register_match(&multiport);
+}
diff --git a/extensions/libipt_owner.c b/extensions/libipt_owner.c
new file mode 100644
index 00000000..29311022
--- /dev/null
+++ b/extensions/libipt_owner.c
@@ -0,0 +1,219 @@
+/* Shared library add-on to iptables to add OWNER matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_owner.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"OWNER match v%s options:\n"
+"[!] --uid-owner userid Match local uid\n"
+"[!] --gid-owner groupid Match local gid\n"
+"[!] --pid-owner processid Match local pid\n"
+"[!] --sid-owner sessionid Match local sid\n"
+"\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "uid-owner", 1, 0, '1' },
+ { "gid-owner", 1, 0, '2' },
+ { "pid-owner", 1, 0, '3' },
+ { "sid-owner", 1, 0, '4' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ /* Can't cache this. */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_owner_info *ownerinfo = (struct ipt_owner_info *)(*match)->data;
+
+ switch (c) {
+ char *end;
+ struct passwd *pwd;
+ struct group *grp;
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+
+ if ((pwd = getpwnam(optarg)))
+ ownerinfo->uid = pwd->pw_uid;
+ else {
+ ownerinfo->uid = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad OWNER UID value `%s'", optarg);
+ }
+ if (invert)
+ ownerinfo->invert |= IPT_OWNER_UID;
+ ownerinfo->match |= IPT_OWNER_UID;
+ *flags = 1;
+ break;
+
+ case '2':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ if ((grp = getgrnam(optarg)))
+ ownerinfo->gid = grp->gr_gid;
+ else {
+ ownerinfo->gid = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad OWNER GID value `%s'", optarg);
+ }
+ if (invert)
+ ownerinfo->invert |= IPT_OWNER_GID;
+ ownerinfo->match |= IPT_OWNER_GID;
+ *flags = 1;
+ break;
+
+ case '3':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ ownerinfo->pid = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad OWNER PID value `%s'", optarg);
+ if (invert)
+ ownerinfo->invert |= IPT_OWNER_PID;
+ ownerinfo->match |= IPT_OWNER_PID;
+ *flags = 1;
+ break;
+
+ case '4':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ ownerinfo->sid = strtoul(optarg, &end, 0);
+ if (*end != '\0' || end == optarg)
+ exit_error(PARAMETER_PROBLEM, "Bad OWNER SID value `%s'", optarg);
+ if (invert)
+ ownerinfo->invert |= IPT_OWNER_SID;
+ ownerinfo->match |= IPT_OWNER_SID;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+print_item(struct ipt_owner_info *info, u_int8_t flag, int numeric, char *label)
+{
+ if(info->match & flag) {
+
+ printf(label);
+
+ if (info->invert & flag)
+ fputc('!', stdout);
+
+ switch(info->match & flag) {
+ case IPT_OWNER_UID:
+ if(!numeric) {
+ struct passwd *pwd = getpwuid(info->uid);
+
+ if(pwd && pwd->pw_name) {
+ printf("%s ", pwd->pw_name);
+ break;
+ }
+ /* FALLTHROUGH */
+ }
+ printf("%u ", info->uid);
+ break;
+ case IPT_OWNER_GID:
+ if(!numeric) {
+ struct group *grp = getgrgid(info->gid);
+
+ if(grp && grp->gr_name) {
+ printf("%s ", grp->gr_name);
+ break;
+ }
+ /* FALLTHROUGH */
+ }
+ printf("%u ", info->gid);
+ break;
+ case IPT_OWNER_PID:
+ printf("%u ", info->pid);
+ break;
+ case IPT_OWNER_SID:
+ printf("%u ", info->sid);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* Final check; must have specified --own. */
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "OWNER match: You must specify one or more options");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ struct ipt_owner_info *info = (struct ipt_owner_info *)match->data;
+
+ print_item(info, IPT_OWNER_UID, numeric, "OWNER UID match ");
+ print_item(info, IPT_OWNER_GID, numeric, "OWNER GID match ");
+ print_item(info, IPT_OWNER_PID, numeric, "OWNER PID match ");
+ print_item(info, IPT_OWNER_SID, numeric, "OWNER SID match ");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ struct ipt_owner_info *info = (struct ipt_owner_info *)match->data;
+
+ print_item(info, IPT_OWNER_UID, 0, "--uid-owner ");
+ print_item(info, IPT_OWNER_GID, 0, "--gid-owner ");
+ print_item(info, IPT_OWNER_PID, 0, "--pid-owner ");
+ print_item(info, IPT_OWNER_SID, 0, "--sid-owner ");
+}
+
+struct iptables_match owner
+= { NULL,
+ "owner",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_owner_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&owner);
+}
diff --git a/extensions/libipt_standard.c b/extensions/libipt_standard.c
new file mode 100644
index 00000000..9a746b2d
--- /dev/null
+++ b/extensions/libipt_standard.c
@@ -0,0 +1,67 @@
+/* Shared library add-on to iptables for standard target support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <getopt.h>
+#include <iptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"Standard v%s options:\n"
+"(If target is DROP, ACCEPT, RETURN or nothing)\n", NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ {0}
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ struct ipt_entry_target **target)
+{
+ return 0;
+}
+
+/* Final check; don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+}
+
+struct iptables_target standard
+= { NULL,
+ "standard",
+ NETFILTER_VERSION,
+ sizeof(int),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ NULL, /* print */
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_target(&standard);
+}
diff --git a/extensions/libipt_state.c b/extensions/libipt_state.c
new file mode 100644
index 00000000..19751d72
--- /dev/null
+++ b/extensions/libipt_state.c
@@ -0,0 +1,162 @@
+/* Shared library add-on to iptables to add state tracking support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_state.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"state v%s options:\n"
+" [!] --state [INVALID|ESTABLISHED|NEW|RELATED][,...]\n"
+" State(s) to match\n"
+"\n", NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "state", 1, 0, '1' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ /* Can't cache this */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+static int
+parse_state(const char *state, size_t strlen, struct ipt_state_info *sinfo)
+{
+ if (strncasecmp(state, "INVALID", strlen) == 0)
+ sinfo->statemask |= IPT_STATE_INVALID;
+ else if (strncasecmp(state, "NEW", strlen) == 0)
+ sinfo->statemask |= IPT_STATE_BIT(IP_CT_NEW);
+ else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+ sinfo->statemask |= IPT_STATE_BIT(IP_CT_ESTABLISHED);
+ else if (strncasecmp(state, "RELATED", strlen) == 0)
+ sinfo->statemask |= IPT_STATE_BIT(IP_CT_RELATED);
+ else
+ return 0;
+ return 1;
+}
+
+static void
+parse_states(const char *arg, struct ipt_state_info *sinfo)
+{
+ const char *comma;
+
+ while ((comma = strchr(arg, ',')) != NULL) {
+ if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+ exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+ arg = comma+1;
+ }
+
+ if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+ exit_error(PARAMETER_PROBLEM, "Bad state `%s'", arg);
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_state_info *sinfo = (struct ipt_state_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+
+ parse_states(argv[optind-1], sinfo);
+ if (invert)
+ sinfo->statemask = ~sinfo->statemask;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; must have specified --state. */
+static void final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM, "You must specify `--state'");
+}
+
+static void print_state(unsigned int statemask)
+{
+ const char *sep = "";
+
+ if (statemask & IPT_STATE_INVALID) {
+ printf("%sINVALID", sep);
+ sep = ",";
+ }
+ if (statemask & IPT_STATE_BIT(IP_CT_NEW)) {
+ printf("%sNEW", sep);
+ sep = ",";
+ }
+ if (statemask & IPT_STATE_BIT(IP_CT_RELATED)) {
+ printf("%sRELATED", sep);
+ sep = ",";
+ }
+ if (statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) {
+ printf("%sESTABLISHED", sep);
+ sep = ",";
+ }
+ printf(" ");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+ printf("state ");
+ print_state(sinfo->statemask);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ struct ipt_state_info *sinfo = (struct ipt_state_info *)match->data;
+
+ printf("--state ");
+ print_state(sinfo->statemask);
+}
+
+struct iptables_match state
+= { NULL,
+ "state",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_state_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&state);
+}
diff --git a/extensions/libipt_tcp.c b/extensions/libipt_tcp.c
new file mode 100644
index 00000000..94285a09
--- /dev/null
+++ b/extensions/libipt_tcp.c
@@ -0,0 +1,439 @@
+/* Shared library add-on to iptables to add TCP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"TCP v%s options:\n"
+" --tcp-flags [!] mask comp match when TCP flags & mask == comp\n"
+" (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
+"[!] --syn match when only SYN flag set\n"
+" (equivalent to --tcp-flags SYN,RST,ACK SYN)\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+" match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+" match destination port(s)\n"
+" --tcp-option [!] number match if TCP option set\n\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "source-port", 1, 0, '1' },
+ { "sport", 1, 0, '1' }, /* synonym */
+ { "destination-port", 1, 0, '2' },
+ { "dport", 1, 0, '2' }, /* synonym */
+ { "syn", 0, 0, '3' },
+ { "tcp-flags", 1, 0, '4' },
+ { "tcp-option", 1, 0, '5' },
+ {0}
+};
+
+static int
+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 = service_to_port(port)) != -1)
+ return (u_int16_t)portnum;
+
+ exit_error(PARAMETER_PROBLEM,
+ "invalid TCP port/service `%s' specified", port);
+}
+
+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);
+}
+
+struct tcp_flag_names {
+ const char *name;
+ unsigned int flag;
+};
+
+static struct tcp_flag_names tcp_flag_names[]
+= { { "FIN", 0x01 },
+ { "SYN", 0x02 },
+ { "RST", 0x04 },
+ { "PSH", 0x08 },
+ { "ACK", 0x10 },
+ { "URG", 0x20 },
+ { "ALL", 0x3F },
+ { "NONE", 0 },
+};
+
+static unsigned int
+parse_tcp_flag(const char *flags)
+{
+ unsigned int ret = 0;
+ char *ptr;
+ char *buffer;
+
+ buffer = strdup(flags);
+
+ for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
+ unsigned int i;
+ for (i = 0;
+ i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
+ i++) {
+ if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
+ ret |= tcp_flag_names[i].flag;
+ break;
+ }
+ }
+ if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
+ exit_error(PARAMETER_PROBLEM,
+ "Unknown TCP flag `%s'", buffer);
+ }
+
+ free(buffer);
+ return ret;
+}
+
+static void
+parse_tcp_flags(struct ipt_tcp *tcpinfo,
+ const char *mask,
+ const char *cmp,
+ int invert)
+{
+ tcpinfo->flg_mask = parse_tcp_flag(mask);
+ tcpinfo->flg_cmp = parse_tcp_flag(cmp);
+
+ if (invert)
+ tcpinfo->invflags |= IPT_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, u_int8_t *result)
+{
+ int ret;
+
+ ret = string_to_number(option, 1, 255);
+ if (ret == -1)
+ exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
+
+ *result = (u_int8_t)ret;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ struct ipt_tcp *tcpinfo = (struct ipt_tcp *)m->data;
+
+ tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
+}
+
+#define TCP_SRC_PORTS 0x01
+#define TCP_DST_PORTS 0x02
+#define TCP_FLAGS 0x04
+#define TCP_OPTION 0x08
+
+/* Function which parses command options; returns true if it
+ ate an option. */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_tcp *tcpinfo = (struct ipt_tcp *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (*flags & TCP_SRC_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--source-port' allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_tcp_ports(argv[optind-1], tcpinfo->spts);
+ if (invert)
+ tcpinfo->invflags |= IPT_TCP_INV_SRCPT;
+ *flags |= TCP_SRC_PORTS;
+ *nfcache |= NFC_IP_SRC_PT;
+ break;
+
+ case '2':
+ if (*flags & TCP_DST_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--destination-port' allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
+ if (invert)
+ tcpinfo->invflags |= IPT_TCP_INV_DSTPT;
+ *flags |= TCP_DST_PORTS;
+ *nfcache |= NFC_IP_DST_PT;
+ break;
+
+ case '3':
+ if (*flags & TCP_FLAGS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one of `--syn' or `--tcp-flags' "
+ " allowed");
+ parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert);
+ *flags |= TCP_FLAGS;
+ *nfcache |= NFC_IP_TCPFLAGS;
+ break;
+
+ case '4':
+ if (*flags & TCP_FLAGS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one of `--syn' or `--tcp-flags' "
+ " allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+
+ if (!argv[optind]
+ || argv[optind][0] == '-' || argv[optind][0] == '!')
+ exit_error(PARAMETER_PROBLEM,
+ "--tcp-flags requires two args.");
+
+ parse_tcp_flags(tcpinfo, optarg, argv[optind++], invert);
+ *flags |= TCP_FLAGS;
+ *nfcache |= NFC_IP_TCPFLAGS;
+ break;
+
+ case '5':
+ if (*flags & TCP_OPTION)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--tcp-option' allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_tcp_option(argv[optind-1], &tcpinfo->option);
+ if (invert)
+ tcpinfo->invflags |= IPT_TCP_INV_OPTION;
+ *flags |= TCP_OPTION;
+ *nfcache |= NFC_IP_PROTO_UNKNOWN;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+ struct servent *service;
+
+ if ((service = getservbyport(htons(port), "tcp")))
+ return service->s_name;
+
+ return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+ char *service;
+
+ if (numeric || (service = port_to_service(port)) == NULL)
+ printf("%u", port);
+ else
+ printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+ int invert, int numeric)
+{
+ const char *inv = invert ? "!" : "";
+
+ if (min != 0 || max != 0xFFFF || invert) {
+ printf("%s", name);
+ if (min == max) {
+ printf(":%s", inv);
+ print_port(min, numeric);
+ } else {
+ printf("s:%s", inv);
+ print_port(min, numeric);
+ printf(":");
+ print_port(max, numeric);
+ }
+ printf(" ");
+ }
+}
+
+static void
+print_option(u_int8_t option, int invert, int numeric)
+{
+ if (option || invert)
+ printf("option=%s%u ", invert ? "!" : "", option);
+}
+
+static void
+print_tcpf(u_int8_t flags)
+{
+ int sole_flag = 1;
+
+ do {
+ unsigned int i;
+
+ /* Terminates because last flag is 0 */
+ for (i = 0; !(flags & tcp_flag_names[i].flag); i++);
+
+ if (!sole_flag)
+ printf(",");
+ printf("%s", tcp_flag_names[i].name);
+ sole_flag = 0;
+
+ flags &= ~tcp_flag_names[i].flag;
+ } while (flags);
+}
+
+static void
+print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
+{
+ if (mask || invert) {
+ printf("flags:%s", invert ? "!" : "");
+ if (numeric)
+ printf("0x02%X/0x02%X ", mask, cmp);
+ else {
+ print_tcpf(cmp);
+ printf("/");
+ print_tcpf(mask);
+ printf(" ");
+ }
+ }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match, int numeric)
+{
+ const struct ipt_tcp *tcp = (struct ipt_tcp *)match->data;
+
+ printf("tcp ");
+ print_ports("spt", tcp->spts[0], tcp->spts[1],
+ tcp->invflags & IPT_TCP_INV_SRCPT,
+ numeric);
+ print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+ tcp->invflags & IPT_TCP_INV_DSTPT,
+ numeric);
+ print_option(tcp->option,
+ tcp->invflags & IPT_TCP_INV_OPTION,
+ numeric);
+ print_flags(tcp->flg_mask, tcp->flg_cmp,
+ tcp->invflags & IPT_TCP_INV_FLAGS,
+ numeric);
+ if (tcp->invflags & ~IPT_TCP_INV_MASK)
+ printf("Unknown invflags: 0x%X ",
+ tcp->invflags & ~IPT_TCP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct ipt_tcp *tcpinfo = (struct ipt_tcp *)match->data;
+
+ if (tcpinfo->spts[0] != 0
+ && tcpinfo->spts[1] != 0xFFFF) {
+ if (tcpinfo->invflags & IPT_TCP_INV_SRCPT)
+ printf("! ");
+ if (tcpinfo->spts[0]
+ != tcpinfo->spts[1])
+ printf("--sport %u-%u ",
+ ntohs(tcpinfo->spts[0]),
+ ntohs(tcpinfo->spts[1]));
+ else
+ printf("--sport %u ",
+ ntohs(tcpinfo->spts[0]));
+ }
+
+ if (tcpinfo->dpts[0] != 0
+ && tcpinfo->dpts[1] != 0xFFFF) {
+ if (tcpinfo->invflags & IPT_TCP_INV_DSTPT)
+ printf("! ");
+ if (tcpinfo->dpts[0]
+ != tcpinfo->dpts[1])
+ printf("--dport %u-%u ",
+ ntohs(tcpinfo->dpts[0]),
+ ntohs(tcpinfo->dpts[1]));
+ else
+ printf("--dport %u ",
+ ntohs(tcpinfo->dpts[0]));
+ }
+
+ if (tcpinfo->option
+ || (tcpinfo->invflags & IPT_TCP_INV_OPTION)) {
+ if (tcpinfo->invflags & IPT_TCP_INV_OPTION)
+ printf("! ");
+ printf("--tcp-option %u ", tcpinfo->option);
+ }
+
+ if (tcpinfo->flg_mask
+ || (tcpinfo->invflags & IPT_TCP_INV_FLAGS)) {
+ if (tcpinfo->invflags & IPT_TCP_INV_FLAGS)
+ printf("! ");
+
+ print_tcpf(tcpinfo->flg_cmp);
+ if (tcpinfo->flg_mask != 0xFF) {
+ printf("/");
+ print_tcpf(tcpinfo->flg_mask);
+ }
+ }
+}
+
+struct iptables_match tcp
+= { NULL,
+ "tcp",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_tcp),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts };
+
+void
+_init(void)
+{
+ register_match(&tcp);
+}
diff --git a/extensions/libipt_tos.c b/extensions/libipt_tos.c
new file mode 100644
index 00000000..eb62081e
--- /dev/null
+++ b/extensions/libipt_tos.c
@@ -0,0 +1,171 @@
+/* Shared library add-on to iptables to add TOS matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_tos.h>
+
+/* TOS names and values. */
+struct TOS_value
+{
+ unsigned char TOS;
+ const char *name;
+} TOS_values[] = {
+ { IPTOS_LOWDELAY, "Minimize-Delay" },
+ { IPTOS_THROUGHPUT, "Maximize-Throughput" },
+ { IPTOS_RELIABILITY, "Maximize-Reliability" },
+ { IPTOS_MINCOST, "Minimize-Cost" },
+ { IPTOS_NORMALSVC, "Normal-Service" },
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ unsigned int i;
+
+ printf(
+"TOS match v%s options:\n"
+"[!] --tos value Match Type of Service field from one of the\n"
+" following numeric or descriptive values:\n",
+NETFILTER_VERSION);
+
+ for (i = 0; i < sizeof(TOS_values)/sizeof(struct TOS_value);i++)
+ printf(" %s %u (0x%02x)\n",
+ TOS_values[i].name,
+ TOS_values[i].TOS,
+ TOS_values[i].TOS);
+ fputc('\n', stdout);
+}
+
+static struct option opts[] = {
+ { "tos", 1, 0, '1' },
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ *nfcache |= NFC_IP_TOS;
+}
+
+static void
+parse_tos(const unsigned char *s, struct ipt_tos_info *info)
+{
+ unsigned int i;
+ int tos = string_to_number(s, 0, 255);
+
+ if (tos != -1) {
+ if (tos == IPTOS_LOWDELAY
+ || tos == IPTOS_THROUGHPUT
+ || tos == IPTOS_RELIABILITY
+ || tos == IPTOS_MINCOST
+ || tos == IPTOS_NORMALSVC) {
+ info->tos = (u_int8_t )tos;
+ return;
+ }
+ } else {
+ for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+ if (strcasecmp(s,TOS_values[i].name) == 0) {
+ info->tos = TOS_values[i].TOS;
+ return;
+ }
+ }
+ exit_error(PARAMETER_PROBLEM, "Bad TOS value `%s'", s);
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_tos_info *tosinfo = (struct ipt_tos_info *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_tos(argv[optind-1], tosinfo);
+ if (invert)
+ tosinfo->invert = 1;
+ *flags = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+print_tos(u_int8_t tos, int invert, int numeric)
+{
+ unsigned int i;
+
+ if (invert)
+ fputc('!', stdout);
+
+ if (!numeric) {
+ for (i = 0; i<sizeof(TOS_values)/sizeof(struct TOS_value); i++)
+ if (TOS_values[i].TOS == tos) {
+ printf("%s ", TOS_values[i].name);
+ return;
+ }
+ }
+ printf("0x%02x ", tos);
+}
+
+/* Final check; must have specified --tos. */
+static void
+final_check(unsigned int flags)
+{
+ if (!flags)
+ exit_error(PARAMETER_PROBLEM,
+ "TOS match: You must specify `--tos'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match,
+ int numeric)
+{
+ printf("TOS match ");
+ print_tos(((struct ipt_tos_info *)match->data)->tos,
+ ((struct ipt_tos_info *)match->data)->invert, numeric);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ printf("--tos ");
+ print_tos(((struct ipt_tos_info *)match->data)->tos,
+ ((struct ipt_tos_info *)match->data)->invert, 0);
+}
+
+struct iptables_match tos
+= { NULL,
+ "tos",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_tos_info),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&tos);
+}
diff --git a/extensions/libipt_udp.c b/extensions/libipt_udp.c
new file mode 100644
index 00000000..e3593579
--- /dev/null
+++ b/extensions/libipt_udp.c
@@ -0,0 +1,252 @@
+/* Shared library add-on to iptables to add UDP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"UDP v%s options:\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+" match source port(s)\n"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n"
+" match destination port(s)\n",
+NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ { "source-port", 1, 0, '1' },
+ { "sport", 1, 0, '1' }, /* synonym */
+ { "destination-port", 1, 0, '2' },
+ { "dport", 1, 0, '2' }, /* synonym */
+ {0}
+};
+
+static int
+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 = service_to_port(port)) != -1)
+ return (u_int16_t)portnum;
+
+ exit_error(PARAMETER_PROBLEM,
+ "invalid UDP port/service `%s' specified", port);
+ }
+
+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);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ struct ipt_udp *udpinfo = (struct ipt_udp *)m->data;
+
+ udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
+}
+
+#define UDP_SRC_PORTS 0x01
+#define UDP_DST_PORTS 0x02
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ struct ipt_udp *udpinfo = (struct ipt_udp *)(*match)->data;
+
+ switch (c) {
+ case '1':
+ if (*flags & UDP_SRC_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--source-port' allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_udp_ports(argv[optind-1], udpinfo->spts);
+ if (invert)
+ udpinfo->invflags |= IPT_UDP_INV_SRCPT;
+ *flags |= UDP_SRC_PORTS;
+ *nfcache |= NFC_IP_SRC_PT;
+ break;
+
+ case '2':
+ if (*flags & UDP_DST_PORTS)
+ exit_error(PARAMETER_PROBLEM,
+ "Only one `--destination-port' allowed");
+ if (check_inverse(optarg, &invert))
+ optind++;
+ parse_udp_ports(argv[optind-1], udpinfo->dpts);
+ if (invert)
+ udpinfo->invflags |= IPT_UDP_INV_DSTPT;
+ *flags |= UDP_DST_PORTS;
+ *nfcache |= NFC_IP_DST_PT;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+ struct servent *service;
+
+ if ((service = getservbyport(htons(port), "udp")))
+ return service->s_name;
+
+ return NULL;
+}
+
+static void
+print_port(u_int16_t port, int numeric)
+{
+ char *service;
+
+ if (numeric || (service = port_to_service(port)) == NULL)
+ printf("%u", port);
+ else
+ printf("%s", service);
+}
+
+static void
+print_ports(const char *name, u_int16_t min, u_int16_t max,
+ int invert, int numeric)
+{
+ const char *inv = invert ? "!" : "";
+
+ if (min != 0 || max != 0xFFFF || invert) {
+ printf("%s", name);
+ if (min == max) {
+ printf(":%s", inv);
+ print_port(min, numeric);
+ } else {
+ printf("s:%s", inv);
+ print_port(min, numeric);
+ printf(":");
+ print_port(max, numeric);
+ }
+ printf(" ");
+ }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+ const struct ipt_entry_match *match, int numeric)
+{
+ const struct ipt_udp *udp = (struct ipt_udp *)match->data;
+
+ printf("udp ");
+ print_ports("spt", udp->spts[0], udp->spts[1],
+ udp->invflags & IPT_UDP_INV_SRCPT,
+ numeric);
+ print_ports("dpt", udp->dpts[0], udp->dpts[1],
+ udp->invflags & IPT_UDP_INV_DSTPT,
+ numeric);
+ if (udp->invflags & ~IPT_UDP_INV_MASK)
+ printf("Unknown invflags: 0x%X ",
+ udp->invflags & ~IPT_UDP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+ const struct ipt_udp *udpinfo = (struct ipt_udp *)match->data;
+
+ if (udpinfo->spts[0] != 0
+ && udpinfo->spts[1] != 0xFFFF) {
+ if (udpinfo->invflags & IPT_UDP_INV_SRCPT)
+ printf("! ");
+ if (udpinfo->spts[0]
+ != udpinfo->spts[1])
+ printf("--sport %u-%u ",
+ ntohs(udpinfo->spts[0]),
+ ntohs(udpinfo->spts[1]));
+ else
+ printf("--sport %u ",
+ ntohs(udpinfo->spts[0]));
+ }
+
+ if (udpinfo->dpts[0] != 0
+ && udpinfo->dpts[1] != 0xFFFF) {
+ if (udpinfo->invflags & IPT_UDP_INV_DSTPT)
+ printf("! ");
+ if (udpinfo->dpts[0]
+ != udpinfo->dpts[1])
+ printf("--dport %u-%u ",
+ ntohs(udpinfo->dpts[0]),
+ ntohs(udpinfo->dpts[1]));
+ else
+ printf("--dport %u ",
+ ntohs(udpinfo->dpts[0]));
+ }
+}
+
+struct iptables_match udp
+= { NULL,
+ "udp",
+ NETFILTER_VERSION,
+ sizeof(struct ipt_udp),
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ &print,
+ &save,
+ opts
+};
+
+void
+_init(void)
+{
+ register_match(&udp);
+}
diff --git a/extensions/libipt_unclean.c b/extensions/libipt_unclean.c
new file mode 100644
index 00000000..50f62bdc
--- /dev/null
+++ b/extensions/libipt_unclean.c
@@ -0,0 +1,66 @@
+/* Shared library add-on to iptables for unclean. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+ printf(
+"unclean v%s takes no options\n"
+"\n", NETFILTER_VERSION);
+}
+
+static struct option opts[] = {
+ {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+ /* Can't cache this. */
+ *nfcache |= NFC_UNKNOWN;
+}
+
+/* Function which parses command options; returns true if it
+ ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+ const struct ipt_entry *entry,
+ unsigned int *nfcache,
+ struct ipt_entry_match **match)
+{
+ return 0;
+}
+
+/* Final check; must have specified --mac. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+}
+
+struct iptables_match unclean
+= { NULL,
+ "unclean",
+ NETFILTER_VERSION,
+ 0,
+ &help,
+ &init,
+ &parse,
+ &final_check,
+ NULL, /* print */
+ &save,
+ opts
+};
+
+void _init(void)
+{
+ register_match(&unclean);
+}
diff --git a/include/iptables.h b/include/iptables.h
new file mode 100644
index 00000000..9b1a4a16
--- /dev/null
+++ b/include/iptables.h
@@ -0,0 +1,122 @@
+#ifndef _IPTABLES_USER_H
+#define _IPTABLES_USER_H
+
+#include "libiptc/libiptc.h"
+
+/* 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;
+
+ /* 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 union ipt_matchinfo 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 */
+ struct option *extra_opts;
+
+ /* Ignore these men behind the curtain: */
+ unsigned int option_offset;
+ struct ipt_entry_match *m;
+ unsigned int mflags;
+};
+
+struct iptables_target
+{
+ struct iptables_target *next;
+
+ ipt_chainlabel name;
+
+ const char *version;
+
+ /* Size of target data. */
+ size_t size;
+
+ /* 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;
+};
+
+/* Your shared library should call one of these. */
+extern void register_match(struct iptables_match *me);
+extern void register_target(struct iptables_target *me);
+
+/* Functions we share */
+enum exittype {
+ OTHER_PROBLEM = 1,
+ PARAMETER_PROBLEM,
+ VERSION_PROBLEM
+};
+extern void exit_printhelp() __attribute__((noreturn));
+extern void exit_tryhelp(int) __attribute__((noreturn));
+int check_inverse(const char option[], int *invert);
+extern int string_to_number(const char *, int, int);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+ format(printf,2,3)));
+extern char *addr_to_dotted(const struct in_addr *addrp);
+struct in_addr *dotted_to_addr(const char *dotted);
+extern const char *program_name, *program_version;
+
+extern int do_command(int argc, char *argv[], char **table,
+ iptc_handle_t *handle);
+/* Keeping track of external matches and targets: linked lists. */
+extern struct iptables_match *iptables_matches;
+extern struct iptables_target *iptables_targets;
+
+extern struct iptables_target *find_target(const char *name, int tryload);
+extern struct iptables_match *find_match(const char *name, int tryload);
+#endif /*_IPTABLES_USER_H*/
diff --git a/include/libipq/libipq.h b/include/libipq/libipq.h
new file mode 100644
index 00000000..bc51ec72
--- /dev/null
+++ b/include/libipq/libipq.h
@@ -0,0 +1,77 @@
+/*
+ * libipq.h
+ *
+ * IPQ library for userspace.
+ *
+ * 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.
+ *
+ */
+#ifndef _LIBIPQ_H
+#define _LIBIPQ_H
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+
+#include <linux/netfilter_ipv4/ip_queue.h>
+
+#ifdef DEBUG_LIBIPQ
+#include <stdio.h>
+#define LDEBUG(x...) fprintf(stderr, ## x)
+#else
+#define LDEBUG(x...)
+#endif /* DEBUG_LIBIPQ */
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC
+#define MSG_TRUNC 0x20
+#endif
+
+struct ipq_handle
+{
+ int fd;
+ u_int8_t blocking;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+};
+
+struct ipq_handle *ipq_create_handle(u_int32_t flags);
+
+int ipq_destroy_handle(struct ipq_handle *h);
+
+ssize_t ipq_read(const struct ipq_handle *h,
+ unsigned char *buf, size_t len, int timeout);
+
+int ipq_set_mode(const struct ipq_handle *h, u_int8_t mode, size_t len);
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf);
+
+int ipq_message_type(const unsigned char *buf);
+
+int ipq_get_msgerr(const unsigned char *buf);
+
+int ipq_set_verdict(const struct ipq_handle *h,
+ unsigned long id,
+ unsigned int verdict,
+ size_t data_len,
+ unsigned char *buf);
+
+int ipq_ctl(const struct ipq_handle *h, int request, ...);
+
+void ipq_perror(const char *s);
+
+#endif /* _LIBIPQ_H */
+
diff --git a/include/libiptc/ipt_kernel_headers.h b/include/libiptc/ipt_kernel_headers.h
new file mode 100644
index 00000000..0fd848e9
--- /dev/null
+++ b/include/libiptc/ipt_kernel_headers.h
@@ -0,0 +1,22 @@
+/* This is the userspace/kernel interface for Generic IP Chains,
+ required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <net/if.h>
+#include <sys/types.h>
+#else /* libc5 */
+#include <sys/socket.h>
+#include <linux/ip.h>
+#include <linux/in.h>
+#include <linux/if.h>
+#include <linux/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#endif
+#endif
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
new file mode 100644
index 00000000..4a964e03
--- /dev/null
+++ b/include/libiptc/libiptc.h
@@ -0,0 +1,131 @@
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifndef IPT_MIN_ALIGN
+#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry_match))
+#endif
+#define IPT_ALIGN(s) (((s) + (IPT_MIN_ALIGN-1)) & ~(IPT_MIN_ALIGN-1))
+
+typedef char ipt_chainlabel[32];
+
+#define IPTC_LABEL_ACCEPT "ACCEPT"
+#define IPTC_LABEL_DROP "DROP"
+#define IPTC_LABEL_QUEUE "QUEUE"
+#define IPTC_LABEL_RETURN "RETURN"
+
+/* Transparent handle type. */
+typedef struct iptc_handle *iptc_handle_t;
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, const iptc_handle_t handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+iptc_handle_t iptc_init(const char *tablename);
+
+/* Iterator functions to run through the chains; prev = NULL means
+ first chain. Returns NULL at end. */
+const char *iptc_next_chain(const char *prev, iptc_handle_t *handle);
+
+/* How many rules in this chain? */
+unsigned int iptc_num_rules(const char *chain, iptc_handle_t *handle);
+
+/* Get n'th rule in this chain. */
+const struct ipt_entry *iptc_get_rule(const char *chain,
+ unsigned int n,
+ iptc_handle_t *handle);
+
+/* Returns a pointer to the target name of this position. */
+const char *iptc_get_target(const char *chain,
+ unsigned int n,
+ iptc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int iptc_builtin(const char *chain, const iptc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *iptc_get_policy(const char *chain,
+ struct ipt_counters *counter,
+ iptc_handle_t *handle);
+
+/* These functions return TRUE for OK or 0 and set errno. If errno ==
+ 0, it means there was a version error (ie. upgrade libiptc). */
+/* Rule numbers start at 1 for the first rule. */
+
+/* Insert the entry `e' in chain `chain' into position `rulenum'. */
+int iptc_insert_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `e'. */
+int iptc_replace_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Append entry `e' to chain `chain'. Equivalent to insert with
+ rulenum = length of chain. */
+int iptc_append_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ iptc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `e'. */
+int iptc_delete_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *origfw,
+ iptc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int iptc_delete_num_entry(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ iptc_handle_t *handle);
+
+/* Check the packet `e' on chain `chain'. Returns the verdict, or
+ NULL and sets errno. */
+const char *iptc_check_packet(const ipt_chainlabel chain,
+ struct ipt_entry *entry,
+ iptc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int iptc_flush_entries(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int iptc_zero_entries(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Creates a new chain. */
+int iptc_create_chain(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Deletes a chain. */
+int iptc_delete_chain(const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Renames a chain. */
+int iptc_rename_chain(const ipt_chainlabel oldname,
+ const ipt_chainlabel newname,
+ iptc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int iptc_set_policy(const ipt_chainlabel chain,
+ const ipt_chainlabel policy,
+ iptc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int iptc_get_references(unsigned int *ref,
+ const ipt_chainlabel chain,
+ iptc_handle_t *handle);
+
+/* Makes the actual changes. */
+int iptc_commit(iptc_handle_t *handle);
+
+/* Get raw socket. */
+int iptc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *iptc_strerror(int err);
+#endif /* _LIBIPTC_H */
diff --git a/iptables-restore.c b/iptables-restore.c
new file mode 100644
index 00000000..9b4ece29
--- /dev/null
+++ b/iptables-restore.c
@@ -0,0 +1,154 @@
+/* Code to restore the iptables state, from file by iptables-save. */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include "packet-filter/userspace/iptables.h"
+#include "packet-filter/userspace/libiptc/libiptc.h"
+
+/* Keeping track of external matches and targets. */
+static struct option options[] = {
+ { "binary", 1, 0, 'b' },
+ { "counters", 1, 0, 'c' },
+ { "verbose", 1, 0, 'v' },
+ { "help", 1, 0, 'h' },
+ { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+ fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n", name);
+ exit(1);
+}
+
+static int clean_slate(iptc_handle_t *handle)
+{
+ /* Skip over builtins. */
+ const char *i, *last = IPTC_LABEL_OUTPUT;
+
+ /* Be careful iterating: it isn't safe during delete. */
+ /* Re-iterate after each delete successful */
+ while ((i = iptc_next_chain(last, handle)) != NULL) {
+ if (!iptc_flush_entries(i, handle)
+ || !iptc_delete_chain(i, handle))
+ return 0;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ iptc_handle_t handle;
+ char buffer[10240];
+ int counters = 0, binary = 0, verbose = 0;
+ unsigned int line = 0;
+ int c;
+ const char *chain;
+ FILE *in;
+
+ program_name = "iptables-restore";
+ program_version = NETFILTER_VERSION;
+
+ /* Don't use getopt here; it would interfere 8-(. */
+ if (optind == argc - 1) {
+ in = fopen(argv[optind], "r");
+ if (!in) {
+ fprintf(stderr, "Can't open %s: %s", argv[optind],
+ strerror(errno));
+ exit(1);
+ }
+ }
+ else if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline");
+ exit(1);
+ }
+ else in = stdin;
+
+ handle = iptc_init();
+ if (!handle)
+ exit_error(VERSION_PROBLEM,
+ "can't initialize iptables-restore: %s",
+ iptc_strerror(errno));
+
+ if (!clean_slate(&handle))
+ exit_error(OTHER_PROBLEM, "Deleting old chains: %s",
+ iptc_strerror(errno));
+
+ /* Grab standard input. */
+ while (fgets(buffer, sizeof(buffer), in)) {
+ int ret;
+
+ line++;
+ if (buffer[0] == '\n') continue;
+ else if (buffer[0] == '#') {
+ if (verbose) fputs(buffer, stdout);
+ continue;
+ } else if (strcmp(buffer, "COMMIT\n") == 0)
+ ret = iptc_commit(&handle);
+ else if (buffer[0] == ':') {
+ /* New chain. */
+ char *chain, *policy;
+
+ /* FIXME: Don't ignore counters. */
+ chain = strtok(buffer+1, " \t\n");
+ if (!chain) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u chain name invalid\n",
+ program_name, line);
+ exit(1);
+ }
+ policy = strtok(NULL, " \t\n");
+ if (!policy) {
+ exit_error(PARAMETER_PROBLEM,
+ "%s: line %u policy invalid\n",
+ program_name, line);
+ exit(1);
+ }
+ if (strcmp(policy, "-") != 0
+ && !iptc_set_policy(chain, policy, &handle))
+ exit_error(OTHER_PROBLEM,
+ "Can't set policy `%s'"
+ " on `%s' line %u: %s\n",
+ chain, policy, line,
+ iptc_strerror(errno));
+ } else {
+ char *newargv[1024];
+ int i;
+ char *ptr = buffer;
+
+ /* FIXME: Don't ignore counters. */
+ if (buffer[0] == '[') {
+ ptr = strchr(buffer, ']');
+ if (!ptr)
+ exit_error(PARAMETER_PROBLEM,
+ "Bad line %u: need ]\n",
+ line);
+ }
+
+ /* strtok: a function only a coder could love */
+ newargv[0] = argv[0];
+ for (i = 1; i < sizeof(newargv)/sizeof(char *); i++) {
+ if (!(newargv[i] = strtok(ptr, " \t\n")))
+ break;
+ ptr = NULL;
+ }
+ if (i == sizeof(newargv)/sizeof(char *)) {
+ fprintf(stderr,
+ "%s: line %u too many arguments\n",
+ program_name, line);
+ exit(1);
+ }
+
+ ret = do_command(i, newargv, &handle);
+ }
+ if (!ret) {
+ fprintf(stderr, "%s: line %u failed\n",
+ program_name, line);
+ exit(1);
+ }
+ }
+
+ return 0;
+}
diff --git a/iptables-save.c b/iptables-save.c
new file mode 100644
index 00000000..40e9d6a5
--- /dev/null
+++ b/iptables-save.c
@@ -0,0 +1,260 @@
+/* Code to save the iptables state, in human readable-form. */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <time.h>
+#include "packet-match/userspace/libiptc/libiptc.h"
+#include "packet-match/userspace/iptables.h"
+
+/* Keeping track of external matches and targets. */
+static struct option options[] = {
+ { "binary", 1, 0, 'b' },
+ { "counters", 1, 0, 'c' },
+ { "dump", 1, 0, 'd' },
+ { 0 }
+};
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+/* This assumes that mask is contiguous, and byte-bounded. */
+static void
+print_iface(char letter, const char *iface, const unsigned char *mask,
+ int invert)
+{
+ unsigned int i;
+
+ if (mask[0] == 0)
+ return;
+
+ printf("-%c %s", letter, invert ? "! " : "");
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (mask[i] != 0) {
+ if (iface[i] != '\0')
+ printf("%c", iface[i]);
+ } else {
+ if (iface[i] != '\0')
+ printf("+");
+ break;
+ }
+ }
+}
+
+/* These are hardcoded backups in iptables.c, so they are safe */
+struct pprot {
+ char *name;
+ u_int8_t num;
+};
+
+static const struct pprot chain_protos[] = {
+ { "tcp", IPPROTO_TCP },
+ { "udp", IPPROTO_UDP },
+ { "icmp", IPPROTO_ICMP },
+};
+
+static void print_proto(u_int16_t proto, int invert)
+{
+ if (proto) {
+ unsigned int i;
+ const char *invertstr = invert ? "! " : "";
+
+ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+ if (chain_protos[i].num == proto) {
+ printf("-p %s%s ",
+ invertstr, chain_protos[i].name);
+ return;
+ }
+
+ printf("-p %s%u ", invertstr, proto);
+ }
+}
+
+static int non_zero(const void *ptr, size_t size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++)
+ if (((char *)ptr)[i])
+ return 0;
+
+ return 1;
+}
+
+/* We want this to be readable, so only print out neccessary fields.
+ * Because that's the kind of world I want to live in. */
+static void print_rule(const struct ipt_entry *e, int counters)
+{
+ if (counters)
+ printf("[%llu,%llu] ", e->counters.pcnt, e->counters.bcnt);
+
+ /* Print IP part. */
+ if (e->ip.smsk.s_addr)
+ printf("-s %s%u.%u.%u.%u/%u.%u.%u.%u ",
+ e->ip.invflags & IPT_INV_SRCIP ? "! " : "",
+ IP_PARTS(e->ip.src.s_addr),
+ IP_PARTS(e->ip.smsk.s_addr));
+ if (e->ip.dmsk.s_addr)
+ printf("-d %s%u.%u.%u.%u/%u.%u.%u.%u ",
+ e->ip.invflags & IPT_INV_SRCIP ? "! " : "",
+ IP_PARTS(e->ip.dst.s_addr),
+ IP_PARTS(e->ip.dmsk.s_addr));
+
+ print_iface('i', e->ip.iniface, e->ip.iniface_mask,
+ e->ip.invflags & IPT_INV_VIA_IN);
+ print_iface('o', e->ip.outiface, e->ip.outiface_mask,
+ e->ip.invflags & IPT_INV_VIA_OUT);
+ print_proto(e->ip.proto, e->ip.invflags & IPT_INV_PROTO);
+
+ if (e->ip.flags & IPT_F_FRAG)
+ printf("%s-f ",
+ e->ip.invflags & IPT_INV_FRAG ? "! " : "");
+
+ if (e->ip.flags & IPT_F_TOS)
+ printf("-t %s0x%02X ",
+ e->ip.invflags & IPT_INV_TOS ? "! " : "",
+ e->ip.tos);
+
+ /* Print matchinfo part */
+ if (e->match_name[0]) {
+ struct iptables_match *match = find_match(e->match_name, 1);
+
+ if (match)
+ match->save(e);
+ else {
+ /* If some bits are non-zero, it implies we *need*
+ to understand it */
+ if (non_zero(&e->matchinfo, sizeof(e->matchinfo))) {
+ fprintf(stderr,
+ "Can't find library for match `%s'\n",
+ e->match_name);
+ exit(1);
+ }
+ }
+ }
+
+ /* Print targinfo part */
+ if (e->target_name[0]) {
+ struct iptables_target *target
+ = find_target(e->target_name, 1);
+
+ if (target)
+ target->save(e);
+ else {
+ /* If some bits are non-zero, it implies we *need*
+ to understand it */
+ if (non_zero(&e->targinfo, sizeof(e->targinfo))) {
+ fprintf(stderr,
+ "Can't find library for target `%s'\n",
+ e->target_name);
+ exit(1);
+ }
+ }
+ }
+ printf("\n");
+}
+
+/* Debugging prototype. */
+extern void dump_entries(iptc_handle_t handle);
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int main(int argc, char *argv[])
+{
+ iptc_handle_t h;
+ const char *chain = NULL;
+ int c;
+ int binary = 0, counters = 0;
+
+ program_name = "iptables-save";
+ program_version = NETFILTER_VERSION;
+
+ while ((c = getopt_long(argc, argv, "bc", options, NULL)) != -1) {
+ switch (c) {
+ case 'b':
+ binary = 1;
+ break;
+
+ case 'c':
+ counters = 1;
+ break;
+
+ case 'd':
+ /* Debugging dump. */
+ h = iptc_init();
+ if (!h)
+ exit_error(OTHER_PROBLEM, "iptc_init: %s\n",
+ iptc_strerror(errno));
+ dump_entries(h);
+ exit(0);
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr, "Unknown arguments found on commandline");
+ exit(1);
+ }
+
+ h = iptc_init();
+ if (!h) {
+ fprintf(stderr, "Can't initialize: %s\n",
+ iptc_strerror(errno));
+ exit(1);
+ }
+
+ if (!binary) {
+ time_t now = time(NULL);
+
+ printf("# Generated by iptables-save v%s on %s",
+ NETFILTER_VERSION, ctime(&now));
+
+ /* Dump out chain names */
+ while ((chain = iptc_next_chain(chain, &h)) != NULL) {
+ printf(":%s ", chain);
+ if (iptc_builtin(chain, &h)) {
+ struct ipt_counters count;
+ printf("%s ",
+ iptc_get_policy(chain, &count, &h));
+ printf("%llu %llu\n", count.pcnt, count.bcnt);
+ } else {
+ printf("- 0 0\n");
+ }
+ }
+
+ /* Dump out rules */
+ while ((chain = iptc_next_chain(chain, &h)) != NULL) {
+ unsigned int i;
+
+ for (i = 0; i < iptc_num_rules(chain, &h); i++) {
+ const struct ipt_entry *e
+ = iptc_get_rule(chain, i, &h);
+
+ if (!e) {
+ fprintf(stderr,
+ "Can't read rule %u of chain %s: %s\n",
+ i, chain, iptc_strerror(errno));
+ exit(1);
+ }
+ print_rule(e, counters);
+ }
+ }
+
+ now = time(NULL);
+ printf("COMMIT\n");
+ printf("# Completed on %s", ctime(&now));
+ } else {
+ /* Binary, huh? OK. */
+ fprintf(stderr, "Binary NYI\n");
+ exit(1);
+ }
+
+ return 0;
+}
diff --git a/iptables-standalone.c b/iptables-standalone.c
new file mode 100644
index 00000000..dfbc0fa3
--- /dev/null
+++ b/iptables-standalone.c
@@ -0,0 +1,50 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * iptables -- IP firewall administration for kernels with
+ * firewall table (aimed for the 2.3 kernels)
+ *
+ * See the accompanying manual page iptables(8) for information
+ * about proper usage of this program.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <iptables.h>
+
+int
+main(int argc, char *argv[])
+{
+ int ret;
+ char *table = "filter";
+ iptc_handle_t handle;
+
+ program_name = "iptables";
+ program_version = NETFILTER_VERSION;
+
+ ret = do_command(argc, argv, &table, &handle);
+ if (ret)
+ ret = iptc_commit(&handle);
+
+ if (!ret)
+ fprintf(stderr, "iptables: %s\n",
+ iptc_strerror(errno));
+
+ exit(!ret);
+}
diff --git a/iptables.8 b/iptables.8
new file mode 100644
index 00000000..a4e44a35
--- /dev/null
+++ b/iptables.8
@@ -0,0 +1,708 @@
+.TH IPTABLES 8 "Mar 20, 2000" "" ""
+.\"
+.\" Man page written by Herve Eychenne <eychenne@info.enserb.u-bordeaux.fr>
+.\" It is based on ipchains man page.
+.\"
+.\" ipchains page by Paul ``Rusty'' Russell March 1997
+.\" Based on the original ipfwadm man page by Jos Vos <jos@xos.nl> (see README)
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+iptables \- IP packet filter administration
+.SH SYNOPSIS
+.BR "iptables -[ADC] " "chain rule-specification [options]"
+.br
+.BR "iptables -[RI] " "chain rulenum rule-specification [options]"
+.br
+.BR "iptables -D " "chain rulenum [options]"
+.br
+.BR "iptables -[LFZ] " "[chain] [options]"
+.br
+.BR "iptables -[NX] " "chain"
+.br
+.BR "iptables -P " "chain target [options]"
+.br
+.BR "iptables -E " "old-chain-name new-chain-name"
+.SH DESCRIPTION
+.B Iptables
+is used to set up, maintain, and inspect the tables of IP packet
+filter rules in the Linux kernel. There are several different tables
+which may be defined, and each table contains a number of built-in
+chains, and may contain user-defined chains.
+
+Each chain is a list of rules which can match a set of packets: each
+rule specifies what to do with a packet which matches. This is called
+a `target', which may be a jump to a user-defined chain in the same
+table.
+
+.SH TARGETS
+A firewall rule specifies criteria for a packet, and a target. If the
+packet does not match, the next rule in the chain is the examined; if
+it does match, then the next rule is specified by the value of the
+target, which can be the name of a user-defined chain, or one of the
+special values
+.IR ACCEPT ,
+.IR DROP ,
+.IR QUEUE ,
+or
+.IR RETURN .
+.PP
+.I ACCEPT
+means to let the packet through.
+.I DROP
+means to drop the packet on the floor.
+.I QUEUE
+means to pass the packet to userspace.
+.I RETURN
+means stop traversing this chain, and resume at the next rule in the
+previous (calling) chain. If the end of a built-in chain is reached,
+or a rule in a built-in chain with target
+.I RETURN
+is matched, the target specified by the chain policy determines the
+fate of the packet.
+.SH TABLES
+There are current three tables (which tables are present at any time
+depends on the kernel configuration options and which modules are
+present).
+.TP
+.B "-t, --table"
+This option specifies the packet matching table which the command
+should operate on. If the kernel is configured with automatic module
+loading, an attempt will be made to load the appropriate module for
+that table if it is not already there.
+
+The tables are as follows:
+.BR "filter"
+This is the default table, and contains the built-in chains INPUT (for
+packets coming into the box itself), FORWARD (for packets being routed
+through the box), and OUTPUT (for locally-generated packets).
+.BR "nat"
+This table is consulted when a packet which is creates a new
+connection is encountered. It consists of three built-ins: PREROUTING
+(for altering packets as soon as they come in), OUTPUT (for altering
+locally-generated packets before routing), and POSTROUTING (for
+altering packets as they are about to go out).
+.BR "mangle"
+This table is used for specialized packet alteration. It has two
+built-in chains: PREROUTING (for altering incoming packets before
+routing) and OUTPUT (for altering locally-generated packets before
+routing).
+.SH OPTIONS
+The options that are recognized by
+.B iptables
+can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific action to perform; only one of them
+can be specified on the command line, unless otherwise specified
+below. For all the long versions of the command and option names, you
+only need to use enough letters to ensure that
+.B iptables
+can differentiate it from all other options.
+.TP
+.BR "-A, --append"
+Append one or more rules to the end of the selected chain.
+When the source and/or destination names resolve to more than one
+address, a rule will be added for each possible address combination.
+.TP
+.BR "-D, --delete"
+Delete one or more rules from the selected chain. There are two
+versions of this command: the rule can be specified as a number in the
+chain (starting at 1 for the first rule) or a rule to match.
+.TP
+.B "-R, --replace"
+Replace a rule in the selected chain. If the source and/or
+destination names resolve to multiple addresses, the command will
+fail. Rules are numbered starting at 1.
+.TP
+.B "-I, --insert"
+Insert one or more rules in the selected chain as the given rule
+number. So, if the rule number is 1, the rule or rules are inserted
+at the head of the chain. This is also the default if no rule number
+is specified.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all
+chains are listed. It is legal to specify the
+.B -Z
+(zero) option as well, in which case the chain(s) will be atomically
+listed and zeroed. The exact output is effected by the other
+arguments given.
+.TP
+.B "-F, --flush"
+Flush the selected chain. This is equivalent to deleting all the
+rules one by one.
+.TP
+.B "-Z, --zero"
+Zero the packet and byte counters in all chains. It is legal to
+specify the
+.B "-L, --list"
+(list) option as well, to see the counters immediately before they are
+cleared; see above.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain of the given name. There must be no
+target of that name already.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no references
+to the chain (if there are you must delete or replace the referring
+rules before the chain can be deleted). If no argument is given, it
+will attempt to delete every non-builtin chain.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. See the section
+.TP
+.B "-E, --rename-chain"
+Rename the user specified chain to the user supplied name; this is
+cosmetic, and has no effect on the structure of the table.
+.B TARGETS
+for the legal targets. Only non-userdefined chains can have policies,
+and neither built-in nor user-defined chains can be policy targets.
+.TP
+.B -h
+Help.
+Give a (currently very brief) description of the command syntax.
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the
+add, delete, replace, append and check commands).
+.TP
+.BR "-p, --proto " "[!] \fIprotocol\fP"
+The protocol of the rule or of the packet to check.
+The specified protocol can be one of
+.IR tcp ,
+.IR udp ,
+.IR icmp ,
+or
+.IR all ,
+or it can be a numeric value, representing one of these protocols or a
+different one. Also a protocol name from /etc/protocols is allowed.
+A "!" argument before the protocol inverts the
+test. The number zero is equivalent to
+.IR all .
+Protocol
+.I all
+will match with all protocols and is taken as default when this
+option is omitted.
+.I All
+may not be used in in combination with the check command.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+Source specification.
+.I Address
+can be either a hostname, a network name, or a plain IP address.
+The
+.I mask
+can be either a network mask or a plain number,
+specifying the number of 1's at the left side of the network mask.
+Thus, a mask of
+.I 24
+is equivalent to
+.IR 255.255.255.0 .
+A "!" argument before the address specification inverts the sense of
+the address. The flag
+.B --src
+is a convenient alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+Destination specification.
+See the description of the
+.B -s
+(source) flag for a detailed description of the syntax. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BI "-j, --jump " "target"
+This specifies the target of the rule; ie. what to do if the packet
+matches it. The target can be a user-defined chain (not the one this
+rule is in), one of the special builtin targets which decide the fate
+of the packet immediately, or an extension (see
+.B EXTENSIONS
+below). If this
+option is omitted in a rule, then matching the rule will have no
+effect on the packet's fate, but the counters on the rule will be
+incremented.
+.TP
+.BR "-i, --in-interface " "[!] [\fIname\fP]"
+Optional name of an interface via which a packet is received (for
+packets entering the
+.BR INPUT ,
+.B FORWARD
+and
+.B PREROUTING
+chains). When the "!" argument is used before the interface name, the
+sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, the string "+" is assumed, which will match with any
+interface name.
+.TP
+.BR "-o, --out-interface " "[!] [\fIname\fP]"
+Optional name of an interface via which a packet is going to
+be sent (for packets entering the
+.BR FORWARD ,
+.B OUTPUT
+and
+.B POSTROUTING
+chains). When the "!" argument is used before the interface name,
+the sense is inverted. If the interface name ends in a "+", then any
+interface which begins with this name will match. If this option is
+omitted, the string "+" is assumed, which will match with any
+interface name.
+.TP
+.B "[!] " "-f, --fragment"
+This means that the rule only refers to second and further fragments
+of fragmented packets. Since there is no way to tell the source or
+destination ports of such a packet (or ICMP type), such a packet will
+not match any rules which specify them. When the "!" argument
+precedes the "-f" flag, the sense is inverted.
+.SS "OTHER OPTIONS"
+The following additional options can be specified:
+.TP
+.B "-v, --verbose"
+Verbose output. This option makes the list command show the interface
+address, the rule options (if any), and the TOS masks. The packet and
+byte counters are also listed, with the suffix 'K', 'M' or 'G' for
+1000, 1,000,000 and 1,000,000,000 multipliers respectively (but see
+the
+.B -x
+flag to change this).
+For appending, insertion, deletion and replacement, this causes
+detailed information on the rule or rules to be printed.
+.TP
+.B "-n, --numeric"
+Numeric output.
+IP addresses and port numbers will be printed in numeric format.
+By default, the program will try to display them as host names,
+network names, or services (whenever applicable).
+.TP
+.B "-x, --exact"
+Expand numbers.
+Display the exact value of the packet and byte counters,
+instead of only the rounded number in K's (multiples of 1000)
+M's (multiples of 1000K) or G's (multiples of 1000M). This option is
+only relevent for the
+.B -L
+command.
+.TP
+.B "--line-numbers"
+When listing rules, add line numbers to the beginning of each rule,
+corresponding to that rule's position in the chain.
+.SH MATCH EXTENSIONS
+iptables can use extended packet matching modules. The following are
+included in the base package, and most of these can be preceeded by a
+.B !
+to invert the sense of the match.
+.SS tcp
+These extensions are loaded if `--protocol tcp' is specified, and no
+other match is specified. It provides the following options:
+.TP
+.BR "--source-port " "[!] [\fIport[:port]\fP] or [\fIport[-port]]\fP]"
+Source port or port range specification. This can either be a service
+name or a port number. An inclusive range can also be specified,
+using the format
+.IR port : port
+or
+.IR port - port .
+If the first port is omitted, "0" is assumed; if the last is omitted,
+"65535" is assumed.
+If the second port greater then the first they will be swapped.
+The flag
+.B --sport
+is an alias for this option.
+.TP
+.BR "--destination-port " "[!] [\fIport[:port]\fP] or [\fIport[-port]]\fP"
+Destination port or port range specification. The flag
+.B --dport
+is an alias for this option.
+.TP
+.BR "--tcp-flags " "[!] \fImask\fP \fIcomp\fP"
+Match when the TCP flags are as specified. The first argument is the
+flags which we should examine, written as a comma-separated list, and
+the second argument is a comma-separated list of flags which must be
+set. Flags are:
+.BR "SYN ACK FIN RST URG PSH ALL NONE" .
+Hence the command
+.br
+ ipchains -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
+.br
+will only match packets with the SYN flag set, and the ACK, FIN and
+RST flags unset.
+.TP
+.B "[!] --syn"
+Only match TCP packets with the SYN bit set and the ACK and FIN bits
+cleared. Such packets are used to request TCP connection initiation;
+for example, blocking such packets coming in an interface will prevent
+incoming TCP connections, but outgoing TCP connections will be
+unaffected.
+It is equivalent to \fB--tcp-flags SYN,RST,ACK SYN\fP.
+If the "!" flag precedes the "--syn", the sense of the
+option is inverted.
+.TP
+.BR "--tcp-option " "[!] \fInumber\fP"
+Match if TCP option set.
+.SS udp
+These extensions are loaded if `--protocol udp' is specified, and no
+other match is specified. It provides the following options:
+.TP
+.BR "--source-port " "[!] [\fIport[:port]\fP] or [\fIport[-port]]\fP"
+Source port or port range specification.
+See the description of the
+.B --source-port
+option of the TCP extension for details.
+.TP
+.BR "--destination-port " "[!] [\fIport[:port]\fP] or [\fIport[-port]]\fP"
+Destination port or port range specification.
+See the description of the
+.B --destination-port
+option of the TCP extension for details.
+.SS icmp
+This extension is loaded if `--protocol icmp' is specified, and no
+other match is specified. It provides the following option:
+.TP
+.BR "--icmp-type " "[!] \fItypename\fP"
+This allows specification of the ICMP type, which can be a numeric
+ICMP type, or one of the ICMP type names shown by the command
+.br
+ iptables -p icmp -h
+.br
+.SS mac
+.TP
+.BR "--mac-source " "[!] \fIaddress\fP"
+Match source MAC address. It must be of the form XX:XX:XX:XX:XX:XX.
+Note that this only makes sense for packets entering the
+.BR PREROUTING ,
+or
+.B INPUT
+chains from an ethernet device.
+.SS limit
+This module matches at a limited rate using a token bucket filter: it
+can be used in combination with the LOG target to give limited
+logging. A rule using this extension will match until this limit is
+reached (unless the `!' flag is used).
+.TP
+.BI "--limit " "rate"
+Maximum average matching rate: specified as a number, with an optional
+`/second', `/minute', `/hour', or `/day' suffix; the default is
+3/hour.
+.TP
+.BI "--limit-burst " "number"
+The maximum initial number of packets to match: this number gets
+recharged by one every time the limit specified above is not reached,
+up to this number; the default is 5.
+.SS multiport
+This module matches a set of source or destination ports. Up to 15
+ports can be specified. It can only be used in conjunction with
+.B "-p tcp"
+or
+.BR "-p udp" .
+.TP
+.BR "--source-port" " [\fIport[,port]\fP]"
+Match if the source port is one of the given ports.
+.TP
+.BR "--destination-port" " [\fIport[,port]\fP]"
+Match if the destination port is one of the given ports.
+.TP
+.BR "--port" " [\fIport[,port]\fP]"
+Match if the both the source and destination ports are equal to each
+other and to one of the given ports.
+.SS mark
+This module matches the netfilter mark field associated with a packet
+(which can be set using the
+.B MARK
+target below).
+.TP
+.BI "--mark " "value[/mask]"
+Matches packets with the given unsigned mark value (if a mask is
+specified, this is logically ANDed with the mark before the
+comparison).
+.SS owner
+This module attempts to match various characteristics of the packet
+creator, for locally-generated packets. It is only valid in the
+OUTPUT chain, and even this some packets (such as ICMP ping responses)
+may have no owner, and hence never match.
+.TP
+.BI "--uid-owner" "userid"
+Matches if the packet was created by a process with the given
+effective user id.
+.TP
+.BI "--gid-owner" "groupid"
+Matches if the packet was created by a process with the given
+effective group id.
+.TP
+.BI "--pid-owner" "processid"
+Matches if the packet was created by a process with the given
+process id.
+.TP
+.BI "--sid-owner" "sessionid"
+Matches if the packet was created by a process in the given session
+group.
+.SS state
+This module, when combined with connection tracking, allows access to
+the connection tracking state for this packet.
+.TP
+.BI "--state" "state"
+Where state is a comma separated list of the connection states to
+match. Possible states are
+.B INVALID
+meaning that the packet is associated with no known connection,
+.B ESTABLISHED
+meaning that the packet is associated with a connection which has seen
+packets in both directions,
+.B NEW
+meaning that the packet has started a new connection, or otherwise
+associated with a connection which has not seen packets in both
+directions, and
+.B RELATED
+meaning that the packet is starting a new connection, but is
+associated with an existing connection, such as an FTP data transfer,
+or an ICMP error.
+.SS unclean
+This module takes no options, but attempts to match packets which seem
+malformed or unusual. This is regarded as experimental.
+.SS tos
+This module matches the 8 bits of Type of Service field in the IP
+header (ie. including the precedence bits).
+.TP
+.BI "--tos" "tos"
+The argument is either a standard name, (use
+.br
+ iptables -m tos -h
+.br
+to see the list), or a numeric value to match.
+.SH TARGET EXTENSIONS
+iptables can use extended target modules: the following are included
+in the standard distribution.
+.SS LOG
+Turn on kernel logging of matching packets. When this option is set
+for a rule, the Linux kernel will print some information on all
+matching packets (like most IP header fields) via
+.IR printk ().
+.TP
+.BI "--log-level " "level"
+Level of logging (numeric or see \fIsyslog.conf\fP(5)).
+.TP
+.BI "--log-prefix " "prefix"
+Prefix log messages with the specified prefix; up to 14 letters long,
+and useful for distinguishing messages in the logs.
+.TP
+.B --log-tcp-sequence
+Log TCP sequence numbers. This is a security risk if the log is
+readable by users.
+.TP
+.B --log-tcp-options
+Log options from the TCP packet header.
+.TP
+.B --log-ip-options
+Log options from the IP packet header.
+.SS MARK
+This is used to set the netfilter mark value associated with the
+packet. It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-mark" "mark"
+.SS REJECT
+This is used to send back an error packet in response to the matched
+packet: otherwise it is equivalent to
+.BR DROP .
+This target is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains. Several options control the nature of the error packet
+returned:
+.TP
+.BI "--reject-with" "type"
+The type given can be
+.BR icmp-net-unreachable ,
+.BR icmp-host-unreachable ,
+.BR icmp-port-unreachable or
+.BR icmp-proto-unreachable
+which return the appropriate ICMP error message (net-unreachable is
+the default). The following special types are also allowed:
+.B tcp-reset
+is only valid if the rule also specifies
+.BR "-p tcp" ,
+and generates a TCP reset packet in response. This is generally not a
+good idea (modern stacks should deal with ICMPs on TCP connection
+initiation attempts).
+.B echo-reply
+can only be used for rules which specify an ICMP ping packet, and
+generates a ping reply.
+.SS TOS
+This is used to set the 8-bit Type of Service field in the IP header.
+It is only valid in the
+.B mangle
+table.
+.TP
+.BI "--set-tos" "tos"
+You can use a numeric TOS values, or use
+.br
+ iptables -j TOS -h
+.br
+to see the list of valid TOS names.
+.SS MIRROR
+This is an experimental demonstration target which inverts the source
+and destination fields in the IP header and retransmits the packet.
+It is only valid in the
+.BR INPUT ,
+.B FORWARD
+and
+.B OUTPUT
+chains.
+.SS SNAT
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain. It specifies that the source address of the packet should be
+modified (and all future packets in this connection will also be
+mangled), and rules should cease being examined. It takes one option:
+.TP
+.BI "--to-source" "<ipaddr>[-<ipaddr>][:port-port]"
+which can specify a single new source IP address, an inclusive range
+of IP addresses, and optionally, a port range (which is only valid if
+the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then source ports below 512 will be
+mapped to other ports below 512: those between 1024 will be mapped to
+ports below 1024, and other ports will be mapped to 1024 or above.
+Where possible, no port alteration will occur.
+.SS DNAT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains. It specifies that the destination address of the packet
+should be modified (and all future packets in this connection will
+also be mangled), and rules should cease being examined. It takes one
+option:
+.TP
+.BI "--to-destination" "<ipaddr>[-<ipaddr>][:port-port]"
+which can specify a single new destination IP address, an inclusive
+range of IP addresses, and optionally, a port range (which is only
+valid if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+If no port range is specified, then the destination port will never be
+modified.
+.SS MASQUERADE
+This target is only valid in the
+.B nat
+table, in the
+.B POSTROUTING
+chain. It should only be used with dynamically assigned IP (dialup)
+connections: if you have a static IP address, you should use the SNAT
+target. Masquerading is equivalent to specifying a mapping to the IP
+address of the interface the packet is going out, but also has the
+effect that connections are
+.I forgotten
+when the interface goes down. This is the correct behaviour when the
+next dialup is unlikely to have the same interface address (and hence
+any established connections are lost anyway). It takes one option:
+.TP
+.BI "--to-ports" "<port>[-<port>]"
+This specifies a range of source ports to use, overriding the default
+.B SNAT
+source port-selection heuristics (see above). This is only valid with
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+.SS REDIRECT
+This target is only valid in the
+.B nat
+table, in the
+.B PREROUTING
+and
+.B OUTPUT
+chains. It alters the destination IP address to send the packet to
+the machine itself (locally-generated packets are mapped to the
+127.0.0.1 address).
+It takes one option:
+.TP
+.BI "--to-ports" "<port>[-<port>]"
+This specifies a destination port or range or ports to use: without
+this, the destination port is never altered. This is only valid with
+if the rule also specifies
+.B "-p tcp"
+or
+.BR "-p udp" ).
+.TP
+.SH DIAGNOSTICS
+Various error messages are printed to standard error. The exit code
+is 0 for correct functioning. Errors which appear to be caused by
+invalid or abused command line parameters cause an exit code of 2, and
+other errors cause an exit code of 1.
+.SH BUGS
+Check is not implemented (yet).
+.SH COMPATIBILITY WITH IPCHAINS
+This
+.B iptables
+is very similar to ipchains by Rusty Russell. The main difference is
+that the chains
+.B INPUT
+and
+.B OUTPUT
+are only traversed for packets coming into the local host and
+originating from the local host respectively. Hence every packet only
+passes through one of the three chains; previously a forwarded packet
+would pass through all three.
+.PP
+The other main difference is that
+.B -i
+refers to the input interface;
+.B -o
+refers to the output interface, and both are available for packets
+entering the
+.B FORWARD
+chain.
+.PP The various forms of NAT have been separated out;
+.B iptables
+is a pure packet filter when using the default `filter' table, with
+optional extension modules. This should simplify much of the previous
+confusion over the combination of IP masquerading and packet filtering
+seen previously. So the following options are handled differently:
+.br
+ -j MASQ
+.br
+ -M -S
+.br
+ -M -L
+.br
+There are several other changes in iptables.
+.SH SEE ALSO
+The iptables-HOWTO, which details more iptables usage, and the
+netfilter-hacking-HOWTO which details the internals.
+.SH AUTHOR
+Rusty Russell wrote iptables, in early consultation with Michael
+Neuling.
+.PP
+Marc Boucher wrote the owner match, the mark stuff, and ran around
+doing cool stuff everywhere.
+.PP
+James Morris wrote the TOS target, and tos match.
+.PP
+Jozsef Kadlecsik wrote the REJECT target.
+.PP
+The Netfilter Core Team is: Marc Boucher, Rusty Russell.
+.\" .. and did I mention that we are incredibly cool people?
diff --git a/iptables.c b/iptables.c
new file mode 100644
index 00000000..e7110ea2
--- /dev/null
+++ b/iptables.c
@@ -0,0 +1,1963 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <iptables.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/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 CMD_NONE 0x0000U
+#define CMD_INSERT 0x0001U
+#define CMD_DELETE 0x0002U
+#define CMD_DELETE_NUM 0x0004U
+#define CMD_REPLACE 0x0008U
+#define CMD_APPEND 0x0010U
+#define CMD_LIST 0x0020U
+#define CMD_FLUSH 0x0040U
+#define CMD_ZERO 0x0080U
+#define CMD_NEW_CHAIN 0x0100U
+#define CMD_DELETE_CHAIN 0x0200U
+#define CMD_SET_POLICY 0x0400U
+#define CMD_CHECK 0x0800U
+#define CMD_RENAME_CHAIN 0x1000U
+#define NUMBER_OF_CMD 13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+ 'N', 'X', 'P', 'C', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE 0x00000U
+#define OPT_NUMERIC 0x00001U
+#define OPT_SOURCE 0x00002U
+#define OPT_DESTINATION 0x00004U
+#define OPT_PROTOCOL 0x00008U
+#define OPT_JUMP 0x00010U
+#define OPT_VERBOSE 0x00020U
+#define OPT_EXPANDED 0x00040U
+#define OPT_VIANAMEIN 0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT 0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define NUMBER_OF_OPT 11
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3'};
+
+static struct option original_opts[] = {
+ { "append", 1, 0, 'A' },
+ { "delete", 1, 0, 'D' },
+ { "insert", 1, 0, 'I' },
+ { "replace", 1, 0, 'R' },
+ { "list", 2, 0, 'L' },
+ { "flush", 2, 0, 'F' },
+ { "zero", 2, 0, 'Z' },
+ { "check", 1, 0, 'C' },
+ { "new-chain", 1, 0, 'N' },
+ { "delete-chain", 2, 0, 'X' },
+ { "rename-chain", 2, 0, 'E' },
+ { "policy", 1, 0, 'P' },
+ { "source", 1, 0, 's' },
+ { "destination", 1, 0, 'd' },
+ { "src", 1, 0, 's' }, /* synonym */
+ { "dst", 1, 0, 'd' }, /* synonym */
+ { "proto", 1, 0, 'p' },
+ { "in-interface", 1, 0, 'i' },
+ { "jump", 1, 0, 'j' },
+ { "table", 1, 0, 't' },
+ { "match", 1, 0, 'm' },
+ { "numeric", 0, 0, 'n' },
+ { "out-interface", 1, 0, 'o' },
+ { "verbose", 0, 0, 'v' },
+ { "exact", 0, 0, 'x' },
+ { "fragments", 0, 0, 'f' },
+ { "version", 0, 0, 'V' },
+ { "help", 2, 0, 'h' },
+ { "line-numbers", 0, 0, '0' },
+ { 0 }
+};
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ * + compulsory
+ * x illegal
+ * optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+ /* -n -s -d -p -j -v -x -i -o -f --line */
+/*INSERT*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*APPEND*/ {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*LIST*/ {' ','x','x','x','x',' ',' ','x','x','x',' '},
+/*FLUSH*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/ {'x','+','+','+','x',' ','x','+','+',' ','x'},
+/*RENAME*/ {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists, but
+ unless we're listing (-L), they have only 0 or 1 entry. */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+ /etc/protocols */
+struct pprot {
+ char *name;
+ u_int8_t num;
+};
+
+static const struct pprot chain_protos[] = {
+ { "tcp", IPPROTO_TCP },
+ { "udp", IPPROTO_UDP },
+ { "icmp", IPPROTO_ICMP },
+ { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto)
+{
+ unsigned int i;
+
+ if (proto) {
+ struct protoent *pent = getprotobynumber(proto);
+ if (pent)
+ return pent->p_name;
+ }
+
+ for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+ if (chain_protos[i].num == proto)
+ return chain_protos[i].name;
+
+ return NULL;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+ static struct in_addr addr;
+ unsigned char *addrp;
+ char *p, *q;
+ int onebyte, i;
+ char buf[20];
+
+ /* copy dotted string, because we need to modify it */
+ strncpy(buf, dotted, sizeof(buf) - 1);
+ addrp = (unsigned char *) &(addr.s_addr);
+
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if ((q = strchr(p, '.')) == NULL)
+ return (struct in_addr *) NULL;
+
+ *q = '\0';
+ if ((onebyte = string_to_number(p, 0, 255)) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[i] = (unsigned char) onebyte;
+ p = q + 1;
+ }
+
+ /* we've checked 3 bytes, now we check the last one */
+ if ((onebyte = string_to_number(p, 0, 255)) == -1)
+ return (struct in_addr *) NULL;
+
+ addrp[3] = (unsigned char) onebyte;
+
+ return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+ struct netent *net;
+ static struct in_addr addr;
+
+ if ((net = getnetbyname(name)) != NULL) {
+ if (net->n_addrtype != AF_INET)
+ return (struct in_addr *) NULL;
+ addr.s_addr = htonl((unsigned long) net->n_net);
+ return &addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+ /* memcpy(dst, src, sizeof(struct in_addr)); */
+ dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ fprintf(stderr, "%s v%s: ", program_name, program_version);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ if (status == VERSION_PROBLEM)
+ fprintf(stderr,
+ "Perhaps iptables or your kernel needs to be upgraded.\n");
+ exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ program_name, program_name );
+ exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+ printf("%s v%s\n\n"
+"Usage: %s -[ADC] chain rule-specification [options]\n"
+" %s -[RI] chain rulenum rule-specification [options]\n"
+" %s -D chain rulenum [options]\n"
+" %s -[LFZ] [chain] [options]\n"
+" %s -[NX] chain\n"
+" %s -E old-chain-name new-chain-name\n"
+" %s -P chain target [options]\n"
+" %s -h (print this help information)\n\n",
+ program_name, program_version, program_name, program_name,
+ program_name, program_name, program_name, program_name,
+ program_name, program_name);
+
+ printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+" --append -A chain Append to chain\n"
+" --delete -D chain Delete matching rule from chain\n"
+" --delete -D chain rulenum\n"
+" Delete rule rulenum (1 = first) from chain\n"
+" --insert -I chain [rulenum]\n"
+" Insert in chain as rulenum (default 1=first)\n"
+" --replace -R chain rulenum\n"
+" Replace rule rulenum (1 = first) in chain\n"
+" --list -L [chain] List the rules in a chain or all chains\n"
+" --flush -F [chain] Delete all rules in chain or all chains\n"
+" --zero -Z [chain] Zero counters in chain or all chains\n"
+" --check -C chain Test this packet on chain\n"
+" --new -N chain Create a new user-defined chain\n"
+" --delete-chain\n"
+" -X [chain] Delete a user-defined chain\n"
+" --policy -P chain target\n"
+" Change policy on chain to target\n"
+" --rename-chain\n"
+" -E old-chain new-chain\n"
+" Change chain name, (moving any references)\n"
+
+"Options:\n"
+" --proto -p [!] proto protocol: by number or name, eg. `tcp'\n"
+" --source -s [!] address[/mask]\n"
+" source specification\n"
+" --destination -d [!] address[/mask]\n"
+" destination specification\n"
+" --in-interface -i [!] input name[+]\n"
+" network interface name ([+] for wildcard)\n"
+" --jump -j target\n"
+" target for rule\n"
+" --numeric -n numeric output of addresses and ports\n"
+" --out-interface -o [!] output name[+]\n"
+" network interface name ([+] for wildcard)\n"
+" --table -t table table to manipulate (default: `filter')\n"
+" --verbose -v verbose mode\n"
+" --exact -x expand numbers (display exact values)\n"
+"[!] --fragment -f match second or further fragments only\n"
+"[!] --version -V print package version.\n");
+
+ /* Print out any special helps. */
+ if (iptables_targets) {
+ printf("\n");
+ iptables_targets->help();
+ }
+ if (iptables_matches) {
+ printf("\n");
+ iptables_matches->help();
+ }
+
+ exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+ int i, j, legal = 0;
+
+ /* Check that commands are valid with options. Complicated by the
+ * fact that if an option is legal with *any* command given, it is
+ * legal overall (ie. -z and -l).
+ */
+ for (i = 0; i < NUMBER_OF_OPT; i++) {
+ legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+ for (j = 0; j < NUMBER_OF_CMD; j++) {
+ if (!(command & (1<<j)))
+ continue;
+
+ if (!(options & (1<<i))) {
+ if (commands_v_options[j][i] == '+')
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply the `-%c' "
+ "option for this command\n",
+ optflags[i]);
+ } else {
+ if (commands_v_options[j][i] != 'x')
+ legal = 1;
+ else if (legal == 0)
+ legal = -1;
+ }
+ }
+ if (legal == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "Illegal option `-%c' with this command\n",
+ optflags[i]);
+ }
+}
+
+static char
+opt2char(int option)
+{
+ const char *ptr;
+ for (ptr = optflags; option > 1; option >>= 1, ptr++);
+
+ return *ptr;
+}
+
+static char
+cmd2char(int option)
+{
+ const char *ptr;
+ for (ptr = cmdflags; option > 1; option >>= 1, ptr++);
+
+ return *ptr;
+}
+
+static void
+add_command(int *cmd, const int newcmd, const int othercmds, int invert)
+{
+ if (invert)
+ exit_error(PARAMETER_PROBLEM, "unexpected ! flag");
+ if (*cmd & (~othercmds))
+ exit_error(PARAMETER_PROBLEM, "Can't use -%c with -%c\n",
+ cmd2char(newcmd), cmd2char(*cmd & (~othercmds)));
+ *cmd |= newcmd;
+}
+
+int
+check_inverse(const char option[], int *invert)
+{
+ if (option && strcmp(option, "!") == 0) {
+ if (*invert)
+ exit_error(PARAMETER_PROBLEM,
+ "Multiple `!' flags not allowed");
+
+ *invert = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+ void *p;
+
+ if ((p = calloc(count, size)) == NULL) {
+ perror("iptables: calloc failed");
+ exit(1);
+ }
+ return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+ void *p;
+
+ if ((p = malloc(size)) == NULL) {
+ perror("iptables: malloc failed");
+ exit(1);
+ }
+ return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+ struct hostent *host;
+ struct in_addr *addr;
+ unsigned int i;
+
+ *naddr = 0;
+ if ((host = gethostbyname(name)) != NULL) {
+ if (host->h_addrtype != AF_INET ||
+ host->h_length != sizeof(struct in_addr))
+ return (struct in_addr *) NULL;
+
+ while (host->h_addr_list[*naddr] != (char *) NULL)
+ (*naddr)++;
+ addr = fw_calloc(*naddr, sizeof(struct in_addr));
+ for (i = 0; i < *naddr; i++)
+ inaddrcpy(&(addr[i]),
+ (struct in_addr *) host->h_addr_list[i]);
+ return addr;
+ }
+
+ return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+ struct hostent *host;
+
+ if ((host = gethostbyaddr((char *) addr,
+ sizeof(struct in_addr), AF_INET)) != NULL)
+ return (char *) host->h_name;
+
+ return (char *) NULL;
+}
+
+/*
+ * All functions starting with "parse" should succeed, otherwise
+ * the program fails.
+ * Most routines return pointers to static data that may change
+ * between calls to the same or other routines with a few exceptions:
+ * "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ * return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+ struct in_addr *addrp, *addrptmp;
+
+ if ((addrptmp = dotted_to_addr(name)) != NULL ||
+ (addrptmp = network_to_addr(name)) != NULL) {
+ addrp = fw_malloc(sizeof(struct in_addr));
+ inaddrcpy(addrp, addrptmp);
+ *naddrs = 1;
+ return addrp;
+ }
+ if ((addrp = host_to_addr(name, naddrs)) != NULL)
+ return addrp;
+
+ exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+ static struct in_addr maskaddr;
+ struct in_addr *addrp;
+ int bits;
+
+ if (mask == NULL) {
+ /* no mask at all defaults to 32 bits */
+ maskaddr.s_addr = 0xFFFFFFFF;
+ return &maskaddr;
+ }
+ if ((addrp = dotted_to_addr(mask)) != NULL)
+ /* dotted_to_addr already returns a network byte order addr */
+ return addrp;
+ if ((bits = string_to_number(mask, 0, 32)) == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "invalid mask `%s' specified", mask);
+ if (bits != 0) {
+ maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+ return &maskaddr;
+ }
+
+ maskaddr.s_addr = 0L;
+ return &maskaddr;
+}
+
+static void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+ struct in_addr *maskp, unsigned int *naddrs)
+{
+ struct in_addr *addrp;
+ char buf[256];
+ char *p;
+ int i, j, k, n;
+
+ strncpy(buf, name, sizeof(buf) - 1);
+ if ((p = strrchr(buf, '/')) != NULL) {
+ *p = '\0';
+ addrp = parse_mask(p + 1);
+ } else
+ addrp = parse_mask(NULL);
+ inaddrcpy(maskp, addrp);
+
+ /* if a null mask is given, the name is ignored, like in "any/0" */
+ if (maskp->s_addr == 0L)
+ strcpy(buf, "0.0.0.0");
+
+ addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+ n = *naddrs;
+ for (i = 0, j = 0; i < n; i++) {
+ addrp[j++].s_addr &= maskp->s_addr;
+ for (k = 0; k < j - 1; k++) {
+ if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+ (*naddrs)--;
+ j--;
+ break;
+ }
+ }
+ }
+}
+
+struct iptables_match *
+find_match(const char *name, int tryload)
+{
+ struct iptables_match *ptr;
+
+ for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+ if (strcmp(name, ptr->name) == 0)
+ break;
+ }
+
+ if (!ptr && tryload) {
+ char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+ + strlen(name)];
+ sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+ dlopen(path, RTLD_NOW);
+ return find_match(name, 0);
+ }
+
+ return ptr;
+}
+
+static u_int16_t
+parse_protocol(const char *s)
+{
+ int proto = string_to_number(s, 0, 65535);
+
+ if (proto == -1) {
+ struct protoent *pent;
+
+ if ((pent = getprotobyname(s)))
+ proto = pent->p_proto;
+ else {
+ unsigned int i;
+ for (i = 0;
+ i < sizeof(chain_protos)/sizeof(struct pprot);
+ i++) {
+ if (strcmp(s, chain_protos[i].name) == 0) {
+ proto = chain_protos[i].num;
+ break;
+ }
+ }
+ if (i == sizeof(chain_protos)/sizeof(struct pprot))
+ exit_error(PARAMETER_PROBLEM,
+ "unknown protocol `%s' specified",
+ s);
+ }
+ }
+
+ return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+ int vialen = strlen(arg);
+ unsigned int i;
+
+ memset(mask, 0, IFNAMSIZ);
+ memset(vianame, 0, IFNAMSIZ);
+
+ if (vialen + 1 > IFNAMSIZ)
+ exit_error(PARAMETER_PROBLEM,
+ "interface name `%s' must be shorter than IFNAMSIZ"
+ " (%i)", arg, IFNAMSIZ-1);
+
+ strcpy(vianame, arg);
+ if (vialen == 0)
+ memset(mask, 0, IFNAMSIZ);
+ else if (vianame[vialen - 1] == '+') {
+ memset(mask, 0xFF, vialen - 1);
+ memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+ /* Remove `+' */
+ vianame[vialen - 1] = '\0';
+ } 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])) {
+ printf("Warning: wierd character in interface"
+ " `%s' (No aliases, :, ! or *).\n",
+ vianame);
+ break;
+ }
+ }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+ int rulenum = string_to_number(rule, 1, INT_MAX);
+
+ if (rulenum == -1)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid rule number `%s'", rule);
+
+ return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+ const char *ptr;
+
+ if (strlen(targetname) < 1)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid target name (too short)");
+
+ if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid target name `%s' (%i chars max)",
+ targetname, sizeof(ipt_chainlabel)-1);
+
+ for (ptr = targetname; *ptr; ptr++)
+ if (isspace(*ptr))
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid target name `%s'", targetname);
+ return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+ struct netent *net;
+
+ if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+ return (char *) net->n_name;
+
+ return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+ static char buf[20];
+ const unsigned char *bytep;
+
+ bytep = (const unsigned char *) &(addrp->s_addr);
+ sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+ return buf;
+}
+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;
+}
+
+int
+string_to_number(const char *s, int min, int max)
+{
+ int number;
+ char *end;
+
+ /* Handle hex, octal, etc. */
+ number = (int)strtol(s, &end, 0);
+ if (*end == '\0' && end != s) {
+ /* we parsed a number, let's see if we want this */
+ if (min <= number && number <= max)
+ return number;
+ }
+ return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+ int invert)
+{
+ if (*options & option)
+ exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+ opt2char(option));
+ *options |= option;
+
+ if (invert) {
+ unsigned int i;
+ for (i = 0; 1 << i != option; i++);
+
+ if (!inverse_for_options[i])
+ exit_error(PARAMETER_PROBLEM,
+ "cannot have ! before -%c",
+ opt2char(option));
+ *invflg |= inverse_for_options[i];
+ }
+}
+
+struct iptables_target *
+find_target(const char *name, int 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) {
+ char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+ + strlen(name)];
+ sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+ dlopen(path, RTLD_NOW);
+ return find_target(name, 0);
+ }
+
+ return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, struct option *newopts,
+ unsigned int *option_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ global_option_offset += OPTION_OFFSET;
+ *option_offset = global_option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+ return merge;
+}
+
+void
+register_match(struct iptables_match *me)
+{
+ if (strcmp(me->version, program_version) != 0) {
+ fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+ program_name, me->name, me->version, program_version);
+ exit(1);
+ }
+
+ if (find_match(me->name, 0)) {
+ fprintf(stderr, "%s: match `%s' already registered.\n",
+ program_name, me->name);
+ exit(1);
+ }
+
+ /* Prepend to list. */
+ me->next = iptables_matches;
+ iptables_matches = me;
+ me->m = NULL;
+ me->mflags = 0;
+
+ opts = merge_options(opts, me->extra_opts, &me->option_offset);
+}
+
+void
+register_target(struct iptables_target *me)
+{
+ if (strcmp(me->version, program_version) != 0) {
+ fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+ program_name, me->name, me->version, program_version);
+ exit(1);
+ }
+
+ if (find_target(me->name, 0)) {
+ fprintf(stderr, "%s: target `%s' already registered.\n",
+ program_name, me->name);
+ exit(1);
+ }
+
+ /* Prepend to list. */
+ me->next = iptables_targets;
+ iptables_targets = me;
+ me->t = NULL;
+ me->tflags = 0;
+
+ opts = merge_options(opts, me->extra_opts, &me->option_offset);
+}
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+ struct ipt_counters counters;
+ const char *pol = iptc_get_policy(chain, &counters, handle);
+ printf("Chain %s", chain);
+ if (pol) {
+ printf(" (policy %s", pol);
+ if (!(format & FMT_NOCOUNTS))
+ printf(" %llu packets, %llu bytes",
+ counters.pcnt, counters.bcnt);
+ printf(")\n");
+ } else {
+ unsigned int refs;
+ iptc_get_references(&refs, chain, handle);
+ printf(" (%u references)\n", refs);
+ }
+
+ if (format & FMT_LINENUMBERS)
+ printf(FMT("%-4s ", "%s "), "num");
+ if (!(format & FMT_NOCOUNTS)) {
+ if (format & FMT_KILOMEGAGIGA) {
+ printf(FMT("%5s ","%s "), "pkts");
+ printf(FMT("%5s ","%s "), "bytes");
+ } else {
+ printf(FMT("%8s ","%s "), "pkts");
+ printf(FMT("%10s ","%s "), "bytes");
+ }
+ }
+ if (!(format & FMT_NOTARGET))
+ printf(FMT("%-9s ","%s "), "target");
+ fputs(" prot ", stdout);
+ if (format & FMT_OPTIONS)
+ fputs("opt", stdout);
+ if (format & FMT_VIA) {
+ printf(FMT(" %-6s ","%s "), "in");
+ printf(FMT("%-6s ","%s "), "out");
+ }
+ printf(FMT(" %-19s ","%s "), "source");
+ printf(FMT(" %-19s "," %s "), "destination");
+ printf("\n");
+}
+
+static 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.name, 1);
+
+ if (match) {
+ if (match->print)
+ match->print(ip, m, numeric);
+ } else {
+ if (m->u.name[0])
+ printf("UNKNOWN match `%s' ", m->u.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, 1);
+ else
+ target = find_target(IPT_STANDARD_TARGET, 1);
+
+ 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);
+ 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->target_size != sizeof(*t))
+ printf("[%u bytes of unknown target data] ",
+ t->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.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+ struct ipt_entry *fw,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ int verbose,
+ iptc_handle_t *handle)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->ip.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->ip.dst.s_addr = daddrs[j].s_addr;
+ if (verbose)
+ print_firewall_line(fw, *handle);
+ ret &= iptc_append_entry(chain, fw, handle);
+ }
+ }
+
+ return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+ struct ipt_entry *fw,
+ unsigned int rulenum,
+ const struct in_addr *saddr,
+ const struct in_addr *daddr,
+ int verbose,
+ iptc_handle_t *handle)
+{
+ fw->ip.src.s_addr = saddr->s_addr;
+ fw->ip.dst.s_addr = daddr->s_addr;
+
+ if (verbose)
+ print_firewall_line(fw, *handle);
+ return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+ struct ipt_entry *fw,
+ unsigned int rulenum,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ int verbose,
+ iptc_handle_t *handle)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->ip.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->ip.dst.s_addr = daddrs[j].s_addr;
+ if (verbose)
+ print_firewall_line(fw, *handle);
+ ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+ }
+ }
+
+ return ret;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+ struct ipt_entry *fw,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ int verbose,
+ iptc_handle_t *handle)
+{
+ unsigned int i, j;
+ int ret = 1;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->ip.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->ip.dst.s_addr = daddrs[j].s_addr;
+ if (verbose)
+ print_firewall_line(fw, *handle);
+ ret &= iptc_delete_entry(chain, fw, handle);
+ }
+ }
+ return ret;
+}
+
+static int
+check_packet(const ipt_chainlabel chain,
+ struct ipt_entry *fw,
+ unsigned int nsaddrs,
+ const struct in_addr saddrs[],
+ unsigned int ndaddrs,
+ const struct in_addr daddrs[],
+ int verbose,
+ iptc_handle_t *handle)
+{
+ int ret = 1;
+ unsigned int i, j;
+ const char *msg;
+
+ for (i = 0; i < nsaddrs; i++) {
+ fw->ip.src.s_addr = saddrs[i].s_addr;
+ for (j = 0; j < ndaddrs; j++) {
+ fw->ip.dst.s_addr = daddrs[j].s_addr;
+ if (verbose)
+ print_firewall_line(fw, *handle);
+ msg = iptc_check_packet(chain, fw, handle);
+ if (!msg) ret = 0;
+ else printf("%s\n", msg);
+ }
+ }
+
+ return ret;
+}
+
+static int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+ int verbose, iptc_handle_t *handle)
+{
+ int ret = 1;
+ const char *chain = NULL;
+
+ while ((chain = iptc_next_chain(chain, handle))) {
+ ret &= fn(chain, verbose, handle);
+ }
+
+ return ret;
+}
+
+static int
+flush_entries(const ipt_chainlabel chain, int verbose,
+ iptc_handle_t *handle)
+{
+ if (!chain)
+ return for_each_chain(flush_entries, verbose, handle);
+
+ if (verbose)
+ fprintf(stdout, "Flushing chain `%s'\n", chain);
+ return iptc_flush_entries(chain, handle);
+ }
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+ iptc_handle_t *handle)
+{
+ if (!chain)
+ return for_each_chain(zero_entries, verbose, handle);
+
+ if (verbose)
+ fprintf(stdout, "Zeroing chain `%s'\n", chain);
+ return iptc_zero_entries(chain, handle);
+}
+
+static int
+delete_chain(const ipt_chainlabel chain, int verbose,
+ iptc_handle_t *handle)
+{
+ if (!chain) {
+ const char *i, *last = NULL;
+ int ret = 1;
+
+ /* Iterate over built-ins */
+ for (i = iptc_next_chain(NULL, handle);
+ i && iptc_builtin(i, *handle);
+ i = iptc_next_chain(i, handle))
+ last = i;
+
+ /* No user-defined chains? */
+ if (!i)
+ return ret;
+
+ /* Be careful iterating: it isn't safe during delete. */
+ /* Re-iterate after each delete successful */
+ while ((i = iptc_next_chain(last, handle)) != NULL) {
+ /* Skip over builtins. */
+ if (!delete_chain(i, verbose, handle)) {
+ /* Delete failed; start next
+ iteration from here */
+ last = i;
+ ret = 0;
+ }
+ }
+ return ret;
+ }
+
+ if (verbose)
+ fprintf(stdout, "Deleting chain `%s'\n", chain);
+ return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+ int expanded, int linenumbers, iptc_handle_t *handle)
+{
+ int found = 0;
+ unsigned int i, format;
+ const char *this = NULL;
+
+ format = FMT_OPTIONS;
+ if (!verbose)
+ format |= FMT_NOCOUNTS;
+ else
+ format |= FMT_VIA;
+
+ if (numeric)
+ format |= FMT_NUMERIC;
+
+ if (!expanded)
+ format |= FMT_KILOMEGAGIGA;
+
+ if (linenumbers)
+ format |= FMT_LINENUMBERS;
+
+
+ while ((this = iptc_next_chain(this, handle)) != NULL) {
+ if (chain && strcmp(chain, this) != 0)
+ continue;
+
+ if (found) printf("\n");
+
+ print_header(format, this, handle);
+ for (i = 0; i < iptc_num_rules(this, handle); i++)
+ print_firewall(iptc_get_rule(this, i, handle),
+ iptc_get_target(this, i, handle),
+ i,
+ format,
+ *handle);
+ found = 1;
+ }
+
+ errno = ENOENT;
+ return found;
+}
+
+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)
+ size += m->m->match_size;
+
+ e = fw_malloc(size + target->target_size);
+ *e = *fw;
+ e->target_offset = size;
+ e->next_offset = size + target->target_size;
+
+ size = 0;
+ for (m = matches; m; m = m->next) {
+ memcpy(e->elems + size, m->m, m->m->match_size);
+ size += m->m->match_size;
+ }
+ memcpy(e->elems + size, target, target->target_size);
+
+ return e;
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+ struct ipt_entry fw, *e = NULL;
+ int invert = 0;
+ unsigned int nsaddrs = 0, ndaddrs = 0;
+ struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+ int c, verbose = 0;
+ const char *chain = NULL;
+ const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+ const char *policy = NULL, *newname = NULL;
+ unsigned int rulenum = 0, options = 0, command = 0;
+ int ret = 1;
+ struct iptables_match *m;
+ struct iptables_target *target = NULL;
+ const char *jumpto = "";
+ char *protocol = NULL;
+
+ memset(&fw, 0, sizeof(fw));
+
+ /* Suppress error messages: we may add new options if we
+ demand-load a protocol. */
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv,
+ "-A:C:D:R:I:L::F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:x",
+ opts, NULL)) != -1) {
+ switch (c) {
+ /*
+ * Command selection
+ */
+ case 'A':
+ add_command(&command, CMD_APPEND, CMD_NONE,
+ invert);
+ chain = optarg;
+ break;
+
+ case 'D':
+ add_command(&command, CMD_DELETE, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!') {
+ rulenum = parse_rulenumber(argv[optind++]);
+ command = CMD_DELETE_NUM;
+ }
+ break;
+
+ case 'C':
+ add_command(&command, CMD_CHECK, CMD_NONE,
+ invert);
+ chain = optarg;
+ break;
+
+ case 'R':
+ add_command(&command, CMD_REPLACE, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "-%c requires a rule number",
+ cmd2char(CMD_REPLACE));
+ break;
+
+ case 'I':
+ add_command(&command, CMD_INSERT, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ rulenum = parse_rulenumber(argv[optind++]);
+ else rulenum = 1;
+ break;
+
+ case 'L':
+ add_command(&command, CMD_LIST, CMD_ZERO,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'F':
+ add_command(&command, CMD_FLUSH, CMD_NONE,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'Z':
+ add_command(&command, CMD_ZERO, CMD_LIST,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'N':
+ add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+ invert);
+ chain = optarg;
+ break;
+
+ case 'X':
+ add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+ invert);
+ if (optarg) chain = optarg;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ chain = argv[optind++];
+ break;
+
+ case 'E':
+ add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ newname = argv[optind++];
+ break;
+
+ case 'P':
+ add_command(&command, CMD_SET_POLICY, CMD_NONE,
+ invert);
+ chain = optarg;
+ if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ policy = argv[optind++];
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "-%c requires a chain and a policy",
+ cmd2char(CMD_SET_POLICY));
+ break;
+
+ case 'h':
+ if (!optarg)
+ optarg = argv[optind];
+
+ exit_printhelp();
+
+ /*
+ * Option selection
+ */
+ case 'p':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+ invert);
+
+ /* Canonicalize into lower case */
+ for (protocol = argv[optind-1]; *protocol; protocol++)
+ *protocol = tolower(*protocol);
+
+ protocol = argv[optind-1];
+ fw.ip.proto = parse_protocol(protocol);
+
+ if (fw.ip.proto == 0
+ && (fw.ip.invflags & IPT_INV_PROTO))
+ exit_error(PARAMETER_PROBLEM,
+ "rule would never match protocol");
+ fw.nfcache |= NFC_IP_PROTO;
+ break;
+
+ case 's':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+ invert);
+ shostnetworkmask = argv[optind-1];
+ fw.nfcache |= NFC_IP_SRC;
+ break;
+
+ case 'd':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+ invert);
+ dhostnetworkmask = argv[optind-1];
+ fw.nfcache |= NFC_IP_DST;
+ break;
+
+ case 'j':
+ set_option(&options, OPT_JUMP, &fw.ip.invflags,
+ invert);
+ jumpto = parse_target(optarg);
+ target = find_target(jumpto, 1);
+
+ if (target) {
+ size_t size = sizeof(struct ipt_entry_target)
+ + IPT_ALIGN(target->size);
+
+ target->t = fw_calloc(size, 1);
+ target->t->target_size = size;
+ strcpy(target->t->u.name, jumpto);
+ target->init(target->t, &fw.nfcache);
+ }
+ break;
+
+
+ case 'i':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ fw.ip.iniface,
+ fw.ip.iniface_mask);
+ fw.nfcache |= NFC_IP_IF_IN;
+ break;
+
+ case 'o':
+ if (check_inverse(optarg, &invert))
+ optind++;
+ set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+ invert);
+ parse_interface(argv[optind-1],
+ fw.ip.outiface,
+ fw.ip.outiface_mask);
+ fw.nfcache |= NFC_IP_IF_OUT;
+ break;
+
+ case 'f':
+ set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+ invert);
+ fw.ip.flags |= IPT_F_FRAG;
+ fw.nfcache |= NFC_IP_FRAG;
+ break;
+
+ case 'v':
+ if (!verbose)
+ set_option(&options, OPT_VERBOSE,
+ &fw.ip.invflags, invert);
+ verbose++;
+ break;
+
+ case 'm':
+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --match");
+
+ m = find_match(optarg, 1);
+ if (!m)
+ exit_error(PARAMETER_PROBLEM,
+ "Couldn't load match `%s'", optarg);
+ else {
+ size_t size = sizeof(struct ipt_entry_match)
+ + IPT_ALIGN(m->size);
+ m->m = fw_calloc(size, 1);
+ m->m->match_size = size;
+ strcpy(m->m->u.name, optarg);
+ m->init(m->m, &fw.nfcache);
+ }
+ break;
+
+ case 'n':
+ set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+ invert);
+ break;
+
+ case 't':
+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "unexpected ! flag before --table");
+ *table = argv[optind-1];
+ break;
+
+ case 'x':
+ set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+ invert);
+ break;
+
+ case 'V':
+ if (invert)
+ printf("Not %s ;-)\n", program_version);
+ else
+ printf("%s v%s\n",
+ program_name, program_version);
+ exit(0);
+
+ case '0':
+ set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+ invert);
+ break;
+
+ case 1: /* non option */
+ if (optarg[0] == '!' && optarg[1] == '\0') {
+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "multiple consecutive ! not"
+ " allowed");
+ invert = TRUE;
+ optarg[0] = '\0';
+ continue;
+ }
+ exit_tryhelp(2);
+
+ default:
+ /* FIXME: This scheme doesn't allow two of the same
+ matches --RR */
+ if (!target
+ || !(target->parse(c - target->option_offset,
+ argv, invert,
+ &target->tflags,
+ &fw, &target->t))) {
+ for (m = iptables_matches; m; m = m->next) {
+ if (m->parse(c - m->option_offset,
+ argv, invert,
+ &m->mflags,
+ &fw,
+ &fw.nfcache,
+ &m->m))
+ break;
+ }
+
+ /* If you listen carefully, you can
+ acually hear this code suck. */
+ if (!iptables_matches
+ && protocol
+ && (m = find_match(protocol, 1))) {
+ /* Try loading protocol */
+ size_t size = sizeof(struct ipt_entry_match)
+ + IPT_ALIGN(m->size);
+
+ m->m = fw_calloc(size, 1);
+ m->m->match_size = size;
+ strcpy(m->m->u.name, protocol);
+ m->init(m->m, &fw.nfcache);
+
+ optind--;
+ continue;
+ }
+ if (!m)
+ exit_error(PARAMETER_PROBLEM,
+ "Unknown arg `%s'",
+ argv[optind-1]);
+ }
+ }
+ invert = FALSE;
+ }
+
+ for (m = iptables_matches; m; m = m->next)
+ m->final_check(m->mflags);
+ if (target)
+ target->final_check(target->tflags);
+
+ /* Fix me: must put inverse options checking here --MN */
+
+ if (optind < argc)
+ exit_error(PARAMETER_PROBLEM,
+ "unknown arguments found on commandline");
+ if (!command)
+ exit_error(PARAMETER_PROBLEM, "no command specified");
+ if (invert)
+ exit_error(PARAMETER_PROBLEM,
+ "nothing appropriate following !");
+
+ if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+ if (!(options & OPT_DESTINATION))
+ dhostnetworkmask = "0.0.0.0/0";
+ if (!(options & OPT_SOURCE))
+ shostnetworkmask = "0.0.0.0/0";
+ }
+
+ if (shostnetworkmask)
+ parse_hostnetworkmask(shostnetworkmask, &saddrs,
+ &(fw.ip.smsk), &nsaddrs);
+
+ if (dhostnetworkmask)
+ parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+ &(fw.ip.dmsk), &ndaddrs);
+
+ if ((nsaddrs > 1 || ndaddrs > 1) &&
+ (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+ exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+ " source or destination IP addresses");
+
+ if (command == CMD_CHECK && fw.ip.invflags != 0)
+ exit_error(PARAMETER_PROBLEM, "! not allowed with -%c",
+ cmd2char(CMD_CHECK));
+
+ if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+ exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+ "specify a unique address");
+
+ generic_opt_check(command, options);
+
+ if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+ exit_error(PARAMETER_PROBLEM,
+ "chain name `%s' too long (must be under %i chars)",
+ chain, IPT_FUNCTION_MAXNAMELEN);
+
+ *handle = iptc_init(*table);
+ if (!*handle)
+ exit_error(VERSION_PROBLEM,
+ "can't initialize iptables table `%s': %s",
+ *table, iptc_strerror(errno));
+
+ if (command == CMD_APPEND
+ || command == CMD_DELETE
+ || command == CMD_INSERT
+ || command == CMD_REPLACE) {
+ /* -o not valid with incoming packets. */
+ if (options & OPT_VIANAMEOUT)
+ if (strcmp(chain, "PREROUTING") == 0
+ || strcmp(chain, "INPUT") == 0) {
+ exit_error(PARAMETER_PROBLEM,
+ "Can't use -%c with %s\n",
+ opt2char(OPT_VIANAMEOUT),
+ chain);
+ }
+
+ /* -i not valid with outgoing packets */
+ if (options & OPT_VIANAMEIN)
+ if (strcmp(chain, "POSTROUTING") == 0
+ || strcmp(chain, "OUTPUT") == 0) {
+ exit_error(PARAMETER_PROBLEM,
+ "Can't use -%c with %s\n",
+ opt2char(OPT_VIANAMEIN),
+ chain);
+ }
+
+ if (target && iptc_is_chain(jumpto, *handle)) {
+ printf("Warning: using chain %s, not extension\n",
+ jumpto);
+
+ target = NULL;
+ }
+
+ /* If they didn't specify a target, or it's a chain
+ name, use standard. */
+ if (!target
+ && (strlen(jumpto) == 0
+ || iptc_is_chain(jumpto, *handle))) {
+ size_t size;
+ target = find_target(IPT_STANDARD_TARGET, 1);
+
+ if (!target)
+ exit_error(OTHER_PROBLEM,
+ "Can't find standard target\n");
+
+ size = sizeof(struct ipt_entry_target)
+ + IPT_ALIGN(target->size);
+ target->t = fw_calloc(size, 1);
+ target->t->target_size = size;
+ strcpy(target->t->u.name, jumpto);
+ target->init(target->t, &fw.nfcache);
+ }
+
+ if (!target) {
+ struct ipt_entry_target unknown_target;
+
+ /* Don't know it. Must be extension with no
+ options? */
+ unknown_target.target_size = sizeof(unknown_target);
+ strcpy(unknown_target.u.name, jumpto);
+
+ e = generate_entry(&fw, iptables_matches,
+ &unknown_target);
+ } else {
+ e = generate_entry(&fw, iptables_matches, target->t);
+ }
+ }
+
+ switch (command) {
+ case CMD_APPEND:
+ ret = append_entry(chain, e,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE,
+ handle);
+ break;
+ case CMD_CHECK:
+ ret = check_packet(chain, e,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE, handle);
+ break;
+ case CMD_DELETE:
+ ret = delete_entry(chain, e,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE,
+ handle);
+ break;
+ case CMD_DELETE_NUM:
+ ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+ break;
+ case CMD_REPLACE:
+ ret = replace_entry(chain, e, rulenum - 1,
+ saddrs, daddrs, options&OPT_VERBOSE,
+ handle);
+ break;
+ case CMD_INSERT:
+ ret = insert_entry(chain, e, rulenum - 1,
+ nsaddrs, saddrs, ndaddrs, daddrs,
+ options&OPT_VERBOSE,
+ handle);
+ break;
+ case CMD_LIST:
+ ret = list_entries(chain,
+ options&OPT_VERBOSE,
+ options&OPT_NUMERIC,
+ options&OPT_EXPANDED,
+ options&OPT_LINENUMBERS,
+ handle);
+ break;
+ case CMD_FLUSH:
+ ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+ break;
+ case CMD_ZERO:
+ ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+ break;
+ case CMD_LIST|CMD_ZERO:
+ ret = list_entries(chain,
+ options&OPT_VERBOSE,
+ options&OPT_NUMERIC,
+ options&OPT_EXPANDED,
+ options&OPT_LINENUMBERS,
+ handle);
+ if (ret)
+ ret = zero_entries(chain,
+ options&OPT_VERBOSE, handle);
+ break;
+ case CMD_NEW_CHAIN:
+ ret = iptc_create_chain(chain, handle);
+ break;
+ case CMD_DELETE_CHAIN:
+ ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+ break;
+ case CMD_RENAME_CHAIN:
+ ret = iptc_rename_chain(chain, newname, handle);
+ break;
+ case CMD_SET_POLICY:
+ ret = iptc_set_policy(chain, policy, handle);
+ break;
+ default:
+ /* We should never reach this... */
+ exit_tryhelp(2);
+ }
+
+ if (verbose > 1)
+ dump_entries(*handle);
+
+ return ret;
+}
diff --git a/libipq/IPQ.notes.txt b/libipq/IPQ.notes.txt
new file mode 100644
index 00000000..a2547fa4
--- /dev/null
+++ b/libipq/IPQ.notes.txt
@@ -0,0 +1,118 @@
+------------------------------------------------------------------------------------
+IPv4 Queuing Documentation
+------------------------------------------------------------------------------------
+
+Note: this file is temporary until the documentation is complete.
+
+Upgrade information:
+ * If upgrading from the queue device (v0.90.4 or below), you will need to
+ delete the old shared library, usually found in
+ /usr/local/lib/iptables/libipt_QUEUE.so
+
+TODO List:
+ * Non-blocking i/o for userspace api
+ * Buffered verdicts
+ * Reschedule processing if userspace busy
+ * Better session reliability
+ * Testsuite scripts, fix/improve tools
+ * Documentation
+ * Multiple queues per protocol?
+ * Performance analysis
+ * Userspace language bindings
+
+
+Overview:
+The following diagram is a conceptual view of how the queue operates:
+
+ +---------+
+ | QUEUE |
+ +---------+
+ | |
+ | +---+ | --> dequeue() --> nf_reinject() [stack]
+ | | V | |
+ | +---+ |
+ | |
+ | +---+ |
+ | | W | |
+ | +---+ |
+ | |
+ | +---+ |
+ | | V | |
+ | +---+ |
+ | |
+ | +---+ |
+ | | V | | <-- set_verdict() [user]
+ | +---+ |
+ | |
+ | +---+ |
+ | | W | |
+ | +---+ |
+ | |
+ | +---+ |
+ | | N | | --> notify_user() [user]
+ | +---+ |
+ | |
+ +---------+ <-- set_mode() [user]
+ ^
+ |
+ enqueue()
+ ^
+ |
+ nf_queue() [stack]
+
+
+The queue is processed via a kernel thread, which is woken up upon enqueue()
+set_mode() and set_verdict().
+
+As the queue is modal, and netlink is connectionless, a reasonable amount of
+state needs to be maintained.
+
+Packet states:
+N = new packet (default initial state)
+W = user notfied, waiting for verdict
+V = verdict set (usually by user)
+
+Queue states (settable by user):
+* HOLD (default initial state)
+enqueue packets
+do not notify user
+do not accept verdicts
+do not dequeue packets
+
+* NORMAL
+enqueue packets
+notify user of new packets (may copy entire packet)
+accept verdicts from user (may include modified packet)
+dequeue packets
+
+* FLUSH (returns to HOLD when queue is empty, unless terminating)
+do not enqueue packets
+do not not notify user
+set verdicts on all packets to NF_DROP
+dequeue all packets for dropping
+
+Note that for HOLD & NORMAL queue states, new packets are dropped if the
+queue is full.
+
+Known bugs:
+- Userspace app gets unknown message from kernel if it sends an invalid
+ message type (should get an NLMSG_ERROR).
+
+Documentation notes:
+libipq:
+- Queue is held after flush completes, user must either start copying
+ or shutdown or the queue will fill up.
+
+- If you get a IPQ_ERR_RTRUNC message, your local receive
+ buffer is probably too small. Netlink has no way of detecting
+ this, and thinks the message was delivered (technically, it was,
+ to your *socket* receive buffer though). Thus you need to respond
+ with an NF_DROP for the packet and use a bigger buffer.
+
+- If you modify a packet, you must recalculate checksums as
+ appropriate before sending it back.
+
+- The code wont stop you from doing this, but try not to set NF_QUEUE
+ verdict on packets.
+
+ \ No newline at end of file
diff --git a/libipq/Makefile b/libipq/Makefile
new file mode 100644
index 00000000..954e5e01
--- /dev/null
+++ b/libipq/Makefile
@@ -0,0 +1,11 @@
+#! /usr/bin/make
+
+EXTRAS+=libipq/libipq.a
+#CFLAGS+=-DDEBUG_LIBIPTQ
+
+ifndef TOPLEVEL_INCLUDED
+local:
+ cd .. && $(MAKE) $(SHARED_LIBS) $(EXTRAS)
+else
+libipq/libipq.a: libipq/libipq.a(libipq/libipq.o)
+endif
diff --git a/libipq/libipq.c b/libipq/libipq.c
new file mode 100644
index 00000000..06e4a02e
--- /dev/null
+++ b/libipq/libipq.c
@@ -0,0 +1,310 @@
+/*
+ * libipq.c
+ *
+ * IPQ userspace library.
+ *
+ * Please note that this library is still developmental, and there may
+ * be some API changes.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libipq/libipq.h>
+
+/****************************************************************************
+ *
+ * Private interface
+ *
+ ****************************************************************************/
+
+enum {
+ IPQ_ERR_NONE = 0,
+ IPQ_ERR_IMPL,
+ IPQ_ERR_HANDLE,
+ IPQ_ERR_SOCKET,
+ IPQ_ERR_BIND,
+ IPQ_ERR_BUFFER,
+ IPQ_ERR_RECV,
+ IPQ_ERR_NLEOF,
+ IPQ_ERR_ADDRLEN,
+ IPQ_ERR_STRUNC,
+ IPQ_ERR_RTRUNC,
+ IPQ_ERR_NLRECV,
+ IPQ_ERR_SEND,
+ IPQ_ERR_SUPP,
+ IPQ_ERR_RECVBUF
+};
+#define IPQ_MAXERR IPQ_ERR_RECVBUF
+
+struct ipq_errmap_t {
+ int errcode;
+ char *message;
+} ipq_errmap[] = {
+ { IPQ_ERR_NONE, "Unknown error" },
+ { IPQ_ERR_IMPL, "Implementation error" },
+ { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
+ { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
+ { IPQ_ERR_BIND, "Unable to bind netlink socket" },
+ { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
+ { IPQ_ERR_RECV, "Failed to receive netlink message" },
+ { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
+ { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
+ { IPQ_ERR_STRUNC, "Sent message truncated" },
+ { IPQ_ERR_RTRUNC, "Received message truncated" },
+ { IPQ_ERR_NLRECV, "Received error from netlink" },
+ { IPQ_ERR_SEND, "Failed to send netlink message" },
+ { IPQ_ERR_SUPP, "Operation not supported" },
+ { IPQ_ERR_RECVBUF, "Receive buffer size invalid" }
+};
+
+static int ipq_errno = IPQ_ERR_NONE;
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+ const void *msg, size_t len);
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+ unsigned char *buf, size_t len);
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+ const struct msghdr *msg,
+ unsigned int flags);
+
+static char *ipq_strerror(int errcode);
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+ const void *msg, size_t len)
+{
+ int status = sendto(h->fd, msg, len, 0,
+ (struct sockaddr *)&h->peer, sizeof(h->peer));
+ if (status < 0)
+ ipq_errno = IPQ_ERR_SEND;
+ return status;
+}
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+ const struct msghdr *msg,
+ unsigned int flags)
+{
+ int status = sendmsg(h->fd, msg, flags);
+ if (status < 0)
+ ipq_errno = IPQ_ERR_SEND;
+ return status;
+}
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+ unsigned char *buf, size_t len)
+{
+ int addrlen, status;
+ struct nlmsghdr *nlh;
+
+ if (len < sizeof(struct nlmsgerr)) {
+ ipq_errno = IPQ_ERR_RECVBUF;
+ return -1;
+ }
+ addrlen = sizeof(h->peer);
+ status = recvfrom(h->fd, buf, len, 0,
+ (struct sockaddr *)&h->peer, &addrlen);
+ if (status < 0) {
+ ipq_errno = IPQ_ERR_RECV;
+ return status;
+ }
+ if (addrlen != sizeof(h->peer)) {
+ ipq_errno = IPQ_ERR_RECV;
+ return -1;
+ }
+ if (status == 0) {
+ ipq_errno = IPQ_ERR_NLEOF;
+ return -1;
+ }
+ nlh = (struct nlmsghdr *)buf;
+ if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
+ ipq_errno = IPQ_ERR_RTRUNC;
+ return -1;
+ }
+ return status;
+}
+
+static char *ipq_strerror(int errcode)
+{
+ if (errcode < 0 || errcode > IPQ_MAXERR)
+ errcode = IPQ_ERR_IMPL;
+ return ipq_errmap[errcode].message;
+}
+
+/****************************************************************************
+ *
+ * Public interface
+ *
+ ****************************************************************************/
+
+/*
+ * Create and initialise an ipq handle.
+ * FIXME: implement flags.
+ */
+struct ipq_handle *ipq_create_handle(u_int32_t flags)
+{
+ int status;
+ struct ipq_handle *h;
+
+ h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
+ if (h == NULL) {
+ ipq_errno = IPQ_ERR_HANDLE;
+ return NULL;
+ }
+ memset(h, 0, sizeof(struct ipq_handle));
+ h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
+ if (h->fd == -1) {
+ ipq_errno = IPQ_ERR_SOCKET;
+ close(h->fd);
+ free(h);
+ return NULL;
+ }
+ memset(&h->local, 0, sizeof(struct sockaddr_nl));
+ h->local.nl_family = AF_NETLINK;
+ h->local.nl_pid = getpid();
+ h->local.nl_groups = 0;
+ status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
+ if (status == -1) {
+ ipq_errno = IPQ_ERR_BIND;
+ close(h->fd);
+ free(h);
+ return NULL;
+ }
+ memset(&h->peer, 0, sizeof(struct sockaddr_nl));
+ h->peer.nl_family = AF_NETLINK;
+ h->peer.nl_pid = 0;
+ h->peer.nl_groups = 0;
+ return h;
+}
+
+/*
+ * No error condition is checked here at this stage, but it may happen
+ * if/when reliable messaging is implemented.
+ */
+int ipq_destroy_handle(struct ipq_handle *h)
+{
+ if (h) {
+ close(h->fd);
+ free(h);
+ }
+ return 0;
+}
+
+int ipq_set_mode(const struct ipq_handle *h,
+ u_int8_t mode, size_t range)
+{
+ struct {
+ struct nlmsghdr nlh;
+ ipq_peer_msg_t pm;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
+ req.nlh.nlmsg_flags = NLM_F_REQUEST;
+ req.nlh.nlmsg_type = IPQM_MODE;
+ req.nlh.nlmsg_pid = h->local.nl_pid;
+ req.pm.msg.mode.value = mode;
+ req.pm.msg.mode.range = range;
+ return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
+}
+
+/* Note: timeout is not yet implemented */
+ssize_t ipq_read(const struct ipq_handle *h,
+ unsigned char *buf, size_t len, int timeout)
+{
+ return ipq_netlink_recvfrom(h, buf, len);
+}
+
+int ipq_message_type(const unsigned char *buf)
+{
+ return ((struct nlmsghdr*)buf)->nlmsg_type;
+}
+
+int ipq_get_msgerr(const unsigned char *buf)
+{
+ struct nlmsghdr *h = (struct nlmsghdr *)buf;
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ return -err->error;
+}
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
+{
+ return NLMSG_DATA((struct nlmsghdr *)(buf));
+}
+
+int ipq_set_verdict(const struct ipq_handle *h,
+ unsigned long id,
+ unsigned int verdict,
+ size_t data_len,
+ unsigned char *buf)
+{
+ unsigned char nvecs;
+ size_t tlen;
+ struct nlmsghdr nlh;
+ ipq_peer_msg_t pm;
+ struct iovec iov[3];
+ struct msghdr msg;
+
+ memset(&nlh, 0, sizeof(nlh));
+ nlh.nlmsg_flags = NLM_F_REQUEST;
+ nlh.nlmsg_type = IPQM_VERDICT;
+ nlh.nlmsg_pid = h->local.nl_pid;
+ memset(&pm, 0, sizeof(pm));
+ pm.msg.verdict.value = verdict;
+ pm.msg.verdict.id = id;
+ pm.msg.verdict.data_len = data_len;
+ iov[0].iov_base = &nlh;
+ iov[0].iov_len = sizeof(nlh);
+ iov[1].iov_base = &pm;
+ iov[1].iov_len = sizeof(pm);
+ tlen = sizeof(nlh) + sizeof(pm);
+ nvecs = 2;
+ if (data_len && buf) {
+ iov[2].iov_base = buf;
+ iov[2].iov_len = data_len;
+ tlen += data_len;
+ nvecs++;
+ }
+ msg.msg_name = (void *)&h->peer;
+ msg.msg_namelen = sizeof(h->peer);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nvecs;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ nlh.nlmsg_len = tlen;
+ return ipq_netlink_sendmsg(h, &msg, 0);
+}
+
+/* Not implemented yet */
+int ipq_ctl(const struct ipq_handle *h, int request, ...)
+{
+ return 1;
+}
+
+void ipq_perror(const char *s)
+{
+ if (s)
+ fputs(s, stderr);
+ else
+ fputs("ERROR", stderr);
+ if (ipq_errno)
+ fprintf(stderr, ": %s", ipq_strerror(ipq_errno));
+ if (errno)
+ fprintf(stderr, ": %s", strerror(errno));
+ fputc('\n', stderr);
+}
diff --git a/libiptc/Makefile b/libiptc/Makefile
new file mode 100644
index 00000000..e1bc7bed
--- /dev/null
+++ b/libiptc/Makefile
@@ -0,0 +1,16 @@
+#! /usr/bin/make
+
+EXTRAS+=libiptc/libiptc.a
+
+ifndef TOPLEVEL_INCLUDED
+local:
+ cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=libiptc/libiptc.d
+
+libiptc/libiptc.a: libiptc/libiptc.a(libiptc/libiptc.o)
+
+libiptc/libiptc.d: %.d: %.c
+ @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.a:@' > $@
+endif
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
new file mode 100644
index 00000000..91097033
--- /dev/null
+++ b/libiptc/libiptc.c
@@ -0,0 +1,1828 @@
+/* Library which manipulates firewall rules. Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include <libiptc/libiptc.h>
+
+#define IP_VERSION 4
+#define IP_OFFSET 0x1FFF
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/local/lib/iptables"
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [NF_IP_PRE_ROUTING] "PREROUTING",
+ [NF_IP_LOCAL_IN] "INPUT",
+ [NF_IP_FORWARD] "FORWARD",
+ [NF_IP_LOCAL_OUT] "OUTPUT",
+ [NF_IP_POST_ROUTING] "POSTROUTING"
+};
+
+struct counter_map
+{
+ enum {
+ COUNTER_MAP_NOMAP,
+ COUNTER_MAP_NORMAL_MAP,
+ COUNTER_MAP_ZEROED
+ } maptype;
+ unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+ struct ipt_entry_target t;
+ char error[IPT_TABLE_MAXNAMELEN];
+};
+
+struct iptc_handle
+{
+ /* Have changes been made? */
+ int changed;
+ /* Size in here reflects original state. */
+ struct ipt_getinfo info;
+
+ struct counter_map *counter_map;
+ /* Array of hook names */
+ const char **hooknames;
+
+ /* Number in here reflects current state. */
+ unsigned int new_number;
+ struct ipt_get_entries entries;
+};
+
+static void do_check(iptc_handle_t h, unsigned int line);
+#define CHECK(h) do_check((h), __LINE__)
+
+static inline int
+get_number(const struct ipt_entry *i,
+ const struct ipt_entry *seek,
+ unsigned int *pos)
+{
+ if (i == seek)
+ return 1;
+ (*pos)++;
+ return 0;
+}
+
+static unsigned int
+entry2index(const iptc_handle_t h, const struct ipt_entry *seek)
+{
+ unsigned int pos = 0;
+
+ if (IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
+ get_number, seek, &pos) == 0) {
+ fprintf(stderr, "ERROR: offset %i not an entry!\n",
+ (unsigned char *)seek - h->entries.entries);
+ abort();
+ }
+ return pos;
+}
+
+static inline int
+get_entry_n(struct ipt_entry *i,
+ unsigned int number,
+ unsigned int *pos,
+ struct ipt_entry **pe)
+{
+ if (*pos == number) {
+ *pe = i;
+ return 1;
+ }
+ (*pos)++;
+ return 0;
+}
+
+static struct ipt_entry *
+index2entry(iptc_handle_t h, unsigned int index)
+{
+ unsigned int pos = 0;
+ struct ipt_entry *ret = NULL;
+
+ IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
+ get_entry_n, index, &pos, &ret);
+
+ return ret;
+}
+
+static inline struct ipt_entry *
+get_entry(iptc_handle_t h, unsigned int offset)
+{
+ return (struct ipt_entry *)(h->entries.entries + offset);
+}
+
+static inline unsigned long
+entry2offset(const iptc_handle_t h, const struct ipt_entry *e)
+{
+ return (unsigned char *)e - h->entries.entries;
+}
+
+static unsigned long
+index2offset(iptc_handle_t h, unsigned int index)
+{
+ return entry2offset(h, index2entry(h, index));
+}
+
+static const char *
+get_errorlabel(iptc_handle_t h, unsigned int offset)
+{
+ struct ipt_entry *e;
+
+ e = get_entry(h, offset);
+ if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) != 0) {
+ fprintf(stderr, "ERROR: offset %u not an error node!\n",
+ offset);
+ abort();
+ }
+
+ return (const char *)ipt_get_target(e)->data;
+}
+
+/* Allocate handle of given size */
+static iptc_handle_t
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+ size_t len;
+ iptc_handle_t h;
+
+ len = sizeof(struct iptc_handle)
+ + size
+ + num_rules * sizeof(struct counter_map);
+
+ if ((h = malloc(len)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ h->changed = 0;
+ h->counter_map = (void *)h
+ + sizeof(struct iptc_handle)
+ + size;
+ strcpy(h->info.name, tablename);
+ strcpy(h->entries.name, tablename);
+
+ return h;
+}
+
+iptc_handle_t
+iptc_init(const char *tablename)
+{
+ iptc_handle_t h;
+ struct ipt_getinfo info;
+ unsigned int i;
+ int tmp;
+ socklen_t s;
+
+ iptc_fn = iptc_init;
+
+ sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (sockfd < 0)
+ return NULL;
+
+ s = sizeof(info);
+ if (strlen(tablename) >= IPT_TABLE_MAXNAMELEN) {
+ errno = EINVAL;
+ return NULL;
+ }
+ strcpy(info.name, tablename);
+ if (getsockopt(sockfd, IPPROTO_IP, IPT_SO_GET_INFO, &info, &s) < 0)
+ return NULL;
+
+ if ((h = alloc_handle(info.name, info.size, info.num_entries))
+ == NULL)
+ return NULL;
+
+/* Too hard --RR */
+#if 0
+ sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
+ dynlib = dlopen(pathname, RTLD_NOW);
+ if (!dynlib) {
+ errno = ENOENT;
+ return NULL;
+ }
+ h->hooknames = dlsym(dynlib, "hooknames");
+ if (!h->hooknames) {
+ errno = ENOENT;
+ return NULL;
+ }
+#else
+ h->hooknames = hooknames;
+#endif
+
+ /* Initialize current state */
+ h->info = info;
+ h->new_number = h->info.num_entries;
+ for (i = 0; i < h->info.num_entries; i++)
+ h->counter_map[i]
+ = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
+
+ h->entries.size = h->info.size;
+
+ tmp = sizeof(struct ipt_get_entries) + h->info.size;
+
+ if (getsockopt(sockfd, IPPROTO_IP, IPT_SO_GET_ENTRIES, &h->entries,
+ &tmp) < 0) {
+ free(h);
+ return NULL;
+ }
+
+ CHECK(h);
+ return h;
+}
+
+#define IP_PARTS_NATIVE(n) \
+(unsigned int)((n)>>24)&0xFF, \
+(unsigned int)((n)>>16)&0xFF, \
+(unsigned int)((n)>>8)&0xFF, \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+static inline int
+print_match(const struct ipt_entry_match *m)
+{
+ printf("Match name: `%s'\n", m->u.name);
+ return 0;
+}
+
+int
+dump_entry(struct ipt_entry *e, const iptc_handle_t handle)
+{
+ size_t i;
+ struct ipt_entry_target *t;
+
+ printf("Entry %u (%lu):\n", entry2index(handle, e),
+ entry2offset(handle, e));
+ printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+ printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+ IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+ printf("Interface: `%s'/", e->ip.iniface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+ printf("to `%s'/", e->ip.outiface);
+ for (i = 0; i < IFNAMSIZ; i++)
+ printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+ printf("\nProtocol: %u\n", e->ip.proto);
+ printf("Flags: %02X\n", e->ip.flags);
+ printf("Invflags: %02X\n", e->ip.invflags);
+ printf("Counters: %llu packets, %llu bytes\n",
+ e->counters.pcnt, e->counters.bcnt);
+ printf("Cache: %08X ", e->nfcache);
+ if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+ if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+ if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
+ if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
+ if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
+ if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
+ if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
+ if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
+ if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
+ if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
+ if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
+ if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
+ if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
+ printf("\n");
+
+ IPT_MATCH_ITERATE(e, print_match);
+
+ t = ipt_get_target(e);
+ printf("Target name: `%s' [%u]\n", t->u.name, t->target_size);
+ if (strcmp(t->u.name, IPT_STANDARD_TARGET) == 0) {
+ int pos = *(int *)t->data;
+ if (pos < 0)
+ printf("verdict=%s\n",
+ pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+ : pos == -NF_DROP-1 ? "NF_DROP"
+ : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+ : pos == IPT_RETURN ? "RETURN"
+ : "UNKNOWN");
+ else
+ printf("verdict=%u\n", pos);
+ } else if (strcmp(t->u.name, IPT_ERROR_TARGET) == 0)
+ printf("error=`%s'\n", t->data);
+
+ printf("\n");
+ return 0;
+}
+
+void
+dump_entries(const iptc_handle_t handle)
+{
+ CHECK(handle);
+
+ printf("libiptc v%s. %u entries, %u bytes.\n",
+ NETFILTER_VERSION,
+ handle->new_number, handle->entries.size);
+ printf("Table `%s'\n", handle->info.name);
+ printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+ handle->info.hook_entry[NF_IP_PRE_ROUTING],
+ handle->info.hook_entry[NF_IP_LOCAL_IN],
+ handle->info.hook_entry[NF_IP_FORWARD],
+ handle->info.hook_entry[NF_IP_LOCAL_OUT],
+ handle->info.hook_entry[NF_IP_POST_ROUTING]);
+ printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+ handle->info.underflow[NF_IP_PRE_ROUTING],
+ handle->info.underflow[NF_IP_LOCAL_IN],
+ handle->info.underflow[NF_IP_FORWARD],
+ handle->info.underflow[NF_IP_LOCAL_OUT],
+ handle->info.underflow[NF_IP_POST_ROUTING]);
+
+ IPT_ENTRY_ITERATE(handle->entries.entries, handle->entries.size,
+ dump_entry, handle);
+}
+
+static inline int
+find_user_label(struct ipt_entry *e, unsigned int *off, const char *name)
+{
+ /* Increment first: they want offset of entry AFTER label */
+ (*off) += e->next_offset;
+
+ if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) == 0
+ && strcmp(ipt_get_target(e)->data, name) == 0)
+ return 1;
+
+ return 0;
+}
+
+/* Returns offset of label. */
+static int
+find_label(unsigned int *off,
+ const char *name,
+ const iptc_handle_t handle)
+{
+ unsigned int i;
+
+ /* Builtin chain name? */
+ i = iptc_builtin(name, handle);
+ if (i != 0) {
+ *off = handle->info.hook_entry[i-1];
+ return 1;
+ }
+
+ /* User chain name? */
+ *off = 0;
+ if (IPT_ENTRY_ITERATE(handle->entries.entries, handle->entries.size,
+ find_user_label, off, name) != 0) {
+ /* last error node doesn't count */
+ if (*off != handle->entries.size)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Does this chain exist? */
+int iptc_is_chain(const char *chain, const iptc_handle_t handle)
+{
+ unsigned int dummy;
+
+ /* avoid infinite recursion */
+#if 0
+ CHECK(handle);
+#endif
+
+ return find_label(&dummy, chain, handle);
+}
+
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const iptc_handle_t handle, unsigned int start)
+{
+ unsigned int last_off, off;
+ struct ipt_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 ipt_entry_target *t;
+ unsigned int i;
+
+ e = get_entry(handle, off);
+
+ /* We hit an entry point. */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ if ((handle->info.valid_hooks & (1 << i))
+ && off == handle->info.hook_entry[i])
+ return last_off;
+ }
+
+ /* We hit a user chain label */
+ t = ipt_get_target(e);
+ if (strcmp(t->u.name, IPT_ERROR_TARGET) == 0)
+ return last_off;
+ }
+ /* SHOULD NEVER HAPPEN */
+ fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+ handle->entries.size, off);
+ abort();
+}
+
+/* Iterator functions to run through the chains; prev = NULL means
+ first chain. Returns NULL at end. */
+const char *
+iptc_next_chain(const char *prev, iptc_handle_t *handle)
+{
+ unsigned int pos;
+ unsigned int i;
+ struct ipt_entry *e;
+
+ CHECK(*handle);
+ if (!prev)
+ pos = 0;
+ else {
+ if (!find_label(&pos, prev, *handle)) {
+ errno = ENOENT;
+ return NULL;
+ }
+ pos = get_chain_end(*handle, pos);
+ /* Next entry. */
+ e = get_entry(*handle, pos);
+ pos += e->next_offset;
+ }
+ e = get_entry(*handle, pos);
+
+ /* Return names of entry points if it is one. */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ if (((*handle)->info.valid_hooks & (1 << i))
+ && pos == (*handle)->info.hook_entry[i])
+ return (*handle)->hooknames[i];
+ }
+ /* If this is the last element, iteration finished */
+ if (pos + e->next_offset == (*handle)->entries.size)
+ return NULL;
+
+ if (strcmp(ipt_get_target(e)->u.name, IPT_ERROR_TARGET) != 0) {
+ /* SHOULD NEVER HAPPEN */
+ fprintf(stderr, "ERROR: position %u/%u not an error label\n",
+ pos, (*handle)->entries.size);
+ abort();
+ }
+
+ return (const char *)ipt_get_target(e)->data;
+}
+
+/* How many rules in this chain? */
+unsigned int
+iptc_num_rules(const char *chain, iptc_handle_t *handle)
+{
+ unsigned int off = 0;
+ struct ipt_entry *start, *end;
+
+ CHECK(*handle);
+ if (!find_label(&off, chain, *handle)) {
+ errno = ENOENT;
+ return (unsigned int)-1;
+ }
+
+ start = get_entry(*handle, off);
+ end = get_entry(*handle, get_chain_end(*handle, off));
+
+ return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const struct ipt_entry *iptc_get_rule(const char *chain,
+ unsigned int n,
+ iptc_handle_t *handle)
+{
+ unsigned int pos = 0, chainindex;
+
+ CHECK(*handle);
+ if (!find_label(&pos, chain, *handle)) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+ return index2entry(*handle, chainindex + n);
+}
+
+static const char *target_name(iptc_handle_t handle, struct ipt_entry *e)
+{
+ int spos;
+ unsigned int labelidx;
+ struct ipt_entry *jumpto;
+
+ if (strcmp(ipt_get_target(e)->u.name, IPT_STANDARD_TARGET) != 0)
+ return ipt_get_target(e)->u.name;
+
+ /* Standard target: evaluate */
+ spos = *(int *)ipt_get_target(e)->data;
+ if (spos < 0) {
+ if (spos == IPT_RETURN)
+ return IPTC_LABEL_RETURN;
+ else if (spos == -NF_ACCEPT-1)
+ return IPTC_LABEL_ACCEPT;
+ else if (spos == -NF_DROP-1)
+ return IPTC_LABEL_DROP;
+ else if (spos == -NF_ACCEPT-1)
+ return IPTC_LABEL_QUEUE;
+
+ fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
+ entry2offset(handle, e), handle->entries.size,
+ spos);
+ abort();
+ }
+
+ jumpto = get_entry(handle, spos);
+
+ /* Fall through rule */
+ if (jumpto == (void *)e + e->next_offset)
+ return "";
+
+ /* Must point to head of a chain: ie. after error rule */
+ labelidx = entry2index(handle, jumpto) - 1;
+ return get_errorlabel(handle, index2offset(handle, labelidx));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *iptc_get_target(const char *chain,
+ unsigned int n,
+ iptc_handle_t *handle)
+{
+ unsigned int pos = 0, chainindex;
+ struct ipt_entry *e;
+
+ CHECK(*handle);
+ if (!find_label(&pos, chain, *handle)) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ chainindex = entry2index(*handle, get_entry(*handle, pos));
+ e = index2entry(*handle, chainindex + n);
+
+ return target_name(*handle, e);
+}
+
+/* Is this a built-in chain? Actually returns hook + 1. */
+int
+iptc_builtin(const char *chain, const iptc_handle_t handle)
+{
+ unsigned int i;
+
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ if ((handle->info.valid_hooks & (1 << i))
+ && handle->hooknames[i]
+ && strcmp(handle->hooknames[i], chain) == 0)
+ return i+1;
+ }
+ return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+iptc_get_policy(const char *chain,
+ struct ipt_counters *counters,
+ iptc_handle_t *handle)
+{
+ unsigned int start;
+ struct ipt_entry *e;
+ int hook;
+
+ CHECK(*handle);
+ hook = iptc_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 ipt_entry *e,
+ unsigned char *base,
+ unsigned int offset, int delta_offset)
+{
+ struct ipt_standard_target *t = (void *)ipt_get_target(e);
+ unsigned int curr = (unsigned char *)e - base;
+
+ /* Trap: insert of fall-through rule. Don't change fall-through
+ verdict to jump-over-next-rule. */
+ if (strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0
+ && t->verdict > (int)offset
+ && !(curr == offset &&
+ t->verdict == curr + e->next_offset)) {
+ t->verdict += delta_offset;
+ }
+
+ return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, iptc_handle_t *handle)
+{
+ IPT_ENTRY_ITERATE((*handle)->entries.entries,
+ (*handle)->entries.size,
+ correct_verdict, (*handle)->entries.entries,
+ offset, delta_offset);
+
+ (*handle)->changed = 1;
+ return 1;
+}
+
+/* If prepend is set, then we are prepending to a chain: if the
+ * insertion position is an entry point, keep the entry point. */
+static int
+insert_rules(unsigned int num_rules, unsigned int rules_size,
+ const struct ipt_entry *insert,
+ unsigned int offset, unsigned int num_rules_offset,
+ int prepend,
+ iptc_handle_t *handle)
+{
+ iptc_handle_t newh;
+ struct ipt_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 < NF_IP_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)->info.size + rules_size,
+ (*handle)->info.num_entries + num_rules);
+ if (!newh)
+ return 0;
+ newh->info = newinfo;
+
+ /* Copy pre... */
+ memcpy(newh->entries.entries, (*handle)->entries.entries, offset);
+ /* ... Insert new ... */
+ memcpy(newh->entries.entries + offset, insert, rules_size);
+ /* ... copy post */
+ memcpy(newh->entries.entries + offset + rules_size,
+ (*handle)->entries.entries + 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_NOMAP, 0 });
+
+ newh->new_number = (*handle)->new_number + num_rules;
+ newh->entries.size = (*handle)->entries.size + rules_size;
+ newh->hooknames = (*handle)->hooknames;
+
+ free(*handle);
+ *handle = newh;
+
+ return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+ unsigned int offset, unsigned int num_rules_offset,
+ iptc_handle_t *handle)
+{
+ unsigned int i;
+
+ if (offset + rules_size > (*handle)->entries.size) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /* Fix up entry points. */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ /* In practice, we never delete up to a hook entry,
+ since the built-in chains are always first,
+ so these two are never equal */
+ if ((*handle)->info.hook_entry[i] >= offset + rules_size)
+ (*handle)->info.hook_entry[i] -= rules_size;
+ else if ((*handle)->info.hook_entry[i] > offset) {
+ fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
+ i, (*handle)->info.hook_entry[i], offset);
+ abort();
+ }
+
+ /* Underflow points to policy (terminal) rule in
+ built-in, so sequality is valid here (when deleting
+ the last rule). */
+ if ((*handle)->info.underflow[i] >= offset + rules_size)
+ (*handle)->info.underflow[i] -= rules_size;
+ else if ((*handle)->info.underflow[i] > offset) {
+ fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
+ i, (*handle)->info.underflow[i], offset);
+ abort();
+ }
+ }
+
+ /* Move the rules down. */
+ memmove((*handle)->entries.entries + offset,
+ (*handle)->entries.entries + offset + rules_size,
+ (*handle)->entries.size - (offset + rules_size));
+
+ /* Move the counter map down. */
+ memmove(&(*handle)->counter_map[num_rules_offset],
+ &(*handle)->counter_map[num_rules_offset + num_rules],
+ sizeof(struct counter_map)
+ * ((*handle)->new_number - (num_rules + num_rules_offset)));
+
+ /* Fix numbers */
+ (*handle)->new_number -= num_rules;
+ (*handle)->entries.size -= rules_size;
+
+ return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(struct ipt_entry *e, int verdict)
+{
+ struct ipt_standard_target *t;
+
+ t = (struct ipt_standard_target *)ipt_get_target(e);
+
+ if (t->target.target_size != IPT_ALIGN(sizeof(struct ipt_standard_target))) {
+ errno = EINVAL;
+ return 0;
+ }
+ /* memset for memcmp convenience on delete/replace */
+ memset(t->target.u.name, 0, IPT_FUNCTION_MAXNAMELEN);
+ strcpy(t->target.u.name, IPT_STANDARD_TARGET);
+ t->verdict = verdict;
+
+ return 1;
+}
+
+static int
+map_target(const iptc_handle_t handle,
+ struct ipt_entry *e,
+ unsigned int offset,
+ struct ipt_entry_target *old)
+{
+ struct ipt_entry_target *t = ipt_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.name, "") == 0)
+ return standard_map(e, offset + e->next_offset);
+ /* Maybe it's a standard target name... */
+ else if (strcmp(t->u.name, IPTC_LABEL_ACCEPT) == 0)
+ return standard_map(e, -NF_ACCEPT - 1);
+ else if (strcmp(t->u.name, IPTC_LABEL_DROP) == 0)
+ return standard_map(e, -NF_DROP - 1);
+ else if (strcmp(t->u.name, IPTC_LABEL_QUEUE) == 0)
+ return standard_map(e, -NF_QUEUE - 1);
+ else if (strcmp(t->u.name, IPTC_LABEL_RETURN) == 0)
+ return standard_map(e, IPT_RETURN);
+ else if (iptc_builtin(t->u.name, handle)) {
+ /* Can't jump to builtins. */
+ errno = EINVAL;
+ return 0;
+ } else {
+ /* Maybe it's an existing chain name. */
+ unsigned int exists;
+
+ if (find_label(&exists, t->u.name, handle))
+ return standard_map(e, exists);
+ }
+
+ /* Must be a module? If not, kernel will reject... */
+ /* memset to all 0 for your memcmp convenience. */
+ memset(t->u.name + strlen(t->u.name),
+ 0,
+ IPT_FUNCTION_MAXNAMELEN - strlen(t->u.name));
+ return 1;
+}
+
+static void
+unmap_target(struct ipt_entry *e, struct ipt_entry_target *old)
+{
+ struct ipt_entry_target *t = ipt_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
+iptc_insert_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle)
+{
+ unsigned int chainoff, chainindex, offset;
+ struct ipt_entry_target old;
+ int ret;
+
+ CHECK(*handle);
+ iptc_fn = iptc_insert_entry;
+ if (!find_label(&chainoff, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ chainindex = entry2index(*handle, get_entry(*handle, chainoff));
+
+ if (index2entry(*handle, chainindex + rulenum)
+ > get_entry(*handle, get_chain_end(*handle, chainoff))) {
+ 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 ipt_entry *)e, offset, &old))
+ return 0;
+
+ ret = insert_rules(1, e->next_offset, e, offset,
+ chainindex + rulenum, rulenum == 0, handle);
+ unmap_target((struct ipt_entry *)e, &old);
+ CHECK(*handle);
+ return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+iptc_replace_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ unsigned int rulenum,
+ iptc_handle_t *handle)
+{
+ unsigned int chainoff, chainindex, offset;
+ struct ipt_entry_target old;
+ int ret;
+
+ CHECK(*handle);
+ iptc_fn = iptc_replace_entry;
+
+ if (!find_label(&chainoff, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ chainindex = entry2index(*handle, get_entry(*handle, chainoff));
+
+ if (index2entry(*handle, chainindex + rulenum)
+ >= get_entry(*handle, get_chain_end(*handle, chainoff))) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ offset = index2offset(*handle, chainindex + rulenum);
+ /* Replace = delete and insert. */
+ if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
+ offset, chainindex + rulenum, handle))
+ return 0;
+
+ if (!map_target(*handle, (struct ipt_entry *)e, offset, &old))
+ return 0;
+ CHECK(*handle);
+
+ ret = insert_rules(1, e->next_offset, e, offset,
+ chainindex + rulenum, 1, handle);
+ unmap_target((struct ipt_entry *)e, &old);
+ CHECK(*handle);
+ return ret;
+}
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+ rulenum = length of chain. */
+int
+iptc_append_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *e,
+ iptc_handle_t *handle)
+{
+ unsigned int startoff, endoff;
+ struct ipt_entry_target old;
+ int ret;
+
+ CHECK(*handle);
+ iptc_fn = iptc_append_entry;
+ if (!find_label(&startoff, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ endoff = get_chain_end(*handle, startoff);
+ if (!map_target(*handle, (struct ipt_entry *)e, endoff, &old))
+ return 0;
+
+ ret = insert_rules(1, e->next_offset, e, endoff,
+ entry2index(*handle, get_entry(*handle, endoff)),
+ 0, handle);
+ unmap_target((struct ipt_entry *)e, &old);
+ CHECK(*handle);
+ return ret;
+}
+
+static inline int
+match_different(const struct ipt_entry_match *a,
+ const char *a_elems,
+ const char *b_elems)
+{
+ const struct ipt_entry_match *b;
+
+ /* Offset of b is the same as a. */
+ b = (void *)b_elems + (a_elems - (char *)a);
+
+ if (a->match_size != b->match_size)
+ return 1;
+
+ if (strcmp(a->u.name, b->u.name) != 0)
+ return 1;
+
+ /* FIXME: If kernel modifies these (eg. RATE), then we'll
+ never match --RR */
+ if (memcmp(a->data, b->data, a->match_size - sizeof(*a)) != 0)
+ return 1;
+
+ return 0;
+}
+
+static inline int
+is_same(const struct ipt_entry *a, const struct ipt_entry *b)
+{
+ unsigned int i;
+ struct ipt_entry_target *ta, *tb;
+
+ 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.proto != b->ip.proto
+ || a->ip.flags != b->ip.flags
+ || a->ip.invflags != b->ip.invflags)
+ return 0;
+
+ for (i = 0; i < IFNAMSIZ; i++) {
+ if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+ return 0;
+ if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+ != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+ return 0;
+ if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+ return 0;
+ if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+ != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+ return 0;
+ }
+
+ if (a->nfcache != b->nfcache
+ || a->target_offset != b->target_offset
+ || a->next_offset != b->next_offset)
+ return 0;
+
+ if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems))
+ return 0;
+
+ ta = ipt_get_target((struct ipt_entry *)a);
+ tb = ipt_get_target((struct ipt_entry *)b);
+ if (ta->target_size != tb->target_size)
+ return 0;
+ if (strcmp(ta->u.name, tb->u.name) != 0)
+ return 0;
+ /* FIXME: If kernel modifies these, then we never match --RR */
+ if (memcmp(ta->data, tb->data, ta->target_size - sizeof(*ta)) != 0)
+ return 0;
+
+ return 1;
+}
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+iptc_delete_entry(const ipt_chainlabel chain,
+ const struct ipt_entry *origfw,
+ iptc_handle_t *handle)
+{
+ unsigned int offset, lastoff;
+ struct ipt_entry *e, *fw;
+
+ CHECK(*handle);
+ iptc_fn = iptc_delete_entry;
+ if (!find_label(&offset, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ fw = malloc(origfw->next_offset);
+ if (fw == NULL) {
+ errno = ENOMEM;
+ return 0;
+ }
+ lastoff = get_chain_end(*handle, offset);
+
+ for (; offset < lastoff; offset += e->next_offset) {
+ struct ipt_entry_target discard;
+
+ memcpy(fw, origfw, origfw->next_offset);
+
+ /* FIXME: handle this in is_same --RR */
+ if (!map_target(*handle, fw, offset, &discard)) {
+ free(fw);
+ return 0;
+ }
+ e = get_entry(*handle, offset);
+
+#if 0
+ printf("Deleting:\n");
+ dump_entry(newe);
+#endif
+ if (is_same(e, fw)) {
+ int ret;
+ ret = delete_rules(1, e->next_offset,
+ offset, entry2index(*handle, e),
+ handle);
+ free(fw);
+ CHECK(*handle);
+ return ret;
+ }
+ }
+
+ free(fw);
+ errno = ENOENT;
+ return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+iptc_delete_num_entry(const ipt_chainlabel chain,
+ unsigned int rulenum,
+ iptc_handle_t *handle)
+{
+ unsigned int chainstart;
+ unsigned int index;
+ int ret;
+ struct ipt_entry *e;
+
+ CHECK(*handle);
+ iptc_fn = iptc_delete_num_entry;
+ if (!find_label(&chainstart, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ index = entry2index(*handle, get_entry(*handle, chainstart))
+ + rulenum;
+
+ if (index
+ >= entry2index(*handle,
+ get_entry(*handle,
+ get_chain_end(*handle, chainstart)))) {
+ errno = E2BIG;
+ return 0;
+ }
+
+ e = index2entry(*handle, index);
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+ index, handle);
+ CHECK(*handle);
+ return ret;
+}
+
+/* Check the packet `fw' on chain `chain'. Returns the verdict, or
+ NULL and sets errno. */
+const char *
+iptc_check_packet(const ipt_chainlabel chain,
+ struct ipt_entry *entry,
+ iptc_handle_t *handle)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+iptc_flush_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
+{
+ unsigned int startoff, endoff, startindex, endindex;
+ int ret;
+
+ CHECK(*handle);
+ iptc_fn = iptc_flush_entries;
+ if (!find_label(&startoff, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+ endoff = get_chain_end(*handle, startoff);
+ startindex = entry2index(*handle, get_entry(*handle, startoff));
+ endindex = entry2index(*handle, get_entry(*handle, endoff));
+
+ ret = delete_rules(endindex - startindex,
+ endoff - startoff, startoff, startindex,
+ handle);
+ CHECK(*handle);
+ return ret;
+}
+
+/* Zeroes the counters in a chain. */
+int
+iptc_zero_entries(const ipt_chainlabel chain, iptc_handle_t *handle)
+{
+ unsigned int i, end;
+
+ CHECK(*handle);
+ if (!find_label(&i, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+ end = get_chain_end(*handle, i);
+
+ i = entry2index(*handle, get_entry(*handle, i));
+ end = entry2index(*handle, get_entry(*handle, end));
+
+ for (; i <= end; i++) {
+ if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
+ (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+ }
+ (*handle)->changed = 1;
+
+ CHECK(*handle);
+ return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+iptc_create_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
+{
+ unsigned int pos;
+ int ret;
+ struct {
+ struct ipt_entry head;
+ struct ipt_error_target name;
+ struct ipt_entry ret;
+ struct ipt_standard_target target;
+ } newc;
+
+ CHECK(*handle);
+ iptc_fn = iptc_create_chain;
+
+ /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+ QUEUE, RETURN. */
+ if (find_label(&pos, chain, *handle)
+ || strcmp(chain, IPTC_LABEL_DROP) == 0
+ || strcmp(chain, IPTC_LABEL_ACCEPT) == 0
+ || strcmp(chain, IPTC_LABEL_QUEUE) == 0
+ || strcmp(chain, IPTC_LABEL_RETURN) == 0) {
+ errno = EEXIST;
+ return 0;
+ }
+
+ if (strlen(chain)+1 > sizeof(ipt_chainlabel)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ memset(&newc, 0, sizeof(newc));
+ newc.head.target_offset = sizeof(struct ipt_entry);
+ newc.head.next_offset
+ = sizeof(struct ipt_entry) + sizeof(struct ipt_error_target);
+ strcpy(newc.name.t.u.name, IPT_ERROR_TARGET);
+ newc.name.t.target_size = sizeof(struct ipt_error_target);
+ strcpy(newc.name.error, chain);
+
+ newc.ret.target_offset = sizeof(struct ipt_entry);
+ newc.ret.next_offset
+ = sizeof(struct ipt_entry)+sizeof(struct ipt_standard_target);
+ strcpy(newc.target.target.u.name, IPT_STANDARD_TARGET);
+ newc.target.target.target_size = sizeof(struct ipt_standard_target);
+ newc.target.verdict = IPT_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);
+ CHECK(*handle);
+ return ret;
+}
+
+static int
+count_ref(struct ipt_entry *e, unsigned int offset, unsigned int *ref)
+{
+ struct ipt_standard_target *t;
+
+ if (strcmp(ipt_get_target(e)->u.name, IPT_STANDARD_TARGET) == 0) {
+ t = (struct ipt_standard_target *)ipt_get_target(e);
+
+ if (t->verdict == offset)
+ (*ref)++;
+ }
+
+ return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+iptc_get_references(unsigned int *ref, const ipt_chainlabel chain,
+ iptc_handle_t *handle)
+{
+ unsigned int offset;
+
+ CHECK(*handle);
+ if (!find_label(&offset, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ *ref = 0;
+ IPT_ENTRY_ITERATE((*handle)->entries.entries,
+ (*handle)->entries.size,
+ count_ref, offset, ref);
+ return 1;
+}
+
+/* Deletes a chain. */
+int
+iptc_delete_chain(const ipt_chainlabel chain, iptc_handle_t *handle)
+{
+ unsigned int chainoff, labelidx, labeloff;
+ unsigned int references;
+ struct ipt_entry *e;
+ int ret;
+
+ CHECK(*handle);
+ if (!iptc_get_references(&references, chain, handle))
+ return 0;
+
+ iptc_fn = iptc_delete_chain;
+
+ if (iptc_builtin(chain, *handle)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ if (references > 0) {
+ errno = EMLINK;
+ return 0;
+ }
+
+ if (!find_label(&chainoff, chain, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ e = get_entry(*handle, chainoff);
+ if (get_chain_end(*handle, chainoff) != chainoff) {
+ errno = ENOTEMPTY;
+ return 0;
+ }
+
+ /* Need label index: preceeds chain start */
+ labelidx = entry2index(*handle, e) - 1;
+ labeloff = index2offset(*handle, labelidx);
+
+ ret = delete_rules(2,
+ get_entry(*handle, labeloff)->next_offset
+ + e->next_offset,
+ labeloff, labelidx, handle);
+ CHECK(*handle);
+ return ret;
+}
+
+/* Renames a chain. */
+int iptc_rename_chain(const ipt_chainlabel oldname,
+ const ipt_chainlabel newname,
+ iptc_handle_t *handle)
+{
+ unsigned int chainoff, labeloff, labelidx;
+ struct ipt_error_target *t;
+
+ CHECK(*handle);
+ iptc_fn = iptc_rename_chain;
+
+ /* find_label doesn't cover built-in targets: DROP, ACCEPT
+ RETURN. */
+ if (find_label(&chainoff, newname, *handle)
+ || strcmp(newname, IPTC_LABEL_DROP) == 0
+ || strcmp(newname, IPTC_LABEL_ACCEPT) == 0
+ || strcmp(newname, IPTC_LABEL_RETURN) == 0) {
+ errno = EEXIST;
+ return 0;
+ }
+
+ if (!find_label(&chainoff, oldname, *handle)
+ || iptc_builtin(oldname, *handle)) {
+ errno = ENOENT;
+ return 0;
+ }
+
+ if (strlen(newname)+1 > sizeof(ipt_chainlabel)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /* Need label index: preceeds chain start */
+ labelidx = entry2index(*handle, get_entry(*handle, chainoff)) - 1;
+ labeloff = index2offset(*handle, labelidx);
+
+ t = (struct ipt_error_target *)
+ ipt_get_target(get_entry(*handle, labeloff));
+
+ memset(t->error, 0, sizeof(t->error));
+ strcpy(t->error, newname);
+ (*handle)->changed = 1;
+
+ CHECK(*handle);
+ return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+iptc_set_policy(const ipt_chainlabel chain,
+ const ipt_chainlabel policy,
+ iptc_handle_t *handle)
+{
+ unsigned int hook;
+ unsigned int policyoff;
+ struct ipt_entry *e;
+ struct ipt_standard_target *t;
+
+ CHECK(*handle);
+ /* Figure out which chain. */
+ hook = iptc_builtin(chain, *handle);
+ if (hook == 0) {
+ errno = EINVAL;
+ return 0;
+ } else
+ hook--;
+
+ policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
+ if (policyoff != (*handle)->info.underflow[hook]) {
+ printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
+ chain, policyoff, (*handle)->info.underflow[hook]);
+ return 0;
+ }
+
+ e = get_entry(*handle, policyoff);
+ t = (struct ipt_standard_target *)ipt_get_target(e);
+
+ if (strcmp(policy, IPTC_LABEL_ACCEPT) == 0)
+ t->verdict = -NF_ACCEPT - 1;
+ else if (strcmp(policy, IPTC_LABEL_DROP) == 0)
+ t->verdict = -NF_DROP - 1;
+ else {
+ errno = EINVAL;
+ return 0;
+ }
+ (*handle)->counter_map[entry2index(*handle, e)]
+ = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+ (*handle)->changed = 1;
+
+ CHECK(*handle);
+ return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+ libiptc.c: In function `iptc_commit':
+ libiptc.c:833: fixed or forbidden register was spilled.
+ This may be due to a compiler bug or to impossible asm
+ statements or clauses.
+*/
+static void
+subtract_counters(struct ipt_counters *answer,
+ const struct ipt_counters *a,
+ const struct ipt_counters *b)
+{
+ answer->pcnt = a->pcnt - b->pcnt;
+ answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+iptc_commit(iptc_handle_t *handle)
+{
+ /* Replace, then map back the counters. */
+ struct ipt_replace *repl;
+ struct ipt_counters_info *newcounters;
+ unsigned int i;
+ size_t counterlen
+ = sizeof(struct ipt_counters_info)
+ + sizeof(struct ipt_counters) * (*handle)->new_number;
+
+ CHECK(*handle);
+#if 0
+ dump_entries(*handle);
+#endif
+
+ /* Don't commit if nothing changed. */
+ if (!(*handle)->changed)
+ goto finished;
+
+ repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+ if (!repl) {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ /* These are the old counters we will get from kernel */
+ repl->counters = malloc(sizeof(struct ipt_counters)
+ * (*handle)->info.num_entries);
+ if (!repl->counters) {
+ free(repl);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ /* These are the counters we're going to put back, later. */
+ newcounters = malloc(counterlen);
+ if (!newcounters) {
+ free(repl->counters);
+ free(repl);
+ errno = ENOMEM;
+ return 0;
+ }
+
+ strcpy(repl->name, (*handle)->info.name);
+ repl->num_entries = (*handle)->new_number;
+ repl->size = (*handle)->entries.size;
+ memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+ sizeof(repl->hook_entry));
+ memcpy(repl->underflow, (*handle)->info.underflow,
+ sizeof(repl->underflow));
+ repl->num_counters = (*handle)->info.num_entries;
+ repl->valid_hooks = (*handle)->info.valid_hooks;
+ memcpy(repl->entries, (*handle)->entries.entries,
+ (*handle)->entries.size);
+
+ if (setsockopt(sockfd, IPPROTO_IP, IPT_SO_SET_REPLACE, repl,
+ sizeof(*repl) + (*handle)->entries.size) < 0) {
+ free(repl->counters);
+ free(repl);
+ free(newcounters);
+ return 0;
+ }
+
+ /* Put counters back. */
+ strcpy(newcounters->name, (*handle)->info.name);
+ newcounters->num_counters = (*handle)->new_number;
+ for (i = 0; i < (*handle)->new_number; i++) {
+ unsigned int mappos = (*handle)->counter_map[i].mappos;
+ switch ((*handle)->counter_map[i].maptype) {
+ case COUNTER_MAP_NOMAP:
+ newcounters->counters[i]
+ = ((struct ipt_counters){ 0, 0 });
+ break;
+
+ case COUNTER_MAP_NORMAL_MAP:
+ /* Original read: X.
+ * Atomic read on replacement: X + Y.
+ * Currently in kernel: Z.
+ * Want in kernel: X + Y + Z.
+ * => Add in X + Y
+ * => Add in replacement read.
+ */
+ newcounters->counters[i] = repl->counters[mappos];
+ break;
+
+ case COUNTER_MAP_ZEROED:
+ /* Original read: X.
+ * Atomic read on replacement: X + Y.
+ * Currently in kernel: Z.
+ * Want in kernel: Y + Z.
+ * => Add in Y.
+ * => Add in (replacement read - original read).
+ */
+ subtract_counters(&newcounters->counters[i],
+ &repl->counters[mappos],
+ &index2entry(*handle, i)->counters);
+ break;
+ }
+ }
+
+ if (setsockopt(sockfd, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS,
+ newcounters, counterlen) < 0) {
+ free(repl->counters);
+ free(repl);
+ free(newcounters);
+ return 0;
+ }
+
+ free(repl->counters);
+ free(repl);
+ free(newcounters);
+
+ finished:
+ free(*handle);
+ *handle = NULL;
+ return 1;
+}
+
+/* Get raw socket. */
+int
+iptc_get_raw_socket()
+{
+ return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+iptc_strerror(int err)
+{
+ unsigned int i;
+ struct table_struct {
+ void *fn;
+ int err;
+ const char *message;
+ } table [] =
+ { { NULL, 0, "Incompatible with this kernel" },
+ { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+ { NULL, ENOSYS, "Will be implemented real soon. I promise." },
+ { NULL, ENOMEM, "Memory allocation problem" },
+ { iptc_init, EPERM, "Permission denied (you must be root)" },
+ { iptc_init, EINVAL, "Module is wrong version" },
+ { iptc_delete_chain, ENOTEMPTY, "Chain is not empty" },
+ { iptc_delete_chain, EINVAL, "Can't delete built-in chain" },
+ { iptc_delete_chain, EMLINK,
+ "Can't delete chain with references left" },
+ { iptc_create_chain, EEXIST, "Chain already exists" },
+ { iptc_insert_entry, E2BIG, "Index of insertion too big" },
+ { iptc_replace_entry, E2BIG, "Index of replacement too big" },
+ { iptc_delete_num_entry, E2BIG, "Index of deletion too big" },
+ { iptc_insert_entry, ELOOP, "Loop found in table" },
+ { iptc_insert_entry, EINVAL, "Target problem" },
+ /* EINVAL for CHECK probably means bad interface. */
+ { iptc_check_packet, EINVAL,
+ "bad arguments (does that interface exist?)" },
+ /* ENOENT for DELETE probably means no matching rule */
+ { iptc_delete_entry, ENOENT,
+ "bad rule (does a matching rule exist in that chain?)" },
+ { NULL, ENOENT, "No extended target/match by that name" }
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((!table[i].fn || table[i].fn == iptc_fn)
+ && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+ if (((u_int32_t *)ip)[i])
+ return 0;
+
+ return 1;
+}
+
+static inline int
+check_match(const struct ipt_entry_match *m, unsigned int *off)
+{
+ assert(m->match_size >= sizeof(struct ipt_entry_match));
+
+ (*off) += m->match_size;
+ return 0;
+}
+
+static inline int
+check_entry(const struct ipt_entry *e, unsigned int *i, unsigned int *off,
+ unsigned int user_offset, int *was_return,
+ iptc_handle_t h)
+{
+ unsigned int toff;
+ struct ipt_standard_target *t;
+
+ assert(e->target_offset >= sizeof(struct ipt_entry));
+ assert(e->next_offset >= e->target_offset
+ + sizeof(struct ipt_entry_target));
+ toff = sizeof(struct ipt_entry);
+ IPT_MATCH_ITERATE(e, check_match, &toff);
+
+ assert(toff == e->target_offset);
+
+ t = (struct ipt_standard_target *)
+ ipt_get_target((struct ipt_entry *)e);
+ assert(t->target.target_size == e->next_offset - e->target_offset);
+ assert(!iptc_is_chain(t->target.u.name, h));
+
+ if (strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0) {
+ assert(t->target.target_size
+ == IPT_ALIGN(sizeof(struct ipt_standard_target)));
+
+ assert(t->verdict == -NF_DROP-1
+ || t->verdict == -NF_ACCEPT-1
+ || t->verdict == IPT_RETURN
+ || t->verdict < (int)h->entries.size);
+
+ if (t->verdict >= 0) {
+ struct ipt_entry *te = get_entry(h, t->verdict);
+ int idx;
+
+ idx = entry2index(h, te);
+ assert(strcmp(ipt_get_target(te)->u.name,
+ IPT_ERROR_TARGET)
+ != 0);
+ assert(te != e);
+
+ /* Prior node must be error node, or this node. */
+ assert(t->verdict == entry2offset(h, e)+e->next_offset
+ || strcmp(ipt_get_target(index2entry(h, idx-1))
+ ->u.name, IPT_ERROR_TARGET)
+ == 0);
+ }
+
+ if (t->verdict == IPT_RETURN
+ && unconditional(&e->ip)
+ && e->target_offset == sizeof(*e))
+ *was_return = 1;
+ else
+ *was_return = 0;
+ } else if (strcmp(t->target.u.name, IPT_ERROR_TARGET) == 0) {
+ assert(t->target.target_size
+ == IPT_ALIGN(sizeof(struct ipt_error_target)));
+
+ /* If this is in user area, previous must have been return */
+ if (*off > user_offset)
+ assert(*was_return);
+
+ *was_return = 0;
+ }
+ else *was_return = 0;
+
+ if (*off == user_offset)
+ assert(strcmp(t->target.u.name, IPT_ERROR_TARGET) == 0);
+
+ (*off) += e->next_offset;
+ (*i)++;
+ return 0;
+}
+
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(iptc_handle_t h, unsigned int line)
+{
+ unsigned int i, n;
+ unsigned int user_offset; /* Offset of first user chain */
+ int was_return;
+
+ assert(h->changed == 0 || h->changed == 1);
+ if (strcmp(h->info.name, "filter") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_LOCAL_IN
+ | 1 << NF_IP_FORWARD
+ | 1 << NF_IP_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+ n = get_chain_end(h, 0);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+ n = get_chain_end(h, n);
+ n += get_entry(h, n)->next_offset;
+ assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+ } else if (strcmp(h->info.name, "nat") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_POST_ROUTING
+ | 1 << NF_IP_LOCAL_OUT));
+
+ 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];
+ } else if (strcmp(h->info.name, "mangle") == 0) {
+ assert(h->info.valid_hooks
+ == (1 << NF_IP_PRE_ROUTING
+ | 1 << NF_IP_LOCAL_OUT));
+
+ /* Hooks should be first three */
+ 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_LOCAL_OUT] == n);
+
+ user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+ } else
+ abort();
+
+ /* User chain == end of last builtin + policy entry */
+ user_offset = get_chain_end(h, user_offset);
+ user_offset += get_entry(h, user_offset)->next_offset;
+
+ /* Overflows should be end of entry chains, and unconditional
+ policy nodes. */
+ for (i = 0; i < NF_IP_NUMHOOKS; i++) {
+ struct ipt_entry *e;
+ struct ipt_standard_target *t;
+
+ if (!(h->info.valid_hooks & (1 << i)))
+ continue;
+ assert(h->info.underflow[i]
+ == get_chain_end(h, h->info.hook_entry[i]));
+
+ e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+ assert(unconditional(&e->ip));
+ assert(e->target_offset == sizeof(*e));
+ assert(e->next_offset == sizeof(*e) + sizeof(*t));
+ t = (struct ipt_standard_target *)ipt_get_target(e);
+
+ assert(strcmp(t->target.u.name, IPT_STANDARD_TARGET) == 0);
+ assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+ /* Hooks and underflows must be valid entries */
+ entry2index(h, get_entry(h, h->info.hook_entry[i]));
+ entry2index(h, get_entry(h, h->info.underflow[i]));
+ }
+
+ assert(h->info.size
+ >= h->info.num_entries * (sizeof(struct ipt_entry)
+ +sizeof(struct ipt_standard_target)));
+
+ assert(h->entries.size
+ >= (h->new_number
+ * (sizeof(struct ipt_entry)
+ + sizeof(struct ipt_standard_target))));
+ assert(strcmp(h->info.name, h->entries.name) == 0);
+
+ i = 0; n = 0;
+ was_return = 0;
+ /* Check all the entries. */
+ IPT_ENTRY_ITERATE(h->entries.entries, h->entries.size,
+ check_entry, &i, &n, user_offset, &was_return, h);
+
+ assert(i == h->new_number);
+ assert(n == h->entries.size);
+
+ /* Final entry must be error node */
+ assert(strcmp(ipt_get_target(index2entry(h, h->new_number-1))->u.name,
+ IPT_ERROR_TARGET) == 0);
+}