iptables-1.2.9-2.3.1.src.rpm
authorMark Huang <mlhuang@cs.princeton.edu>
Fri, 29 Oct 2004 15:06:42 +0000 (15:06 +0000)
committerMark Huang <mlhuang@cs.princeton.edu>
Fri, 29 Oct 2004 15:06:42 +0000 (15:06 +0000)
217 files changed:
COPYING [new file with mode: 0644]
INCOMPATIBILITIES [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
Rules.make [new file with mode: 0644]
extensions/.BALANCE-test [new file with mode: 0755]
extensions/.FTOS-test [new file with mode: 0755]
extensions/.IPMARK-test [new file with mode: 0755]
extensions/.IPV4OPTSSTRIP-test [new file with mode: 0755]
extensions/.NETLINK-test [new file with mode: 0755]
extensions/.REJECT-test6 [new file with mode: 0755]
extensions/.ROUTE-test [new file with mode: 0755]
extensions/.ROUTE-test6 [new file with mode: 0755]
extensions/.TCPLAG-test [new file with mode: 0755]
extensions/.XOR-test [new file with mode: 0755]
extensions/.ah-test6 [new file with mode: 0755]
extensions/.condition-test [new file with mode: 0755]
extensions/.condition-test6 [new file with mode: 0755]
extensions/.connbytes-test [new file with mode: 0755]
extensions/.esp-test6 [new file with mode: 0755]
extensions/.frag-test6 [new file with mode: 0755]
extensions/.fuzzy-test [new file with mode: 0755]
extensions/.fuzzy-test6 [new file with mode: 0755]
extensions/.ipv4options-test [new file with mode: 0755]
extensions/.ipv6header-test6 [new file with mode: 0755]
extensions/.mport-test [new file with mode: 0755]
extensions/.nth-test [new file with mode: 0755]
extensions/.nth-test6 [new file with mode: 0755]
extensions/.opts-test6 [new file with mode: 0755]
extensions/.osf-test [new file with mode: 0755]
extensions/.psd-test [new file with mode: 0755]
extensions/.quota-test [new file with mode: 0755]
extensions/.random-test [new file with mode: 0755]
extensions/.random-test6 [new file with mode: 0755]
extensions/.recent-test [new file with mode: 0755]
extensions/.record-rpc-test [new file with mode: 0755]
extensions/.rt-test6 [new file with mode: 0755]
extensions/.string-test [new file with mode: 0755]
extensions/.time-test [new file with mode: 0755]
extensions/.u32-test [new file with mode: 0755]
extensions/Makefile [new file with mode: 0644]
extensions/libip6t_HL.c [new file with mode: 0644]
extensions/libip6t_LOG.c [new file with mode: 0644]
extensions/libip6t_MARK.c [new file with mode: 0644]
extensions/libip6t_REJECT.c [new file with mode: 0644]
extensions/libip6t_ROUTE.c [new file with mode: 0644]
extensions/libip6t_TRACE.c [new file with mode: 0644]
extensions/libip6t_ah.c [new file with mode: 0644]
extensions/libip6t_condition.c [new file with mode: 0644]
extensions/libip6t_dst.c [new file with mode: 0644]
extensions/libip6t_esp.c [new file with mode: 0644]
extensions/libip6t_eui64.c [new file with mode: 0644]
extensions/libip6t_frag.c [new file with mode: 0644]
extensions/libip6t_fuzzy.c [new file with mode: 0644]
extensions/libip6t_hbh.c [new file with mode: 0644]
extensions/libip6t_hl.c [new file with mode: 0644]
extensions/libip6t_icmpv6.c [new file with mode: 0644]
extensions/libip6t_ipv6header.c [new file with mode: 0644]
extensions/libip6t_length.c [new file with mode: 0644]
extensions/libip6t_limit.c [new file with mode: 0644]
extensions/libip6t_mac.c [new file with mode: 0644]
extensions/libip6t_mark.c [new file with mode: 0644]
extensions/libip6t_multiport.c [new file with mode: 0644]
extensions/libip6t_nth.c [new file with mode: 0644]
extensions/libip6t_owner.c [new file with mode: 0644]
extensions/libip6t_random.c [new file with mode: 0644]
extensions/libip6t_rt.c [new file with mode: 0644]
extensions/libip6t_standard.c [new file with mode: 0644]
extensions/libip6t_tcp.c [new file with mode: 0644]
extensions/libip6t_udp.c [new file with mode: 0644]
extensions/libipt_BALANCE.c [new file with mode: 0644]
extensions/libipt_CLASSIFY.c [new file with mode: 0644]
extensions/libipt_CONNMARK.c [new file with mode: 0644]
extensions/libipt_DNAT.c [new file with mode: 0644]
extensions/libipt_DSCP.c [new file with mode: 0644]
extensions/libipt_ECN.c [new file with mode: 0644]
extensions/libipt_FTOS.c [new file with mode: 0644]
extensions/libipt_IPMARK.c [new file with mode: 0644]
extensions/libipt_IPV4OPTSSTRIP.c [new file with mode: 0644]
extensions/libipt_LOG.c [new file with mode: 0644]
extensions/libipt_MARK.c [new file with mode: 0644]
extensions/libipt_MASQUERADE.c [new file with mode: 0644]
extensions/libipt_MIRROR.c [new file with mode: 0644]
extensions/libipt_NETLINK.c [new file with mode: 0644]
extensions/libipt_NETMAP.c [new file with mode: 0644]
extensions/libipt_NOTRACK.c [new file with mode: 0644]
extensions/libipt_REDIRECT.c [new file with mode: 0644]
extensions/libipt_REJECT.c [new file with mode: 0644]
extensions/libipt_ROUTE.c [new file with mode: 0644]
extensions/libipt_SAME.c [new file with mode: 0644]
extensions/libipt_SNAT.c [new file with mode: 0644]
extensions/libipt_TARPIT.c [new file with mode: 0644]
extensions/libipt_TCPLAG.c [new file with mode: 0644]
extensions/libipt_TCPMSS.c [new file with mode: 0644]
extensions/libipt_TOS.c [new file with mode: 0644]
extensions/libipt_TRACE.c [new file with mode: 0644]
extensions/libipt_TTL.c [new file with mode: 0644]
extensions/libipt_ULOG.c [new file with mode: 0644]
extensions/libipt_XOR.c [new file with mode: 0644]
extensions/libipt_addrtype.c [new file with mode: 0644]
extensions/libipt_ah.c [new file with mode: 0644]
extensions/libipt_condition.c [new file with mode: 0644]
extensions/libipt_connbytes.c [new file with mode: 0644]
extensions/libipt_connlimit.c [new file with mode: 0644]
extensions/libipt_connmark.c [new file with mode: 0644]
extensions/libipt_conntrack.c [new file with mode: 0644]
extensions/libipt_dscp.c [new file with mode: 0644]
extensions/libipt_dscp_helper.c [new file with mode: 0644]
extensions/libipt_ecn.c [new file with mode: 0644]
extensions/libipt_esp.c [new file with mode: 0644]
extensions/libipt_fuzzy.c [new file with mode: 0644]
extensions/libipt_helper.c [new file with mode: 0644]
extensions/libipt_icmp.c [new file with mode: 0644]
extensions/libipt_iprange.c [new file with mode: 0644]
extensions/libipt_ipv4options.c [new file with mode: 0644]
extensions/libipt_length.c [new file with mode: 0644]
extensions/libipt_limit.c [new file with mode: 0644]
extensions/libipt_mac.c [new file with mode: 0644]
extensions/libipt_mark.c [new file with mode: 0644]
extensions/libipt_mport.c [new file with mode: 0644]
extensions/libipt_multiport.c [new file with mode: 0644]
extensions/libipt_nth.c [new file with mode: 0644]
extensions/libipt_osf.c [new file with mode: 0644]
extensions/libipt_owner.c [new file with mode: 0644]
extensions/libipt_physdev.c [new file with mode: 0644]
extensions/libipt_pkttype.c [new file with mode: 0644]
extensions/libipt_psd.c [new file with mode: 0644]
extensions/libipt_quota.c [new file with mode: 0644]
extensions/libipt_random.c [new file with mode: 0644]
extensions/libipt_realm.c [new file with mode: 0644]
extensions/libipt_recent.c [new file with mode: 0644]
extensions/libipt_record_rpc.c [new file with mode: 0644]
extensions/libipt_rpc.c [new file with mode: 0644]
extensions/libipt_sctp.c [new file with mode: 0644]
extensions/libipt_standard.c [new file with mode: 0644]
extensions/libipt_state.c [new file with mode: 0644]
extensions/libipt_string.c [new file with mode: 0644]
extensions/libipt_tcp.c [new file with mode: 0644]
extensions/libipt_tcpmss.c [new file with mode: 0644]
extensions/libipt_time.c [new file with mode: 0644]
extensions/libipt_tos.c [new file with mode: 0644]
extensions/libipt_ttl.c [new file with mode: 0644]
extensions/libipt_u32.c [new file with mode: 0644]
extensions/libipt_udp.c [new file with mode: 0644]
extensions/libipt_unclean.c [new file with mode: 0644]
include/ip6tables.h [new file with mode: 0644]
include/iptables.h [new file with mode: 0644]
include/iptables_common.h [new file with mode: 0644]
include/libipq/ip_queue_64.h [new file with mode: 0644]
include/libipq/libipq.h [new file with mode: 0644]
include/libiptc/ipt_kernel_headers.h [new file with mode: 0644]
include/libiptc/libip6tc.h [new file with mode: 0644]
include/libiptc/libiptc.h [new file with mode: 0644]
include/libipulog/libipulog.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CLASSIFY.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_CONNMARK.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_DSCP.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ECN.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_FTOS.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_SAME.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_TCPMSS.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_TTL.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ULOG.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ah.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connlimit.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_connmark.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_conntrack.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_dscp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ecn.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_esp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_helper.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_iprange.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_length.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_physdev.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_pkttype.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_realm.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_rpc.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_sctp.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_tcpmss.h [new file with mode: 0644]
include/linux/netfilter_ipv4/ipt_ttl.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_HL.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_REJECT.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_ah.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_esp.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_frag.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_hl.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_length.h [new file with mode: 0644]
include/linux/netfilter_ipv6/ip6t_owner.h [new file with mode: 0644]
ip6tables-restore.8 [new file with mode: 0644]
ip6tables-restore.c [new file with mode: 0644]
ip6tables-save.8 [new file with mode: 0644]
ip6tables-save.c [new file with mode: 0644]
ip6tables-standalone.c [new file with mode: 0644]
ip6tables.c [new file with mode: 0644]
iptables-restore.8 [new file with mode: 0644]
iptables-restore.c [new file with mode: 0644]
iptables-save.8 [new file with mode: 0644]
iptables-save.c [new file with mode: 0644]
iptables-standalone.c [new file with mode: 0644]
iptables.c [new file with mode: 0644]
libipq/Makefile [new file with mode: 0644]
libipq/ipq_create_handle.3 [new file with mode: 0644]
libipq/ipq_destroy_handle.3 [new file with mode: 0644]
libipq/ipq_errstr.3 [new file with mode: 0644]
libipq/ipq_get_msgerr.3 [new file with mode: 0644]
libipq/ipq_get_packet.3 [new file with mode: 0644]
libipq/ipq_message_type.3 [new file with mode: 0644]
libipq/ipq_perror.3 [new file with mode: 0644]
libipq/ipq_read.3 [new file with mode: 0644]
libipq/ipq_set_mode.3 [new file with mode: 0644]
libipq/ipq_set_verdict.3 [new file with mode: 0644]
libipq/libipq.3 [new file with mode: 0644]
libipq/libipq.c [new file with mode: 0644]
libiptc/Makefile [new file with mode: 0644]
libiptc/libip4tc.c [new file with mode: 0644]
libiptc/libip6tc.c [new file with mode: 0644]
libiptc/libiptc.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       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/INCOMPATIBILITIES b/INCOMPATIBILITIES
new file mode 100644 (file)
index 0000000..fd695e1
--- /dev/null
@@ -0,0 +1,6 @@
+INCOMPATIBILITIES:
+
+- The REJECT target has an '--reject-with admin-prohib' option which used
+  with kernels that do not support it, will result in a plain DROP instead
+  of REJECT.  Use with caution.
+  Kernels that do support it:
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..3455fa6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,41 @@
+FOLLOW THESE STEPS:
+
+0) There may be some outstanding bugfixes or tweaks which are not yet
+   in the official kernel.  Those are now (as of iptables-1.2.7) kept
+   in a seperate package, called patch-o-matic.  It is available from
+   ftp://ftp.netfilter.org/pub/patch-o-matic/
+
+1) Next, make the package.
+       % make KERNEL_DIR=<<where-you-built-your-kernel>>
+
+2) Finally, you need to to install the shared libraries, and the binary:
+       # make install KERNEL_DIR=<<where-you-built-your-kernel>>
+
+If you are a developer, you can install the headers, development libraries
+and associated development man pages, with:
+       # make install-devel
+
+That's it!
+================================================================
+PROBLEMS YOU MAY ENCOUNTER:
+
+1) This package requires a 2.4.4 kernel, or above.
+
+2) If you get the kernel directory wrong, you may see a message like:
+       Please try `make KERNEL_DIR=path-to-correct-kernel'
+
+3) If you want to specify alternate directories for installation
+(instead of /usr/ bin lib man), do this:
+
+       % make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man
+       # make BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/man install
+
+4) If you want to build a statically linked version of the iptables binary,
+   without the need for loading the plugins at runtime (e.g. for an embedded
+   device or router-on-a-disk), please use
+
+       % make NO_SHARED_LIBS=1
+
+NOTE: make sure you build with at least the correct LIBDIR=
+specification, otherwise iptables(8) won't know where to find the
+dynamic objects.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..de4e199
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,208 @@
+# uncomment this to get a fully statically linked version
+# NO_SHARED_LIBS = 1
+
+# uncomment this to disable IPv6 support
+# DO_IPV6 = 0
+
+######################################################################
+# YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE
+######################################################################
+
+# Standard part of Makefile for topdir.
+TOPLEVEL_INCLUDED=YES
+
+ifndef KERNEL_DIR
+KERNEL_DIR=/usr/src/linux
+endif
+IPTABLES_VERSION:=1.2.9
+OLD_IPTABLES_VERSION:=1.2.8
+
+PREFIX:=/usr
+LIBDIR:=$(PREFIX)/lib
+BINDIR:=$(PREFIX)/sbin
+MANDIR:=$(PREFIX)/man
+INCDIR:=$(PREFIX)/include
+
+# directory for new iptables releases
+RELEASE_DIR:=/tmp
+
+# Need libc6 for this.  FIXME: Should covert to autoconf.
+ifeq ($(shell [ -f /usr/include/netinet/ip6.h ] && echo YES), YES)
+DO_IPV6:=1
+endif
+
+COPT_FLAGS:=-O2
+CFLAGS:=$(COPT_FLAGS) -Wall -Wunused -I$(KERNEL_DIR)/include -Iinclude/ -DIPTABLES_VERSION=\"$(IPTABLES_VERSION)\" #-g -DDEBUG #-pg # -DIPTC_DEBUG
+
+ifdef NO_SHARED_LIBS
+CFLAGS += -DNO_SHARED_LIBS=1
+endif
+
+ifndef NO_SHARED_LIBS
+DEPFILES = $(SHARED_LIBS:%.so=%.d)
+SH_CFLAGS:=$(CFLAGS) -fPIC
+STATIC_LIBS  =
+STATIC6_LIBS =
+LDFLAGS      = -rdynamic
+LDLIBS       = -ldl
+else
+DEPFILES = $(EXT_OBJS:%.o=%.d)
+STATIC_LIBS  = extensions/libext.a
+STATIC6_LIBS = extensions/libext6.a
+LDFLAGS      = -static
+LDLIBS       =
+endif
+
+EXTRAS+=iptables iptables.o
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables $(DESTDIR)$(MANDIR)/man8/iptables.8
+
+# No longer experimental.
+EXTRAS+=iptables-save iptables-restore
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/iptables-save $(DESTDIR)$(BINDIR)/iptables-restore $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8
+
+ifeq ($(DO_IPV6), 1)
+EXTRAS+=ip6tables ip6tables.o
+EXTRA_INSTALLS+=$(DESTDIR)$(BINDIR)/ip6tables $(DESTDIR)$(MANDIR)/man8/ip6tables.8
+EXTRAS_EXP+=ip6tables-save ip6tables-restore
+EXTRA_INSTALLS_EXP+=$(DESTDIR)$(BINDIR)/ip6tables-save $(DESTDIR)$(BINDIR)/ip6tables-restore # $(DESTDIR)$(MANDIR)/man8/iptables-restore.8 $(DESTDIR)$(MANDIR)/man8/iptables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-save.8 $(DESTDIR)$(MANDIR)/man8/ip6tables-restore.8
+endif
+
+# Sparc64 hack
+ifeq ($(shell uname -m),sparc64)
+# The kernel is 64-bit, even though userspace is 32.
+CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+# HPPA hack
+ifeq ($(shell uname -m),parisc64)
+# The kernel is 64-bit, even though userspace is 32.
+CFLAGS+=-DIPT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32
+endif
+
+ifndef IPT_LIBDIR
+IPT_LIBDIR:=$(LIBDIR)/iptables
+endif
+
+.PHONY: default
+default: print-extensions all
+
+.PHONY: print-extensions
+print-extensions:
+       @[ -n "$(OPTIONALS)" ] && echo Extensions found: $(OPTIONALS)
+
+iptables.o: iptables.c
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+iptables: iptables-standalone.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables: iptables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+iptables-save: iptables-save.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables-save: iptables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+iptables-restore: iptables-restore.c iptables.o $(STATIC_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIPT_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/iptables-restore: iptables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables.o: ip6tables.c
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" -c -o $@ $<
+
+ip6tables: ip6tables-standalone.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables: ip6tables
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-save: ip6tables-save.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-save: ip6tables-save
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+ip6tables-restore: ip6tables-restore.c ip6tables.o $(STATIC6_LIBS) libiptc/libiptc.a
+       $(CC) $(CFLAGS) -DIP6T_LIB_DIR=\"$(IPT_LIBDIR)\" $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+$(DESTDIR)$(BINDIR)/ip6tables-restore: ip6tables-restore
+       @[ -d $(DESTDIR)$(BINDIR) ] || mkdir -p $(DESTDIR)$(BINDIR)
+       cp $< $@
+
+$(DESTDIR)$(MANDIR)/man8/%.8: %.8
+       @[ -d $(DESTDIR)$(MANDIR)/man8 ] || mkdir -p $(DESTDIR)$(MANDIR)/man8
+       cp $< $@
+
+EXTRA_DEPENDS+=iptables-standalone.d iptables.d
+
+iptables-standalone.d iptables.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d $*.o:@' > $@
+
+
+# Development Targets
+.PHONY: install-devel-man3
+install-devel-man3: $(DEVEL_MAN3)
+       @[ -d $(DESTDIR)$(MANDIR)/man3 ] || mkdir -p $(DESTDIR)$(MANDIR)/man3
+       @cp -v $(DEVEL_MAN3) $(DESTDIR)$(MANDIR)/man3
+
+.PHONY: install-devel-headers
+install-devel-headers: $(DEVEL_HEADERS)
+       @[ -d $(DESTDIR)$(INCDIR) ] || mkdir -p $(DESTDIR)$(INCDIR)
+       @cp -v $(DEVEL_HEADERS) $(DESTDIR)$(INCDIR)
+
+.PHONY: install-devel-libs
+install-devel-libs: $(DEVEL_LIBS)
+       @[ -d $(DESTDIR)$(LIBDIR) ] || mkdir -p $(DESTDIR)$(LIBDIR)
+       @cp -v $(DEVEL_LIBS) $(DESTDIR)$(LIBDIR)
+
+.PHONY: install-devel
+install-devel: all install-devel-man3 install-devel-headers install-devel-libs
+
+.PHONY: distclean
+distclean: clean
+       @rm -f TAGS `find . -name '*~' -o -name '.*~'` `find . -name '*.rej'` `find . -name '*.d'` .makefirst
+
+# Rusty's distro magic.
+.PHONY: distrib
+distrib: check distclean delrelease $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2 diff md5sums # nowhitespace
+
+# Makefile must not define:
+# -g -pg -DIPTC_DEBUG
+.PHONY: check
+check:
+       @if echo $(CFLAGS) | egrep -e '-g|-pg|IPTC_DEBUG' >/dev/null; then echo Remove debugging flags; exit 1; else exit 0; fi
+
+.PHONY: nowhitespace
+nowhitespace:
+       @if grep -n '[  ]$$' `find . -name 'Makefile' -o -name '*.[ch]'`; then exit 1; else exit 0; fi
+
+.PHONY: delrelease
+delrelease:
+       rm -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+
+$(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2:
+       cd .. && ln -sf userspace iptables-$(IPTABLES_VERSION) && tar cvf - --exclude CVS iptables-$(IPTABLES_VERSION)/. | bzip2 -9 > $@ && rm iptables-$(IPTABLES_VERSION)
+
+.PHONY: diff
+diff: $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @mkdir /tmp/diffdir
+       @cd /tmp/diffdir && tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(IPTABLES_VERSION).tar.bz2
+       @set -e; cd /tmp/diffdir; tar -x --bzip2 -f $(RELEASE_DIR)/iptables-$(OLD_IPTABLES_VERSION).tar.bz2; echo Creating patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2; diff -urN iptables-$(OLD_IPTABLES_VERSION) iptables-$(IPTABLES_VERSION) | bzip2 -9 > $(RELEASE_DIR)/patch-iptables-$(OLD_IPTABLES_VERSION)-$(IPTABLES_VERSION).bz2
+       @rm -rf /tmp/diffdir
+
+.PHONY: md5sums
+md5sums:
+       cd $(RELEASE_DIR)/ && md5sum patch-iptables-*-$(IPTABLES_VERSION).bz2 iptables-$(IPTABLES_VERSION).tar.bz2
+
+# $(wildcard) fails wierdly with make v.3.78.1.
+include $(shell echo */Makefile)
+include Rules.make
diff --git a/Rules.make b/Rules.make
new file mode 100644 (file)
index 0000000..e8aa160
--- /dev/null
@@ -0,0 +1,51 @@
+#! /usr/bin/make
+
+all: $(SHARED_LIBS) $(EXTRAS)
+
+experimental: $(EXTRAS_EXP)
+
+# Have to handle extensions which no longer exist.
+clean: $(EXTRA_CLEANS)
+       rm -f $(SHARED_LIBS) $(EXTRAS) $(EXTRAS_EXP) $(SHARED_LIBS:%.so=%_sh.o)
+       rm -f extensions/initext.c extensions/initext6.c
+       @find . -name '*.[ao]' -o -name '*.so' | xargs rm -f
+
+install: all $(EXTRA_INSTALLS)
+       @if [ -f /usr/bin/iptables -a "$(BINDIR)" = "/usr/sbin" ];\
+       then echo 'Erasing iptables from old location (now /usr/sbin).';\
+       rm -f /usr/bin/iptables;\
+       fi
+
+install-experimental: $(EXTRA_INSTALLS_EXP)
+
+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 $*_sh.o:@' > $@
+
+$(SHARED_LIBS): %.so : %_sh.o
+       $(LD) -shared -o $@ $<
+
+%_sh.o : %.c
+       $(CC) $(SH_CFLAGS) -o $@ -c $<
+
+.makefirst:
+       @echo Making dependencies: please wait...
+       @touch .makefirst
+
+# This is useful for when dependencies completely screwed
+%.h::
+       @echo Something wrong... deleting dependencies.
+       @-rm -f $(DEPFILES) $(EXTRA_DEPENDS) .makefirst
+       @[ -d $(KERNEL_DIR)/include/linux/netfilter_ipv4 ] || echo -e '\n\n    Please try `make KERNEL_DIR=path-to-correct-kernel'\'.'\n\n'
+       @exit 1
+
+-include $(DEPFILES) $(EXTRA_DEPENDS)
+-include .makefirst
diff --git a/extensions/.BALANCE-test b/extensions/.BALANCE-test
new file mode 100755 (executable)
index 0000000..3a46d74
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_BALANCE.c ] && echo BALANCE
diff --git a/extensions/.FTOS-test b/extensions/.FTOS-test
new file mode 100755 (executable)
index 0000000..d07fce7
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_FTOS.h ] && echo FTOS
diff --git a/extensions/.IPMARK-test b/extensions/.IPMARK-test
new file mode 100755 (executable)
index 0000000..7996c88
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if IPMARK patch is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_IPMARK.h ] && echo IPMARK
diff --git a/extensions/.IPV4OPTSSTRIP-test b/extensions/.IPV4OPTSSTRIP-test
new file mode 100755 (executable)
index 0000000..cfd84ee
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if IPV4OPTSSTRIP patch is applied.
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_IPV4OPTSSTRIP.c ] && echo IPV4OPTSSTRIP
diff --git a/extensions/.NETLINK-test b/extensions/.NETLINK-test
new file mode 100755 (executable)
index 0000000..fe94c0c
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_NETLINK.c ] && echo NETLINK
diff --git a/extensions/.REJECT-test6 b/extensions/.REJECT-test6
new file mode 100755 (executable)
index 0000000..1f09694
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+FILE=$KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_REJECT.h
+# True if REJECT is applied.
+[ -f $FILE ] && grep IP6T_ICMP6_NO_ROUTE 2>&1 >/dev/null $FILE && echo REJECT
diff --git a/extensions/.ROUTE-test b/extensions/.ROUTE-test
new file mode 100755 (executable)
index 0000000..8b7b3f0
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_ROUTE.c ] && echo ROUTE
diff --git a/extensions/.ROUTE-test6 b/extensions/.ROUTE-test6
new file mode 100755 (executable)
index 0000000..7994970
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_ROUTE.h ] && echo ROUTE
diff --git a/extensions/.TCPLAG-test b/extensions/.TCPLAG-test
new file mode 100755 (executable)
index 0000000..248f128
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_TCPLAG.c ] && echo TCPLAG
diff --git a/extensions/.XOR-test b/extensions/.XOR-test
new file mode 100755 (executable)
index 0000000..92707da
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_XOR.c ] && echo XOR
diff --git a/extensions/.ah-test6 b/extensions/.ah-test6
new file mode 100755 (executable)
index 0000000..1812c56
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_ah.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_ah.h ] && echo ah
diff --git a/extensions/.condition-test b/extensions/.condition-test
new file mode 100755 (executable)
index 0000000..20f3bc7
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if condition is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_condition.h ] && echo condition
diff --git a/extensions/.condition-test6 b/extensions/.condition-test6
new file mode 100755 (executable)
index 0000000..f4af61f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if condition6 is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_condition.h ] && echo condition
diff --git a/extensions/.connbytes-test b/extensions/.connbytes-test
new file mode 100755 (executable)
index 0000000..350140f
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_connbytes.c ] && echo connbytes
diff --git a/extensions/.esp-test6 b/extensions/.esp-test6
new file mode 100755 (executable)
index 0000000..ff63ca4
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_esp.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_esp.h ] && echo esp
diff --git a/extensions/.frag-test6 b/extensions/.frag-test6
new file mode 100755 (executable)
index 0000000..ff3650d
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_frag.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_frag.h ] && echo frag
diff --git a/extensions/.fuzzy-test b/extensions/.fuzzy-test
new file mode 100755 (executable)
index 0000000..f6575a9
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_fuzzy.h ] && echo fuzzy 
diff --git a/extensions/.fuzzy-test6 b/extensions/.fuzzy-test6
new file mode 100755 (executable)
index 0000000..034263e
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_fuzzy.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_fuzzy.h ] && echo fuzzy
diff --git a/extensions/.ipv4options-test b/extensions/.ipv4options-test
new file mode 100755 (executable)
index 0000000..134ab09
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if ipv4options is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_ipv4options.h ] && echo ipv4options
diff --git a/extensions/.ipv6header-test6 b/extensions/.ipv6header-test6
new file mode 100755 (executable)
index 0000000..47f6f06
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_ipv6header.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_ipv6header.h ] && echo ipv6header
diff --git a/extensions/.mport-test b/extensions/.mport-test
new file mode 100755 (executable)
index 0000000..411a083
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_mport.c ] && echo mport
diff --git a/extensions/.nth-test b/extensions/.nth-test
new file mode 100755 (executable)
index 0000000..536da95
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if nth is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_nth.h ] && echo nth
diff --git a/extensions/.nth-test6 b/extensions/.nth-test6
new file mode 100755 (executable)
index 0000000..7dbe091
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if nth is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_nth.h ] && echo nth
diff --git a/extensions/.opts-test6 b/extensions/.opts-test6
new file mode 100755 (executable)
index 0000000..1ed2013
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_hbh.c -a -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_dst.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_opts.h ] && echo hbh dst
diff --git a/extensions/.osf-test b/extensions/.osf-test
new file mode 100755 (executable)
index 0000000..bc3ad8f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if osf is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_osf.h ] && echo osf
diff --git a/extensions/.psd-test b/extensions/.psd-test
new file mode 100755 (executable)
index 0000000..9d05088
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if psd is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_psd.h ] && echo psd
diff --git a/extensions/.quota-test b/extensions/.quota-test
new file mode 100755 (executable)
index 0000000..761bf14
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_quota.h ] && echo quota
+
diff --git a/extensions/.random-test b/extensions/.random-test
new file mode 100755 (executable)
index 0000000..7626722
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if random is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_random.h ] && echo random
diff --git a/extensions/.random-test6 b/extensions/.random-test6
new file mode 100755 (executable)
index 0000000..25a431f
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if random is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_random.h ] && echo random
diff --git a/extensions/.recent-test b/extensions/.recent-test
new file mode 100755 (executable)
index 0000000..2a47fc9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if recent match patch is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_recent.h ] && echo recent
diff --git a/extensions/.record-rpc-test b/extensions/.record-rpc-test
new file mode 100755 (executable)
index 0000000..4ff9fe2
--- /dev/null
@@ -0,0 +1,3 @@
+#! /bin/sh
+# True if record rpc is applied.
+[ -f $KERNEL_DIR/net/ipv4/netfilter/ipt_record_rpc.c ] && echo record_rpc
diff --git a/extensions/.rt-test6 b/extensions/.rt-test6
new file mode 100755 (executable)
index 0000000..e8d5855
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+[ -f $KERNEL_DIR/net/ipv6/netfilter/ip6t_rt.c -a -f $KERNEL_DIR/include/linux/netfilter_ipv6/ip6t_rt.h ] && echo rt
diff --git a/extensions/.string-test b/extensions/.string-test
new file mode 100755 (executable)
index 0000000..609f1c2
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_string.h ] && echo string
diff --git a/extensions/.time-test b/extensions/.time-test
new file mode 100755 (executable)
index 0000000..7f0390e
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if time is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_time.h ] && echo time
diff --git a/extensions/.u32-test b/extensions/.u32-test
new file mode 100755 (executable)
index 0000000..77d8a00
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+# True if u32 is applied.
+[ -f $KERNEL_DIR/include/linux/netfilter_ipv4/ipt_u32.h ] && echo u32
diff --git a/extensions/Makefile b/extensions/Makefile
new file mode 100644 (file)
index 0000000..db9d604
--- /dev/null
@@ -0,0 +1,88 @@
+#! /usr/bin/make
+
+# WARNING:
+# only add extensions here that are either present in the kernel, or whose
+# header files are present in the include/linux directory of this iptables
+# package (HW)
+#
+PF_EXT_SLIB:=ah connlimit connmark conntrack dscp ecn esp helper icmp iprange length limit mac mark multiport owner physdev pkttype realm rpc standard state tcp tcpmss tos ttl udp unclean CLASSIFY CONNMARK DNAT DSCP ECN LOG MARK MASQUERADE MIRROR NETMAP NOTRACK REDIRECT REJECT SAME SNAT TARPIT TCPMSS TOS TRACE TTL ULOG
+PF6_EXT_SLIB:=eui64 hl icmpv6 length limit mac mark multiport owner standard tcp udp HL LOG MARK TRACE
+
+# Optionals
+PF_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
+PF6_EXT_SLIB_OPTS:=$(foreach T,$(wildcard extensions/.*-test6),$(shell KERNEL_DIR=$(KERNEL_DIR) $(T)))
+
+PF_EXT_SLIB+=$(PF_EXT_SLIB_OPTS)
+PF6_EXT_SLIB+=$(PF6_EXT_SLIB_OPTS)
+
+OPTIONALS+=$(patsubst %,IPv4:%,$(PF_EXT_SLIB_OPTS))
+OPTIONALS+=$(patsubst %,IPv6:%,$(PF6_EXT_SLIB_OPTS))
+
+ifndef NO_SHARED_LIBS
+SHARED_LIBS+=$(foreach T,$(PF_EXT_SLIB),extensions/libipt_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF_EXT_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libipt_$(T).so)
+
+ifeq ($(DO_IPV6), 1)
+SHARED_LIBS+=$(foreach T,$(PF6_EXT_SLIB),extensions/libip6t_$(T).so)
+EXTRA_INSTALLS+=$(foreach T, $(PF6_EXT_SLIB), $(DESTDIR)$(LIBDIR)/iptables/libip6t_$(T).so)
+endif
+else   # NO_SHARED_LIBS
+EXT_OBJS+=$(foreach T,$(PF_EXT_SLIB),extensions/libipt_$(T).o)
+EXT_FUNC+=$(foreach T,$(PF_EXT_SLIB),ipt_$(T))
+EXT_OBJS+= extensions/initext.o
+ifeq ($(DO_IPV6), 1)
+EXT6_OBJS+=$(foreach T,$(PF6_EXT_SLIB),extensions/libip6t_$(T).o)
+EXT6_FUNC+=$(foreach T,$(PF6_EXT_SLIB),ip6t_$(T))
+EXT6_OBJS+= extensions/initext6.o
+endif  # DO_IPV6
+endif  # NO_SHARED_LIBS
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(SHARED_LIBS)
+endif
+
+ifdef NO_SHARED_LIBS
+extensions/libext.a: $(EXT_OBJS)
+       rm -f $@; ar crv $@ $(EXT_OBJS)
+
+extensions/libext6.a: $(EXT6_OBJS)
+       rm -f $@; ar crv $@ $(EXT6_OBJS)
+
+extensions/initext.o: extensions/initext.c
+extensions/initext6.o: extensions/initext6.c
+
+extensions/initext.c: extensions/Makefile
+       echo "" > $@
+       for i in $(EXT_FUNC); do \
+               echo "extern void $${i}_init(void);" >> $@; \
+       done
+       echo "void init_extensions(void) {" >> $@
+       for i in $(EXT_FUNC); do \
+               echo "  $${i}_init();" >> $@; \
+       done
+       echo "}" >> $@
+
+extensions/initext6.c: extensions/Makefile
+       echo "" > $@
+       for i in $(EXT6_FUNC); do \
+               echo "extern void $${i}_init(void);" >> $@; \
+       done
+       echo "void init_extensions(void) {" >> $@
+       for i in $(EXT6_FUNC); do \
+               echo "  $${i}_init();" >> $@; \
+       done
+       echo "}" >> $@
+
+extensions/lib%.o: extensions/lib%.c
+       $(CC) $(CFLAGS) -D_INIT=$*_init -c -o $@ $<
+
+endif
+$(DESTDIR)$(LIBDIR)/iptables/libipt_%.so: extensions/libipt_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
+       cp $< $@
+
+$(DESTDIR)$(LIBDIR)/iptables/libip6t_%.so: extensions/libip6t_%.so
+       @[ -d $(DESTDIR)$(LIBDIR)/iptables ] || mkdir -p $(DESTDIR)$(LIBDIR)/iptables
+       cp $< $@
diff --git a/extensions/libip6t_HL.c b/extensions/libip6t_HL.c
new file mode 100644 (file)
index 0000000..7c24915
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * IPv6 Hop Limit Target module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl target
+ * This program is distributed under the terms of GNU GPL
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_HL.h>
+
+#define IP6T_HL_USED   1
+
+static void init(struct ip6t_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"HL target v%s options\n"
+"  --hl-set value              Set HL to <value>\n"
+"  --hl-dec value              Decrement HL by <value>\n"
+"  --hl-inc value              Increment HL by <value>\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ip6t_entry *entry,
+               struct ip6t_entry_target **target)
+{
+       struct ip6t_HL_info *info = (struct ip6t_HL_info *) (*target)->data;
+       u_int8_t value;
+
+       if (*flags & IP6T_HL_USED) {
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify HL option twice");
+       }
+
+       if (!optarg) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "HL: You must specify a value");
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                               "HL: unexpected `!'");
+       
+       value = atoi(optarg);
+
+       switch (c) {
+
+               case '1':
+                       info->mode = IP6T_HL_SET;
+                       break;
+
+               case '2':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "HL: decreasing by 0?");
+                       }
+
+                       info->mode = IP6T_HL_DEC;
+                       break;
+
+               case '3':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "HL: increasing by 0?");
+                       }
+
+                       info->mode = IP6T_HL_INC;
+                       break;
+
+               default:
+                       return 0;
+
+       }
+       
+       info->hop_limit = value;
+       *flags |= IP6T_HL_USED;
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IP6T_HL_USED))
+               exit_error(PARAMETER_PROBLEM,
+                               "HL: You must specify an action");
+}
+
+static void save(const struct ip6t_ip6 *ip,
+               const struct ip6t_entry_target *target)
+{
+       const struct ip6t_HL_info *info = 
+               (struct ip6t_HL_info *) target->data;
+
+       switch (info->mode) {
+               case IP6T_HL_SET:
+                       printf("--hl-set ");
+                       break;
+               case IP6T_HL_DEC:
+                       printf("--hl-dec ");
+                       break;
+
+               case IP6T_HL_INC:
+                       printf("--hl-inc ");
+                       break;
+       }
+       printf("%u ", info->hop_limit);
+}
+
+static void print(const struct ip6t_ip6 *ip,
+               const struct ip6t_entry_target *target, int numeric)
+{
+       const struct ip6t_HL_info *info =
+               (struct ip6t_HL_info *) target->data;
+
+       printf("HL ");
+       switch (info->mode) {
+               case IP6T_HL_SET:
+                       printf("set to ");
+                       break;
+               case IP6T_HL_DEC:
+                       printf("decrement by ");
+                       break;
+               case IP6T_HL_INC:
+                       printf("increment by ");
+                       break;
+       }
+       printf("%u ", info->hop_limit);
+}
+
+static struct option opts[] = {
+       { "hl-set", 1, 0, '1' },
+       { "hl-dec", 1, 0, '2' },
+       { "hl-inc", 1, 0, '3' },
+       { 0 }
+};
+
+static
+struct ip6tables_target HL = { NULL, 
+       "HL",
+       IPTABLES_VERSION,
+       IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       IP6T_ALIGN(sizeof(struct ip6t_HL_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts 
+};
+
+void _init(void)
+{
+       register_target6(&HL);
+}
diff --git a/extensions/libip6t_LOG.c b/extensions/libip6t_LOG.c
new file mode 100644 (file)
index 0000000..997e439
--- /dev/null
@@ -0,0 +1,262 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_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",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
+       { .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
+       { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
+       { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
+       { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct ip6t_log_info *loginfo = (struct ip6t_log_info *)t->data;
+
+       loginfo->level = LOG_DEFAULT_LEVEL;
+
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+struct ip6t_log_names {
+       const char *name;
+       unsigned int level;
+};
+
+static struct ip6t_log_names ip6t_log_names[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },           /* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },         /* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static u_int8_t
+parse_level(const char *level)
+{
+       unsigned int lev = -1;
+       unsigned int set = 0;
+
+       if (string_to_number(level, 0, 7, &lev) == -1) {
+               unsigned int i = 0;
+
+               for (i = 0;
+                    i < sizeof(ip6t_log_names) / sizeof(struct ip6t_log_names);
+                    i++) {
+                       if (strncasecmp(level, ip6t_log_names[i].name,
+                                       strlen(level)) == 0) {
+                               if (set++)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "log-level `%s' ambiguous",
+                                                  level);
+                               lev = ip6t_log_names[i].level;
+                       }
+               }
+
+               if (!set)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "log-level `%s' unknown", level);
+       }
+
+       return (u_int8_t)lev;
+}
+
+#define IP6T_LOG_OPT_LEVEL 0x01
+#define IP6T_LOG_OPT_PREFIX 0x02
+#define IP6T_LOG_OPT_TCPSEQ 0x04
+#define IP6T_LOG_OPT_TCPOPT 0x08
+#define IP6T_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 ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_log_info *loginfo = (struct ip6t_log_info *)(*target)->data;
+
+       switch (c) {
+       case '!':
+               if (*flags & IP6T_LOG_OPT_LEVEL)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-level twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --log-level");
+
+               loginfo->level = parse_level(optarg);
+               *flags |= IP6T_LOG_OPT_LEVEL;
+               break;
+
+       case '#':
+               if (*flags & IP6T_LOG_OPT_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-prefix twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       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 |= IP6T_LOG_OPT_PREFIX;
+               break;
+
+       case '1':
+               if (*flags & IP6T_LOG_OPT_TCPSEQ)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-sequence "
+                                  "twice");
+
+               loginfo->logflags |= IP6T_LOG_TCPSEQ;
+               *flags |= IP6T_LOG_OPT_TCPSEQ;
+               break;
+
+       case '2':
+               if (*flags & IP6T_LOG_OPT_TCPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-tcp-options twice");
+
+               loginfo->logflags |= IP6T_LOG_TCPOPT;
+               *flags |= IP6T_LOG_OPT_TCPOPT;
+               break;
+
+       case '3':
+               if (*flags & IP6T_LOG_OPT_IPOPT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --log-ip-options twice");
+
+               loginfo->logflags |= IP6T_LOG_IPOPT;
+               *flags |= IP6T_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 ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_log_info *loginfo
+               = (const struct ip6t_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(ip6t_log_names) / sizeof(struct ip6t_log_names);
+                    i++) {
+                       if (loginfo->level == ip6t_log_names[i].level) {
+                               printf("level %s ", ip6t_log_names[i].name);
+                               break;
+                       }
+               }
+               if (i == sizeof(ip6t_log_names) / sizeof(struct ip6t_log_names))
+                       printf("UNKNOWN level %u ", loginfo->level);
+               if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+                       printf("tcp-sequence ");
+               if (loginfo->logflags & IP6T_LOG_TCPOPT)
+                       printf("tcp-options ");
+               if (loginfo->logflags & IP6T_LOG_IPOPT)
+                       printf("ip-options ");
+               if (loginfo->logflags & ~(IP6T_LOG_MASK))
+                       printf("unknown-flags ");
+       }
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("prefix `%s' ", loginfo->prefix);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ip6t_log_info *loginfo
+               = (const struct ip6t_log_info *)target->data;
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("--log-prefix \"%s\" ", loginfo->prefix);
+
+       if (loginfo->level != LOG_DEFAULT_LEVEL)
+               printf("--log-level %d ", loginfo->level);
+
+       if (loginfo->logflags & IP6T_LOG_TCPSEQ)
+               printf("--log-tcp-sequence ");
+       if (loginfo->logflags & IP6T_LOG_TCPOPT)
+               printf("--log-tcp-options ");
+       if (loginfo->logflags & IP6T_LOG_IPOPT)
+               printf("--log-ip-options ");
+}
+
+static
+struct ip6tables_target log
+= {
+    .name          = "LOG",
+    .version       = IPTABLES_VERSION,
+    .size          = IP6T_ALIGN(sizeof(struct ip6t_log_info)),
+    .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_log_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target6(&log);
+}
diff --git a/extensions/libip6t_MARK.c b/extensions/libip6t_MARK.c
new file mode 100644 (file)
index 0000000..292f957
--- /dev/null
@@ -0,0 +1,110 @@
+/* Shared library add-on to iptables to add MARK target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_MARK.h>
+
+/* 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",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "set-mark", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_mark_target_info *markinfo
+               = (struct ip6t_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");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_mark_target_info *markinfo =
+               (const struct ip6t_mark_target_info *)target->data;
+
+       printf("MARK set 0x%lx ", markinfo->mark);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_target *target)
+{
+       const struct ip6t_mark_target_info *markinfo =
+               (const struct ip6t_mark_target_info *)target->data;
+
+       printf("--set-mark 0x%lx ", markinfo->mark);
+}
+
+static
+struct ip6tables_target mark = {
+       .name          = "MARK",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_mark_target_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_mark_target_info)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target6(&mark);
+}
diff --git a/extensions/libip6t_REJECT.c b/extensions/libip6t_REJECT.c
new file mode 100644 (file)
index 0000000..8c4cc7f
--- /dev/null
@@ -0,0 +1,173 @@
+/* Shared library add-on to iptables to add customized REJECT support.
+ *
+ * (C) 2000 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ * 
+ * ported to IPv6 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_REJECT.h>
+
+struct reject_names {
+       const char *name;
+       const char *alias;
+       enum ip6t_reject_with with;
+       const char *desc;
+};
+
+static const struct reject_names reject_table[] = {
+       {"icmp6-no-route", "no-route",
+               IP6T_ICMP6_NO_ROUTE, "ICMPv6 no route"},
+       {"icmp6-adm-prohibited", "adm-prohibited",
+               IP6T_ICMP6_ADM_PROHIBITED, "ICMPv6 administratively prohibited"},
+#if 0
+       {"icmp6-not-neighbor", "not-neighbor"},
+               IP6T_ICMP6_NOT_NEIGHBOR, "ICMPv6 not a neighbor"},
+#endif
+       {"icmp6-addr-unreachable", "addr-unreach",
+               IP6T_ICMP6_ADDR_UNREACH, "ICMPv6 address unreachable"},
+       {"icmp6-port-unreachable", "port-unreach",
+               IP6T_ICMP6_PORT_UNREACH, "ICMPv6 port unreachable"},
+       {"tcp-reset", "tcp-reset",
+               IP6T_TCP_RESET, "TCP RST packet"}
+};
+
+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 ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct ip6t_reject_info *reject = (struct ip6t_reject_info *)t->data;
+
+       /* default */
+       reject->with = IP6T_ICMP6_PORT_UNREACH;
+
+       /* 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 ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_reject_info *reject = 
+               (struct ip6t_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, NULL, 0))
+                       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 */
+               break;
+       }
+       return 0;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out ipt_reject_info. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_reject_info *reject
+               = (const struct ip6t_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 ip6t_ip6 *ip, 
+                const struct ip6t_entry_target *target)
+{
+       const struct ip6t_reject_info *reject
+               = (const struct ip6t_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);
+}
+
+struct ip6tables_target reject
+= { NULL,
+    "REJECT",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_reject_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target6(&reject);
+}
diff --git a/extensions/libip6t_ROUTE.c b/extensions/libip6t_ROUTE.c
new file mode 100644 (file)
index 0000000..71dada8
--- /dev/null
@@ -0,0 +1,209 @@
+/* Shared library add-on to iptables to add ROUTE v6 target support.
+ * Author : Cedric de Launois, <delaunois@info.ucl.ac.be>
+ * v 1.0 2003/06/24
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ROUTE.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ROUTE target v%s options:\n"
+"    --oif   \tifname \t\tRoute the packet through `ifname' network interface\n"
+"    --gw    \tip     \t\tRoute the packet via this gateway\n"
+"    --continue\t     \t\tRoute the packet and continue traversing the rules.\n"
+"\n",
+"1.0");
+}
+
+static struct option opts[] = {
+       { "oif", 1, 0, '1' },
+       { "iif", 1, 0, '2' },
+       { "gw", 1, 0, '3' },
+       { "continue", 0, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_target *t, unsigned int *nfcache)
+{
+       struct ip6t_route_target_info *route_info = 
+               (struct ip6t_route_target_info*)t->data;
+
+       route_info->oif[0] = '\0';
+       route_info->iif[0] = '\0';
+       route_info->gw[0] = 0;
+       route_info->gw[1] = 0;
+       route_info->gw[2] = 0;
+       route_info->gw[3] = 0;
+       route_info->flags = 0;
+}
+
+
+#define IP6T_ROUTE_OPT_OIF      0x01
+#define IP6T_ROUTE_OPT_IIF      0x02
+#define IP6T_ROUTE_OPT_GW       0x04
+#define IP6T_ROUTE_OPT_CONTINUE 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 ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       struct ip6t_route_target_info *route_info = 
+               (struct ip6t_route_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_ROUTE_OPT_OIF)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --oif twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --oif");
+
+               if (strlen(optarg) > sizeof(route_info->oif) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum interface name length %u",
+                                  sizeof(route_info->oif) - 1);
+
+               strcpy(route_info->oif, optarg);
+               *flags |= IP6T_ROUTE_OPT_OIF;
+               break;
+
+       case '2':
+               exit_error(PARAMETER_PROBLEM,
+                          "--iif option not implemented");
+               break;
+
+       case '3':
+               if (*flags & IP6T_ROUTE_OPT_GW)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --gw twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --gw");
+
+               if (!inet_pton(AF_INET6, optarg, (struct in6_addr*)&route_info->gw)) {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid IPv6 address %s",
+                                  optarg);
+               }
+
+               *flags |= IP6T_ROUTE_OPT_GW;
+               break;
+
+       case '4':
+               if (*flags & IP6T_ROUTE_OPT_CONTINUE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --continue twice");
+
+               route_info->flags |= IP6T_ROUTE_CONTINUE;
+               *flags |= IP6T_ROUTE_OPT_CONTINUE;
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ROUTE target: oif or gw option required");
+}
+
+
+/* Prints out the targinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_target *target,
+      int numeric)
+{
+       const struct ip6t_route_target_info *route_info
+               = (const struct ip6t_route_target_info *)target->data;
+
+       printf("ROUTE ");
+
+       if (route_info->oif[0])
+               printf("oif:%s ", route_info->oif);
+
+       if (route_info->gw[0] 
+           || route_info->gw[1] 
+           || route_info->gw[2] 
+           || route_info->gw[3]) {
+               char address[INET6_ADDRSTRLEN];
+               printf("gw:%s ", inet_ntop(AF_INET6, route_info->gw, address, INET6_ADDRSTRLEN));
+       }
+
+       if (route_info->flags & IP6T_ROUTE_CONTINUE)
+               printf("continue");
+
+}
+
+
+static void save(const struct ip6t_ip6 *ip, 
+                const struct ip6t_entry_target *target)
+{
+       const struct ip6t_route_target_info *route_info
+               = (const struct ip6t_route_target_info *)target->data;
+
+       if (route_info->oif[0])
+               printf("--oif %s ", route_info->oif);
+
+       if (route_info->gw[0] 
+           || route_info->gw[1] 
+           || route_info->gw[2] 
+           || route_info->gw[3]) {
+               char address[INET6_ADDRSTRLEN];
+               printf("--gw %s ", inet_ntop(AF_INET6, route_info->gw, address, INET6_ADDRSTRLEN));
+       }
+
+       if (route_info->flags & IP6T_ROUTE_CONTINUE)
+               printf("--continue ");
+}
+
+
+static
+struct ip6tables_target route
+= { NULL,
+    "ROUTE",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_route_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target6(&route);
+}
diff --git a/extensions/libip6t_TRACE.c b/extensions/libip6t_TRACE.c
new file mode 100644 (file)
index 0000000..00d8591
--- /dev/null
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add TRACE target support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TRACE target v%s takes no options\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      struct ip6t_entry_target **target)
+{
+       return 0;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct ip6tables_target trace
+= {    .next = NULL,
+       .name = "TRACE",
+       .version = IPTABLES_VERSION,
+       .size = IP6T_ALIGN(0),
+       .userspacesize = IP6T_ALIGN(0),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = NULL, /* print */
+       .save = NULL, /* save */
+       .extra_opts = opts
+};
+
+void _init(void)
+{
+       register_target6(&trace);
+}
diff --git a/extensions/libip6t_ah.c b/extensions/libip6t_ah.c
new file mode 100644 (file)
index 0000000..794e02e
--- /dev/null
@@ -0,0 +1,227 @@
+/* Shared library add-on to ip6tables to add AH support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_ah.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"AH v%s options:\n"
+" --ahspi [!] spi[:spi]         match spi (range)\n"
+" --ahlen [!] length            total length of this header\n"
+" --ahres                       check the reserved filed, too\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "ahspi", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "ahlen", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "ahres", .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_ah_spi(const char *spistr, const char *typestr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi = strtoul(spistr, &ep, 0);
+
+       if ( spistr == ep )
+               exit_error(PARAMETER_PROBLEM,
+                          "AH no valid digits in %s `%s'", typestr, spistr);
+
+       if ( spi == ULONG_MAX  && errno == ERANGE )
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, spistr);
+
+       if ( *spistr != '\0'  && *ep != '\0' )
+               exit_error(PARAMETER_PROBLEM,
+                          "AH error parsing %s `%s'", typestr, spistr);
+
+       return (u_int32_t) spi;
+}
+
+static void
+parse_ah_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_ah_spi(buffer, "spi");
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_ah_spi(buffer, "spi") : 0;
+               spis[1] = cp[0] ? parse_ah_spi(cp, "spi") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_ah *ahinfo = (struct ip6t_ah *)m->data;
+
+       ahinfo->spis[1] = 0xFFFFFFFF;
+       ahinfo->hdrlen = 0;
+       ahinfo->hdrres = 0;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_ah *ahinfo = (struct ip6t_ah *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_AH_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_ah_spis(argv[optind-1], ahinfo->spis);
+               if (invert)
+                       ahinfo->invflags |= IP6T_AH_INV_SPI;
+               *flags |= IP6T_AH_SPI;
+               break;
+       case '2':
+               if (*flags & IP6T_AH_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahlen' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               ahinfo->hdrlen = parse_ah_spi(argv[optind-1], "length");
+               if (invert)
+                       ahinfo->invflags |= IP6T_AH_INV_LEN;
+               *flags |= IP6T_AH_LEN;
+               break;
+       case '3':
+               if (*flags & IP6T_AH_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahres' allowed");
+               ahinfo->hdrres = 1;
+               *flags |= IP6T_AH_RES;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               if (min == max)
+                       printf("%s:%s%u ", name, inv, min);
+               else
+                       printf("%ss:%s%u:%u ", name, inv, min, max);
+       }
+}
+
+static void
+print_len(const char *name, u_int32_t len, int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (len != 0 || invert)
+               printf("%s:%s%u ", name, inv, len);
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_ah *ah = (struct ip6t_ah *)match->data;
+
+       printf("ah ");
+       print_spis("spi", ah->spis[0], ah->spis[1],
+                   ah->invflags & IP6T_AH_INV_SPI);
+       print_len("length", ah->hdrlen, 
+                   ah->invflags & IP6T_AH_INV_LEN);
+
+       if (ah->hdrres)
+               printf("reserved ");
+
+       if (ah->invflags & ~IP6T_AH_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      ah->invflags & ~IP6T_AH_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_ah *ahinfo = (struct ip6t_ah *)match->data;
+
+       if (!(ahinfo->spis[0] == 0
+           && ahinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--ahspi %s", 
+                       (ahinfo->invflags & IP6T_AH_INV_SPI) ? "! " : "");
+               if (ahinfo->spis[0]
+                   != ahinfo->spis[1])
+                       printf("%u:%u ",
+                              ahinfo->spis[0],
+                              ahinfo->spis[1]);
+               else
+                       printf("%u ",
+                              ahinfo->spis[0]);
+       }
+
+       if (ahinfo->hdrlen != 0 || (ahinfo->invflags & IP6T_AH_INV_LEN) ) {
+               printf("--ahlen %s%u ", 
+                       (ahinfo->invflags & IP6T_AH_INV_LEN) ? "! " : "", 
+                       ahinfo->hdrlen);
+       }
+
+       if (ahinfo->hdrres != 0 )
+               printf("--ahres ");
+}
+
+static
+struct ip6tables_match ah = {
+       .name          = "ah",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_ah)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_ah)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&ah);
+}
diff --git a/extensions/libip6t_condition.c b/extensions/libip6t_condition.c
new file mode 100644 (file)
index 0000000..f58b3bc
--- /dev/null
@@ -0,0 +1,115 @@
+/* Shared library add-on to ip6tables for condition match */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include<linux/netfilter_ipv6/ip6_tables.h>
+#include<linux/netfilter_ipv6/ip6t_condition.h>
+
+
+static void
+help(void)
+{
+       printf("condition match v%s options:\n"
+              "--condition [!] filename       "
+              "Match on boolean value stored in /proc file\n",
+              IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' },
+       { .name = 0 }
+};
+
+
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry, unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct condition6_info *info =
+           (struct condition6_info *) (*match)->data;
+
+       if (c == 'X') {
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple conditions");
+
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (strlen(argv[optind - 1]) < CONDITION6_NAME_LEN)
+                       strcpy(info->name, argv[optind - 1]);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "File name too long");
+
+               info->invert = invert;
+               *flags = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "Condition match: must specify --condition");
+}
+
+
+static void
+print(const struct ip6t_ip6 *ip,
+                 const struct ip6t_entry_match *match, int numeric)
+{
+       const struct condition6_info *info =
+           (const struct condition6_info *) match->data;
+
+       printf("condition %s%s ", (info->invert) ? "!" : "", info->name);
+}
+
+
+static void
+save(const struct ip6t_ip6 *ip,
+                const struct ip6t_entry_match *match)
+{
+       const struct condition6_info *info =
+           (const struct condition6_info *) match->data;
+
+       printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name);
+}
+
+
+static struct ip6tables_match condition = {
+       .name = "condition",
+       .version = IPTABLES_VERSION,
+       .size = IP6T_ALIGN(sizeof(struct condition6_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct condition6_info)),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = &print,
+       .save = &save,
+       .extra_opts = opts
+};
+
+
+void
+_init(void)
+{
+       register_match6(&condition);
+}
diff --git a/extensions/libip6t_dst.c b/extensions/libip6t_dst.c
new file mode 100644 (file)
index 0000000..19ca23c
--- /dev/null
@@ -0,0 +1,269 @@
+/* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#ifdef HOPBYHOP
+#define UNAME "HBH"
+#define LNAME "hbh"
+#else
+#define UNAME "DST"
+#define LNAME "dst"
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+UNAME " v%s options:\n"
+" --" LNAME "-len [!] length           total length of this header\n"
+" --" LNAME "-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
+"                               Options and its length (list, max: %d)\n", 
+IPTABLES_VERSION, IP6T_OPTS_OPTSNR);
+}
+
+static struct option opts[] = {
+       { .name = LNAME "-len",        .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = LNAME "-opts",       .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = LNAME "-not-strict", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id = strtoul(idstr, &ep, 0);
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          UNAME " no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          UNAME " error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static int
+parse_options(const char *optsstr, u_int16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+       
+       buffer = strdup(optsstr);
+        if (!buffer)
+               exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp = buffer, i = 0; cp && i < IP6T_OPTS_OPTSNR; cp = next, i++)
+        {
+                next = strchr(cp, ',');
+
+                if (next)
+                       *next++='\0';
+
+                range = strchr(cp, ':');
+
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+                                exit_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+
+                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8); 
+                if (range) {
+                       if (opts[i] == 0)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "PAD0 hasn't got length");
+                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
+                                       0x000000FF);
+                } else
+                        opts[i] |= (0x00FF);
+
+#ifdef DEBUG
+               printf("opts str: %s %s\n", cp, range);
+               printf("opts opt: %04X\n", opts[i]);
+#endif
+       }
+
+        if (cp)
+               exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#ifdef DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
+
+       optinfo->hdrlen = 0;
+       optinfo->flags = 0;
+       optinfo->invflags = 0;
+       optinfo->optsnr = 0;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_OPTS_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-len' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
+               if (invert)
+                       optinfo->invflags |= IP6T_OPTS_INV_LEN;
+               optinfo->flags |= IP6T_OPTS_LEN;
+               *flags |= IP6T_OPTS_LEN;
+               break;
+       case '2':
+               if (*flags & IP6T_OPTS_OPTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-opts' allowed");
+                check_inverse(optarg, &invert, &optind, 0);
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                               " '!' not allowed with `--" LNAME "-opts'");
+               optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
+               optinfo->flags |= IP6T_OPTS_OPTS;
+               *flags |= IP6T_OPTS_OPTS;
+               break;
+       case '3':
+               if (*flags & IP6T_OPTS_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--" LNAME "-not-strict' allowed");
+               if ( !(*flags & IP6T_OPTS_OPTS) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--" LNAME "-opts ...' required before `--"
+                                  LNAME "-not-strict'");
+               optinfo->flags |= IP6T_OPTS_NSTRICT;
+               *flags |= IP6T_OPTS_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_options(int optsnr, u_int16_t *optsp)
+{
+       unsigned int i;
+
+       for(i = 0; i < optsnr; i++) {
+               printf("%d", (optsp[i] & 0xFF00) >> 8);
+
+               if ((optsp[i] & 0x00FF) != 0x00FF)
+                       printf(":%d", (optsp[i] & 0x00FF));
+
+               printf("%c", (i != optsnr - 1) ? ',' : ' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       printf(LNAME " ");
+       if (optinfo->flags & IP6T_OPTS_LEN)
+               printf("length:%s%u ",
+                       optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "",
+                       optinfo->hdrlen);
+
+       if (optinfo->flags & IP6T_OPTS_OPTS)
+               printf("opts ");
+
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+
+       if (optinfo->flags & IP6T_OPTS_NSTRICT)
+               printf("not-strict ");
+
+       if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("--" LNAME "-len %s%u ", 
+                       (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", 
+                       optinfo->hdrlen);
+       }
+
+       if (optinfo->flags & IP6T_OPTS_OPTS)
+               printf("--" LNAME "-opts ");
+
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+
+       if (optinfo->flags & IP6T_OPTS_NSTRICT)
+               printf("--" LNAME "-not-strict ");
+}
+
+static
+struct ip6tables_match optstruct = {
+       .name          = LNAME,
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_opts)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&optstruct);
+}
diff --git a/extensions/libip6t_esp.c b/extensions/libip6t_esp.c
new file mode 100644 (file)
index 0000000..29e865d
--- /dev/null
@@ -0,0 +1,182 @@
+/* Shared library add-on to ip6tables to add ESP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_esp.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ESP v%s options:\n"
+" --espspi [!] spi[:spi]        match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "espspi", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_esp_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi = strtoul(spistr, &ep, 0);
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_esp_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_esp_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_esp_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_esp_spi(cp) : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_esp *espinfo = (struct ip6t_esp *)m->data;
+
+       espinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define ESP_SPI 0x01
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_esp *espinfo = (struct ip6t_esp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & ESP_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--espspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_esp_spis(argv[optind-1], espinfo->spis);
+               if (invert)
+                       espinfo->invflags |= IP6T_ESP_INV_SPI;
+               *flags |= ESP_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               if (min == max)
+                       printf("%s:%s%u ", name, inv, min);
+               else
+                       printf("%ss:%s%u:%u ", name, inv, min, max);
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_esp *esp = (struct ip6t_esp *)match->data;
+
+       printf("esp ");
+       print_spis("spi", esp->spis[0], esp->spis[1],
+                   esp->invflags & IP6T_ESP_INV_SPI);
+       if (esp->invflags & ~IP6T_ESP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      esp->invflags & ~IP6T_ESP_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_esp *espinfo = (struct ip6t_esp *)match->data;
+
+       if (!(espinfo->spis[0] == 0
+           && espinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--espspi %s", 
+                       (espinfo->invflags & IP6T_ESP_INV_SPI) ? "! " : "");
+               if (espinfo->spis[0]
+                   != espinfo->spis[1])
+                       printf("%u:%u ",
+                              espinfo->spis[0],
+                              espinfo->spis[1]);
+               else
+                       printf("%u ",
+                              espinfo->spis[0]);
+       }
+
+}
+
+static
+struct ip6tables_match esp = {
+       .name          = "esp",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_esp)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_esp)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&esp);
+}
diff --git a/extensions/libip6t_eui64.c b/extensions/libip6t_eui64.c
new file mode 100644 (file)
index 0000000..56dca06
--- /dev/null
@@ -0,0 +1,87 @@
+/* Shared library add-on to ip6tables to add EUI64 address checking 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 <ip6tables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"eui64 v%s options:\n"
+" This module hasn't got any option\n"
+" This module checks for EUI64 IPv6 addresses\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       return 0;
+}
+
+/* Final check */
+static void final_check(unsigned int flags)
+{
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("eui64 ");
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+
+}
+
+static
+struct ip6tables_match eui64
+= { NULL,
+    "eui64",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(int)),
+    IP6T_ALIGN(sizeof(int)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&eui64);
+}
diff --git a/extensions/libip6t_frag.c b/extensions/libip6t_frag.c
new file mode 100644 (file)
index 0000000..51a14fa
--- /dev/null
@@ -0,0 +1,272 @@
+/* Shared library add-on to ip6tables to add Fragmentation header support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_frag.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"FRAG v%s options:\n"
+" --fragid [!] id[:id]          match the id (range)\n"
+" --fraglen [!] length          total length of this header\n"
+" --fragres                     check the reserved filed, too\n"
+" --fragfirst                   matches on the first fragment\n"
+" [--fragmore|--fraglast]       there are more fragments or this\n"
+"                               is the last one\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "fragid",    .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "fraglen",   .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "fragres",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = "fragfirst", .has_arg = 0, .flag = 0, .val = '4' },
+       { .name = "fragmore",  .has_arg = 0, .flag = 0, .val = '5' },
+       { .name = "fraglast",  .has_arg = 0, .flag = 0, .val = '6' },
+       { .name = 0 }
+};
+
+static u_int32_t
+parse_frag_id(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id = strtoul(idstr, &ep, 0);
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "FRAG no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "FRAG error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static void
+parse_frag_ids(const char *idstring, u_int32_t *ids)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(idstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ids[0] = ids[1] = parse_frag_id(buffer,"id");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ids[0] = buffer[0] ? parse_frag_id(buffer,"id") : 0;
+               ids[1] = cp[0] ? parse_frag_id(cp,"id") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_frag *fraginfo = (struct ip6t_frag *)m->data;
+
+       fraginfo->ids[0] = 0x0L;
+       fraginfo->ids[1] = 0xFFFFFFFF;
+       fraginfo->hdrlen = 0;
+       fraginfo->flags = 0;
+       fraginfo->invflags = 0;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_frag *fraginfo = (struct ip6t_frag *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_FRAG_IDS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragid' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_frag_ids(argv[optind-1], fraginfo->ids);
+               if (invert)
+                       fraginfo->invflags |= IP6T_FRAG_INV_IDS;
+               fraginfo->flags |= IP6T_FRAG_IDS;
+               *flags |= IP6T_FRAG_IDS;
+               break;
+       case '2':
+               if (*flags & IP6T_FRAG_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fraglen' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               fraginfo->hdrlen = parse_frag_id(argv[optind-1], "length");
+               if (invert)
+                       fraginfo->invflags |= IP6T_FRAG_INV_LEN;
+               fraginfo->flags |= IP6T_FRAG_LEN;
+               *flags |= IP6T_FRAG_LEN;
+               break;
+       case '3':
+               if (*flags & IP6T_FRAG_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragres' allowed");
+               fraginfo->flags |= IP6T_FRAG_RES;
+               *flags |= IP6T_FRAG_RES;
+               break;
+       case '4':
+               if (*flags & IP6T_FRAG_FST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--fragfirst' allowed");
+               fraginfo->flags |= IP6T_FRAG_FST;
+               *flags |= IP6T_FRAG_FST;
+               break;
+       case '5':
+               if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) 
+                       exit_error(PARAMETER_PROBLEM,
+                          "Only one `--fragmore' or `--fraglast' allowed");
+               fraginfo->flags |= IP6T_FRAG_MF;
+               *flags |= IP6T_FRAG_MF;
+               break;
+       case '6':
+               if (*flags & (IP6T_FRAG_MF|IP6T_FRAG_NMF)) 
+                       exit_error(PARAMETER_PROBLEM,
+                          "Only one `--fragmore' or `--fraglast' allowed");
+               fraginfo->flags |= IP6T_FRAG_NMF;
+               *flags |= IP6T_FRAG_NMF;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_ids(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max)
+                       printf(":%s%u ", inv, min);
+               else
+                       printf("s:%s%u:%u ", inv, min, max);
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_frag *frag = (struct ip6t_frag *)match->data;
+
+       printf("frag ");
+       print_ids("id", frag->ids[0], frag->ids[1],
+                   frag->invflags & IP6T_FRAG_INV_IDS);
+
+       if (frag->flags & IP6T_FRAG_LEN) {
+               printf("length:%s%u ",
+                       frag->invflags & IP6T_FRAG_INV_LEN ? "!" : "",
+                       frag->hdrlen);
+       }
+
+       if (frag->flags & IP6T_FRAG_RES)
+               printf("reserved ");
+
+       if (frag->flags & IP6T_FRAG_FST)
+               printf("first ");
+
+       if (frag->flags & IP6T_FRAG_MF)
+               printf("more ");
+
+       if (frag->flags & IP6T_FRAG_NMF)
+               printf("last ");
+
+       if (frag->invflags & ~IP6T_FRAG_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      frag->invflags & ~IP6T_FRAG_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_frag *fraginfo = (struct ip6t_frag *)match->data;
+
+       if (!(fraginfo->ids[0] == 0
+           && fraginfo->ids[1] == 0xFFFFFFFF)) {
+               printf("--fragid %s", 
+                       (fraginfo->invflags & IP6T_FRAG_INV_IDS) ? "! " : "");
+               if (fraginfo->ids[0]
+                   != fraginfo->ids[1])
+                       printf("%u:%u ",
+                              fraginfo->ids[0],
+                              fraginfo->ids[1]);
+               else
+                       printf("%u ",
+                              fraginfo->ids[0]);
+       }
+
+       if (fraginfo->flags & IP6T_FRAG_LEN) {
+               printf("--fraglen %s%u ", 
+                       (fraginfo->invflags & IP6T_FRAG_INV_LEN) ? "! " : "", 
+                       fraginfo->hdrlen);
+       }
+
+       if (fraginfo->flags & IP6T_FRAG_RES)
+               printf("--fragres ");
+
+       if (fraginfo->flags & IP6T_FRAG_FST)
+               printf("--fragfirst ");
+
+       if (fraginfo->flags & IP6T_FRAG_MF)
+               printf("--fragmore ");
+
+       if (fraginfo->flags & IP6T_FRAG_NMF)
+               printf("--fraglast ");
+}
+
+static
+struct ip6tables_match frag = {
+       .name          = "frag",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_frag)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_frag)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void
+_init(void)
+{
+       register_match6(&frag);
+}
diff --git a/extensions/libip6t_fuzzy.c b/extensions/libip6t_fuzzy.c
new file mode 100644 (file)
index 0000000..65c2acf
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+   Shared library add-on to iptables to add match support for the fuzzy match.
+
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+2002-08-07 Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Initial version.
+2003-04-08 Maciej Soltysiak <solt@dns.toxicfilms.tv> : IPv6 Port
+2003-06-09 Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Bug corrections in
+the save function , thanks to information given by Jean-Francois Patenaude.
+
+*/
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_fuzzy.h>
+
+
+static void
+help(void)
+{
+       printf(
+"fuzzy v%s options:\n"
+"                      --lower-limit number (in packets per second)\n"
+"                      --upper-limit number\n"
+,IPTABLES_VERSION);
+};
+
+static struct option opts[] = {
+       { .name = "lower-limit", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "upper-limit", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = 0 }
+};
+
+/* Initialize data structures */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_fuzzy_info *presentinfo = (struct ip6t_fuzzy_info *)(m)->data;
+       *nfcache |= NFC_UNKNOWN;
+
+       /*
+        * Default rates ( I'll improve this very soon with something based
+        * on real statistics of the running machine ) .
+       */
+
+       presentinfo->minimum_rate = 1000;
+       presentinfo->maximum_rate = 2000;
+}
+
+#define IP6T_FUZZY_OPT_MINIMUM 0x01
+#define IP6T_FUZZY_OPT_MAXIMUM 0x02
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_fuzzy_info *fuzzyinfo =
+               (struct ip6t_fuzzy_info *)(*match)->data;
+
+       u_int32_t num;
+
+       switch (c) {
+
+       case '1':
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,"Can't specify ! --lower-limit");
+
+       if (*flags & IP6T_FUZZY_OPT_MINIMUM)
+                     exit_error(PARAMETER_PROBLEM,"Can't specify --lower-limit twice");
+
+       if (string_to_number(optarg,1,MAXFUZZYRATE,&num) == -1 || num < 1)
+                       exit_error(PARAMETER_PROBLEM,"BAD --lower-limit");
+
+               fuzzyinfo->minimum_rate = num ;
+
+               *flags |= IP6T_FUZZY_OPT_MINIMUM;
+
+               break;
+
+       case '2':
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,"Can't specify ! --upper-limit");
+
+       if (*flags & IP6T_FUZZY_OPT_MAXIMUM)
+          exit_error(PARAMETER_PROBLEM,"Can't specify --upper-limit twice");
+
+       if (string_to_number(optarg,1,MAXFUZZYRATE,&num) == -1 || num < 1)
+               exit_error(PARAMETER_PROBLEM,"BAD --upper-limit");
+
+               fuzzyinfo->maximum_rate = num;
+
+               *flags |= IP6T_FUZZY_OPT_MAXIMUM;
+
+               break ;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+}
+
+static void
+print(const struct ip6t_ip6 *ipv6,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_fuzzy_info *fuzzyinfo
+               = (const struct ip6t_fuzzy_info *)match->data;
+
+       printf(" fuzzy: lower limit = %u pps - upper limit = %u pps ",
+               fuzzyinfo->minimum_rate, fuzzyinfo->maximum_rate);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ipv6, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_fuzzy_info *fuzzyinfo
+               = (const struct ip6t_fuzzy_info *)match->data;
+
+       printf("--lower-limit %u --upper-limit %u ",
+               fuzzyinfo->minimum_rate, fuzzyinfo->maximum_rate);
+}
+
+struct ip6tables_match fuzzy_match = {
+       .name          = "fuzzy",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_fuzzy_info)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match6(&fuzzy_match);
+}
diff --git a/extensions/libip6t_hbh.c b/extensions/libip6t_hbh.c
new file mode 100644 (file)
index 0000000..d0acd42
--- /dev/null
@@ -0,0 +1,264 @@
+/* Shared library add-on to ip6tables to add Hop-by-Hop and Dst headers support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+/*#include <linux/in6.h>*/
+#include <linux/netfilter_ipv6/ip6t_opts.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+                                        
+#define DEBUG          0
+#define HOPBYHOP       1
+#define UNAME          (HOPBYHOP ? "HBH" : "DST")
+#define LNAME          (HOPBYHOP ? "hbh" : "dst")
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"%s v%s options:\n"
+" --%s-len [!] length           total length of this header\n"
+" --%s-opts TYPE[:LEN][,TYPE[:LEN]...] \n"
+"                               Options and its length (list, max: %d)\n", 
+UNAME , IPTABLES_VERSION, LNAME, LNAME, IP6T_OPTS_OPTSNR);
+}
+
+#if HOPBYHOP
+static struct option opts[] = {
+       { "hbh-len", 1, 0, '1' },
+       { "hbh-opts", 1, 0, '2' },
+       { "hbh-not-strict", 1, 0, '3' },
+       {0}
+};
+#else
+static struct option opts[] = {
+       { "dst-len", 1, 0, '1' },
+       { "dst-opts", 1, 0, '2' },
+       { "dst-not-strict", 1, 0, '3' },
+       {0}
+};
+#endif
+
+static u_int32_t
+parse_opts_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id =  strtoul(idstr,&ep,0) ;
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s no valid digits in %s `%s'", UNAME, typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s error parsing %s `%s'", UNAME, typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static int
+parse_options(const char *optsstr, u_int16_t *opts)
+{
+        char *buffer, *cp, *next, *range;
+        unsigned int i;
+       
+       buffer = strdup(optsstr);
+        if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp=buffer, i=0; cp && i<IP6T_OPTS_OPTSNR; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+                range = strchr(cp, ':');
+                if (range) {
+                        if (i == IP6T_OPTS_OPTSNR-1)
+                                exit_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+                opts[i] = (u_int16_t)((parse_opts_num(cp,"opt") & 0x000000FF)<<8); 
+                if (range) {
+                       if (opts[i] == 0)
+                               exit_error(PARAMETER_PROBLEM, "PAD0 hasn't got length");
+                        opts[i] |= (u_int16_t)(parse_opts_num(range,"length") &
+                                       0x000000FF);
+                } else {
+                        opts[i] |= (0x00FF);
+               }
+
+#if DEBUG
+               printf("opts str: %s %s\n", cp, range);
+               printf("opts opt: %04X\n", opts[i]);
+#endif
+       }
+        if (cp) exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#if DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)m->data;
+
+       optinfo->hdrlen = 0;
+       optinfo->flags = 0;
+       optinfo->invflags = 0;
+       optinfo->optsnr = 0;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_opts *optinfo = (struct ip6t_opts *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_OPTS_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-len' allowed", LNAME);
+               check_inverse(optarg, &invert, &optind, 0);
+               optinfo->hdrlen = parse_opts_num(argv[optind-1], "length");
+               if (invert)
+                       optinfo->invflags |= IP6T_OPTS_INV_LEN;
+               optinfo->flags |= IP6T_OPTS_LEN;
+               *flags |= IP6T_OPTS_LEN;
+               break;
+       case '2':
+               if (*flags & IP6T_OPTS_OPTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-opts' allowed", LNAME);
+                check_inverse(optarg, &invert, &optind, 0);
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                               " '!' not allowed with `--%s-opts'", LNAME);
+               optinfo->optsnr = parse_options(argv[optind-1], optinfo->opts);
+               optinfo->flags |= IP6T_OPTS_OPTS;
+               *flags |= IP6T_OPTS_OPTS;
+               break;
+       case '3':
+               if (*flags & IP6T_OPTS_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--%s-not-strict' allowed", LNAME);
+               if ( !(*flags & IP6T_OPTS_OPTS) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--%s-opts ...' required before `--%s-not-strict'", LNAME, LNAME);
+               optinfo->flags |= IP6T_OPTS_NSTRICT;
+               *flags |= IP6T_OPTS_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_options(int optsnr, u_int16_t *optsp)
+{
+       unsigned int i;
+
+       for(i=0; i<optsnr; i++){
+               printf("%d", (optsp[i] & 0xFF00)>>8);
+               if ((optsp[i] & 0x00FF) != 0x00FF){
+                       printf(":%d", (optsp[i] & 0x00FF));
+               } 
+               printf("%c", (i!=optsnr-1)?',':' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       printf("%s ", LNAME);
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("length");
+               printf(":%s", optinfo->invflags & IP6T_OPTS_INV_LEN ? "!" : "");
+               printf("%u", optinfo->hdrlen);
+               printf(" ");
+       }
+       if (optinfo->flags & IP6T_OPTS_OPTS) printf("opts ");
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+       if (optinfo->flags & IP6T_OPTS_NSTRICT) printf("not-strict ");
+       if (optinfo->invflags & ~IP6T_OPTS_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      optinfo->invflags & ~IP6T_OPTS_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_opts *optinfo = (struct ip6t_opts *)match->data;
+
+       if (optinfo->flags & IP6T_OPTS_LEN) {
+               printf("--%s-len %s%u ", LNAME, 
+                       (optinfo->invflags & IP6T_OPTS_INV_LEN) ? "! " : "", 
+                       optinfo->hdrlen);
+       }
+
+       if (optinfo->flags & IP6T_OPTS_OPTS) printf("--%s-opts ", LNAME);
+       print_options(optinfo->optsnr, (u_int16_t *)optinfo->opts);
+       if (optinfo->flags & IP6T_OPTS_NSTRICT) printf("--%s-not-strict ", LNAME);
+
+}
+
+static
+struct ip6tables_match optstruct
+= { NULL,
+#if HOPBYHOP
+    "hbh",
+#else
+    "dst",
+#endif
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_opts)),
+    IP6T_ALIGN(sizeof(struct ip6t_opts)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match6(&optstruct);
+}
diff --git a/extensions/libip6t_hl.c b/extensions/libip6t_hl.c
new file mode 100644 (file)
index 0000000..2d068b8
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * IPv6 Hop Limit matching module
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl match
+ * This program is released under the terms of GNU GPL
+ * Cleanups by Stephane Ouellette <ouellettes@videotron.ca>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ip6tables.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_hl.h>
+
+static void help(void) 
+{
+       printf(
+"HL match v%s options:\n"
+"  --hl-eq [!] value   Match hop limit value\n"
+"  --hl-lt value       Match HL < value\n"
+"  --hl-gt value       Match HL > value\n"
+, IPTABLES_VERSION);
+}
+
+static void init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       /* caching not yet implemented */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ip6t_entry *entry, unsigned int *nfcache,
+               struct ip6t_entry_match **match)
+{
+       struct ip6t_hl_info *info = (struct ip6t_hl_info *) (*match)->data;
+       u_int8_t value;
+
+       check_inverse(optarg, &invert, &optind, 0);
+       value = atoi(argv[optind-1]);
+
+       if (*flags) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify HL option twice");
+
+       if (!optarg)
+               exit_error(PARAMETER_PROBLEM,
+                               "hl: You must specify a value");
+       switch (c) {
+               case '2':
+                       if (invert)
+                               info->mode = IP6T_HL_NE;
+                       else
+                               info->mode = IP6T_HL_EQ;
+
+                       /* is 0 allowed? */
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               case '3':
+                       if (invert) 
+                               exit_error(PARAMETER_PROBLEM,
+                                               "hl: unexpected `!'");
+
+                       info->mode = IP6T_HL_LT;
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               case '4':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                               "hl: unexpected `!'");
+
+                       info->mode = IP6T_HL_GT;
+                       info->hop_limit = value;
+                       *flags = 1;
+
+                       break;
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags) 
+               exit_error(PARAMETER_PROBLEM,
+                       "HL match: You must specify one of "
+                       "`--hl-eq', `--hl-lt', `--hl-gt'");
+}
+
+static void print(const struct ip6t_ip6 *ip, 
+               const struct ip6t_entry_match *match,
+               int numeric)
+{
+       static const char *op[] = {
+               [IP6T_HL_EQ] = "==",
+               [IP6T_HL_NE] = "!=",
+               [IP6T_HL_LT] = "<",
+               [IP6T_HL_GT] = ">" };
+
+       const struct ip6t_hl_info *info = 
+               (struct ip6t_hl_info *) match->data;
+
+       printf("HL match HL %s %u ", op[info->mode], info->hop_limit);
+}
+
+static void save(const struct ip6t_ip6 *ip, 
+               const struct ip6t_entry_match *match)
+{
+       static const char *op[] = {
+               [IP6T_HL_EQ] = "eq",
+               [IP6T_HL_NE] = "eq !",
+               [IP6T_HL_LT] = "lt",
+               [IP6T_HL_GT] = "gt" };
+
+       const struct ip6t_hl_info *info =
+               (struct ip6t_hl_info *) match->data;
+
+       printf("--hl-%s %u ", op[info->mode], info->hop_limit);
+}
+
+static struct option opts[] = {
+       { .name = "hl",    .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "hl-eq", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "hl-lt", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = "hl-gt", .has_arg = 1, .flag = 0, .val = '4' },
+       { 0 }
+};
+
+static
+struct ip6tables_match hl = {
+       .name          = "hl",
+       .version       = IPTABLES_VERSION,
+       .size          = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
+       .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_hl_info)),
+       .help          = &help,
+       .init          = &init,
+       .parse         = &parse,
+       .final_check   = &final_check,
+       .print         = &print,
+       .save          = &save,
+       .extra_opts    = opts
+};
+
+
+void _init(void) 
+{
+       register_match6(&hl);
+}
diff --git a/extensions/libip6t_icmpv6.c b/extensions/libip6t_icmpv6.c
new file mode 100644 (file)
index 0000000..52d7871
--- /dev/null
@@ -0,0 +1,278 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+struct icmpv6_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmpv6_names icmpv6_codes[] = {
+       { "destination-unreachable", 1, 0, 0xFF },
+       {   "no-route", 1, 0, 0 },
+       {   "communication-prohibited", 1, 1, 1 },
+       {   "address-unreachable", 1, 3, 3 },
+       {   "port-unreachable", 1, 4, 4 },
+
+       { "packet-too-big", 2, 0, 0xFF },
+
+       { "time-exceeded", 3, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 3, 0, 0xFF },
+       {   "ttl-zero-during-transit", 3, 0, 0 },
+       {   "ttl-zero-during-reassembly", 3, 1, 1 },
+
+       { "parameter-problem", 4, 0, 0xFF },
+       {   "bad-header", 4, 0, 0 },
+       {   "unknown-header-type", 4, 1, 1 },
+       {   "unknown-option", 4, 2, 2 },
+
+       { "echo-request", 128, 0, 0xFF },
+       /* Alias */ { "ping", 128, 0, 0xFF },
+
+       { "echo-reply", 129, 0, 0xFF },
+       /* Alias */ { "pong", 129, 0, 0xFF },
+
+       { "router-solicitation", 133, 0, 0xFF },
+
+       { "router-advertisement", 134, 0, 0xFF },
+
+       { "neighbour-solicitation", 135, 0, 0xFF },
+       /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF },
+
+       { "neighbour-advertisement", 136, 0, 0xFF },
+       /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF },
+
+       { "redirect", 137, 0, 0xFF },
+
+};
+
+static void
+print_icmpv6types()
+{
+       unsigned int i;
+       printf("Valid ICMPv6 Types:");
+
+       for (i = 0; i < sizeof(icmpv6_codes)/sizeof(struct icmpv6_names); i++) {
+               if (i && icmpv6_codes[i].type == icmpv6_codes[i-1].type) {
+                       if (icmpv6_codes[i].code_min == icmpv6_codes[i-1].code_min
+                           && (icmpv6_codes[i].code_max
+                               == icmpv6_codes[i-1].code_max))
+                               printf(" (%s)", icmpv6_codes[i].name);
+                       else
+                               printf("\n   %s", icmpv6_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmpv6_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMPv6 v%s options:\n"
+" --icmpv6-type [!] typename   match icmpv6 type\n"
+"                              (or numeric type or type/code)\n"
+"\n", IPTABLES_VERSION);
+       print_icmpv6types();
+}
+
+static struct option opts[] = {
+       { "icmpv6-type", 1, 0, '1' },
+       {0}
+};
+
+static unsigned int
+parse_icmpv6(const char *icmpv6type, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmpv6_codes)/sizeof(struct icmpv6_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmpv6_codes[i].name, icmpv6type, strlen(icmpv6type))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMPv6 type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmpv6type,
+                                          icmpv6_codes[match].name,
+                                          icmpv6_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmpv6_codes[match].type;
+               code[0] = icmpv6_codes[match].code_min;
+               code[1] = icmpv6_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmpv6type) + 1];
+               unsigned int number;
+
+               strcpy(buffer, icmpv6type);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               if (string_to_number(buffer, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMPv6 type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       if (string_to_number(slash+1, 0, 255, &number) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMPv6 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_IP6_SRC_PT;
+       else return NFC_IP6_SRC_PT | NFC_IP6_DST_PT;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)m->data;
+
+       icmpv6info->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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_icmp *icmpv6info = (struct ip6t_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               *nfcache |= parse_icmpv6(argv[optind-1],
+                                      &icmpv6info->type,
+                                      icmpv6info->code);
+               if (invert)
+                       icmpv6info->invflags |= IP6T_ICMP_INV;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmpv6type(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(icmpv6_codes)/sizeof(struct icmpv6_names);
+                    i++) {
+                       if (icmpv6_codes[i].type == type
+                           && icmpv6_codes[i].code_min == code_min
+                           && icmpv6_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmpv6_codes)/sizeof(struct icmpv6_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmpv6_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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+       printf("ipv6-icmp ");
+       print_icmpv6type(icmpv6->type, icmpv6->code[0], icmpv6->code[1],
+                      icmpv6->invflags & IP6T_ICMP_INV,
+                      numeric);
+
+       if (icmpv6->invflags & ~IP6T_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmpv6->invflags & ~IP6T_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_icmp *icmpv6 = (struct ip6t_icmp *)match->data;
+
+       if (icmpv6->invflags & IP6T_ICMP_INV)
+               printf("! ");
+
+       printf("--icmpv6-type %u", icmpv6->type);
+       if (icmpv6->code[0] != 0 || icmpv6->code[1] != 0xFF)
+               printf("/%u", icmpv6->code[0]);
+       printf(" ");
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+static struct ip6tables_match icmpv6
+= { NULL,
+    "icmp6",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+    IP6T_ALIGN(sizeof(struct ip6t_icmp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&icmpv6);
+}
diff --git a/extensions/libip6t_ipv6header.c b/extensions/libip6t_ipv6header.c
new file mode 100644 (file)
index 0000000..3437e22
--- /dev/null
@@ -0,0 +1,319 @@
+/* ipv6header match - matches IPv6 packets based
+on whether they contain certain headers */
+
+/* Original idea: Brad Chapman 
+ * Rewritten by: Andras Kis-Szabo <kisza@sch.bme.hu> */
+
+#include <getopt.h>
+#include <ip6tables.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/types.h>
+
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_ipv6header.h>
+
+/* This maybe required 
+#include <linux/in.h>
+#include <linux/in6.h>
+*/
+
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+ *    /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+struct numflag {
+       u_int8_t proto;
+       u_int8_t flag;
+};
+
+static const struct pprot chain_protos[] = {
+       { "hop-by-hop", IPPROTO_HOPOPTS },
+       { "protocol", IPPROTO_RAW },
+       { "hop", IPPROTO_HOPOPTS },
+       { "dst", IPPROTO_DSTOPTS },
+       { "route", IPPROTO_ROUTING },
+       { "frag", IPPROTO_FRAGMENT },
+       { "auth", IPPROTO_AH },
+       { "esp", IPPROTO_ESP },
+       { "none", IPPROTO_NONE },
+       { "prot", IPPROTO_RAW },
+       { "0", IPPROTO_HOPOPTS },
+       { "60", IPPROTO_DSTOPTS },
+       { "43", IPPROTO_ROUTING },
+       { "44", IPPROTO_FRAGMENT },
+       { "51", IPPROTO_AH },
+       { "50", IPPROTO_ESP },
+       { "59", IPPROTO_NONE },
+       { "255", IPPROTO_RAW },
+       /* { "all", 0 }, */
+};
+
+static const struct numflag chain_flags[] = {
+       { IPPROTO_HOPOPTS, MASK_HOPOPTS },
+       { IPPROTO_DSTOPTS, MASK_DSTOPTS },
+       { IPPROTO_ROUTING, MASK_ROUTING },
+       { IPPROTO_FRAGMENT, MASK_FRAGMENT },
+       { IPPROTO_AH, MASK_AH },
+       { IPPROTO_ESP, MASK_ESP },
+       { IPPROTO_NONE, MASK_NONE },
+       { IPPROTO_RAW, MASK_PROTO },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+        unsigned int i;
+
+        if (proto && !nolookup) {
+                struct protoent *pent = getprotobynumber(proto);
+                if (pent)
+                        return pent->p_name;
+        }
+
+        for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+                if (chain_protos[i].num == proto)
+                        return chain_protos[i].name;
+
+        return NULL;
+}
+
+static u_int16_t
+name_to_proto(const char *s)
+{
+        unsigned int proto=0;
+        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 header `%s' specified",
+                               s);
+        }
+
+        return (u_int16_t)proto;
+}
+
+static unsigned int 
+add_proto_to_mask(int proto){
+       unsigned int i=0, flag=0;
+
+       for (i = 0;
+               i < sizeof(chain_flags)/sizeof(struct numflag);
+               i++) {
+                       if (proto == chain_flags[i].proto){
+                               flag = chain_flags[i].flag;
+                               break;
+                       }
+       }
+
+       if (i == sizeof(chain_flags)/sizeof(struct numflag))
+               exit_error(PARAMETER_PROBLEM,
+               "unknown header `%d' specified",
+               proto);
+       
+       return flag;
+}      
+
+static void
+help(void)
+{
+       printf(
+"ipv6header v%s match options:\n"
+"--header [!] headers     Type of header to match, by name\n"
+"                         names: hop,dst,route,frag,auth,esp,none,proto\n"
+"                    long names: hop-by-hop,ipv6-opts,ipv6-route,\n"
+"                                ipv6-frag,ah,esp,ipv6-nonxt,protocol\n"
+"                       numbers: 0,60,43,44,51,50,59\n"
+"--soft                    The header CONTAINS the specified extensions\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "header", 1, 0, '1' },
+       { "soft", 0, 0, '2' },
+       { 0 }
+};
+
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)m->data;
+
+       info->matchflags = 0x00;
+       info->invflags = 0x00;
+       info->modeflag = 0x00;
+       /* No caching (yet) */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static unsigned int
+parse_header(const char *flags) {
+        unsigned int ret = 0;
+        char *ptr;
+        char *buffer;
+
+        buffer = strdup(flags);
+
+        for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) 
+               ret |= add_proto_to_mask(name_to_proto(ptr));
+                
+        free(buffer);
+        return ret;
+}
+
+#define IPV6_HDR_HEADER        0x01
+#define IPV6_HDR_SOFT  0x02
+
+/* Parses command options; returns 0 if it ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_ipv6header_info *info = (struct ip6t_ipv6header_info *)(*match)->data;
+
+       switch (c) {
+               case '1' : 
+                       /* Parse the provided header names */
+                       if (*flags & IPV6_HDR_HEADER)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Only one `--header' allowed");
+
+                       check_inverse(optarg, &invert, &optind, 0);
+
+                       if (! (info->matchflags = parse_header(argv[optind-1])) )
+                               exit_error(PARAMETER_PROBLEM, "ip6t_ipv6header: cannot parse header names");
+
+                       if (invert) 
+                               info->invflags |= 0xFF;
+                       *flags |= IPV6_HDR_HEADER;
+                       break;
+               case '2' : 
+                       /* Soft-mode requested? */
+                       if (*flags & IPV6_HDR_SOFT)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Only one `--soft' allowed");
+
+                       info->modeflag |= 0xFF;
+                       *flags |= IPV6_HDR_SOFT;
+                       break;
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Checks the flags variable */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags) exit_error(PARAMETER_PROBLEM, "ip6t_ipv6header: no options specified");
+}
+
+static void
+print_header(u_int8_t flags){
+        int have_flag = 0;
+
+        while (flags) {
+                unsigned int i;
+
+                for (i = 0; (flags & chain_flags[i].flag) == 0; i++);
+
+                if (have_flag)
+                        printf(",");
+
+                printf("%s", proto_to_name(chain_flags[i].proto,0));
+                have_flag = 1;
+
+                flags &= ~chain_flags[i].flag;
+        }
+
+        if (!have_flag)
+                printf("NONE");
+}
+
+/* Prints out the match */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+       printf("ipv6header ");
+
+        if (info->matchflags || info->invflags) {
+                printf("flags:%s", info->invflags ? "!" : "");
+                if (numeric)
+                        printf("0x%02X ", info->matchflags);
+                else {
+                        print_header(info->matchflags);
+                        printf(" ");
+                }
+        }
+
+       if (info->modeflag)
+               printf("soft ");
+
+       return;
+}
+
+/* Saves the match */
+static void
+save(const struct ip6t_ip6 *ip,
+     const struct ip6t_entry_match *match)
+{
+
+       const struct ip6t_ipv6header_info *info = (const struct ip6t_ipv6header_info *)match->data;
+
+       printf("--header ");
+       printf("%s", info->invflags ? "!" : "");
+       print_header(info->matchflags);
+       printf(" ");
+       if (info->modeflag)
+               printf("--soft ");
+
+       return;
+}
+
+static
+struct ip6tables_match ipv6header
+= { NULL,
+    "ipv6header",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_ipv6header_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&ipv6header);
+}
diff --git a/extensions/libip6t_length.c b/extensions/libip6t_length.c
new file mode 100644 (file)
index 0000000..b8fa15b
--- /dev/null
@@ -0,0 +1,161 @@
+/* Shared library add-on to ip6tables to add packet length matching support. */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_length.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"length v%s options:\n"
+"[!] --length length[:length]    Match packet length against value or range\n"
+"                                of values (inclusive)\n",
+IPTABLES_VERSION);
+
+}
+  
+static struct option opts[] = {
+       { "length", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static u_int16_t
+parse_length(const char *s)
+{
+
+       int len;
+       
+       if (string_to_number(s, 0, 0xFFFF, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s);
+       else
+               return (u_int16_t )len;
+}
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_lengths(const char *s, struct ip6t_length_info *info)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               info->min = info->max = parse_length(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               info->min = buffer[0] ? parse_length(buffer) : 0;
+               info->max = cp[0] ? parse_length(cp) : 0xFFFF;
+       }
+       free(buffer);
+       
+       if (info->min > info->max)
+               exit_error(PARAMETER_PROBLEM,
+                          "length min. range value `%u' greater than max. "
+                          "range value `%u'", info->min, info->max);
+       
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_length_info *info = (struct ip6t_length_info *)(*match)->data;
+
+       switch (c) {
+               case '1':
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "length: `--length' may only be "
+                                          "specified once");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_lengths(argv[optind-1], info);
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               default:
+                       return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --length. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "length: You must specify `--length'");
+}
+
+/* Common match printing code. */
+static void
+print_length(struct ip6t_length_info *info)
+{
+       if (info->invert)
+               printf("! ");
+       
+       if (info->max == info->min)
+               printf("%u ", info->min);
+       else
+               printf("%u:%u ", info->min, info->max);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("length ");
+       print_length((struct ip6t_length_info *)match->data);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       printf("--length ");
+       print_length((struct ip6t_length_info *)match->data);
+}
+
+struct ip6tables_match length
+= { NULL,
+    "length",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_length_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&length);
+}
diff --git a/extensions/libip6t_limit.c b/extensions/libip6t_limit.c
new file mode 100644 (file)
index 0000000..9516252
--- /dev/null
@@ -0,0 +1,199 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <stddef.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_limit.h>
+
+#define IP6T_LIMIT_AVG "3/hour"
+#define IP6T_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 "IP6T_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", IPTABLES_VERSION, IP6T_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 > IP6T_LIMIT_SCALE)
+               exit_error(PARAMETER_PROBLEM, "Rate too fast `%s'\n", rate);
+
+       *val = IP6T_LIMIT_SCALE * mult / r;
+       return 1;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)m->data;
+
+       parse_rate(IP6T_LIMIT_AVG, &r->avg);
+       r->burst = IP6T_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)(*match)->data;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       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, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --limit-burst");
+
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       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", IP6T_LIMIT_SCALE*24*60*60 },
+             { "hour", IP6T_LIMIT_SCALE*60*60 },
+             { "min", IP6T_LIMIT_SCALE*60 },
+             { "sec", IP6T_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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_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 ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_rateinfo *r = (struct ip6t_rateinfo *)match->data;
+
+       printf("--limit "); print_rate(r->avg);
+       if (r->burst != IP6T_LIMIT_BURST)
+               printf("--limit-burst %u ", r->burst);
+}
+
+static
+struct ip6tables_match limit
+= { NULL,
+    "limit",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_rateinfo)),
+    offsetof(struct ip6t_rateinfo, prev),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&limit);
+}
diff --git a/extensions/libip6t_mac.c b/extensions/libip6t_mac.c
new file mode 100644 (file)
index 0000000..617b353
--- /dev/null
@@ -0,0 +1,150 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_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", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mac-source", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static void
+parse_mac(const char *mac, struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_mac_info *macinfo = (struct ip6t_mac_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               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])
+{
+       unsigned int i;
+
+       printf("%02X", 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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("MAC ");
+
+       if (((struct ip6t_mac_info *)match->data)->invert)
+               printf("! ");
+
+       print_mac(((struct ip6t_mac_info *)match->data)->srcaddr);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       if (((struct ip6t_mac_info *)match->data)->invert)
+               printf("! ");
+
+       printf("--mac-source ");
+       print_mac(((struct ip6t_mac_info *)match->data)->srcaddr);
+}
+
+static
+struct ip6tables_match mac
+= { NULL,
+    "mac",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_mac_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&mac);
+}
diff --git a/extensions/libip6t_mark.c b/extensions/libip6t_mark.c
new file mode 100644 (file)
index 0000000..4aa606e
--- /dev/null
@@ -0,0 +1,129 @@
+/* Shared library add-on to ip6tables to add NFMARK matching support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_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",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mark", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_mark_info *markinfo = (struct ip6t_mark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       printf("MARK match ");
+       print_mark(((struct ip6t_mark_info *)match->data)->mark,
+                 ((struct ip6t_mark_info *)match->data)->mask,
+                 ((struct ip6t_mark_info *)match->data)->invert, numeric);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       printf("--mark ");
+       print_mark(((struct ip6t_mark_info *)match->data)->mark,
+                 ((struct ip6t_mark_info *)match->data)->mask,
+                 ((struct ip6t_mark_info *)match->data)->invert, 0);
+}
+
+static
+struct ip6tables_match mark
+= { NULL,
+    "mark",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_mark_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&mark);
+}
diff --git a/extensions/libip6t_multiport.c b/extensions/libip6t_multiport.c
new file mode 100644 (file)
index 0000000..dc5bbf4
--- /dev/null
@@ -0,0 +1,265 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if ((string_to_number(port, 0, 65535, &portnum)) != -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<IP6T_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 ip6t_entry_match *m, unsigned int *nfcache)
+{
+}
+
+static const char *
+check_proto(const struct ip6t_entry *entry)
+{
+       if (entry->ipv6.proto == IPPROTO_TCP)
+               return "tcp";
+       else if (entry->ipv6.proto == IPPROTO_UDP)
+               return "udp";
+       else if (!entry->ipv6.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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       const char *proto;
+       struct ip6t_multiport *multiinfo
+               = (struct ip6t_multiport *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_SOURCE;
+               *nfcache |= NFC_IP6_SRC_PT;
+               break;
+
+       case '2':
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_DESTINATION;
+               *nfcache |= NFC_IP6_DST_PT;
+               break;
+
+       case '3':
+               proto = check_proto(entry);
+               multiinfo->count = parse_multi_ports(argv[optind-1],
+                                                    multiinfo->ports, proto);
+               multiinfo->flags = IP6T_MULTIPORT_EITHER;
+               *nfcache |= NFC_IP6_SRC_PT | NFC_IP6_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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_multiport *multiinfo
+               = (const struct ip6t_multiport *)match->data;
+       unsigned int i;
+
+       printf("multiport ");
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IP6T_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 ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_multiport *multiinfo
+               = (const struct ip6t_multiport *)match->data;
+       unsigned int i;
+
+       switch (multiinfo->flags) {
+       case IP6T_MULTIPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IP6T_MULTIPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IP6T_MULTIPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       for (i=0; i < multiinfo->count; i++) {
+               printf("%s", i ? "," : "");
+               print_port(multiinfo->ports[i], ip->proto, 1);
+       }
+       printf(" ");
+}
+
+static
+struct ip6tables_match multiport
+= { NULL,
+    "multiport",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+    IP6T_ALIGN(sizeof(struct ip6t_multiport)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match6(&multiport);
+}
diff --git a/extensions/libip6t_nth.c b/extensions/libip6t_nth.c
new file mode 100644 (file)
index 0000000..638074d
--- /dev/null
@@ -0,0 +1,238 @@
+/* 
+   Shared library add-on to iptables to add match support for every Nth packet
+   
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+   2001-07-17 Fabrice MARIE <fabrice@netfilter.org> : initial development.
+   2001-09-20 Richard Wagner (rwagner@cloudnet.com)
+        * added support for multiple counters
+        * added support for matching on individual packets
+          in the counter cycle
+*/
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_nth.h>
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"nth v%s options:\n"
+"   --every     Nth              Match every Nth packet\n"
+"  [--counter]  num              Use counter 0-%u (default:0)\n"
+"  [--start]    num              Initialize the counter at the number 'num'\n"
+"                                instead of 0. Must be between 0 and Nth-1\n"
+"  [--packet]   num              Match on 'num' packet. Must be between 0\n"
+"                                and Nth-1.\n\n"
+"                                If --packet is used for a counter than\n"
+"                                there must be Nth number of --packet\n"
+"                                rules, covering all values between 0 and\n"
+"                                Nth-1 inclusively.\n",
+IPTABLES_VERSION, IP6T_NTH_NUM_COUNTERS-1);
+}
+
+static struct option opts[] = {
+       { "every", 1, 0, '1' },
+       { "start", 1, 0, '2' },
+        { "counter", 1, 0, '3' },
+        { "packet", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+#define IP6T_NTH_OPT_EVERY     0x01
+#define IP6T_NTH_OPT_NOT_EVERY 0x02
+#define IP6T_NTH_OPT_START     0x04
+#define IP6T_NTH_OPT_COUNTER     0x08
+#define IP6T_NTH_OPT_PACKET      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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_nth_info *nthinfo = (struct ip6t_nth_info *)(*match)->data;
+       unsigned int num;
+
+       switch (c) {
+       case '1':
+               /* check for common mistakes... */
+               if ((!invert) && (*flags & IP6T_NTH_OPT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --every twice");
+               if (invert && (*flags & IP6T_NTH_OPT_NOT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --every twice");
+               if ((!invert) && (*flags & IP6T_NTH_OPT_NOT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --every with ! --every");
+               if (invert && (*flags & IP6T_NTH_OPT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --every with --every");
+
+               /* Remember, this function will interpret a leading 0 to be 
+                  Octal, a leading 0x to be hexdecimal... */
+                if (string_to_number(optarg, 2, 100, &num) == -1 || num < 2)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --every `%s', must be between 2 and 100", optarg);
+
+               /* assign the values */
+               nthinfo->every = num-1;
+               nthinfo->startat = 0;
+                nthinfo->packet = 0xFF;
+                if(!(*flags & IP6T_NTH_OPT_EVERY))
+                {
+                        nthinfo->counter = 0;
+                }
+               if (invert)
+               {
+                       *flags |= IP6T_NTH_OPT_NOT_EVERY;
+                       nthinfo->not = 1;
+               }
+               else
+               {
+                       *flags |= IP6T_NTH_OPT_EVERY;
+                       nthinfo->not = 0;
+               }
+               break;
+       case '2':
+               /* check for common mistakes... */
+               if (!((*flags & IP6T_NTH_OPT_EVERY) ||
+                     (*flags & IP6T_NTH_OPT_NOT_EVERY)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --start before --every");
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify with ! --start");
+               if (*flags & IP6T_NTH_OPT_START)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --start twice");
+               if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --start `%s', must between 0 and %u", optarg, nthinfo->every);
+               *flags |= IP6T_NTH_OPT_START;
+               nthinfo->startat = num;
+               break;
+        case '3':
+                /* check for common mistakes... */
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify with ! --counter");
+                if (*flags & IP6T_NTH_OPT_COUNTER)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --counter twice");
+                if (string_to_number(optarg, 0, IP6T_NTH_NUM_COUNTERS-1, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --counter `%s', must between 0 and %u", optarg, IP6T_NTH_NUM_COUNTERS-1);
+                /* assign the values */
+                *flags |= IP6T_NTH_OPT_COUNTER;
+                nthinfo->counter = num;
+                break;
+        case '4':
+                /* check for common mistakes... */
+                if (!((*flags & IP6T_NTH_OPT_EVERY) ||
+                      (*flags & IP6T_NTH_OPT_NOT_EVERY)))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet before --every");
+                if ((*flags & IP6T_NTH_OPT_NOT_EVERY))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet with ! --every");
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify with ! --packet");
+                if (*flags & IP6T_NTH_OPT_PACKET)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet twice");
+                if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --packet `%s', must between 0 and %u", optarg, nthinfo->every);
+                *flags |= IP6T_NTH_OPT_PACKET;
+                nthinfo->packet = num;
+                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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_nth_info *nthinfo
+               = (const struct ip6t_nth_info *)match->data;
+
+       if (nthinfo->not == 1)
+               printf(" !");
+       printf("every %uth ", (nthinfo->every +1));
+       if (nthinfo->counter != 0) 
+               printf("counter #%u ", (nthinfo->counter));
+        if (nthinfo->packet != 0xFF)
+                printf("packet #%u ", nthinfo->packet);
+       if (nthinfo->startat != 0)
+               printf("start at %u ", nthinfo->startat);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_nth_info *nthinfo
+               = (const struct ip6t_nth_info *)match->data;
+
+       if (nthinfo->not == 1)
+               printf("! ");
+       printf("--every %u ", (nthinfo->every +1));
+       printf("--counter %u ", (nthinfo->counter));
+       if (nthinfo->startat != 0)
+               printf("--start %u ", nthinfo->startat );
+        if (nthinfo->packet != 0xFF)
+                printf("--packet %u ", nthinfo->packet );
+}
+
+struct ip6tables_match nth
+= { NULL,
+    "nth",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_nth_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&nth);
+}
diff --git a/extensions/libip6t_owner.c b/extensions/libip6t_owner.c
new file mode 100644 (file)
index 0000000..ed78530
--- /dev/null
@@ -0,0 +1,258 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6t_owner.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+#ifdef IP6T_OWNER_COMM
+       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"
+"[!] --cmd-owner name       Match local command name\n"
+"\n",
+IPTABLES_VERSION);
+#else
+       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",
+IPTABLES_VERSION);
+#endif /* IP6T_OWNER_COMM */
+}
+
+static struct option opts[] = {
+       { "uid-owner", 1, 0, '1' },
+       { "gid-owner", 1, 0, '2' },
+       { "pid-owner", 1, 0, '3' },
+       { "sid-owner", 1, 0, '4' },
+#ifdef IP6T_OWNER_COMM
+       { "cmd-owner", 1, 0, '5' },
+#endif
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_owner_info *ownerinfo = (struct ip6t_owner_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+               struct passwd *pwd;
+               struct group *grp;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               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 |= IP6T_OWNER_UID;
+               ownerinfo->match |= IP6T_OWNER_UID;
+               *flags = 1;
+               break;
+
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 |= IP6T_OWNER_GID;
+               ownerinfo->match |= IP6T_OWNER_GID;
+               *flags = 1;
+               break;
+
+       case '3':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 |= IP6T_OWNER_PID;
+               ownerinfo->match |= IP6T_OWNER_PID;
+               *flags = 1;
+               break;
+
+       case '4':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 |= IP6T_OWNER_SID;
+               ownerinfo->match |= IP6T_OWNER_SID;
+               *flags = 1;
+               break;
+
+#ifdef IP6T_OWNER_COMM
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+               if(strlen(optarg) > sizeof(ownerinfo->comm))
+                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %d characters", optarg, sizeof(ownerinfo->comm));
+               
+               strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+
+               if (invert)
+                       ownerinfo->invert |= IP6T_OWNER_COMM;
+               ownerinfo->match |= IP6T_OWNER_COMM;
+               *flags = 1;
+               break;
+#endif
+               
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_item(struct ip6t_owner_info *info, u_int8_t flag, int numeric, char *label)
+{
+       if(info->match & flag) {
+
+               printf(label);
+
+               if (info->invert & flag)
+                       printf("! ");
+
+               switch(info->match & flag) {
+               case IP6T_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 IP6T_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 IP6T_OWNER_PID:
+                       printf("%u ", info->pid);
+                       break;
+               case IP6T_OWNER_SID:
+                       printf("%u ", info->sid);
+                       break;
+#ifdef IP6T_OWNER_COMM
+               case IP6T_OWNER_COMM:
+                       printf("%.*s ", (int)sizeof(info->comm), info->comm);
+                       break;
+#endif
+               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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       struct ip6t_owner_info *info = (struct ip6t_owner_info *)match->data;
+
+       print_item(info, IP6T_OWNER_UID, numeric, "OWNER UID match ");
+       print_item(info, IP6T_OWNER_GID, numeric, "OWNER GID match ");
+       print_item(info, IP6T_OWNER_PID, numeric, "OWNER PID match ");
+       print_item(info, IP6T_OWNER_SID, numeric, "OWNER SID match ");
+#ifdef IP6T_OWNER_COMM
+       print_item(info, IP6T_OWNER_COMM, numeric, "OWNER CMD match ");
+#endif
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       struct ip6t_owner_info *info = (struct ip6t_owner_info *)match->data;
+
+       print_item(info, IP6T_OWNER_UID, 0, "--uid-owner ");
+       print_item(info, IP6T_OWNER_GID, 0, "--gid-owner ");
+       print_item(info, IP6T_OWNER_PID, 0, "--pid-owner ");
+       print_item(info, IP6T_OWNER_SID, 0, "--sid-owner ");
+#ifdef IP6T_OWNER_COMM
+       print_item(info, IP6T_OWNER_COMM, 0, "--cmd-owner ");
+#endif
+}
+
+static
+struct ip6tables_match owner
+= { NULL,
+    "owner",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_owner_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&owner);
+}
diff --git a/extensions/libip6t_random.c b/extensions/libip6t_random.c
new file mode 100644 (file)
index 0000000..5f151ea
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+   Shared library add-on to iptables to add match support for random match.
+   
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+   2001-10-14 Fabrice MARIE <fabrice@netfilter.org> : initial development.
+   2003-04-30 Maciej Soltysiak <solt@dns.toxicfilms.tv> : IPv6 port.
+*/
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/netfilter_ipv6/ip6t_random.h>
+
+/**
+ * The kernel random routing returns numbers between 0 and 255.
+ * To ease the task of the user in choosing the probability
+ * of matching, we want him to be able to use percentages.
+ * Therefore we have to accept numbers in percentage here,
+ * turn them into number between 0 and 255 for the kernel module,
+ * and turn them back to percentages when we print/save
+ * the rule.
+ */
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"random v%s options:\n"
+"  [--average]     percent      The probability in percentage of the match\n"
+"                               If ommited, a probability of 50%% percent is set.\n"
+"                               Percentage must be within : 1 <= percent <= 99.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "average", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_rand_info *randinfo = (struct ip6t_rand_info *)(m)->data;
+       *nfcache |= NFC_UNKNOWN;
+
+       /* We assign the average to be 50 which is our default value */
+       /* 50 * 2.55 = 128 */
+       randinfo->average = 128;
+}
+
+#define IP6T_RAND_OPT_AVERAGE  0x01
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_rand_info *randinfo = (struct ip6t_rand_info *)(*match)->data;
+       unsigned int num;
+
+       switch (c) {
+       case '1':
+               /* check for common mistakes... */
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --average");
+               if (*flags & IP6T_RAND_OPT_AVERAGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --average twice");
+
+               /* Remember, this function will interpret a leading 0 to be 
+                  Octal, a leading 0x to be hexdecimal... */
+                if (string_to_number(optarg, 1, 99, &num) == -1 || num < 1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --average `%s', must be between 1 and 99", optarg);
+
+               /* assign the values */
+               randinfo->average = (int)(num * 2.55);
+               *flags |= IP6T_RAND_OPT_AVERAGE;
+               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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match,
+      int numeric)
+{
+       const struct ip6t_rand_info *randinfo
+               = (const struct ip6t_rand_info *)match->data;
+       div_t result = div((randinfo->average*100), 255);
+       if (result.rem > 127)  /* round up... */
+               ++result.quot;
+
+       printf(" random %u%% ", result.quot);
+}
+
+/* Saves the union ip6t_targinfo in parsable form to stdout. */
+static void
+save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_rand_info *randinfo
+               = (const struct ip6t_rand_info *)match->data;
+       div_t result = div((randinfo->average *100), 255);
+       if (result.rem > 127)  /* round up... */
+               ++result.quot;
+
+       printf("--average %u ", result.quot);
+}
+
+struct ip6tables_match rand_match
+= { NULL,
+    "random",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
+    IP6T_ALIGN(sizeof(struct ip6t_rand_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match6(&rand_match);
+}
diff --git a/extensions/libip6t_rt.c b/extensions/libip6t_rt.c
new file mode 100644 (file)
index 0000000..06cab16
--- /dev/null
@@ -0,0 +1,364 @@
+/* Shared library add-on to ip6tables to add Routing header support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ip6tables.h>
+/*#include <linux/in6.h>*/
+#include <linux/netfilter_ipv6/ip6t_rt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+                                        
+/*#define DEBUG        1*/
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"RT v%s options:\n"
+" --rt-type [!] type            match the type\n"
+" --rt-segsleft [!] num[:num]   match the Segments Left field (range)\n"
+" --rt-len [!] length           total length of this header\n"
+" --rt-0-res                    check the reserved filed, too (type 0)\n"
+" --rt-0-addrs ADDR[,ADDR...]   Type=0 addresses (list, max: %d)\n"
+" --rt-0-not-strict             List of Type=0 addresses not a strict list\n",
+IPTABLES_VERSION, IP6T_RT_HOPS);
+}
+
+static struct option opts[] = {
+       { "rt-type", 1, 0, '1' },
+       { "rt-segsleft", 1, 0, '2' },
+       { "rt-len", 1, 0, '3' },
+       { "rt-0-res", 0, 0, '4' },
+       { "rt-0-addrs", 1, 0, '5' },
+       { "rt-0-not-strict", 0, 0, '6' },
+       {0}
+};
+
+static u_int32_t
+parse_rt_num(const char *idstr, const char *typestr)
+{
+       unsigned long int id;
+       char* ep;
+
+       id =  strtoul(idstr,&ep,0) ;
+
+       if ( idstr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "RT no valid digits in %s `%s'", typestr, idstr);
+       }
+       if ( id == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "%s `%s' specified too big: would overflow",
+                          typestr, idstr);
+       }       
+       if ( *idstr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "RT error parsing %s `%s'", typestr, idstr);
+       }
+       return (u_int32_t) id;
+}
+
+static void
+parse_rt_segsleft(const char *idstring, u_int32_t *ids)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(idstring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               ids[0] = ids[1] = parse_rt_num(buffer,"segsleft");
+       else {
+               *cp = '\0';
+               cp++;
+
+               ids[0] = buffer[0] ? parse_rt_num(buffer,"segsleft") : 0;
+               ids[1] = cp[0] ? parse_rt_num(cp,"segsleft") : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+#ifdef DEBUG
+       fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+        exit_error(PARAMETER_PROBLEM, "bad address: %s", num);
+
+       return (struct in6_addr *)NULL;
+}
+
+
+static int
+parse_addresses(const char *addrstr, struct in6_addr *addrp)
+{
+        char *buffer, *cp, *next;
+        unsigned int i;
+       
+       buffer = strdup(addrstr);
+        if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+                       
+        for (cp=buffer, i=0; cp && i<IP6T_RT_HOPS; cp=next,i++)
+        {
+                next=strchr(cp, ',');
+                if (next) *next++='\0';
+               memcpy(&(addrp[i]), numeric_to_addr(cp), sizeof(struct in6_addr));
+#if DEBUG
+               printf("addr str: %s\n", cp);
+               printf("addr ip6: %s\n", addr_to_numeric((numeric_to_addr(cp))));
+               printf("addr [%d]: %s\n", i, addr_to_numeric(&(addrp[i])));
+#endif
+       }
+        if (cp) exit_error(PARAMETER_PROBLEM, "too many addresses specified");
+
+       free(buffer);
+
+#if DEBUG
+       printf("addr nr: %d\n", i);
+#endif
+
+       return i;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_rt *rtinfo = (struct ip6t_rt *)m->data;
+
+       rtinfo->rt_type = 0x0L;
+       rtinfo->segsleft[0] = 0x0L;
+       rtinfo->segsleft[1] = 0xFFFFFFFF;
+       rtinfo->hdrlen = 0;
+       rtinfo->flags = 0;
+       rtinfo->invflags = 0;
+       rtinfo->addrnr = 0;
+}
+
+/* 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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_rt *rtinfo = (struct ip6t_rt *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IP6T_RT_TYP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-type' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               rtinfo->rt_type = parse_rt_num(argv[optind-1], "type");
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_TYP;
+               rtinfo->flags |= IP6T_RT_TYP;
+               *flags |= IP6T_RT_TYP;
+               break;
+       case '2':
+               if (*flags & IP6T_RT_SGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-segsleft' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_rt_segsleft(argv[optind-1], rtinfo->segsleft);
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_SGS;
+               rtinfo->flags |= IP6T_RT_SGS;
+               *flags |= IP6T_RT_SGS;
+               break;
+       case '3':
+               if (*flags & IP6T_RT_LEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-len' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               rtinfo->hdrlen = parse_rt_num(argv[optind-1], "length");
+               if (invert)
+                       rtinfo->invflags |= IP6T_RT_INV_LEN;
+               rtinfo->flags |= IP6T_RT_LEN;
+               *flags |= IP6T_RT_LEN;
+               break;
+       case '4':
+               if (*flags & IP6T_RT_RES)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-res' allowed");
+               if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-type 0' required before `--rt-0-res'");
+               rtinfo->flags |= IP6T_RT_RES;
+               *flags |= IP6T_RT_RES;
+               break;
+       case '5':
+               if (*flags & IP6T_RT_FST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-addrs' allowed");
+               if ( !(*flags & IP6T_RT_TYP) || (rtinfo->rt_type != 0) || (rtinfo->invflags & IP6T_RT_INV_TYP) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-type 0' required before `--rt-0-addrs'");
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  " '!' not allowed with `--rt-0-addrs'");
+               rtinfo->addrnr = parse_addresses(argv[optind-1], rtinfo->addrs);
+               rtinfo->flags |= IP6T_RT_FST;
+               *flags |= IP6T_RT_FST;
+               break;
+       case '6':
+               if (*flags & IP6T_RT_FST_NSTRICT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--rt-0-not-strict' allowed");
+               if ( !(*flags & IP6T_RT_FST) )
+                       exit_error(PARAMETER_PROBLEM,
+                                  "`--rt-0-addr ...' required before `--rt-0-not-strict'");
+               rtinfo->flags |= IP6T_RT_FST_NSTRICT;
+               *flags |= IP6T_RT_FST_NSTRICT;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_nums(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               printf(" ");
+       }
+}
+
+static void
+print_addresses(int addrnr, struct in6_addr *addrp)
+{
+       unsigned int i;
+
+       for(i=0; i<addrnr; i++){
+               printf("%s%c", addr_to_numeric(&(addrp[i])), (i!=addrnr-1)?',':' ');
+       }
+}
+
+/* Prints out the union ip6t_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+       printf("rt ");
+       if (rtinfo->flags & IP6T_RT_TYP)
+           printf("type:%s%d ", rtinfo->invflags & IP6T_RT_INV_TYP ? "!" : "",
+                   rtinfo->rt_type);
+       print_nums("segsleft", rtinfo->segsleft[0], rtinfo->segsleft[1],
+                   rtinfo->invflags & IP6T_RT_INV_SGS);
+       if (rtinfo->flags & IP6T_RT_LEN) {
+               printf("length");
+               printf(":%s", rtinfo->invflags & IP6T_RT_INV_LEN ? "!" : "");
+               printf("%u", rtinfo->hdrlen);
+               printf(" ");
+       }
+       if (rtinfo->flags & IP6T_RT_RES) printf("reserved ");
+       if (rtinfo->flags & IP6T_RT_FST) printf("0-addrs ");
+       print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+       if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("0-not-strict ");
+       if (rtinfo->invflags & ~IP6T_RT_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      rtinfo->invflags & ~IP6T_RT_INV_MASK);
+}
+
+/* Saves the union ip6t_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_rt *rtinfo = (struct ip6t_rt *)match->data;
+
+       if (rtinfo->flags & IP6T_RT_TYP) {
+               printf("--rt-type %s%u ", 
+                       (rtinfo->invflags & IP6T_RT_INV_TYP) ? "! " : "", 
+                       rtinfo->rt_type);
+       }
+
+       if (!(rtinfo->segsleft[0] == 0
+           && rtinfo->segsleft[1] == 0xFFFFFFFF)) {
+               printf("--rt-segsleft %s", 
+                       (rtinfo->invflags & IP6T_RT_INV_SGS) ? "! " : "");
+               if (rtinfo->segsleft[0]
+                   != rtinfo->segsleft[1])
+                       printf("%u:%u ",
+                              rtinfo->segsleft[0],
+                              rtinfo->segsleft[1]);
+               else
+                       printf("%u ",
+                              rtinfo->segsleft[0]);
+       }
+
+       if (rtinfo->flags & IP6T_RT_LEN) {
+               printf("--rt-len %s%u ", 
+                       (rtinfo->invflags & IP6T_RT_INV_LEN) ? "! " : "", 
+                       rtinfo->hdrlen);
+       }
+
+       if (rtinfo->flags & IP6T_RT_RES) printf("--rt-0-res ");
+       if (rtinfo->flags & IP6T_RT_FST) printf("--rt-0-addrs ");
+       print_addresses(rtinfo->addrnr, (struct in6_addr *)rtinfo->addrs);
+       if (rtinfo->flags & IP6T_RT_FST_NSTRICT) printf("--rt-0-not-strict ");
+
+}
+
+static
+struct ip6tables_match rt
+= { NULL,
+    "rt",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_rt)),
+    IP6T_ALIGN(sizeof(struct ip6t_rt)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match6(&rt);
+}
diff --git a/extensions/libip6t_standard.c b/extensions/libip6t_standard.c
new file mode 100644 (file)
index 0000000..b8a0ce7
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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 <ip6tables.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", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+/* Initialize the target. */
+static void
+init(struct ip6t_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 ip6t_entry *entry,
+      struct ip6t_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 ip6t_ip6 *ip6, const struct ip6t_entry_target *target)
+{
+}
+
+static
+struct ip6tables_target standard
+= { NULL,
+    "standard",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(int)),
+    IP6T_ALIGN(sizeof(int)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    NULL, /* print */
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target6(&standard);
+}
diff --git a/extensions/libip6t_tcp.c b/extensions/libip6t_tcp.c
new file mode 100644 (file)
index 0000000..a1a912c
--- /dev/null
@@ -0,0 +1,446 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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;
+               
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM, 
+                                  "invalid portrange (min > max)");
+       }
+       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'", ptr);
+               }
+
+       free(buffer);
+       return ret;
+}
+
+static void
+parse_tcp_flags(struct ip6t_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 |= IP6T_TCP_INV_FLAGS;
+}
+
+static void
+parse_tcp_option(const char *option, u_int8_t *result)
+{
+       unsigned int ret;
+
+       if (string_to_number(option, 1, 255, &ret) == -1)
+               exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
+
+       *result = (u_int8_t)ret;
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & TCP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->spts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_SRCPT;
+               *flags |= TCP_SRC_PORTS;
+               *nfcache |= NFC_IP6_SRC_PT;
+               break;
+
+       case '2':
+               if (*flags & TCP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_DSTPT;
+               *flags |= TCP_DST_PORTS;
+               *nfcache |= NFC_IP6_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_IP6_TCPFLAGS;
+               break;
+
+       case '4':
+               if (*flags & TCP_FLAGS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one of `--syn' or `--tcp-flags' "
+                                  " allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--tcp-flags requires two args.");
+
+               parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind], 
+                               invert);
+               optind++;
+               *flags |= TCP_FLAGS;
+               *nfcache |= NFC_IP6_TCPFLAGS;
+               break;
+
+       case '5':
+               if (*flags & TCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--tcp-option' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_option(argv[optind-1], &tcpinfo->option);
+               if (invert)
+                       tcpinfo->invflags |= IP6T_TCP_INV_OPTION;
+               *flags |= TCP_OPTION;
+               *nfcache |= NFC_IP6_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 have_flag = 0;
+
+       while (flags) {
+               unsigned int i;
+
+               for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", tcp_flag_names[i].name);
+               have_flag = 1;
+
+               flags &= ~tcp_flag_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+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("0x%02X/0x%02X ", mask, cmp);
+               else {
+                       print_tcpf(mask);
+                       printf("/");
+                       print_tcpf(cmp);
+                       printf(" ");
+               }
+       }
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_tcp *tcp = (struct ip6t_tcp *)match->data;
+
+       printf("tcp ");
+       print_ports("spt", tcp->spts[0], tcp->spts[1],
+                   tcp->invflags & IP6T_TCP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
+                   tcp->invflags & IP6T_TCP_INV_DSTPT,
+                   numeric);
+       print_option(tcp->option,
+                    tcp->invflags & IP6T_TCP_INV_OPTION,
+                    numeric);
+       print_flags(tcp->flg_mask, tcp->flg_cmp,
+                   tcp->invflags & IP6T_TCP_INV_FLAGS,
+                   numeric);
+       if (tcp->invflags & ~IP6T_TCP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      tcp->invflags & ~IP6T_TCP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)match->data;
+
+       if (tcpinfo->spts[0] != 0
+           || tcpinfo->spts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_SRCPT)
+                       printf("! ");
+               if (tcpinfo->spts[0]
+                   != tcpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              tcpinfo->spts[0],
+                              tcpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              tcpinfo->spts[0]);
+       }
+
+       if (tcpinfo->dpts[0] != 0
+           || tcpinfo->dpts[1] != 0xFFFF) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_DSTPT)
+                       printf("! ");
+               if (tcpinfo->dpts[0]
+                   != tcpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              tcpinfo->dpts[0],
+                              tcpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              tcpinfo->dpts[0]);
+       }
+
+       if (tcpinfo->option
+           || (tcpinfo->invflags & IP6T_TCP_INV_OPTION)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_OPTION)
+                       printf("! ");
+               printf("--tcp-option %u ", tcpinfo->option);
+       }
+
+       if (tcpinfo->flg_mask
+           || (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)) {
+               if (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)
+                       printf("! ");
+
+               printf("--tcp-flags ");
+               if (tcpinfo->flg_mask != 0xFF) {
+                       print_tcpf(tcpinfo->flg_mask);
+               }
+               printf(" ");
+               print_tcpf(tcpinfo->flg_cmp);
+               printf(" ");
+       }
+}
+
+static
+struct ip6tables_match tcp
+= { NULL,
+    "tcp",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+    IP6T_ALIGN(sizeof(struct ip6t_tcp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts };
+
+void
+_init(void)
+{
+       register_match6(&tcp);
+}
diff --git a/extensions/libip6t_udp.c b/extensions/libip6t_udp.c
new file mode 100644 (file)
index 0000000..139386c
--- /dev/null
@@ -0,0 +1,256 @@
+/* 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 <ip6tables.h>
+#include <linux/netfilter_ipv6/ip6_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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ip6t_entry_match *m, unsigned int *nfcache)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_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 ip6t_entry *entry,
+      unsigned int *nfcache,
+      struct ip6t_entry_match **match)
+{
+       struct ip6t_udp *udpinfo = (struct ip6t_udp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & UDP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->spts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_SRCPT;
+               *flags |= UDP_SRC_PORTS;
+               *nfcache |= NFC_IP6_SRC_PT;
+               break;
+
+       case '2':
+               if (*flags & UDP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_udp_ports(argv[optind-1], udpinfo->dpts);
+               if (invert)
+                       udpinfo->invflags |= IP6T_UDP_INV_DSTPT;
+               *flags |= UDP_DST_PORTS;
+               *nfcache |= NFC_IP6_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 ip6t_ip6 *ip,
+      const struct ip6t_entry_match *match, int numeric)
+{
+       const struct ip6t_udp *udp = (struct ip6t_udp *)match->data;
+
+       printf("udp ");
+       print_ports("spt", udp->spts[0], udp->spts[1],
+                   udp->invflags & IP6T_UDP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", udp->dpts[0], udp->dpts[1],
+                   udp->invflags & IP6T_UDP_INV_DSTPT,
+                   numeric);
+       if (udp->invflags & ~IP6T_UDP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      udp->invflags & ~IP6T_UDP_INV_MASK);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match)
+{
+       const struct ip6t_udp *udpinfo = (struct ip6t_udp *)match->data;
+
+       if (udpinfo->spts[0] != 0
+           || udpinfo->spts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_SRCPT)
+                       printf("! ");
+               if (udpinfo->spts[0]
+                   != udpinfo->spts[1])
+                       printf("--sport %u:%u ",
+                              udpinfo->spts[0],
+                              udpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              udpinfo->spts[0]);
+       }
+
+       if (udpinfo->dpts[0] != 0
+           || udpinfo->dpts[1] != 0xFFFF) {
+               if (udpinfo->invflags & IP6T_UDP_INV_DSTPT)
+                       printf("! ");
+               if (udpinfo->dpts[0]
+                   != udpinfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              udpinfo->dpts[0],
+                              udpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              udpinfo->dpts[0]);
+       }
+}
+
+static
+struct ip6tables_match udp
+= { NULL,
+    "udp",
+    IPTABLES_VERSION,
+    IP6T_ALIGN(sizeof(struct ip6t_udp)),
+    IP6T_ALIGN(sizeof(struct ip6t_udp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match6(&udp);
+}
diff --git a/extensions/libipt_BALANCE.c b/extensions/libipt_BALANCE.c
new file mode 100644 (file)
index 0000000..7f0b916
--- /dev/null
@@ -0,0 +1,153 @@
+/* Shared library add-on to iptables to add simple load-balance 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>
+
+#define BREAKUP_IP(x) (x)>>24, ((x)>>16) & 0xFF, ((x)>>8) & 0xFF, (x) & 0xFF
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"BALANCE v%s options:\n"
+" --to-destination <ipaddr>-<ipaddr>\n"
+"                              Addresses to map destination to.\n",
+IPTABLES_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)
+{
+       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 range of IPs */
+static void
+parse_to(char *arg, struct ip_nat_range *range)
+{
+       char *dash;
+       struct in_addr *ip;
+
+       range->flags |= IP_NAT_RANGE_MAP_IPS;
+       dash = strchr(arg, '-');
+       if (dash)
+               *dash = '\0';
+       else
+               exit_error(PARAMETER_PROBLEM, "Bad IP range `%s'\n", arg);
+
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
+                          arg);
+       range->min_ip = ip->s_addr;
+       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;
+}
+
+/* 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;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to-destination");
+
+               parse_to(optarg, &mr->range[0]);
+               *flags = 1;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; need --to-dest. */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "BALANCE needs --to-destination");
+}
+
+/* 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];
+       struct in_addr a;
+
+       a.s_addr = r->min_ip;
+
+       printf("balance %s", addr_to_dotted(&a));
+       a.s_addr = r->max_ip;
+       printf("-%s ", addr_to_dotted(&a));
+}
+
+/* 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];
+       struct in_addr a;
+
+       a.s_addr = r->min_ip;
+       printf("--to-destination %s", addr_to_dotted(&a));
+       a.s_addr = r->max_ip;
+       printf("-%s ", addr_to_dotted(&a));
+}
+
+static
+struct iptables_target balance
+= { NULL,
+    "BALANCE",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&balance);
+}
diff --git a/extensions/libipt_CLASSIFY.c b/extensions/libipt_CLASSIFY.c
new file mode 100644 (file)
index 0000000..ac14b76
--- /dev/null
@@ -0,0 +1,130 @@
+/* Shared library add-on to iptables to add CLASSIFY 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_CLASSIFY.h>
+#include <linux/types.h>
+#include <linux/pkt_sched.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CLASSIFY target v%s options:\n"
+"  --set-class [MAJOR:MINOR]    Set skb->priority value\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-class", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+}
+
+int string_to_priority(const unsigned char *s, unsigned int *p)
+{
+       unsigned int i, j;
+
+       if (sscanf(s, "%x:%x", &i, &j) != 2)
+               return 1;
+       
+       *p = TC_H_MAKE(i<<16, j);
+       return 0;
+}
+
+/* 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_classify_target_info *clinfo
+               = (struct ipt_classify_target_info *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (string_to_priority(optarg, &clinfo->priority))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Bad class value `%s'", optarg);
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CLASSIFY: Can't specify --set-class twice");
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CLASSIFY: Parameter --set-class is required");
+}
+
+static void
+print_class(unsigned int priority, int numeric)
+{
+       printf("%x:%x ", TC_H_MAJ(priority)>>16, TC_H_MIN(priority));
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_classify_target_info *clinfo =
+               (const struct ipt_classify_target_info *)target->data;
+       printf("CLASSIFY set ");
+       print_class(clinfo->priority, 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_classify_target_info *clinfo =
+               (const struct ipt_classify_target_info *)target->data;
+
+       printf("--set-class %.4x:%.4x ",
+              TC_H_MAJ(clinfo->priority)>>16, TC_H_MIN(clinfo->priority));
+}
+
+static
+struct iptables_target classify
+= { NULL,
+    "CLASSIFY",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+    IPT_ALIGN(sizeof(struct ipt_classify_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&classify);
+}
diff --git a/extensions/libipt_CONNMARK.c b/extensions/libipt_CONNMARK.c
new file mode 100644 (file)
index 0000000..5222182
--- /dev/null
@@ -0,0 +1,168 @@
+/* Shared library add-on to iptables to add CONNMARK 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_CONNMARK.h>
+
+#if 0
+struct markinfo {
+       struct ipt_entry_target t;
+       struct ipt_connmark_target_info mark;
+};
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK target v%s options:\n"
+"  --set-mark value              Set conntrack mark value\n"
+"  --save-mark                   Save the packet nfmark on the connection\n"
+"  --restore-mark                Restore saved nfmark value\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mark", 1, 0, '1' },
+       { "save-mark", 0, 0, '2' },
+       { "restore-mark", 0, 0, '3' },
+       { 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_connmark_target_info *markinfo
+               = (struct ipt_connmark_target_info *)(*target)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               markinfo->mode = IPT_CONNMARK_SET;
+               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,
+                                  "CONNMARK target: Can't specify --set-mark twice");
+               *flags = 1;
+               break;
+       case '2':
+               markinfo->mode = IPT_CONNMARK_SAVE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --save-mark twice");
+               *flags = 1;
+               break;
+       case '3':
+               markinfo->mode = IPT_CONNMARK_RESTORE;
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "CONNMARK target: Can't specify --restore-mark twice");
+               *flags = 1;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "CONNMARK 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_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("CONNMARK set ");
+           print_mark(markinfo->mark, numeric);
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("CONNMARK save ");
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("CONNMARK restore ");
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+/* 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_connmark_target_info *markinfo =
+               (const struct ipt_connmark_target_info *)target->data;
+
+       switch (markinfo->mode) {
+       case IPT_CONNMARK_SET:
+           printf("--set-mark 0x%lx ", markinfo->mark);
+           break;
+       case IPT_CONNMARK_SAVE:
+           printf("--save-mark ");
+           break;
+       case IPT_CONNMARK_RESTORE:
+           printf("--restore-mark ");
+           break;
+       default:
+           printf("ERROR: UNKNOWN CONNMARK MODE ");
+           break;
+       }
+}
+
+static
+struct iptables_target mark
+= { NULL,
+    "CONNMARK",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    IPT_ALIGN(sizeof(struct ipt_connmark_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&mark);
+}
diff --git a/extensions/libipt_DNAT.c b/extensions/libipt_DNAT.c
new file mode 100644 (file)
index 0000000..9b82675
--- /dev/null
@@ -0,0 +1,246 @@
+/* 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",
+IPTABLES_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.u.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, NULL, 0))
+                       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(" ");
+       }
+}
+
+static
+struct iptables_target dnat
+= { NULL,
+    "DNAT",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(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_DSCP.c b/extensions/libipt_DSCP.c
new file mode 100644 (file)
index 0000000..f3bf079
--- /dev/null
@@ -0,0 +1,165 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2000- 2002 by Matthew G. Marsh <mgm@paktronix.com>,
+ *                  Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_DSCP.c borrowed heavily from libipt_TOS.c
+ *
+ * --set-class added by Iain Barnes
+ */
+#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_DSCP.h>
+
+/* This is evil, but it's my code - HW*/
+#include "libipt_dscp_helper.c"
+
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"DSCP target options\n"
+"  --set-dscp value            Set DSCP field in packet header to value\n"
+"                              This value can be in decimal (ex: 32)\n"
+"                              or in hex (ex: 0x20)\n"
+"  --set-dscp-class class      Set the DSCP field in packet header to the\n"
+"                              value represented by the DiffServ class value.\n"
+"                              This class may be EF,BE or any of the CSxx "
+"                              or AFxx classes.\n"
+"\n"
+"                              These two options are mutually exclusive !\n"
+);
+}
+
+static struct option opts[] = {
+       { "set-dscp", 1, 0, 'F' },
+       { "set-dscp-class", 1, 0, 'G' },
+       { 0 }
+};
+
+static void
+parse_dscp(const unsigned char *s, struct ipt_DSCP_info *dinfo)
+{
+       unsigned int dscp;
+       
+       if (string_to_number(s, 0, 255, &dscp) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid dscp `%s'\n", s);
+
+       if (dscp > IPT_DSCP_MAX)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP `%d` out of range\n", dscp);
+
+       dinfo->dscp = (u_int8_t )dscp;
+       return;
+}
+
+
+static void
+parse_class(const unsigned char *s, struct ipt_DSCP_info *dinfo)
+{
+       unsigned int dscp = class_to_dscp(s);
+
+       /* Assign the value */
+       dinfo->dscp = (u_int8_t)dscp;
+}
+
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_DSCP_info *dinfo
+               = (struct ipt_DSCP_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP target: Only use --set-dscp ONCE!");
+               parse_dscp(optarg, dinfo);
+               *flags = 1;
+               break;
+       case 'G':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP target: Only use --set-dscp-class ONCE!");
+               parse_class(optarg, dinfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP target: Parameter --set-dscp is required");
+}
+
+static void
+print_dscp(u_int8_t dscp, int numeric)
+{
+       printf("0x%02x ", dscp);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_DSCP_info *dinfo =
+               (const struct ipt_DSCP_info *)target->data;
+       printf("DSCP set ");
+       print_dscp(dinfo->dscp, 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_DSCP_info *dinfo =
+               (const struct ipt_DSCP_info *)target->data;
+
+       printf("--set-dscp 0x%02x ", dinfo->dscp);
+}
+
+static
+struct iptables_target dscp
+= { NULL,
+    "DSCP",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+    IPT_ALIGN(sizeof(struct ipt_DSCP_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&dscp);
+}
diff --git a/extensions/libipt_ECN.c b/extensions/libipt_ECN.c
new file mode 100644 (file)
index 0000000..205964e
--- /dev/null
@@ -0,0 +1,185 @@
+/* Shared library add-on to iptables for ECN, $Version$
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ECN.c borrowed heavily from libipt_DSCP.c
+ *
+ * $Id: libipt_ECN.c,v 1.12 2003/01/13 12:35:28 laforge Exp $
+ */
+#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_ECN.h>
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"ECN target v%s options\n"
+"  --ecn-tcp-remove            Remove all ECN bits from TCP header\n",
+               IPTABLES_VERSION);
+}
+
+#if 0
+"ECN target v%s EXPERIMENTAL options (use with extreme care!)\n"
+"  --ecn-ip-ect                        Set the IPv4 ECT codepoint (0 to 3)\n"
+"  --ecn-tcp-cwr               Set the IPv4 CWR bit (0 or 1)\n"
+"  --ecn-tcp-ece               Set the IPv4 ECE bit (0 or 1)\n",
+#endif
+
+
+static struct option opts[] = {
+       { "ecn-tcp-remove", 0, 0, 'F' },
+       { "ecn-tcp-cwr", 1, 0, 'G' },
+       { "ecn-tcp-ece", 1, 0, 'H' },
+       { "ecn-ip-ect", 1, 0, '9' },
+       { 0 }
+};
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       unsigned int result;
+       struct ipt_ECN_info *einfo
+               = (struct ipt_ECN_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-remove ONCE!");
+               einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.ece = 0;
+               einfo->proto.tcp.cwr = 0;
+               *flags = 1;
+               break;
+       case 'G':
+               if (*flags & IPT_ECN_OP_SET_CWR)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-cwr ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_CWR;
+               einfo->proto.tcp.cwr = result;
+               *flags |= IPT_ECN_OP_SET_CWR;
+               break;
+       case 'H':
+               if (*flags & IPT_ECN_OP_SET_ECE)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-tcp-ece ONCE!");
+               if (string_to_number(optarg, 0, 1, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_ECE;
+               einfo->proto.tcp.ece = result;
+               *flags |= IPT_ECN_OP_SET_ECE;
+               break;
+       case '9':
+               if (*flags & IPT_ECN_OP_SET_IP)
+                       exit_error(PARAMETER_PROBLEM,
+                               "ECN target: Only use --ecn-ip-ect ONCE!");
+               if (string_to_number(optarg, 0, 3, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN target: Value out of range");
+               einfo->operation |= IPT_ECN_OP_SET_IP;
+               einfo->ip_ect = result;
+               *flags |= IPT_ECN_OP_SET_IP;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ECN target: Parameter --ecn-tcp-remove is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       printf("ECN ");
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("TCP remove ");
+       else {
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("ECE=%u ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("CWR=%u ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("ECT codepoint=%u ", einfo->ip_ect);
+       }
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       const struct ipt_ECN_info *einfo =
+               (const struct ipt_ECN_info *)target->data;
+
+       if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR)
+           && einfo->proto.tcp.ece == 0
+           && einfo->proto.tcp.cwr == 0)
+               printf("--ecn-tcp-remove ");
+       else {
+
+               if (einfo->operation & IPT_ECN_OP_SET_ECE)
+                       printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece);
+
+               if (einfo->operation & IPT_ECN_OP_SET_CWR)
+                       printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr);
+
+               if (einfo->operation & IPT_ECN_OP_SET_IP)
+                       printf("--ecn-ip-ect %d ", einfo->ip_ect);
+       }
+}
+
+static
+struct iptables_target ecn
+= { NULL,
+    "ECN",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+    IPT_ALIGN(sizeof(struct ipt_ECN_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&ecn);
+}
diff --git a/extensions/libipt_FTOS.c b/extensions/libipt_FTOS.c
new file mode 100644 (file)
index 0000000..3ff2d5a
--- /dev/null
@@ -0,0 +1,134 @@
+/* Shared library add-on to iptables for FTOS
+ *
+ * (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_FTOS.c borrowed heavily from libipt_TOS.c  11/09/2000
+ *
+ */
+#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_FTOS.h>
+
+struct finfo {
+       struct ipt_entry_target t;
+       u_int8_t ftos;
+};
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"FTOS target options\n"
+"  --set-ftos value            Set TOS field in packet header to value\n"
+"                              This value can be in decimal (ex: 32)\n"
+"                              or in hex (ex: 0x20)\n"
+);
+}
+
+static struct option opts[] = {
+       { "set-ftos", 1, 0, 'F' },
+       { 0 }
+};
+
+static void
+parse_ftos(const unsigned char *s, struct ipt_FTOS_info *finfo)
+{
+       unsigned int ftos;
+       
+       if (string_to_number(s, 0, 255, &ftos) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid ftos `%s'\n", s);
+       finfo->ftos = (u_int8_t )ftos;
+       return;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       struct ipt_FTOS_info *finfo
+               = (struct ipt_FTOS_info *)(*target)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "FTOS target: Only use --set-ftos ONCE!");
+               parse_ftos(optarg, finfo);
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "FTOS target: Parameter --set-ftos is required");
+}
+
+static void
+print_ftos(u_int8_t ftos, int numeric)
+{
+       printf("0x%02x ", ftos);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_FTOS_info *finfo =
+               (const struct ipt_FTOS_info *)target->data;
+       printf("TOS set ");
+       print_ftos(finfo->ftos, 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_FTOS_info *finfo =
+               (const struct ipt_FTOS_info *)target->data;
+
+       printf("--set-ftos 0x%02x ", finfo->ftos);
+}
+
+static
+struct iptables_target ftos
+= { NULL,
+    "FTOS",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
+    IPT_ALIGN(sizeof(struct ipt_FTOS_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&ftos);
+}
diff --git a/extensions/libipt_IPMARK.c b/extensions/libipt_IPMARK.c
new file mode 100644 (file)
index 0000000..ae335c4
--- /dev/null
@@ -0,0 +1,170 @@
+/* Shared library add-on to iptables to add IPMARK target support.
+ * (C) 2003 by Grzegorz Janoszka <Grzegorz.Janoszka@pro.onet.pl>
+ *
+ * based on original MARK target
+ * 
+ * This program is distributed under the terms of GNU GPL
+ */
+#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_IPMARK.h>
+
+#define IPT_ADDR_USED        1
+#define IPT_AND_MASK_USED    2
+#define IPT_OR_MASK_USED     4
+
+struct ipmarkinfo {
+       struct ipt_entry_target t;
+       struct ipt_ipmark_target_info ipmark;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"IPMARK target v%s options:\n"
+"  --addr src/dst         use source or destination ip address\n"
+"  --and-mask value       logical AND ip address with this value becomes MARK\n"
+"  --or-mask value        logical OR ip address with this value becomes MARK\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "addr", 1, 0, '1' },
+       { "and-mask", 1, 0, '2' },
+       { "or-mask", 1, 0, '3' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_ipmark_target_info *ipmarkinfo =
+               (struct ipt_ipmark_target_info *)t->data;
+
+       ipmarkinfo->andmask=0xffffffff;
+       ipmarkinfo->ormask=0;
+
+       *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_ipmark_target_info *ipmarkinfo
+               = (struct ipt_ipmark_target_info *)(*target)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               if(!strcmp(optarg, "src")) ipmarkinfo->addr=IPT_IPMARK_SRC;
+                 else if(!strcmp(optarg, "dst")) ipmarkinfo->addr=IPT_IPMARK_DST;
+                   else exit_error(PARAMETER_PROBLEM, "Bad addr value `%s' - should be `src' or `dst'", optarg);
+               if (*flags & IPT_ADDR_USED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "IPMARK target: Can't specify --addr twice");
+               *flags |= IPT_ADDR_USED;
+               break;
+       
+       case '2':
+               ipmarkinfo->andmask = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad and-mask value `%s'", optarg);
+               if (*flags & IPT_AND_MASK_USED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "IPMARK target: Can't specify --and-mask twice");
+               *flags |= IPT_AND_MASK_USED;
+               break;
+       case '3':
+               ipmarkinfo->ormask = strtoul(optarg, &end, 0);
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad or-mask value `%s'", optarg);
+               if (*flags & IPT_OR_MASK_USED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "IPMARK target: Can't specify --or-mask twice");
+               *flags |= IPT_OR_MASK_USED;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!(flags & IPT_ADDR_USED))
+               exit_error(PARAMETER_PROBLEM,
+                          "IPMARK target: Parameter --addr is required");
+       if (!(flags & (IPT_AND_MASK_USED | IPT_OR_MASK_USED)))
+               exit_error(PARAMETER_PROBLEM,
+                          "IPMARK target: Parameter --and-mask or --or-mask is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_ipmark_target_info *ipmarkinfo =
+               (const struct ipt_ipmark_target_info *)target->data;
+
+       if(ipmarkinfo->addr == IPT_IPMARK_SRC)
+         printf("IPMARK src");
+       else
+         printf("IPMARK dst");
+       printf(" ip and 0x%lx or 0x%lx", ipmarkinfo->andmask, ipmarkinfo->ormask);
+}
+
+/* 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_ipmark_target_info *ipmarkinfo =
+               (const struct ipt_ipmark_target_info *)target->data;
+
+       if(ipmarkinfo->addr == IPT_IPMARK_SRC)
+         printf("--addr=src ");
+       else
+         printf("--addr=dst ");
+       if(ipmarkinfo->andmask != 0xffffffff)
+         printf("--and-mask 0x%lx ", ipmarkinfo->andmask);
+       if(ipmarkinfo->ormask != 0)
+         printf("--or-mask 0x%lx ", ipmarkinfo->ormask);
+}
+
+static
+struct iptables_target ipmark
+= { NULL,
+    "IPMARK",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
+    IPT_ALIGN(sizeof(struct ipt_ipmark_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&ipmark);
+}
diff --git a/extensions/libipt_IPV4OPTSSTRIP.c b/extensions/libipt_IPV4OPTSSTRIP.c
new file mode 100644 (file)
index 0000000..f1dcec0
--- /dev/null
@@ -0,0 +1,81 @@
+/* Shared library add-on to iptables for IPV4OPTSSTRIP
+ * This modules strip all the IP options.
+ *
+ * (C) 2001 by Fabrice MARIE <fabrice@netfilter.org>
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+        *nfcache |= NFC_UNKNOWN;
+}
+
+static void help(void) 
+{
+       printf("IPV4OPTSSTRIP v%s target takes no option !! Make sure you use it in the mangle table.\n",
+              IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+/* 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;
+}
+
+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)
+{
+       /* nothing to print, we don't take option... */
+}
+
+/* Saves the stuff in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       /* nothing to print, we don't take option... */
+}
+
+static
+struct iptables_target IPV4OPTSSTRIP
+= { NULL,
+    "IPV4OPTSSTRIP",
+    IPTABLES_VERSION,
+    IPT_ALIGN(0),
+    IPT_ALIGN(0),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&IPV4OPTSSTRIP);
+}
diff --git a/extensions/libipt_LOG.c b/extensions/libipt_LOG.c
new file mode 100644 (file)
index 0000000..f8c8e4b
--- /dev/null
@@ -0,0 +1,262 @@
+/* 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",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "log-level",        .has_arg = 1, .flag = 0, .val = '!' },
+       { .name = "log-prefix",       .has_arg = 1, .flag = 0, .val = '#' },
+       { .name = "log-tcp-sequence", .has_arg = 0, .flag = 0, .val = '1' },
+       { .name = "log-tcp-options",  .has_arg = 0, .flag = 0, .val = '2' },
+       { .name = "log-ip-options",   .has_arg = 0, .flag = 0, .val = '3' },
+       { .name = 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[]
+= { { .name = "alert",   .level = LOG_ALERT },
+    { .name = "crit",    .level = LOG_CRIT },
+    { .name = "debug",   .level = LOG_DEBUG },
+    { .name = "emerg",   .level = LOG_EMERG },
+    { .name = "error",   .level = LOG_ERR },           /* DEPRECATED */
+    { .name = "info",    .level = LOG_INFO },
+    { .name = "notice",  .level = LOG_NOTICE },
+    { .name = "panic",   .level = LOG_EMERG },         /* DEPRECATED */
+    { .name = "warning", .level = LOG_WARNING }
+};
+
+static u_int8_t
+parse_level(const char *level)
+{
+       unsigned int lev = -1;
+       unsigned int set = 0;
+
+       if (string_to_number(level, 0, 7, &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 (set++)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "log-level `%s' ambiguous",
+                                                  level);
+                               lev = ipt_log_names[i].level;
+                       }
+               }
+
+               if (!set)
+                       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, NULL, 0))
+                       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, NULL, 0))
+                       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 %d ", 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 ");
+}
+
+static
+struct iptables_target log
+= {
+    .name          = "LOG",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_log_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_log_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_target(&log);
+}
diff --git a/extensions/libipt_MARK.c b/extensions/libipt_MARK.c
new file mode 100644 (file)
index 0000000..0863041
--- /dev/null
@@ -0,0 +1,121 @@
+/* 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",
+IPTABLES_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) {
+       case '1':
+               if (string_to_number(optarg, 0, 0xffffffff, 
+                                    (unsigned int *)&markinfo->mark))
+                       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);
+}
+
+static
+struct iptables_target mark
+= { NULL,
+    "MARK",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_mark_target_info)),
+    IPT_ALIGN(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 (file)
index 0000000..c30e2fa
--- /dev/null
@@ -0,0 +1,168 @@
+/* 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",
+IPTABLES_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
+                       = 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.  Present reader excepted. */
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Port range `%s' funky\n", arg);
+               mr->range[0].min.tcp.port = htons(port);
+               mr->range[0].max.tcp.port = htons(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, NULL, 0))
+                       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];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("masq ports: ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(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("--to-ports %hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+       }
+}
+
+static
+struct iptables_target masq
+= { NULL,
+    "MASQUERADE",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(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_MIRROR.c b/extensions/libipt_MIRROR.c
new file mode 100644 (file)
index 0000000..b5996fa
--- /dev/null
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add MIRROR target support. */
+#include <stdio.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(
+"MIRROR target v%s takes no options\n",
+IPTABLES_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;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_target mirror
+= { NULL,
+    "MIRROR",
+    IPTABLES_VERSION,
+    IPT_ALIGN(0),
+    IPT_ALIGN(0),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    NULL, /* print */
+    NULL, /* save */
+    opts
+};
+
+void _init(void)
+{
+       register_target(&mirror);
+}
diff --git a/extensions/libipt_NETLINK.c b/extensions/libipt_NETLINK.c
new file mode 100644 (file)
index 0000000..9e71990
--- /dev/null
@@ -0,0 +1,158 @@
+/* Provides a NETLINK target, identical to that of the ipchains -o flag */
+/* AUTHOR: Gianni Tedesco <gianni@ecsc.co.uk> */
+#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_NETLINK.h>
+
+static void help(void)
+{
+       printf("NETLINK v%s options:\n"
+               " --nldrop              Drop the packet too\n"
+               " --nlmark <number>     Mark the packet\n"
+               " --nlsize <bytes>      Limit packet size\n",
+              IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"nldrop", 0, 0, 'd'},
+       {"nlmark", 1, 0, 'm'},
+       {"nlsize", 1, 0, 's'},
+       {0}
+};
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_nldata *nld = (struct ipt_nldata *) t->data;
+       
+       nld->flags=0;
+       
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* Parse command options */
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+                const struct ipt_entry *entry,
+                struct ipt_entry_target **target)
+{
+       struct ipt_nldata *nld=(struct ipt_nldata *)(*target)->data;
+
+       switch (c) {
+               case 'd':
+                       if (MASK(*flags, USE_DROP))
+                               exit_error(PARAMETER_PROBLEM,
+                               "Can't specify --nldrop twice");
+
+                       if ( check_inverse(optarg, &invert, NULL, 0) ) {
+                               MASK_UNSET(nld->flags, USE_DROP);
+                       } else {
+                               MASK_SET(nld->flags, USE_DROP);
+                       }
+
+                       MASK_SET(*flags, USE_DROP);                     
+
+                       break;
+               case 'm':
+                       if (MASK(*flags, USE_MARK))
+                               exit_error(PARAMETER_PROBLEM,
+                               "Can't specify --nlmark twice");
+
+                       if (check_inverse(optarg, &invert, NULL, 0)) {
+                               MASK_UNSET(nld->flags, USE_MARK);
+                       }else{
+                               MASK_SET(nld->flags, USE_MARK);
+                               nld->mark=atoi(optarg);
+                       }
+
+                       MASK_SET(*flags, USE_MARK);
+                       break;
+               case 's':
+                       if (MASK(*flags, USE_SIZE))
+                               exit_error(PARAMETER_PROBLEM,
+                               "Can't specify --nlsize twice");
+
+                       if ( atoi(optarg) <= 0 )
+                               exit_error(PARAMETER_PROBLEM,
+                               "--nlsize must be larger than zero");
+                       
+
+                       if (check_inverse(optarg, &invert, NULL, 0)) {
+                               MASK_UNSET(nld->flags, USE_SIZE);
+                       }else{
+                               MASK_SET(nld->flags, USE_SIZE);
+                               nld->size=atoi(optarg);
+                       }
+                       MASK_SET(*flags, USE_SIZE);
+                       break;
+
+               default:
+                       return 0;
+       }
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       /* ?? */
+}
+
+/* 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_nldata *nld
+           = (const struct ipt_nldata *) target->data;
+
+       if ( MASK(nld->flags, USE_DROP) )
+               printf("--nldrop ");
+
+       if ( MASK(nld->flags, USE_MARK) )
+               printf("--nlmark %i ", nld->mark);
+
+       if ( MASK(nld->flags, USE_SIZE) )
+               printf("--nlsize %i ", nld->size);              
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_nldata *nld
+           = (const struct ipt_nldata *) target->data;
+
+       if ( MASK(nld->flags, USE_DROP) )
+               printf("nldrop ");
+
+       if ( MASK(nld->flags, USE_MARK) )
+               printf("nlmark %i ", nld->mark);
+
+       if ( MASK(nld->flags, USE_SIZE) )
+               printf("nlsize %i ", nld->size);
+}
+
+static
+struct iptables_target netlink = { NULL,
+       "NETLINK",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_nldata)),
+       IPT_ALIGN(sizeof(struct ipt_nldata)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts
+};
+
+void _init(void)
+{
+       register_target(&netlink);
+}
+
diff --git a/extensions/libipt_NETMAP.c b/extensions/libipt_NETMAP.c
new file mode 100644 (file)
index 0000000..a45acb9
--- /dev/null
@@ -0,0 +1,202 @@
+/* Shared library add-on to iptables to add static NAT support.
+   Author: Svenning Soerensen <svenning@post5.tele.dk>
+*/
+
+#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>
+
+#define MODULENAME "NETMAP"
+
+static struct option opts[] = {
+       { "to", 1, 0, '1' },
+       { 0 }
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(MODULENAME" v%s options:\n"
+              "  --%s address[/mask]\n"
+              "                                Network address to map to.\n\n",
+              IPTABLES_VERSION, opts[0].name);
+}
+
+static u_int32_t
+bits2netmask(int bits)
+{
+       u_int32_t netmask, bm;
+
+       if (bits >= 32 || bits < 0)
+               return(~0);
+       for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1)
+               netmask |= bm;
+       return htonl(netmask);
+}
+
+static int
+netmask2bits(u_int32_t netmask)
+{
+       u_int32_t bm;
+       int bits;
+
+       netmask = ntohl(netmask);
+       for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
+               bits++;
+       if (netmask)
+               return -1; /* holes in netmask */
+       return bits;
+}
+
+/* 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 network address */
+static void
+parse_to(char *arg, struct ip_nat_range *range)
+{
+       char *slash;
+       struct in_addr *ip;
+       u_int32_t netmask;
+       unsigned int bits;
+
+       range->flags |= IP_NAT_RANGE_MAP_IPS;
+       slash = strchr(arg, '/');
+       if (slash)
+               *slash = '\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 (slash) {
+               if (strchr(slash+1, '.')) {
+                       ip = dotted_to_addr(slash+1);
+                       if (!ip)
+                               exit_error(PARAMETER_PROBLEM, "Bad netmask `%s'\n",
+                                          slash+1);
+                       netmask = ip->s_addr;
+               }
+               else {
+                       if (string_to_number(slash+1, 0, 32, &bits) == -1)
+                               exit_error(PARAMETER_PROBLEM, "Bad netmask `%s'\n",
+                                          slash+1);
+                       netmask = bits2netmask(bits);
+               }
+               /* Don't allow /0 (/1 is probably insane, too) */
+               if (netmask == 0)
+                       exit_error(PARAMETER_PROBLEM, "Netmask needed\n");
+       }
+       else
+               netmask = ~0;
+
+       if (range->min_ip & ~netmask) {
+               if (slash)
+                       *slash = '/';
+               exit_error(PARAMETER_PROBLEM, "Bad network address `%s'\n",
+                          arg);
+       }
+       range->max_ip = range->min_ip | ~netmask;
+}
+
+/* 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;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --%s", opts[0].name);
+
+               parse_to(optarg, &mr->range[0]);
+               *flags = 1;
+               return 1;
+
+       default:
+               return 0;
+       }
+}
+
+/* Final check; need --to */
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          MODULENAME" needs --%s", opts[0].name);
+}
+
+/* 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];
+       struct in_addr a;
+       int bits;
+
+       a.s_addr = r->min_ip;
+       printf("%s", addr_to_dotted(&a));
+       a.s_addr = ~(r->min_ip ^ r->max_ip);
+       bits = netmask2bits(a.s_addr);
+       if (bits < 0)
+               printf("/%s", addr_to_dotted(&a));
+       else
+               printf("/%d", bits);
+}
+
+/* Saves the targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       printf("--%s ", opts[0].name);
+       print(ip, target, 0);
+}
+
+static
+struct iptables_target target_module
+= { NULL,
+    MODULENAME,
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&target_module);
+}
+
diff --git a/extensions/libipt_NOTRACK.c b/extensions/libipt_NOTRACK.c
new file mode 100644 (file)
index 0000000..39489ae
--- /dev/null
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add NOTRACK target support. */
+#include <stdio.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(
+"NOTRACK target v%s takes no options\n",
+IPTABLES_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;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_target notrack 
+= {    .next = NULL,
+       .name = "NOTRACK",
+       .version = IPTABLES_VERSION,
+       .size = IPT_ALIGN(0),
+       .userspacesize = IPT_ALIGN(0),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = NULL, /* print */
+       .save = NULL, /* save */
+       .extra_opts = opts
+};
+
+void _init(void)
+{
+       register_target(&notrack);
+}
diff --git a/extensions/libipt_REDIRECT.c b/extensions/libipt_REDIRECT.c
new file mode 100644 (file)
index 0000000..052b533
--- /dev/null
@@ -0,0 +1,169 @@
+/* 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",
+IPTABLES_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
+                       = 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", arg);
+               mr->range[0].min.tcp.port = htons(port);
+               mr->range[0].max.tcp.port = htons(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, NULL, 0))
+                       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];
+
+       if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
+               printf("redir ports ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(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("--to-ports ");
+               printf("%hu", ntohs(r->min.tcp.port));
+               if (r->max.tcp.port != r->min.tcp.port)
+                       printf("-%hu", ntohs(r->max.tcp.port));
+               printf(" ");
+       }
+}
+
+static
+struct iptables_target redir
+= { NULL,
+    "REDIRECT",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(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 (file)
index 0000000..8170edd
--- /dev/null
@@ -0,0 +1,192 @@
+/* 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>
+#include <linux/version.h>
+
+/* If we are compiling against a kernel that does not support
+ * IPT_ICMP_ADMIN_PROHIBITED, we are emulating it.
+ * The result will be a plain DROP of the packet instead of
+ * reject. -- Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ */
+#ifndef IPT_ICMP_ADMIN_PROHIBITED
+#define IPT_ICMP_ADMIN_PROHIBITED      IPT_TCP_RESET + 1
+#endif
+
+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-proto-unreachable", "proto-unreach",
+               IPT_ICMP_PROT_UNREACHABLE, "ICMP protocol unreachable"},
+       {"icmp-port-unreachable", "port-unreach",
+               IPT_ICMP_PORT_UNREACHABLE, "ICMP port unreachable (default)"},
+#if 0
+       {"echo-reply", "echoreply",
+        IPT_ICMP_ECHOREPLY, "for ICMP echo only: faked ICMP echo reply"},
+#endif
+       {"icmp-net-prohibited", "net-prohib",
+        IPT_ICMP_NET_PROHIBITED, "ICMP network prohibited"},
+       {"icmp-host-prohibited", "host-prohib",
+        IPT_ICMP_HOST_PROHIBITED, "ICMP host prohibited"},
+       {"tcp-reset", "tcp-reset",
+        IPT_TCP_RESET, "TCP RST packet"},
+       {"icmp-admin-prohibited", "admin-prohib",
+        IPT_ICMP_ADMIN_PROHIBITED, "ICMP administratively prohibited (*)"}
+};
+
+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();
+
+       printf("(*) See man page or read the INCOMPATIBILITES file for compatibility issues.\n");
+}
+
+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, NULL, 0))
+                       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;
+                       }
+               }
+               /* This due to be dropped late in 2.4 pre-release cycle --RR */
+               if (strncasecmp("echo-reply", optarg, strlen(optarg)) == 0
+                   || strncasecmp("echoreply", optarg, strlen(optarg)) == 0)
+                       fprintf(stderr, "--reject-with echo-reply no longer"
+                               " supported\n");
+               exit_error(PARAMETER_PROBLEM, "unknown reject type `%s'",optarg);
+       default:
+               /* Fall through */
+               break;
+       }
+       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;
+       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);
+}
+
+static
+struct iptables_target reject
+= { NULL,
+    "REJECT",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_reject_info)),
+    IPT_ALIGN(sizeof(struct ipt_reject_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&reject);
+}
diff --git a/extensions/libipt_ROUTE.c b/extensions/libipt_ROUTE.c
new file mode 100644 (file)
index 0000000..1cb3e89
--- /dev/null
@@ -0,0 +1,233 @@
+/* Shared library add-on to iptables to add ROUTE target support.
+ * Author : Cedric de Launois, <delaunois@info.ucl.ac.be>
+ * v 1.8 2003/06/24
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ROUTE.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ROUTE target v%s options:\n"
+"    --oif   \tifname \t\tRoute the packet through `ifname' network interface\n"
+"    --iif   \tifname \t\tChange the packet's incoming interface to `ifname'\n"
+"    --gw    \tip     \t\tRoute the packet via this gateway\n"
+"    --continue\t     \t\tRoute the packet and continue traversing the\n"
+"            \t       \t\trules. Not valid with --iif.\n"
+"\n",
+"1.8");
+}
+
+static struct option opts[] = {
+       { "oif", 1, 0, '1' },
+       { "iif", 1, 0, '2' },
+       { "gw", 1, 0, '3' },
+       { "continue", 0, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_route_target_info *route_info = 
+               (struct ipt_route_target_info*)t->data;
+
+       route_info->oif[0] = '\0';
+       route_info->iif[0] = '\0';
+       route_info->gw = 0;
+       route_info->flags = 0;
+}
+
+
+#define IPT_ROUTE_OPT_OIF      0x01
+#define IPT_ROUTE_OPT_IIF      0x02
+#define IPT_ROUTE_OPT_GW       0x04
+#define IPT_ROUTE_OPT_CONTINUE 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,
+      struct ipt_entry_target **target)
+{
+       struct ipt_route_target_info *route_info = 
+               (struct ipt_route_target_info*)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPT_ROUTE_OPT_OIF)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --oif twice");
+
+               if (*flags & IPT_ROUTE_OPT_IIF)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't use --oif and --iif together");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --oif");
+
+               if (strlen(optarg) > sizeof(route_info->oif) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum interface name length %u",
+                                  sizeof(route_info->oif) - 1);
+
+               strcpy(route_info->oif, optarg);
+               *flags |= IPT_ROUTE_OPT_OIF;
+               break;
+
+       case '2':
+               if (*flags & IPT_ROUTE_OPT_IIF)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --iif twice");
+
+               if (*flags & IPT_ROUTE_OPT_OIF)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't use --iif and --oif together");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --iif");
+
+               if (strlen(optarg) > sizeof(route_info->iif) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum interface name length %u",
+                                  sizeof(route_info->iif) - 1);
+
+               strcpy(route_info->iif, optarg);
+               *flags |= IPT_ROUTE_OPT_IIF;
+               break;
+
+       case '3':
+               if (*flags & IPT_ROUTE_OPT_GW)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --gw twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --gw");
+
+               if (!inet_aton(optarg, (struct in_addr*)&route_info->gw)) {
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid IP address %s",
+                                  optarg);
+               }
+
+               *flags |= IPT_ROUTE_OPT_GW;
+               break;
+
+       case '4':
+               if (*flags & IPT_ROUTE_OPT_CONTINUE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --continue twice");
+
+               route_info->flags |= IPT_ROUTE_CONTINUE;
+               *flags |= IPT_ROUTE_OPT_CONTINUE;
+
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ROUTE target: oif, iif or gw option required");
+
+       if ((flags & IPT_ROUTE_OPT_CONTINUE) && (flags & IPT_ROUTE_OPT_IIF))
+               exit_error(PARAMETER_PROBLEM,
+                          "ROUTE target: can't continue traversing the rules with iif option");
+}
+
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_route_target_info *route_info
+               = (const struct ipt_route_target_info *)target->data;
+
+       printf("ROUTE ");
+
+       if (route_info->oif[0])
+               printf("oif:%s ", route_info->oif);
+
+       if (route_info->iif[0])
+               printf("iif:%s ", route_info->iif);
+
+       if (route_info->gw) {
+               struct in_addr ip = { route_info->gw };
+               printf("gw:%s ", inet_ntoa(ip));
+       }
+
+       if (route_info->flags & IPT_ROUTE_CONTINUE)
+               printf("continue");
+
+}
+
+
+static void save(const struct ipt_ip *ip, 
+                const struct ipt_entry_target *target)
+{
+       const struct ipt_route_target_info *route_info
+               = (const struct ipt_route_target_info *)target->data;
+
+       if (route_info->oif[0])
+               printf("--oif %s ", route_info->oif);
+
+       if (route_info->iif[0])
+               printf("--iif %s ", route_info->iif);
+
+       if (route_info->gw) {
+               struct in_addr ip = { route_info->gw };
+               printf("--gw %s ", inet_ntoa(ip));
+       }
+
+       if (route_info->flags & IPT_ROUTE_CONTINUE)
+               printf("--continue ");
+}
+
+
+static
+struct iptables_target route
+= { NULL,
+    "ROUTE",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_route_target_info)),
+    IPT_ALIGN(sizeof(struct ipt_route_target_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&route);
+}
diff --git a/extensions/libipt_SAME.c b/extensions/libipt_SAME.c
new file mode 100644 (file)
index 0000000..e9c42a8
--- /dev/null
@@ -0,0 +1,210 @@
+/* Shared library add-on to iptables to add simple non load-balancing SNAT 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>
+#include <linux/netfilter_ipv4/ipt_SAME.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"SAME v%s options:\n"
+" --to <ipaddr>-<ipaddr>\n"
+"                              Addresses to map source to.\n"
+"                               May be specified more than\n"
+"                                once for multiple ranges.\n"
+" --nodst\n"
+"                              Don't use destination-ip in\n"
+"                                         source selection\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "to", 1, 0, '1' },
+       { "nodst", 0, 0, '2'},
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_same_info *mr = (struct ipt_same_info *)t->data;
+
+       /* Set default to 0 */
+       mr->rangesize = 0;
+       mr->info = 0;
+       mr->ipnum = 0;
+       
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* Parses range of IPs */
+static void
+parse_to(char *arg, struct ip_nat_range *range)
+{
+       char *dash;
+       struct in_addr *ip;
+
+       range->flags |= IP_NAT_RANGE_MAP_IPS;
+       dash = strchr(arg, '-');
+
+       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;
+       if (dash)
+               if (range->min_ip > range->max_ip)
+                       exit_error(PARAMETER_PROBLEM, "Bad IP range `%s-%s'\n", 
+                                  arg, dash+1);
+}
+
+#define IPT_SAME_OPT_TO                        0x01
+#define IPT_SAME_OPT_NODST             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,
+      struct ipt_entry_target **target)
+{
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)(*target)->data;
+
+       switch (c) {
+       case '1':
+               if (mr->rangesize == IPT_SAME_MAX_RANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Too many ranges specified, maximum "
+                                  "is %i ranges.\n",
+                                  IPT_SAME_MAX_RANGE);
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --to");
+
+               parse_to(optarg, &mr->range[mr->rangesize]);
+               mr->rangesize++;
+               *flags |= IPT_SAME_OPT_TO;
+               break;
+               
+       case '2':
+               if (*flags & IPT_SAME_OPT_NODST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --nodst twice");
+               
+               mr->info |= IPT_SAME_NODST;
+               *flags |= IPT_SAME_OPT_NODST;
+               break;
+               
+       default:
+               return 0;
+       }
+       
+       return 1;
+}
+
+/* Final check; need --to. */
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_SAME_OPT_TO))
+               exit_error(PARAMETER_PROBLEM,
+                          "SAME needs --to");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       int count;
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)target->data;
+       
+       printf("same:");
+       
+       for (count = 0; count < mr->rangesize; count++) {
+               struct ip_nat_range *r = &mr->range[count];
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+
+               printf("%s", addr_to_dotted(&a));
+               a.s_addr = r->max_ip;
+               
+               if (r->min_ip == r->max_ip)
+                       printf(" ");
+               else
+                       printf("-%s ", addr_to_dotted(&a));
+       }
+       
+       if (mr->info & IPT_SAME_NODST)
+               printf("nodst ");
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+       int count;
+       struct ipt_same_info *mr
+               = (struct ipt_same_info *)target->data;
+
+       for (count = 0; count < mr->rangesize; count++) {
+               struct ip_nat_range *r = &mr->range[count];
+               struct in_addr a;
+
+               a.s_addr = r->min_ip;
+               printf("--to %s", addr_to_dotted(&a));
+               a.s_addr = r->max_ip;
+
+               if (r->min_ip == r->max_ip)
+                       printf(" ");
+               else
+                       printf("-%s ", addr_to_dotted(&a));
+       }
+       
+       if (mr->info & IPT_SAME_NODST)
+               printf("--nodst ");
+}
+
+static
+struct iptables_target same
+= { NULL,
+    "SAME",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_same_info)),
+    IPT_ALIGN(sizeof(struct ipt_same_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&same);
+}
diff --git a/extensions/libipt_SNAT.c b/extensions/libipt_SNAT.c
new file mode 100644 (file)
index 0000000..d909c2a
--- /dev/null
@@ -0,0 +1,246 @@
+/* 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",
+IPTABLES_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.u.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, NULL, 0))
+                       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(" ");
+       }
+}
+
+static
+struct iptables_target snat
+= { NULL,
+    "SNAT",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
+    IPT_ALIGN(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_TARPIT.c b/extensions/libipt_TARPIT.c
new file mode 100644 (file)
index 0000000..643ce61
--- /dev/null
@@ -0,0 +1,67 @@
+/* Shared library add-on to iptables for TARPIT support */
+#include <stdio.h>
+#include <getopt.h>
+#include <iptables.h>
+
+static void
+help(void)
+{
+       fputs(
+"TARPIT takes no options\n"
+"\n", stdout);
+}
+
+static struct option opts[] = {
+       { 0 }
+};
+
+static void
+init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      struct ipt_entry_target **target)
+{
+       return 0;
+}
+
+static void final_check(unsigned int flags)
+{
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
+{
+}
+
+static
+struct iptables_target tarpit
+= { NULL,
+    "TARPIT",
+    IPTABLES_VERSION,
+    IPT_ALIGN(0),
+    IPT_ALIGN(0),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&tarpit);
+}
diff --git a/extensions/libipt_TCPLAG.c b/extensions/libipt_TCPLAG.c
new file mode 100644 (file)
index 0000000..27361e7
--- /dev/null
@@ -0,0 +1,228 @@
+/* libipt_TCPLAG.c -- module for iptables to interface with TCPLAG target
+ * Copyright (C) 2002 Telford Tendys <telford@triode.net.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * Shared library add-on to iptables for TCPLAG target control
+ *
+ * This allows installation and removal of the TCPLAG target
+ * Note that there is a lot more commentary in this file than
+ * the average libipt target (i.e. more than none) but these
+ * are just my deductions based on examination of the source
+ * and 
+ */
+#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_TCPLAG.h>
+
+/*
+ * This merely dumps out text for the user
+ * (saves keeping the manpage up to date)
+ */
+static void help( void )
+{
+       printf( "TCPLAG options:\n"
+                       " --log-level=n    Set the syslog level to n (integer 0 to 7)\n\n"
+                       " --log-prefix=xx  Prefix log messages with xx\n" );
+}
+
+/*
+ * See "man getopt_long" for an explanation of this structure
+ *
+ * If one of our options DOES happen to come up then we get
+ * a callback into parse(), our vals must not overlap with any
+ * normal iptables short options (I think) because there is only
+ * one actual options handler and it can't tell whose options it
+ * is really looking at unless they are all distinct.
+ *
+ * These are exactly the same as the LOG target options
+ * and have the same purpose.
+ */
+static const struct option opts[] =
+{
+       { "log-level",     1, 0, '!' },
+       { "log-prefix",    1, 0, '#' },
+       { 0 }
+};
+
+/*
+ * This gives us a chance to install some initial values in
+ * our own private data structure (which is at t->data).
+ * Probably we could fiddle with t->tflags too but there is
+ * no great advantage in doing so.
+ * 
+ * TODO: Find documentation for the above flags which
+ *       can be ored into nfcache...
+ *
+ * NFC_IP6_DST_PT
+ * NFC_IP6_PROTO_UNKNOWN
+ * NFC_IP6_SRC_PT
+ * NFC_IP6_TCPFLAGS
+ * NFC_IP_DST_PT
+ * NFC_IP_SRC_PT
+ * NFC_IP_TOS
+ * NFC_UNKNOWN             -- This one seems safest
+ */
+static void init( struct ipt_entry_target *t, unsigned int *nfcache )
+{
+       struct ipt_tcplag *el = (struct ipt_tcplag *)t->data;
+       memset( el, 0, sizeof( struct ipt_tcplag ));
+       el->level = 4; /* Default to warning level */
+       strcpy( el->prefix, "TCPLAG:" ); /* Give a reasonable default prefix */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/*
+ * It doesn't take much thought to see how little thought has gone into
+ * this particular API. However, to add to that I'd just like to say that
+ * it can be made to work and small miracles are still miracles.
+ *
+ * The input parameters are as follows:
+ * 
+ *  c      --  the 'val' from opts[] above, could possibly be something
+ *             we cannot recognise in which case return(0).
+ *             If we do recognise it then return(1).
+ *
+ *  argv   --  in case we want to take parameters from the command line,
+ *             not sure how to safely ensure that the parameter that
+ *             we want to take will really exist, presumably getopt_long()
+ *             will have already checked such things (what about optional
+ *             parameters huh?).
+ *
+ *  invert --  if the option parameter had '!' in front of it, usually this
+ *             would inversion of the matching sense but I don't think it
+ *             is useful in the case of targets.
+ *
+ *  flags  --  always (*target)->tflags for those who feel it is better
+ *             to access this field indirectly <shrug> starts of
+ *             zero for a fresh target, gets fed into final_check().
+ *
+ *  entry  --  apparently useless
+ *
+ *  target --  the record that holds data about this target,
+ *             most importantly, our private data is (*target)->data
+ *             (this has already been malloced for us).
+ */
+static int parse( int c, char **argv, int invert, unsigned int *flags,
+                                 const struct ipt_entry *entry, struct ipt_entry_target **target )
+{
+       struct ipt_tcplag *el = (struct ipt_tcplag *)( *target )->data;
+/*
+ * Yeah, we could complain about options being issued twice but
+ * is it really worth the trouble? Will it make the world a better place?
+ */
+       switch( c )
+       {
+/*
+ * I really can't be bothered with the syslog naming convention,
+ * it isn't terribly useful anyhow.
+ */
+               case '!':
+                       el->level = strtol( optarg, 0, 10 );
+                       return( 1 );
+/*
+ * 15 chars should be plenty
+ */
+               case '#':
+                       strncpy( el->prefix, optarg, 15 );
+                       el->prefix[ 14 ] = 0; /* Force termination */
+                       return( 1 );
+       }
+       return( 0 );
+}
+
+/*
+ * This gets given the (*target)->tflags value from
+ * the parse() above and it gets called after all the
+ * parsing of options is completed. Thus if one option
+ * requires another option you can test the flags and
+ * decide whether everything is in order.
+ *
+ * If there is a problem then do something like:
+ *             exit_error( PARAMETER_PROBLEM, "foobar parameters detected in TCPLAG target");
+ *
+ * In this case, no errors are possible
+ */
+static void final_check( unsigned int flags ) { }
+/*
+ * This print is for the purpose of user-readable display
+ * such as what "iptables -L" would give. The notes in
+ * iptables.h say that target could possibly be a null pointer
+ * but coding of the various libipt_XX.c modules suggests
+ * that it is safe to presume target is correctly initialised.
+ */
+static void print(const struct ipt_ip *ip, const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_tcplag *el = (const struct ipt_tcplag *)target->data;
+       printf("TCPLAG <%d>", el->level );
+       if( el->prefix[ 0 ])
+       {
+               printf( "%s", el->prefix );
+       }
+}
+
+/*
+ * As above but command-line style printout
+ * (machine-readable for restoring table)
+ */
+static void save( const struct ipt_ip *ip, const struct ipt_entry_target *target )
+{
+       const struct ipt_tcplag *el = (const struct ipt_tcplag *)target->data;
+       printf("TCPLAG --log-level=%d", el->level );
+       if( el->prefix[ 0 ])
+       {
+/*
+ * FIXME: Should have smarter quoting
+ */
+               printf( " --log-prefix='%s'", el->prefix );
+       }
+}
+
+/*
+ * The version must match the iptables version exactly
+ * which is a big pain, could use `iptables -V` in makefile
+ * but we can't guarantee compatibility with all iptables
+ * so we are stuck with only supporting one particular version.
+ */
+static struct iptables_target targ =
+{
+next:            0,
+name:             "TCPLAG",
+version:          "1.2.3",
+size:             IPT_ALIGN( sizeof( struct ipt_tcplag )),
+userspacesize:    IPT_ALIGN( sizeof( struct ipt_tcplag )),
+help:             &help,
+init:             &init,
+parse:            &parse,
+final_check:      &final_check,
+print:            &print,
+save:             &save,
+extra_opts:       opts
+};
+
+/*
+ * Always nervous trusting _init() but oh well that is the standard
+ * so have to go ahead and use it. This registers your target into
+ * the list of available targets so that your options become available.
+ */
+void _init( void ) { register_target( &targ ); }
diff --git a/extensions/libipt_TCPMSS.c b/extensions/libipt_TCPMSS.c
new file mode 100644 (file)
index 0000000..6892b52
--- /dev/null
@@ -0,0 +1,135 @@
+/* Shared library add-on to iptables to add TCPMSS target support.
+ *
+ * Copyright (c) 2000 Marc Boucher
+*/
+#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_TCPMSS.h>
+
+struct mssinfo {
+       struct ipt_entry_target t;
+       struct ipt_tcpmss_info mss;
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TCPMSS target v%s mutually-exclusive options:\n"
+"  --set-mss value               explicitly set MSS option to specified value\n"
+"  --clamp-mss-to-pmtu           automatically clamp MSS value to (path_MTU - 40)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "set-mss", 1, 0, '1' },
+       { "clamp-mss-to-pmtu", 0, 0, '2' },
+       { 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_tcpmss_info *mssinfo
+               = (struct ipt_tcpmss_info *)(*target)->data;
+
+       switch (c) {
+               unsigned int mssval;
+
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               if (string_to_number(optarg, 0, 65535 - 40, &mssval) == -1)
+                       exit_error(PARAMETER_PROBLEM, "Bad TCPMSS value `%s'", optarg);
+               
+               mssinfo->mss = mssval;
+               *flags = 1;
+               break;
+
+       case '2':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "TCPMSS target: Only one option may be specified");
+               mssinfo->mss = IPT_TCPMSS_CLAMP_PMTU;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "TCPMSS target: At least one parameter is required");
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target,
+      int numeric)
+{
+       const struct ipt_tcpmss_info *mssinfo =
+               (const struct ipt_tcpmss_info *)target->data;
+       if(mssinfo->mss == IPT_TCPMSS_CLAMP_PMTU)
+               printf("TCPMSS clamp to PMTU ");
+       else
+               printf("TCPMSS set %u ", mssinfo->mss);
+}
+
+/* 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_tcpmss_info *mssinfo =
+               (const struct ipt_tcpmss_info *)target->data;
+
+       if(mssinfo->mss == IPT_TCPMSS_CLAMP_PMTU)
+               printf("--clamp-mss-to-pmtu ");
+       else
+               printf("--set-mss %u ", mssinfo->mss);
+}
+
+static
+struct iptables_target mss
+= { NULL,
+    "TCPMSS",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+    IPT_ALIGN(sizeof(struct ipt_tcpmss_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_target(&mss);
+}
diff --git a/extensions/libipt_TOS.c b/extensions/libipt_TOS.c
new file mode 100644 (file)
index 0000000..87c3816
--- /dev/null
@@ -0,0 +1,175 @@
+/* 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. */
+static
+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",
+IPTABLES_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, tos;
+
+       if (string_to_number(s, 0, 255, &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);
+}
+
+static
+struct iptables_target tos
+= { NULL,
+    "TOS",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tos_target_info)),
+    IPT_ALIGN(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_TRACE.c b/extensions/libipt_TRACE.c
new file mode 100644 (file)
index 0000000..7217999
--- /dev/null
@@ -0,0 +1,63 @@
+/* Shared library add-on to iptables to add TRACE target support. */
+#include <stdio.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(
+"TRACE target v%s takes no options\n",
+IPTABLES_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;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_target trace
+= {    .next = NULL,
+       .name = "TRACE",
+       .version = IPTABLES_VERSION,
+       .size = IPT_ALIGN(0),
+       .userspacesize = IPT_ALIGN(0),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = NULL, /* print */
+       .save = NULL, /* save */
+       .extra_opts = opts
+};
+
+void _init(void)
+{
+       register_target(&trace);
+}
diff --git a/extensions/libipt_TTL.c b/extensions/libipt_TTL.c
new file mode 100644 (file)
index 0000000..84d6424
--- /dev/null
@@ -0,0 +1,164 @@
+/* Shared library add-on to iptables for the TTL target
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * $Id: libipt_TTL.c,v 1.6 2002/05/29 13:08:16 laforge Exp $
+ *
+ * This program is distributed under the terms of GNU GPL
+ */
+#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_TTL.h>
+
+#define IPT_TTL_USED   1
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+"TTL target v%s options\n"
+"  --ttl-set value             Set TTL to <value>\n"
+"  --ttl-dec value             Decrement TTL by <value>\n"
+"  --ttl-inc value             Increment TTL by <value>\n"
+, IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ipt_entry *entry,
+               struct ipt_entry_target **target)
+{
+       struct ipt_TTL_info *info = (struct ipt_TTL_info *) (*target)->data;
+       u_int8_t value;
+
+       if (*flags & IPT_TTL_USED) {
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify TTL option twice");
+       }
+
+       if (!optarg) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "TTL: You must specify a value");
+
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM,
+                               "TTL: unexpected `!'");
+       
+       value = atoi(optarg);
+
+       switch (c) {
+
+               case '1':
+                       info->mode = IPT_TTL_SET;
+                       break;
+
+               case '2':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "TTL: decreasing by 0?");
+                       }
+
+                       info->mode = IPT_TTL_DEC;
+                       break;
+
+               case '3':
+                       if (value == 0) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "TTL: increasing by 0?");
+                       }
+
+                       info->mode = IPT_TTL_INC;
+                       break;
+
+               default:
+                       return 0;
+
+       }
+       
+       info->ttl = value;
+       *flags |= IPT_TTL_USED;
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_TTL_USED))
+               exit_error(PARAMETER_PROBLEM,
+                               "TTL: You must specify an action");
+}
+
+static void save(const struct ipt_ip *ip,
+               const struct ipt_entry_target *target)
+{
+       const struct ipt_TTL_info *info = 
+               (struct ipt_TTL_info *) target->data;
+
+       switch (info->mode) {
+               case IPT_TTL_SET:
+                       printf("--ttl-set ");
+                       break;
+               case IPT_TTL_DEC:
+                       printf("--ttl-dec ");
+                       break;
+
+               case IPT_TTL_INC:
+                       printf("--ttl-inc ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static void print(const struct ipt_ip *ip,
+               const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_TTL_info *info =
+               (struct ipt_TTL_info *) target->data;
+
+       printf("TTL ");
+       switch (info->mode) {
+               case IPT_TTL_SET:
+                       printf("set to ");
+                       break;
+               case IPT_TTL_DEC:
+                       printf("decrement by ");
+                       break;
+               case IPT_TTL_INC:
+                       printf("increment by ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static struct option opts[] = {
+       { "ttl-set", 1, 0, '1' },
+       { "ttl-dec", 1, 0, '2' },
+       { "ttl-inc", 1, 0, '3' },
+       { 0 }
+};
+
+static
+struct iptables_target TTL = { NULL, 
+       "TTL",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       IPT_ALIGN(sizeof(struct ipt_TTL_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts 
+};
+
+void _init(void)
+{
+       register_target(&TTL);
+}
diff --git a/extensions/libipt_ULOG.c b/extensions/libipt_ULOG.c
new file mode 100644 (file)
index 0000000..41ee991
--- /dev/null
@@ -0,0 +1,207 @@
+/* Shared library add-on to iptables to add ULOG support.
+ * 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * multipart netlink support based on ideas by Sebastian Zander 
+ *                                             <zander@fokus.gmd.de>
+ *
+ * This software is released under the terms of GNU GPL
+ * 
+ * libipt_ULOG.c,v 1.7 2001/01/30 11:55:02 laforge Exp
+ */
+#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_ULOG.h>
+
+#define ULOG_DEFAULT_NLGROUP 1
+#define ULOG_DEFAULT_QTHRESHOLD 1
+
+
+void print_groups(unsigned int gmask)
+{
+       int b;
+       unsigned int test;
+
+       for (b = 31; b >= 0; b--) {
+               test = (1 << b);
+               if (gmask & test)
+                       printf("%d ", b + 1);
+       }
+}
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf("ULOG v%s options:\n"
+              " --ulog-nlgroup nlgroup         NETLINK group used for logging\n"
+              " --ulog-cprange size            Bytes of each packet to be passed\n"
+              " --ulog-qthreshold              Threshold of in-kernel queue\n"
+              " --ulog-prefix prefix           Prefix log messages with this prefix.\n\n",
+              IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {"ulog-nlgroup", 1, 0, '!'},
+       {"ulog-prefix", 1, 0, '#'},
+       {"ulog-cprange", 1, 0, 'A'},
+       {"ulog-qthreshold", 1, 0, 'B'},
+       {0}
+};
+
+/* Initialize the target. */
+static void init(struct ipt_entry_target *t, unsigned int *nfcache)
+{
+       struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) t->data;
+
+       loginfo->nl_group = ULOG_DEFAULT_NLGROUP;
+       loginfo->qthreshold = ULOG_DEFAULT_QTHRESHOLD;
+
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+#define IPT_LOG_OPT_NLGROUP 0x01
+#define IPT_LOG_OPT_PREFIX 0x02
+#define IPT_LOG_OPT_CPRANGE 0x04
+#define IPT_LOG_OPT_QTHRESHOLD 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,
+                struct ipt_entry_target **target)
+{
+       struct ipt_ulog_info *loginfo =
+           (struct ipt_ulog_info *) (*target)->data;
+       int group_d;
+
+       switch (c) {
+       case '!':
+               if (*flags & IPT_LOG_OPT_NLGROUP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-nlgroup twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --ulog-nlgroup");
+               group_d = atoi(optarg);
+               if (group_d > 32 || group_d < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--ulog-nlgroup has to be between 1 and 32");
+
+               loginfo->nl_group = (1 << (group_d - 1));
+
+               *flags |= IPT_LOG_OPT_NLGROUP;
+               break;
+
+       case '#':
+               if (*flags & IPT_LOG_OPT_PREFIX)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-prefix twice");
+
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --ulog-prefix");
+
+               if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum prefix length %u for --ulog-prefix",
+                                  sizeof(loginfo->prefix) - 1);
+
+               strcpy(loginfo->prefix, optarg);
+               *flags |= IPT_LOG_OPT_PREFIX;
+               break;
+       case 'A':
+               if (*flags & IPT_LOG_OPT_CPRANGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-cprange twice");
+               if (atoi(optarg) < 0)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Negative copy range?");
+               loginfo->copy_range = atoi(optarg);
+               *flags |= IPT_LOG_OPT_CPRANGE;
+               break;
+       case 'B':
+               if (*flags & IPT_LOG_OPT_QTHRESHOLD)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ulog-qthreshold twice");
+               if (atoi(optarg) < 1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Negative or zero queue threshold ?");
+               if (atoi(optarg) > ULOG_MAX_QLEN)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Maximum queue length exceeded");
+               loginfo->qthreshold = atoi(optarg);
+               *flags |= IPT_LOG_OPT_QTHRESHOLD;
+               break;
+       }
+       return 1;
+}
+
+/* Final check; nothing. */
+static void final_check(unsigned int flags)
+{
+}
+
+/* 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_ulog_info *loginfo
+           = (const struct ipt_ulog_info *) target->data;
+
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("--ulog-prefix \"%s\" ", loginfo->prefix);
+
+       if (loginfo->nl_group != ULOG_DEFAULT_NLGROUP) {
+               printf("--ulog-nlgroup ");
+               print_groups(loginfo->nl_group);
+       }
+       if (loginfo->copy_range)
+               printf("--ulog-cprange %d ", loginfo->copy_range);
+
+       if (loginfo->qthreshold != ULOG_DEFAULT_QTHRESHOLD)
+               printf("--ulog-qthreshold %d ", loginfo->qthreshold);
+}
+
+/* Prints out the targinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_ulog_info *loginfo
+           = (const struct ipt_ulog_info *) target->data;
+
+       printf("ULOG ");
+       printf("copy_range %d nlgroup ", loginfo->copy_range);
+       print_groups(loginfo->nl_group);
+       if (strcmp(loginfo->prefix, "") != 0)
+               printf("prefix `%s' ", loginfo->prefix);
+       printf("queue_threshold %d ", loginfo->qthreshold);
+}
+
+static
+struct iptables_target ulog = { NULL,
+       "ULOG",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       IPT_ALIGN(sizeof(struct ipt_ulog_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts
+};
+
+void _init(void)
+{
+       register_target(&ulog);
+}
diff --git a/extensions/libipt_XOR.c b/extensions/libipt_XOR.c
new file mode 100644 (file)
index 0000000..3b05a30
--- /dev/null
@@ -0,0 +1,112 @@
+/* Shared library add-on to iptables for the XOR target
+ * (C) 2000 by Tim Vandermeersch <Tim.Vandermeersch@pandora.be>
+ * Based on libipt_TTL.c
+ *
+ * Version 1.0
+ *
+ * This program is distributed under the terms of GNU GPL
+ */
+
+#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_XOR.h>
+
+#define        IPT_KEY_SET             1
+#define IPT_BLOCKSIZE_SET      2
+
+static void init(struct ipt_entry_target *t, unsigned int *nfcache) 
+{
+}
+
+static void help(void) 
+{
+       printf(
+               "XOR target v%s options\n"
+               "  --key string           Set key to \"string\"\n"
+               "  --block-size           Set block size\n",
+               IPTABLES_VERSION);
+}
+
+static int parse(int c, char **argv, int invert, unsigned int *flags,
+               const struct ipt_entry *entry, 
+               struct ipt_entry_target **target)
+{
+       struct ipt_XOR_info *info = (struct ipt_XOR_info *) (*target)->data;
+       
+       if (!optarg)
+               exit_error(PARAMETER_PROBLEM, "XOR: too few arguments");
+       
+       if (check_inverse(optarg, &invert, NULL, 0))
+               exit_error(PARAMETER_PROBLEM, "XOR: unexpected '!'");
+
+       switch (c) {    
+               case '1':
+                       strncpy(info->key, optarg, 30);
+                       *flags |= IPT_KEY_SET;
+                       break;
+               case '2':
+                       info->block_size = atoi(optarg);
+                       *flags |= IPT_BLOCKSIZE_SET;
+                       break;
+               default:
+                       return 0;
+       }
+       
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & IPT_KEY_SET))
+               exit_error(PARAMETER_PROBLEM, "XOR: You must specify a key");
+       if (!(flags & IPT_BLOCKSIZE_SET))
+               exit_error(PARAMETER_PROBLEM, "XOR: You must specify a block-size");
+}
+
+static void save (const struct ipt_ip *ip,
+               const struct ipt_entry_target *target)
+{
+       const struct ipt_XOR_info *info = (struct ipt_XOR_info *) target->data;
+
+       printf("--key %s ", info->key);
+       printf("--block-size %u ", info->block_size);
+}
+
+static void print (const struct ipt_ip *ip,
+       const struct ipt_entry_target *target, int numeric)
+{
+       const struct ipt_XOR_info *info = (struct ipt_XOR_info *) target->data;
+
+       printf("key: %s ", info->key);
+       printf("block-size: %u ", info->block_size);
+}
+
+static struct option opts[] = {
+       { "key", 1, 0, '1' },
+       { "block-size", 1, 0, '2' },
+       { 0 }
+};
+
+static struct iptables_target XOR = { NULL, 
+       "XOR",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_XOR_info)),
+       IPT_ALIGN(sizeof(struct ipt_XOR_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts 
+};
+
+void _init(void)
+{
+       register_target(&XOR);
+}
diff --git a/extensions/libipt_addrtype.c b/extensions/libipt_addrtype.c
new file mode 100644 (file)
index 0000000..093e915
--- /dev/null
@@ -0,0 +1,214 @@
+/* Shared library add-on to iptables to add addrtype matching support 
+ * 
+ * This program is released under the terms of GNU GPL */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_addrtype.h>
+
+/* from linux/rtnetlink.h, must match order of enumeration */
+static char *rtn_names[] = {
+       "UNSPEC",
+       "UNICAST",
+       "LOCAL",
+       "BROADCAST",
+       "ANYCAST",
+       "MULTICAST",
+       "BLACKHOLE",
+       "UNREACHABLE",
+       "PROHIBIT",
+       "THROW",
+       "NAT",
+       "XRESOLVE",
+       NULL
+};
+
+static void help_types(void)
+{
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               printf("                                %s\n", rtn_names[i]);
+}
+
+static void help(void) 
+{
+       printf(
+"Address type match v%s options:\n"
+" [!] --src-type type[,...]      Match source address type\n"
+" [!] --dst-type type[,...]      Match destination address type\n"
+"\n"
+"Valid types:           \n"
+, IPTABLES_VERSION);
+       help_types();
+}
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       /* caching not yet implemented */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static int
+parse_type(const char *name, size_t strlen, u_int16_t *mask)
+{
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               if (strncasecmp(name, rtn_names[i], strlen) == 0) {
+                       /* build up bitmask for kernel module */
+                       *mask |= (1 << i);
+                       return 1;
+               }
+
+       return 0;
+}
+
+static void parse_types(const char *arg, u_int16_t *mask)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_type(arg, comma-arg, mask))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: bad type `%s'", arg);
+               arg = comma + 1;
+       }
+
+       if (strlen(arg) == 0 || !parse_type(arg, strlen(arg), mask))
+               exit_error(PARAMETER_PROBLEM, "addrtype: bad type `%s'", arg);
+}
+       
+#define IPT_ADDRTYPE_OPT_SRCTYPE       0x1
+#define IPT_ADDRTYPE_OPT_DSTTYPE       0x2
+
+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_addrtype_info *info =
+               (struct ipt_addrtype_info *) (*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags&IPT_ADDRTYPE_OPT_SRCTYPE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: can't specify src-type twice");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_types(argv[optind-1], &info->source);
+               if (invert)
+                       info->invert_source = 1;
+               *flags |= IPT_ADDRTYPE_OPT_SRCTYPE;
+               break;
+       case '2':
+               if (*flags&IPT_ADDRTYPE_OPT_DSTTYPE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "addrtype: can't specify dst-type twice");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_types(argv[optind-1], &info->dest);
+               if (invert)
+                       info->invert_dest = 1;
+               *flags |= IPT_ADDRTYPE_OPT_DSTTYPE;
+               break;
+       default:
+               return 0;
+       }
+       
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!(flags & (IPT_ADDRTYPE_OPT_SRCTYPE|IPT_ADDRTYPE_OPT_DSTTYPE)))
+               exit_error(PARAMETER_PROBLEM,
+                          "addrtype: you must specify --src-type or --dst-type");
+}
+
+static void print_types(u_int16_t mask)
+{
+       const char *sep = "";
+       int i;
+
+       for (i = 0; rtn_names[i]; i++)
+               if (mask & (1 << i)) {
+                       printf("%s%s", sep, rtn_names[i]);
+                       sep = ",";
+               }
+
+       printf(" ");
+}
+
+static void print(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match,
+               int numeric)
+{
+       const struct ipt_addrtype_info *info = 
+               (struct ipt_addrtype_info *) match->data;
+
+       printf("ADDRTYPE match ");
+       if (info->source) {
+               printf("src-type ");
+               if (info->invert_source)
+                       printf("!");
+               print_types(info->source);
+       }
+       if (info->dest) {
+               printf("dst-type ");
+               if (info->invert_dest)
+                       printf("!");
+               print_types(info->dest);
+       }
+}
+
+static void save(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match)
+{
+       const struct ipt_addrtype_info *info =
+               (struct ipt_addrtype_info *) match->data;
+
+       if (info->source) {
+               printf("--src-type ");
+               if (info->invert_source)
+                       printf("! ");
+               print_types(info->source);
+       }
+       if (info->dest) {
+               printf("--dst-type ");
+               if (info->invert_dest)
+                       printf("! ");
+               print_types(info->dest);
+       }
+}
+
+static struct option opts[] = {
+       { "src-type", 1, 0, '1' },
+       { "dst-type", 1, 0, '2' },
+       { 0 }
+};
+
+static
+struct iptables_match addrtype = {
+       NULL,
+       "addrtype",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       IPT_ALIGN(sizeof(struct ipt_addrtype_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts
+};
+
+
+void _init(void) 
+{
+       register_match(&addrtype);
+}
diff --git a/extensions/libipt_ah.c b/extensions/libipt_ah.c
new file mode 100644 (file)
index 0000000..1e58398
--- /dev/null
@@ -0,0 +1,191 @@
+/* Shared library add-on to iptables to add AH support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_ah.h>
+                                        
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"AH v%s options:\n"
+" --ahspi [!] spi[:spi]\n"
+"                              match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "ahspi", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_ah_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi =  strtoul(spistr,&ep,0) ;
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "AH no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "AH error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_ah_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_ah_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_ah_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_ah_spi(cp) : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_ah *ahinfo = (struct ipt_ah *)m->data;
+
+       ahinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define AH_SPI 0x01
+
+/* 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_ah *ahinfo = (struct ipt_ah *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & AH_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--ahspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_ah_spis(argv[optind-1], ahinfo->spis);
+               if (invert)
+                       ahinfo->invflags |= IPT_AH_INV_SPI;
+               *flags |= AH_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               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_ah *ah = (struct ipt_ah *)match->data;
+
+       printf("ah ");
+       print_spis("spi", ah->spis[0], ah->spis[1],
+                   ah->invflags & IPT_AH_INV_SPI);
+       if (ah->invflags & ~IPT_AH_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      ah->invflags & ~IPT_AH_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_ah *ahinfo = (struct ipt_ah *)match->data;
+
+       if (!(ahinfo->spis[0] == 0
+           && ahinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--ahspi %s", 
+                       (ahinfo->invflags & IPT_AH_INV_SPI) ? "! " : "");
+               if (ahinfo->spis[0]
+                   != ahinfo->spis[1])
+                       printf("%u:%u ",
+                              ahinfo->spis[0],
+                              ahinfo->spis[1]);
+               else
+                       printf("%u ",
+                              ahinfo->spis[0]);
+       }
+
+}
+
+static
+struct iptables_match ah
+= { NULL,
+    "ah",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_ah)),
+    IPT_ALIGN(sizeof(struct ipt_ah)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match(&ah);
+}
diff --git a/extensions/libipt_condition.c b/extensions/libipt_condition.c
new file mode 100644 (file)
index 0000000..750111b
--- /dev/null
@@ -0,0 +1,115 @@
+/* Shared library add-on to iptables for condition match */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include<linux/netfilter_ipv4/ip_tables.h>
+#include<linux/netfilter_ipv4/ipt_condition.h>
+
+
+static void
+help(void)
+{
+       printf("condition match v%s options:\n"
+              "--condition [!] filename       "
+              "Match on boolean value stored in /proc file\n",
+              IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' },
+       { .name = 0 }
+};
+
+
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+
+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 condition_info *info =
+           (struct condition_info *) (*match)->data;
+
+       if (c == 'X') {
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple conditions");
+
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (strlen(argv[optind - 1]) < CONDITION_NAME_LEN)
+                       strcpy(info->name, argv[optind - 1]);
+               else
+                       exit_error(PARAMETER_PROBLEM,
+                                  "File name too long");
+
+               info->invert = invert;
+               *flags = 1;
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "Condition match: must specify --condition");
+}
+
+
+static void
+print(const struct ipt_ip *ip,
+                 const struct ipt_entry_match *match, int numeric)
+{
+       const struct condition_info *info =
+           (const struct condition_info *) match->data;
+
+       printf("condition %s%s ", (info->invert) ? "!" : "", info->name);
+}
+
+
+static void
+save(const struct ipt_ip *ip,
+                const struct ipt_entry_match *match)
+{
+       const struct condition_info *info =
+           (const struct condition_info *) match->data;
+
+       printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name);
+}
+
+
+static struct iptables_match condition = {
+       .name = "condition",
+       .version = IPTABLES_VERSION,
+       .size = IPT_ALIGN(sizeof(struct condition_info)),
+       .userspacesize = IPT_ALIGN(sizeof(struct condition_info)),
+       .help = &help,
+       .init = &init,
+       .parse = &parse,
+       .final_check = &final_check,
+       .print = &print,
+       .save = &save,
+       .extra_opts = opts
+};
+
+
+void
+_init(void)
+{
+       register_match(&condition);
+}
diff --git a/extensions/libipt_connbytes.c b/extensions/libipt_connbytes.c
new file mode 100644 (file)
index 0000000..abc37be
--- /dev/null
@@ -0,0 +1,134 @@
+/* Shared library add-on to iptables to add byte 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_connbytes.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"connbytes v%s options:\n"
+" [!] --connbytes from:[to]\n"
+"                              Transfered byte range to match\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "connbytes", 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_range(const char *arg, struct ipt_connbytes_info *si)
+{
+       char *colon,*p;
+
+       si->from = strtol(arg,&colon,10);
+       if (*colon != ':') 
+               exit_error(PARAMETER_PROBLEM, "Bad range `%s'", arg);
+       si->to = strtol(colon+1,&p,10);
+       if (p == colon+1) {
+               /* second number omited */
+               si->to = 0xffffffff;
+       }
+       if (si->from > si->to)
+               exit_error(PARAMETER_PROBLEM, "%lu should be less than %lu", si->from,si->to);
+}
+
+/* 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_connbytes_info *sinfo = (struct ipt_connbytes_info *)(*match)->data;
+       int i;
+
+       switch (c) {
+       case '1':
+               if (check_inverse(optarg, &invert, optind, 0))
+                       optind++;
+
+               parse_range(argv[optind-1], sinfo);
+               if (invert) {
+                       i = sinfo->from;
+                       sinfo->from = sinfo->to;
+                       sinfo->to = i;
+               }
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--connbytes'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
+
+       if (sinfo->from > sinfo->to) 
+               printf("connbytes ! %lu:%lu",sinfo->to,sinfo->from);
+       else
+               printf("connbytes %lu:%lu",sinfo->from,sinfo->to);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connbytes_info *sinfo = (struct ipt_connbytes_info *)match->data;
+
+       if (sinfo->from > sinfo->to) 
+               printf("! --connbytes %lu:%lu",sinfo->to,sinfo->from);
+       else
+               printf("--connbytes %lu:%lu",sinfo->from,sinfo->to);
+}
+
+static
+struct iptables_match state
+= { NULL,
+    "connbytes",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+    IPT_ALIGN(sizeof(struct ipt_connbytes_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_connlimit.c b/extensions/libipt_connlimit.c
new file mode 100644 (file)
index 0000000..c82c6e4
--- /dev/null
@@ -0,0 +1,132 @@
+/* Shared library add-on to iptables to add connection limit support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ipt_connlimit.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"connlimit v%s options:\n"
+"[!] --connlimit-above n               match if the number of existing tcp connections is (not) above n\n"
+" --connlimit-mask n           group hosts using mask\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "connlimit-above", 1, 0, '1' },
+       { "connlimit-mask",  1, 0, '2' },
+       {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_connlimit_info *info = (struct ipt_connlimit_info*)(*match)->data;
+
+       if (0 == (*flags & 2)) {
+               /* set default mask unless we've already seen a mask option */
+               info->mask = htonl(0xFFFFFFFF);
+       }
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               info->limit = atoi(argv[optind-1]);
+               info->inverse = invert;
+               *flags |= 1;
+               break;
+
+       case '2':
+               info->mask = htonl(0xFFFFFFFF << (32 - atoi(argv[optind-1])));
+               *flags |= 2;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check */
+static void final_check(unsigned int flags)
+{
+       if (!flags & 1)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--connlimit-above'");
+}
+
+static int
+count_bits(u_int32_t mask)
+{
+       int i, bits;
+
+       for (bits = 0, i = 31; i >= 0; i--) {
+               if (mask & htonl((u_int32_t)1 << i)) {
+                       bits++;
+                       continue;
+               }
+               break;
+       }
+       return bits;
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_connlimit_info *info = (struct ipt_connlimit_info*)match->data;
+
+       printf("#conn/%d %s %d ", count_bits(info->mask),
+              info->inverse ? "<" : ">", info->limit);
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_connlimit_info *info = (struct ipt_connlimit_info*)match->data;
+
+       printf("%s--connlimit-above %d ",info->inverse ? "! " : "",info->limit);
+       printf("--connlimit-mask %d ",count_bits(info->mask));
+}
+
+static struct iptables_match connlimit = {
+       name:           "connlimit",
+       version:        IPTABLES_VERSION,
+       size:           IPT_ALIGN(sizeof(struct ipt_connlimit_info)),
+       userspacesize:  offsetof(struct ipt_connlimit_info,data),
+       help:           help,
+       init:           init,
+       parse:          parse,
+       final_check:    final_check,
+       print:          print,
+       save:           save,
+       extra_opts:     opts
+};
+
+void _init(void)
+{
+       register_match(&connlimit);
+}
diff --git a/extensions/libipt_connmark.c b/extensions/libipt_connmark.c
new file mode 100644 (file)
index 0000000..8f81f02
--- /dev/null
@@ -0,0 +1,131 @@
+/* Shared library add-on to iptables to add CONNMARK 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_connmark.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"CONNMARK match v%s options:\n"
+"[!] --mark value[/mask]         Match nfmark value with optional mask\n"
+"\n",
+IPTABLES_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_connmark_info *markinfo = (struct ipt_connmark_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 numeric)
+{
+       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)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       printf("CONNMARK match ");
+       if (info->invert)
+               printf("!");
+       print_mark(info->mark, info->mask, 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)
+{
+       struct ipt_connmark_info *info = (struct ipt_connmark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static
+struct iptables_match mark
+= { NULL,
+    "connmark",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    IPT_ALIGN(sizeof(struct ipt_connmark_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&mark);
+}
diff --git a/extensions/libipt_conntrack.c b/extensions/libipt_conntrack.c
new file mode 100644 (file)
index 0000000..63b38e9
--- /dev/null
@@ -0,0 +1,529 @@
+/* Shared library add-on to iptables for conntrack matching support.
+ * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_conntrack.h>
+#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
+#include <linux/netfilter_ipv4/ipt_conntrack.h>
+
+#ifndef IPT_CONNTRACK_STATE_UNTRACKED
+#define IPT_CONNTRACK_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 3))
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"conntrack match v%s options:\n"
+" [!] --ctstate [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED|SNAT|DNAT][,...]\n"
+"                              State(s) to match\n"
+" [!] --ctproto        proto           Protocol to match; by number or name, eg. `tcp'\n"
+"     --ctorigsrc  [!] address[/mask]\n"
+"                              Original source specification\n"
+"     --ctorigdst  [!] address[/mask]\n"
+"                              Original destination specification\n"
+"     --ctreplsrc  [!] address[/mask]\n"
+"                              Reply source specification\n"
+"     --ctrepldst  [!] address[/mask]\n"
+"                              Reply destination specification\n"
+" [!] --ctstatus [NONE|EXPECTED|SEEN_REPLY|ASSURED|CONFIRMED][,...]\n"
+"                              Status(es) to match\n"
+" [!] --ctexpire time[:time]   Match remaining lifetime in seconds against\n"
+"                              value or range of values (inclusive)\n"
+"\n", IPTABLES_VERSION);
+}
+
+
+
+static struct option opts[] = {
+       { "ctstate", 1, 0, '1' },
+       { "ctproto", 1, 0, '2' },
+       { "ctorigsrc", 1, 0, '3' },
+       { "ctorigdst", 1, 0, '4' },
+       { "ctreplsrc", 1, 0, '5' },
+       { "ctrepldst", 1, 0, '6' },
+       { "ctstatus", 1, 0, '7' },
+       { "ctexpire", 1, 0, '8' },
+       {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_conntrack_info *sinfo)
+{
+       if (strncasecmp(state, "INVALID", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_INVALID;
+       else if (strncasecmp(state, "NEW", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_NEW);
+       else if (strncasecmp(state, "ESTABLISHED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED);
+       else if (strncasecmp(state, "RELATED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED);
+       else if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_UNTRACKED;
+       else if (strncasecmp(state, "SNAT", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_SNAT;
+       else if (strncasecmp(state, "DNAT", strlen) == 0)
+               sinfo->statemask |= IPT_CONNTRACK_STATE_DNAT;
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_states(const char *arg, struct ipt_conntrack_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_state(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_state(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad ctstate `%s'", arg);
+}
+
+static int
+parse_status(const char *status, size_t strlen, struct ipt_conntrack_info *sinfo)
+{
+       if (strncasecmp(status, "NONE", strlen) == 0)
+               sinfo->statusmask |= 0;
+       else if (strncasecmp(status, "EXPECTED", strlen) == 0)
+               sinfo->statusmask |= IPS_EXPECTED;
+       else if (strncasecmp(status, "SEEN_REPLY", strlen) == 0)
+               sinfo->statusmask |= IPS_SEEN_REPLY;
+       else if (strncasecmp(status, "ASSURED", strlen) == 0)
+               sinfo->statusmask |= IPS_ASSURED;
+#ifdef IPS_CONFIRMED
+       else if (strncasecmp(status, "CONFIRMED", strlen) == 0)
+               sinfo->stausmask |= IPS_CONFIRMED;
+#endif
+       else
+               return 0;
+       return 1;
+}
+
+static void
+parse_statuses(const char *arg, struct ipt_conntrack_info *sinfo)
+{
+       const char *comma;
+
+       while ((comma = strchr(arg, ',')) != NULL) {
+               if (comma == arg || !parse_status(arg, comma-arg, sinfo))
+                       exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
+               arg = comma+1;
+       }
+
+       if (strlen(arg) == 0 || !parse_status(arg, strlen(arg), sinfo))
+               exit_error(PARAMETER_PROBLEM, "Bad ctstatus `%s'", arg);
+}
+
+
+static unsigned long
+parse_expire(const char *s)
+{
+       unsigned int len;
+       
+       if (string_to_number(s, 0, 0xFFFFFFFF, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "expire value invalid: `%s'\n", s);
+       else
+               return len;
+}
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_expires(const char *s, struct ipt_conntrack_info *sinfo)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               sinfo->expires_min = sinfo->expires_max = parse_expire(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               sinfo->expires_min = buffer[0] ? parse_expire(buffer) : 0;
+               sinfo->expires_max = cp[0] ? parse_expire(cp) : 0xFFFFFFFF;
+       }
+       free(buffer);
+       
+       if (sinfo->expires_min > sinfo->expires_max)
+               exit_error(PARAMETER_PROBLEM,
+                          "expire min. range value `%lu' greater than max. "
+                          "range value `%lu'", sinfo->expires_min, sinfo->expires_max);
+       
+}
+
+/* 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_conntrack_info *sinfo = (struct ipt_conntrack_info *)(*match)->data;
+       char *protocol = NULL;
+       unsigned int naddrs = 0;
+       struct in_addr *addrs = NULL;
+
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_states(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_STATE;
+               }
+               sinfo->flags |= IPT_CONNTRACK_STATE;
+               break;
+
+       case '2':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if(invert)
+                       sinfo->invflags |= IPT_CONNTRACK_PROTO;
+
+               /* Canonicalize into lower case */
+               for (protocol = argv[optind-1]; *protocol; protocol++)
+                       *protocol = tolower(*protocol);
+
+               protocol = argv[optind-1];
+               sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum = parse_protocol(protocol);
+
+               if (sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum == 0
+                   && (sinfo->invflags & IPT_INV_PROTO))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "rule would never match protocol");
+
+               sinfo->flags |= IPT_CONNTRACK_PROTO;
+               break;
+
+       case '3':
+               check_inverse(optarg, &invert, &optind, 9);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_ORIGSRC;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_ORIGSRC;
+               break;
+
+       case '4':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_ORIGDST;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_ORIGDST;
+               break;
+
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_REPLSRC;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->sipmsk[IP_CT_DIR_REPLY],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_REPLY].src.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_REPLSRC;
+               break;
+
+       case '6':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (invert)
+                       sinfo->invflags |= IPT_CONNTRACK_REPLDST;
+
+               parse_hostnetworkmask(argv[optind-1], &addrs,
+                                       &sinfo->dipmsk[IP_CT_DIR_REPLY],
+                                       &naddrs);
+               if(naddrs > 1)
+                       exit_error(PARAMETER_PROBLEM,
+                               "multiple IP addresses not allowed");
+
+               if(naddrs == 1) {
+                       sinfo->tuple[IP_CT_DIR_REPLY].dst.ip = addrs[0].s_addr;
+               }
+
+               sinfo->flags |= IPT_CONNTRACK_REPLDST;
+               break;
+
+       case '7':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_statuses(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_STATUS;
+               }
+               sinfo->flags |= IPT_CONNTRACK_STATUS;
+               break;
+
+       case '8':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               parse_expires(argv[optind-1], sinfo);
+               if (invert) {
+                       sinfo->invflags |= IPT_CONNTRACK_EXPIRES;
+               }
+               sinfo->flags |= IPT_CONNTRACK_EXPIRES;
+               break;
+
+       default:
+               return 0;
+       }
+
+       *flags = sinfo->flags;
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify one or more options");
+}
+
+static void
+print_state(unsigned int statemask)
+{
+       const char *sep = "";
+
+       if (statemask & IPT_CONNTRACK_STATE_INVALID) {
+               printf("%sINVALID", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_NEW)) {
+               printf("%sNEW", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_RELATED)) {
+               printf("%sRELATED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_BIT(IP_CT_ESTABLISHED)) {
+               printf("%sESTABLISHED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_UNTRACKED) {
+               printf("%sUNTRACKED", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_SNAT) {
+               printf("%sSNAT", sep);
+               sep = ",";
+       }
+       if (statemask & IPT_CONNTRACK_STATE_DNAT) {
+               printf("%sDNAT", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+static void
+print_status(unsigned int statusmask)
+{
+       const char *sep = "";
+
+       if (statusmask & IPS_EXPECTED) {
+               printf("%sEXPECTED", sep);
+               sep = ",";
+       }
+       if (statusmask & IPS_SEEN_REPLY) {
+               printf("%sSEEN_REPLY", sep);
+               sep = ",";
+       }
+       if (statusmask & IPS_ASSURED) {
+               printf("%sASSURED", sep);
+               sep = ",";
+       }
+#ifdef IPS_CONFIRMED
+       if (statusmask & IPS_CONFIRMED) {
+               printf("%sCONFIRMED", sep);
+               sep =",";
+       }
+#endif
+       if (statusmask == 0) {
+               printf("%sNONE", sep);
+               sep = ",";
+       }
+       printf(" ");
+}
+
+static void
+print_addr(struct in_addr *addr, struct in_addr *mask, int inv, int numeric)
+{
+       char buf[BUFSIZ];
+
+        if (inv)
+                       fputc('!', stdout);
+
+       if (mask->s_addr == 0L && !numeric)
+               printf("%s ", "anywhere");
+       else {
+               if (numeric)
+                       sprintf(buf, "%s", addr_to_dotted(addr));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(addr));
+               strcat(buf, mask_to_dotted(mask));
+               printf("%s ", buf);
+       }
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void
+matchinfo_print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric, const char *optpfx)
+{
+       struct ipt_conntrack_info *sinfo = (struct ipt_conntrack_info *)match->data;
+
+       if(sinfo->flags & IPT_CONNTRACK_STATE) {
+               printf("%sctstate ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_STATE)
+                       printf("! ");
+               print_state(sinfo->statemask);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_ORIGSRC) {
+               printf("%sctorigsrc ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
+                   &sinfo->sipmsk[IP_CT_DIR_ORIGINAL],
+                   sinfo->invflags & IPT_CONNTRACK_ORIGSRC,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_ORIGDST) {
+               printf("%sctorigdst ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
+                   &sinfo->dipmsk[IP_CT_DIR_ORIGINAL],
+                   sinfo->invflags & IPT_CONNTRACK_ORIGDST,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_REPLSRC) {
+               printf("%sctreplsrc ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
+                   &sinfo->sipmsk[IP_CT_DIR_REPLY],
+                   sinfo->invflags & IPT_CONNTRACK_REPLSRC,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_REPLDST) {
+               printf("%sctrepldst ", optpfx);
+
+               print_addr(
+                   (struct in_addr *)&sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
+                   &sinfo->dipmsk[IP_CT_DIR_REPLY],
+                   sinfo->invflags & IPT_CONNTRACK_REPLDST,
+                   numeric);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_STATUS) {
+               printf("%sctstatus ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_STATE)
+                       printf("! ");
+               print_status(sinfo->statusmask);
+       }
+
+       if(sinfo->flags & IPT_CONNTRACK_EXPIRES) {
+               printf("%sctexpire ", optpfx);
+               if (sinfo->invflags & IPT_CONNTRACK_EXPIRES)
+                       printf("! ");
+
+               if (sinfo->expires_max == sinfo->expires_min)
+                       printf("%lu ", sinfo->expires_min);
+               else
+                       printf("%lu:%lu ", sinfo->expires_min, sinfo->expires_max);
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       matchinfo_print(ip, match, numeric, "");
+}
+
+/* Saves the matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       matchinfo_print(ip, match, 0, "--");
+}
+
+static
+struct iptables_match conntrack
+= { NULL,
+    "conntrack",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+    IPT_ALIGN(sizeof(struct ipt_conntrack_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&conntrack);
+}
diff --git a/extensions/libipt_dscp.c b/extensions/libipt_dscp.c
new file mode 100644 (file)
index 0000000..1d61da8
--- /dev/null
@@ -0,0 +1,179 @@
+/* Shared library add-on to iptables for DSCP
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_dscp.c borrowed heavily from libipt_tos.c
+ *
+ * --class support added by Iain Barnes
+ * 
+ * For a list of DSCP codepoints see 
+ * http://www.iana.org/assignments/dscp-registry
+ *
+ */
+#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_dscp.h>
+
+/* This is evil, but it's my code - HW*/
+#include "libipt_dscp_helper.c"
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache) 
+{
+       *nfcache |= NFC_IP_TOS;
+}
+
+static void help(void) 
+{
+       printf(
+"DSCP match v%s options\n"
+"[!] --dscp value              Match DSCP codepoint with numerical value\n"
+"                              This value can be in decimal (ex: 32)\n"
+"                              or in hex (ex: 0x20)\n"
+"[!] --dscp-class name         Match the DiffServ class. This value may\n"
+"                              be any of the BE,EF, AFxx or CSx classes\n"
+"\n"
+"                              These two options are mutually exclusive !\n"
+                               , IPTABLES_VERSION
+);
+}
+
+static struct option opts[] = {
+       { "dscp", 1, 0, 'F' },
+       { "dscp-class", 1, 0, 'G' },
+       { 0 }
+};
+
+static void
+parse_dscp(const unsigned char *s, struct ipt_dscp_info *dinfo)
+{
+       unsigned int dscp;
+       
+       if (string_to_number(s, 0, 255, &dscp) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid dscp `%s'\n", s);
+
+       if (dscp > IPT_DSCP_MAX)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP `%d` out of range\n", dscp);
+
+       dinfo->dscp = (u_int8_t )dscp;
+       return;
+}
+
+
+static void
+parse_class(const char *s, struct ipt_dscp_info *dinfo)
+{
+       unsigned int dscp = class_to_dscp(s);
+
+       /* Assign the value */
+       dinfo->dscp = (u_int8_t)dscp;
+}
+
+
+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_dscp_info *dinfo
+               = (struct ipt_dscp_info *)(*match)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "DSCP match: Only use --dscp ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_dscp(argv[optind-1], dinfo);
+               if (invert)
+                       dinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       case 'G':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                       "DSCP match: Only use --dscp-class ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_class(argv[optind - 1], dinfo);
+               if (invert)
+                       dinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "DSCP match: Parameter --dscp is required");
+}
+
+static void
+print_dscp(u_int8_t dscp, int invert, int numeric)
+{
+       if (invert)
+               fputc('!', stdout);
+
+       printf("0x%02x ", dscp);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_dscp_info *dinfo =
+               (const struct ipt_dscp_info *)match->data;
+       printf("DSCP match ");
+       print_dscp(dinfo->dscp, dinfo->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)
+{
+       const struct ipt_dscp_info *dinfo =
+               (const struct ipt_dscp_info *)match->data;
+
+       printf("--dscp ");
+       print_dscp(dinfo->dscp, dinfo->invert, 1);
+}
+
+static
+struct iptables_match dscp
+= { NULL,
+    "dscp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+    IPT_ALIGN(sizeof(struct ipt_dscp_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&dscp);
+}
diff --git a/extensions/libipt_dscp_helper.c b/extensions/libipt_dscp_helper.c
new file mode 100644 (file)
index 0000000..0279670
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * DiffServ classname <-> DiffServ codepoint mapping functions.
+ *
+ * The latest list of the mappings can be found at:
+ * <http://www.iana.org/assignments/dscp-registry>
+ *
+ * This code is released under the GNU GPL v2, 1991
+ * 
+ * Author: Iain Barnes
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <iptables_common.h>
+
+
+
+static struct ds_class
+{
+       const char *name;
+       unsigned int dscp;
+} ds_classes[] = 
+{
+       { "CS0", 0x00 },
+       { "CS1", 0x08 },
+       { "CS2", 0x10 },
+       { "CS3", 0x18 },
+       { "CS4", 0x20 },
+       { "CS5", 0x28 },
+       { "CS6", 0x30 },
+       { "CS7", 0x38 },
+       { "BE", 0x00 },
+       { "AF11", 0x0a },
+       { "AF12", 0x0c },
+       { "AF13", 0x0e },
+       { "AF21", 0x12 },
+       { "AF22", 0x14 },
+       { "AF23", 0x16 },
+       { "AF31", 0x1a },
+       { "AF32", 0x1c },
+       { "AF33", 0x1e },
+       { "AF41", 0x22 },
+       { "AF42", 0x24 },
+       { "AF43", 0x26 },
+       { "EF", 0x2e }
+};
+
+
+
+static unsigned int 
+class_to_dscp(const char *name)
+{
+       int i;
+
+       for (i = 0; i < sizeof(ds_classes) / sizeof(struct ds_class); i++) {
+               if (!strncasecmp(name, ds_classes[i].name,
+                                       strlen(ds_classes[i].name))) 
+                       return ds_classes[i].dscp;
+       }
+
+       exit_error(PARAMETER_PROBLEM, 
+                       "Invalid DSCP value `%s'\n", name);
+}
+
+
+
+static const char *
+dscp_to_name(unsigned int dscp)
+{
+       int i;
+
+       for (i = 0; i < sizeof(ds_classes) / sizeof(struct ds_class); i++) {
+               if (dscp == ds_classes[i].dscp)
+                       return ds_classes[i].name;
+       }
+
+       
+       exit_error(PARAMETER_PROBLEM,
+                       "Invalid DSCP value `%d'\n", dscp);
+}
+
+
diff --git a/extensions/libipt_ecn.c b/extensions/libipt_ecn.c
new file mode 100644 (file)
index 0000000..d7b7f3b
--- /dev/null
@@ -0,0 +1,177 @@
+/* Shared library add-on to iptables for ECN matching
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#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_ecn.h>
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache) 
+{
+       *nfcache |= NFC_IP_TOS;
+}
+
+static void help(void) 
+{
+       printf(
+"ECN match v%s options\n"
+"[!] --ecn-tcp-cwr             Match CWR bit of TCP header\n"
+"[!] --ecn-tcp-ece             Match ECE bit of TCP header\n"
+"[!] --ecn-ip-ect [0..3]       Match ECN codepoint in IPv4 header\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "ecn-tcp-cwr", .has_arg = 0, .flag = 0, .val = 'F' },
+       { .name = "ecn-tcp-ece", .has_arg = 0, .flag = 0, .val = 'G' },
+       { .name = "ecn-ip-ect",  .has_arg = 1, .flag = 0, .val = 'H' },
+       { .name = 0 }
+};
+
+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)
+{
+       unsigned int result;
+       struct ipt_ecn_info *einfo
+               = (struct ipt_ecn_info *)(*match)->data;
+
+       switch (c) {
+       case 'F':
+               if (*flags & IPT_ECN_OP_MATCH_CWR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->operation |= IPT_ECN_OP_MATCH_CWR;
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_CWR;
+               *flags |= IPT_ECN_OP_MATCH_CWR;
+               break;
+
+       case 'G':
+               if (*flags & IPT_ECN_OP_MATCH_ECE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               einfo->operation |= IPT_ECN_OP_MATCH_ECE;
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_ECE;
+               *flags |= IPT_ECN_OP_MATCH_ECE;
+               break;
+
+       case 'H':
+               if (*flags & IPT_ECN_OP_MATCH_IP)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: can only use parameter ONCE!");
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       einfo->invert |= IPT_ECN_OP_MATCH_IP;
+               *flags |= IPT_ECN_OP_MATCH_IP;
+               einfo->operation |= IPT_ECN_OP_MATCH_IP;
+               if (string_to_number(optarg, 0, 3, &result))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ECN match: Value out of range");
+               einfo->ip_ect = result;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "ECN match: some option required");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_ecn_info *einfo =
+               (const struct ipt_ecn_info *)match->data;
+
+       printf("ECN match ");
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_ECE)
+                       fputc('!', stdout);
+               printf("ECE ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_CWR)
+                       fputc('!', stdout);
+               printf("CWR ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_IP)
+                       fputc('!', stdout);
+               printf("ECT=%d ", einfo->ip_ect);
+       }
+}
+
+/* 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_ecn_info *einfo =
+               (const struct ipt_ecn_info *)match->data;
+       
+       if (einfo->operation & IPT_ECN_OP_MATCH_ECE) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_ECE)
+                       printf("! ");
+               printf("--ecn-tcp-ece ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_CWR) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_CWR)
+                       printf("! ");
+               printf("--ecn-tcp-cwr ");
+       }
+
+       if (einfo->operation & IPT_ECN_OP_MATCH_IP) {
+               if (einfo->invert & IPT_ECN_OP_MATCH_IP)
+                       printf("! ");
+               printf("--ecn-ip-ect %d", einfo->ip_ect);
+       }
+}
+
+static
+struct iptables_match ecn
+= { .name          = "ecn",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_ecn_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&ecn);
+}
diff --git a/extensions/libipt_esp.c b/extensions/libipt_esp.c
new file mode 100644 (file)
index 0000000..531fdd1
--- /dev/null
@@ -0,0 +1,191 @@
+/* Shared library add-on to iptables to add ESP support. */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_esp.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ESP v%s options:\n"
+" --espspi [!] spi[:spi]\n"
+"                              match spi (range)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "espspi", 1, 0, '1' },
+       {0}
+};
+
+static u_int32_t
+parse_esp_spi(const char *spistr)
+{
+       unsigned long int spi;
+       char* ep;
+
+       spi =  strtoul(spistr,&ep,0) ;
+
+       if ( spistr == ep ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP no valid digits in spi `%s'", spistr);
+       }
+       if ( spi == ULONG_MAX  && errno == ERANGE ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "spi `%s' specified too big: would overflow", spistr);
+       }       
+       if ( *spistr != '\0'  && *ep != '\0' ) {
+               exit_error(PARAMETER_PROBLEM,
+                          "ESP error parsing spi `%s'", spistr);
+       }
+       return (u_int32_t) spi;
+}
+
+static void
+parse_esp_spis(const char *spistring, u_int32_t *spis)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(spistring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               spis[0] = spis[1] = parse_esp_spi(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               spis[0] = buffer[0] ? parse_esp_spi(buffer) : 0;
+               spis[1] = cp[0] ? parse_esp_spi(cp) : 0xFFFFFFFF;
+       }
+       free(buffer);
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_esp *espinfo = (struct ipt_esp *)m->data;
+
+       espinfo->spis[1] = 0xFFFFFFFF;
+}
+
+#define ESP_SPI 0x01
+
+/* 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_esp *espinfo = (struct ipt_esp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & ESP_SPI)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--espspi' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_esp_spis(argv[optind-1], espinfo->spis);
+               if (invert)
+                       espinfo->invflags |= IPT_ESP_INV_SPI;
+               *flags |= ESP_SPI;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; we don't care. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+static void
+print_spis(const char *name, u_int32_t min, u_int32_t max,
+           int invert)
+{
+       const char *inv = invert ? "!" : "";
+
+       if (min != 0 || max != 0xFFFFFFFF || invert) {
+               printf("%s", name);
+               if (min == max) {
+                       printf(":%s", inv);
+                       printf("%u", min);
+               } else {
+                       printf("s:%s", inv);
+                       printf("%u",min);
+                       printf(":");
+                       printf("%u",max);
+               }
+               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_esp *esp = (struct ipt_esp *)match->data;
+
+       printf("esp ");
+       print_spis("spi", esp->spis[0], esp->spis[1],
+                   esp->invflags & IPT_ESP_INV_SPI);
+       if (esp->invflags & ~IPT_ESP_INV_MASK)
+               printf("Unknown invflags: 0x%X ",
+                      esp->invflags & ~IPT_ESP_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_esp *espinfo = (struct ipt_esp *)match->data;
+
+       if (!(espinfo->spis[0] == 0
+           && espinfo->spis[1] == 0xFFFFFFFF)) {
+               printf("--espspi %s", 
+                       (espinfo->invflags & IPT_ESP_INV_SPI) ? "! " : "");
+               if (espinfo->spis[0]
+                   != espinfo->spis[1])
+                       printf("%u:%u ",
+                              espinfo->spis[0],
+                              espinfo->spis[1]);
+               else
+                       printf("%u ",
+                              espinfo->spis[0]);
+       }
+
+}
+
+static
+struct iptables_match esp
+= { NULL,
+    "esp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_esp)),
+    IPT_ALIGN(sizeof(struct ipt_esp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match(&esp);
+}
diff --git a/extensions/libipt_fuzzy.c b/extensions/libipt_fuzzy.c
new file mode 100644 (file)
index 0000000..6ebcb9f
--- /dev/null
@@ -0,0 +1,159 @@
+/* 
+   Shared library add-on to iptables to add match support for the fuzzy match.
+   
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+2002-08-07 Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Initial version.
+2003-06-09 Hime Aguiar e Oliveira Jr. <hime@engineer.com> : Bug corrections in
+the save function , thanks to information given by Jean-Francois Patenaude .
+
+*/
+
+#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_fuzzy.h>
+
+
+static void
+help(void)
+{
+       printf(
+"fuzzy v%s options:\n"
+"                      --lower-limit number (in packets per second)\n"
+"                      --upper-limit number\n"
+,IPTABLES_VERSION);
+};
+
+static struct option opts[] = {
+       { "lower-limit", 1 , 0 , '1' } ,
+       { "upper-limit", 1 , 0 , '2' } ,
+       { 0 }
+};
+
+/* Initialize data structures */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_fuzzy_info *presentinfo = (struct ipt_fuzzy_info *)(m)->data;
+       *nfcache |= NFC_UNKNOWN;
+
+       /*
+        * Default rates ( I'll improve this very soon with something based 
+        * on real statistics of the running machine ) .
+       */
+
+       presentinfo->minimum_rate = 1000;
+       presentinfo->maximum_rate = 2000;
+}
+
+#define IPT_FUZZY_OPT_MINIMUM  0x01
+#define IPT_FUZZY_OPT_MAXIMUM  0x02
+
+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_fuzzy_info *fuzzyinfo = (struct ipt_fuzzy_info *)(*match)->data;
+
+       u_int32_t num;
+
+       switch (c) {
+
+       case '1':
+               
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,"Can't specify ! --lower-limit");
+
+       if (*flags & IPT_FUZZY_OPT_MINIMUM)
+                     exit_error(PARAMETER_PROBLEM,"Can't specify --lower-limit twice");
+       
+       if (string_to_number(optarg,1,MAXFUZZYRATE,&num) == -1 || num < 1)
+                       exit_error(PARAMETER_PROBLEM,"BAD --lower-limit");
+
+               fuzzyinfo->minimum_rate = num ;
+
+               *flags |= IPT_FUZZY_OPT_MINIMUM;
+               
+               break;
+
+       case '2':
+
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,"Can't specify ! --upper-limit");
+
+       if (*flags & IPT_FUZZY_OPT_MAXIMUM)
+          exit_error(PARAMETER_PROBLEM,"Can't specify --upper-limit twice");
+
+       if (string_to_number(optarg,1,MAXFUZZYRATE,&num) == -1 || num < 1)
+               exit_error(PARAMETER_PROBLEM,"BAD --upper-limit");
+
+               fuzzyinfo->maximum_rate = num ;
+
+               *flags |= IPT_FUZZY_OPT_MAXIMUM;
+
+               break ;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_fuzzy_info *fuzzyinfo
+               = (const struct ipt_fuzzy_info *)match->data;
+
+       printf(" fuzzy: lower limit = %u pps - upper limit = %u pps ",fuzzyinfo->minimum_rate,fuzzyinfo->maximum_rate);
+
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_fuzzy_info *fuzzyinfo
+               = (const struct ipt_fuzzy_info *)match->data;
+
+       printf("--lower-limit %u ",fuzzyinfo->minimum_rate);
+       printf("--upper-limit %u ",fuzzyinfo->maximum_rate);
+
+}
+
+struct iptables_match fuzzy_match
+= { NULL,
+    "fuzzy",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
+    IPT_ALIGN(sizeof(struct ipt_fuzzy_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&fuzzy_match);
+}
diff --git a/extensions/libipt_helper.c b/extensions/libipt_helper.c
new file mode 100644 (file)
index 0000000..4b16e02
--- /dev/null
@@ -0,0 +1,108 @@
+/* Shared library add-on to iptables to add related packet 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_helper.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"helper match v%s options:\n"
+"[!] --helper string        Match helper identified by string\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "helper", 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_helper_info *info = (struct ipt_helper_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &invert, 0);
+               strncpy(info->name, optarg, 29);
+               if (invert)
+                       info->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --helper. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "helper match: You must specify `--helper'");
+}
+
+/* Prints out the info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_helper_info *info = (struct ipt_helper_info *)match->data;
+
+       printf("helper match %s\"%s\" ", info->invert ? "! " : "", info->name);
+}
+
+/* Saves the union ipt_info in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_helper_info *info = (struct ipt_helper_info *)match->data;
+
+       printf("%s--helper \"%s\" ",info->invert ? "! " : "", info->name);
+}
+
+static
+struct iptables_match helper
+= { NULL,
+    "helper",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_helper_info)),
+    IPT_ALIGN(sizeof(struct ipt_helper_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&helper);
+}
diff --git a/extensions/libipt_icmp.c b/extensions/libipt_icmp.c
new file mode 100644 (file)
index 0000000..f1d3dcd
--- /dev/null
@@ -0,0 +1,304 @@
+/* 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>
+
+/* special hack for icmp-type 'any': 
+ * Up to kernel <=2.4.20 the problem was:
+ * '-p icmp ' matches all icmp packets
+ * '-p icmp -m icmp' matches _only_ ICMP type 0 :(
+ * This is now fixed by initializing the field * to icmp type 0xFF
+ * See: https://bugzilla.netfilter.org/cgi-bin/bugzilla/show_bug.cgi?id=37
+ */
+
+struct icmp_names {
+       const char *name;
+       u_int8_t type;
+       u_int8_t code_min, code_max;
+};
+
+static const struct icmp_names icmp_codes[] = {
+       { "any", 0xFF, 0, 0xFF },
+       { "echo-reply", 0, 0, 0xFF },
+       /* Alias */ { "pong", 0, 0, 0xFF },
+
+       { "destination-unreachable", 3, 0, 0xFF },
+       {   "network-unreachable", 3, 0, 0 },
+       {   "host-unreachable", 3, 1, 1 },
+       {   "protocol-unreachable", 3, 2, 2 },
+       {   "port-unreachable", 3, 3, 3 },
+       {   "fragmentation-needed", 3, 4, 4 },
+       {   "source-route-failed", 3, 5, 5 },
+       {   "network-unknown", 3, 6, 6 },
+       {   "host-unknown", 3, 7, 7 },
+       {   "network-prohibited", 3, 9, 9 },
+       {   "host-prohibited", 3, 10, 10 },
+       {   "TOS-network-unreachable", 3, 11, 11 },
+       {   "TOS-host-unreachable", 3, 12, 12 },
+       {   "communication-prohibited", 3, 13, 13 },
+       {   "host-precedence-violation", 3, 14, 14 },
+       {   "precedence-cutoff", 3, 15, 15 },
+
+       { "source-quench", 4, 0, 0xFF },
+
+       { "redirect", 5, 0, 0xFF },
+       {   "network-redirect", 5, 0, 0 },
+       {   "host-redirect", 5, 1, 1 },
+       {   "TOS-network-redirect", 5, 2, 2 },
+       {   "TOS-host-redirect", 5, 3, 3 },
+
+       { "echo-request", 8, 0, 0xFF },
+       /* Alias */ { "ping", 8, 0, 0xFF },
+
+       { "router-advertisement", 9, 0, 0xFF },
+
+       { "router-solicitation", 10, 0, 0xFF },
+
+       { "time-exceeded", 11, 0, 0xFF },
+       /* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+       {   "ttl-zero-during-transit", 11, 0, 0 },
+       {   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+       { "parameter-problem", 12, 0, 0xFF },
+       {   "ip-header-bad", 12, 0, 0 },
+       {   "required-option-missing", 12, 1, 1 },
+
+       { "timestamp-request", 13, 0, 0xFF },
+
+       { "timestamp-reply", 14, 0, 0xFF },
+
+       { "address-mask-request", 17, 0, 0xFF },
+
+       { "address-mask-reply", 18, 0, 0xFF }
+};
+
+static void
+print_icmptypes()
+{
+       unsigned int i;
+       printf("Valid ICMP Types:");
+
+       for (i = 0; i < sizeof(icmp_codes)/sizeof(struct icmp_names); i++) {
+               if (i && icmp_codes[i].type == icmp_codes[i-1].type) {
+                       if (icmp_codes[i].code_min == icmp_codes[i-1].code_min
+                           && (icmp_codes[i].code_max
+                               == icmp_codes[i-1].code_max))
+                               printf(" (%s)", icmp_codes[i].name);
+                       else
+                               printf("\n   %s", icmp_codes[i].name);
+               }
+               else
+                       printf("\n%s", icmp_codes[i].name);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ICMP v%s options:\n"
+" --icmp-type [!] typename     match icmp type\n"
+"                              (or numeric type or type/code)\n"
+"\n", IPTABLES_VERSION);
+       print_icmptypes();
+}
+
+static struct option opts[] = {
+       { "icmp-type", 1, 0, '1' },
+       {0}
+};
+
+static unsigned int
+parse_icmp(const char *icmptype, u_int8_t *type, u_int8_t code[])
+{
+       unsigned int limit = sizeof(icmp_codes)/sizeof(struct icmp_names);
+       unsigned int match = limit;
+       unsigned int i;
+
+       for (i = 0; i < limit; i++) {
+               if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))
+                   == 0) {
+                       if (match != limit)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Ambiguous ICMP type `%s':"
+                                          " `%s' or `%s'?",
+                                          icmptype,
+                                          icmp_codes[match].name,
+                                          icmp_codes[i].name);
+                       match = i;
+               }
+       }
+
+       if (match != limit) {
+               *type = icmp_codes[match].type;
+               code[0] = icmp_codes[match].code_min;
+               code[1] = icmp_codes[match].code_max;
+       } else {
+               char *slash;
+               char buffer[strlen(icmptype) + 1];
+               unsigned int number;
+
+               strcpy(buffer, icmptype);
+               slash = strchr(buffer, '/');
+
+               if (slash)
+                       *slash = '\0';
+
+               if (string_to_number(buffer, 0, 255, &number) == -1)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid ICMP type `%s'\n", buffer);
+               *type = number;
+               if (slash) {
+                       if (string_to_number(slash+1, 0, 255, &number) == -1)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Invalid ICMP code `%s'\n",
+                                          slash+1);
+                       code[0] = code[1] = number;
+               } else {
+                       code[0] = 0;
+                       code[1] = 0xFF;
+               }
+       }
+
+       if (code[0] == 0 && code[1] == 0xFF)
+               return NFC_IP_SRC_PT;
+       else return NFC_IP_SRC_PT | NFC_IP_DST_PT;
+}
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_icmp *icmpinfo = (struct ipt_icmp *)m->data;
+
+       icmpinfo->type = 0xFF;
+       icmpinfo->code[1] = 0xFF;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_icmp *icmpinfo = (struct ipt_icmp *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               *nfcache |= parse_icmp(argv[optind-1],
+                                      &icmpinfo->type,
+                                      icmpinfo->code);
+               if (invert)
+                       icmpinfo->invflags |= IPT_ICMP_INV;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void print_icmptype(u_int8_t type,
+                          u_int8_t code_min, u_int8_t code_max,
+                          int invert,
+                          int numeric)
+{
+       if (!numeric) {
+               unsigned int i;
+
+               for (i = 0;
+                    i < sizeof(icmp_codes)/sizeof(struct icmp_names);
+                    i++) {
+                       if (icmp_codes[i].type == type
+                           && icmp_codes[i].code_min == code_min
+                           && icmp_codes[i].code_max == code_max)
+                               break;
+               }
+
+               if (i != sizeof(icmp_codes)/sizeof(struct icmp_names)) {
+                       printf("%s%s ",
+                              invert ? "!" : "",
+                              icmp_codes[i].name);
+                       return;
+               }
+       }
+
+       if (invert)
+               printf("!");
+
+       printf("type %u", type);
+       if (code_min == 0 && code_max == 0xFF)
+               printf(" ");
+       else if (code_min == code_max)
+               printf(" code %u ", code_min);
+       else
+               printf(" codes %u-%u ", code_min, code_max);
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       printf("icmp ");
+       print_icmptype(icmp->type, icmp->code[0], icmp->code[1],
+                      icmp->invflags & IPT_ICMP_INV,
+                      numeric);
+
+       if (icmp->invflags & ~IPT_ICMP_INV)
+               printf("Unknown invflags: 0x%X ",
+                      icmp->invflags & ~IPT_ICMP_INV);
+}
+
+/* Saves the match in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_icmp *icmp = (struct ipt_icmp *)match->data;
+
+       if (icmp->invflags & IPT_ICMP_INV)
+               printf("! ");
+
+       printf("--icmp-type %u", icmp->type);
+       if (icmp->code[0] != 0 || icmp->code[1] != 0xFF)
+               printf("/%u", icmp->code[0]);
+       printf(" ");
+}
+
+/* Final check; we don't care. */
+static void final_check(unsigned int flags)
+{
+}
+
+static
+struct iptables_match icmp
+= { NULL,
+    "icmp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_icmp)),
+    IPT_ALIGN(sizeof(struct ipt_icmp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&icmp);
+}
diff --git a/extensions/libipt_iprange.c b/extensions/libipt_iprange.c
new file mode 100644 (file)
index 0000000..8445884
--- /dev/null
@@ -0,0 +1,195 @@
+/* Shared library add-on to iptables to add IP range 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_iprange.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"iprange match v%s options:\n"
+"[!] --src-range ip-ip        Match source IP in the specified range\n"
+"[!] --dst-range ip-ip        Match destination IP in the specified range\n"
+"\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "src-range", 1, 0, '1' },
+       { "dst-range", 1, 0, '2' },
+       {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_iprange(char *arg, struct ipt_iprange *range)
+{
+       char *dash;
+       struct in_addr *ip;
+
+       dash = strchr(arg, '-');
+       if (dash)
+               *dash = '\0';
+               
+       ip = dotted_to_addr(arg);
+       if (!ip)
+               exit_error(PARAMETER_PROBLEM, "iprange match: 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, "iprange match: Bad IP address `%s'\n",
+                                  dash+1);
+               range->max_ip = ip->s_addr;
+       } else
+               range->max_ip = range->min_ip;
+}
+
+/* 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_iprange_info *info = (struct ipt_iprange_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPRANGE_SRC)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "iprange match: Only use --src-range ONCE!");
+               *flags |= IPRANGE_SRC;
+
+               info->flags |= IPRANGE_SRC;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert) {
+                       info->flags |= IPRANGE_SRC_INV;
+                       printf("hoho\n");
+               }
+               parse_iprange(optarg, &info->src);              
+
+               break;
+
+       case '2':
+               if (*flags & IPRANGE_DST)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "iprange match: Only use --dst-range ONCE!");
+               *flags |= IPRANGE_DST;
+
+               info->flags |= IPRANGE_DST;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->flags |= IPRANGE_DST_INV;
+
+               parse_iprange(optarg, &info->dst);              
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --src-range or --dst-range. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "iprange match: You must specify `--src-range' or `--dst-range'");
+}
+
+static void
+print_iprange(const struct ipt_iprange *range)
+{
+       const unsigned char *byte_min, *byte_max;
+
+       byte_min = (const unsigned char *) &(range->min_ip);
+       byte_max = (const unsigned char *) &(range->max_ip);
+       printf("%d.%d.%d.%d-%d.%d.%d.%d ", 
+               byte_min[0], byte_min[1], byte_min[2], byte_min[3],
+               byte_max[0], byte_max[1], byte_max[2], byte_max[3]);
+}
+
+/* Prints out the info. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_iprange_info *info = (struct ipt_iprange_info *)match->data;
+
+       if (info->flags & IPRANGE_SRC) {
+               printf("source IP range ");
+               if (info->flags & IPRANGE_SRC_INV)
+                       printf("! ");
+               print_iprange(&info->src);
+       }
+       if (info->flags & IPRANGE_DST) {
+               printf("destination IP range ");
+               if (info->flags & IPRANGE_DST_INV)
+                       printf("! ");
+               print_iprange(&info->dst);
+       }
+}
+
+/* Saves the union ipt_info in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_iprange_info *info = (struct ipt_iprange_info *)match->data;
+
+       if (info->flags & IPRANGE_SRC) {
+               if (info->flags & IPRANGE_SRC_INV)
+                       printf("! ");
+               printf("--src-range ");
+               print_iprange(&info->src);
+               if (info->flags & IPRANGE_DST)
+                       fputc(' ', stdout);
+       }
+       if (info->flags & IPRANGE_DST) {
+               if (info->flags & IPRANGE_DST_INV)
+                       printf("! ");
+               printf("--dst-range ");
+               print_iprange(&info->dst);
+       }
+}
+
+static
+struct iptables_match iprange
+= { NULL,
+    "iprange",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+    IPT_ALIGN(sizeof(struct ipt_iprange_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&iprange);
+}
diff --git a/extensions/libipt_ipv4options.c b/extensions/libipt_ipv4options.c
new file mode 100644 (file)
index 0000000..2aec10b
--- /dev/null
@@ -0,0 +1,321 @@
+/* Shared library add-on to iptables to add ipv4 options 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_ipv4options.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"ipv4options v%s options:\n"
+"      --ssrr    (match strict source routing flag)\n"
+"      --lsrr    (match loose  source routing flag)\n"
+"      --no-srr  (match packets with no source routing)\n\n"
+"  [!] --rr      (match record route flag)\n\n"
+"  [!] --ts      (match timestamp flag)\n\n"
+"  [!] --ra      (match router-alert option)\n\n"
+"  [!] --any-opt (match any option or no option at all if used with '!')\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "ssrr", 0, 0, '1' },
+       { "lsrr", 0, 0, '2' },
+       { "no-srr", 0, 0, '3'},
+       { "rr", 0, 0, '4'},
+       { "ts", 0, 0, '5'},
+       { "ra", 0, 0, '6'},
+       { "any-opt", 0, 0, '7'},
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       /* caching not yet implemented */
+        *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_ipv4options_info *info = (struct ipt_ipv4options_info *)(*match)->data;
+
+       switch (c)
+       {
+               /* strict-source-routing */
+       case '1':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ipv4options: unexpected `!' with --ssrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --ssrr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ssrr with --lsrr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ssrr with --no-srr");
+
+               info->options |= IPT_IPV4OPTION_MATCH_SSRR;
+               *flags |= IPT_IPV4OPTION_MATCH_SSRR;
+               break;
+
+               /* loose-source-routing */
+       case '2':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                  "ipv4options: unexpected `!' with --lsrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --lsrr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --lsrr with --ssrr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --lsrr with --no-srr");
+               info->options |= IPT_IPV4OPTION_MATCH_LSRR;
+               *flags |= IPT_IPV4OPTION_MATCH_LSRR;
+               break;
+
+               /* no-source-routing */
+       case '3':
+               if (invert) 
+                       exit_error(PARAMETER_PROBLEM,
+                                          "ipv4options: unexpected `!' with --no-srr");
+               if (*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --no-srr twice");
+               if (*flags & IPT_IPV4OPTION_MATCH_SSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --no-srr with --ssrr");
+               if (*flags & IPT_IPV4OPTION_MATCH_LSRR)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --no-srr with --lsrr");
+               info->options |= IPT_IPV4OPTION_DONT_MATCH_SRR;
+               *flags |= IPT_IPV4OPTION_DONT_MATCH_SRR;
+               break;
+
+               /* record-route */
+       case '4':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --rr twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --rr with ! --rr");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_RR))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr with --rr");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_RR;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_RR;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_RR;
+                       *flags |= IPT_IPV4OPTION_MATCH_RR;
+               }
+               break;
+
+               /* timestamp */
+       case '5':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ts twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ts twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ts with ! --ts");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ts with --ts");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_TIMESTAMP;
+                       *flags |= IPT_IPV4OPTION_MATCH_TIMESTAMP;
+               }
+               break;
+
+               /* router-alert  */
+       case '6':
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ra twice"); 
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --rr twice");
+               if ((!invert) && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --ra with ! --ra");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --ra with --ra");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT;
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_ROUTER_ALERT;
+                       *flags |= IPT_IPV4OPTION_MATCH_ROUTER_ALERT;
+               }
+               break;
+
+               /* any option */
+       case '7' :
+               if ((!invert) && (*flags & IPT_IPV4OPTION_MATCH_ANY_OPT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --any-opt twice");
+               if (invert && (*flags & IPT_IPV4OPTION_MATCH_ANY_OPT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt with --any-opt");
+               if (invert && (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt twice");
+               if ((!invert) &&
+                   ((*flags & IPT_IPV4OPTION_DONT_MATCH_SRR)       ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_RR)        ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP) ||
+                    (*flags & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --any-opt with any other negative ipv4options match");
+               if (invert &&
+                   ((*flags & IPT_IPV4OPTION_MATCH_LSRR)      ||
+                    (*flags & IPT_IPV4OPTION_MATCH_SSRR)      ||
+                    (*flags & IPT_IPV4OPTION_MATCH_RR)        ||
+                    (*flags & IPT_IPV4OPTION_MATCH_TIMESTAMP) ||
+                    (*flags & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --any-opt with any other positive ipv4options match");
+               if (invert) {
+                       info->options |= IPT_IPV4OPTION_DONT_MATCH_ANY_OPT;
+                       *flags |= IPT_IPV4OPTION_DONT_MATCH_ANY_OPT;    
+               }
+               else {
+                       info->options |= IPT_IPV4OPTION_MATCH_ANY_OPT;
+                       *flags |= IPT_IPV4OPTION_MATCH_ANY_OPT;
+               }
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM,
+                          "ipv4options match: you must specify some parameters. See iptables -m ipv4options --help for help.'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_ipv4options_info *info = ((struct ipt_ipv4options_info *)match->data);
+
+       printf(" IPV4OPTS");
+       if (info->options & IPT_IPV4OPTION_MATCH_SSRR)
+               printf(" SSRR");
+       else if (info->options & IPT_IPV4OPTION_MATCH_LSRR)
+               printf(" LSRR");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_SRR)
+               printf(" !SRR");
+       if (info->options & IPT_IPV4OPTION_MATCH_RR)
+               printf(" RR");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_RR)
+               printf(" !RR");
+       if (info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP)
+               printf(" TS");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)
+               printf(" !TS");
+       if (info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)
+               printf(" RA");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)
+               printf(" !RA");
+       if (info->options & IPT_IPV4OPTION_MATCH_ANY_OPT)
+               printf(" ANYOPT ");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)
+               printf(" NOOPT");
+
+       printf(" ");
+}
+
+/* Saves the data in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_ipv4options_info *info = ((struct ipt_ipv4options_info *)match->data);
+
+       if (info->options & IPT_IPV4OPTION_MATCH_SSRR)
+               printf(" --ssrr");
+       else if (info->options & IPT_IPV4OPTION_MATCH_LSRR)
+               printf(" --lsrr");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_SRR)
+               printf(" --no-srr");
+       if (info->options & IPT_IPV4OPTION_MATCH_RR)
+               printf(" --rr");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_RR)
+               printf(" ! --rr");
+       if (info->options & IPT_IPV4OPTION_MATCH_TIMESTAMP)
+               printf(" --ts");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_TIMESTAMP)
+               printf(" ! --ts");
+       if (info->options & IPT_IPV4OPTION_MATCH_ROUTER_ALERT)
+               printf(" --ra");
+       else if (info->options & IPT_IPV4OPTION_DONT_MATCH_ROUTER_ALERT)
+               printf(" ! --ra");
+       if (info->options & IPT_IPV4OPTION_MATCH_ANY_OPT)
+               printf(" --any-opt");
+       if (info->options & IPT_IPV4OPTION_DONT_MATCH_ANY_OPT)
+               printf(" ! --any-opt");
+
+       printf(" ");
+}
+
+static
+struct iptables_match ipv4options_struct
+= { NULL,
+    "ipv4options",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+    IPT_ALIGN(sizeof(struct ipt_ipv4options_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&ipv4options_struct);
+}
diff --git a/extensions/libipt_length.c b/extensions/libipt_length.c
new file mode 100644 (file)
index 0000000..8ac98b6
--- /dev/null
@@ -0,0 +1,160 @@
+/* Shared library add-on to iptables to add packet length 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_length.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"length v%s options:\n"
+"[!] --length length[:length]    Match packet length against value or range\n"
+"                                of values (inclusive)\n",
+IPTABLES_VERSION);
+
+}
+  
+static struct option opts[] = {
+       { "length", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static u_int16_t
+parse_length(const char *s)
+{
+       unsigned int len;
+       
+       if (string_to_number(s, 0, 0xFFFF, &len) == -1)
+               exit_error(PARAMETER_PROBLEM, "length invalid: `%s'\n", s);
+       else
+               return (u_int16_t )len;
+}
+
+/* If a single value is provided, min and max are both set to the value */
+static void
+parse_lengths(const char *s, struct ipt_length_info *info)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(s);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               info->min = info->max = parse_length(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               info->min = buffer[0] ? parse_length(buffer) : 0;
+               info->max = cp[0] ? parse_length(cp) : 0xFFFF;
+       }
+       free(buffer);
+       
+       if (info->min > info->max)
+               exit_error(PARAMETER_PROBLEM,
+                          "length min. range value `%u' greater than max. "
+                          "range value `%u'", info->min, info->max);
+       
+}
+
+/* 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_length_info *info = (struct ipt_length_info *)(*match)->data;
+
+       switch (c) {
+               case '1':
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "length: `--length' may only be "
+                                          "specified once");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_lengths(argv[optind-1], info);
+                       if (invert)
+                               info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               default:
+                       return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --length. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "length: You must specify `--length'");
+}
+
+/* Common match printing code. */
+static void
+print_length(struct ipt_length_info *info)
+{
+       if (info->invert)
+               printf("! ");
+       
+       if (info->max == info->min)
+               printf("%u ", info->min);
+       else
+               printf("%u:%u ", info->min, info->max);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("length ");
+       print_length((struct ipt_length_info *)match->data);
+}
+
+/* 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("--length ");
+       print_length((struct ipt_length_info *)match->data);
+}
+
+static
+struct iptables_match length
+= { NULL,
+    "length",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_length_info)),
+    IPT_ALIGN(sizeof(struct ipt_length_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&length);
+}
diff --git a/extensions/libipt_limit.c b/extensions/libipt_limit.c
new file mode 100644 (file)
index 0000000..af381fa
--- /dev/null
@@ -0,0 +1,199 @@
+/* Shared library add-on to iptables to add limit support.
+ *
+ * Jérôme de Vivie   <devivie@info.enserb.u-bordeaux.fr>
+ * Hervé Eychenne    <rv@wallfire.org>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <stddef.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", IPTABLES_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;
+       unsigned int num;
+
+       switch(c) {
+       case '%':
+               if (check_inverse(optarg, &invert, NULL, 0))
+                       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, NULL, 0))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unexpected `!' after --limit-burst");
+
+               if (string_to_number(optarg, 0, 10000, &num) == -1)
+                       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 < rates[i].mult%period)
+                       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);
+}
+
+static
+struct iptables_match limit
+= { NULL,
+    "limit",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_rateinfo)),
+    offsetof(struct ipt_rateinfo, prev),
+    &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 (file)
index 0000000..d1079a5
--- /dev/null
@@ -0,0 +1,150 @@
+/* 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", IPTABLES_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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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])
+{
+       unsigned int i;
+
+       printf("%02X", 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 ");
+
+       if (((struct ipt_mac_info *)match->data)->invert)
+               printf("! ");
+       
+       print_mac(((struct ipt_mac_info *)match->data)->srcaddr);
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       if (((struct ipt_mac_info *)match->data)->invert)
+               printf("! ");
+
+       printf("--mac-source ");
+       print_mac(((struct ipt_mac_info *)match->data)->srcaddr);
+}
+
+static
+struct iptables_match mac
+= { NULL,
+    "mac",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_mac_info)),
+    IPT_ALIGN(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 (file)
index 0000000..4aa780b
--- /dev/null
@@ -0,0 +1,133 @@
+/* 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",
+IPTABLES_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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 numeric)
+{
+       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)
+{
+       struct ipt_mark_info *info = (struct ipt_mark_info *)match->data;
+
+       printf("MARK match ");
+
+       if (info->invert)
+               printf("!");
+       
+       print_mark(info->mark, info->mask, 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)
+{
+       struct ipt_mark_info *info = (struct ipt_mark_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+       
+       printf("--mark ");
+       print_mark(info->mark, info->mask, 0);
+}
+
+static
+struct iptables_match mark
+= { NULL,
+    "mark",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_mark_info)),
+    IPT_ALIGN(sizeof(struct ipt_mark_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&mark);
+}
diff --git a/extensions/libipt_mport.c b/extensions/libipt_mport.c
new file mode 100644 (file)
index 0000000..2ae61ff
--- /dev/null
@@ -0,0 +1,307 @@
+/* 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_mport.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"mport v%s options:\n"
+" --source-ports port[,port:port,port...]\n"
+" --sports ...\n"
+"                              match source port(s)\n"
+" --destination-ports port[,port:port,port...]\n"
+" --dports ...\n"
+"                              match destination port(s)\n"
+" --ports port[,port:port,port]\n"
+"                              match both source and destination port(s)\n",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -1 ||
+           (portnum = service_to_port(port, proto)) != -1)
+               return (u_int16_t)portnum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid port/service `%s' specified", port);
+}
+
+static void
+parse_multi_ports(const char *portstring, struct ipt_mport *minfo,
+                  const char *proto)
+{
+       char *buffer, *cp, *next, *range;
+       unsigned int i;
+        u_int16_t m;
+
+       buffer = strdup(portstring);
+       if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
+
+        minfo->pflags = 0;
+
+       for (cp=buffer, i=0, m=1; cp && i<IPT_MULTI_PORTS; cp=next,i++,m<<=1)
+       {
+               next=strchr(cp, ',');
+               if (next) *next++='\0';
+                range = strchr(cp, ':');
+                if (range) {
+                        if (i == IPT_MULTI_PORTS-1)
+                                exit_error(PARAMETER_PROBLEM,
+                                           "too many ports specified");
+                        *range++ = '\0';
+                }
+               minfo->ports[i] = parse_port(cp, proto);
+                if (range) {
+                        minfo->pflags |= m;
+                        minfo->ports[++i] = parse_port(range, proto);
+                        if (minfo->ports[i-1] >= minfo->ports[i])
+                                exit_error(PARAMETER_PROBLEM,
+                                           "invalid portrange specified");
+                        m <<= 1;
+                }
+       }
+       if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
+        if (i == IPT_MULTI_PORTS-1)
+                minfo->ports[i] = minfo->ports[i-1];
+        else if (i < IPT_MULTI_PORTS-1) {
+                minfo->ports[i] = ~0;
+                minfo->pflags |= 1<<i;
+        }
+       free(buffer);
+}
+
+/* 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_mport *minfo
+               = (struct ipt_mport *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               proto = check_proto(entry);
+               parse_multi_ports(argv[optind-1], minfo, proto);
+               minfo->flags = IPT_MPORT_SOURCE;
+               *nfcache |= NFC_IP_SRC_PT;
+               break;
+
+       case '2':
+               proto = check_proto(entry);
+               parse_multi_ports(argv[optind-1], minfo, proto);
+               minfo->flags = IPT_MPORT_DESTINATION;
+               *nfcache |= NFC_IP_DST_PT;
+               break;
+
+       case '3':
+               proto = check_proto(entry);
+               parse_multi_ports(argv[optind-1], minfo, proto);
+               minfo->flags = IPT_MPORT_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, "mport expects 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_mport *minfo
+               = (const struct ipt_mport *)match->data;
+       unsigned int i;
+        u_int16_t pflags = minfo->pflags;
+
+       printf("mport ");
+
+       switch (minfo->flags) {
+       case IPT_MPORT_SOURCE:
+               printf("sports ");
+               break;
+
+       case IPT_MPORT_DESTINATION:
+               printf("dports ");
+               break;
+
+       case IPT_MPORT_EITHER:
+               printf("ports ");
+               break;
+
+       default:
+               printf("ERROR ");
+               break;
+       }
+
+       for (i=0; i < IPT_MULTI_PORTS; i++) {
+                if (pflags & (1<<i)
+                    && minfo->ports[i] == 65535)
+                        break;
+                if (i == IPT_MULTI_PORTS-1
+                    && minfo->ports[i-1] == minfo->ports[i])
+                        break;
+               printf("%s", i ? "," : "");
+               print_port(minfo->ports[i], ip->proto, numeric);
+                if (pflags & (1<<i)) {
+                        printf(":");
+                        print_port(minfo->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_mport *minfo
+               = (const struct ipt_mport *)match->data;
+       unsigned int i;
+        u_int16_t pflags = minfo->pflags;
+
+       switch (minfo->flags) {
+       case IPT_MPORT_SOURCE:
+               printf("--sports ");
+               break;
+
+       case IPT_MPORT_DESTINATION:
+               printf("--dports ");
+               break;
+
+       case IPT_MPORT_EITHER:
+               printf("--ports ");
+               break;
+       }
+
+       for (i=0; i < IPT_MULTI_PORTS; i++) {
+                if (pflags & (1<<i)
+                    && minfo->ports[i] == 65535)
+                        break;
+                if (i == IPT_MULTI_PORTS-1
+                    && minfo->ports[i-1] == minfo->ports[i])
+                        break;
+               printf("%s", i ? "," : "");
+               print_port(minfo->ports[i], ip->proto, 1);
+                if (pflags & (1<<i)) {
+                        printf(":");
+                        print_port(minfo->ports[++i], ip->proto, 1);
+                }
+       }
+       printf(" ");
+}
+
+struct iptables_match mport
+= { NULL,
+    "mport",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_mport)),
+    IPT_ALIGN(sizeof(struct ipt_mport)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match(&mport);
+}
diff --git a/extensions/libipt_multiport.c b/extensions/libipt_multiport.c
new file mode 100644 (file)
index 0000000..c9a98b3
--- /dev/null
@@ -0,0 +1,265 @@
+/* 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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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, 1);
+       }
+       printf(" ");
+}
+
+static
+struct iptables_match multiport
+= { NULL,
+    "multiport",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_multiport)),
+    IPT_ALIGN(sizeof(struct ipt_multiport)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match(&multiport);
+}
diff --git a/extensions/libipt_nth.c b/extensions/libipt_nth.c
new file mode 100644 (file)
index 0000000..ab8f97d
--- /dev/null
@@ -0,0 +1,238 @@
+/* 
+   Shared library add-on to iptables to add match support for every Nth packet
+   
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+   2001-07-17 Fabrice MARIE <fabrice@netfilter.org> : initial development.
+   2001-09-20 Richard Wagner (rwagner@cloudnet.com)
+        * added support for multiple counters
+        * added support for matching on individual packets
+          in the counter cycle
+*/
+
+#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_nth.h>
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"nth v%s options:\n"
+"   --every     Nth              Match every Nth packet\n"
+"  [--counter]  num              Use counter 0-%u (default:0)\n"
+"  [--start]    num              Initialize the counter at the number 'num'\n"
+"                                instead of 0. Must be between 0 and Nth-1\n"
+"  [--packet]   num              Match on 'num' packet. Must be between 0\n"
+"                                and Nth-1.\n\n"
+"                                If --packet is used for a counter than\n"
+"                                there must be Nth number of --packet\n"
+"                                rules, covering all values between 0 and\n"
+"                                Nth-1 inclusively.\n",
+IPTABLES_VERSION, IPT_NTH_NUM_COUNTERS-1);
+}
+
+static struct option opts[] = {
+       { "every", 1, 0, '1' },
+       { "start", 1, 0, '2' },
+        { "counter", 1, 0, '3' },
+        { "packet", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+#define IPT_NTH_OPT_EVERY      0x01
+#define IPT_NTH_OPT_NOT_EVERY  0x02
+#define IPT_NTH_OPT_START      0x04
+#define IPT_NTH_OPT_COUNTER     0x08
+#define IPT_NTH_OPT_PACKET      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,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_nth_info *nthinfo = (struct ipt_nth_info *)(*match)->data;
+       unsigned int num;
+
+       switch (c) {
+       case '1':
+               /* check for common mistakes... */
+               if ((!invert) && (*flags & IPT_NTH_OPT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --every twice");
+               if (invert && (*flags & IPT_NTH_OPT_NOT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --every twice");
+               if ((!invert) && (*flags & IPT_NTH_OPT_NOT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --every with ! --every");
+               if (invert && (*flags & IPT_NTH_OPT_EVERY))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --every with --every");
+
+               /* Remember, this function will interpret a leading 0 to be 
+                  Octal, a leading 0x to be hexdecimal... */
+                if (string_to_number(optarg, 2, 100, &num) == -1 || num < 2)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --every `%s', must be between 2 and 100", optarg);
+
+               /* assign the values */
+               nthinfo->every = num-1;
+               nthinfo->startat = 0;
+                nthinfo->packet = 0xFF;
+                if(!(*flags & IPT_NTH_OPT_EVERY))
+                {
+                        nthinfo->counter = 0;
+                }
+               if (invert)
+               {
+                       *flags |= IPT_NTH_OPT_NOT_EVERY;
+                       nthinfo->not = 1;
+               }
+               else
+               {
+                       *flags |= IPT_NTH_OPT_EVERY;
+                       nthinfo->not = 0;
+               }
+               break;
+       case '2':
+               /* check for common mistakes... */
+               if (!((*flags & IPT_NTH_OPT_EVERY) ||
+                     (*flags & IPT_NTH_OPT_NOT_EVERY)))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --start before --every");
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify with ! --start");
+               if (*flags & IPT_NTH_OPT_START)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --start twice");
+               if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --start `%s', must between 0 and %u", optarg, nthinfo->every);
+               *flags |= IPT_NTH_OPT_START;
+               nthinfo->startat = num;
+               break;
+        case '3':
+                /* check for common mistakes... */
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify with ! --counter");
+                if (*flags & IPT_NTH_OPT_COUNTER)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --counter twice");
+                if (string_to_number(optarg, 0, IPT_NTH_NUM_COUNTERS-1, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --counter `%s', must between 0 and %u", optarg, IPT_NTH_NUM_COUNTERS-1);
+                /* assign the values */
+                *flags |= IPT_NTH_OPT_COUNTER;
+                nthinfo->counter = num;
+                break;
+        case '4':
+                /* check for common mistakes... */
+                if (!((*flags & IPT_NTH_OPT_EVERY) ||
+                      (*flags & IPT_NTH_OPT_NOT_EVERY)))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet before --every");
+                if ((*flags & IPT_NTH_OPT_NOT_EVERY))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet with ! --every");
+                if (invert)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify with ! --packet");
+                if (*flags & IPT_NTH_OPT_PACKET)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --packet twice");
+                if (string_to_number(optarg, 0, nthinfo->every, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --packet `%s', must between 0 and %u", optarg, nthinfo->every);
+                *flags |= IPT_NTH_OPT_PACKET;
+                nthinfo->packet = num;
+                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_match *match,
+      int numeric)
+{
+       const struct ipt_nth_info *nthinfo
+               = (const struct ipt_nth_info *)match->data;
+
+       if (nthinfo->not == 1)
+               printf(" !");
+       printf("every %uth ", (nthinfo->every +1));
+       if (nthinfo->counter != 0) 
+               printf("counter #%u ", (nthinfo->counter));
+        if (nthinfo->packet != 0xFF)
+                printf("packet #%u ", nthinfo->packet);
+       if (nthinfo->startat != 0)
+               printf("start at %u ", nthinfo->startat);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_nth_info *nthinfo
+               = (const struct ipt_nth_info *)match->data;
+
+       if (nthinfo->not == 1)
+               printf("! ");
+       printf("--every %u ", (nthinfo->every +1));
+       printf("--counter %u ", (nthinfo->counter));
+       if (nthinfo->startat != 0)
+               printf("--start %u ", nthinfo->startat );
+        if (nthinfo->packet != 0xFF)
+                printf("--packet %u ", nthinfo->packet );
+}
+
+struct iptables_match nth
+= { NULL,
+    "nth",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_nth_info)),
+    IPT_ALIGN(sizeof(struct ipt_nth_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&nth);
+}
diff --git a/extensions/libipt_osf.c b/extensions/libipt_osf.c
new file mode 100644 (file)
index 0000000..205f071
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * libipt_osf.c
+ *
+ * Copyright (c) 2003 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ *
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * iptables interface for OS fingerprint matching module.
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_osf.h>
+
+static void help(void)
+{
+       printf("OS fingerprint match v%s options:\n"
+               "  --genre [!] string          Match a OS genre bypassive fingerprinting.\n",
+               IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "genre",     .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = 0 }
+};
+
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+
+static void parse_string(const unsigned char *s, struct ipt_osf_info *info)
+{
+       if (strlen(s) < MAXGENRELEN) 
+               strcpy(info->genre, s);
+       else 
+               exit_error(PARAMETER_PROBLEM, "Genre string too long `%s' [%d], max=%d", 
+                               s, strlen(s), MAXGENRELEN);
+}
+
+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_osf_info *info = (struct ipt_osf_info *)(*match)->data;
+       
+       switch(c) 
+       {
+               case '1':
+                       if (*flags)
+                               exit_error(PARAMETER_PROBLEM, "Can't specify multiple strings");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_string(argv[optind-1], info);
+                       if (invert)
+                               info->invert = 1;
+                       info->len=strlen((char *)info->genre);
+                       *flags = 1;
+                       break;
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "OS fingerprint match: You must specify `--genre'");
+}
+
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
+{
+       const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
+
+       printf("OS fingerprint match %s%s ", (info->invert) ? "!" : "", info->genre);
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_osf_info *info = (const struct ipt_osf_info*) match->data;
+
+       printf("--genre %s%s ", (info->invert) ? "! ": "", info->genre);
+}
+
+
+static struct iptables_match osf_match = {
+    .name          = "osf",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_osf_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_osf_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+
+void _init(void)
+{
+       register_match(&osf_match);
+}
diff --git a/extensions/libipt_owner.c b/extensions/libipt_owner.c
new file mode 100644 (file)
index 0000000..73f0ed5
--- /dev/null
@@ -0,0 +1,257 @@
+/* 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)
+{
+#ifdef IPT_OWNER_COMM
+       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"
+"[!] --cmd-owner name       Match local command name\n"
+"\n",
+IPTABLES_VERSION);
+#else
+       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",
+IPTABLES_VERSION);
+#endif /* IPT_OWNER_COMM */
+}
+
+static struct option opts[] = {
+       { "uid-owner", 1, 0, '1' },
+       { "gid-owner", 1, 0, '2' },
+       { "pid-owner", 1, 0, '3' },
+       { "sid-owner", 1, 0, '4' },
+#ifdef IPT_OWNER_COMM
+       { "cmd-owner", 1, 0, '5' },
+#endif
+       {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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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;
+
+#ifdef IPT_OWNER_COMM
+       case '5':
+               check_inverse(optarg, &invert, &optind, 0);
+               if(strlen(optarg) > sizeof(ownerinfo->comm))
+                       exit_error(PARAMETER_PROBLEM, "OWNER CMD `%s' too long, max %d characters", optarg, sizeof(ownerinfo->comm));
+
+               strncpy(ownerinfo->comm, optarg, sizeof(ownerinfo->comm));
+
+               if (invert)
+                       ownerinfo->invert |= IPT_OWNER_COMM;
+               ownerinfo->match |= IPT_OWNER_COMM;
+               *flags = 1;
+               break;
+#endif
+
+       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) {
+
+               if (info->invert & flag)
+                       printf("! ");
+
+               printf(label);
+
+               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;
+#ifdef IPT_OWNER_COMM
+               case IPT_OWNER_COMM:
+                       printf("%.*s ", (int)sizeof(info->comm), info->comm);
+                       break;
+#endif
+               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 ");
+#ifdef IPT_OWNER_COMM
+       print_item(info, IPT_OWNER_COMM, numeric, "OWNER CMD match ");
+#endif
+}
+
+/* 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 ");
+#ifdef IPT_OWNER_COMM
+       print_item(info, IPT_OWNER_COMM, 0, "--cmd-owner ");
+#endif
+}
+
+static
+struct iptables_match owner
+= { NULL,
+    "owner",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_owner_info)),
+    IPT_ALIGN(sizeof(struct ipt_owner_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&owner);
+}
diff --git a/extensions/libipt_physdev.c b/extensions/libipt_physdev.c
new file mode 100644 (file)
index 0000000..9149d90
--- /dev/null
@@ -0,0 +1,232 @@
+/* Shared library add-on to iptables to add bridge port matching support. */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_physdev.h>
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <net/ethernet.h>
+#else
+#include <linux/if_ether.h>
+#endif
+
+static void
+help(void)
+{
+       printf(
+"physdev v%s options:\n"
+" --physdev-in [!] input name[+]               bridge port name ([+] for wildcard)\n"
+" --physdev-out [!] output name[+]     bridge port name ([+] for wildcard)\n"
+" [!] --physdev-is-in                  arrived on a bridge device\n"
+" [!] --physdev-is-out                 will leave on a bridge device\n"
+" [!] --physdev-is-bridged             it's a bridged packet\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "physdev-in", 1, 0, '1' },
+       { "physdev-out", 1, 0, '2' },
+       { "physdev-is-in", 0, 0, '3' },
+       { "physdev-is-out", 0, 0, '4' },
+       { "physdev-is-bridged", 0, 0, '5' },
+       {0}
+};
+
+/* copied from iptables.c */
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i])
+                           && vianame[i] != '_'
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+}
+
+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_physdev_info *info =
+               (struct ipt_physdev_info*)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & IPT_PHYSDEV_OP_IN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physindev, info->in_mask);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_IN;
+               info->bitmask |= IPT_PHYSDEV_OP_IN;
+               *flags |= IPT_PHYSDEV_OP_IN;
+               break;
+
+       case '2':
+               if (*flags & IPT_PHYSDEV_OP_OUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_interface(argv[optind-1], info->physoutdev,
+                               info->out_mask);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_OUT;
+               info->bitmask |= IPT_PHYSDEV_OP_OUT;
+               *flags |= IPT_PHYSDEV_OP_OUT;
+               break;
+
+       case '3':
+               if (*flags & IPT_PHYSDEV_OP_ISIN)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IPT_PHYSDEV_OP_ISIN;
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_ISIN;
+               *flags |= IPT_PHYSDEV_OP_ISIN;
+               break;
+
+       case '4':
+               if (*flags & IPT_PHYSDEV_OP_ISOUT)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               info->bitmask |= IPT_PHYSDEV_OP_ISOUT;
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_ISOUT;
+               *flags |= IPT_PHYSDEV_OP_ISOUT;
+               break;
+
+       case '5':
+               if (*flags & IPT_PHYSDEV_OP_BRIDGED)
+                       goto multiple_use;
+               check_inverse(optarg, &invert, &optind, 0);
+               if (invert)
+                       info->invert |= IPT_PHYSDEV_OP_BRIDGED;
+               *flags |= IPT_PHYSDEV_OP_BRIDGED;
+               info->bitmask |= IPT_PHYSDEV_OP_BRIDGED;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+multiple_use:
+       exit_error(PARAMETER_PROBLEM,
+          "multiple use of the same physdev option is not allowed");
+
+}
+
+static void final_check(unsigned int flags)
+{
+       if (flags == 0)
+               exit_error(PARAMETER_PROBLEM, "PHYSDEV: no physdev option specified");
+}
+
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_physdev_info *info =
+               (struct ipt_physdev_info*)match->data;
+
+       printf("PHYSDEV match");
+       if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_physdev_info *info =
+               (struct ipt_physdev_info*)match->data;
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISIN)
+               printf("%s --physdev-is-in",
+                      info->invert & IPT_PHYSDEV_OP_ISIN ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_IN)
+               printf("%s --physdev-in %s",
+               (info->invert & IPT_PHYSDEV_OP_IN) ? " !":"", info->physindev);
+
+       if (info->bitmask & IPT_PHYSDEV_OP_ISOUT)
+               printf("%s --physdev-is-out",
+                      info->invert & IPT_PHYSDEV_OP_ISOUT ? " !":"");
+       if (info->bitmask & IPT_PHYSDEV_OP_OUT)
+               printf("%s --physdev-out %s",
+               (info->invert & IPT_PHYSDEV_OP_OUT) ? " !":"", info->physoutdev);
+       if (info->bitmask & IPT_PHYSDEV_OP_BRIDGED)
+               printf("%s --physdev-is-bridged",
+                      info->invert & IPT_PHYSDEV_OP_BRIDGED ? " !":"");
+       printf(" ");
+}
+
+static
+struct iptables_match physdev
+= { NULL,
+    "physdev",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+    IPT_ALIGN(sizeof(struct ipt_physdev_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&physdev);
+}
diff --git a/extensions/libipt_pkttype.c b/extensions/libipt_pkttype.c
new file mode 100644 (file)
index 0000000..9141e48
--- /dev/null
@@ -0,0 +1,174 @@
+/* 
+ * Shared library add-on to iptables to match 
+ * packets by their type (BROADCAST, UNICAST, MULTICAST). 
+ *
+ * Michal Ludvig <michal@logix.cz>
+ */
+#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/if_packet.h>
+#include <linux/netfilter_ipv4/ipt_pkttype.h>
+
+#define        PKTTYPE_VERSION "0.1"
+
+struct pkttypes {
+       const char *name;
+       unsigned char pkttype;
+       unsigned char printhelp;
+       const char *help;
+};
+
+static const struct pkttypes supported_types[] = {
+       {"unicast", PACKET_HOST, 1, "to us"},
+       {"broadcast", PACKET_BROADCAST, 1, "to all"},
+       {"multicast", PACKET_MULTICAST, 1, "to group"},
+/*
+       {"otherhost", PACKET_OTHERHOST, 1, "to someone else"},
+       {"outgoing", PACKET_OUTGOING, 1, "outgoing of any type"},
+*/
+       /* aliases */
+       {"bcast", PACKET_BROADCAST, 0, NULL},
+       {"mcast", PACKET_MULTICAST, 0, NULL},
+       {"host", PACKET_HOST, 0, NULL}
+};
+
+static void print_types()
+{
+       unsigned int    i;
+       
+       printf("Valid packet types:\n");
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(supported_types[i].printhelp == 1)
+                       printf("\t%-14s\t\t%s\n", supported_types[i].name, supported_types[i].help);
+       }
+       printf("\n");
+}
+
+/* Function which prints out usage message. */
+static void help(void)
+{
+       printf(
+"pkt_type v%s options:\n"
+"  --pkt-type [!] packettype\tmatch packet type\n"
+"\n", PKTTYPE_VERSION);
+       print_types();
+}
+
+static struct option opts[] = {
+       {"pkt-type", 1, 0, '1'},
+       {0}
+};
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+static void parse_pkttype(const char *pkttype, struct ipt_pkttype_info *info)
+{
+       unsigned int    i;
+       
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(strcasecmp(pkttype, supported_types[i].name)==0)
+               {
+                       info->pkttype=supported_types[i].pkttype;
+                       return;
+               }
+       }
+       
+       exit_error(PARAMETER_PROBLEM, "Bad packet type '%s'", pkttype);
+}
+
+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_pkttype_info *info = (struct ipt_pkttype_info *)(*match)->data;
+       
+       switch(c)
+       {
+               case '1':
+                       check_inverse(optarg, &invert, &optind, 0);
+                       parse_pkttype(argv[optind-1], info);
+                       if(invert)
+                               info->invert=1;
+                       *flags=1;
+                       break;
+
+               default: 
+                       return 0;
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM, "You must specify `--pkt-type'");
+}
+
+static void print_pkttype(struct ipt_pkttype_info *info)
+{
+       unsigned int    i;
+       
+       for (i = 0; i < sizeof(supported_types)/sizeof(struct pkttypes); i++)
+       {
+               if(supported_types[i].pkttype==info->pkttype)
+               {
+                       printf("%s ", supported_types[i].name);
+                       return;
+               }
+       }
+
+       printf("%d ", info->pkttype);   /* in case we didn't find an entry in named-packtes */
+}
+
+static void print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
+{
+       struct ipt_pkttype_info *info = (struct ipt_pkttype_info *)match->data;
+       
+       printf("PKTTYPE %s= ", info->invert?"!":"");
+       print_pkttype(info);
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_pkttype_info *info = (struct ipt_pkttype_info *)match->data;
+       
+       printf("--pkt-type %s", info->invert?"! ":"");
+       print_pkttype(info);
+}
+
+static
+struct iptables_match pkttype = {
+    NULL,
+    "pkttype",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+    IPT_ALIGN(sizeof(struct ipt_pkttype_info)),
+    &help,
+    &init,
+    &parse, 
+    &final_check, 
+    &print,
+    &save, 
+    opts
+};
+
+void _init(void)
+{
+       register_match(&pkttype);
+}
diff --git a/extensions/libipt_psd.c b/extensions/libipt_psd.c
new file mode 100644 (file)
index 0000000..8a6198e
--- /dev/null
@@ -0,0 +1,202 @@
+/* 
+  Shared library add-on to iptables to add PSD support 
+   
+  Copyright (C) 2000,2001 astaro AG
+
+  This file is distributed under the terms of the GNU General Public
+  License (GPL). Copies of the GPL can be obtained from:
+     ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+  2000-05-04 Markus Hennig <hennig@astaro.de> : initial
+  2000-08-18 Dennis Koslowski <koslowski@astaro.de> : first release
+  2000-12-01 Dennis Koslowski <koslowski@astaro.de> : UDP scans detection added
+  2001-02-04 Jan Rekorajski <baggins@pld.org.pl> : converted from target to match
+  2003-03-02 Harald Welte <laforge@netfilter.org>: fix 'storage' bug
+*/
+
+#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_psd.h>
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"psd v%s options:\n"
+" --psd-weight-threshold threshhold  Portscan detection weight threshold\n\n"
+" --psd-delay-threshold  delay       Portscan detection delay threshold\n\n"
+" --psd-lo-ports-weight  lo          Privileged ports weight\n\n"
+" --psd-hi-ports-weight  hi          High ports weight\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "psd-weight-threshold", 1, 0, '1' },
+       { "psd-delay-threshold", 1, 0, '2' },
+       { "psd-lo-ports-weight", 1, 0, '3' },
+       { "psd-hi-ports-weight", 1, 0, '4' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_psd_info *psdinfo = (struct ipt_psd_info *)m->data;
+
+       psdinfo->weight_threshold = SCAN_WEIGHT_THRESHOLD;  
+       psdinfo->delay_threshold = SCAN_DELAY_THRESHOLD;
+       psdinfo->lo_ports_weight = PORT_WEIGHT_PRIV;
+       psdinfo->hi_ports_weight = PORT_WEIGHT_HIGH;
+       /* Can't cache this */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+
+typedef struct _code {
+       char    *c_name;
+       int     c_val;
+} CODE;
+
+
+
+#define IPT_PSD_OPT_CTRESH 0x01
+#define IPT_PSD_OPT_DTRESH 0x02
+#define IPT_PSD_OPT_LPWEIGHT 0x04
+#define IPT_PSD_OPT_HPWEIGHT 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_psd_info *psdinfo = (struct ipt_psd_info *)(*match)->data;
+       unsigned int num;
+       
+       if (!optarg)
+               exit_error(PARAMETER_PROBLEM, "missing optarg");
+
+       /* string_to_number needs a leading space */
+
+       switch (c) {
+       /* PSD-weight-threshold */
+       case '1':
+               if (*flags & IPT_PSD_OPT_CTRESH)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --psd-weight-threshold "
+                                  "twice");
+                if (string_to_number(optarg, 0, 10000, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --psd-weight-threshold `%s'", optarg);
+               psdinfo->weight_threshold = num;
+               *flags |= IPT_PSD_OPT_CTRESH;
+               break;
+
+       /* PSD-delay-threshold */
+       case '2':
+               if (*flags & IPT_PSD_OPT_DTRESH)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --psd-delay-threshold twice");
+                if (string_to_number(optarg, 0, 10000, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --psd-delay-threshold `%s'", optarg);
+               psdinfo->delay_threshold = num;
+               *flags |= IPT_PSD_OPT_DTRESH;
+               break;
+
+       /* PSD-lo-ports-weight */
+       case '3':
+               if (*flags & IPT_PSD_OPT_LPWEIGHT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --psd-lo-ports-weight twice");
+                if (string_to_number(optarg, 0, 10000, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --psd-lo-ports-weight `%s'", optarg);
+               psdinfo->lo_ports_weight = num;
+               *flags |= IPT_PSD_OPT_LPWEIGHT;
+               break;
+
+       /* PSD-hi-ports-weight */
+       case '4':
+               if (*flags & IPT_PSD_OPT_HPWEIGHT)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --psd-hi-ports-weight twice");
+                if (string_to_number(optarg, 0, 10000, &num) == -1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --psd-hi-ports-weight `%s'", optarg);
+               psdinfo->hi_ports_weight = num;
+               *flags |= IPT_PSD_OPT_HPWEIGHT;
+               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_match *match,
+      int numeric)
+{
+       const struct ipt_psd_info *psdinfo
+               = (const struct ipt_psd_info *)match->data;
+
+       printf("psd ");
+       printf("weight-threshold: %u ", psdinfo->weight_threshold);
+       printf("delay-threshold: %u ", psdinfo->delay_threshold);
+       printf("lo-ports-weight: %u ", psdinfo->lo_ports_weight);
+       printf("hi-ports-weight: %u ", psdinfo->hi_ports_weight);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_psd_info *psdinfo
+               = (const struct ipt_psd_info *)match->data;
+
+       printf("--psd-weight-threshold %u ", psdinfo->weight_threshold);
+       printf("--psd-delay-threshold %u ", psdinfo->delay_threshold);
+       printf("--psd-lo-ports-weight %u ", psdinfo->lo_ports_weight);
+       printf("--psd-hi-ports-weight %u ", psdinfo->hi_ports_weight);
+}
+
+static
+struct iptables_match psd
+= { NULL,
+    "psd",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_psd_info)),
+    IPT_ALIGN(sizeof(struct ipt_psd_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&psd);
+}
diff --git a/extensions/libipt_quota.c b/extensions/libipt_quota.c
new file mode 100644 (file)
index 0000000..078f9e3
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Shared library add-on to iptables to add quota support
+ *
+ * Sam Johnston <samj@samj.net>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ipt_quota.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+static struct option opts[] = {
+        {"quota", 1, 0, '1'},
+        {0}
+};
+
+/* print usage */
+static void
+help(void)
+{
+        printf("quota options:\n"
+               " --quota quota                 quota (bytes)\n" "\n");
+}
+
+/* initialise match */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+        /* no can cache */
+        *nfcache |= NFC_UNKNOWN;
+}
+
+/* print matchinfo */
+static void
+print(const struct ipt_ip *ip, const struct ipt_entry_match *match, int numeric)
+{
+        struct ipt_quota_info *q = (struct ipt_quota_info *) match->data;
+        printf("quota: %llu bytes", (unsigned long long) q->quota);
+}
+
+/* save matchinfo */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+        struct ipt_quota_info *q = (struct ipt_quota_info *) match->data;
+        printf("--quota %llu ", (unsigned long long) q->quota);
+}
+
+/* parse quota option */
+static int
+parse_quota(const char *s, u_int64_t * quota)
+{
+        *quota = strtoull(s, (char **) NULL, 10);
+
+#ifdef DEBUG_IPT_QUOTA
+        printf("Quota: %llu\n", *quota);
+#endif
+
+        if (*quota == -1)
+                exit_error(PARAMETER_PROBLEM, "quota invalid: '%s'\n", s);
+        else
+                return 1;
+}
+
+/* parse all options, returning true if we found any for us */
+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_quota_info *info = (struct ipt_quota_info *) (*match)->data;
+
+        switch (c) {
+        case '1':
+                if (check_inverse(optarg, &invert, NULL, 0))
+                        exit_error(PARAMETER_PROBLEM, "quota: unexpected '!'");
+                if (!parse_quota(optarg, &info->quota))
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad quota: '%s'", optarg);
+                break;
+
+        default:
+                return 0;
+        }
+        return 1;
+}
+
+/* no final check */
+static void
+final_check(unsigned int flags)
+{
+}
+
+struct iptables_match quota = { NULL,
+        "quota",
+        IPTABLES_VERSION,
+        IPT_ALIGN(sizeof (struct ipt_quota_info)),
+        IPT_ALIGN(sizeof (struct ipt_quota_info)),
+        &help,
+        &init,
+        &parse,
+        &final_check,
+        &print,
+        &save,
+        opts
+};
+
+void
+_init(void)
+{
+        register_match(&quota);
+}
diff --git a/extensions/libipt_random.c b/extensions/libipt_random.c
new file mode 100644 (file)
index 0000000..97f09a4
--- /dev/null
@@ -0,0 +1,151 @@
+/* 
+   Shared library add-on to iptables to add match support for random match.
+   
+   This file is distributed under the terms of the GNU General Public
+   License (GPL). Copies of the GPL can be obtained from:
+   ftp://prep.ai.mit.edu/pub/gnu/GPL
+
+   2001-10-14 Fabrice MARIE <fabrice@netfilter.org> : initial development.
+*/
+
+#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_random.h>
+
+/**
+ * The kernel random routing returns numbers between 0 and 255.
+ * To ease the task of the user in choosing the probability
+ * of matching, we want him to be able to use percentages.
+ * Therefore we have to accept numbers in percentage here,
+ * turn them into number between 0 and 255 for the kernel module,
+ * and turn them back to percentages when we print/save
+ * the rule.
+ */
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"random v%s options:\n"
+"  [--average]     percent      The probability in percentage of the match\n"
+"                               If ommited, a probability of 50%% percent is set.\n"
+"                               Percentage must be within : 1 <= percent <= 99.\n\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "average", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the target. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_rand_info *randinfo = (struct ipt_rand_info *)(m)->data;
+       *nfcache |= NFC_UNKNOWN;
+
+       /* We assign the average to be 50 which is our default value */
+       /* 50 * 2.55 = 128 */
+       randinfo->average = 128;
+}
+
+#define IPT_RAND_OPT_AVERAGE   0x01
+
+/* 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_rand_info *randinfo = (struct ipt_rand_info *)(*match)->data;
+       unsigned int num;
+
+       switch (c) {
+       case '1':
+               /* check for common mistakes... */
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify ! --average");
+               if (*flags & IPT_RAND_OPT_AVERAGE)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify --average twice");
+
+               /* Remember, this function will interpret a leading 0 to be 
+                  Octal, a leading 0x to be hexdecimal... */
+                if (string_to_number(optarg, 1, 99, &num) == -1 || num < 1)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "bad --average `%s', must be between 1 and 99", optarg);
+
+               /* assign the values */
+               randinfo->average = (int)(num * 2.55);
+               *flags |= IPT_RAND_OPT_AVERAGE;
+               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_match *match,
+      int numeric)
+{
+       const struct ipt_rand_info *randinfo
+               = (const struct ipt_rand_info *)match->data;
+       div_t result = div((randinfo->average*100), 255);
+       if (result.rem > 127)  /* round up... */
+               ++result.quot;
+
+       printf(" random %u%% ", result.quot);
+}
+
+/* Saves the union ipt_targinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_rand_info *randinfo
+               = (const struct ipt_rand_info *)match->data;
+       div_t result = div((randinfo->average *100), 255);
+       if (result.rem > 127)  /* round up... */
+               ++result.quot;
+
+       printf("--average %u ", result.quot);
+}
+
+struct iptables_match rand_match
+= { NULL,
+    "random",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_rand_info)),
+    IPT_ALIGN(sizeof(struct ipt_rand_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&rand_match);
+}
diff --git a/extensions/libipt_realm.c b/extensions/libipt_realm.c
new file mode 100644 (file)
index 0000000..60a3897
--- /dev/null
@@ -0,0 +1,135 @@
+/* Shared library add-on to iptables to add realm matching 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_realm.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"REALM v%s options:\n"
+" --realm [!] value[/mask]\n"
+"                              Match realm\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "realm", 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_realm_info *realminfo = (struct ipt_realm_info *)(*match)->data;
+
+       switch (c) {
+               char *end;
+       case '1':
+               check_inverse(optarg, &invert, &optind, 0);
+               realminfo->id = strtoul(optarg, &end, 0);
+               if (*end == '/') {
+                       realminfo->mask = strtoul(end+1, &end, 0);
+               } else
+                       realminfo->mask = 0xffffffff;
+               if (*end != '\0' || end == optarg)
+                       exit_error(PARAMETER_PROBLEM, "Bad REALM value `%s'", optarg);
+               if (invert)
+                       realminfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_realm(unsigned long id, unsigned long mask, int invert, int numeric)
+{
+       if (invert)
+               fputc('!', stdout);
+
+       if(mask != 0xffffffff)
+               printf("0x%lx/0x%lx ", id, mask);
+       else
+               printf("0x%lx ", id);
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("REALM match ");
+       print_realm(((struct ipt_realm_info *)match->data)->id,
+                  ((struct ipt_realm_info *)match->data)->mask,
+                  ((struct ipt_realm_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("--realm ");
+       print_realm(((struct ipt_realm_info *)match->data)->id,
+                  ((struct ipt_realm_info *)match->data)->mask,
+                  ((struct ipt_realm_info *)match->data)->invert, 0);
+}
+
+/* Final check; must have specified --mark. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "REALM match: You must specify `--realm'");
+}
+
+struct iptables_match realm
+= { NULL,
+    "realm",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_realm_info)),
+    IPT_ALIGN(sizeof(struct ipt_realm_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&realm);
+}
+
+
diff --git a/extensions/libipt_recent.c b/extensions/libipt_recent.c
new file mode 100644 (file)
index 0000000..aa32aa0
--- /dev/null
@@ -0,0 +1,237 @@
+/* Shared library add-on to iptables to add recent 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_recent.h>
+
+/* Need these in order to not fail when compiling against an older kernel. */
+#ifndef RECENT_NAME
+#define RECENT_NAME    "ipt_recent"
+#endif /* RECENT_NAME */
+
+#ifndef RECENT_VER
+#define RECENT_VER     "unknown"
+#endif /* RECENT_VER */
+
+#ifndef IPT_RECENT_NAME_LEN
+#define IPT_RECENT_NAME_LEN    200
+#endif /* IPT_RECENT_NAME_LEN */
+
+/* Options for this module */
+static struct option opts[] = {
+       { .name = "set",      .has_arg = 0, .flag = 0, .val = 201 }, 
+       { .name = "rcheck",   .has_arg = 0, .flag = 0, .val = 202 }, 
+       { .name = "update",   .has_arg = 0, .flag = 0, .val = 203 },
+       { .name = "seconds",  .has_arg = 1, .flag = 0, .val = 204 }, 
+       { .name = "hitcount", .has_arg = 1, .flag = 0, .val = 205 },
+       { .name = "remove",   .has_arg = 0, .flag = 0, .val = 206 },
+       { .name = "rttl",     .has_arg = 0, .flag = 0, .val = 207 },
+       { .name = "name",     .has_arg = 1, .flag = 0, .val = 208 },
+       { .name = "rsource",  .has_arg = 0, .flag = 0, .val = 209 },
+       { .name = "rdest",    .has_arg = 0, .flag = 0, .val = 210 },
+       { .name = 0,          .has_arg = 0, .flag = 0, .val = 0   }
+};
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"recent v%s options:\n"
+"[!] --set                       Add source address to list, always matches.\n"
+"[!] --rcheck                    Match if source address in list.\n"
+"[!] --update                    Match if source address in list, also update last-seen time.\n"
+"[!] --remove                    Match if source address in list, also removes that address from list.\n"
+"    --seconds seconds           For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address last seen within\n"
+"                                the last 'seconds' seconds.\n"
+"    --hitcount hits             For check and update commands above.\n"
+"                                Specifies that the match will only occur if source address seen hits times.\n"
+"                                May be used in conjunction with the seconds option.\n"
+"    --rttl                      For check and update commands above.\n"
+"                                Specifies that the match will only occur if the source address and the TTL\n"
+"                                match between this packet and the one which was set.\n"
+"                                Useful if you have problems with people spoofing their source address in order\n"
+"                                to DoS you via this module.\n"
+"    --name name                 Name of the recent list to be used.  DEFAULT used if none given.\n"
+"    --rsource                   Match/Save the source address of each packet in the recent list table (default).\n"
+"    --rdest                     Match/Save the destination address of each packet in the recent list table.\n"
+RECENT_NAME " " RECENT_VER ": Stephen Frost <sfrost@snowman.net>.  http://snowman.net/projects/ipt_recent/\n"
+,
+IPTABLES_VERSION);
+
+}
+  
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)(match)->data;
+
+       *nfcache |= NFC_UNKNOWN;
+
+       strncpy(info->name,"DEFAULT",IPT_RECENT_NAME_LEN);
+       info->side = IPT_RECENT_SOURCE;
+}
+
+/* Function which parses command options; returns true if it
+   ate an option */
+static int
+parse(int c, char **argv, int invert, unsigned int *flags,
+      const struct ipt_entry *entry,
+      unsigned int *nfcache,
+      struct ipt_entry_match **match)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)(*match)->data;
+       switch (c) {
+               case 201:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_SET;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+                       
+               case 202:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_CHECK;
+                       if(invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 203:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_UPDATE;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 206:
+                       if (*flags) exit_error(PARAMETER_PROBLEM,
+                                       "recent: only one of `--set', `--rcheck' "
+                                       "`--update' or `--remove' may be set");
+                       check_inverse(optarg, &invert, &optind, 0);
+                       info->check_set |= IPT_RECENT_REMOVE;
+                       if (invert) info->invert = 1;
+                       *flags = 1;
+                       break;
+
+               case 204:
+                       info->seconds = atoi(optarg);
+                       break;
+
+               case 205:
+                       info->hit_count = atoi(optarg);
+                       break;
+
+               case 207:
+                       info->check_set |= IPT_RECENT_TTL;
+                       break;
+
+               case 208:
+                       strncpy(info->name,optarg,IPT_RECENT_NAME_LEN);
+                       break;
+
+               case 209:
+                       info->side = IPT_RECENT_SOURCE;
+                       break;
+
+               case 210:
+                       info->side = IPT_RECENT_DEST;
+                       break;
+
+               default:
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* Final check; must have specified a specific option. */
+static void
+final_check(unsigned int flags)
+{
+
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                       "recent: you must specify one of `--set', `--rcheck' "
+                       "`--update' or `--remove'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+
+       if (info->invert)
+               fputc('!', stdout);
+
+       printf("recent: ");
+       if(info->check_set & IPT_RECENT_SET) printf("SET ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("CHECK ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("UPDATE ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("REMOVE ");
+       if(info->seconds) printf("seconds: %d ",info->seconds);
+       if(info->hit_count) printf("hit_count: %d ",info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("TTL-Match ");
+       if(info->name) printf("name: %s ",info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("side: source ");
+       if(info->side == IPT_RECENT_DEST) printf("side: dest");
+}
+
+/* Saves the union ipt_matchinfo in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_recent_info *info = (struct ipt_recent_info *)match->data;
+
+       if (info->invert)
+               printf("! ");
+
+       if(info->check_set & IPT_RECENT_SET) printf("--set ");
+       if(info->check_set & IPT_RECENT_CHECK) printf("--rcheck ");
+       if(info->check_set & IPT_RECENT_UPDATE) printf("--update ");
+       if(info->check_set & IPT_RECENT_REMOVE) printf("--remove ");
+       if(info->seconds) printf("--seconds %d ",info->seconds);
+       if(info->hit_count) printf("--hitcount %d ",info->hit_count);
+       if(info->check_set & IPT_RECENT_TTL) printf("--rttl ");
+       if(info->name) printf("--name %s ",info->name);
+       if(info->side == IPT_RECENT_SOURCE) printf("--rsource ");
+       if(info->side == IPT_RECENT_DEST) printf("--rdest ");
+}
+
+/* Structure for iptables to use to communicate with module */
+static struct iptables_match recent = { 
+    .next          = NULL,
+    .name          = "recent",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_recent_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&recent);
+}
diff --git a/extensions/libipt_record_rpc.c b/extensions/libipt_record_rpc.c
new file mode 100644 (file)
index 0000000..819c8ef
--- /dev/null
@@ -0,0 +1,74 @@
+/* Shared library add-on to iptables for rpc match */
+#include <stdio.h>
+#include <getopt.h>
+#include <iptables.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"record_rpc v%s takes no options\n"
+"\n", IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       {0}
+};
+
+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)
+{
+}
+
+/* Prints out the union ipt_matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+}
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+}
+
+static
+struct iptables_match record_rpc
+= { NULL,
+    "record_rpc",
+    IPTABLES_VERSION,
+    IPT_ALIGN(0),
+    IPT_ALIGN(0),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&record_rpc);
+}
diff --git a/extensions/libipt_rpc.c b/extensions/libipt_rpc.c
new file mode 100644 (file)
index 0000000..7fb0586
--- /dev/null
@@ -0,0 +1,374 @@
+/* RPC extension for IP connection matching, Version 2.2
+ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
+ *     - original rpc tracking module
+ *     - "recent" connection handling for kernel 2.3+ netfilter
+ *
+ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
+ *     - upgraded conntrack modules to oldnat api - kernel 2.4.0+
+ *
+ * (C) 2002,2003 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
+ *     - upgraded conntrack modules to newnat api - kernel 2.4.20+
+ *     - extended matching to support filtering on procedures
+ *
+ * libipt_rpc.c,v 2.2 2003/01/12 18:30:00
+ *
+ *     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.
+ **
+ *     Userspace library syntax:
+ *     --rpc [--rpcs procedure1,procedure2,...procedure128] [--static]
+ *
+ *     Procedures can be supplied in either numeric or named formats.
+ *     Without --rpcs, this module will behave as the old record-rpc.
+ **
+ *     Note to all:
+ *
+ *     RPCs should not be exposed to the internet - ask the Pentagon;
+ *
+ *       "The unidentified crackers pleaded guilty in July to charges
+ *        of juvenile delinquency stemming from a string of Pentagon
+ *        network intrusions in February.
+ *
+ *        The youths, going by the names TooShort and Makaveli, used
+ *        a common server security hole to break in, according to
+ *        Dane Jasper, owner of the California Internet service
+ *        provider, Sonic. They used the hole, known as the 'statd'
+ *        exploit, to attempt more than 800 break-ins, Jasper said."
+ *
+ *     From: Wired News; "Pentagon Kids Kicked Off Grid" - Nov 6, 1998
+ *     URL:  http://www.wired.com/news/politics/0,1283,16098,00.html
+ **
+ */
+
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <rpc/rpc.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_rpc.h>
+#include <time.h>
+
+
+const int IPT_RPC_RPCS = 1;
+const int IPT_RPC_STRC = 2;
+
+const int IPT_RPC_INT_LBL = 1;
+const int IPT_RPC_INT_NUM = 2;
+const int IPT_RPC_INT_BTH = 3;
+
+const int IPT_RPC_CHAR_LEN = 11;
+const int IPT_RPC_MAX_ENTS = 128;
+
+const char preerr[11] = "RPC match:";
+
+
+static int k_itoa(char *string, int number)
+{
+       int maxoctet = IPT_RPC_CHAR_LEN - 1;
+       int store[IPT_RPC_CHAR_LEN];
+       int counter;
+
+
+        for (counter=0 ; maxoctet != 0 && number != 0; counter++, maxoctet--) {
+               store[counter] = number / 10;
+               store[counter] = number - ( store[counter] * 10 );
+               number = number / 10;
+        }
+
+        for ( ; counter != 0; counter--, string++)
+               *string = store[counter - 1] + 48;
+
+       *string = 0;
+
+       return(0);
+}
+
+
+static int k_atoi(signed char *string)
+{
+       unsigned int result = 0;
+       int maxoctet = IPT_RPC_CHAR_LEN;
+
+
+        for ( ; *string != 0 && maxoctet != 0; maxoctet--, string++) {
+                if (*string < 0)
+                        return(0);
+                if (*string == 0)
+                        break;
+                if (*string < 48 || *string > 57) {
+                        return(0);
+                }
+                result = result * 10 + ( *string - 48 );
+        }
+
+       return(result);
+}
+
+
+static void print_rpcs(char *c_procs, int i_procs, int labels)
+{
+       int   proc_ctr;
+       char *proc_ptr;
+       unsigned int proc_num;
+       struct rpcent *rpcent;
+
+
+       for (proc_ctr=0; proc_ctr <= i_procs; proc_ctr++) {
+
+               if ( proc_ctr != 0 )
+                       printf(",");
+
+               proc_ptr = c_procs;
+               proc_ptr += proc_ctr * IPT_RPC_CHAR_LEN;
+               proc_num = k_atoi(proc_ptr);
+
+               /* labels(1) == no labels, only numbers
+                * labels(2) == no numbers, only labels
+                * labels(3) == both labels and numbers
+                */
+
+               if (labels == IPT_RPC_INT_LBL || labels == IPT_RPC_INT_BTH ) {
+                       if ( (rpcent = getrpcbynumber(proc_num)) == NULL )
+                               printf("unknown");
+                       else
+                               printf("%s", rpcent->r_name);
+               }
+
+               if (labels == IPT_RPC_INT_BTH )
+                       printf("(");
+
+               if (labels == IPT_RPC_INT_NUM || labels == IPT_RPC_INT_BTH )
+                       printf("%i", proc_num);
+
+               if (labels == IPT_RPC_INT_BTH )
+                       printf(")");
+
+       }
+
+}
+
+
+static void help(void) 
+{
+       printf(
+               "RPC v%s options:\n"
+               "  --rpcs list,of,procedures"
+               "\ta list of rpc program numbers to apply\n"
+               "\t\t\t\tie. 100003,mountd,rquotad (numeric or\n"
+               "\t\t\t\tname form; see /etc/rpc).\n"
+               "  --strict"
+               "\t\t\ta flag to force the drop of packets\n"
+               "\t\t\t\tnot containing \"get\" portmapper requests.\n",
+               IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { "rpcs", 1, 0, '1'},
+       { "strict", 0, 0, '2'},
+       {0}
+};
+
+
+static void init(struct ipt_entry_match *match, unsigned int *nfcache)
+{
+       struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
+
+
+       /* caching not yet implemented */
+        *nfcache |= NFC_UNKNOWN;
+
+       /* initialise those funky user vars */
+       rpcinfo->i_procs = -1;
+       rpcinfo->strict  =  0;
+       memset((char *)rpcinfo->c_procs, 0, sizeof(rpcinfo->c_procs));
+}
+
+
+static void parse_rpcs_string(char *string, struct ipt_entry_match **match)
+{
+       char err1[64] = "%s invalid --rpcs option-set: `%s' (at character %i)";
+       char err2[64] = "%s unable to resolve rpc name entry: `%s'";
+       char err3[64] = "%s maximum number of --rpc options (%i) exceeded";
+       char buf[256];
+       char *dup = buf;
+       int idup = 0;
+       int term = 0;
+       char *src, *dst;
+       char *c_procs;
+       struct rpcent *rpcent_ptr;
+       struct ipt_rpc_info *rpcinfo = (struct ipt_rpc_info *)(*match)->data;
+
+
+       memset(buf, 0, sizeof(buf));
+
+       for (src=string, dst=buf; term != 1 ; src++, dst++) {
+
+               if ( *src != ',' && *src != '\0' ) {
+                       if ( ( *src >= 65 && *src <= 90 ) || ( *src >= 97 && *src <= 122) ) {
+                               *dst = *src;
+                               idup = 1;
+
+                       } else if ( *src >= 48 && *src <= 57 ) {
+                               *dst = *src;
+
+                       } else {
+                               exit_error(PARAMETER_PROBLEM, err1, preerr,
+                                          string, src - string + 1);
+
+                       }
+
+               } else {
+                       *dst = '\0';
+                       if ( idup == 1 ) {
+                               if ( (rpcent_ptr = getrpcbyname(dup)) == NULL )
+                                       exit_error(PARAMETER_PROBLEM, err2,
+                                                  preerr, dup);
+                               idup = rpcent_ptr->r_number;
+                       } else {
+                               idup = k_atoi(dup);
+                       }
+
+                       rpcinfo->i_procs++;
+                       if ( rpcinfo->i_procs > IPT_RPC_MAX_ENTS )
+                               exit_error(PARAMETER_PROBLEM, err3, preerr,
+                                          IPT_RPC_MAX_ENTS);
+                               
+                       c_procs  = (char *)rpcinfo->c_procs;
+                       c_procs += rpcinfo->i_procs * IPT_RPC_CHAR_LEN;
+                       
+                       memset(buf, 0, sizeof(buf));
+                       k_itoa((char *)dup, idup);
+
+                       strcpy(c_procs, dup);
+       
+                       if ( *src == '\0')
+                               term = 1;
+
+                       idup = 0;
+                       memset(buf, 0, sizeof(buf));
+                       dst = (char *)buf - 1;
+               }
+       }
+
+       return;
+}
+
+
+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_rpc_info *rpcinfo = (struct ipt_rpc_info *)(*match)->data;
+
+
+       switch (c)
+       {
+       case '1':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "%s unexpected '!' with --rpcs\n", preerr);
+               if (*flags & IPT_RPC_RPCS)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "%s repeated use of --rpcs\n", preerr);
+               parse_rpcs_string(optarg, match);
+
+               *flags |= IPT_RPC_RPCS;
+               break;
+
+       case '2':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "%s unexpected '!' with --strict\n", preerr);
+               if (*flags & IPT_RPC_STRC)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "%s repeated use of --strict\n", preerr);
+               rpcinfo->strict = 1;
+               *flags |= IPT_RPC_STRC;
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 1;
+
+}
+
+
+static void final_check(unsigned int flags)
+{
+       if (flags != (flags | IPT_RPC_RPCS)) {
+               printf("%s option \"--rpcs\" was not used ... reverting ", preerr);
+               printf("to old \"record-rpc\" functionality ..\n");
+       }
+}
+
+
+static void print(const struct ipt_ip *ip,
+               const struct ipt_entry_match *match,
+               int numeric)
+{
+       struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
+
+
+       printf("RPCs");
+       if(rpcinfo->strict == 1)
+               printf("[strict]");
+
+       printf(": ");
+
+       if(rpcinfo->i_procs == -1) {
+               printf("any(*)");
+
+       } else {
+               print_rpcs((char *)&rpcinfo->c_procs, rpcinfo->i_procs, IPT_RPC_INT_BTH);
+       }
+       printf(" ");
+
+}
+
+
+static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_rpc_info *rpcinfo = ((struct ipt_rpc_info *)match->data);
+
+
+       if(rpcinfo->i_procs > -1) {
+               printf("--rpcs ");
+               print_rpcs((char *)&rpcinfo->c_procs, rpcinfo->i_procs, IPT_RPC_INT_NUM);
+               printf(" ");
+       }
+
+       if(rpcinfo->strict == 1)
+               printf("--strict ");
+
+}
+
+
+static struct iptables_match rpcstruct = { NULL,
+    "rpc",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_rpc_info)),
+    IPT_ALIGN(sizeof(struct ipt_rpc_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+
+void _init(void)
+{
+       register_match(&rpcstruct);
+}
+
diff --git a/extensions/libipt_sctp.c b/extensions/libipt_sctp.c
new file mode 100644 (file)
index 0000000..d512fa6
--- /dev/null
@@ -0,0 +1,402 @@
+/* Shared library add-on to iptables for SCTP matching
+ *
+ * (C) 2003 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This program is distributed under the terms of GNU GPL v2, 1991
+ *
+ * libipt_ecn.c borrowed heavily from libipt_dscp.c
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <netdb.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_sctp.h>
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       struct ipt_sctp_info *einfo = (struct ipt_sctp_info *)m->data;
+
+       einfo->spts[1] = einfo->dpts[1] = 0xFFFF;
+}
+
+static void help(void) 
+{
+       printf(
+"SCTP match v%s options\n"
+" --sctp-chunks [!] mask comp  match when SCTP chunks & mask == comp\n"
+" --source-port [!] port[:port]\n"
+" --sport ...\n"
+"                               match source port(s)"
+" --destination-port [!] port[:port]\n"
+" --dport ...\n\n",
+       IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { .name = "source-port", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "sport", .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "destination-port", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "dport", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = "sctp-chunks", .has_arg = 1, .flag = 0, .val = '3' },
+       { .name = 0 }
+};
+
+static int
+service_to_port(const char *name)
+{
+       struct servent *service;
+
+       if ((service = getservbyname(name, "sctp")) != NULL)
+               return ntohs((unsigned short) service->s_port);
+
+       return -1;
+}
+
+static u_int16_t
+parse_sctp_port(const char *port)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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_sctp_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_sctp_port(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               ports[0] = buffer[0] ? parse_sctp_port(buffer) : 0;
+               ports[1] = cp[0] ? parse_sctp_port(cp) : 0xFFFF;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       free(buffer);
+}
+
+struct sctp_chunk_names {
+       const char *name;
+       unsigned int flag;
+};
+
+/* FIXME: */
+#define ALL_CHUNKS     0xabcdef
+static struct sctp_chunk_names sctp_chunk_names[]
+= { { .name = "DATA",          .flag = (1 << 0) },
+    { .name = "INIT",          .flag = (1 << 1) },
+    { .name = "INIT_ACK",      .flag = (1 << 2) },
+    { .name = "SACK",          .flag = (1 << 3) },
+    { .name = "HEARTBEAT",     .flag = (1 << 4) },
+    { .name = "HEARTBEAT_ACK", .flag = (1 << 5) },
+    { .name = "ABORT",         .flag = (1 << 6) },
+    { .name = "SHUTDOWN",      .flag = (1 << 7) },
+    { .name = "SHUTDOWN_ACK",  .flag = (1 << 8) },
+    { .name = "ERROR",         .flag = (1 << 9) },
+    { .name = "COOKIE_ECHO",   .flag = (1 << 10) },
+    { .name = "COOKIE_ACK",    .flag = (1 << 11) },
+    { .name = "ECN_ECNE",      .flag = (1 << 12) },
+    { .name = "ECN_CWR",       .flag = (1 << 13) },
+    { .name = "SHUTDOWN_COMPLETE", .flag = (1 << 14) },
+    { .name = "ASCONF",                .flag = (1 << 31) },
+    { .name = "ASCONF_ACK",    .flag = (1 << 30) },
+    { .name = "ALL",           .flag = ALL_CHUNKS },
+    { .name = "NONE",          .flag = 0 },
+};
+
+
+static unsigned int
+parse_sctp_chunk(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;
+               int found = 0;
+               for (i = 0;
+                    i < sizeof(sctp_chunk_names)/sizeof(struct sctp_chunk_names);
+                    i++) {
+                       if (strcasecmp(sctp_chunk_names[i].name, ptr) == 0) {
+                               ret |= sctp_chunk_names[i].flag;
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Unknown sctp chunk `%s'", ptr);
+       }
+
+       free(buffer);
+       return ret;
+}
+
+static void
+parse_sctp_chunks(struct ipt_sctp_info *einfo,
+               const char *mask,
+               const char *cmp,
+               int invert)
+{
+       einfo->chunks = parse_sctp_chunk(mask);
+       einfo->chunk_mask = parse_sctp_chunk(cmp);
+
+       if (invert)
+               einfo->invflags |= IPT_SCTP_INV_CHUNKS;
+}
+
+#define SCTP_SRC_PORTS 0x01
+#define SCTP_DST_PORTS 0x02
+#define SCTP_CHUNKS    0x03
+
+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_sctp_info *einfo
+               = (struct ipt_sctp_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags & SCTP_SRC_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--source-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->spts);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_INV_SRCPT;
+               *flags |= SCTP_SRC_PORTS;
+               *nfcache |= NFC_IP_SRC_PT;
+               break;
+
+       case '2':
+               if (*flags & SCTP_DST_PORTS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--destination-port' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_sctp_ports(argv[optind-1], einfo->dpts);
+               if (invert)
+                       einfo->invflags |= IPT_SCTP_INV_DSTPT;
+               *flags |= SCTP_DST_PORTS;
+               *nfcache |= NFC_IP_DST_PT;
+               break;
+
+       case '3':
+               if (*flags & SCTP_CHUNKS)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--sctp-chunks' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind] 
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--sctp-chunks requires two args");
+
+               parse_sctp_chunks(einfo, argv[optind-1], argv[optind], invert);
+               optind++;
+               *flags |= SCTP_CHUNKS;
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+static void
+final_check(unsigned int flags)
+{
+}
+
+static char *
+port_to_service(int port)
+{
+       struct servent *service;
+
+       if ((service = getservbyport(htons(port), "sctp")))
+               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_chunk(u_int32_t chunks)
+{
+       unsigned int have_flag = 0;
+
+       while (chunks) {
+               unsigned int i;
+
+               for (i = 0; (chunks & sctp_chunk_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", sctp_chunk_names[i].name);
+               have_flag = 1;
+
+               chunks &= ~sctp_chunk_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+static void
+print_chunks(u_int32_t mask, u_int32_t cmp, int invert, int numeric)
+{
+       if (mask || invert) {
+               printf("flags:%s", invert ? "!" : "");
+               if (numeric)
+                       printf("0x%04X/0x%04X ", mask, cmp);
+               else {
+                       print_chunk(mask);
+                       printf("/");
+                       print_chunk(cmp);
+                       printf(" ");
+               }
+       }
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_sctp_info *einfo =
+               (const struct ipt_sctp_info *)match->data;
+
+       printf("sctp ");
+
+       print_ports("spt", einfo->spts[0], einfo->spts[1],
+                   einfo->invflags & IPT_SCTP_INV_SRCPT,
+                   numeric);
+       print_ports("dpt", einfo->dpts[0], einfo->dpts[1],
+                   einfo->invflags & IPT_SCTP_INV_DSTPT,
+                   numeric);
+
+       print_chunks(einfo->chunks, einfo->chunk_mask,
+                    einfo->invflags & ~IPT_SCTP_INV_MASK,
+                    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)
+{
+       const struct ipt_sctp_info *einfo =
+               (const struct ipt_sctp_info *)match->data;
+
+       if (einfo->spts[0] != 0
+           || einfo->spts[1] != 0xFFFF) {
+               if (einfo->invflags & IPT_SCTP_INV_SRCPT)
+                       printf("! ");
+               if (einfo->spts[0] != einfo->spts[1])
+                       printf("--sport %u:%u ", 
+                              einfo->spts[0], einfo->spts[1]);
+               else
+                       printf("--sport %u ", einfo->spts[0]);
+       }
+
+       if (einfo->dpts[0] != 0
+           || einfo->dpts[1] != 0xFFFF) {
+               if (einfo->invflags & IPT_SCTP_INV_DSTPT)
+                       printf("! ");
+               if (einfo->dpts[0] != einfo->dpts[1])
+                       printf("--dport %u:%u ",
+                              einfo->dpts[0], einfo->dpts[1]);
+               else
+                       printf("--dport %u ", einfo->dpts[0]);
+       }
+
+       if (einfo->chunks
+           || (einfo->invflags & IPT_SCTP_INV_CHUNKS)) {
+               if (einfo->invflags & IPT_SCTP_INV_CHUNKS)
+                       printf("! ");
+               printf("--sctp-chunks ");
+               if (einfo->chunks != ALL_CHUNKS) {
+                       print_chunk(einfo->chunks);
+               }
+               printf(" ");
+               print_chunk(einfo->chunk_mask);
+               printf(" ");
+       }
+}
+
+static
+struct iptables_match sctp
+= { .name          = "sctp",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_sctp_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+void _init(void)
+{
+       register_match(&sctp);
+}
diff --git a/extensions/libipt_standard.c b/extensions/libipt_standard.c
new file mode 100644 (file)
index 0000000..d79ad7c
--- /dev/null
@@ -0,0 +1,69 @@
+/* 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", IPTABLES_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)
+{
+}
+
+static
+struct iptables_target standard
+= { NULL,
+    "standard",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(int)),
+    IPT_ALIGN(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 (file)
index 0000000..3662d94
--- /dev/null
@@ -0,0 +1,173 @@
+/* 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>
+
+#ifndef IPT_STATE_UNTRACKED
+#define IPT_STATE_UNTRACKED (1 << (IP_CT_NUMBER + 1))
+#endif
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"state v%s options:\n"
+" [!] --state [INVALID|ESTABLISHED|NEW|RELATED|UNTRACKED][,...]\n"
+"                              State(s) to match\n"
+"\n", IPTABLES_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 if (strncasecmp(state, "UNTRACKED", strlen) == 0)
+               sinfo->statemask |= IPT_STATE_UNTRACKED;
+       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':
+               check_inverse(optarg, &invert, &optind, 0);
+
+               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 = ",";
+       }
+       if (statemask & IPT_STATE_UNTRACKED) {
+               printf("%sUNTRACKED", 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);
+}
+
+static
+struct iptables_match state
+= { NULL,
+    "state",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_state_info)),
+    IPT_ALIGN(sizeof(struct ipt_state_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&state);
+}
diff --git a/extensions/libipt_string.c b/extensions/libipt_string.c
new file mode 100644 (file)
index 0000000..b4710f9
--- /dev/null
@@ -0,0 +1,227 @@
+/* Shared library add-on to iptables to add string matching support. 
+ * 
+ * Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
+ *
+ * ChangeLog
+ *     27.01.2001: Gianni Tedesco <gianni@ecsc.co.uk>
+ *             Changed --tos to --string in save(). Also
+ *             updated to work with slightly modified
+ *             ipt_string_info.
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_string.h>
+
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"STRING match v%s options:\n"
+"--string [!] string          Match a string in a packet\n"
+"--hex-string [!] string      Match a hex string in a packet\n",
+IPTABLES_VERSION);
+}
+
+
+static struct option opts[] = {
+       { .name = "string",     .has_arg = 1, .flag = 0, .val = '1' },
+       { .name = "hex-string", .has_arg = 1, .flag = 0, .val = '2' },
+       { .name = 0 }
+};
+
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+
+static void
+parse_string(const unsigned char *s, struct ipt_string_info *info)
+{      
+       if (strlen(s) <= BM_MAX_NLEN) strcpy(info->string, s);
+       else exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
+}
+
+
+static void
+parse_hex_string(const unsigned char *s, struct ipt_string_info *info)
+{
+       int i=0, slen, sindex=0, schar;
+       short hex_f = 0, literal_f = 0;
+       char hextmp[3];
+
+       slen = strlen(s);
+
+       if (slen == 0) {
+               exit_error(PARAMETER_PROBLEM,
+                       "STRING must contain at least one char");
+       }
+
+       while (i < slen) {
+               if (s[i] == '\\' && !hex_f) {
+                       literal_f = 1;
+               } else if (s[i] == '\\') {
+                       exit_error(PARAMETER_PROBLEM,
+                               "Cannot include literals in hex data");
+               } else if (s[i] == '|') {
+                       if (hex_f)
+                               hex_f = 0;
+                       else
+                               hex_f = 1;
+                       if (i+1 >= slen)
+                               break;
+                       else
+                               i++;  /* advance to the next character */
+               }
+
+               if (literal_f) {
+                       if (i+1 >= slen) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Bad literal placement at end of string");
+                       }
+                       info->string[sindex] = s[i+1];
+                       i += 2;  /* skip over literal char */
+                       literal_f = 0;
+               } else if (hex_f) {
+                       if (i+1 >= slen) {
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Odd number of hex digits");
+                       }
+                       if (i+2 >= slen) {
+                               /* must end with a "|" */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex block");
+                       }
+                       if (! isxdigit(s[i])) /* check for valid hex char */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i]);
+                       if (! isxdigit(s[i+1])) /* check for valid hex char */
+                               exit_error(PARAMETER_PROBLEM, "Invalid hex char `%c'", s[i+1]);
+                       hextmp[0] = s[i];
+                       hextmp[1] = s[i+1];
+                       hextmp[2] = '\0';
+                       if (! sscanf(hextmp, "%x", &schar))
+                               exit_error(PARAMETER_PROBLEM,
+                                       "Invalid hex char `%c'", s[i]);
+                       info->string[sindex] = (char) schar;
+                       if (s[i+2] == ' ')
+                               i += 3;  /* spaces included in the hex block */
+                       else
+                               i += 2;
+               } else {  /* the char is not part of hex data, so just copy */
+                       info->string[sindex] = s[i];
+                       i++;
+               }
+               if (sindex > BM_MAX_NLEN)
+                       exit_error(PARAMETER_PROBLEM, "STRING too long `%s'", s);
+               sindex++;
+       }
+       info->len = sindex;
+}
+
+
+/* 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_string_info *stringinfo = (struct ipt_string_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple strings");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_string(argv[optind-1], stringinfo);
+               if (invert)
+                       stringinfo->invert = 1;
+               stringinfo->len=strlen((char *)&stringinfo->string);
+               *flags = 1;
+               break;
+
+       case '2':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Can't specify multiple strings");
+
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_hex_string(argv[optind-1], stringinfo);  /* sets length */
+               if (invert)
+                       stringinfo->invert = 1;
+               *flags = 1;
+               break;
+
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+
+/* Final check; must have specified --string. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "STRING match: You must specify `--string'");
+}
+
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_string_info *info =
+           (const struct ipt_string_info*) match->data;
+
+       printf("STRING match %s%s ", (info->invert) ? "!" : "", info->string);
+}
+
+
+/* Saves the union ipt_matchinfo in parseable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       const struct ipt_string_info *info =
+           (const struct ipt_string_info*) match->data;
+
+       printf("--string %s%s ", (info->invert) ? "! ": "", info->string);
+}
+
+
+static struct iptables_match string = {
+    .name          = "string",
+    .version       = IPTABLES_VERSION,
+    .size          = IPT_ALIGN(sizeof(struct ipt_string_info)),
+    .userspacesize = IPT_ALIGN(sizeof(struct ipt_string_info)),
+    .help          = &help,
+    .init          = &init,
+    .parse         = &parse,
+    .final_check   = &final_check,
+    .print         = &print,
+    .save          = &save,
+    .extra_opts    = opts
+};
+
+
+void _init(void)
+{
+       register_match(&string);
+}
diff --git a/extensions/libipt_tcp.c b/extensions/libipt_tcp.c
new file mode 100644 (file)
index 0000000..37613ac
--- /dev/null
@@ -0,0 +1,445 @@
+/* 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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       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'", ptr);
+               }
+
+       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)
+{
+       unsigned int ret;
+
+       if (string_to_number(option, 1, 255, &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");
+               check_inverse(optarg, &invert, &optind, 0);
+               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");
+               check_inverse(optarg, &invert, &optind, 0);
+               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");
+               check_inverse(optarg, &invert, &optind, 0);
+
+               if (!argv[optind]
+                   || argv[optind][0] == '-' || argv[optind][0] == '!')
+                       exit_error(PARAMETER_PROBLEM,
+                                  "--tcp-flags requires two args.");
+
+               parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind],
+                               invert);
+               optind++;
+               *flags |= TCP_FLAGS;
+               *nfcache |= NFC_IP_TCPFLAGS;
+               break;
+
+       case '5':
+               if (*flags & TCP_OPTION)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--tcp-option' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               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 have_flag = 0;
+
+       while (flags) {
+               unsigned int i;
+
+               for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
+
+               if (have_flag)
+                       printf(",");
+               printf("%s", tcp_flag_names[i].name);
+               have_flag = 1;
+
+               flags &= ~tcp_flag_names[i].flag;
+       }
+
+       if (!have_flag)
+               printf("NONE");
+}
+
+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("0x%02X/0x%02X ", mask, cmp);
+               else {
+                       print_tcpf(mask);
+                       printf("/");
+                       print_tcpf(cmp);
+                       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 ",
+                              tcpinfo->spts[0],
+                              tcpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              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 ",
+                              tcpinfo->dpts[0],
+                              tcpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              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("! ");
+               printf("--tcp-flags ");
+               if (tcpinfo->flg_mask != 0xFF) {
+                       print_tcpf(tcpinfo->flg_mask);
+               }
+               printf(" ");
+               print_tcpf(tcpinfo->flg_cmp);
+               printf(" ");
+       }
+}
+
+static
+struct iptables_match tcp
+= { NULL,
+    "tcp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tcp)),
+    IPT_ALIGN(sizeof(struct ipt_tcp)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts };
+
+void
+_init(void)
+{
+       register_match(&tcp);
+}
diff --git a/extensions/libipt_tcpmss.c b/extensions/libipt_tcpmss.c
new file mode 100644 (file)
index 0000000..2eb9797
--- /dev/null
@@ -0,0 +1,161 @@
+/* Shared library add-on to iptables to add tcp MSS 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_tcpmss.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"tcpmss match v%s options:\n"
+"[!] --mss value[:value]       Match TCP MSS range.\n"
+"                              (only valid for TCP SYN or SYN/ACK packets)\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "mss", 1, 0, '1' },
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_IP_PROTO_UNKNOWN;
+}
+
+static u_int16_t
+parse_tcp_mssvalue(const char *mssvalue)
+{
+       unsigned int mssvaluenum;
+
+       if (string_to_number(mssvalue, 0, 65535, &mssvaluenum) != -1)
+               return (u_int16_t)mssvaluenum;
+
+       exit_error(PARAMETER_PROBLEM,
+                  "Invalid mss `%s' specified", mssvalue);
+}
+
+static void
+parse_tcp_mssvalues(const char *mssvaluestring,
+                   u_int16_t *mss_min, u_int16_t *mss_max)
+{
+       char *buffer;
+       char *cp;
+
+       buffer = strdup(mssvaluestring);
+       if ((cp = strchr(buffer, ':')) == NULL)
+               *mss_min = *mss_max = parse_tcp_mssvalue(buffer);
+       else {
+               *cp = '\0';
+               cp++;
+
+               *mss_min = buffer[0] ? parse_tcp_mssvalue(buffer) : 0;
+               *mss_max = cp[0] ? parse_tcp_mssvalue(cp) : 0xFFFF;
+       }
+       free(buffer);
+}
+
+/* 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_tcpmss_match_info *mssinfo =
+               (struct ipt_tcpmss_match_info *)(*match)->data;
+
+       switch (c) {
+       case '1':
+               if (*flags)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Only one `--mss' allowed");
+               check_inverse(optarg, &invert, &optind, 0);
+               parse_tcp_mssvalues(argv[optind-1],
+                                   &mssinfo->mss_min, &mssinfo->mss_max);
+               if (invert)
+                       mssinfo->invert = 1;
+               *flags = 1;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+static void
+print_tcpmss(u_int16_t mss_min, u_int16_t mss_max, int invert, int numeric)
+{
+       if (invert)
+               printf("! ");
+
+       if (mss_min == mss_max)
+               printf("%u ", mss_min);
+       else
+               printf("%u:%u ", mss_min, mss_max);
+}
+
+/* Final check; must have specified --mss. */
+static void
+final_check(unsigned int flags)
+{
+       if (!flags)
+               exit_error(PARAMETER_PROBLEM,
+                          "tcpmss match: You must specify `--mss'");
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       const struct ipt_tcpmss_match_info *mssinfo =
+               (const struct ipt_tcpmss_match_info *)match->data;
+
+       printf("tcpmss match ");
+       print_tcpmss(mssinfo->mss_min, mssinfo->mss_max,
+                    mssinfo->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)
+{
+       const struct ipt_tcpmss_match_info *mssinfo =
+               (const struct ipt_tcpmss_match_info *)match->data;
+
+       printf("--mss ");
+       print_tcpmss(mssinfo->mss_min, mssinfo->mss_max,
+                    mssinfo->invert, 0);
+}
+
+static
+struct iptables_match tcpmss
+= { NULL,
+    "tcpmss",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+    IPT_ALIGN(sizeof(struct ipt_tcpmss_match_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&tcpmss);
+}
diff --git a/extensions/libipt_time.c b/extensions/libipt_time.c
new file mode 100644 (file)
index 0000000..909ca81
--- /dev/null
@@ -0,0 +1,313 @@
+/* Shared library add-on to iptables to add TIME 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_time.h>
+#include <time.h>
+
+static int globaldays;
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf(
+"TIME v%s options:\n"
+" --timestart value --timestop value --days listofdays\n"
+"          timestart value : HH:MM\n"
+"          timestop  value : HH:MM\n"
+"          listofdays value: a list of days to apply -> ie. Mon,Tue,Wed,Thu,Fri. Case sensitive\n",
+IPTABLES_VERSION);
+}
+
+static struct option opts[] = {
+       { "timestart", 1, 0, '1' },
+       { "timestop", 1, 0, '2' },
+       { "days", 1, 0, '3'},
+       {0}
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       /* caching not yet implemented */
+        *nfcache |= NFC_UNKNOWN;
+       globaldays = 0;
+}
+
+/**
+ * param: part1, a pointer on a string 2 chars maximum long string, that will contain the hours.
+ * param: part2, a pointer on a string 2 chars maximum long string, that will contain the minutes.
+ * param: str_2_parse, the string to parse.
+ * return: 1 if ok, 0 if error.
+ */
+static int
+split_time(char **part1, char **part2, const char *str_2_parse)
+{
+       unsigned short int i,j=0;
+       char *rpart1 = *part1;
+       char *rpart2 = *part2;
+       unsigned char found_column = 0;
+
+       /* Check the length of the string */
+       if (strlen(str_2_parse) > 5)
+               return 0;
+       /* parse the first part until the ':' */
+       for (i=0; i<2; i++)
+       {
+               if (str_2_parse[i] == ':')
+                       found_column = 1;
+               else
+                       rpart1[i] = str_2_parse[i];
+       }
+       if (!found_column)
+               i++;
+       j=i;
+       /* parse the second part */
+       for (; i<strlen(str_2_parse); i++)
+       {
+               rpart2[i-j] = str_2_parse[i];
+       }
+       /* if we are here, format should be ok. */
+       return 1;
+}
+
+static void
+parse_time_string(unsigned int *hour, unsigned int *minute, const char *time)
+{
+       char *hours;
+       char *minutes;
+
+       hours = (char *)malloc(3);
+       minutes = (char *)malloc(3);
+       bzero((void *)hours, 3);
+       bzero((void *)minutes, 3);
+
+       if (split_time(&hours, &minutes, time) == 1)
+       {
+                /* if the number starts with 0, replace it with a space else
+                   this string_to_number will interpret it as octal !! */
+                if ((hours[0] == '0') && (hours[1] != '\0'))
+                       hours[0] = ' ';
+               if ((minutes[0] == '0') && (minutes[1] != '\0'))
+                       minutes[0] = ' ';
+
+               if((string_to_number(hours, 0, 23, hour) == -1) ||
+                       (string_to_number(minutes, 0, 59, minute) == -1)) {
+                       *hour = *minute = (-1);
+               }
+       }
+       if ((*hour != (-1)) && (*minute != (-1))) {
+               free(hours);
+               free(minutes);
+               return;
+       }
+
+       /* If we are here, there was a problem ..*/
+       exit_error(PARAMETER_PROBLEM,
+                  "invalid time `%s' specified, should be HH:MM format", time);
+}
+
+/* return 1->ok, return 0->error */
+static int
+parse_day(int *days, int from, int to, const char *string)
+{
+       char *dayread;
+       char *days_str[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+       unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
+       unsigned int i;
+
+       dayread = (char *)malloc(4);
+       bzero(dayread, 4);
+       if ((to-from) != 3) {
+               free(dayread);
+               return 0;
+       }
+       for (i=from; i<to; i++)
+               dayread[i-from] = string[i];
+       for (i=0; i<7; i++)
+               if (strcmp(dayread, days_str[i]) == 0)
+               {
+                       *days |= days_of_week[i];
+                       free(dayread);
+                       return 1;
+               }
+       /* if we are here, we didn't read a valid day */
+       free(dayread);
+       return 0;
+}
+
+static void
+parse_days_string(int *days, const char *daystring)
+{
+       int len;
+       int i=0;
+       char *err = "invalid days `%s' specified, should be Sun,Mon,Tue... format";
+
+       len = strlen(daystring);
+       if (len < 3)
+               exit_error(PARAMETER_PROBLEM, err, daystring);  
+       while(i<len)
+       {
+               if (parse_day(days, i, i+3, daystring) == 0)
+                       exit_error(PARAMETER_PROBLEM, err, daystring);
+               i += 4;
+       }
+}
+
+#define IPT_TIME_START 0x01
+#define IPT_TIME_STOP  0x02
+#define IPT_TIME_DAYS  0x04
+
+
+/* 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_time_info *timeinfo = (struct ipt_time_info *)(*match)->data;
+       int hours, minutes;
+
+       switch (c)
+       {
+               /* timestart */
+       case '1':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --timestart");
+               if (*flags & IPT_TIME_START)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --timestart twice");
+               parse_time_string(&hours, &minutes, optarg);
+               timeinfo->time_start = (hours * 60) + minutes;
+               *flags |= IPT_TIME_START;
+               break;
+               /* timestop */
+       case '2':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --timestop");
+               if (*flags & IPT_TIME_STOP)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --timestop twice");
+               parse_time_string(&hours, &minutes, optarg);
+               timeinfo->time_stop = (hours * 60) + minutes;
+               *flags |= IPT_TIME_STOP;
+               break;
+
+               /* days */
+       case '3':
+               if (invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                   "unexpected '!' with --days");
+               if (*flags & IPT_TIME_DAYS)
+                        exit_error(PARAMETER_PROBLEM,
+                                   "Can't specify --days twice");
+               parse_days_string(&globaldays, optarg);
+               timeinfo->days_match = globaldays;
+               *flags |= IPT_TIME_DAYS;
+               break;
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+/* Final check; must have specified --timestart --timestop --days. */
+static void
+final_check(unsigned int flags)
+{
+       if (flags != (IPT_TIME_START | IPT_TIME_STOP | IPT_TIME_DAYS))
+               exit_error(PARAMETER_PROBLEM,
+                          "TIME match: You must specify `--timestart --timestop and --days'");
+}
+
+
+static void
+print_days(int daynum)
+{
+       char *days[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+       unsigned short int days_of_week[7] = {64, 32, 16, 8, 4, 2, 1};
+       unsigned short int i, nbdays=0;
+
+       for (i=0; i<7; i++) {
+               if ((days_of_week[i] & daynum) == days_of_week[i])
+               {
+                       if (nbdays>0)
+                               printf(",%s", days[i]);
+                       else
+                               printf("%s", days[i]);
+                       ++nbdays;
+               }
+       }
+}
+
+static void
+divide_time(int fulltime, int *hours, int *minutes)
+{
+       *hours = fulltime / 60;
+       *minutes = fulltime % 60;
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       struct ipt_time_info *time = ((struct ipt_time_info *)match->data);
+       int hour_start, hour_stop, minute_start, minute_stop;
+
+       divide_time(time->time_start, &hour_start, &minute_start);
+       divide_time(time->time_stop, &hour_stop, &minute_stop);
+       printf(" TIME from %d:%d to %d:%d on ",
+              hour_start, minute_start,
+              hour_stop, minute_stop);
+       print_days(time->days_match);
+       printf(" ");
+}
+
+/* Saves the data in parsable form to stdout. */
+static void
+save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
+{
+       struct ipt_time_info *time = ((struct ipt_time_info *)match->data);
+       int hour_start, hour_stop, minute_start, minute_stop;
+
+       divide_time(time->time_start, &hour_start, &minute_start);
+       divide_time(time->time_stop, &hour_stop, &minute_stop);
+       printf(" --timestart %.2d:%.2d --timestop %.2d:%.2d --days ",
+              hour_start, minute_start,
+              hour_stop, minute_stop);
+       print_days(time->days_match);
+       printf(" ");
+}
+
+static
+struct iptables_match timestruct
+= { NULL,
+    "time",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_time_info)),
+    IPT_ALIGN(sizeof(struct ipt_time_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&timestruct);
+}
diff --git a/extensions/libipt_tos.c b/extensions/libipt_tos.c
new file mode 100644 (file)
index 0000000..6786911
--- /dev/null
@@ -0,0 +1,176 @@
+/* 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. */
+static
+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",
+IPTABLES_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;
+       unsigned int tos;
+
+       if (string_to_number(s, 0, 255, &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':
+               check_inverse(optarg, &invert, &optind, 0);
+               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 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);
+}
+
+/* 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)
+{
+       const struct ipt_tos_info *info = (const struct ipt_tos_info *)match->data;
+    
+       printf("TOS match ");
+       if (info->invert)
+               printf("!");
+       print_tos(info->tos, 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)
+{
+       const struct ipt_tos_info *info = (const struct ipt_tos_info *)match->data;
+    
+       if (info->invert)
+               printf("! ");
+       printf("--tos ");
+       print_tos(info->tos, 0);
+}
+
+static
+struct iptables_match tos
+= { NULL,
+    "tos",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_tos_info)),
+    IPT_ALIGN(sizeof(struct ipt_tos_info)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void _init(void)
+{
+       register_match(&tos);
+}
diff --git a/extensions/libipt_ttl.c b/extensions/libipt_ttl.c
new file mode 100644 (file)
index 0000000..4507fe9
--- /dev/null
@@ -0,0 +1,177 @@
+/* Shared library add-on to iptables to add TTL matching support 
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ *
+ * $Id: libipt_ttl.c,v 1.6 2002/05/29 13:08:16 laforge Exp $
+ *
+ * This program is released under the terms of GNU GPL */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <iptables.h>
+
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ttl.h>
+
+static void help(void) 
+{
+       printf(
+"TTL match v%s options:\n"
+"  --ttl-eq value      Match time to live value\n"
+"  --ttl-lt value      Match TTL < value\n"
+"  --ttl-gt value      Match TTL > value\n"
+, IPTABLES_VERSION);
+}
+
+static void init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       /* caching not yet implemented */
+       *nfcache |= NFC_UNKNOWN;
+}
+
+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_ttl_info *info = (struct ipt_ttl_info *) (*match)->data;
+       u_int8_t value;
+
+       check_inverse(optarg, &invert, &optind, 0);
+       value = atoi(argv[optind-1]);
+
+       if (*flags) 
+               exit_error(PARAMETER_PROBLEM, 
+                               "Can't specify TTL option twice");
+
+       if (!optarg)
+               exit_error(PARAMETER_PROBLEM,
+                               "ttl: You must specify a value");
+       switch (c) {
+               case '2':
+                       if (invert)
+                               info->mode = IPT_TTL_NE;
+                       else
+                               info->mode = IPT_TTL_EQ;
+
+                       /* is 0 allowed? */
+                       info->ttl = value;
+                       *flags = 1;
+
+                       break;
+               case '3':
+                       if (invert) 
+                               exit_error(PARAMETER_PROBLEM,
+                                               "ttl: unexpected `!'");
+
+                       info->mode = IPT_TTL_LT;
+                       info->ttl = value;
+                       *flags = 1;
+
+                       break;
+               case '4':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                               "ttl: unexpected `!'");
+
+                       info->mode = IPT_TTL_GT;
+                       info->ttl = value;
+                       *flags = 1;
+
+                       break;
+               default:
+                       return 0;
+
+       }
+
+       return 1;
+}
+
+static void final_check(unsigned int flags)
+{
+       if (!flags) 
+               exit_error(PARAMETER_PROBLEM,
+                       "TTL match: You must specify one of "
+                       "`--ttl-eq', `--ttl-lt', `--ttl-gt");
+}
+
+static void print(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match,
+               int numeric)
+{
+       const struct ipt_ttl_info *info = 
+               (struct ipt_ttl_info *) match->data;
+
+       printf("TTL match ");
+       switch (info->mode) {
+               case IPT_TTL_EQ:
+                       printf("TTL == ");
+                       break;
+               case IPT_TTL_NE:
+                       printf("TTL != ");
+                       break;
+               case IPT_TTL_LT:
+                       printf("TTL < ");
+                       break;
+               case IPT_TTL_GT:
+                       printf("TTL > ");
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static void save(const struct ipt_ip *ip, 
+               const struct ipt_entry_match *match)
+{
+       const struct ipt_ttl_info *info =
+               (struct ipt_ttl_info *) match->data;
+
+       switch (info->mode) {
+               case IPT_TTL_EQ:
+                       printf("--ttl-eq ");
+                       break;
+               case IPT_TTL_NE:
+                       printf("! --ttl-eq ");
+                       break;
+               case IPT_TTL_LT:
+                       printf("--ttl-lt ");
+                       break;
+               case IPT_TTL_GT:
+                       printf("--ttl-gt ");
+                       break;
+               default:
+                       /* error */
+                       break;
+       }
+       printf("%u ", info->ttl);
+}
+
+static struct option opts[] = {
+       { "ttl", 1, 0, '2' },
+       { "ttl-eq", 1, 0, '2'},
+       { "ttl-lt", 1, 0, '3'},
+       { "ttl-gt", 1, 0, '4'},
+       { 0 }
+};
+
+static
+struct iptables_match ttl = {
+       NULL,
+       "ttl",
+       IPTABLES_VERSION,
+       IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       IPT_ALIGN(sizeof(struct ipt_ttl_info)),
+       &help,
+       &init,
+       &parse,
+       &final_check,
+       &print,
+       &save,
+       opts
+};
+
+
+void _init(void) 
+{
+       register_match(&ttl);
+}
diff --git a/extensions/libipt_u32.c b/extensions/libipt_u32.c
new file mode 100644 (file)
index 0000000..788413c
--- /dev/null
@@ -0,0 +1,272 @@
+/* Shared library add-on to iptables to add u32 matching,
+ * generalized matching on values found at packet offsets
+ *
+ * Detailed doc is in the kernel module source
+ * net/ipv4/netfilter/ipt_u32.c
+ *
+ * (C) 2002 by Don Cohen <don-netf@isis.cs3-inc.com>
+ * Released under the terms of GNU GPL v2
+ */
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <iptables.h>
+#include <linux/netfilter_ipv4/ipt_u32.h>
+#include <errno.h>
+#include <ctype.h>
+
+/* Function which prints out usage message. */
+static void
+help(void)
+{
+       printf( "u32 v%s options:\n"
+               " --u32 tests\n"
+               " tests := location = value | tests && location = value\n"
+               " value := range | value , range\n"
+               " range := number | number : number\n"
+               " location := number | location operator number\n"
+               " operator := & | << | >> | @\n"
+               ,IPTABLES_VERSION);
+}
+
+/* defined in /usr/include/getopt.h maybe in man getopt */
+static struct option opts[] = {
+       { "u32", 1, 0, '1' },
+       { 0 }
+};
+
+/* Initialize the match. */
+static void
+init(struct ipt_entry_match *m, unsigned int *nfcache)
+{
+       *nfcache |= NFC_UNKNOWN;
+}
+
+/* shared printing code */
+static void print_u32(struct ipt_u32 *data)
+{
+       unsigned int testind;
+
+       for (testind=0; testind < data->ntests; testind++) {
+               if (testind) printf("&&");
+               {
+                       unsigned int i;
+
+                       printf("0x%x", data->tests[testind].location[0].number);
+                       for (i = 1; i < data->tests[testind].nnums; i++) {
+                               switch (data->tests[testind].location[i].nextop) {
+                               case IPT_U32_AND: printf("&"); break;
+                               case IPT_U32_LEFTSH: printf("<<"); break;
+                               case IPT_U32_RIGHTSH: printf(">>"); break;
+                               case IPT_U32_AT: printf("@"); break;
+                               }
+                               printf("0x%x", data->tests[testind].location[i].number);
+                       }
+                       printf("=");
+                       for (i = 0; i < data->tests[testind].nvalues; i++) {
+                               if (i) printf(",");
+                               if (data->tests[testind].value[i].min
+                                   == data->tests[testind].value[i].max)
+                                       printf("0x%x", data->tests[testind].value[i].min);
+                               else printf("0x%x:0x%x", data->tests[testind].value[i].min,
+                                           data->tests[testind].value[i].max);
+                       }
+               }
+       }
+       printf(" ");
+}
+
+/* string_to_number is not quite what we need here ... */
+u_int32_t parse_number(char **s, int pos)
+{
+       u_int32_t number;
+       char *end;
+       errno = 0;
+
+       number = strtol(*s, &end, 0);
+       if (end == *s)
+               exit_error(PARAMETER_PROBLEM, 
+                          "u32: at char %d expected number", pos);
+       if (errno)
+               exit_error(PARAMETER_PROBLEM, 
+                          "u32: at char %d error reading number", pos);
+       *s = end;
+       return number;
+}
+
+/* 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_u32 *data = (struct ipt_u32 *)(*match)->data;
+       char *arg = argv[optind-1]; /* the argument string */
+       char *start = arg;
+       int state=0, testind=0, locind=0, valind=0;
+
+       if (c != '1') return 0;
+       /* states: 0 = looking for numbers and operations, 1 = looking for ranges */
+       while (1) { /* read next operand/number or range */
+               while (isspace(*arg)) 
+                       arg++;  /* skip white space */
+               if (! *arg) { /* end of argument found */
+                       if (state == 0)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: input ended in location spec");
+                       if (valind == 0)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: test ended with no value spec");
+                       data->tests[testind].nnums = locind;
+                       data->tests[testind].nvalues = valind;
+                       testind++;
+                       data->ntests=testind;
+                       if (testind > U32MAXSIZE)
+                               exit_error(PARAMETER_PROBLEM, 
+                                          "u32: at char %d too many &&'s",
+                                          arg-start);
+                       /* debugging 
+                          print_u32(data);printf("\n");
+                          exit_error(PARAMETER_PROBLEM, "debugging output done"); */
+                       return 1;
+               }
+               if (state == 0) {
+                       /* reading location: read a number if nothing read yet,
+                          otherwise either op number or = to end location spec */       
+                       if (*arg == '=') {
+                               if (locind == 0)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d location spec missing", arg-start);
+                               else {
+                                       arg++; 
+                                       state=1;
+                               }
+                       }
+                       else {
+                               if (locind) { /* need op before number */
+                                       if (*arg == '&') {
+                                               data->tests[testind].location[locind].nextop = IPT_U32_AND;
+                                       }
+                                       else if (*arg == '<') {
+                                               arg++;
+                                               if (*arg != '<')
+                                                       exit_error(PARAMETER_PROBLEM,
+                                                                  "u32: at char %d a second < expected", arg-start);
+                                               data->tests[testind].location[locind].nextop = IPT_U32_LEFTSH;
+                                       }
+                                       else if (*arg == '>') {
+                                               arg++;
+                                               if (*arg != '>')
+                                                       exit_error(PARAMETER_PROBLEM,
+                                                                  "u32: at char %d a second > expected", arg-start);
+                                               data->tests[testind].location[locind].nextop = IPT_U32_RIGHTSH;
+                                       }
+                                       else if (*arg == '@') {
+                                               data->tests[testind].location[locind].nextop = IPT_U32_AT;
+                                       }
+                                       else exit_error(PARAMETER_PROBLEM,
+                                                       "u32: at char %d operator expected", arg-start);
+                                       arg++;
+                               }
+                               /* now a number; string_to_number skips white space? */
+                               data->tests[testind].location[locind].number =
+                                       parse_number(&arg, arg-start);
+                               locind++;
+                               if (locind > U32MAXSIZE)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d too many operators", arg-start);
+                       }
+               }
+               else {
+                       /* state 1 - reading values: read a range if nothing read yet,
+                          otherwise either ,range or && to end test spec */
+                       if (*arg == '&') {
+                               arg++;
+                               if (*arg != '&')
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d a second & expected", arg-start);
+                               if (valind == 0)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d value spec missing", arg-start);
+                               else {
+                                       data->tests[testind].nnums = locind;
+                                       data->tests[testind].nvalues = valind;
+                                       testind++;
+                                       if (testind > U32MAXSIZE)
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "u32: at char %d too many &&'s", arg-start);
+                                       arg++; state=0; locind=0; valind=0;
+                               }
+                       }
+                       else { /* read value range */
+                               if (valind) { /* need , before number */
+                                       if (*arg != ',')
+                                               exit_error(PARAMETER_PROBLEM,
+                                                          "u32: at char %d expected , or &&", arg-start);
+                                       arg++;
+                               }
+                               data->tests[testind].value[valind].min = parse_number(&arg, arg-start);
+                               while (isspace(*arg)) 
+                                       arg++;  /* another place white space could be */
+                               if (*arg==':') {
+                                       arg++;
+                                       data->tests[testind].value[valind].max
+                                               = parse_number(&arg, arg-start);
+                               }
+                               else data->tests[testind].value[valind].max
+                                            = data->tests[testind].value[valind].min;
+                               valind++;
+                               if (valind > U32MAXSIZE)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "u32: at char %d too many ,'s", arg-start);
+                       }
+               }
+       }
+}
+
+/* Final check; must specify something. */
+static void
+final_check(unsigned int flags)
+{
+}
+
+/* Prints out the matchinfo. */
+static void
+print(const struct ipt_ip *ip,
+      const struct ipt_entry_match *match,
+      int numeric)
+{
+       printf("u32 ");
+       print_u32((struct ipt_u32 *)match->data);
+}
+
+/* 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("--u32 ");
+       print_u32((struct ipt_u32 *)match->data);
+}
+
+struct iptables_match u32
+= { NULL,
+    "u32",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_u32)),
+    IPT_ALIGN(sizeof(struct ipt_u32)),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    &print,
+    &save,
+    opts
+};
+
+void
+_init(void)
+{
+       register_match(&u32);
+}
diff --git a/extensions/libipt_udp.c b/extensions/libipt_udp.c
new file mode 100644 (file)
index 0000000..ccea210
--- /dev/null
@@ -0,0 +1,256 @@
+/* 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",
+IPTABLES_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)
+{
+       unsigned int portnum;
+
+       if (string_to_number(port, 0, 65535, &portnum) != -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;
+
+               if (ports[0] > ports[1])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "invalid portrange (min > max)");
+       }
+       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");
+               check_inverse(optarg, &invert, &optind, 0);
+               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");
+               check_inverse(optarg, &invert, &optind, 0);
+               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 ",
+                              udpinfo->spts[0],
+                              udpinfo->spts[1]);
+               else
+                       printf("--sport %u ",
+                              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 ",
+                              udpinfo->dpts[0],
+                              udpinfo->dpts[1]);
+               else
+                       printf("--dport %u ",
+                              udpinfo->dpts[0]);
+       }
+}
+
+static
+struct iptables_match udp
+= { NULL,
+    "udp",
+    IPTABLES_VERSION,
+    IPT_ALIGN(sizeof(struct ipt_udp)),
+    IPT_ALIGN(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 (file)
index 0000000..16cc0a5
--- /dev/null
@@ -0,0 +1,63 @@
+/* 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", IPTABLES_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)
+{
+}
+
+static
+struct iptables_match unclean
+= { NULL,
+    "unclean",
+    IPTABLES_VERSION,
+    IPT_ALIGN(0),
+    IPT_ALIGN(0),
+    &help,
+    &init,
+    &parse,
+    &final_check,
+    NULL, /* print */
+    NULL, /* save */
+    opts
+};
+
+void _init(void)
+{
+       register_match(&unclean);
+}
diff --git a/include/ip6tables.h b/include/ip6tables.h
new file mode 100644 (file)
index 0000000..b098f81
--- /dev/null
@@ -0,0 +1,135 @@
+#ifndef _IP6TABLES_USER_H
+#define _IP6TABLES_USER_H
+
+#include "iptables_common.h"
+#include "libiptc/libip6tc.h"
+
+/* Include file for additions: new matches and targets. */
+struct ip6tables_match
+{
+       struct ip6tables_match *next;
+
+       ip6t_chainlabel name;
+
+       const char *version;
+
+       /* Size of match data. */
+       size_t size;
+
+       /* Size of match data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the match. */
+       void (*init)(struct ip6t_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 ip6t_entry *entry,
+                    unsigned int *nfcache,
+                    struct ip6t_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 ip6t_ip6 *ip,
+                     const struct ip6t_entry_match *match, int numeric);
+
+       /* Saves the union ipt_matchinfo in parsable form to stdout. */
+       void (*save)(const struct ip6t_ip6 *ip,
+                    const struct ip6t_entry_match *match);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ip6t_entry_match *m;
+       unsigned int mflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct ip6tables_target
+{
+       struct ip6tables_target *next;
+       
+       ip6t_chainlabel name;
+
+       const char *version;
+
+       /* Size of target data. */
+       size_t size;
+
+       /* Size of target data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the target. */
+       void (*init)(struct ip6t_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 ip6t_entry *entry,
+                    struct ip6t_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 ip6t_ip6 *ip,
+                     const struct ip6t_entry_target *target, int numeric);
+
+       /* Saves the targinfo in parsable form to stdout. */
+       void (*save)(const struct ip6t_ip6 *ip,
+                    const struct ip6t_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 ip6t_entry_target *t;
+       unsigned int tflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+extern int line;
+
+/* Your shared library should call one of these. */
+extern void register_match6(struct ip6tables_match *me);
+extern void register_target6(struct ip6tables_target *me);
+
+extern int do_command6(int argc, char *argv[], char **table,
+                      ip6tc_handle_t *handle);
+/* Keeping track of external matches and targets: linked lists. */
+extern struct ip6tables_match *ip6tables_matches;
+extern struct ip6tables_target *ip6tables_targets;
+
+enum ip6t_tryload {
+       DONT_LOAD,
+       TRY_LOAD,
+       LOAD_MUST_SUCCEED
+};
+
+extern struct ip6tables_target *find_target(const char *name, enum ip6t_tryload);
+extern struct ip6tables_match *find_match(const char *name, enum ip6t_tryload);
+
+extern int for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *), int verbose, int builtinstoo, ip6tc_handle_t *handle);
+extern int flush_entries(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
+extern int delete_chain(const ip6t_chainlabel chain, int verbose, ip6tc_handle_t *handle);
+extern int ip6tables_insmod(const char *modname, const char *modprobe);
+
+#endif /*_IP6TABLES_USER_H*/
diff --git a/include/iptables.h b/include/iptables.h
new file mode 100644 (file)
index 0000000..205984e
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef _IPTABLES_USER_H
+#define _IPTABLES_USER_H
+
+#include "iptables_common.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;
+
+       /* Size of match data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the match. */
+       void (*init)(struct ipt_entry_match *m, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ipt_entry *entry,
+                    unsigned int *nfcache,
+                    struct ipt_entry_match **match);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the match iff non-NULL: put space at end */
+       void (*print)(const struct ipt_ip *ip,
+                     const struct ipt_entry_match *match, int numeric);
+
+       /* Saves the match info in parsable form to stdout. */
+       void (*save)(const struct ipt_ip *ip,
+                    const struct ipt_entry_match *match);
+
+       /* Pointer to list of extra command-line options */
+       const struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ipt_entry_match *m;
+       unsigned int mflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+struct iptables_target
+{
+       struct iptables_target *next;
+
+       ipt_chainlabel name;
+
+       const char *version;
+
+       /* Size of target data. */
+       size_t size;
+
+       /* Size of target data relevent for userspace comparison purposes */
+       size_t userspacesize;
+
+       /* Function which prints out usage message. */
+       void (*help)(void);
+
+       /* Initialize the target. */
+       void (*init)(struct ipt_entry_target *t, unsigned int *nfcache);
+
+       /* Function which parses command options; returns true if it
+           ate an option */
+       int (*parse)(int c, char **argv, int invert, unsigned int *flags,
+                    const struct ipt_entry *entry,
+                    struct ipt_entry_target **target);
+
+       /* Final check; exit if not ok. */
+       void (*final_check)(unsigned int flags);
+
+       /* Prints out the target iff non-NULL: put space at end */
+       void (*print)(const struct ipt_ip *ip,
+                     const struct ipt_entry_target *target, int numeric);
+
+       /* Saves the targinfo in parsable form to stdout. */
+       void (*save)(const struct ipt_ip *ip,
+                    const struct ipt_entry_target *target);
+
+       /* Pointer to list of extra command-line options */
+       struct option *extra_opts;
+
+       /* Ignore these men behind the curtain: */
+       unsigned int option_offset;
+       struct ipt_entry_target *t;
+       unsigned int tflags;
+       unsigned int used;
+#ifdef NO_SHARED_LIBS
+       unsigned int loaded; /* simulate loading so options are merged properly */
+#endif
+};
+
+extern int line;
+
+/* Your shared library should call one of these. */
+extern void register_match(struct iptables_match *me);
+extern void register_target(struct iptables_target *me);
+
+extern struct in_addr *dotted_to_addr(const char *dotted);
+extern char *addr_to_dotted(const struct in_addr *addrp);
+extern char *addr_to_anyname(const struct in_addr *addr);
+extern char *mask_to_dotted(const struct in_addr *mask);
+
+extern void parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                      struct in_addr *maskp, unsigned int *naddrs);
+extern u_int16_t parse_protocol(const char *s);
+
+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;
+
+enum ipt_tryload {
+       DONT_LOAD,
+       TRY_LOAD,
+       LOAD_MUST_SUCCEED
+};
+
+extern struct iptables_target *find_target(const char *name, enum ipt_tryload);
+extern struct iptables_match *find_match(const char *name, enum ipt_tryload);
+
+extern int delete_chain(const ipt_chainlabel chain, int verbose,
+                       iptc_handle_t *handle);
+extern int flush_entries(const ipt_chainlabel chain, int verbose, 
+                       iptc_handle_t *handle);
+extern int for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+               int verbose, int builtinstoo, iptc_handle_t *handle);
+#endif /*_IPTABLES_USER_H*/
diff --git a/include/iptables_common.h b/include/iptables_common.h
new file mode 100644 (file)
index 0000000..25f23c3
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _IPTABLES_COMMON_H
+#define _IPTABLES_COMMON_H
+/* Shared definitions between ipv4 and ipv6. */
+
+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, int *optind, int argc);
+extern int string_to_number(const char *, 
+                           unsigned int, 
+                           unsigned int,
+                           unsigned int *);
+extern int iptables_insmod(const char *modname, const char *modprobe);
+void exit_error(enum exittype, char *, ...)__attribute__((noreturn,
+                                                         format(printf,2,3)));
+extern const char *program_name, *program_version;
+
+#ifdef NO_SHARED_LIBS
+# ifdef _INIT
+#  define _init _INIT
+# endif
+  extern void init_extensions(void);
+#endif
+
+#endif /*_IPTABLES_COMMON_H*/
diff --git a/include/libipq/ip_queue_64.h b/include/libipq/ip_queue_64.h
new file mode 100644 (file)
index 0000000..b0c3222
--- /dev/null
@@ -0,0 +1,62 @@
+/* Redefine everything for a 64-bit kernel on 32-bit userspace */
+
+/* Based on ip_queue.h by (C) 2000 James Morris, this code is GPL. */
+#ifndef _IP_QUEUE_H
+#define _IP_QUEUE_H
+
+#include <net/if.h>
+#include <sys/types.h>
+
+/* Messages sent from kernel */
+typedef struct ipq_packet_msg {
+       u_int64_t packet_id;    /* ID of queued packet */
+       u_int64_t mark;         /* Netfilter mark value */
+       int64_t timestamp_sec;  /* Packet arrival time (seconds) */
+       int64_t timestamp_usec; /* Packet arrvial time (+useconds) */
+       unsigned int hook;              /* Netfilter hook we rode in on */
+       char indev_name[IFNAMSIZ];      /* Name of incoming interface */
+       char outdev_name[IFNAMSIZ];     /* Name of outgoing interface */
+       unsigned short hw_protocol;     /* Hardware protocol (network order) */
+       unsigned short hw_type;         /* Hardware type */
+       unsigned char hw_addrlen;       /* Hardware address length */
+       unsigned char hw_addr[8];       /* Hardware address */
+       u_int64_t data_len;             /* Length of packet data */
+       unsigned char payload[0];       /* Optional packet data */
+} ipq_packet_msg_t;
+
+/* Messages sent from userspace */
+typedef struct ipq_mode_msg {
+       unsigned char value;            /* Requested mode */
+       u_int64_t range;                /* Optional range of packet requested */
+} ipq_mode_msg_t;
+
+typedef struct ipq_verdict_msg {
+       unsigned int value;             /* Verdict to hand to netfilter */
+       u_int64_t id;           /* Packet ID for this verdict */
+       u_int64_t data_len;             /* Length of replacement data */
+       unsigned char payload[0];       /* Optional replacement packet */
+} ipq_verdict_msg_t;
+
+typedef struct ipq_peer_msg {
+       union {
+               ipq_verdict_msg_t verdict;
+               ipq_mode_msg_t mode;
+       } msg;
+} ipq_peer_msg_t;
+
+/* Packet delivery modes */
+enum {
+       IPQ_COPY_NONE,          /* Initial mode, packets are dropped */
+       IPQ_COPY_META,          /* Copy metadata */
+       IPQ_COPY_PACKET         /* Copy metadata + packet (range) */
+};
+#define IPQ_COPY_MAX IPQ_COPY_PACKET
+
+/* Types of messages */
+#define IPQM_BASE      0x10    /* standard netlink messages below this */
+#define IPQM_MODE      (IPQM_BASE + 1)         /* Mode request from peer */
+#define IPQM_VERDICT   (IPQM_BASE + 2)         /* Verdict from peer */ 
+#define IPQM_PACKET    (IPQM_BASE + 3)         /* Packet from kernel */
+#define IPQM_MAX       (IPQM_BASE + 4)
+
+#endif /*_IP_QUEUE_H*/
diff --git a/include/libipq/libipq.h b/include/libipq/libipq.h
new file mode 100644 (file)
index 0000000..f60fc4b
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * libipq.h
+ *
+ * IPQ library for userspace.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#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>
+
+#ifdef KERNEL_64_USERSPACE_32
+#include "ip_queue_64.h"
+typedef u_int64_t ipq_id_t;
+#else
+#include <linux/netfilter_ipv4/ip_queue.h>
+typedef unsigned long ipq_id_t;
+#endif
+
+#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, u_int32_t protocol);
+
+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,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf);
+
+int ipq_ctl(const struct ipq_handle *h, int request, ...);
+
+char *ipq_errstr(void);
+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 (file)
index 0000000..18861fe
--- /dev/null
@@ -0,0 +1,27 @@
+/* This is the userspace/kernel interface for Generic IP Chains,
+   required for libc6. */
+#ifndef _FWCHAINS_KERNEL_HEADERS_H
+#define _FWCHAINS_KERNEL_HEADERS_H
+
+#include <limits.h>
+
+#if defined(__GLIBC__) && __GLIBC__ == 2
+#include <netinet/ip.h>
+#include <netinet/in.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>
+#include <linux/types.h>
+#include <linux/in6.h>
+#endif
+#endif
diff --git a/include/libiptc/libip6tc.h b/include/libiptc/libip6tc.h
new file mode 100644 (file)
index 0000000..7a247c4
--- /dev/null
@@ -0,0 +1,154 @@
+#ifndef _LIBIP6TC_H
+#define _LIBIP6TC_H
+/* Library which manipulates firewall rules. Version 0.2. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#ifndef IP6T_MIN_ALIGN
+#define IP6T_MIN_ALIGN (__alignof__(struct ip6t_entry))
+#endif
+#define IP6T_ALIGN(s) (((s) + (IP6T_MIN_ALIGN-1)) & ~(IP6T_MIN_ALIGN-1))
+
+typedef char ip6t_chainlabel[32];
+
+#define IP6TC_LABEL_ACCEPT "ACCEPT"
+#define IP6TC_LABEL_DROP "DROP"
+#define IP6TC_LABEL_QUEUE   "QUEUE"
+#define IP6TC_LABEL_RETURN "RETURN"
+
+/* Transparent handle type. */
+typedef struct ip6tc_handle *ip6tc_handle_t;
+
+/* Does this chain exist? */
+int ip6tc_is_chain(const char *chain, const ip6tc_handle_t handle);
+
+/* Take a snapshot of the rules. Returns NULL on error. */
+ip6tc_handle_t ip6tc_init(const char *tablename);
+
+/* Cleanup after ip6tc_init(). */
+void ip6tc_free(ip6tc_handle_t *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *ip6tc_first_chain(ip6tc_handle_t *handle);
+const char *ip6tc_next_chain(ip6tc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ip6t_entry *ip6tc_first_rule(const char *chain,
+                                         ip6tc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ip6t_entry *ip6tc_next_rule(const struct ip6t_entry *prev,
+                                        ip6tc_handle_t *handle);
+
+/* Returns a pointer to the target name of this position. */
+const char *ip6tc_get_target(const struct ip6t_entry *e,
+                            ip6tc_handle_t *handle);
+
+/* Is this a built-in chain? */
+int ip6tc_builtin(const char *chain, const ip6tc_handle_t handle);
+
+/* Get the policy of a given built-in chain */
+const char *ip6tc_get_policy(const char *chain,
+                            struct ip6t_counters *counters,
+                            ip6tc_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 `fw' in chain `chain' into position `rulenum'. */
+int ip6tc_insert_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *e,
+                      unsigned int rulenum,
+                      ip6tc_handle_t *handle);
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int ip6tc_replace_entry(const ip6t_chainlabel chain,
+                       const struct ip6t_entry *e,
+                       unsigned int rulenum,
+                       ip6tc_handle_t *handle);
+
+/* Append entry `fw' to chain `chain'. Equivalent to insert with
+   rulenum = length of chain. */
+int ip6tc_append_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *e,
+                      ip6tc_handle_t *handle);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int ip6tc_delete_entry(const ip6t_chainlabel chain,
+                      const struct ip6t_entry *origfw,
+                      unsigned char *matchmask,
+                      ip6tc_handle_t *handle);
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int ip6tc_delete_num_entry(const ip6t_chainlabel chain,
+                          unsigned int rulenum,
+                          ip6tc_handle_t *handle);
+
+/* Check the packet `fw' on chain `chain'. Returns the verdict, or
+   NULL and sets errno. */
+const char *ip6tc_check_packet(const ip6t_chainlabel chain,
+                              struct ip6t_entry *,
+                              ip6tc_handle_t *handle);
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int ip6tc_flush_entries(const ip6t_chainlabel chain,
+                       ip6tc_handle_t *handle);
+
+/* Zeroes the counters in a chain. */
+int ip6tc_zero_entries(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Creates a new chain. */
+int ip6tc_create_chain(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Deletes a chain. */
+int ip6tc_delete_chain(const ip6t_chainlabel chain,
+                      ip6tc_handle_t *handle);
+
+/* Renames a chain. */
+int ip6tc_rename_chain(const ip6t_chainlabel oldname,
+                      const ip6t_chainlabel newname,
+                      ip6tc_handle_t *handle);
+
+/* Sets the policy on a built-in chain. */
+int ip6tc_set_policy(const ip6t_chainlabel chain,
+                    const ip6t_chainlabel policy,
+                    struct ip6t_counters *counters,
+                    ip6tc_handle_t *handle);
+
+/* Get the number of references to this chain */
+int ip6tc_get_references(unsigned int *ref, const ip6t_chainlabel chain,
+                        ip6tc_handle_t *handle);
+
+/* read packet and byte counters for a specific rule */
+struct ip6t_counters *ip6tc_read_counter(const ip6t_chainlabel chain,
+                                       unsigned int rulenum,
+                                       ip6tc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int ip6tc_zero_counter(const ip6t_chainlabel chain,
+                      unsigned int rulenum,
+                      ip6tc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int ip6tc_set_counter(const ip6t_chainlabel chain,
+                     unsigned int rulenum,
+                     struct ip6t_counters *counters,
+                     ip6tc_handle_t *handle);
+
+/* Makes the actual changes. */
+int ip6tc_commit(ip6tc_handle_t *handle);
+
+/* Get raw socket. */
+int ip6tc_get_raw_socket();
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *ip6tc_strerror(int err);
+
+/* Return prefix length, or -1 if not contiguous */
+int ipv6_prefix_length(const struct in6_addr *a);
+
+#endif /* _LIBIP6TC_H */
diff --git a/include/libiptc/libiptc.h b/include/libiptc/libiptc.h
new file mode 100644 (file)
index 0000000..50765d9
--- /dev/null
@@ -0,0 +1,166 @@
+#ifndef _LIBIPTC_H
+#define _LIBIPTC_H
+/* Library which manipulates filtering rules. */
+
+#include <libiptc/ipt_kernel_headers.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef IPT_MIN_ALIGN
+/* ipt_entry has pointers and u_int64_t's in it, so if you align to
+   it, you'll also align to any crazy matches and targets someone
+   might write */
+#define IPT_MIN_ALIGN (__alignof__(struct ipt_entry))
+#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);
+
+/* Cleanup after iptc_init(). */
+void iptc_free(iptc_handle_t *h);
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *iptc_first_chain(iptc_handle_t *handle);
+const char *iptc_next_chain(iptc_handle_t *handle);
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const struct ipt_entry *iptc_first_rule(const char *chain,
+                                       iptc_handle_t *handle);
+
+/* Returns NULL when rules run out. */
+const struct ipt_entry *iptc_next_rule(const struct ipt_entry *prev,
+                                      iptc_handle_t *handle);
+
+/* Returns a pointer to the target name of this entry. */
+const char *iptc_get_target(const struct ipt_entry *e,
+                           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', subject to
+   matchmask (array of length == origfw) */
+int iptc_delete_entry(const ipt_chainlabel chain,
+                     const struct ipt_entry *origfw,
+                     unsigned char *matchmask,
+                     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,
+                   struct ipt_counters *counters,
+                   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);
+
+/* read packet and byte counters for a specific rule */
+struct ipt_counters *iptc_read_counter(const ipt_chainlabel chain,
+                                      unsigned int rulenum,
+                                      iptc_handle_t *handle);
+
+/* zero packet and byte counters for a specific rule */
+int iptc_zero_counter(const ipt_chainlabel chain,
+                     unsigned int rulenum,
+                     iptc_handle_t *handle);
+
+/* set packet and byte counters for a specific rule */
+int iptc_set_counter(const ipt_chainlabel chain,
+                    unsigned int rulenum,
+                    struct ipt_counters *counters,
+                    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);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _LIBIPTC_H */
diff --git a/include/libipulog/libipulog.h b/include/libipulog/libipulog.h
new file mode 100644 (file)
index 0000000..3f4cc2c
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _LIBIPULOG_H
+#define _LIBIPULOG_H
+
+/* libipulog.h,v 1.3 2001/05/21 19:15:16 laforge Exp */
+
+#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 <net/if.h>
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+
+/* FIXME: glibc sucks */
+#ifndef MSG_TRUNC 
+#define MSG_TRUNC      0x20
+#endif
+
+struct ipulog_handle;
+
+u_int32_t ipulog_group2gmask(u_int32_t group);
+
+struct ipulog_handle *ipulog_create_handle(u_int32_t gmask);
+
+void ipulog_destroy_handle(struct ipulog_handle *h);
+
+ssize_t ipulog_read(struct ipulog_handle *h,
+                   unsigned char *buf, size_t len, int timeout);
+
+ulog_packet_msg_t *ipulog_get_packet(struct ipulog_handle *h,
+                                    const unsigned char *buf,
+                                    size_t len);
+
+void ipulog_perror(const char *s);
+
+#endif /* _LIBULOG_H */
diff --git a/include/linux/netfilter_ipv4/ipt_CLASSIFY.h b/include/linux/netfilter_ipv4/ipt_CLASSIFY.h
new file mode 100644 (file)
index 0000000..7596e3d
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _IPT_CLASSIFY_H
+#define _IPT_CLASSIFY_H
+
+struct ipt_classify_target_info {
+       u_int32_t priority;
+};
+
+#endif /*_IPT_CLASSIFY_H */
diff --git a/include/linux/netfilter_ipv4/ipt_CONNMARK.h b/include/linux/netfilter_ipv4/ipt_CONNMARK.h
new file mode 100644 (file)
index 0000000..b237a4f
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _IPT_CONNMARK_H_target
+#define _IPT_CONNMARK_H_target
+
+enum {
+       IPT_CONNMARK_SET = 0,
+       IPT_CONNMARK_SAVE,
+       IPT_CONNMARK_RESTORE
+};
+
+struct ipt_connmark_target_info {
+       unsigned long mark;
+       u_int8_t mode;
+};
+
+#endif /*_IPT_CONNMARK_H_target*/
diff --git a/include/linux/netfilter_ipv4/ipt_DSCP.h b/include/linux/netfilter_ipv4/ipt_DSCP.h
new file mode 100644 (file)
index 0000000..678edee
--- /dev/null
@@ -0,0 +1,20 @@
+/* iptables module for setting the IPv4 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * based on ipt_FTOS.c (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * Id: ipt_DSCP.h,v 1.7 2002/03/14 12:03:13 laforge Exp
+*/
+#ifndef _IPT_DSCP_TARGET_H
+#define _IPT_DSCP_TARGET_H
+#include <linux/netfilter_ipv4/ipt_dscp.h>
+
+/* target info */
+struct ipt_DSCP_info {
+       u_int8_t dscp;
+};
+
+#endif /* _IPT_DSCP_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_ECN.h b/include/linux/netfilter_ipv4/ipt_ECN.h
new file mode 100644 (file)
index 0000000..a711cd1
--- /dev/null
@@ -0,0 +1,31 @@
+/* Header file for iptables ipt_ECN target
+ *
+ * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * Id: ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp
+*/
+#ifndef _IPT_ECN_TARGET_H
+#define _IPT_ECN_TARGET_H
+#include <linux/netfilter_ipv4/ipt_DSCP.h>
+
+#define IPT_ECN_IP_MASK        (~IPT_DSCP_MASK)
+
+#define IPT_ECN_OP_SET_IP      0x01    /* set ECN bits of IPv4 header */
+#define IPT_ECN_OP_SET_ECE     0x10    /* set ECE bit of TCP header */
+#define IPT_ECN_OP_SET_CWR     0x20    /* set CWR bit of TCP header */
+
+#define IPT_ECN_OP_MASK                0xce
+
+struct ipt_ECN_info {
+       u_int8_t operation;     /* bitset of operations */
+       u_int8_t ip_ect;        /* ECT codepoint of IPv4 header, pre-shifted */
+       union {
+               struct {
+                       u_int8_t ece:1, cwr:1; /* TCP ECT bits */
+               } tcp;
+       } proto;
+};
+
+#endif /* _IPT_ECN_TARGET_H */
diff --git a/include/linux/netfilter_ipv4/ipt_FTOS.h b/include/linux/netfilter_ipv4/ipt_FTOS.h
new file mode 100644 (file)
index 0000000..3b04559
--- /dev/null
@@ -0,0 +1,16 @@
+/* Set TOS field in header to any value
+ *
+ * (C) 2000 by Matthew G. Marsh <mgm@paktronix.com>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * ipt_FTOS.h borrowed heavily from ipt_TOS.h  11/09/2000
+*/
+#ifndef _IPT_FTOS_H
+#define _IPT_FTOS_H
+
+struct ipt_FTOS_info {
+       u_int8_t ftos;
+};
+
+#endif /*_IPT_FTOS_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_SAME.h b/include/linux/netfilter_ipv4/ipt_SAME.h
new file mode 100644 (file)
index 0000000..428b213
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _IPT_SAME_H
+#define _IPT_SAME_H
+
+#define IPT_SAME_MAX_RANGE     10
+
+#define IPT_SAME_NODST         0x01
+
+struct ipt_same_info
+{
+       unsigned char info;
+
+       unsigned int rangesize;
+
+       unsigned int ipnum;
+
+       u_int32_t *iparray;
+
+       /* hangs off end. */
+       struct ip_nat_range range[IPT_SAME_MAX_RANGE];
+};
+
+#endif /*_IPT_SAME_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TCPMSS.h b/include/linux/netfilter_ipv4/ipt_TCPMSS.h
new file mode 100644 (file)
index 0000000..aadb395
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IPT_TCPMSS_H
+#define _IPT_TCPMSS_H
+
+struct ipt_tcpmss_info {
+       u_int16_t mss;
+};
+
+#define IPT_TCPMSS_CLAMP_PMTU 0xffff
+
+#endif /*_IPT_TCPMSS_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_TTL.h b/include/linux/netfilter_ipv4/ipt_TTL.h
new file mode 100644 (file)
index 0000000..f669b8c
--- /dev/null
@@ -0,0 +1,19 @@
+/* TTL modification module for IP tables
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+enum {
+       IPT_TTL_SET = 0,
+       IPT_TTL_INC,
+       IPT_TTL_DEC
+};
+
+#define IPT_TTL_MAXMODE        IPT_TTL_DEC
+
+struct ipt_TTL_info {
+       u_int8_t        mode;
+       u_int8_t        ttl;
+};
+#endif
diff --git a/include/linux/netfilter_ipv4/ipt_ULOG.h b/include/linux/netfilter_ipv4/ipt_ULOG.h
new file mode 100644 (file)
index 0000000..86a99ee
--- /dev/null
@@ -0,0 +1,46 @@
+/* Header file for IP tables userspace logging, Version 1.8 
+ *
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * 
+ * Distributed under the terms of GNU GPL */
+
+#ifndef _IPT_ULOG_H
+#define _IPT_ULOG_H
+
+#ifndef NETLINK_NFLOG
+#define NETLINK_NFLOG  5
+#endif
+
+#define ULOG_MAC_LEN   80
+#define ULOG_PREFIX_LEN        32
+
+#define ULOG_MAX_QLEN  50
+/* Why 50? Well... there is a limit imposed by the slab cache 131000
+ * bytes. So the multipart netlink-message has to be < 131000 bytes.
+ * Assuming a standard ethernet-mtu of 1500, we could define this up
+ * to 80... but even 50 seems to be big enough. */
+
+/* private data structure for each rule with a ULOG target */
+struct ipt_ulog_info {
+       unsigned int nl_group;
+       size_t copy_range;
+       size_t qthreshold;
+       char prefix[ULOG_PREFIX_LEN];
+};
+
+/* Format of the ULOG packets passed through netlink */
+typedef struct ulog_packet_msg {
+       unsigned long mark;
+       long timestamp_sec;
+       long timestamp_usec;
+       unsigned int hook;
+       char indev_name[IFNAMSIZ];
+       char outdev_name[IFNAMSIZ];
+       size_t data_len;
+       char prefix[ULOG_PREFIX_LEN];
+       unsigned char mac_len;
+       unsigned char mac[ULOG_MAC_LEN];
+       unsigned char payload[0];
+} ulog_packet_msg_t;
+
+#endif /*_IPT_ULOG_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_ah.h b/include/linux/netfilter_ipv4/ipt_ah.h
new file mode 100644 (file)
index 0000000..7b9a2ac
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IPT_AH_H
+#define _IPT_AH_H
+
+struct ipt_ah
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_ah. */
+#define IPT_AH_INV_SPI         0x01    /* Invert the sense of spi. */
+#define IPT_AH_INV_MASK        0x01    /* All possible flags. */
+
+#endif /*_IPT_AH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_connlimit.h b/include/linux/netfilter_ipv4/ipt_connlimit.h
new file mode 100644 (file)
index 0000000..d99193b
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _IPT_CONNLIMIT_H
+#define _IPT_CONNLIMIT_H
+
+struct ipt_connlimit_data;
+
+struct ipt_connlimit_info {
+       int limit;
+       int inverse;
+       u_int32_t mask;
+       struct ipt_connlimit_data *data;
+};
+#endif /* _IPT_CONNLIMIT_H */
diff --git a/include/linux/netfilter_ipv4/ipt_connmark.h b/include/linux/netfilter_ipv4/ipt_connmark.h
new file mode 100644 (file)
index 0000000..e19742a
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_CONNMARK_H
+#define _IPT_CONNMARK_H
+
+struct ipt_connmark_info {
+       unsigned long mark, mask;
+       u_int8_t invert;
+};
+
+#endif /*_IPT_CONNMARK_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_conntrack.h b/include/linux/netfilter_ipv4/ipt_conntrack.h
new file mode 100644 (file)
index 0000000..eb97456
--- /dev/null
@@ -0,0 +1,39 @@
+/* Header file for kernel module to match connection tracking information.
+ * GPL (C) 2001  Marc Boucher (marc@mbsi.ca).
+ */
+
+#ifndef _IPT_CONNTRACK_H
+#define _IPT_CONNTRACK_H
+
+#define IPT_CONNTRACK_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP_CT_IS_REPLY+1))
+#define IPT_CONNTRACK_STATE_INVALID (1 << 0)
+
+#define IPT_CONNTRACK_STATE_SNAT (1 << (IP_CT_NUMBER + 1))
+#define IPT_CONNTRACK_STATE_DNAT (1 << (IP_CT_NUMBER + 2))
+
+/* flags, invflags: */
+#define IPT_CONNTRACK_STATE    0x01
+#define IPT_CONNTRACK_PROTO    0x02
+#define IPT_CONNTRACK_ORIGSRC  0x04
+#define IPT_CONNTRACK_ORIGDST  0x08
+#define IPT_CONNTRACK_REPLSRC  0x10
+#define IPT_CONNTRACK_REPLDST  0x20
+#define IPT_CONNTRACK_STATUS   0x40
+#define IPT_CONNTRACK_EXPIRES  0x80
+
+struct ipt_conntrack_info
+{
+       unsigned int statemask, statusmask;
+
+       struct ip_conntrack_tuple tuple[IP_CT_DIR_MAX];
+       struct in_addr sipmsk[IP_CT_DIR_MAX], dipmsk[IP_CT_DIR_MAX];
+
+       unsigned long expires_min, expires_max;
+
+       /* Flags word */
+       u_int8_t flags;
+       /* Inverse flags */
+       u_int8_t invflags;
+};
+#endif /*_IPT_CONNTRACK_H*/
+
diff --git a/include/linux/netfilter_ipv4/ipt_dscp.h b/include/linux/netfilter_ipv4/ipt_dscp.h
new file mode 100644 (file)
index 0000000..b6c59bd
--- /dev/null
@@ -0,0 +1,23 @@
+/* iptables module for matching the IPv4 DSCP field
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * See RFC2474 for a description of the DSCP field within the IP Header.
+ *
+ * Id: ipt_dscp.h,v 1.3 2002/08/05 19:00:21 laforge Exp
+*/
+#ifndef _IPT_DSCP_H
+#define _IPT_DSCP_H
+
+#define IPT_DSCP_MASK  0xfc    /* 11111100 */
+#define IPT_DSCP_SHIFT 2
+#define IPT_DSCP_MAX   0x3f    /* 00111111 */
+
+/* match info */
+struct ipt_dscp_info {
+       u_int8_t dscp;
+       u_int8_t invert;
+};
+
+#endif /* _IPT_DSCP_H */
diff --git a/include/linux/netfilter_ipv4/ipt_ecn.h b/include/linux/netfilter_ipv4/ipt_ecn.h
new file mode 100644 (file)
index 0000000..326c0c0
--- /dev/null
@@ -0,0 +1,33 @@
+/* iptables module for matching the ECN header in IPv4 and TCP header
+ *
+ * (C) 2002 Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ * 
+ * Id: ipt_ecn.h,v 1.4 2002/08/05 19:39:00 laforge Exp
+*/
+#ifndef _IPT_ECN_H
+#define _IPT_ECN_H
+#include <linux/netfilter_ipv4/ipt_dscp.h>
+
+#define IPT_ECN_IP_MASK        (~IPT_DSCP_MASK)
+
+#define IPT_ECN_OP_MATCH_IP    0x01
+#define IPT_ECN_OP_MATCH_ECE   0x10
+#define IPT_ECN_OP_MATCH_CWR   0x20
+
+#define IPT_ECN_OP_MATCH_MASK  0xce
+
+/* match info */
+struct ipt_ecn_info {
+       u_int8_t operation;
+       u_int8_t invert;
+       u_int8_t ip_ect;
+       union {
+               struct {
+                       u_int8_t ect;
+               } tcp;
+       } proto;
+};
+
+#endif /* _IPT_ECN_H */
diff --git a/include/linux/netfilter_ipv4/ipt_esp.h b/include/linux/netfilter_ipv4/ipt_esp.h
new file mode 100644 (file)
index 0000000..c782a83
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IPT_ESP_H
+#define _IPT_ESP_H
+
+struct ipt_esp
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_esp. */
+#define IPT_ESP_INV_SPI                0x01    /* Invert the sense of spi. */
+#define IPT_ESP_INV_MASK       0x01    /* All possible flags. */
+
+#endif /*_IPT_ESP_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_helper.h b/include/linux/netfilter_ipv4/ipt_helper.h
new file mode 100644 (file)
index 0000000..6f12ecb
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _IPT_HELPER_H
+#define _IPT_HELPER_H
+
+struct ipt_helper_info {
+       int invert;
+       char name[30];
+};
+#endif /* _IPT_HELPER_H */
diff --git a/include/linux/netfilter_ipv4/ipt_iprange.h b/include/linux/netfilter_ipv4/ipt_iprange.h
new file mode 100644 (file)
index 0000000..3ecb3bd
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IPT_IPRANGE_H
+#define _IPT_IPRANGE_H
+
+#define IPRANGE_SRC            0x01    /* Match source IP address */
+#define IPRANGE_DST            0x02    /* Match destination IP address */
+#define IPRANGE_SRC_INV                0x10    /* Negate the condition */
+#define IPRANGE_DST_INV                0x20    /* Negate the condition */
+
+struct ipt_iprange {
+       /* Inclusive: network order. */
+       u_int32_t min_ip, max_ip;
+};
+
+struct ipt_iprange_info
+{
+       struct ipt_iprange src;
+       struct ipt_iprange dst;
+
+       /* Flags from above */
+       u_int8_t flags;
+};
+
+#endif /* _IPT_IPRANGE_H */
diff --git a/include/linux/netfilter_ipv4/ipt_length.h b/include/linux/netfilter_ipv4/ipt_length.h
new file mode 100644 (file)
index 0000000..6e08852
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_LENGTH_H
+#define _IPT_LENGTH_H
+
+struct ipt_length_info {
+    u_int16_t  min, max;
+    u_int8_t   invert;
+};
+
+#endif /*_IPT_LENGTH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_physdev.h b/include/linux/netfilter_ipv4/ipt_physdev.h
new file mode 100644 (file)
index 0000000..7538c86
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _IPT_PHYSDEV_H
+#define _IPT_PHYSDEV_H
+
+#ifdef __KERNEL__
+#include <linux/if.h>
+#endif
+
+#define IPT_PHYSDEV_OP_IN              0x01
+#define IPT_PHYSDEV_OP_OUT             0x02
+#define IPT_PHYSDEV_OP_BRIDGED         0x04
+#define IPT_PHYSDEV_OP_ISIN            0x08
+#define IPT_PHYSDEV_OP_ISOUT           0x10
+#define IPT_PHYSDEV_OP_MASK            (0x20 - 1)
+
+struct ipt_physdev_info {
+       char physindev[IFNAMSIZ];
+       char in_mask[IFNAMSIZ];
+       char physoutdev[IFNAMSIZ];
+       char out_mask[IFNAMSIZ];
+       u_int8_t invert;
+       u_int8_t bitmask;
+};
+
+#endif /*_IPT_PHYSDEV_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_pkttype.h b/include/linux/netfilter_ipv4/ipt_pkttype.h
new file mode 100644 (file)
index 0000000..c189e94
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_PKTTYPE_H
+#define _IPT_PKTTYPE_H
+
+struct ipt_pkttype_info {
+       int     pkttype;
+       int     invert;
+};
+
+#endif /*_IPT_PKTTYPE_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_realm.h b/include/linux/netfilter_ipv4/ipt_realm.h
new file mode 100644 (file)
index 0000000..2357731
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_REALM_H
+#define _IPT_REALM_H
+
+struct ipt_realm_info {
+       u_int32_t id;
+       u_int32_t mask;
+       u_int8_t invert;
+};
+#endif /*_IPT_REALM_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_rpc.h b/include/linux/netfilter_ipv4/ipt_rpc.h
new file mode 100644 (file)
index 0000000..c204b7f
--- /dev/null
@@ -0,0 +1,35 @@
+/* RPC extension for IP netfilter matching, Version 2.2
+ * (C) 2000 by Marcelo Barbosa Lima <marcelo.lima@dcc.unicamp.br>
+ *     - original rpc tracking module
+ *     - "recent" connection handling for kernel 2.3+ netfilter
+ *
+ * (C) 2001 by Rusty Russell <rusty@rustcorp.com.au>
+ *     - upgraded conntrack modules to oldnat api - kernel 2.4.0+
+ *
+ * (C) 2002 by Ian (Larry) Latter <Ian.Latter@mq.edu.au>
+ *     - upgraded conntrack modules to newnat api - kernel 2.4.20+
+ *     - extended matching to support filtering on procedures
+ *
+ * ipt_rpc.h.c,v 2.2 2003/01/12 18:30:00
+ *
+ *     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.
+ **
+ */
+
+#ifndef _IPT_RPC_H
+#define _IPT_RPC_H
+
+struct ipt_rpc_data;
+
+struct ipt_rpc_info {
+       int inverse;
+       int strict;
+       const char c_procs[1408];
+       int i_procs;
+       struct ipt_rpc_data *data;
+};
+
+#endif /* _IPT_RPC_H */
diff --git a/include/linux/netfilter_ipv4/ipt_sctp.h b/include/linux/netfilter_ipv4/ipt_sctp.h
new file mode 100644 (file)
index 0000000..4b2cafc
--- /dev/null
@@ -0,0 +1,25 @@
+/* iptables module for matching the SCTP header
+ *
+ * (C) 2003 Harald Welte <laforge@gnumonks.org>
+ *
+ * This software is distributed under GNU GPL v2, 1991
+ *
+ * $Id: ipt_sctp.h,v 1.1 2003/05/03 18:05:58 laforge Exp $
+ */
+#ifndef _IPT_SCTP_H
+#define _IPT_SCTP_H
+
+struct ipt_sctp_info {
+       u_int16_t spts[2];                      /* Souce port range */
+       u_int16_t dpts[2];                      /* Destination port range */
+       u_int32_t chunks;                       /* chunks to be matched */
+       u_int32_t chunk_mask;                   /* chunk mask to be matched */
+       u_int8_t invflags;                      /* Inverse flags */
+};
+
+#define IPT_SCTP_INV_SRCPT     0x01    /* Invert the sense of source ports */
+#define IPT_SCTP_INV_DSTPT     0x02    /* Invert the sense of dest ports */
+#define IPT_SCTP_INV_CHUNKS    0x03    /* Invert the sense of chunks */
+#define IPT_SCTP_INV_MASK      0x03    /* All possible flags */
+
+#endif /* _IPT_SCTP_H */
diff --git a/include/linux/netfilter_ipv4/ipt_tcpmss.h b/include/linux/netfilter_ipv4/ipt_tcpmss.h
new file mode 100644 (file)
index 0000000..e2b1439
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef _IPT_TCPMSS_MATCH_H
+#define _IPT_TCPMSS_MATCH_H
+
+struct ipt_tcpmss_match_info {
+    u_int16_t mss_min, mss_max;
+    u_int8_t invert;
+};
+
+#endif /*_IPT_TCPMSS_MATCH_H*/
diff --git a/include/linux/netfilter_ipv4/ipt_ttl.h b/include/linux/netfilter_ipv4/ipt_ttl.h
new file mode 100644 (file)
index 0000000..ee24fd8
--- /dev/null
@@ -0,0 +1,21 @@
+/* IP tables module for matching the value of the TTL
+ * (C) 2000 by Harald Welte <laforge@gnumonks.org> */
+
+#ifndef _IPT_TTL_H
+#define _IPT_TTL_H
+
+enum {
+       IPT_TTL_EQ = 0,         /* equals */
+       IPT_TTL_NE,             /* not equals */
+       IPT_TTL_LT,             /* less than */
+       IPT_TTL_GT,             /* greater than */
+};
+
+
+struct ipt_ttl_info {
+       u_int8_t        mode;
+       u_int8_t        ttl;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_HL.h b/include/linux/netfilter_ipv6/ip6t_HL.h
new file mode 100644 (file)
index 0000000..afb7813
--- /dev/null
@@ -0,0 +1,22 @@
+/* Hop Limit modification module for ip6tables
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's TTL module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+       IP6T_HL_SET = 0,
+       IP6T_HL_INC,
+       IP6T_HL_DEC
+};
+
+#define IP6T_HL_MAXMODE        IP6T_HL_DEC
+
+struct ip6t_HL_info {
+       u_int8_t        mode;
+       u_int8_t        hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_REJECT.h b/include/linux/netfilter_ipv6/ip6t_REJECT.h
new file mode 100644 (file)
index 0000000..7266402
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _IP6T_REJECT_H
+#define _IP6T_REJECT_H
+
+enum ip6t_reject_with {
+       IP6T_ICMP6_NO_ROUTE,
+       IP6T_ICMP6_ADM_PROHIBITED,
+       IP6T_ICMP6_ADDR_UNREACH,
+       IP6T_ICMP6_PORT_UNREACH,
+       IP6T_TCP_RESET
+};
+
+struct ip6t_reject_info {
+       enum ip6t_reject_with with;      /* reject type */
+};
+
+#endif /*_IP6T_REJECT_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_ah.h b/include/linux/netfilter_ipv6/ip6t_ah.h
new file mode 100644 (file)
index 0000000..c4f0793
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _IP6T_AH_H
+#define _IP6T_AH_H
+
+struct ip6t_ah
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  hdrres;                       /* Test of the Reserved Filed */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_AH_SPI 0x01
+#define IP6T_AH_LEN 0x02
+#define IP6T_AH_RES 0x04
+
+/* Values for "invflags" field in struct ip6t_ah. */
+#define IP6T_AH_INV_SPI                0x01    /* Invert the sense of spi. */
+#define IP6T_AH_INV_LEN                0x02    /* Invert the sense of length. */
+#define IP6T_AH_INV_MASK       0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_AH_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_esp.h b/include/linux/netfilter_ipv6/ip6t_esp.h
new file mode 100644 (file)
index 0000000..01142b9
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef _IP6T_ESP_H
+#define _IP6T_ESP_H
+
+struct ip6t_esp
+{
+       u_int32_t spis[2];                      /* Security Parameter Index */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+/* Values for "invflags" field in struct ip6t_esp. */
+#define IP6T_ESP_INV_SPI               0x01    /* Invert the sense of spi. */
+#define IP6T_ESP_INV_MASK      0x01    /* All possible flags. */
+
+#endif /*_IP6T_ESP_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_frag.h b/include/linux/netfilter_ipv6/ip6t_frag.h
new file mode 100644 (file)
index 0000000..449a57e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _IP6T_FRAG_H
+#define _IP6T_FRAG_H
+
+struct ip6t_frag
+{
+       u_int32_t ids[2];                       /* Security Parameter Index */
+       u_int32_t hdrlen;                       /* Header Length */
+       u_int8_t  flags;                        /*  */
+       u_int8_t  invflags;                     /* Inverse flags */
+};
+
+#define IP6T_FRAG_IDS          0x01
+#define IP6T_FRAG_LEN          0x02
+#define IP6T_FRAG_RES          0x04
+#define IP6T_FRAG_FST          0x08
+#define IP6T_FRAG_MF           0x10
+#define IP6T_FRAG_NMF                  0x20
+
+/* Values for "invflags" field in struct ip6t_frag. */
+#define IP6T_FRAG_INV_IDS      0x01    /* Invert the sense of ids. */
+#define IP6T_FRAG_INV_LEN      0x02    /* Invert the sense of length. */
+#define IP6T_FRAG_INV_MASK     0x03    /* All possible flags. */
+
+#define MASK_HOPOPTS    128
+#define MASK_DSTOPTS    64
+#define MASK_ROUTING    32
+#define MASK_FRAGMENT   16
+#define MASK_AH         8
+#define MASK_ESP        4
+#define MASK_NONE       2
+#define MASK_PROTO      1
+
+#endif /*_IP6T_FRAG_H*/
diff --git a/include/linux/netfilter_ipv6/ip6t_hl.h b/include/linux/netfilter_ipv6/ip6t_hl.h
new file mode 100644 (file)
index 0000000..5ef91b8
--- /dev/null
@@ -0,0 +1,22 @@
+/* ip6tables module for matching the Hop Limit value
+ * Maciej Soltysiak <solt@dns.toxicfilms.tv>
+ * Based on HW's ttl module */
+
+#ifndef _IP6T_HL_H
+#define _IP6T_HL_H
+
+enum {
+       IP6T_HL_EQ = 0,         /* equals */
+       IP6T_HL_NE,             /* not equals */
+       IP6T_HL_LT,             /* less than */
+       IP6T_HL_GT,             /* greater than */
+};
+
+
+struct ip6t_hl_info {
+       u_int8_t        mode;
+       u_int8_t        hop_limit;
+};
+
+
+#endif
diff --git a/include/linux/netfilter_ipv6/ip6t_length.h b/include/linux/netfilter_ipv6/ip6t_length.h
new file mode 100644 (file)
index 0000000..7fc09f9
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _IP6T_LENGTH_H
+#define _IP6T_LENGTH_H
+
+struct ip6t_length_info {
+       u_int16_t  min, max;
+       u_int8_t   invert;
+};
+
+#endif /*_IP6T_LENGTH_H*/
+       
diff --git a/include/linux/netfilter_ipv6/ip6t_owner.h b/include/linux/netfilter_ipv6/ip6t_owner.h
new file mode 100644 (file)
index 0000000..19937da
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef _IP6T_OWNER_H
+#define _IP6T_OWNER_H
+
+/* match and invert flags */
+#define IP6T_OWNER_UID 0x01
+#define IP6T_OWNER_GID 0x02
+#define IP6T_OWNER_PID 0x04
+#define IP6T_OWNER_SID 0x08
+
+struct ip6t_owner_info {
+    uid_t uid;
+    gid_t gid;
+    pid_t pid;
+    pid_t sid;
+    u_int8_t match, invert;    /* flags */
+};
+
+#endif /*_IPT_OWNER_H*/
diff --git a/ip6tables-restore.8 b/ip6tables-restore.8
new file mode 100644 (file)
index 0000000..43c1268
--- /dev/null
@@ -0,0 +1,51 @@
+.TH IP6TABLES-RESTORE 8 "Jan 30, 2002" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables-restore \- Restore IPv6 Tables
+.SH SYNOPSIS
+.BR "ip6tables-restore " "[-c] [-n]"
+.br
+.SH DESCRIPTION
+.PP
+.B ip6tables-restore
+is used to restore IPv6 Tables from data specified on STDIN. Use 
+I/O redirection provided by your shell to read from a file
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+restore the values of all packet and byte counters
+.TP
+\fB\-n\fR, \fB\-\-noflush\fR 
+.TP
+don't flush the previous contents of the table. If not specified, 
+.B ip6tables-restore
+flushes (deletes) all previous contents of the respective IPv6 Table.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu>
+.SH SEE ALSO
+.BR ip6tables-save "(8), " ip6tables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/ip6tables-restore.c b/ip6tables-restore.c
new file mode 100644 (file)
index 0000000..74b5c78
--- /dev/null
@@ -0,0 +1,387 @@
+/* Code to restore the iptables state, from file by ip6tables-save. 
+ * Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ *
+ * based on iptables-restore
+ * Authors:
+ *     Harald Welte <laforge@gnumonks.org>
+ *     Rusty Russell <rusty@linuxcare.com.au>
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: ip6tables-restore.c,v 1.12 2003/05/02 15:30:11 laforge Exp $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "ip6tables.h"
+#include "libiptc/libip6tc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 1, 0, 'v' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+ip6tc_handle_t create_handle(const char *tablename, const char* modprobe)
+{
+       ip6tc_handle_t handle;
+
+       handle = ip6tc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               ip6tables_insmod("ip6_tables", modprobe);
+               handle = ip6tc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+int parse_counters(char *string, struct ip6t_counters *ctr)
+{
+       return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt) == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+int main(int argc, char *argv[])
+{
+       ip6tc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IP6T_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0;
+
+       program_name = "ip6tables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 'h':
+                               print_usage("ip6tables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret;
+
+               line++;
+               if (buffer[0] == '\n') continue;
+               else if (buffer[0] == '#') {
+                       if (verbose) fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       DEBUGP("Calling commit\n");
+                       ret = ip6tc_commit(&handle);
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IP6T_TABLE_MAXNAMELEN);
+
+                       if (handle)
+                               ip6tc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (!ip6tc_builtin(chain, handle)) {
+                               DEBUGP("Creating new chain '%s'\n", chain);
+                               if (!ip6tc_create_chain(chain, &handle))
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "error creating chain "
+                                                  "'%s':%s\n", chain,
+                                                  strerror(errno));
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ip6t_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       parse_counters(ctrs, &count);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ip6t_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!ip6tc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               ip6tc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_start = parsestart;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       if (quote_open) {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               param_start++;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar-param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+                                       
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer+param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command6(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command6(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+
+       return 0;
+}
diff --git a/ip6tables-save.8 b/ip6tables-save.8
new file mode 100644 (file)
index 0000000..c8b3e96
--- /dev/null
@@ -0,0 +1,50 @@
+.TH IP6TABLES-SAVE 8 "Jan 30, 2002" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    This program is free software; you can redistribute it and/or modify
+.\"    it under the terms of the GNU General Public License as published by
+.\"    the Free Software Foundation; either version 2 of the License, or
+.\"    (at your option) any later version.
+.\"
+.\"    This program is distributed in the hope that it will be useful,
+.\"    but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"    GNU General Public License for more details.
+.\"
+.\"    You should have received a copy of the GNU General Public License
+.\"    along with this program; if not, write to the Free Software
+.\"    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+.\"
+.\"
+.SH NAME
+ip6tables-save \- Save IPv6 Tables
+.SH SYNOPSIS
+.BR "ip6tables-save " "[-c] [-t table]"
+.br
+.SH DESCRIPTION
+.PP
+.B ip6tables-save
+is used to dump the contents of an IPv6 Table in easily parseable format
+to STDOUT. Use I/O-redirection provided by your shell to write to a file.
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+include the current values of all packet and byte counters in the output
+.TP
+\fB\-t\fR, \fB\-\-table\fR \fBtablename\fR
+.TP
+restrict output to only one table. If not specified, output includes all
+available tables.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHORS
+Harald Welte <laforge@gnumonks.org>
+.br
+Andras Kis-Szabo <kisza@sch.bme.hu>
+.SH SEE ALSO
+.BR ip6tables-restore "(8), " ip6tables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/ip6tables-save.c b/ip6tables-save.c
new file mode 100644 (file)
index 0000000..77cc325
--- /dev/null
@@ -0,0 +1,355 @@
+/* Code to save the ip6tables state, in human readable-form. */
+/* Author:  Andras Kis-Szabo <kisza@sch.bme.hu>
+ * Original code: iptables-save
+ * Authors: Paul 'Rusty' Russel <rusty@linuxcare.com.au> and
+ *         Harald Welte <laforge@gnumonks.org>
+ * This code is distributed under the terms of GNU GPL v2
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <time.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include "libiptc/libip6tc.h"
+#include "ip6tables.h"
+
+static int binary = 0, counters = 0;
+
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "dump", 0, 0, 'd' },
+       { "table", 1, 0, 't' },
+       { 0 }
+};
+
+
+/* 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 {
+                       /* we can access iface[i-1] here, because 
+                        * a few lines above we make sure that mask[0] != 0 */
+                       if (iface[i-1] != '\0')
+                               printf("+");
+                       break;
+               }
+       }
+
+       printf(" ");
+}
+
+/* These are hardcoded backups in ip6tables.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 },
+       { "icmpv6", IPPROTO_ICMPV6 },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+};
+
+/* The ip6tables looks up the /etc/protocols. */
+static void print_proto(u_int16_t proto, int invert)
+{
+       if (proto) {
+               unsigned int i;
+               const char *invertstr = invert ? "! " : "";
+
+                struct protoent *pent = getprotobynumber(proto);
+                if (pent) {
+                       printf("-p %s%s ",
+                              invertstr, pent->p_name);
+                       return;
+               }
+
+               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 print_match(const struct ip6t_entry_match *e,
+                       const struct ip6t_ip6 *ip)
+{
+       struct ip6tables_match *match
+               = find_match(e->u.user.name, TRY_LOAD);
+
+       if (match) {
+               printf("-m %s ", e->u.user.name);
+
+               /* some matches don't provide a save function */
+               if (match->save)
+                       match->save(ip, e);
+       } else {
+               if (e->u.match_size) {
+                       fprintf(stderr,
+                               "Can't find library for match `%s'\n",
+                               e->u.user.name);
+                       exit(1);
+               }
+       }
+       return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, const struct in6_addr *ip, const struct in6_addr *mask, int invert)
+{
+       char buf[51];
+       int l = ipv6_prefix_length(mask);
+
+       if (!mask && !ip)
+               return;
+
+       printf("%s %s%s",
+               prefix,
+               invert ? "! " : "",
+               inet_ntop(AF_INET6, ip, buf, sizeof buf));
+
+       if (l == -1)
+               printf("/%s ", inet_ntop(AF_INET6, mask, buf, sizeof buf));
+       else
+               printf("/%d ", l);
+}
+
+/* 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 ip6t_entry *e, 
+               ip6tc_handle_t *h, const char *chain, int counters)
+{
+       struct ip6t_entry_target *t;
+       const char *target_name;
+
+       /* print counters */
+       if (counters)
+               printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
+
+       /* print chain name */
+       printf("-A %s ", chain);
+
+       /* Print IP part. */
+       print_ip("-s", &(e->ipv6.src), &(e->ipv6.smsk),
+                       e->ipv6.invflags & IP6T_INV_SRCIP);     
+
+       print_ip("-d", &(e->ipv6.dst), &(e->ipv6.dmsk),
+                       e->ipv6.invflags & IP6T_INV_DSTIP);
+
+       print_iface('i', e->ipv6.iniface, e->ipv6.iniface_mask,
+                   e->ipv6.invflags & IP6T_INV_VIA_IN);
+
+       print_iface('o', e->ipv6.outiface, e->ipv6.outiface_mask,
+                   e->ipv6.invflags & IP6T_INV_VIA_OUT);
+
+       print_proto(e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
+
+#if 0
+       /* not definied in ipv6
+        * FIXME: linux/netfilter_ipv6/ip6_tables: IP6T_INV_FRAG why definied? */
+       if (e->ipv6.flags & IPT_F_FRAG)
+               printf("%s-f ",
+                      e->ipv6.invflags & IP6T_INV_FRAG ? "! " : "");
+#endif
+
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("%s-? %d ",
+                      e->ipv6.invflags & IP6T_INV_TOS ? "! " : "", 
+                      e->ipv6.tos);
+
+       /* Print matchinfo part */
+       if (e->target_offset) {
+               IP6T_MATCH_ITERATE(e, print_match, &e->ipv6);
+       }
+
+       /* Print target name */ 
+       target_name = ip6tc_get_target(e, h);
+       if (target_name && (*target_name != '\0'))
+               printf("-j %s ", target_name);
+
+       /* Print targinfo part */
+       t = ip6t_get_target((struct ip6t_entry *)e);
+       if (t->u.user.name[0]) {
+               struct ip6tables_target *target
+                       = find_target(t->u.user.name, TRY_LOAD);
+
+               if (!target) {
+                       fprintf(stderr, "Can't find library for target `%s'\n",
+                               t->u.user.name);
+                       exit(1);
+               }
+
+               if (target->save)
+                       target->save(&e->ipv6, t);
+               else {
+                       /* If the target size is greater than ip6t_entry_target
+                        * there is something to be saved, we just don't know
+                        * how to print it */
+                       if (t->u.target_size != 
+                           sizeof(struct ip6t_entry_target)) {
+                               fprintf(stderr, "Target `%s' is missing "
+                                               "save function\n",
+                                       t->u.user.name);
+                               exit(1);
+                       }
+               }
+       }
+       printf("\n");
+}
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+        int ret = 1;
+       FILE *procfile = NULL;
+       char tablename[IP6T_TABLE_MAXNAMELEN+1];
+
+       procfile = fopen("/proc/net/ip6_tables_names", "r");
+       if (!procfile)
+               return 0;
+
+       while (fgets(tablename, sizeof(tablename), procfile)) {
+               if (tablename[strlen(tablename) - 1] != '\n')
+                       exit_error(OTHER_PROBLEM, 
+                                  "Badly formed tablename `%s'\n",
+                                  tablename);
+               tablename[strlen(tablename) - 1] = '\0';
+               ret &= func(tablename);
+       }
+
+       return ret;
+}
+       
+
+static int do_output(const char *tablename)
+{
+       ip6tc_handle_t h;
+       const char *chain = NULL;
+
+       if (!tablename)
+               return for_each_table(&do_output);
+
+       h = ip6tc_init(tablename);
+       if (!h)
+               exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
+                          ip6tc_strerror(errno));
+
+       if (!binary) {
+               time_t now = time(NULL);
+
+               printf("# Generated by ip6tables-save v%s on %s",
+                      IPTABLES_VERSION, ctime(&now));
+               printf("*%s\n", tablename);
+
+               /* Dump out chain names first, 
+                * thereby preventing dependency conflicts */
+               for (chain = ip6tc_first_chain(&h);
+                    chain;
+                    chain = ip6tc_next_chain(&h)) {
+
+                       printf(":%s ", chain);
+                       if (ip6tc_builtin(chain, h)) {
+                               struct ip6t_counters count;
+                               printf("%s ",
+                                      ip6tc_get_policy(chain, &count, &h));
+                               printf("[%llu:%llu]\n", count.pcnt, count.bcnt);
+                       } else {
+                               printf("- [0:0]\n");
+                       }
+               }
+
+
+               for (chain = ip6tc_first_chain(&h);
+                    chain;
+                    chain = ip6tc_next_chain(&h)) {
+                       const struct ip6t_entry *e;
+
+                       /* Dump out rules */
+                       e = ip6tc_first_rule(chain, &h);
+                       while(e) {
+                               print_rule(e, &h, chain, counters);
+                               e = ip6tc_next_rule(e, &h);
+                       }
+               }
+
+               now = time(NULL);
+               printf("COMMIT\n");
+               printf("# Completed on %s", ctime(&now));
+       } else {
+               /* Binary, huh?  OK. */
+               exit_error(OTHER_PROBLEM, "Binary NYI\n");
+       }
+
+       ip6tc_free(&h);
+
+       return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int main(int argc, char *argv[])
+{
+       const char *tablename = NULL;
+       int c;
+
+       program_name = "ip6tables-save";
+       program_version = IPTABLES_VERSION;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+               switch (c) {
+               case 'b':
+                       binary = 1;
+                       break;
+
+               case 'c':
+                       counters = 1;
+                       break;
+
+               case 't':
+                       /* Select specific table. */
+                       tablename = optarg;
+                       break;
+               case 'd':
+                       do_output(tablename);
+                       exit(0);
+               }
+       }
+
+       if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+
+       return !do_output(tablename);
+}
diff --git a/ip6tables-standalone.c b/ip6tables-standalone.c
new file mode 100644 (file)
index 0000000..7391ccf
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <ip6tables.h>
+
+int
+main(int argc, char *argv[])
+{
+       int ret;
+       char *table = "filter";
+       ip6tc_handle_t handle = NULL;
+
+       program_name = "ip6tables";
+       program_version = IPTABLES_VERSION;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       ret = do_command6(argc, argv, &table, &handle);
+       if (ret)
+               ret = ip6tc_commit(&handle);
+
+       if (!ret)
+               fprintf(stderr, "ip6tables: %s\n",
+                       ip6tc_strerror(errno));
+
+       exit(!ret);
+}
diff --git a/ip6tables.c b/ip6tables.c
new file mode 100644 (file)
index 0000000..553e924
--- /dev/null
@@ -0,0 +1,2305 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     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 <ip6tables.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IP6T_LIB_DIR
+#define IP6T_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_LINENUMBERS 0x00200U
+#define OPT_COUNTERS   0x00400U
+#define NUMBER_OF_OPT  11
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for ip6tables-restore. ip6tables-restore.c sets line to the
+ * current line of the input file, in order to give a more precise error
+ * message. ip6tables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ip6t_entry_target *
+ip6t_get_target(struct ip6t_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IP6T_INV_SRCIP,
+/* -d */ IP6T_INV_DSTIP,
+/* -p */ IP6T_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IP6T_INV_VIA_IN,
+/* -o */ IP6T_INV_VIA_OUT,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct ip6tables_match *ip6tables_matches = NULL;
+struct ip6tables_target *ip6tables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries6(const ip6tc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmpv6", IPPROTO_ICMPV6 },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+       memcpy(dst, src, sizeof(struct in6_addr));
+       /* dst->s6_addr = src->s6_addr; */
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct ip6tables_match *m = NULL;
+       struct ip6tables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+/*"[!] --fragment      -f              match second or further fragments only\n"*/
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able to add a --help 
+          to the commandline, and see expected results. So we call help for all 
+          matches & targets */
+       for (t=ip6tables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=ip6tables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<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, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("ip6tables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("ip6tables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static char *
+addr_to_numeric(const struct in6_addr *addrp)
+{
+       /* 0000:0000:0000:0000:0000:000.000.000.000
+        * 0000:0000:0000:0000:0000:0000:0000:0000 */
+       static char buf[50+1];
+       return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
+}
+
+static struct in6_addr *
+numeric_to_addr(const char *num)
+{
+       static struct in6_addr ap;
+       int err;
+       if ((err=inet_pton(AF_INET6, num, &ap)) == 1)
+               return &ap;
+#ifdef DEBUG
+       fprintf(stderr, "\nnumeric2addr: %d\n", err);
+#endif
+       return (struct in6_addr *)NULL;
+}
+
+
+static struct in6_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct addrinfo hints;
+        struct addrinfo *res;
+        static struct in6_addr *addr;
+       int err;
+
+       memset(&hints, 0, sizeof(hints));
+        hints.ai_flags=AI_CANONNAME;
+        hints.ai_family=AF_INET6;
+        hints.ai_socktype=SOCK_RAW;
+        hints.ai_protocol=41;
+        hints.ai_next=NULL;
+
+       *naddr = 0;
+        if ( (err=getaddrinfo(name, NULL, &hints, &res)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"Name2IP: %s\n",gai_strerror(err)); 
+#endif
+                return (struct in6_addr *) NULL;
+        } else {
+               if (res->ai_family != AF_INET6 ||
+                   res->ai_addrlen != sizeof(struct sockaddr_in6))
+                       return (struct in6_addr *) NULL;
+
+#ifdef DEBUG
+                fprintf(stderr, "resolved: len=%d  %s ", res->ai_addrlen, 
+                    addr_to_numeric(&(((struct sockaddr_in6 *)res->ai_addr)->sin6_addr)));
+#endif
+               /* Get the first element of the address-chain */
+               addr = fw_calloc(1, sizeof(struct in6_addr));
+               in6addrcpy(addr, (struct in6_addr *)
+                       &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr);
+               freeaddrinfo(res);
+               *naddr = 1;
+               return addr;
+       }
+
+       return (struct in6_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in6_addr *addr)
+{
+       struct sockaddr_in6 saddr;
+       int err;
+       static char hostname[NI_MAXHOST];
+
+       memset(&saddr, 0, sizeof(struct sockaddr_in6));
+       in6addrcpy(&(saddr.sin6_addr),(struct in6_addr *)addr);
+       saddr.sin6_family = AF_INET6;
+
+        if ( (err=getnameinfo((struct sockaddr *)&saddr,
+                              sizeof(struct sockaddr_in6),
+                              hostname, sizeof(hostname)-1,
+                              NULL, 0, 0)) != 0 ){
+#ifdef DEBUG
+                fprintf(stderr,"IP2Name: %s\n",gai_strerror(err)); 
+#endif
+                return (char *) NULL;
+        } else {
+#ifdef DEBUG
+               fprintf (stderr, "\naddr2host: %s\n", hostname);
+#endif
+
+               return hostname;
+       }
+
+       return (char *) NULL;
+}
+
+static char *
+mask_to_numeric(const struct in6_addr *addrp)
+{
+       static char buf[50+2];
+       int l = ipv6_prefix_length(addrp);
+       if (l == -1) {
+               strcpy(buf, "/");
+               strcat(buf, addr_to_numeric(addrp));
+               return buf;
+       }
+       sprintf(buf, "/%d", l);
+       return buf;
+}
+
+static struct in6_addr *
+network_to_addr(const char *name)
+{
+       /*      abort();*/
+       /* TODO: not implemented yet, but the exception breaks the
+        *       name resolvation */
+       return (struct in6_addr *)NULL;
+}
+
+static char *
+addr_to_anyname(const struct in6_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL)
+               return name;
+
+       return addr_to_numeric(addr);
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in6_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in6_addr *addrp, *addrptmp;
+
+       if ((addrptmp = numeric_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in6_addr));
+               in6addrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in6_addr *
+parse_mask(char *mask)
+{
+       static struct in6_addr maskaddr;
+       struct in6_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 128 bits */
+               memset(&maskaddr, 0xff, sizeof maskaddr);
+               return &maskaddr;
+       }
+       if ((addrp = numeric_to_addr(mask)) != NULL)
+               return addrp;
+       if (string_to_number(mask, 0, 128, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               char *p = (char *)&maskaddr;
+               memset(p, 0xff, bits / 8);
+               memset(p + (bits / 8) + 1, 0, (128 - bits) / 8);
+               p[bits / 8] = 0xff << (8 - (bits & 7));
+               return &maskaddr;
+       }
+
+       memset(&maskaddr, 0, sizeof maskaddr);
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in6_addr **addrpp,
+                     struct in6_addr *maskp, unsigned int *naddrs)
+{
+       struct in6_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       in6addrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (!memcmp(maskp, &in6addr_any, sizeof(in6addr_any)))
+               strcpy(buf, "::");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               int k;
+               for (k = 0; k < 4; k++)
+                       addrp[j].in6_u.u6_addr32[k] &= maskp->in6_u.u6_addr32[k];
+               j++;
+               for (k = 0; k < j - 1; k++) {
+                       if (IN6_ARE_ADDR_EQUAL(&addrp[k], &addrp[j - 1])) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct ip6tables_match *
+find_match(const char *name, enum ip6t_tryload tryload)
+{
+       struct ip6tables_match *ptr;
+       int icmphack = 0;
+  
+       /* This is ugly as hell. Nonetheless, there is no way of changing
+        * this without hurting backwards compatibility */
+       if ( (strcmp(name,"icmpv6") == 0) ||
+            (strcmp(name,"ipv6-icmp") == 0) ||
+            (strcmp(name,"icmp6") == 0) ) icmphack = 1;
+       if (!icmphack) {
+               for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+                       if (strcmp(name, ptr->name) == 0)
+                               break;
+               }
+       } else {
+               for (ptr = ip6tables_matches; ptr; ptr = ptr->next) {
+                       if (strcmp("icmp6", ptr->name) == 0)
+                               break;
+               }
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               if (!icmphack)
+                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+               else
+                       sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", "icmpv6");
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct ip6tables_match *
+find_proto(const char *pname, enum ip6t_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ip6t_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ip6t_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct ip6tables_target *
+find_target(const char *name, enum ip6t_tryload tryload)
+{
+       struct ip6tables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IP6TC_LABEL_ACCEPT) == 0
+           || strcmp(name, IP6TC_LABEL_DROP) == 0
+           || strcmp(name, IP6TC_LABEL_QUEUE) == 0
+           || strcmp(name, IP6TC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = ip6tables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IP6T_LIB_DIR) + sizeof("/libip6t_.so")
+                        + strlen(name)];
+               sprintf(path, IP6T_LIB_DIR "/libip6t_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match6(struct ip6tables_match *me)
+{
+       struct ip6tables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &ip6tables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target6(struct ip6tables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IP6T_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = ip6tables_targets;
+       ip6tables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, ip6tc_handle_t *handle)
+{
+       struct ip6t_counters counters;
+       const char *pol = ip6tc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!ip6tc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ip6t_entry_match *m,
+           const struct ip6t_ip6 *ip,
+           int numeric)
+{
+       struct ip6tables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ip6t_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const ip6tc_handle_t handle)
+{
+       struct ip6tables_target *target = NULL;
+       const struct ip6t_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!ip6tc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IP6T_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       flags = fw->ipv6.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ipv6.invflags & IP6T_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ipv6.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ipv6.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(' ', stdout); /* Invert flag of FRAG */
+               fputc(' ', stdout); /* -f */
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.iniface[0] != '\0') {
+                       strcat(iface, fw->ipv6.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ipv6.invflags & IP6T_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ipv6.outiface[0] != '\0') {
+                       strcat(iface, fw->ipv6.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_SRCIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.smsk, &in6addr_any, sizeof in6addr_any) 
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.src)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ipv6.invflags & IP6T_INV_DSTIP ? '!' : ' ', stdout);
+       if (!memcmp(&fw->ipv6.dmsk, &in6addr_any, sizeof in6addr_any)
+           && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_numeric(&(fw->ipv6.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ipv6.dst)));
+               strcat(buf, mask_to_numeric(&(fw->ipv6.dmsk)));
+               printf(FMT("%-19s","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IP6T_MATCH_ITERATE(fw, print_match, &fw->ipv6, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ipv6, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ip6t_entry *fw,
+                   const ip6tc_handle_t h)
+{
+       struct ip6t_entry_target *t;
+
+       t = ip6t_get_target((struct ip6t_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ip6t_chainlabel chain,
+             struct ip6t_entry *fw,
+             unsigned int rulenum,
+             const struct in6_addr *saddr,
+             const struct in6_addr *daddr,
+             int verbose,
+             ip6tc_handle_t *handle)
+{
+       fw->ipv6.src = *saddr;
+       fw->ipv6.dst = *daddr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return ip6tc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ip6t_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct ip6tables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ip6t_entry);
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                        + ip6tables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ip6t_entry));
+       mptr = mask + sizeof(struct ip6t_entry);
+
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                      + m->userspacesize);
+               mptr += IP6T_ALIGN(sizeof(struct ip6t_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF, 
+              IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+              + ip6tables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ip6t_chainlabel chain,
+            struct ip6t_entry *fw,
+            unsigned int nsaddrs,
+            const struct in6_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in6_addr daddrs[],
+            int verbose,
+            ip6tc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ipv6.src = saddrs[i];
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ipv6.dst = daddrs[j];
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= ip6tc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ip6t_chainlabel, int, ip6tc_handle_t *),
+              int verbose, int builtinstoo, ip6tc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ip6t_chainlabel) * chaincount);
+       i = 0;
+       chain = ip6tc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ip6t_chainlabel), chain);
+               i++;
+               chain = ip6tc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && ip6tc_builtin(chains + i*sizeof(ip6t_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ip6t_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ip6t_chainlabel chain, int verbose,
+             ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return ip6tc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return ip6tc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ip6t_chainlabel chain, int verbose,
+            ip6tc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return ip6tc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ip6t_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, ip6tc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = ip6tc_first_chain(handle);
+            this;
+            this = ip6tc_next_chain(handle)) {
+               const struct ip6t_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = ip6tc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      ip6tc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = ip6tc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int ip6tables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+       int i=0;
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               /* close open file descriptors */
+               for (i=0; i< 10; i++) {
+                 close(i);
+               }
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ip6t_entry *
+generate_entry(const struct ip6t_entry *fw,
+              struct ip6tables_match *matches,
+              struct ip6t_entry_target *target)
+{
+       unsigned int size;
+       struct ip6tables_match *m;
+       struct ip6t_entry *e;
+
+       size = sizeof(struct ip6t_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command6(int argc, char *argv[], char **table, ip6tc_handle_t *handle)
+{
+       struct ip6t_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in6_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct ip6tables_match *m;
+       struct ip6tables_target *target = NULL;
+       struct ip6tables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+       char icmp6p[] = "icmpv6";
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = ip6tables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = ip6tables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:bvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!ip6tables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ipv6.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       if ( strcmp(protocol,"ipv6-icmp") == 0)
+                               protocol = icmp6p;
+                       fw.ipv6.proto = parse_protocol(protocol);
+                       fw.ipv6.flags |= IP6T_F_PROTO;
+
+                       if (fw.ipv6.proto == 0
+                           && (fw.ipv6.invflags & IP6T_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP6_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ipv6.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP6_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ipv6.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP6_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ipv6.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IP6T_ALIGN(sizeof(struct ip6t_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.iniface,
+                                       fw.ipv6.iniface_mask);
+                       fw.nfcache |= NFC_IP6_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ipv6.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ipv6.outiface,
+                                       fw.ipv6.outiface_mask);
+                       fw.nfcache |= NFC_IP6_IF_OUT;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ipv6.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ipv6.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ipv6.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = ip6tables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IP6T_ALIGN(sizeof(struct ip6t_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = ip6tables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "::0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "::0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ipv6.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ipv6.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ipv6.invflags & (IP6T_INV_SRCIP | IP6T_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IP6T_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IP6T_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = ip6tc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               ip6tables_insmod("ip6_tables", modprobe);
+               *handle = ip6tc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                       "can't initialize ip6tables table `%s': %s",
+                       *table, ip6tc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && ip6tc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || ip6tc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IP6T_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ip6t_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, ip6tables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = ip6tc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = ip6tc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = ip6tc_rename_chain(chain, newname,        handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = ip6tc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries6(*handle);
+
+       return ret;
+}
diff --git a/iptables-restore.8 b/iptables-restore.8
new file mode 100644 (file)
index 0000000..e2649e5
--- /dev/null
@@ -0,0 +1,49 @@
+.TH IPTABLES-RESTORE 8 "Jan 04, 2001" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    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-restore \- Restore IP Tables
+.SH SYNOPSIS
+.BR "iptables-restore " "[-c] [-n]"
+.br
+.SH DESCRIPTION
+.PP
+.B iptables-restore
+is used to restore IP Tables from data specified on STDIN. Use 
+I/O redirection provided by your shell to read from a file
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+restore the values of all packet and byte counters
+.TP
+\fB\-n\fR, \fB\-\-noflush\fR 
+.TP
+don't flush the previous contents of the table. If not specified, 
+.B iptables-restore
+flushes (deletes) all previous contents of the respective IP Table.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHOR
+Harald Welte <laforge@gnumonks.org>
+.SH SEE ALSO
+.BR iptables-save "(8), " iptables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables-restore.c b/iptables-restore.c
new file mode 100644 (file)
index 0000000..79cb259
--- /dev/null
@@ -0,0 +1,388 @@
+/* Code to restore the iptables state, from file by iptables-save. 
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * based on previous code from Rusty Russell <rusty@linuxcare.com.au>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ * $Id: iptables-restore.c,v 1.26 2003/05/02 15:30:11 laforge Exp $
+ */
+
+#include <getopt.h>
+#include <sys/errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "iptables.h"
+#include "libiptc/libiptc.h"
+
+#ifdef DEBUG
+#define DEBUGP(x, args...) fprintf(stderr, x, ## args)
+#else
+#define DEBUGP(x, args...) 
+#endif
+
+static int binary = 0, counters = 0, verbose = 0, noflush = 0;
+
+/* Keeping track of external matches and targets.  */
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "verbose", 1, 0, 'v' },
+       { "help", 0, 0, 'h' },
+       { "noflush", 0, 0, 'n'},
+       { "modprobe", 1, 0, 'M'},
+       { 0 }
+};
+
+static void print_usage(const char *name, const char *version) __attribute__((noreturn));
+
+static void print_usage(const char *name, const char *version)
+{
+       fprintf(stderr, "Usage: %s [-b] [-c] [-v] [-h]\n"
+                       "          [ --binary ]\n"
+                       "          [ --counters ]\n"
+                       "          [ --verbose ]\n"
+                       "          [ --help ]\n"
+                       "          [ --noflush ]\n"
+                       "          [ --modprobe=<command>]\n", name);
+               
+       exit(1);
+}
+
+iptc_handle_t create_handle(const char *tablename, const char* modprobe )
+{
+       iptc_handle_t handle;
+
+       handle = iptc_init(tablename);
+
+       if (!handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               handle = iptc_init(tablename);
+       }
+
+       if (!handle) {
+               exit_error(PARAMETER_PROBLEM, "%s: unable to initialize"
+                       "table '%s'\n", program_name, tablename);
+               exit(1);
+       }
+       return handle;
+}
+
+int parse_counters(char *string, struct ipt_counters *ctr)
+{
+       if (string != NULL)
+               return (sscanf(string, "[%llu:%llu]", &ctr->pcnt, &ctr->bcnt)
+                       == 2);
+       else
+               return (0 == 2);
+}
+
+/* global new argv and argc */
+static char *newargv[255];
+static int newargc;
+
+/* function adding one argument to newargv, updating newargc 
+ * returns true if argument added, false otherwise */
+static int add_argv(char *what) {
+       DEBUGP("add_argv: %s\n", what);
+       if (what && ((newargc + 1) < sizeof(newargv)/sizeof(char *))) {
+               newargv[newargc] = strdup(what);
+               newargc++;
+               return 1;
+       } else 
+               return 0;
+}
+
+static void free_argv(void) {
+       int i;
+
+       for (i = 0; i < newargc; i++)
+               free(newargv[i]);
+}
+
+int main(int argc, char *argv[])
+{
+       iptc_handle_t handle = NULL;
+       char buffer[10240];
+       int c;
+       char curtable[IPT_TABLE_MAXNAMELEN + 1];
+       FILE *in;
+       const char *modprobe = 0;
+       int in_table = 0;
+
+       program_name = "iptables-restore";
+       program_version = IPTABLES_VERSION;
+       line = 0;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcvhnM:", options, NULL)) != -1) {
+               switch (c) {
+                       case 'b':
+                               binary = 1;
+                               break;
+                       case 'c':
+                               counters = 1;
+                               break;
+                       case 'v':
+                               verbose = 1;
+                               break;
+                       case 'h':
+                               print_usage("iptables-restore",
+                                           IPTABLES_VERSION);
+                               break;
+                       case 'n':
+                               noflush = 1;
+                               break;
+                       case 'M':
+                               modprobe = optarg;
+                               break;
+               }
+       }
+       
+       if (optind == argc - 1) {
+               in = fopen(argv[optind], "r");
+               if (!in) {
+                       fprintf(stderr, "Can't open %s: %s", argv[optind],
+                               strerror(errno));
+                       exit(1);
+               }
+       }
+       else if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+       else in = stdin;
+       
+       /* Grab standard input. */
+       while (fgets(buffer, sizeof(buffer), in)) {
+               int ret = 0;
+
+               line++;
+               if (buffer[0] == '\n') continue;
+               else if (buffer[0] == '#') {
+                       if (verbose) fputs(buffer, stdout);
+                       continue;
+               } else if ((strcmp(buffer, "COMMIT\n") == 0) && (in_table)) {
+                       DEBUGP("Calling commit\n");
+                       ret = iptc_commit(&handle);
+                       in_table = 0;
+               } else if ((buffer[0] == '*') && (!in_table)) {
+                       /* New table */
+                       char *table;
+
+                       table = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, table '%s'\n", line, table);
+                       if (!table) {
+                               exit_error(PARAMETER_PROBLEM, 
+                                       "%s: line %u table name invalid\n",
+                                       program_name, line);
+                               exit(1);
+                       }
+                       strncpy(curtable, table, IPT_TABLE_MAXNAMELEN);
+
+                       if (handle)
+                               iptc_free(&handle);
+
+                       handle = create_handle(table, modprobe);
+                       if (noflush == 0) {
+                               DEBUGP("Cleaning all chains of table '%s'\n",
+                                       table);
+                               for_each_chain(flush_entries, verbose, 1, 
+                                               &handle);
+       
+                               DEBUGP("Deleting all user-defined chains "
+                                      "of table '%s'\n", table);
+                               for_each_chain(delete_chain, verbose, 0, 
+                                               &handle) ;
+                       }
+
+                       ret = 1;
+                       in_table = 1;
+
+               } else if ((buffer[0] == ':') && (in_table)) {
+                       /* New chain. */
+                       char *policy, *chain;
+
+                       chain = strtok(buffer+1, " \t\n");
+                       DEBUGP("line %u, chain '%s'\n", line, chain);
+                       if (!chain) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u chain name invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (!iptc_builtin(chain, handle)) {
+                               DEBUGP("Creating new chain '%s'\n", chain);
+                               if (!iptc_create_chain(chain, &handle)) 
+                                       exit_error(PARAMETER_PROBLEM, 
+                                                  "error creating chain "
+                                                  "'%s':%s\n", chain, 
+                                                  strerror(errno));
+                       }
+
+                       policy = strtok(NULL, " \t\n");
+                       DEBUGP("line %u, policy '%s'\n", line, policy);
+                       if (!policy) {
+                               exit_error(PARAMETER_PROBLEM,
+                                          "%s: line %u policy invalid\n",
+                                          program_name, line);
+                               exit(1);
+                       }
+
+                       if (strcmp(policy, "-") != 0) {
+                               struct ipt_counters count;
+
+                               if (counters) {
+                                       char *ctrs;
+                                       ctrs = strtok(NULL, " \t\n");
+
+                                       parse_counters(ctrs, &count);
+
+                               } else {
+                                       memset(&count, 0, 
+                                              sizeof(struct ipt_counters));
+                               }
+
+                               DEBUGP("Setting policy of chain %s to %s\n",
+                                       chain, policy);
+
+                               if (!iptc_set_policy(chain, policy, &count,
+                                                    &handle))
+                                       exit_error(OTHER_PROBLEM,
+                                               "Can't set policy `%s'"
+                                               " on `%s' line %u: %s\n",
+                                               chain, policy, line,
+                                               iptc_strerror(errno));
+                       }
+
+                       ret = 1;
+
+               } else if (in_table) {
+                       int a;
+                       char *ptr = buffer;
+                       char *pcnt = NULL;
+                       char *bcnt = NULL;
+                       char *parsestart;
+
+                       /* the parser */
+                       char *param_start, *curchar;
+                       int quote_open;
+
+                       /* reset the newargv */
+                       newargc = 0;
+
+                       if (buffer[0] == '[') {
+                               /* we have counters in our input */
+                               ptr = strchr(buffer, ']');
+                               if (!ptr)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               pcnt = strtok(buffer+1, ":");
+                               if (!pcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need :\n",
+                                                  line);
+
+                               bcnt = strtok(NULL, "]");
+                               if (!bcnt)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Bad line %u: need ]\n",
+                                                  line);
+
+                               /* start command parsing after counter */
+                               parsestart = ptr + 1;
+                       } else {
+                               /* start command parsing at start of line */
+                               parsestart = buffer;
+                       }
+
+                       add_argv(argv[0]);
+                       add_argv("-t");
+                       add_argv((char *) &curtable);
+                       
+                       if (counters && pcnt && bcnt) {
+                               add_argv("--set-counters");
+                               add_argv((char *) pcnt);
+                               add_argv((char *) bcnt);
+                       }
+
+                       /* After fighting with strtok enough, here's now
+                        * a 'real' parser. According to Rusty I'm now no
+                        * longer a real hacker, but I can live with that */
+
+                       quote_open = 0;
+                       param_start = parsestart;
+                       
+                       for (curchar = parsestart; *curchar; curchar++) {
+                               if (*curchar == '"') {
+                                       if (quote_open) {
+                                               quote_open = 0;
+                                               *curchar = ' ';
+                                       } else {
+                                               quote_open = 1;
+                                               param_start++;
+                                       }
+                               } 
+                               if (*curchar == ' '
+                                   || *curchar == '\t'
+                                   || * curchar == '\n') {
+                                       char param_buffer[1024];
+                                       int param_len = curchar-param_start;
+
+                                       if (quote_open)
+                                               continue;
+
+                                       if (!param_len) {
+                                               /* two spaces? */
+                                               param_start++;
+                                               continue;
+                                       }
+                                       
+                                       /* end of one parameter */
+                                       strncpy(param_buffer, param_start,
+                                               param_len);
+                                       *(param_buffer+param_len) = '\0';
+
+                                       /* check if table name specified */
+                                       if (!strncmp(param_buffer, "-t", 3)
+                                            || !strncmp(param_buffer, "--table", 8)) {
+                                               exit_error(PARAMETER_PROBLEM, 
+                                                  "Line %u seems to have a "
+                                                  "-t table option.\n", line);
+                                               exit(1);
+                                       }
+
+                                       add_argv(param_buffer);
+                                       param_start += param_len + 1;
+                               } else {
+                                       /* regular character, skip */
+                               }
+                       }
+
+                       DEBUGP("calling do_command(%u, argv, &%s, handle):\n",
+                               newargc, curtable);
+
+                       for (a = 0; a < newargc; a++)
+                               DEBUGP("argv[%u]: %s\n", a, newargv[a]);
+
+                       ret = do_command(newargc, newargv, 
+                                        &newargv[2], &handle);
+
+                       free_argv();
+               }
+               if (!ret) {
+                       fprintf(stderr, "%s: line %u failed\n",
+                                       program_name, line);
+                       exit(1);
+               }
+       }
+
+       return 0;
+}
diff --git a/iptables-save.8 b/iptables-save.8
new file mode 100644 (file)
index 0000000..f9c7d65
--- /dev/null
@@ -0,0 +1,48 @@
+.TH IPTABLES-SAVE 8 "Jan 04, 2001" "" ""
+.\"
+.\" Man page written by Harald Welte <laforge@gnumonks.org>
+.\" It is based on the iptables man page.
+.\"
+.\"    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-save \- Save IP Tables
+.SH SYNOPSIS
+.BR "iptables-save " "[-c] [-t table]"
+.br
+.SH DESCRIPTION
+.PP
+.B iptables-save
+is used to dump the contents of an IP Table in easily parseable format
+to STDOUT. Use I/O-redirection provided by your shell to write to a file.
+.TP
+\fB\-c\fR, \fB\-\-counters\fR
+include the current values of all packet and byte counters in the output
+.TP
+\fB\-t\fR, \fB\-\-table\fR \fBtablename\fR
+.TP
+restrict output to only one table. If not specified, output includes all
+available tables.
+.SH BUGS
+None known as of iptables-1.2.1 release
+.SH AUTHOR
+Harald Welte <laforge@gnumonks.org>
+.SH SEE ALSO
+.BR iptables-restore "(8), " iptables "(8) "
+.PP
+The iptables-HOWTO, which details more iptables usage, the NAT-HOWTO,
+which details NAT, and the netfilter-hacking-HOWTO which details the
+internals.
diff --git a/iptables-save.c b/iptables-save.c
new file mode 100644 (file)
index 0000000..90163b5
--- /dev/null
@@ -0,0 +1,354 @@
+/* Code to save the iptables state, in human readable-form. */
+/* (C) 1999 by Paul 'Rusty' Russell <rusty@rustcorp.com.au> and
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * This code is distributed under the terms of GNU GPL v2
+ *
+ */
+#include <getopt.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <time.h>
+#include "libiptc/libiptc.h"
+#include "iptables.h"
+
+static int binary = 0, counters = 0;
+
+static struct option options[] = {
+       { "binary", 0, 0, 'b' },
+       { "counters", 0, 0, 'c' },
+       { "dump", 0, 0, 'd' },
+       { "table", 1, 0, 't' },
+       { 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 {
+                       /* we can access iface[i-1] here, because 
+                        * a few lines above we make sure that mask[0] != 0 */
+                       if (iface[i-1] != '\0')
+                               printf("+");
+                       break;
+               }
+       }
+
+       printf(" ");
+}
+
+/* These are hardcoded backups in iptables.c, so they are safe */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* FIXME: why don't we use /etc/protocols ? */
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+};
+
+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);
+       }
+}
+
+#if 0
+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;
+}
+#endif
+
+static int print_match(const struct ipt_entry_match *e,
+                       const struct ipt_ip *ip)
+{
+       struct iptables_match *match
+               = find_match(e->u.user.name, TRY_LOAD);
+
+       if (match) {
+               printf("-m %s ", e->u.user.name);
+
+               /* some matches don't provide a save function */
+               if (match->save)
+                       match->save(ip, e);
+       } else {
+               if (e->u.match_size) {
+                       fprintf(stderr,
+                               "Can't find library for match `%s'\n",
+                               e->u.user.name);
+                       exit(1);
+               }
+       }
+       return 0;
+}
+
+/* print a given ip including mask if neccessary */
+static void print_ip(char *prefix, u_int32_t ip, u_int32_t mask, int invert)
+{
+       if (!mask && !ip)
+               return;
+
+       printf("%s %s%u.%u.%u.%u",
+               prefix,
+               invert ? "! " : "",
+               IP_PARTS(ip));
+
+       if (mask != 0xffffffff) 
+               printf("/%u.%u.%u.%u ", IP_PARTS(mask));
+       else
+               printf(" ");
+}
+
+/* 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, 
+               iptc_handle_t *h, const char *chain, int counters)
+{
+       struct ipt_entry_target *t;
+       const char *target_name;
+
+       /* print counters */
+       if (counters)
+               printf("[%llu:%llu] ", e->counters.pcnt, e->counters.bcnt);
+
+       /* print chain name */
+       printf("-A %s ", chain);
+
+       /* Print IP part. */
+       print_ip("-s", e->ip.src.s_addr,e->ip.smsk.s_addr,
+                       e->ip.invflags & IPT_INV_SRCIP);        
+
+       print_ip("-d", e->ip.dst.s_addr, e->ip.dmsk.s_addr,
+                       e->ip.invflags & IPT_INV_DSTIP);
+
+       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 ? "! " : "");
+
+       /* Print matchinfo part */
+       if (e->target_offset) {
+               IPT_MATCH_ITERATE(e, print_match, &e->ip);
+       }
+
+       /* Print target name */ 
+       target_name = iptc_get_target(e, h);
+       if (target_name && (*target_name != '\0'))
+               printf("-j %s ", target_name);
+
+       /* Print targinfo part */
+       t = ipt_get_target((struct ipt_entry *)e);
+       if (t->u.user.name[0]) {
+               struct iptables_target *target
+                       = find_target(t->u.user.name, TRY_LOAD);
+
+               if (!target) {
+                       fprintf(stderr, "Can't find library for target `%s'\n",
+                               t->u.user.name);
+                       exit(1);
+               }
+
+               if (target->save)
+                       target->save(&e->ip, t);
+               else {
+                       /* If the target size is greater than ipt_entry_target
+                        * there is something to be saved, we just don't know
+                        * how to print it */
+                       if (t->u.target_size != 
+                           sizeof(struct ipt_entry_target)) {
+                               fprintf(stderr, "Target `%s' is missing "
+                                               "save function\n",
+                                       t->u.user.name);
+                               exit(1);
+                       }
+               }
+       }
+       printf("\n");
+}
+
+/* Debugging prototype. */
+static int for_each_table(int (*func)(const char *tablename))
+{
+        int ret = 1;
+       FILE *procfile = NULL;
+       char tablename[IPT_TABLE_MAXNAMELEN+1];
+
+       procfile = fopen("/proc/net/ip_tables_names", "r");
+       if (!procfile)
+               return 0;
+
+       while (fgets(tablename, sizeof(tablename), procfile)) {
+               if (tablename[strlen(tablename) - 1] != '\n')
+                       exit_error(OTHER_PROBLEM, 
+                                  "Badly formed tablename `%s'\n",
+                                  tablename);
+               tablename[strlen(tablename) - 1] = '\0';
+               ret &= func(tablename);
+       }
+
+       return ret;
+}
+       
+
+static int do_output(const char *tablename)
+{
+       iptc_handle_t h;
+       const char *chain = NULL;
+
+       if (!tablename)
+               return for_each_table(&do_output);
+
+       h = iptc_init(tablename);
+       if (!h)
+               exit_error(OTHER_PROBLEM, "Can't initialize: %s\n",
+                          iptc_strerror(errno));
+
+       if (!binary) {
+               time_t now = time(NULL);
+
+               printf("# Generated by iptables-save v%s on %s",
+                      IPTABLES_VERSION, ctime(&now));
+               printf("*%s\n", tablename);
+
+               /* Dump out chain names first, 
+                * thereby preventing dependency conflicts */
+               for (chain = iptc_first_chain(&h);
+                    chain;
+                    chain = iptc_next_chain(&h)) {
+                       
+                       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");
+                       }
+               }
+                               
+
+               for (chain = iptc_first_chain(&h);
+                    chain;
+                    chain = iptc_next_chain(&h)) {
+                       const struct ipt_entry *e;
+
+                       /* Dump out rules */
+                       e = iptc_first_rule(chain, &h);
+                       while(e) {
+                               print_rule(e, &h, chain, counters);
+                               e = iptc_next_rule(e, &h);
+                       }
+               }
+
+               now = time(NULL);
+               printf("COMMIT\n");
+               printf("# Completed on %s", ctime(&now));
+       } else {
+               /* Binary, huh?  OK. */
+               exit_error(OTHER_PROBLEM, "Binary NYI\n");
+       }
+
+       iptc_free(&h);
+
+       return 1;
+}
+
+/* Format:
+ * :Chain name POLICY packets bytes
+ * rule
+ */
+int main(int argc, char *argv[])
+{
+       const char *tablename = NULL;
+       int c;
+
+       program_name = "iptables-save";
+       program_version = IPTABLES_VERSION;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       while ((c = getopt_long(argc, argv, "bcdt:", options, NULL)) != -1) {
+               switch (c) {
+               case 'b':
+                       binary = 1;
+                       break;
+
+               case 'c':
+                       counters = 1;
+                       break;
+
+               case 't':
+                       /* Select specific table. */
+                       tablename = optarg;
+                       break;
+               case 'd':
+                       do_output(tablename);
+                       exit(0);
+               }
+       }
+
+       if (optind < argc) {
+               fprintf(stderr, "Unknown arguments found on commandline");
+               exit(1);
+       }
+
+       return !do_output(tablename);
+}
diff --git a/iptables-standalone.c b/iptables-standalone.c
new file mode 100644 (file)
index 0000000..8a4c90e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * Based on the ipchains code by Paul Russell and Michael Neuling
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     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 <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <iptables.h>
+
+int
+main(int argc, char *argv[])
+{
+       int ret;
+       char *table = "filter";
+       iptc_handle_t handle = NULL;
+
+       program_name = "iptables";
+       program_version = IPTABLES_VERSION;
+
+#ifdef NO_SHARED_LIBS
+       init_extensions();
+#endif
+
+       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.c b/iptables.c
new file mode 100644 (file)
index 0000000..5863ef4
--- /dev/null
@@ -0,0 +1,2303 @@
+/* Code to take an iptables-style command line and do it. */
+
+/*
+ * Author: Paul.Russell@rustcorp.com.au and mneuling@radlogic.com.au
+ *
+ * (C) 2000-2002 by the netfilter coreteam <coreteam@netfilter.org>:
+ *                 Paul 'Rusty' Russell <rusty@rustcorp.com.au>
+ *                 Marc Boucher <marc+nf@mbsi.ca>
+ *                 James Morris <jmorris@intercode.com.au>
+ *                 Harald Welte <laforge@gnumonks.org>
+ *                 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
+ *
+ *     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 <unistd.h>
+#include <iptables.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+
+#define FMT_NUMERIC    0x0001
+#define FMT_NOCOUNTS   0x0002
+#define FMT_KILOMEGAGIGA 0x0004
+#define FMT_OPTIONS    0x0008
+#define FMT_NOTABLE    0x0010
+#define FMT_NOTARGET   0x0020
+#define FMT_VIA                0x0040
+#define FMT_NONEWLINE  0x0080
+#define FMT_LINENUMBERS 0x0100
+
+#define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \
+                       | FMT_NUMERIC | FMT_NOTABLE)
+#define FMT(tab,notab) ((format) & FMT_NOTABLE ? (notab) : (tab))
+
+
+#define CMD_NONE               0x0000U
+#define CMD_INSERT             0x0001U
+#define CMD_DELETE             0x0002U
+#define CMD_DELETE_NUM         0x0004U
+#define CMD_REPLACE            0x0008U
+#define CMD_APPEND             0x0010U
+#define CMD_LIST               0x0020U
+#define CMD_FLUSH              0x0040U
+#define CMD_ZERO               0x0080U
+#define CMD_NEW_CHAIN          0x0100U
+#define CMD_DELETE_CHAIN       0x0200U
+#define CMD_SET_POLICY         0x0400U
+#define CMD_CHECK              0x0800U
+#define CMD_RENAME_CHAIN       0x1000U
+#define NUMBER_OF_CMD  13
+static const char cmdflags[] = { 'I', 'D', 'D', 'R', 'A', 'L', 'F', 'Z',
+                                'N', 'X', 'P', 'E' };
+
+#define OPTION_OFFSET 256
+
+#define OPT_NONE       0x00000U
+#define OPT_NUMERIC    0x00001U
+#define OPT_SOURCE     0x00002U
+#define OPT_DESTINATION        0x00004U
+#define OPT_PROTOCOL   0x00008U
+#define OPT_JUMP       0x00010U
+#define OPT_VERBOSE    0x00020U
+#define OPT_EXPANDED   0x00040U
+#define OPT_VIANAMEIN  0x00080U
+#define OPT_VIANAMEOUT 0x00100U
+#define OPT_FRAGMENT    0x00200U
+#define OPT_LINENUMBERS 0x00400U
+#define OPT_COUNTERS   0x00800U
+#define NUMBER_OF_OPT  12
+static const char optflags[NUMBER_OF_OPT]
+= { 'n', 's', 'd', 'p', 'j', 'v', 'x', 'i', 'o', 'f', '3', 'c'};
+
+static struct option original_opts[] = {
+       { "append", 1, 0, 'A' },
+       { "delete", 1, 0,  'D' },
+       { "insert", 1, 0,  'I' },
+       { "replace", 1, 0,  'R' },
+       { "list", 2, 0,  'L' },
+       { "flush", 2, 0,  'F' },
+       { "zero", 2, 0,  'Z' },
+       { "new-chain", 1, 0,  'N' },
+       { "delete-chain", 2, 0,  'X' },
+       { "rename-chain", 1, 0,  'E' },
+       { "policy", 1, 0,  'P' },
+       { "source", 1, 0, 's' },
+       { "destination", 1, 0,  'd' },
+       { "src", 1, 0,  's' }, /* synonym */
+       { "dst", 1, 0,  'd' }, /* synonym */
+       { "protocol", 1, 0,  'p' },
+       { "in-interface", 1, 0, 'i' },
+       { "jump", 1, 0, 'j' },
+       { "table", 1, 0, 't' },
+       { "match", 1, 0, 'm' },
+       { "numeric", 0, 0, 'n' },
+       { "out-interface", 1, 0, 'o' },
+       { "verbose", 0, 0, 'v' },
+       { "exact", 0, 0, 'x' },
+       { "fragments", 0, 0, 'f' },
+       { "version", 0, 0, 'V' },
+       { "help", 2, 0, 'h' },
+       { "line-numbers", 0, 0, '0' },
+       { "modprobe", 1, 0, 'M' },
+       { "set-counters", 1, 0, 'c' },
+       { 0 }
+};
+
+/* we need this for iptables-restore.  iptables-restore.c sets line to the
+ * current line of the input file, in order  to give a more precise error
+ * message.  iptables itself doesn't need this, so it is initialized to the
+ * magic number of -1 */
+int line = -1;
+
+#ifndef __OPTIMIZE__
+struct ipt_entry_target *
+ipt_get_target(struct ipt_entry *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options.  If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ *  +  compulsory
+ *  x  illegal
+ *     optional
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+       /*     -n  -s  -d  -p  -j  -v  -x  -i  -o  -f  --line */
+/*INSERT*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*DELETE_NUM*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*REPLACE*/   {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*APPEND*/    {'x',' ',' ',' ',' ',' ','x',' ',' ',' ','x'},
+/*LIST*/      {' ','x','x','x','x',' ',' ','x','x','x',' '},
+/*FLUSH*/     {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*ZERO*/      {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*NEW_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*DEL_CHAIN*/ {'x','x','x','x','x',' ','x','x','x','x','x'},
+/*SET_POLICY*/{'x','x','x','x','x',' ','x','x','x','x','x'},
+/*CHECK*/     {'x','+','+','+','x',' ','x',' ',' ',' ','x'},
+/*RENAME*/    {'x','x','x','x','x',' ','x','x','x','x','x'}
+};
+
+static int inverse_for_options[NUMBER_OF_OPT] =
+{
+/* -n */ 0,
+/* -s */ IPT_INV_SRCIP,
+/* -d */ IPT_INV_DSTIP,
+/* -p */ IPT_INV_PROTO,
+/* -j */ 0,
+/* -v */ 0,
+/* -x */ 0,
+/* -i */ IPT_INV_VIA_IN,
+/* -o */ IPT_INV_VIA_OUT,
+/* -f */ IPT_INV_FRAG,
+/*--line*/ 0
+};
+
+const char *program_version;
+const char *program_name;
+
+/* Keeping track of external matches and targets: linked lists.  */
+struct iptables_match *iptables_matches = NULL;
+struct iptables_target *iptables_targets = NULL;
+
+/* Extra debugging from libiptc */
+extern void dump_entries(const iptc_handle_t handle);
+
+/* A few hardcoded protocols for 'all' and in case the user has no
+   /etc/protocols */
+struct pprot {
+       char *name;
+       u_int8_t num;
+};
+
+/* Primitive headers... */
+/* defined in netinet/in.h */
+#if 0
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51
+#endif
+#endif
+
+static const struct pprot chain_protos[] = {
+       { "tcp", IPPROTO_TCP },
+       { "udp", IPPROTO_UDP },
+       { "icmp", IPPROTO_ICMP },
+       { "esp", IPPROTO_ESP },
+       { "ah", IPPROTO_AH },
+       { "all", 0 },
+};
+
+static char *
+proto_to_name(u_int8_t proto, int nolookup)
+{
+       unsigned int i;
+
+       if (proto && !nolookup) {
+               struct protoent *pent = getprotobynumber(proto);
+               if (pent)
+                       return pent->p_name;
+       }
+
+       for (i = 0; i < sizeof(chain_protos)/sizeof(struct pprot); i++)
+               if (chain_protos[i].num == proto)
+                       return chain_protos[i].name;
+
+       return NULL;
+}
+
+struct in_addr *
+dotted_to_addr(const char *dotted)
+{
+       static struct in_addr addr;
+       unsigned char *addrp;
+       char *p, *q;
+       unsigned int onebyte;
+       int i;
+       char buf[20];
+
+       /* copy dotted string, because we need to modify it */
+       strncpy(buf, dotted, sizeof(buf) - 1);
+       addrp = (unsigned char *) &(addr.s_addr);
+
+       p = buf;
+       for (i = 0; i < 3; i++) {
+               if ((q = strchr(p, '.')) == NULL)
+                       return (struct in_addr *) NULL;
+
+               *q = '\0';
+               if (string_to_number(p, 0, 255, &onebyte) == -1)
+                       return (struct in_addr *) NULL;
+
+               addrp[i] = (unsigned char) onebyte;
+               p = q + 1;
+       }
+
+       /* we've checked 3 bytes, now we check the last one */
+       if (string_to_number(p, 0, 255, &onebyte) == -1)
+               return (struct in_addr *) NULL;
+
+       addrp[3] = (unsigned char) onebyte;
+
+       return &addr;
+}
+
+static struct in_addr *
+network_to_addr(const char *name)
+{
+       struct netent *net;
+       static struct in_addr addr;
+
+       if ((net = getnetbyname(name)) != NULL) {
+               if (net->n_addrtype != AF_INET)
+                       return (struct in_addr *) NULL;
+               addr.s_addr = htonl((unsigned long) net->n_net);
+               return &addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static void
+inaddrcpy(struct in_addr *dst, struct in_addr *src)
+{
+       /* memcpy(dst, src, sizeof(struct in_addr)); */
+       dst->s_addr = src->s_addr;
+}
+
+void
+exit_error(enum exittype status, char *msg, ...)
+{
+       va_list args;
+
+       va_start(args, msg);
+       fprintf(stderr, "%s v%s: ", program_name, program_version);
+       vfprintf(stderr, msg, args);
+       va_end(args);
+       fprintf(stderr, "\n");
+       if (status == PARAMETER_PROBLEM)
+               exit_tryhelp(status);
+       if (status == VERSION_PROBLEM)
+               fprintf(stderr,
+                       "Perhaps iptables or your kernel needs to be upgraded.\n");
+       exit(status);
+}
+
+void
+exit_tryhelp(int status)
+{
+       if (line != -1)
+               fprintf(stderr, "Error occurred at line: %d\n", line);
+       fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+                       program_name, program_name );
+       exit(status);
+}
+
+void
+exit_printhelp(void)
+{
+       struct iptables_match *m = NULL;
+       struct iptables_target *t = NULL;
+
+       printf("%s v%s\n\n"
+"Usage: %s -[AD] chain rule-specification [options]\n"
+"       %s -[RI] chain rulenum rule-specification [options]\n"
+"       %s -D chain rulenum [options]\n"
+"       %s -[LFZ] [chain] [options]\n"
+"       %s -[NX] chain\n"
+"       %s -E old-chain-name new-chain-name\n"
+"       %s -P chain target [options]\n"
+"       %s -h (print this help information)\n\n",
+              program_name, program_version, program_name, program_name,
+              program_name, program_name, program_name, program_name,
+              program_name, program_name);
+
+       printf(
+"Commands:\n"
+"Either long or short options are allowed.\n"
+"  --append  -A chain          Append to chain\n"
+"  --delete  -D chain          Delete matching rule from chain\n"
+"  --delete  -D chain rulenum\n"
+"                              Delete rule rulenum (1 = first) from chain\n"
+"  --insert  -I chain [rulenum]\n"
+"                              Insert in chain as rulenum (default 1=first)\n"
+"  --replace -R chain rulenum\n"
+"                              Replace rule rulenum (1 = first) in chain\n"
+"  --list    -L [chain]                List the rules in a chain or all chains\n"
+"  --flush   -F [chain]                Delete all rules in  chain or all chains\n"
+"  --zero    -Z [chain]                Zero counters in chain or all chains\n"
+"  --new     -N chain          Create a new user-defined chain\n"
+"  --delete-chain\n"
+"            -X [chain]                Delete a user-defined chain\n"
+"  --policy  -P chain target\n"
+"                              Change policy on chain to target\n"
+"  --rename-chain\n"
+"            -E old-chain new-chain\n"
+"                              Change chain name, (moving any references)\n"
+
+"Options:\n"
+"  --proto     -p [!] proto    protocol: by number or name, eg. `tcp'\n"
+"  --source    -s [!] address[/mask]\n"
+"                              source specification\n"
+"  --destination -d [!] address[/mask]\n"
+"                              destination specification\n"
+"  --in-interface -i [!] input name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --jump      -j target\n"
+"                              target for rule (may load target extension)\n"
+"  --match     -m match\n"
+"                              extended match (may load extension)\n"
+"  --numeric   -n              numeric output of addresses and ports\n"
+"  --out-interface -o [!] output name[+]\n"
+"                              network interface name ([+] for wildcard)\n"
+"  --table     -t table        table to manipulate (default: `filter')\n"
+"  --verbose   -v              verbose mode\n"
+"  --line-numbers              print line numbers when listing\n"
+"  --exact     -x              expand numbers (display exact values)\n"
+"[!] --fragment        -f              match second or further fragments only\n"
+"  --modprobe=<command>                try to insert modules using this command\n"
+"  --set-counters PKTS BYTES   set the counter during insert/append\n"
+"[!] --version -V              print package version.\n");
+
+       /* Print out any special helps. A user might like to be able
+          to add a --help to the commandline, and see expected
+          results. So we call help for all matches & targets */
+       for (t=iptables_targets;t;t=t->next) {
+               printf("\n");
+               t->help();
+       }
+       for (m=iptables_matches;m;m=m->next) {
+               printf("\n");
+               m->help();
+       }
+       exit(0);
+}
+
+static void
+generic_opt_check(int command, int options)
+{
+       int i, j, legal = 0;
+
+       /* Check that commands are valid with options.  Complicated by the
+        * fact that if an option is legal with *any* command given, it is
+        * legal overall (ie. -z and -l).
+        */
+       for (i = 0; i < NUMBER_OF_OPT; i++) {
+               legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */
+
+               for (j = 0; j < NUMBER_OF_CMD; j++) {
+                       if (!(command & (1<<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, int *optind, int argc)
+{
+       if (option && strcmp(option, "!") == 0) {
+               if (*invert)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Multiple `!' flags not allowed");
+               *invert = TRUE;
+               if (optind) {
+                       *optind = *optind+1;
+                       if (argc && *optind > argc)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "no argument following `!'");
+               }
+
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void *
+fw_calloc(size_t count, size_t size)
+{
+       void *p;
+
+       if ((p = calloc(count, size)) == NULL) {
+               perror("iptables: calloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static void *
+fw_malloc(size_t size)
+{
+       void *p;
+
+       if ((p = malloc(size)) == NULL) {
+               perror("iptables: malloc failed");
+               exit(1);
+       }
+       return p;
+}
+
+static struct in_addr *
+host_to_addr(const char *name, unsigned int *naddr)
+{
+       struct hostent *host;
+       struct in_addr *addr;
+       unsigned int i;
+
+       *naddr = 0;
+       if ((host = gethostbyname(name)) != NULL) {
+               if (host->h_addrtype != AF_INET ||
+                   host->h_length != sizeof(struct in_addr))
+                       return (struct in_addr *) NULL;
+
+               while (host->h_addr_list[*naddr] != (char *) NULL)
+                       (*naddr)++;
+               addr = fw_calloc(*naddr, sizeof(struct in_addr));
+               for (i = 0; i < *naddr; i++)
+                       inaddrcpy(&(addr[i]),
+                                 (struct in_addr *) host->h_addr_list[i]);
+               return addr;
+       }
+
+       return (struct in_addr *) NULL;
+}
+
+static char *
+addr_to_host(const struct in_addr *addr)
+{
+       struct hostent *host;
+
+       if ((host = gethostbyaddr((char *) addr,
+                                 sizeof(struct in_addr), AF_INET)) != NULL)
+               return (char *) host->h_name;
+
+       return (char *) NULL;
+}
+
+/*
+ *     All functions starting with "parse" should succeed, otherwise
+ *     the program fails.
+ *     Most routines return pointers to static data that may change
+ *     between calls to the same or other routines with a few exceptions:
+ *     "host_to_addr", "parse_hostnetwork", and "parse_hostnetworkmask"
+ *     return global static data.
+*/
+
+static struct in_addr *
+parse_hostnetwork(const char *name, unsigned int *naddrs)
+{
+       struct in_addr *addrp, *addrptmp;
+
+       if ((addrptmp = dotted_to_addr(name)) != NULL ||
+           (addrptmp = network_to_addr(name)) != NULL) {
+               addrp = fw_malloc(sizeof(struct in_addr));
+               inaddrcpy(addrp, addrptmp);
+               *naddrs = 1;
+               return addrp;
+       }
+       if ((addrp = host_to_addr(name, naddrs)) != NULL)
+               return addrp;
+
+       exit_error(PARAMETER_PROBLEM, "host/network `%s' not found", name);
+}
+
+static struct in_addr *
+parse_mask(char *mask)
+{
+       static struct in_addr maskaddr;
+       struct in_addr *addrp;
+       unsigned int bits;
+
+       if (mask == NULL) {
+               /* no mask at all defaults to 32 bits */
+               maskaddr.s_addr = 0xFFFFFFFF;
+               return &maskaddr;
+       }
+       if ((addrp = dotted_to_addr(mask)) != NULL)
+               /* dotted_to_addr already returns a network byte order addr */
+               return addrp;
+       if (string_to_number(mask, 0, 32, &bits) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "invalid mask `%s' specified", mask);
+       if (bits != 0) {
+               maskaddr.s_addr = htonl(0xFFFFFFFF << (32 - bits));
+               return &maskaddr;
+       }
+
+       maskaddr.s_addr = 0L;
+       return &maskaddr;
+}
+
+void
+parse_hostnetworkmask(const char *name, struct in_addr **addrpp,
+                     struct in_addr *maskp, unsigned int *naddrs)
+{
+       struct in_addr *addrp;
+       char buf[256];
+       char *p;
+       int i, j, k, n;
+
+       strncpy(buf, name, sizeof(buf) - 1);
+       if ((p = strrchr(buf, '/')) != NULL) {
+               *p = '\0';
+               addrp = parse_mask(p + 1);
+       } else
+               addrp = parse_mask(NULL);
+       inaddrcpy(maskp, addrp);
+
+       /* if a null mask is given, the name is ignored, like in "any/0" */
+       if (maskp->s_addr == 0L)
+               strcpy(buf, "0.0.0.0");
+
+       addrp = *addrpp = parse_hostnetwork(buf, naddrs);
+       n = *naddrs;
+       for (i = 0, j = 0; i < n; i++) {
+               addrp[j++].s_addr &= maskp->s_addr;
+               for (k = 0; k < j - 1; k++) {
+                       if (addrp[k].s_addr == addrp[j - 1].s_addr) {
+                               (*naddrs)--;
+                               j--;
+                               break;
+                       }
+               }
+       }
+}
+
+struct iptables_match *
+find_match(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_match *ptr;
+
+       for (ptr = iptables_matches; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified target as match. */
+                       ptr = find_match(name, DONT_LOAD);
+
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load match `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load match `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find match `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+/* Christophe Burki wants `-p 6' to imply `-m tcp'.  */
+static struct iptables_match *
+find_proto(const char *pname, enum ipt_tryload tryload, int nolookup)
+{
+       unsigned int proto;
+
+       if (string_to_number(pname, 0, 255, &proto) != -1) {
+               char *protoname = proto_to_name(proto, nolookup);
+
+               if (protoname)
+                       return find_match(protoname, tryload);
+       } else
+               return find_match(pname, tryload);
+
+       return NULL;
+}
+
+u_int16_t
+parse_protocol(const char *s)
+{
+       unsigned int proto;
+
+       if (string_to_number(s, 0, 255, &proto) == -1) {
+               struct protoent *pent;
+
+               if ((pent = getprotobyname(s)))
+                       proto = pent->p_proto;
+               else {
+                       unsigned int i;
+                       for (i = 0;
+                            i < sizeof(chain_protos)/sizeof(struct pprot);
+                            i++) {
+                               if (strcmp(s, chain_protos[i].name) == 0) {
+                                       proto = chain_protos[i].num;
+                                       break;
+                               }
+                       }
+                       if (i == sizeof(chain_protos)/sizeof(struct pprot))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unknown protocol `%s' specified",
+                                          s);
+               }
+       }
+
+       return (u_int16_t)proto;
+}
+
+static void
+parse_interface(const char *arg, char *vianame, unsigned char *mask)
+{
+       int vialen = strlen(arg);
+       unsigned int i;
+
+       memset(mask, 0, IFNAMSIZ);
+       memset(vianame, 0, IFNAMSIZ);
+
+       if (vialen + 1 > IFNAMSIZ)
+               exit_error(PARAMETER_PROBLEM,
+                          "interface name `%s' must be shorter than IFNAMSIZ"
+                          " (%i)", arg, IFNAMSIZ-1);
+
+       strcpy(vianame, arg);
+       if (vialen == 0)
+               memset(mask, 0, IFNAMSIZ);
+       else if (vianame[vialen - 1] == '+') {
+               memset(mask, 0xFF, vialen - 1);
+               memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1);
+               /* Don't remove `+' here! -HW */
+       } else {
+               /* Include nul-terminator in match */
+               memset(mask, 0xFF, vialen + 1);
+               memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1);
+               for (i = 0; vianame[i]; i++) {
+                       if (!isalnum(vianame[i]) 
+                           && vianame[i] != '_' 
+                           && vianame[i] != '.') {
+                               printf("Warning: wierd character in interface"
+                                      " `%s' (No aliases, :, ! or *).\n",
+                                      vianame);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Can't be zero. */
+static int
+parse_rulenumber(const char *rule)
+{
+       unsigned int rulenum;
+
+       if (string_to_number(rule, 1, INT_MAX, &rulenum) == -1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid rule number `%s'", rule);
+
+       return rulenum;
+}
+
+static const char *
+parse_target(const char *targetname)
+{
+       const char *ptr;
+
+       if (strlen(targetname) < 1)
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name (too short)");
+
+       if (strlen(targetname)+1 > sizeof(ipt_chainlabel))
+               exit_error(PARAMETER_PROBLEM,
+                          "Invalid target name `%s' (%i chars max)",
+                          targetname, sizeof(ipt_chainlabel)-1);
+
+       for (ptr = targetname; *ptr; ptr++)
+               if (isspace(*ptr))
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Invalid target name `%s'", targetname);
+       return targetname;
+}
+
+static char *
+addr_to_network(const struct in_addr *addr)
+{
+       struct netent *net;
+
+       if ((net = getnetbyaddr((long) ntohl(addr->s_addr), AF_INET)) != NULL)
+               return (char *) net->n_name;
+
+       return (char *) NULL;
+}
+
+char *
+addr_to_dotted(const struct in_addr *addrp)
+{
+       static char buf[20];
+       const unsigned char *bytep;
+
+       bytep = (const unsigned char *) &(addrp->s_addr);
+       sprintf(buf, "%d.%d.%d.%d", bytep[0], bytep[1], bytep[2], bytep[3]);
+       return buf;
+}
+
+char *
+addr_to_anyname(const struct in_addr *addr)
+{
+       char *name;
+
+       if ((name = addr_to_host(addr)) != NULL ||
+           (name = addr_to_network(addr)) != NULL)
+               return name;
+
+       return addr_to_dotted(addr);
+}
+
+char *
+mask_to_dotted(const struct in_addr *mask)
+{
+       int i;
+       static char buf[20];
+       u_int32_t maskaddr, bits;
+
+       maskaddr = ntohl(mask->s_addr);
+
+       if (maskaddr == 0xFFFFFFFFL)
+               /* we don't want to see "/32" */
+               return "";
+
+       i = 32;
+       bits = 0xFFFFFFFEL;
+       while (--i >= 0 && maskaddr != bits)
+               bits <<= 1;
+       if (i >= 0)
+               sprintf(buf, "/%d", i);
+       else
+               /* mask was not a decent combination of 1's and 0's */
+               sprintf(buf, "/%s", addr_to_dotted(mask));
+
+       return buf;
+}
+
+int
+string_to_number(const char *s, unsigned int min, unsigned int max,
+                unsigned int *ret)
+{
+       long number;
+       char *end;
+
+       /* Handle hex, octal, etc. */
+       errno = 0;
+       number = strtol(s, &end, 0);
+       if (*end == '\0' && end != s) {
+               /* we parsed a number, let's see if we want this */
+               if (errno != ERANGE && min <= number && number <= max) {
+                       *ret = number;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static void
+set_option(unsigned int *options, unsigned int option, u_int8_t *invflg,
+          int invert)
+{
+       if (*options & option)
+               exit_error(PARAMETER_PROBLEM, "multiple -%c flags not allowed",
+                          opt2char(option));
+       *options |= option;
+
+       if (invert) {
+               unsigned int i;
+               for (i = 0; 1 << i != option; i++);
+
+               if (!inverse_for_options[i])
+                       exit_error(PARAMETER_PROBLEM,
+                                  "cannot have ! before -%c",
+                                  opt2char(option));
+               *invflg |= inverse_for_options[i];
+       }
+}
+
+struct iptables_target *
+find_target(const char *name, enum ipt_tryload tryload)
+{
+       struct iptables_target *ptr;
+
+       /* Standard target? */
+       if (strcmp(name, "") == 0
+           || strcmp(name, IPTC_LABEL_ACCEPT) == 0
+           || strcmp(name, IPTC_LABEL_DROP) == 0
+           || strcmp(name, IPTC_LABEL_QUEUE) == 0
+           || strcmp(name, IPTC_LABEL_RETURN) == 0)
+               name = "standard";
+
+       for (ptr = iptables_targets; ptr; ptr = ptr->next) {
+               if (strcmp(name, ptr->name) == 0)
+                       break;
+       }
+
+#ifndef NO_SHARED_LIBS
+       if (!ptr && tryload != DONT_LOAD) {
+               char path[sizeof(IPT_LIB_DIR) + sizeof("/libipt_.so")
+                        + strlen(name)];
+               sprintf(path, IPT_LIB_DIR "/libipt_%s.so", name);
+               if (dlopen(path, RTLD_NOW)) {
+                       /* Found library.  If it didn't register itself,
+                          maybe they specified match as a target. */
+                       ptr = find_target(name, DONT_LOAD);
+                       if (!ptr)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Couldn't load target `%s'\n",
+                                          name);
+               } else if (tryload == LOAD_MUST_SUCCEED)
+                       exit_error(PARAMETER_PROBLEM,
+                                  "Couldn't load target `%s':%s\n",
+                                  name, dlerror());
+       }
+#else
+       if (ptr && !ptr->loaded) {
+               if (tryload != DONT_LOAD)
+                       ptr->loaded = 1;
+               else
+                       ptr = NULL;
+       }
+       if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
+               exit_error(PARAMETER_PROBLEM,
+                          "Couldn't find target `%s'\n", name);
+       }
+#endif
+
+       if (ptr)
+               ptr->used = 1;
+
+       return ptr;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+             unsigned int *option_offset)
+{
+       unsigned int num_old, num_new, i;
+       struct option *merge;
+
+       for (num_old = 0; oldopts[num_old].name; num_old++);
+       for (num_new = 0; newopts[num_new].name; num_new++);
+
+       global_option_offset += OPTION_OFFSET;
+       *option_offset = global_option_offset;
+
+       merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+       memcpy(merge, oldopts, num_old * sizeof(struct option));
+       for (i = 0; i < num_new; i++) {
+               merge[num_old + i] = newopts[i];
+               merge[num_old + i].val += *option_offset;
+       }
+       memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+       return merge;
+}
+
+void
+register_match(struct iptables_match *me)
+{
+       struct iptables_match **i;
+
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_match(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: match `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Append to list. */
+       for (i = &iptables_matches; *i; i = &(*i)->next);
+       me->next = NULL;
+       *i = me;
+
+       me->m = NULL;
+       me->mflags = 0;
+}
+
+void
+register_target(struct iptables_target *me)
+{
+       if (strcmp(me->version, program_version) != 0) {
+               fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
+                       program_name, me->name, me->version, program_version);
+               exit(1);
+       }
+
+       if (find_target(me->name, DONT_LOAD)) {
+               fprintf(stderr, "%s: target `%s' already registered.\n",
+                       program_name, me->name);
+               exit(1);
+       }
+
+       if (me->size != IPT_ALIGN(me->size)) {
+               fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
+                       program_name, me->name, me->size);
+               exit(1);
+       }
+
+       /* Prepend to list. */
+       me->next = iptables_targets;
+       iptables_targets = me;
+       me->t = NULL;
+       me->tflags = 0;
+}
+
+static void
+print_num(u_int64_t number, unsigned int format)
+{
+       if (format & FMT_KILOMEGAGIGA) {
+               if (number > 99999) {
+                       number = (number + 500) / 1000;
+                       if (number > 9999) {
+                               number = (number + 500) / 1000;
+                               if (number > 9999) {
+                                       number = (number + 500) / 1000;
+                                       if (number > 9999) {
+                                               number = (number + 500) / 1000;
+                                               printf(FMT("%4lluT ","%lluT "), number);
+                                       }
+                                       else printf(FMT("%4lluG ","%lluG "), number);
+                               }
+                               else printf(FMT("%4lluM ","%lluM "), number);
+                       } else
+                               printf(FMT("%4lluK ","%lluK "), number);
+               } else
+                       printf(FMT("%5llu ","%llu "), number);
+       } else
+               printf(FMT("%8llu ","%llu "), number);
+}
+
+
+static void
+print_header(unsigned int format, const char *chain, iptc_handle_t *handle)
+{
+       struct ipt_counters counters;
+       const char *pol = iptc_get_policy(chain, &counters, handle);
+       printf("Chain %s", chain);
+       if (pol) {
+               printf(" (policy %s", pol);
+               if (!(format & FMT_NOCOUNTS)) {
+                       fputc(' ', stdout);
+                       print_num(counters.pcnt, (format|FMT_NOTABLE));
+                       fputs("packets, ", stdout);
+                       print_num(counters.bcnt, (format|FMT_NOTABLE));
+                       fputs("bytes", stdout);
+               }
+               printf(")\n");
+       } else {
+               unsigned int refs;
+               if (!iptc_get_references(&refs, chain, handle))
+                       printf(" (ERROR obtaining refs)\n");
+               else
+                       printf(" (%u references)\n", refs);
+       }
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4s ", "%s "), "num");
+       if (!(format & FMT_NOCOUNTS)) {
+               if (format & FMT_KILOMEGAGIGA) {
+                       printf(FMT("%5s ","%s "), "pkts");
+                       printf(FMT("%5s ","%s "), "bytes");
+               } else {
+                       printf(FMT("%8s ","%s "), "pkts");
+                       printf(FMT("%10s ","%s "), "bytes");
+               }
+       }
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ","%s "), "target");
+       fputs(" prot ", stdout);
+       if (format & FMT_OPTIONS)
+               fputs("opt", stdout);
+       if (format & FMT_VIA) {
+               printf(FMT(" %-6s ","%s "), "in");
+               printf(FMT("%-6s ","%s "), "out");
+       }
+       printf(FMT(" %-19s ","%s "), "source");
+       printf(FMT(" %-19s "," %s "), "destination");
+       printf("\n");
+}
+
+
+static int
+print_match(const struct ipt_entry_match *m,
+           const struct ipt_ip *ip,
+           int numeric)
+{
+       struct iptables_match *match = find_match(m->u.user.name, TRY_LOAD);
+
+       if (match) {
+               if (match->print)
+                       match->print(ip, m, numeric);
+               else
+                       printf("%s ", match->name);
+       } else {
+               if (m->u.user.name[0])
+                       printf("UNKNOWN match `%s' ", m->u.user.name);
+       }
+       /* Don't stop iterating. */
+       return 0;
+}
+
+/* e is called `fw' here for hysterical raisins */
+static void
+print_firewall(const struct ipt_entry *fw,
+              const char *targname,
+              unsigned int num,
+              unsigned int format,
+              const iptc_handle_t handle)
+{
+       struct iptables_target *target = NULL;
+       const struct ipt_entry_target *t;
+       u_int8_t flags;
+       char buf[BUFSIZ];
+
+       if (!iptc_is_chain(targname, handle))
+               target = find_target(targname, TRY_LOAD);
+       else
+               target = find_target(IPT_STANDARD_TARGET, LOAD_MUST_SUCCEED);
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       flags = fw->ip.flags;
+
+       if (format & FMT_LINENUMBERS)
+               printf(FMT("%-4u ", "%u "), num+1);
+
+       if (!(format & FMT_NOCOUNTS)) {
+               print_num(fw->counters.pcnt, format);
+               print_num(fw->counters.bcnt, format);
+       }
+
+       if (!(format & FMT_NOTARGET))
+               printf(FMT("%-9s ", "%s "), targname);
+
+       fputc(fw->ip.invflags & IPT_INV_PROTO ? '!' : ' ', stdout);
+       {
+               char *pname = proto_to_name(fw->ip.proto, format&FMT_NUMERIC);
+               if (pname)
+                       printf(FMT("%-5s", "%s "), pname);
+               else
+                       printf(FMT("%-5hu", "%hu "), fw->ip.proto);
+       }
+
+       if (format & FMT_OPTIONS) {
+               if (format & FMT_NOTABLE)
+                       fputs("opt ", stdout);
+               fputc(fw->ip.invflags & IPT_INV_FRAG ? '!' : '-', stdout);
+               fputc(flags & IPT_F_FRAG ? 'f' : '-', stdout);
+               fputc(' ', stdout);
+       }
+
+       if (format & FMT_VIA) {
+               char iface[IFNAMSIZ+2];
+
+               if (fw->ip.invflags & IPT_INV_VIA_IN) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.iniface[0] != '\0') {
+                       strcat(iface, fw->ip.iniface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT(" %-6s ","in %s "), iface);
+
+               if (fw->ip.invflags & IPT_INV_VIA_OUT) {
+                       iface[0] = '!';
+                       iface[1] = '\0';
+               }
+               else iface[0] = '\0';
+
+               if (fw->ip.outiface[0] != '\0') {
+                       strcat(iface, fw->ip.outiface);
+               }
+               else if (format & FMT_NUMERIC) strcat(iface, "*");
+               else strcat(iface, "any");
+               printf(FMT("%-6s ","out %s "), iface);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_SRCIP ? '!' : ' ', stdout);
+       if (fw->ip.smsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","%s "), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.src)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.src)));
+               strcat(buf, mask_to_dotted(&(fw->ip.smsk)));
+               printf(FMT("%-19s ","%s "), buf);
+       }
+
+       fputc(fw->ip.invflags & IPT_INV_DSTIP ? '!' : ' ', stdout);
+       if (fw->ip.dmsk.s_addr == 0L && !(format & FMT_NUMERIC))
+               printf(FMT("%-19s ","-> %s"), "anywhere");
+       else {
+               if (format & FMT_NUMERIC)
+                       sprintf(buf, "%s", addr_to_dotted(&(fw->ip.dst)));
+               else
+                       sprintf(buf, "%s", addr_to_anyname(&(fw->ip.dst)));
+               strcat(buf, mask_to_dotted(&(fw->ip.dmsk)));
+               printf(FMT("%-19s ","-> %s"), buf);
+       }
+
+       if (format & FMT_NOTABLE)
+               fputs("  ", stdout);
+
+       IPT_MATCH_ITERATE(fw, print_match, &fw->ip, format & FMT_NUMERIC);
+
+       if (target) {
+               if (target->print)
+                       /* Print the target information. */
+                       target->print(&fw->ip, t, format & FMT_NUMERIC);
+       } else if (t->u.target_size != sizeof(*t))
+               printf("[%u bytes of unknown target data] ",
+                      t->u.target_size - sizeof(*t));
+
+       if (!(format & FMT_NONEWLINE))
+               fputc('\n', stdout);
+}
+
+static void
+print_firewall_line(const struct ipt_entry *fw,
+                   const iptc_handle_t h)
+{
+       struct ipt_entry_target *t;
+
+       t = ipt_get_target((struct ipt_entry *)fw);
+       print_firewall(fw, t->u.user.name, 0, FMT_PRINT_RULE, h);
+}
+
+static int
+append_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_append_entry(chain, fw, handle);
+               }
+       }
+
+       return ret;
+}
+
+static int
+replace_entry(const ipt_chainlabel chain,
+             struct ipt_entry *fw,
+             unsigned int rulenum,
+             const struct in_addr *saddr,
+             const struct in_addr *daddr,
+             int verbose,
+             iptc_handle_t *handle)
+{
+       fw->ip.src.s_addr = saddr->s_addr;
+       fw->ip.dst.s_addr = daddr->s_addr;
+
+       if (verbose)
+               print_firewall_line(fw, *handle);
+       return iptc_replace_entry(chain, fw, rulenum, handle);
+}
+
+static int
+insert_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int rulenum,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_insert_entry(chain, fw, rulenum, handle);
+               }
+       }
+
+       return ret;
+}
+
+static unsigned char *
+make_delete_mask(struct ipt_entry *fw)
+{
+       /* Establish mask for comparison */
+       unsigned int size;
+       struct iptables_match *m;
+       unsigned char *mask, *mptr;
+
+       size = sizeof(struct ipt_entry);
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       mask = fw_calloc(1, size
+                        + IPT_ALIGN(sizeof(struct ipt_entry_target))
+                        + iptables_targets->size);
+
+       memset(mask, 0xFF, sizeof(struct ipt_entry));
+       mptr = mask + sizeof(struct ipt_entry);
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memset(mptr, 0xFF,
+                      IPT_ALIGN(sizeof(struct ipt_entry_match))
+                      + m->userspacesize);
+               mptr += IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
+       }
+
+       memset(mptr, 0xFF,
+              IPT_ALIGN(sizeof(struct ipt_entry_target))
+              + iptables_targets->userspacesize);
+
+       return mask;
+}
+
+static int
+delete_entry(const ipt_chainlabel chain,
+            struct ipt_entry *fw,
+            unsigned int nsaddrs,
+            const struct in_addr saddrs[],
+            unsigned int ndaddrs,
+            const struct in_addr daddrs[],
+            int verbose,
+            iptc_handle_t *handle)
+{
+       unsigned int i, j;
+       int ret = 1;
+       unsigned char *mask;
+
+       mask = make_delete_mask(fw);
+       for (i = 0; i < nsaddrs; i++) {
+               fw->ip.src.s_addr = saddrs[i].s_addr;
+               for (j = 0; j < ndaddrs; j++) {
+                       fw->ip.dst.s_addr = daddrs[j].s_addr;
+                       if (verbose)
+                               print_firewall_line(fw, *handle);
+                       ret &= iptc_delete_entry(chain, fw, mask, handle);
+               }
+       }
+       return ret;
+}
+
+int
+for_each_chain(int (*fn)(const ipt_chainlabel, int, iptc_handle_t *),
+              int verbose, int builtinstoo, iptc_handle_t *handle)
+{
+        int ret = 1;
+       const char *chain;
+       char *chains;
+       unsigned int i, chaincount = 0;
+
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               chaincount++;
+               chain = iptc_next_chain(handle);
+        }
+
+       chains = fw_malloc(sizeof(ipt_chainlabel) * chaincount);
+       i = 0;
+       chain = iptc_first_chain(handle);
+       while (chain) {
+               strcpy(chains + i*sizeof(ipt_chainlabel), chain);
+               i++;
+               chain = iptc_next_chain(handle);
+        }
+
+       for (i = 0; i < chaincount; i++) {
+               if (!builtinstoo
+                   && iptc_builtin(chains + i*sizeof(ipt_chainlabel),
+                                   *handle))
+                       continue;
+               ret &= fn(chains + i*sizeof(ipt_chainlabel), verbose, handle);
+       }
+
+       free(chains);
+        return ret;
+}
+
+int
+flush_entries(const ipt_chainlabel chain, int verbose,
+             iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(flush_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Flushing chain `%s'\n", chain);
+       return iptc_flush_entries(chain, handle);
+}
+
+static int
+zero_entries(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(zero_entries, verbose, 1, handle);
+
+       if (verbose)
+               fprintf(stdout, "Zeroing chain `%s'\n", chain);
+       return iptc_zero_entries(chain, handle);
+}
+
+int
+delete_chain(const ipt_chainlabel chain, int verbose,
+            iptc_handle_t *handle)
+{
+       if (!chain)
+               return for_each_chain(delete_chain, verbose, 0, handle);
+
+       if (verbose)
+               fprintf(stdout, "Deleting chain `%s'\n", chain);
+       return iptc_delete_chain(chain, handle);
+}
+
+static int
+list_entries(const ipt_chainlabel chain, int verbose, int numeric,
+            int expanded, int linenumbers, iptc_handle_t *handle)
+{
+       int found = 0;
+       unsigned int format;
+       const char *this;
+
+       format = FMT_OPTIONS;
+       if (!verbose)
+               format |= FMT_NOCOUNTS;
+       else
+               format |= FMT_VIA;
+
+       if (numeric)
+               format |= FMT_NUMERIC;
+
+       if (!expanded)
+               format |= FMT_KILOMEGAGIGA;
+
+       if (linenumbers)
+               format |= FMT_LINENUMBERS;
+
+       for (this = iptc_first_chain(handle);
+            this;
+            this = iptc_next_chain(handle)) {
+               const struct ipt_entry *i;
+               unsigned int num;
+
+               if (chain && strcmp(chain, this) != 0)
+                       continue;
+
+               if (found) printf("\n");
+
+               print_header(format, this, handle);
+               i = iptc_first_rule(this, handle);
+
+               num = 0;
+               while (i) {
+                       print_firewall(i,
+                                      iptc_get_target(i, handle),
+                                      num++,
+                                      format,
+                                      *handle);
+                       i = iptc_next_rule(i, handle);
+               }
+               found = 1;
+       }
+
+       errno = ENOENT;
+       return found;
+}
+
+static char *get_modprobe(void)
+{
+       int procfile;
+       char *ret;
+
+       procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
+       if (procfile < 0)
+               return NULL;
+
+       ret = malloc(1024);
+       if (ret) {
+               switch (read(procfile, ret, 1024)) {
+               case -1: goto fail;
+               case 1024: goto fail; /* Partial read.  Wierd */
+               }
+               if (ret[strlen(ret)-1]=='\n') 
+                       ret[strlen(ret)-1]=0;
+               close(procfile);
+               return ret;
+       }
+ fail:
+       free(ret);
+       close(procfile);
+       return NULL;
+}
+
+int iptables_insmod(const char *modname, const char *modprobe)
+{
+       char *buf = NULL;
+       char *argv[3];
+       int i=0;
+
+       /* If they don't explicitly set it, read out of kernel */
+       if (!modprobe) {
+               buf = get_modprobe();
+               if (!buf)
+                       return -1;
+               modprobe = buf;
+       }
+
+       switch (fork()) {
+       case 0:
+               /* close open file descriptors */
+               for (i=0; i< 10; i++) {
+                 close(i);
+               }
+               argv[0] = (char *)modprobe;
+               argv[1] = (char *)modname;
+               argv[2] = NULL;
+               execv(argv[0], argv);
+
+               /* not usually reached */
+               exit(0);
+       case -1:
+               return -1;
+
+       default: /* parent */
+               wait(NULL);
+       }
+
+       free(buf);
+       return 0;
+}
+
+static struct ipt_entry *
+generate_entry(const struct ipt_entry *fw,
+              struct iptables_match *matches,
+              struct ipt_entry_target *target)
+{
+       unsigned int size;
+       struct iptables_match *m;
+       struct ipt_entry *e;
+
+       size = sizeof(struct ipt_entry);
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               size += m->m->u.match_size;
+       }
+
+       e = fw_malloc(size + target->u.target_size);
+       *e = *fw;
+       e->target_offset = size;
+       e->next_offset = size + target->u.target_size;
+
+       size = 0;
+       for (m = matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               memcpy(e->elems + size, m->m, m->m->u.match_size);
+               size += m->m->u.match_size;
+       }
+       memcpy(e->elems + size, target, target->u.target_size);
+
+       return e;
+}
+
+int do_command(int argc, char *argv[], char **table, iptc_handle_t *handle)
+{
+       struct ipt_entry fw, *e = NULL;
+       int invert = 0;
+       unsigned int nsaddrs = 0, ndaddrs = 0;
+       struct in_addr *saddrs = NULL, *daddrs = NULL;
+
+       int c, verbose = 0;
+       const char *chain = NULL;
+       const char *shostnetworkmask = NULL, *dhostnetworkmask = NULL;
+       const char *policy = NULL, *newname = NULL;
+       unsigned int rulenum = 0, options = 0, command = 0;
+       const char *pcnt = NULL, *bcnt = NULL;
+       int ret = 1;
+       struct iptables_match *m;
+       struct iptables_target *target = NULL;
+       struct iptables_target *t;
+       const char *jumpto = "";
+       char *protocol = NULL;
+       const char *modprobe = NULL;
+       int proto_used = 0;
+
+       memset(&fw, 0, sizeof(fw));
+
+       opts = original_opts;
+       global_option_offset = 0;
+
+       /* re-set optind to 0 in case do_command gets called
+        * a second time */
+       optind = 0;
+
+       /* clear mflags in case do_command gets called a second time
+        * (we clear the global list of all matches for security)*/
+       for (m = iptables_matches; m; m = m->next) {
+               m->mflags = 0;
+               m->used = 0;
+       }
+
+       for (t = iptables_targets; t; t = t->next) {
+               t->tflags = 0;
+               t->used = 0;
+       }
+
+       /* Suppress error messages: we may add new options if we
+           demand-load a protocol. */
+       opterr = 0;
+
+       while ((c = getopt_long(argc, argv,
+          "-A:D:R:I:L::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvnt:m:xc:",
+                                          opts, NULL)) != -1) {
+               switch (c) {
+                       /*
+                        * Command selection
+                        */
+               case 'A':
+                       add_command(&command, CMD_APPEND, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'D':
+                       add_command(&command, CMD_DELETE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!') {
+                               rulenum = parse_rulenumber(argv[optind++]);
+                               command = CMD_DELETE_NUM;
+                       }
+                       break;
+
+               case 'R':
+                       add_command(&command, CMD_REPLACE, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a rule number",
+                                          cmd2char(CMD_REPLACE));
+                       break;
+
+               case 'I':
+                       add_command(&command, CMD_INSERT, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               rulenum = parse_rulenumber(argv[optind++]);
+                       else rulenum = 1;
+                       break;
+
+               case 'L':
+                       add_command(&command, CMD_LIST, CMD_ZERO,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'F':
+                       add_command(&command, CMD_FLUSH, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'Z':
+                       add_command(&command, CMD_ZERO, CMD_LIST,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                               && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'N':
+                       if (optarg && *optarg == '-')
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name not allowed to start "
+                                          "with `-'\n");
+                       if (find_target(optarg, TRY_LOAD))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "chain name may not clash "
+                                          "with target name\n");
+                       add_command(&command, CMD_NEW_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       break;
+
+               case 'X':
+                       add_command(&command, CMD_DELETE_CHAIN, CMD_NONE,
+                                   invert);
+                       if (optarg) chain = optarg;
+                       else if (optind < argc && argv[optind][0] != '-'
+                                && argv[optind][0] != '!')
+                               chain = argv[optind++];
+                       break;
+
+               case 'E':
+                       add_command(&command, CMD_RENAME_CHAIN, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               newname = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires old-chain-name and "
+                                          "new-chain-name",
+                                           cmd2char(CMD_RENAME_CHAIN));
+                       break;
+
+               case 'P':
+                       add_command(&command, CMD_SET_POLICY, CMD_NONE,
+                                   invert);
+                       chain = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               policy = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                          "-%c requires a chain and a policy",
+                                          cmd2char(CMD_SET_POLICY));
+                       break;
+
+               case 'h':
+                       if (!optarg)
+                               optarg = argv[optind];
+
+                       /* iptables -p icmp -h */
+                       if (!iptables_matches && protocol)
+                               find_match(protocol, TRY_LOAD);
+
+                       exit_printhelp();
+
+                       /*
+                        * Option selection
+                        */
+               case 'p':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_PROTOCOL, &fw.ip.invflags,
+                                  invert);
+
+                       /* Canonicalize into lower case */
+                       for (protocol = argv[optind-1]; *protocol; protocol++)
+                               *protocol = tolower(*protocol);
+
+                       protocol = argv[optind-1];
+                       fw.ip.proto = parse_protocol(protocol);
+
+                       if (fw.ip.proto == 0
+                           && (fw.ip.invflags & IPT_INV_PROTO))
+                               exit_error(PARAMETER_PROBLEM,
+                                          "rule would never match protocol");
+                       fw.nfcache |= NFC_IP_PROTO;
+                       break;
+
+               case 's':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_SOURCE, &fw.ip.invflags,
+                                  invert);
+                       shostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_SRC;
+                       break;
+
+               case 'd':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_DESTINATION, &fw.ip.invflags,
+                                  invert);
+                       dhostnetworkmask = argv[optind-1];
+                       fw.nfcache |= NFC_IP_DST;
+                       break;
+
+               case 'j':
+                       set_option(&options, OPT_JUMP, &fw.ip.invflags,
+                                  invert);
+                       jumpto = parse_target(optarg);
+                       /* TRY_LOAD (may be chain name) */
+                       target = find_target(jumpto, TRY_LOAD);
+
+                       if (target) {
+                               size_t size;
+
+                               size = IPT_ALIGN(sizeof(struct ipt_entry_target))
+                                       + target->size;
+
+                               target->t = fw_calloc(1, size);
+                               target->t->u.target_size = size;
+                               strcpy(target->t->u.user.name, jumpto);
+                               target->init(target->t, &fw.nfcache);
+                               opts = merge_options(opts, target->extra_opts, &target->option_offset);
+                       }
+                       break;
+
+
+               case 'i':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEIN, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.iniface,
+                                       fw.ip.iniface_mask);
+                       fw.nfcache |= NFC_IP_IF_IN;
+                       break;
+
+               case 'o':
+                       check_inverse(optarg, &invert, &optind, argc);
+                       set_option(&options, OPT_VIANAMEOUT, &fw.ip.invflags,
+                                  invert);
+                       parse_interface(argv[optind-1],
+                                       fw.ip.outiface,
+                                       fw.ip.outiface_mask);
+                       fw.nfcache |= NFC_IP_IF_OUT;
+                       break;
+
+               case 'f':
+                       set_option(&options, OPT_FRAGMENT, &fw.ip.invflags,
+                                  invert);
+                       fw.ip.flags |= IPT_F_FRAG;
+                       fw.nfcache |= NFC_IP_FRAG;
+                       break;
+
+               case 'v':
+                       if (!verbose)
+                               set_option(&options, OPT_VERBOSE,
+                                          &fw.ip.invflags, invert);
+                       verbose++;
+                       break;
+
+               case 'm': {
+                       size_t size;
+
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --match");
+
+                       m = find_match(optarg, LOAD_MUST_SUCCEED);
+                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                        + m->size;
+                       m->m = fw_calloc(1, size);
+                       m->m->u.match_size = size;
+                       strcpy(m->m->u.user.name, m->name);
+                       m->init(m->m, &fw.nfcache);
+                       opts = merge_options(opts, m->extra_opts, &m->option_offset);
+               }
+               break;
+
+               case 'n':
+                       set_option(&options, OPT_NUMERIC, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 't':
+                       if (invert)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "unexpected ! flag before --table");
+                       *table = argv[optind-1];
+                       break;
+
+               case 'x':
+                       set_option(&options, OPT_EXPANDED, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'V':
+                       if (invert)
+                               printf("Not %s ;-)\n", program_version);
+                       else
+                               printf("%s v%s\n",
+                                      program_name, program_version);
+                       exit(0);
+
+               case '0':
+                       set_option(&options, OPT_LINENUMBERS, &fw.ip.invflags,
+                                  invert);
+                       break;
+
+               case 'M':
+                       modprobe = optarg;
+                       break;
+
+               case 'c':
+
+                       set_option(&options, OPT_COUNTERS, &fw.ip.invflags,
+                                  invert);
+                       pcnt = optarg;
+                       if (optind < argc && argv[optind][0] != '-'
+                           && argv[optind][0] != '!')
+                               bcnt = argv[optind++];
+                       else
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c requires packet and byte counter",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(pcnt, "%llu", &fw.counters.pcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c packet counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+
+                       if (sscanf(bcnt, "%llu", &fw.counters.bcnt) != 1)
+                               exit_error(PARAMETER_PROBLEM,
+                                       "-%c byte counter not numeric",
+                                       opt2char(OPT_COUNTERS));
+                       
+                       break;
+
+
+               case 1: /* non option */
+                       if (optarg[0] == '!' && optarg[1] == '\0') {
+                               if (invert)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "multiple consecutive ! not"
+                                                  " allowed");
+                               invert = TRUE;
+                               optarg[0] = '\0';
+                               continue;
+                       }
+                       printf("Bad argument `%s'\n", optarg);
+                       exit_tryhelp(2);
+
+               default:
+                       /* FIXME: This scheme doesn't allow two of the same
+                          matches --RR */
+                       if (!target
+                           || !(target->parse(c - target->option_offset,
+                                              argv, invert,
+                                              &target->tflags,
+                                              &fw, &target->t))) {
+                               for (m = iptables_matches; m; m = m->next) {
+                                       if (!m->used)
+                                               continue;
+
+                                       if (m->parse(c - m->option_offset,
+                                                    argv, invert,
+                                                    &m->mflags,
+                                                    &fw,
+                                                    &fw.nfcache,
+                                                    &m->m))
+                                               break;
+                               }
+
+                               /* If you listen carefully, you can
+                                  actually hear this code suck. */
+
+                               /* some explanations (after four different bugs
+                                * in 3 different releases): If we encountere a
+                                * parameter, that has not been parsed yet,
+                                * it's not an option of an explicitly loaded
+                                * match or a target.  However, we support
+                                * implicit loading of the protocol match
+                                * extension.  '-p tcp' means 'l4 proto 6' and
+                                * at the same time 'load tcp protocol match on
+                                * demand if we specify --dport'.
+                                *
+                                * To make this work, we need to make sure:
+                                * - the parameter has not been parsed by
+                                *   a match (m above)
+                                * - a protocol has been specified
+                                * - the protocol extension has not been
+                                *   loaded yet, or is loaded and unused
+                                *   [think of iptables-restore!]
+                                * - the protocol extension can be successively
+                                *   loaded
+                                */
+                               if (m == NULL
+                                   && protocol
+                                   && (!find_proto(protocol, DONT_LOAD,
+                                                  options&OPT_NUMERIC) 
+                                       || (find_proto(protocol, DONT_LOAD,
+                                                       options&OPT_NUMERIC)
+                                           && (proto_used == 0))
+                                      )
+                                   && (m = find_proto(protocol, TRY_LOAD,
+                                                      options&OPT_NUMERIC))) {
+                                       /* Try loading protocol */
+                                       size_t size;
+                                       
+                                       proto_used = 1;
+
+                                       size = IPT_ALIGN(sizeof(struct ipt_entry_match))
+                                                        + m->size;
+
+                                       m->m = fw_calloc(1, size);
+                                       m->m->u.match_size = size;
+                                       strcpy(m->m->u.user.name, m->name);
+                                       m->init(m->m, &fw.nfcache);
+
+                                       opts = merge_options(opts,
+                                           m->extra_opts, &m->option_offset);
+
+                                       optind--;
+                                       continue;
+                               }
+                               if (!m)
+                                       exit_error(PARAMETER_PROBLEM,
+                                                  "Unknown arg `%s'",
+                                                  argv[optind-1]);
+                       }
+               }
+               invert = FALSE;
+       }
+
+       for (m = iptables_matches; m; m = m->next) {
+               if (!m->used)
+                       continue;
+
+               m->final_check(m->mflags);
+       }
+
+       if (target)
+               target->final_check(target->tflags);
+
+       /* Fix me: must put inverse options checking here --MN */
+
+       if (optind < argc)
+               exit_error(PARAMETER_PROBLEM,
+                          "unknown arguments found on commandline");
+       if (!command)
+               exit_error(PARAMETER_PROBLEM, "no command specified");
+       if (invert)
+               exit_error(PARAMETER_PROBLEM,
+                          "nothing appropriate following !");
+
+       if (command & (CMD_REPLACE | CMD_INSERT | CMD_DELETE | CMD_APPEND)) {
+               if (!(options & OPT_DESTINATION))
+                       dhostnetworkmask = "0.0.0.0/0";
+               if (!(options & OPT_SOURCE))
+                       shostnetworkmask = "0.0.0.0/0";
+       }
+
+       if (shostnetworkmask)
+               parse_hostnetworkmask(shostnetworkmask, &saddrs,
+                                     &(fw.ip.smsk), &nsaddrs);
+
+       if (dhostnetworkmask)
+               parse_hostnetworkmask(dhostnetworkmask, &daddrs,
+                                     &(fw.ip.dmsk), &ndaddrs);
+
+       if ((nsaddrs > 1 || ndaddrs > 1) &&
+           (fw.ip.invflags & (IPT_INV_SRCIP | IPT_INV_DSTIP)))
+               exit_error(PARAMETER_PROBLEM, "! not allowed with multiple"
+                          " source or destination IP addresses");
+
+       if (command == CMD_REPLACE && (nsaddrs != 1 || ndaddrs != 1))
+               exit_error(PARAMETER_PROBLEM, "Replacement rule does not "
+                          "specify a unique address");
+
+       generic_opt_check(command, options);
+
+       if (chain && strlen(chain) > IPT_FUNCTION_MAXNAMELEN)
+               exit_error(PARAMETER_PROBLEM,
+                          "chain name `%s' too long (must be under %i chars)",
+                          chain, IPT_FUNCTION_MAXNAMELEN);
+
+       /* only allocate handle if we weren't called with a handle */
+       if (!*handle)
+               *handle = iptc_init(*table);
+
+       if (!*handle) {
+               /* try to insmod the module if iptc_init failed */
+               iptables_insmod("ip_tables", modprobe);
+               *handle = iptc_init(*table);
+       }
+
+       if (!*handle)
+               exit_error(VERSION_PROBLEM,
+                          "can't initialize iptables table `%s': %s",
+                          *table, iptc_strerror(errno));
+
+       if (command == CMD_APPEND
+           || command == CMD_DELETE
+           || command == CMD_INSERT
+           || command == CMD_REPLACE) {
+               if (strcmp(chain, "PREROUTING") == 0
+                   || strcmp(chain, "INPUT") == 0) {
+                       /* -o not valid with incoming packets. */
+                       if (options & OPT_VIANAMEOUT)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEOUT),
+                                          chain);
+               }
+
+               if (strcmp(chain, "POSTROUTING") == 0
+                   || strcmp(chain, "OUTPUT") == 0) {
+                       /* -i not valid with outgoing packets */
+                       if (options & OPT_VIANAMEIN)
+                               exit_error(PARAMETER_PROBLEM,
+                                          "Can't use -%c with %s\n",
+                                          opt2char(OPT_VIANAMEIN),
+                                          chain);
+               }
+
+               if (target && iptc_is_chain(jumpto, *handle)) {
+                       printf("Warning: using chain %s, not extension\n",
+                              jumpto);
+
+                       target = NULL;
+               }
+
+               /* If they didn't specify a target, or it's a chain
+                  name, use standard. */
+               if (!target
+                   && (strlen(jumpto) == 0
+                       || iptc_is_chain(jumpto, *handle))) {
+                       size_t size;
+
+                       target = find_target(IPT_STANDARD_TARGET,
+                                            LOAD_MUST_SUCCEED);
+
+                       size = sizeof(struct ipt_entry_target)
+                               + target->size;
+                       target->t = fw_calloc(1, size);
+                       target->t->u.target_size = size;
+                       strcpy(target->t->u.user.name, jumpto);
+                       target->init(target->t, &fw.nfcache);
+               }
+
+               if (!target) {
+                       /* it is no chain, and we can't load a plugin.
+                        * We cannot know if the plugin is corrupt, non
+                        * existant OR if the user just misspelled a
+                        * chain. */
+                       find_target(jumpto, LOAD_MUST_SUCCEED);
+               } else {
+                       e = generate_entry(&fw, iptables_matches, target->t);
+               }
+       }
+
+       switch (command) {
+       case CMD_APPEND:
+               ret = append_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE:
+               ret = delete_entry(chain, e,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_DELETE_NUM:
+               ret = iptc_delete_num_entry(chain, rulenum - 1, handle);
+               break;
+       case CMD_REPLACE:
+               ret = replace_entry(chain, e, rulenum - 1,
+                                   saddrs, daddrs, options&OPT_VERBOSE,
+                                   handle);
+               break;
+       case CMD_INSERT:
+               ret = insert_entry(chain, e, rulenum - 1,
+                                  nsaddrs, saddrs, ndaddrs, daddrs,
+                                  options&OPT_VERBOSE,
+                                  handle);
+               break;
+       case CMD_LIST:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               break;
+       case CMD_FLUSH:
+               ret = flush_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_ZERO:
+               ret = zero_entries(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_LIST|CMD_ZERO:
+               ret = list_entries(chain,
+                                  options&OPT_VERBOSE,
+                                  options&OPT_NUMERIC,
+                                  options&OPT_EXPANDED,
+                                  options&OPT_LINENUMBERS,
+                                  handle);
+               if (ret)
+                       ret = zero_entries(chain,
+                                          options&OPT_VERBOSE, handle);
+               break;
+       case CMD_NEW_CHAIN:
+               ret = iptc_create_chain(chain, handle);
+               break;
+       case CMD_DELETE_CHAIN:
+               ret = delete_chain(chain, options&OPT_VERBOSE, handle);
+               break;
+       case CMD_RENAME_CHAIN:
+               ret = iptc_rename_chain(chain, newname, handle);
+               break;
+       case CMD_SET_POLICY:
+               ret = iptc_set_policy(chain, policy, NULL, handle);
+               break;
+       default:
+               /* We should never reach this... */
+               exit_tryhelp(2);
+       }
+
+       if (verbose > 1)
+               dump_entries(*handle);
+
+       return ret;
+}
diff --git a/libipq/Makefile b/libipq/Makefile
new file mode 100644 (file)
index 0000000..64633f3
--- /dev/null
@@ -0,0 +1,28 @@
+#! /usr/bin/make
+
+EXTRAS+=libipq/libipq.a
+#CFLAGS+=-DDEBUG_LIBIPTQ
+
+DEVEL_MAN3+=libipq/ipq_create_handle.3 \
+       libipq/ipq_destroy_handle.3 \
+       libipq/ipq_errstr.3 \
+       libipq/ipq_get_msgerr.3 \
+       libipq/ipq_get_packet.3 \
+       libipq/ipq_message_type.3 \
+       libipq/ipq_perror.3 \
+       libipq/ipq_read.3 \
+       libipq/ipq_set_mode.3 \
+       libipq/ipq_set_verdict.3 \
+       libipq/libipq.3
+
+DEVEL_LIBS+=libipq/libipq.a
+
+DEVEL_HEADERS+=include/libipq/libipq.h
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(SHARED_LIBS) $(EXTRAS)
+else
+libipq/libipq.a: libipq/libipq.a(libipq/libipq.o)
+endif
+
diff --git a/libipq/ipq_create_handle.3 b/libipq/ipq_create_handle.3
new file mode 100644 (file)
index 0000000..08502dc
--- /dev/null
@@ -0,0 +1,84 @@
+.TH IPQ_CREATE_HANDLE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+\" $Id: ipq_create_handle.3,v 1.3 2001/11/24 15:09:20 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_create_handle, ipq_destroy_handle - create and destroy libipq handles.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "struct ipq_handle *ipq_create_handle(u_int32_t " flags ", u_int32_t " protocol ");"
+.br
+.BI "int ipq_destroy_handle(struct ipq_handle *" h );
+.SH DESCRIPTION
+The
+.B ipq_create_handle
+function initialises libipq for an application, attempts to bind to the
+Netlink socket used by ip_queue, and returns an opaque context handle.  It
+should be the first libipq function to be called by an application.  The
+handle returned should be used in all subsequent library calls which 
+require a handle parameter.
+.PP
+The
+.I flags
+parameter is not currently used and should be set to zero by the application
+for forward compatibility.
+.PP
+The
+.I protocol
+parameter is used to specify the protocol of the packets to be queued.
+Valid values are PF_INET for IPv4 and PF_INET6 for IPv6.  Currently, 
+only one protocol may be queued at a time for a handle.
+.PP
+The
+.B ipq_destroy_handle
+function frees up resources allocated by
+.BR ipq_create_handle ,
+and should be used when the handle is no longer required by the application.
+.SH RETURN VALUES
+On success,
+.B ipq_create_handle
+returns a pointer to a context handle.
+.br
+On failure, NULL is returned.
+.PP
+On success,
+.B ipq_destroy_handle
+returns zero.
+.br
+On failure, -1 is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_destroy_handle.3 b/libipq/ipq_destroy_handle.3
new file mode 100644 (file)
index 0000000..29dcd98
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_create_handle.3
diff --git a/libipq/ipq_errstr.3 b/libipq/ipq_errstr.3
new file mode 100644 (file)
index 0000000..baa00ec
--- /dev/null
@@ -0,0 +1,66 @@
+.TH IPQ_ERRSTR 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_errstr.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_errstr, ipq_perror - libipq error handling routines
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "char *ipq_errstr(" void );
+.br
+.BI "void ipq_perror(const char *" s );
+.SH DESCRIPTION
+The
+.B ipq_errstr
+function returns a descriptive error message based on the current
+value of the internal
+.B ipq_errno
+variable.  All libipq API functions set this internal variable
+upon failure.
+.PP
+The
+.B ipq_perror
+function prints an error message to stderr corresponding to the
+current value of the internal
+.B ipq_error
+variable, and the global
+.B errno
+variable (if set).  The error message is prefixed with the string
+.I s
+as supplied by the application. If
+.I s
+is NULL, the error message is prefixed with the string "ERROR".
+.SH RETURN VALUE
+.B ipq_errstr
+returns an error message as outlined above.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_get_msgerr.3 b/libipq/ipq_get_msgerr.3
new file mode 100644 (file)
index 0000000..8a28be3
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_get_packet.3 b/libipq/ipq_get_packet.3
new file mode 100644 (file)
index 0000000..8a28be3
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_message_type.3
diff --git a/libipq/ipq_message_type.3 b/libipq/ipq_message_type.3
new file mode 100644 (file)
index 0000000..97d15eb
--- /dev/null
@@ -0,0 +1,136 @@
+.TH IPQ_MESSAGE_TYPE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_message_type.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_message_type, ipq_get_packet, ipq_getmsgerr - query queue messages
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_message_type(const unsigned char *" buf ");"
+.br
+.BI "ipq_packet_msg_t *ipq_get_packet(const unsigned char *" buf ");"
+.br
+.BI "int ipq_get_msgerr(const unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_message_type
+function returns the type of queue message returned to userspace
+via
+.BR ipq_read .
+.PP
+.B ipq_message_type
+should always be called following a successful call to
+.B ipq_read
+to determine whether the message is a packet message or an
+error message. The
+.I buf
+parameter should be the same data obtained from
+the previous call to
+.BR ipq_read .
+.PP
+.B ipq_message_type
+will return one of the following values:
+.TP
+.B NLMSG_ERROR
+An error message generated by the Netlink transport.
+.PP
+.TP
+.B IPQM_PACKET
+A packet message containing packet metadata and optional packet payload data.
+.PP
+The
+.B ipq_get_packet
+function should be called if
+.B ipq_message_type
+returns
+.BR IPQM_PACKET .
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The pointer returned by
+.B ipq_get_packet
+points to a packet message, which is declared as follows:
+.PP
+.RS
+.nf
+typedef struct ipq_packet_msg {
+       unsigned long packet_id;        /* ID of queued packet */
+       unsigned long mark;             /* Netfilter mark value */
+       long timestamp_sec;             /* Packet arrival time (seconds) */
+       long timestamp_usec;            /* Packet arrvial time (+useconds) */
+       unsigned int hook;              /* Netfilter hook we rode in on */
+       char indev_name[IFNAMSIZ];      /* Name of incoming interface */
+       char outdev_name[IFNAMSIZ];     /* Name of outgoing interface */
+       unsigned short hw_protocol;     /* Hardware protocol (network order) */
+       unsigned short hw_type;         /* Hardware type */
+       unsigned char hw_addrlen;       /* Hardware address length */
+       unsigned char hw_addr[8];       /* Hardware address */
+       size_t data_len;                /* Length of packet data */
+       unsigned char payload[0];       /* Optional packet data */
+} ipq_packet_msg_t;
+.fi
+.RE
+.PP
+Each of these fields may be read by the application.  If the queue mode
+is
+.B IPQ_COPY_PACKET
+and the
+.I data_len
+value is greater than zero, the packet payload contents may be accessed
+in the memory following the
+.B ipq_packet_msg_t
+structure to a range of
+.I data_len.
+.PP
+The
+.I packet_id
+field contains a packet identifier to be used when calling
+.BR ipq_set_verdict .
+.PP
+The
+.B ipq_get_msgerr
+function should be called if
+.B ipq_message_type
+returns
+.BR NLMSG_ERROR.
+The
+.I buf
+parameter should point to the same data used for the call to
+.BR ipq_message_type .
+The value returned by
+.B ipq_get_msgerr
+is set by higher level kernel code and corresponds to standard
+.B errno
+values.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
diff --git a/libipq/ipq_perror.3 b/libipq/ipq_perror.3
new file mode 100644 (file)
index 0000000..6efd53d
--- /dev/null
@@ -0,0 +1 @@
+.so man3/ipq_errstr.3
diff --git a/libipq/ipq_read.3 b/libipq/ipq_read.3
new file mode 100644 (file)
index 0000000..afdf5a9
--- /dev/null
@@ -0,0 +1,106 @@
+.TH IPQ_READ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_read.3,v 1.3 2001/10/16 16:58:25 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_read - read queue messages from ip_queue and read into supplied buffer
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "ssize_t ipq_read(const struct ipq_handle *" h ", unsigned char *" buf ", size_t " len ", int " timeout ");"
+.SH DESCRIPTION
+The
+.B ipq_read
+function reads a queue message from the kernel and copies it to
+the memory pointed to by 
+.I buf
+to a maximum length of
+. IR len .
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The caller is responsible for ensuring that the memory pointed to by
+.I buf
+is large enough to contain
+.I len
+bytes.
+.PP
+The
+.I timeout
+parameter may be used to set a timeout for the operation, specified in microseconds.
+This is implemented internally by the library via the
+.BR select
+system call.  A value of zero provides normal, backwards-compatible blocking behaviour
+with no timeout.  A negative value causes the function to return immediately.
+.PP
+Data returned via
+.I buf
+should not be accessed directly.  Use the 
+.BR ipq_message_type ,
+.BR ipq_get_packet ", and"
+.BR ipq_get_msgerr
+functions to access the queue message in the buffer.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned when no timeout
+value is specified.
+.br
+On success with a timeout value specified, zero is returned if no data
+was available to read, or if a non-blocked signal was caught.  In the
+latter case, the global
+.B errno
+value will be set to 
+.BR EINTR .
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+While the
+.B ipq_read
+function may return successfully, the queue message copied to the buffer
+may itself be an error message from a higher level kernel component.  Use
+.B ipq_message_type
+to determine if it is an error message, and
+.B ipq_get_msgerr
+to access the value of the message.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the timeout feature, which appeared in the 1.2.4 release of iptables.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3),
+.BR select (2).
+
diff --git a/libipq/ipq_set_mode.3 b/libipq/ipq_set_mode.3
new file mode 100644 (file)
index 0000000..19b8856
--- /dev/null
@@ -0,0 +1,107 @@
+.TH IPQ_SET_MODE 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_set_mode.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_set_mode - set the ip_queue queuing mode
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_mode(const struct ipq_handle *" h ", u_int8_t " mode ", size_t " range );
+.SH DESCRIPTION
+The
+.B ipq_set_mode
+function sends a message to the kernel ip_queue module, specifying whether
+packet metadata only, or packet payloads as well as metadata should be copied to
+userspace.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I mode
+parameter must be one of:
+.TP
+.B IPQ_COPY_META
+Copy only packet metadata to userspace.
+.TP
+.B IPQ_COPY_PACKET
+Copy packet metadata and packet payloads to userspace.
+.PP
+The
+.I range
+parameter is used to specify how many bytes of the payload to copy
+to userspace.  It is only valid for
+.B IPQ_COPY_PACKET
+mode and is otherwise ignored.  The maximum useful value for
+.I range
+is 65535 (greater values will be clamped to this by ip_queue).
+.PP
+.B ipq_set_mode
+is usually used immediately following
+.B ipq_create_handle
+to enable the flow of packets to userspace.
+.PP
+Note that as the underlying Netlink messaging transport is connectionless,
+the ip_queue module does not know that a userspace application is ready to
+communicate until it receives a message such as this.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On failure, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH DIAGNOSTICS
+A relatively common failure may occur if the ip_queue module is not loaded.
+In this case, the following code excerpt:
+.PP
+.RS
+.nf
+status = ipq_set_mode(h, IPQ_COPY_META, 0);
+if (status < 0) {
+       ipq_perror("myapp");
+       ipq_destroy_handle(h);
+       exit(1);
+}
+.RE
+.fi
+.PP
+would generate the following output:
+.PP
+.I myapp: Failed to send netlink message: Connection refused
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR libipq (3),
+.BR iptables (8).
diff --git a/libipq/ipq_set_verdict.3 b/libipq/ipq_set_verdict.3
new file mode 100644 (file)
index 0000000..bdccb8f
--- /dev/null
@@ -0,0 +1,95 @@
+.TH IPQ_SET_VERDICT 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: ipq_set_verdict.3,v 1.2 2001/10/16 14:41:02 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+ipq_set_verdict - issue verdict and optionally modified packet to kernel
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.sp
+.BI "int ipq_set_verdict(const struct ipq_handle *" h ", ipq_id_t " id ", unsigned int " verdict ", size_t " data_len ", unsigned char *" buf ");"
+.SH DESCRIPTION
+The
+.B ipq_set_verdict
+function issues a verdict on a packet previously obtained with
+.BR ipq_read ,
+specifing the intended disposition of the packet, and optionally
+supplying a modified version of the payload data.
+.PP
+The
+.I h
+parameter is a context handle which must previously have been returned 
+successfully from a call to
+.BR ipq_create_handle .
+.PP
+The
+.I id
+parameter is the packet identifier obtained via
+.BR ipq_get_packet .
+.PP
+The
+.I verdict
+parameter must be one of:
+.TP
+.B NF_ACCEPT
+Accept the packet and continue traversal within the kernel.
+.br
+.TP
+.B NF_DROP
+Drop the packet.
+.PP
+The
+.I data_len
+parameter is the length of the data pointed to
+by
+.IR buf ,
+the optional replacement payload data.
+.PP
+If simply setting a verdict without modifying the payload data, use zero
+for
+.I data_len
+and NULL for
+.IR buf .
+.PP
+The application is responsible for recalculating any packet checksums
+when modifying packets.
+.SH RETURN VALUE
+On failure, -1 is returned.
+.br
+On success, a non-zero positive value is returned.
+.SH ERRORS
+On error, a descriptive error message will be available
+via the
+.B ipq_errstr
+function.
+.SH BUGS
+None known.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH SEE ALSO
+.BR iptables (8),
+.BR libipq (3).
+
diff --git a/libipq/libipq.3 b/libipq/libipq.3
new file mode 100644 (file)
index 0000000..1a0984d
--- /dev/null
@@ -0,0 +1,279 @@
+.TH LIBIPQ 3 "16 October 2001" "Linux iptables 1.2" "Linux Programmer's Manual" 
+.\"
+.\" $Id: libipq.3,v 1.5 2001/11/24 15:09:20 jamesm Exp $
+.\"
+.\"     Copyright (c) 2000-2001 Netfilter Core Team
+.\"
+.\"     This program is free software; you can redistribute it and/or modify
+.\"     it under the terms of the GNU General Public License as published by
+.\"     the Free Software Foundation; either version 2 of the License, or
+.\"     (at your option) any later version.
+.\"
+.\"     This program is distributed in the hope that it will be useful,
+.\"     but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\"     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\"     GNU General Public License for more details.
+.\"
+.\"     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
+libipq \- iptables userspace packet queuing library.
+.SH SYNOPSIS
+.B #include <linux/netfilter.h>
+.br
+.B #include <libipq.h>
+.SH DESCRIPTION
+libipq is a development library for iptables userspace packet queuing.
+.SS Userspace Packet Queuing
+Netfilter provides a mechanism for passing packets out of the stack for
+queueing to userspace, then receiving these packets back into the kernel
+with a verdict specifying what to do with the packets (such as ACCEPT
+or DROP).  These packets may also be modified in userspace prior to
+reinjection back into the kernel.
+.PP
+For each supported protocol, a kernel module called a
+.I queue handler
+may register with Netfilter to perform the mechanics of passing
+packets to and from userspace.
+.PP
+The standard queue handler for IPv4 is ip_queue.  It is provided as an
+experimental module with 2.4 kernels, and uses a Netlink socket for
+kernel/userspace communication.
+.PP
+Once ip_queue is loaded, IP packets may be selected with iptables
+and queued for userspace processing via the QUEUE target.  For example,
+running the following commands:
+.PP
+       # modprobe iptable_filter
+.br    
+       # modprobe ip_queue
+.br    
+       # iptables -A OUTPUT -p icmp -j QUEUE
+.PP
+will cause any locally generated ICMP packets (e.g. ping output) to
+be sent to the ip_queue module, which will then attempt to deliver the
+packets to a userspace application.  If no userspace application is waiting,
+the packets will be dropped
+.PP
+An application may receive and process these packets via libipq.
+.PP
+.PP
+.SS Libipq Overview
+Libipq provides an API for communicating with ip_queue.  The following is
+an overview of API usage, refer to individual man pages for more details
+on each function.
+.PP
+.B Initialisation
+.br
+To initialise the library, call
+.BR ipq_create_handle (3).
+This will attempt to bind to the Netlink socket used by ip_queue and
+return an opaque context handle for subsequent library calls.
+.PP
+.B Setting the Queue Mode
+.br
+.BR ipq_set_mode (3)
+allows the application to specify whether packet metadata, or packet
+payloads as well as metadata are copied to userspace.  It is also used to
+initially notify ip_queue that an application is ready to receive queue
+messages.
+.PP
+.B Receiving Packets from the Queue
+.br
+.BR ipq_read (3)
+waits for queue messages to arrive from ip_queue and copies
+them into a supplied buffer.
+Queue messages may be
+.I packet messages
+or
+.I error messages.
+.PP
+The type of packet may be determined with
+.BR ipq_message_type (3).
+.PP
+If it's a packet message, the metadata and optional payload may be retrieved with
+.BR ipq_get_packet (3).
+.PP
+To retrieve the value of an error message, use
+.BR ipq_get_msgerr (3).
+.PP
+.B Issuing Verdicts on Packets
+.br
+To issue a verdict on a packet, and optionally return a modified version
+of the packet to the kernel, call
+.BR ipq_set_verdict (3).
+.PP
+.B Error Handling
+.br
+An error string corresponding to the current value of the internal error
+variable
+.B ipq_errno
+may be obtained with
+.BR ipq_errstr (3).
+.PP
+For simple applications, calling
+.BR ipq_perror (3)
+will print the same message as
+.BR ipq_errstr (3),
+as well as the string corresponding to the global
+.B errno
+value (if set) to stderr.
+.PP
+.B Cleaning Up
+.br
+To free up the Netlink socket and destroy resources associated with
+the context handle, call
+.BR ipq_destroy_handle (3).
+.SH SUMMARY
+.TP 4
+.BR ipq_create_handle (3)
+Initialise library, return context handle.
+.TP
+.BR ipq_set_mode (3)
+Set the queue mode, to copy either packet metadata, or payloads
+as well as metadata to userspace.
+.TP
+.BR ipq_read (3)
+Wait for a queue message to arrive from ip_queue and read it into
+a buffer.
+.TP
+.BR ipq_message_type (3)
+Determine message type in the buffer.
+.TP
+.BR ipq_get_packet (3)
+Retrieve a packet message from the buffer.
+.TP
+.BR ipq_get_msgerr (3)
+Retrieve an error message from the buffer.
+.TP
+.BR ipq_set_verdict (3)
+Set a verdict on a packet, optionally replacing its contents.
+.TP
+.BR ipq_errstr (3)
+Return an error message corresponding to the internal ipq_errno variable.
+.TP
+.BR ipq_perror (3)
+Helper function to print error messages to stderr.
+.TP
+.BR ipq_destroy_handle (3)
+Destroy context handle and associated resources.
+.SH EXAMPLE
+The following is an example of a simple application which receives
+packets and issues NF_ACCEPT verdicts on each packet.
+.RS
+.nf
+/*
+ * This code is GPL.
+ */
+#include <linux/netfilter.h>
+#include <libipq.h>
+#include <stdio.h>
+
+#define BUFSIZE 2048 
+
+static void die(struct ipq_handle *h)
+{
+       ipq_perror("passer");
+       ipq_destroy_handle(h);
+       exit(1);
+}
+
+int main(int argc, char **argv)
+{
+       int status;
+       unsigned char buf[BUFSIZE];
+       struct ipq_handle *h;
+       
+       h = ipq_create_handle(0, PF_INET);
+       if (!h)
+               die(h);
+               
+       status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
+       if (status < 0)
+               die(h);
+               
+       do{
+               status = ipq_read(h, buf, BUFSIZE, 0);
+               if (status < 0)
+                       die(h);
+                       
+               switch (ipq_message_type(buf)) {
+                       case NLMSG_ERROR:
+                               fprintf(stderr, "Received error message %d\\n",
+                                       ipq_get_msgerr(buf));
+                               break;
+                               
+                       case IPQM_PACKET: {
+                               ipq_packet_msg_t *m = ipq_get_packet(buf);
+                               
+                               status = ipq_set_verdict(h, m->packet_id,
+                                                        NF_ACCEPT, 0, NULL);
+                               if (status < 0)
+                                       die(h);
+                               break;
+                       }
+                       
+                       default:
+                               fprintf(stderr, "Unknown message type!\\n");
+                               break;
+               }
+       } while (1);
+       
+       ipq_destroy_handle(h);
+       return 0;
+}
+.RE
+.fi
+.PP
+Pointers to more libipq application examples may be found in The
+Netfilter FAQ.
+.SH DIAGNOSTICS
+For information about monitoring and tuning ip_queue, refer to the
+Linux 2.4 Packet Filtering HOWTO.
+.PP
+If an application modifies a packet, it needs to also update any
+checksums for the packet.  Typically, the kernel will silently discard
+modified packets with invalid checksums. 
+.SH SECURITY
+Processes require CAP_NET_ADMIN capabilty to access the kernel ip_queue
+module.  Such processes can potentially access and modify any IP packets
+received, generated or forwarded by the kernel.
+.SH TODO
+Per-handle
+.B ipq_errno
+values.
+.SH BUGS
+Probably.
+.SH AUTHOR
+James Morris <jmorris@intercode.com.au>
+.SH COPYRIGHT
+Copyright (c) 2000-2001 Netfilter Core Team.
+.PP
+Distributed under the GNU General Public License.
+.SH CREDITS
+Joost Remijn implemented the
+.B ipq_read
+timeout feature, which appeared in the 1.2.4 release of iptables.
+.PP
+Fernando Anton added support for IPv6.
+.SH SEE ALSO
+.BR iptables (8),
+.BR ipq_create_handle (3),
+.BR ipq_destroy_handle (3),
+.BR ipq_errstr (3),
+.BR ipq_get_msgerr (3),
+.BR ipq_get_packet (3),
+.BR ipq_message_type (3),
+.BR ipq_perror (3),
+.BR ipq_read (3),
+.BR ipq_set_mode (3),
+.BR ipq_set_verdict (3).
+.PP
+The Netfilter home page at http://netfilter.samba.org/
+which has links to The Networking Concepts HOWTO, The Linux 2.4 Packet
+Filtering HOWTO, The Linux 2.4 NAT HOWTO, The Netfilter Hacking HOWTO,
+The Netfilter FAQ and many other useful resources.
+
diff --git a/libipq/libipq.c b/libipq/libipq.c
new file mode 100644 (file)
index 0000000..a25ad4c
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * libipq.c
+ *
+ * IPQ userspace library.
+ *
+ * Please note that this library is still developmental, and there may
+ * be some API changes.
+ *
+ * Author: James Morris <jmorris@intercode.com.au>
+ *
+ * 07-11-2001 Modified by Fernando Anton to add support for IPv6.
+ *
+ * Copyright (c) 2000-2001 Netfilter Core Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.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,
+       IPQ_ERR_TIMEOUT,
+        IPQ_ERR_PROTOCOL
+};
+#define IPQ_MAXERR IPQ_ERR_PROTOCOL
+
+struct ipq_errmap_t {
+       int errcode;
+       char *message;
+} ipq_errmap[] = {
+       { IPQ_ERR_NONE, "Unknown error" },
+       { IPQ_ERR_IMPL, "Implementation error" },
+       { IPQ_ERR_HANDLE, "Unable to create netlink handle" },
+       { IPQ_ERR_SOCKET, "Unable to create netlink socket" },
+       { IPQ_ERR_BIND, "Unable to bind netlink socket" },
+       { IPQ_ERR_BUFFER, "Unable to allocate buffer" },
+       { IPQ_ERR_RECV, "Failed to receive netlink message" },
+       { IPQ_ERR_NLEOF, "Received EOF on netlink socket" },
+       { IPQ_ERR_ADDRLEN, "Invalid peer address length" },
+       { IPQ_ERR_STRUNC, "Sent message truncated" },
+       { IPQ_ERR_RTRUNC, "Received message truncated" },
+       { IPQ_ERR_NLRECV, "Received error from netlink" },
+       { IPQ_ERR_SEND, "Failed to send netlink message" },
+       { IPQ_ERR_SUPP, "Operation not supported" },
+       { IPQ_ERR_RECVBUF, "Receive buffer size invalid" },
+       { IPQ_ERR_TIMEOUT, "Timeout"},
+       { IPQ_ERR_PROTOCOL, "Invalid protocol specified" }
+};
+
+static int ipq_errno = IPQ_ERR_NONE;
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len);
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout);
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags);
+
+static char *ipq_strerror(int errcode);
+
+static ssize_t ipq_netlink_sendto(const struct ipq_handle *h,
+                                  const void *msg, size_t len)
+{
+       int status = sendto(h->fd, msg, len, 0,
+                           (struct sockaddr *)&h->peer, sizeof(h->peer));
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_sendmsg(const struct ipq_handle *h,
+                                   const struct msghdr *msg,
+                                   unsigned int flags)
+{
+       int status = sendmsg(h->fd, msg, flags);
+       if (status < 0)
+               ipq_errno = IPQ_ERR_SEND;
+       return status;
+}
+
+static ssize_t ipq_netlink_recvfrom(const struct ipq_handle *h,
+                                    unsigned char *buf, size_t len,
+                                    int timeout)
+{
+       int addrlen, status;
+       struct nlmsghdr *nlh;
+
+       if (len < sizeof(struct nlmsgerr)) {
+               ipq_errno = IPQ_ERR_RECVBUF;
+               return -1;
+       }
+       addrlen = sizeof(h->peer);
+
+       if (timeout != 0) {
+               int ret;
+               struct timeval tv;
+               fd_set read_fds;
+               
+               if (timeout < 0) {
+                       /* non-block non-timeout */
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 0;
+               } else {
+                       tv.tv_sec = timeout / 1000000;
+                       tv.tv_usec = timeout % 1000000;
+               }
+
+               FD_ZERO(&read_fds);
+               FD_SET(h->fd, &read_fds);
+               ret = select(h->fd+1, &read_fds, NULL, NULL, &tv);
+               if (ret < 0) {
+                       if (errno == EINTR) {
+                               return 0;
+                       } else {
+                               ipq_errno = IPQ_ERR_RECV;
+                               return -1;
+                       }
+               }
+               if (!FD_ISSET(h->fd, &read_fds)) {
+                       ipq_errno = IPQ_ERR_TIMEOUT;
+                       return 0;
+               }
+       }
+       status = recvfrom(h->fd, buf, len, 0,
+                             (struct sockaddr *)&h->peer, &addrlen);
+       if (status < 0) {
+               ipq_errno = IPQ_ERR_RECV;
+               return status;
+       }
+       if (addrlen != sizeof(h->peer)) {
+               ipq_errno = IPQ_ERR_RECV;
+               return -1;
+       }
+       if (h->peer.nl_pid != 0) {
+               ipq_errno = IPQ_ERR_RECV;
+               return -1;
+       }
+       if (status == 0) {
+               ipq_errno = IPQ_ERR_NLEOF;
+               return -1;
+       }
+       nlh = (struct nlmsghdr *)buf;
+       if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > status) {
+               ipq_errno = IPQ_ERR_RTRUNC;
+               return -1;
+       }
+       return status;
+}
+
+static char *ipq_strerror(int errcode)
+{
+       if (errcode < 0 || errcode > IPQ_MAXERR)
+               errcode = IPQ_ERR_IMPL;
+       return ipq_errmap[errcode].message;
+}
+
+/****************************************************************************
+ *
+ * Public interface
+ *
+ ****************************************************************************/
+
+/*
+ * Create and initialise an ipq handle.
+ */
+struct ipq_handle *ipq_create_handle(u_int32_t flags, u_int32_t protocol)
+{
+       int status;
+       struct ipq_handle *h;
+
+       h = (struct ipq_handle *)malloc(sizeof(struct ipq_handle));
+       if (h == NULL) {
+               ipq_errno = IPQ_ERR_HANDLE;
+               return NULL;
+       }
+       
+       memset(h, 0, sizeof(struct ipq_handle));
+       
+        if (protocol == PF_INET)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);
+        else if (protocol == PF_INET6)
+                h->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_IP6_FW);
+        else {
+               ipq_errno = IPQ_ERR_PROTOCOL;
+               free(h);
+               return NULL;
+        }
+        
+       if (h->fd == -1) {
+               ipq_errno = IPQ_ERR_SOCKET;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->local, 0, sizeof(struct sockaddr_nl));
+       h->local.nl_family = AF_NETLINK;
+       h->local.nl_pid = getpid();
+       h->local.nl_groups = 0;
+       status = bind(h->fd, (struct sockaddr *)&h->local, sizeof(h->local));
+       if (status == -1) {
+               ipq_errno = IPQ_ERR_BIND;
+               close(h->fd);
+               free(h);
+               return NULL;
+       }
+       memset(&h->peer, 0, sizeof(struct sockaddr_nl));
+       h->peer.nl_family = AF_NETLINK;
+       h->peer.nl_pid = 0;
+       h->peer.nl_groups = 0;
+       return h;
+}
+
+/*
+ * No error condition is checked here at this stage, but it may happen
+ * if/when reliable messaging is implemented.
+ */
+int ipq_destroy_handle(struct ipq_handle *h)
+{
+       if (h) {
+               close(h->fd);
+               free(h);
+       }
+       return 0;
+}
+
+int ipq_set_mode(const struct ipq_handle *h,
+                 u_int8_t mode, size_t range)
+{
+       struct {
+               struct nlmsghdr nlh;
+               ipq_peer_msg_t pm;
+       } req;
+
+       memset(&req, 0, sizeof(req));
+       req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(req));
+       req.nlh.nlmsg_flags = NLM_F_REQUEST;
+       req.nlh.nlmsg_type = IPQM_MODE;
+       req.nlh.nlmsg_pid = h->local.nl_pid;
+       req.pm.msg.mode.value = mode;
+       req.pm.msg.mode.range = range;
+       return ipq_netlink_sendto(h, (void *)&req, req.nlh.nlmsg_len);
+}
+
+/*
+ * timeout is in microseconds (1 second is 1000000 (1 million) microseconds)
+ *
+ */
+ssize_t ipq_read(const struct ipq_handle *h,
+                 unsigned char *buf, size_t len, int timeout)
+{
+       return ipq_netlink_recvfrom(h, buf, len, timeout);
+}
+
+int ipq_message_type(const unsigned char *buf)
+{
+       return ((struct nlmsghdr*)buf)->nlmsg_type;
+}
+
+int ipq_get_msgerr(const unsigned char *buf)
+{
+       struct nlmsghdr *h = (struct nlmsghdr *)buf;
+       struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+       return -err->error;
+}
+
+ipq_packet_msg_t *ipq_get_packet(const unsigned char *buf)
+{
+       return NLMSG_DATA((struct nlmsghdr *)(buf));
+}
+
+int ipq_set_verdict(const struct ipq_handle *h,
+                    ipq_id_t id,
+                    unsigned int verdict,
+                    size_t data_len,
+                    unsigned char *buf)
+{
+       unsigned char nvecs;
+       size_t tlen;
+       struct nlmsghdr nlh;
+       ipq_peer_msg_t pm;
+       struct iovec iov[3];
+       struct msghdr msg;
+
+       memset(&nlh, 0, sizeof(nlh));
+       nlh.nlmsg_flags = NLM_F_REQUEST;
+       nlh.nlmsg_type = IPQM_VERDICT;
+       nlh.nlmsg_pid = h->local.nl_pid;
+       memset(&pm, 0, sizeof(pm));
+       pm.msg.verdict.value = verdict;
+       pm.msg.verdict.id = id;
+       pm.msg.verdict.data_len = data_len;
+       iov[0].iov_base = &nlh;
+       iov[0].iov_len = sizeof(nlh);
+       iov[1].iov_base = &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;
+}
+
+char *ipq_errstr(void)
+{
+       return ipq_strerror(ipq_errno);
+}
+
+void ipq_perror(const char *s)
+{
+       if (s)
+               fputs(s, stderr);
+       else
+               fputs("ERROR", stderr);
+       if (ipq_errno)
+               fprintf(stderr, ": %s", ipq_errstr());
+       if (errno)
+               fprintf(stderr, ": %s", strerror(errno));
+       fputc('\n', stderr);
+}
diff --git a/libiptc/Makefile b/libiptc/Makefile
new file mode 100644 (file)
index 0000000..1fbebef
--- /dev/null
@@ -0,0 +1,23 @@
+#! /usr/bin/make
+
+EXTRAS+=libiptc/libiptc.a
+
+DEVEL_LIBS+=libiptc/libiptc.a
+
+ifndef TOPLEVEL_INCLUDED
+local:
+       cd .. && $(MAKE) $(KERN_TARGETS) $(SHARED_LIBS) $(EXTRAS)
+
+else
+EXTRA_DEPENDS+=libiptc/libip4tc.d
+
+libiptc/libiptc.a: libiptc/libiptc.a(libiptc/libip4tc.o)
+
+ifeq ($(DO_IPV6), 1)
+EXTRA_DEPENDS+= libiptc/libip6tc.d
+libiptc/libiptc.a: libiptc/libiptc.a(libiptc/libip6tc.o)
+endif
+
+libiptc/libip4tc.d libiptc/libip6tc.d: %.d: %.c
+       @-$(CC) -M -MG $(CFLAGS) $< | sed -e 's@^.*\.o:@$*.d libiptc/libiptc.a($*.o):@' > $@
+endif
diff --git a/libiptc/libip4tc.c b/libiptc/libip4tc.c
new file mode 100644 (file)
index 0000000..76a8281
--- /dev/null
@@ -0,0 +1,520 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libiptc.h"
+
+#define IP_VERSION     4
+#define IP_OFFSET      0x1FFF
+
+#define HOOK_PRE_ROUTING       NF_IP_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP_LOCAL_IN
+#define HOOK_FORWARD           NF_IP_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP_POST_ROUTING
+#ifdef NF_IP_DROPPING
+#define HOOK_DROPPING          NF_IP_DROPPING
+#endif
+
+#define STRUCT_ENTRY_TARGET    struct ipt_entry_target
+#define STRUCT_ENTRY           struct ipt_entry
+#define STRUCT_ENTRY_MATCH     struct ipt_entry_match
+#define STRUCT_GETINFO         struct ipt_getinfo
+#define STRUCT_GET_ENTRIES     struct ipt_get_entries
+#define STRUCT_COUNTERS                struct ipt_counters
+#define STRUCT_COUNTERS_INFO   struct ipt_counters_info
+#define STRUCT_STANDARD_TARGET struct ipt_standard_target
+#define STRUCT_REPLACE         struct ipt_replace
+
+#define STRUCT_TC_HANDLE       struct iptc_handle
+#define TC_HANDLE_T            iptc_handle_t
+
+#define ENTRY_ITERATE          IPT_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IPT_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IPT_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ipt_get_target
+
+#define ERROR_TARGET           IPT_ERROR_TARGET
+#define NUMHOOKS               NF_IP_NUMHOOKS
+
+#define IPT_CHAINLABEL         ipt_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries
+#define TC_IS_CHAIN            iptc_is_chain
+#define TC_FIRST_CHAIN         iptc_first_chain
+#define TC_NEXT_CHAIN          iptc_next_chain
+#define TC_FIRST_RULE          iptc_first_rule
+#define TC_NEXT_RULE           iptc_next_rule
+#define TC_GET_TARGET          iptc_get_target
+#define TC_BUILTIN             iptc_builtin
+#define TC_GET_POLICY          iptc_get_policy
+#define TC_INSERT_ENTRY                iptc_insert_entry
+#define TC_REPLACE_ENTRY       iptc_replace_entry
+#define TC_APPEND_ENTRY                iptc_append_entry
+#define TC_DELETE_ENTRY                iptc_delete_entry
+#define TC_DELETE_NUM_ENTRY    iptc_delete_num_entry
+#define TC_CHECK_PACKET                iptc_check_packet
+#define TC_FLUSH_ENTRIES       iptc_flush_entries
+#define TC_ZERO_ENTRIES                iptc_zero_entries
+#define TC_READ_COUNTER                iptc_read_counter
+#define TC_ZERO_COUNTER                iptc_zero_counter
+#define TC_SET_COUNTER         iptc_set_counter
+#define TC_CREATE_CHAIN                iptc_create_chain
+#define TC_GET_REFERENCES      iptc_get_references
+#define TC_DELETE_CHAIN                iptc_delete_chain
+#define TC_RENAME_CHAIN                iptc_rename_chain
+#define TC_SET_POLICY          iptc_set_policy
+#define TC_GET_RAW_SOCKET      iptc_get_raw_socket
+#define TC_INIT                        iptc_init
+#define TC_FREE                        iptc_free
+#define TC_COMMIT              iptc_commit
+#define TC_STRERROR            iptc_strerror
+
+#define TC_AF                  AF_INET
+#define TC_IPPROTO             IPPROTO_IP
+
+#define SO_SET_REPLACE         IPT_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IPT_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IPT_SO_GET_INFO
+#define SO_GET_ENTRIES         IPT_SO_GET_ENTRIES
+#define SO_GET_VERSION         IPT_SO_GET_VERSION
+
+#define STANDARD_TARGET                IPT_STANDARD_TARGET
+#define LABEL_RETURN           IPTC_LABEL_RETURN
+#define LABEL_ACCEPT           IPTC_LABEL_ACCEPT
+#define LABEL_DROP             IPTC_LABEL_DROP
+#define LABEL_QUEUE            IPTC_LABEL_QUEUE
+
+#define ALIGN                  IPT_ALIGN
+#define RETURN                 IPT_RETURN
+
+#include "libiptc.c"
+
+#define IP_PARTS_NATIVE(n)                     \
+(unsigned int)((n)>>24)&0xFF,                  \
+(unsigned int)((n)>>16)&0xFF,                  \
+(unsigned int)((n)>>8)&0xFF,                   \
+(unsigned int)((n)&0xFF)
+
+#define IP_PARTS(n) IP_PARTS_NATIVE(ntohl(n))
+
+int
+dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle)
+{
+       size_t i;
+       STRUCT_ENTRY_TARGET *t;
+
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       printf("SRC IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.src.s_addr),IP_PARTS(e->ip.smsk.s_addr));
+       printf("DST IP: %u.%u.%u.%u/%u.%u.%u.%u\n",
+              IP_PARTS(e->ip.dst.s_addr),IP_PARTS(e->ip.dmsk.s_addr));
+       printf("Interface: `%s'/", e->ip.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ip.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ip.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ip.proto);
+       printf("Flags: %02X\n", e->ip.flags);
+       printf("Invflags: %02X\n", e->ip.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              e->counters.pcnt, e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       if (e->nfcache & NFC_IP_SRC) printf("IP_SRC ");
+       if (e->nfcache & NFC_IP_DST) printf("IP_DST ");
+       if (e->nfcache & NFC_IP_IF_IN) printf("IP_IF_IN ");
+       if (e->nfcache & NFC_IP_IF_OUT) printf("IP_IF_OUT ");
+       if (e->nfcache & NFC_IP_TOS) printf("IP_TOS ");
+       if (e->nfcache & NFC_IP_PROTO) printf("IP_PROTO ");
+       if (e->nfcache & NFC_IP_OPTIONS) printf("IP_OPTIONS ");
+       if (e->nfcache & NFC_IP_TCPFLAGS) printf("IP_TCPFLAGS ");
+       if (e->nfcache & NFC_IP_SRC_PT) printf("IP_SRC_PT ");
+       if (e->nfcache & NFC_IP_DST_PT) printf("IP_DST_PT ");
+       if (e->nfcache & NFC_IP_PROTO_UNKNOWN) printf("IP_PROTO_UNKNOWN ");
+       printf("\n");
+
+       IPT_MATCH_ITERATE(e, print_match);
+
+       t = GET_TARGET(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == -NF_QUEUE-1 ? "NF_QUEUE"
+                              : pos == RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IPT_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b, unsigned char *matchmask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (a->ip.src.s_addr != b->ip.src.s_addr
+           || a->ip.dst.s_addr != b->ip.dst.s_addr
+           || a->ip.smsk.s_addr != b->ip.smsk.s_addr
+           || a->ip.dmsk.s_addr != b->ip.dmsk.s_addr
+           || a->ip.proto != b->ip.proto
+           || a->ip.flags != b->ip.flags
+           || a->ip.invflags != b->ip.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ip.iniface_mask[i] != b->ip.iniface_mask[i])
+                       return 0;
+               if ((a->ip.iniface[i] & a->ip.iniface_mask[i])
+                   != (b->ip.iniface[i] & b->ip.iniface_mask[i]))
+                       return 0;
+               if (a->ip.outiface_mask[i] != b->ip.outiface_mask[i])
+                       return 0;
+               if ((a->ip.outiface[i] & a->ip.outiface_mask[i])
+                   != (b->ip.outiface[i] & b->ip.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IPT_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return 0;
+
+       ta = GET_TARGET((STRUCT_ENTRY *)a);
+       tb = GET_TARGET((STRUCT_ENTRY *)b);
+       if (ta->u.target_size != tb->u.target_size)
+               return 0;
+       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+               return 0;
+
+       mptr += sizeof(*ta);
+       if (target_different(ta->data, tb->data,
+                            ta->u.target_size - sizeof(*ta), mptr))
+               return 0;
+
+       return 1;
+}
+
+/***************************** DEBUGGING ********************************/
+static inline int
+unconditional(const struct ipt_ip *ip)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ip)/sizeof(u_int32_t); i++)
+               if (((u_int32_t *)ip)[i])
+                       return 0;
+
+       return 1;
+}
+
+static inline int
+check_match(const STRUCT_ENTRY_MATCH *m, unsigned int *off)
+{
+       assert(m->u.match_size >= sizeof(STRUCT_ENTRY_MATCH));
+       assert(ALIGN(m->u.match_size) == m->u.match_size);
+
+       (*off) += m->u.match_size;
+       return 0;
+}
+
+static inline int
+check_entry(const STRUCT_ENTRY *e, unsigned int *i, unsigned int *off,
+           unsigned int user_offset, int *was_return,
+           TC_HANDLE_T h)
+{
+       unsigned int toff;
+       STRUCT_STANDARD_TARGET *t;
+
+       assert(e->target_offset >= sizeof(STRUCT_ENTRY));
+       assert(e->next_offset >= e->target_offset
+              + sizeof(STRUCT_ENTRY_TARGET));
+       toff = sizeof(STRUCT_ENTRY);
+       IPT_MATCH_ITERATE(e, check_match, &toff);
+
+       assert(toff == e->target_offset);
+
+       t = (STRUCT_STANDARD_TARGET *)
+               GET_TARGET((STRUCT_ENTRY *)e);
+       /* next_offset will have to be multiple of entry alignment. */
+       assert(e->next_offset == ALIGN(e->next_offset));
+       assert(e->target_offset == ALIGN(e->target_offset));
+       assert(t->target.u.target_size == ALIGN(t->target.u.target_size));
+       assert(!TC_IS_CHAIN(t->target.u.user.name, h));
+
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(STRUCT_STANDARD_TARGET)));
+
+               assert(t->verdict == -NF_DROP-1
+                      || t->verdict == -NF_ACCEPT-1
+                      || t->verdict == RETURN
+                      || t->verdict < (int)h->entries.size);
+
+               if (t->verdict >= 0) {
+                       STRUCT_ENTRY *te = get_entry(h, t->verdict);
+                       int idx;
+
+                       idx = entry2index(h, te);
+                       assert(strcmp(GET_TARGET(te)->u.user.name,
+                                     IPT_ERROR_TARGET)
+                              != 0);
+                       assert(te != e);
+
+                       /* Prior node must be error node, or this node. */
+                       assert(t->verdict == entry2offset(h, e)+e->next_offset
+                              || strcmp(GET_TARGET(index2entry(h, idx-1))
+                                        ->u.user.name, IPT_ERROR_TARGET)
+                              == 0);
+               }
+
+               if (t->verdict == RETURN
+                   && unconditional(&e->ip)
+                   && e->target_offset == sizeof(*e))
+                       *was_return = 1;
+               else
+                       *was_return = 0;
+       } else if (strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0) {
+               assert(t->target.u.target_size
+                      == ALIGN(sizeof(struct ipt_error_target)));
+
+               /* If this is in user area, previous must have been return */
+               if (*off > user_offset)
+                       assert(*was_return);
+
+               *was_return = 0;
+       }
+       else *was_return = 0;
+
+       if (*off == user_offset)
+               assert(strcmp(t->target.u.user.name, IPT_ERROR_TARGET) == 0);
+
+       (*off) += e->next_offset;
+       (*i)++;
+       return 0;
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_LOCAL_IN
+                          | 1 << NF_IP_FORWARD
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_POST_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)));
+
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_OUT)) || 
+                      (h->info.valid_hooks
+                       == (1 << NF_IP_PRE_ROUTING
+                           | 1 << NF_IP_LOCAL_IN
+                           | 1 << NF_IP_FORWARD
+                           | 1 << NF_IP_LOCAL_OUT
+                           | 1 << NF_IP_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP_POST_ROUTING];
+               }
+       } else if (strcmp(h->info.name, "raw") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP_PRE_ROUTING
+                          | 1 << NF_IP_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP_LOCAL_OUT];
+
+#ifdef NF_IP_DROPPING
+       } else if (strcmp(h->info.name, "drop") == 0) {
+               assert(h->info.valid_hooks == (1 << NF_IP_DROPPING));
+
+               /* Hook should be first */
+               assert(h->info.hook_entry[NF_IP_DROPPING] == 0);
+               user_offset = 0;
+#endif
+       } else {
+               fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ip));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc/libip6tc.c b/libiptc/libip6tc.c
new file mode 100644 (file)
index 0000000..f7947db
--- /dev/null
@@ -0,0 +1,462 @@
+/* Library which manipulates firewall rules.  Version 0.1. */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C)1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+   COPYING for details). */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+
+#ifdef DEBUG_CONNTRACK
+#define inline
+#endif
+
+#if !defined(__GLIBC__) || (__GLIBC__ < 2)
+typedef unsigned int socklen_t;
+#endif
+
+#include "libiptc/libip6tc.h"
+
+#define HOOK_PRE_ROUTING       NF_IP6_PRE_ROUTING
+#define HOOK_LOCAL_IN          NF_IP6_LOCAL_IN
+#define HOOK_FORWARD           NF_IP6_FORWARD
+#define HOOK_LOCAL_OUT         NF_IP6_LOCAL_OUT
+#define HOOK_POST_ROUTING      NF_IP6_POST_ROUTING
+
+#define STRUCT_ENTRY_TARGET    struct ip6t_entry_target
+#define STRUCT_ENTRY           struct ip6t_entry
+#define STRUCT_ENTRY_MATCH     struct ip6t_entry_match
+#define STRUCT_GETINFO         struct ip6t_getinfo
+#define STRUCT_GET_ENTRIES     struct ip6t_get_entries
+#define STRUCT_COUNTERS                struct ip6t_counters
+#define STRUCT_COUNTERS_INFO   struct ip6t_counters_info
+#define STRUCT_STANDARD_TARGET struct ip6t_standard_target
+#define STRUCT_REPLACE         struct ip6t_replace
+
+#define STRUCT_TC_HANDLE       struct ip6tc_handle
+#define TC_HANDLE_T            ip6tc_handle_t
+
+#define ENTRY_ITERATE          IP6T_ENTRY_ITERATE
+#define TABLE_MAXNAMELEN       IP6T_TABLE_MAXNAMELEN
+#define FUNCTION_MAXNAMELEN    IP6T_FUNCTION_MAXNAMELEN
+
+#define GET_TARGET             ip6t_get_target
+
+#define ERROR_TARGET           IP6T_ERROR_TARGET
+#define NUMHOOKS               NF_IP6_NUMHOOKS
+
+#define IPT_CHAINLABEL         ip6t_chainlabel
+
+#define TC_DUMP_ENTRIES                dump_entries6
+#define TC_IS_CHAIN            ip6tc_is_chain
+#define TC_FIRST_CHAIN         ip6tc_first_chain
+#define TC_NEXT_CHAIN          ip6tc_next_chain
+#define TC_FIRST_RULE          ip6tc_first_rule
+#define TC_NEXT_RULE           ip6tc_next_rule
+#define TC_GET_TARGET          ip6tc_get_target
+#define TC_BUILTIN             ip6tc_builtin
+#define TC_GET_POLICY          ip6tc_get_policy
+#define TC_INSERT_ENTRY                ip6tc_insert_entry
+#define TC_REPLACE_ENTRY       ip6tc_replace_entry
+#define TC_APPEND_ENTRY                ip6tc_append_entry
+#define TC_DELETE_ENTRY                ip6tc_delete_entry
+#define TC_DELETE_NUM_ENTRY    ip6tc_delete_num_entry
+#define TC_CHECK_PACKET                ip6tc_check_packet
+#define TC_FLUSH_ENTRIES       ip6tc_flush_entries
+#define TC_ZERO_ENTRIES                ip6tc_zero_entries
+#define TC_ZERO_COUNTER                ip6tc_zero_counter
+#define TC_READ_COUNTER                ip6tc_read_counter
+#define TC_SET_COUNTER         ip6tc_set_counter
+#define TC_CREATE_CHAIN                ip6tc_create_chain
+#define TC_GET_REFERENCES      ip6tc_get_references
+#define TC_DELETE_CHAIN                ip6tc_delete_chain
+#define TC_RENAME_CHAIN                ip6tc_rename_chain
+#define TC_SET_POLICY          ip6tc_set_policy
+#define TC_GET_RAW_SOCKET      ip6tc_get_raw_socket
+#define TC_INIT                        ip6tc_init
+#define TC_FREE                        ip6tc_free
+#define TC_COMMIT              ip6tc_commit
+#define TC_STRERROR            ip6tc_strerror
+
+#define TC_AF                  AF_INET6
+#define TC_IPPROTO             IPPROTO_IPV6
+
+#define SO_SET_REPLACE         IP6T_SO_SET_REPLACE
+#define SO_SET_ADD_COUNTERS    IP6T_SO_SET_ADD_COUNTERS
+#define SO_GET_INFO            IP6T_SO_GET_INFO
+#define SO_GET_ENTRIES         IP6T_SO_GET_ENTRIES
+#define SO_GET_VERSION         IP6T_SO_GET_VERSION
+
+#define STANDARD_TARGET                IP6T_STANDARD_TARGET
+#define LABEL_RETURN           IP6TC_LABEL_RETURN
+#define LABEL_ACCEPT           IP6TC_LABEL_ACCEPT
+#define LABEL_DROP             IP6TC_LABEL_DROP
+#define LABEL_QUEUE            IP6TC_LABEL_QUEUE
+
+#define ALIGN                  IP6T_ALIGN
+#define RETURN                 IP6T_RETURN
+
+#include "libiptc.c"
+
+#define BIT6(a, l) \
+ ((ntohl(a->in6_u.u6_addr32[(l) / 32]) >> (31 - ((l) & 31))) & 1)
+
+int
+ipv6_prefix_length(const struct in6_addr *a)
+{
+       int l, i;
+       for (l = 0; l < 128; l++) {
+               if (BIT6(a, l) == 0)
+                       break;
+       }
+       for (i = l + 1; i < 128; i++) {
+               if (BIT6(a, i) == 1)
+                       return -1;
+       }
+       return l;
+}
+
+static int
+dump_entry(struct ip6t_entry *e, const ip6tc_handle_t handle)
+{
+       size_t i;
+       char buf[40];
+       int len;
+       struct ip6t_entry_target *t;
+       
+       printf("Entry %u (%lu):\n", entry2index(handle, e),
+              entry2offset(handle, e));
+       puts("SRC IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.smsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.smsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       puts("DST IP: ");
+       inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof buf);
+       puts(buf);
+       putchar('/');
+       len = ipv6_prefix_length(&e->ipv6.dmsk);
+       if (len != -1)
+               printf("%d", len);
+       else {
+               inet_ntop(AF_INET6, &e->ipv6.dmsk, buf, sizeof buf);
+               puts(buf);
+       }
+       putchar('\n');
+       
+       printf("Interface: `%s'/", e->ipv6.iniface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.iniface_mask[i] ? 'X' : '.');
+       printf("to `%s'/", e->ipv6.outiface);
+       for (i = 0; i < IFNAMSIZ; i++)
+               printf("%c", e->ipv6.outiface_mask[i] ? 'X' : '.');
+       printf("\nProtocol: %u\n", e->ipv6.proto);
+       if (e->ipv6.flags & IP6T_F_TOS)
+               printf("TOS: %u\n", e->ipv6.tos);
+       printf("Flags: %02X\n", e->ipv6.flags);
+       printf("Invflags: %02X\n", e->ipv6.invflags);
+       printf("Counters: %llu packets, %llu bytes\n",
+              e->counters.pcnt, e->counters.bcnt);
+       printf("Cache: %08X ", e->nfcache);
+       if (e->nfcache & NFC_ALTERED) printf("ALTERED ");
+       if (e->nfcache & NFC_UNKNOWN) printf("UNKNOWN ");
+       if (e->nfcache & NFC_IP6_SRC) printf("IP6_SRC ");
+       if (e->nfcache & NFC_IP6_DST) printf("IP6_DST ");
+       if (e->nfcache & NFC_IP6_IF_IN) printf("IP6_IF_IN ");
+       if (e->nfcache & NFC_IP6_IF_OUT) printf("IP6_IF_OUT ");
+       if (e->nfcache & NFC_IP6_TOS) printf("IP6_TOS ");
+       if (e->nfcache & NFC_IP6_PROTO) printf("IP6_PROTO ");
+       if (e->nfcache & NFC_IP6_OPTIONS) printf("IP6_OPTIONS ");
+       if (e->nfcache & NFC_IP6_TCPFLAGS) printf("IP6_TCPFLAGS ");
+       if (e->nfcache & NFC_IP6_SRC_PT) printf("IP6_SRC_PT ");
+       if (e->nfcache & NFC_IP6_DST_PT) printf("IP6_DST_PT ");
+       if (e->nfcache & NFC_IP6_PROTO_UNKNOWN) printf("IP6_PROTO_UNKNOWN ");
+       printf("\n");
+       
+       IP6T_MATCH_ITERATE(e, print_match);
+
+       t = ip6t_get_target(e);
+       printf("Target name: `%s' [%u]\n", t->u.user.name, t->u.target_size);
+       if (strcmp(t->u.user.name, IP6T_STANDARD_TARGET) == 0) {
+               int pos = *(int *)t->data;
+               if (pos < 0)
+                       printf("verdict=%s\n",
+                              pos == -NF_ACCEPT-1 ? "NF_ACCEPT"
+                              : pos == -NF_DROP-1 ? "NF_DROP"
+                              : pos == IP6T_RETURN ? "RETURN"
+                              : "UNKNOWN");
+               else
+                       printf("verdict=%u\n", pos);
+       } else if (strcmp(t->u.user.name, IP6T_ERROR_TARGET) == 0)
+               printf("error=`%s'\n", t->data);
+
+       printf("\n");
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a, const STRUCT_ENTRY *b,
+       unsigned char *matchmask)
+{
+       unsigned int i;
+       STRUCT_ENTRY_TARGET *ta, *tb;
+       unsigned char *mptr;
+
+       /* Always compare head structures: ignore mask here. */
+       if (memcmp(&a->ipv6.src, &b->ipv6.src, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dst, &b->ipv6.dst, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.smsk, &b->ipv6.smsk, sizeof(struct in6_addr))
+           || memcmp(&a->ipv6.dmsk, &b->ipv6.dmsk, sizeof(struct in6_addr))
+           || a->ipv6.proto != b->ipv6.proto
+           || a->ipv6.tos != b->ipv6.tos
+           || a->ipv6.flags != b->ipv6.flags
+           || a->ipv6.invflags != b->ipv6.invflags)
+               return 0;
+
+       for (i = 0; i < IFNAMSIZ; i++) {
+               if (a->ipv6.iniface_mask[i] != b->ipv6.iniface_mask[i])
+                       return 0;
+               if ((a->ipv6.iniface[i] & a->ipv6.iniface_mask[i])
+                   != (b->ipv6.iniface[i] & b->ipv6.iniface_mask[i]))
+                       return 0;
+               if (a->ipv6.outiface_mask[i] != b->ipv6.outiface_mask[i])
+                       return 0;
+               if ((a->ipv6.outiface[i] & a->ipv6.outiface_mask[i])
+                   != (b->ipv6.outiface[i] & b->ipv6.outiface_mask[i]))
+                       return 0;
+       }
+
+       if (a->nfcache != b->nfcache
+           || a->target_offset != b->target_offset
+           || a->next_offset != b->next_offset)
+               return 0;
+
+       mptr = matchmask + sizeof(STRUCT_ENTRY);
+       if (IP6T_MATCH_ITERATE(a, match_different, a->elems, b->elems, &mptr))
+               return 0;
+
+       ta = GET_TARGET((STRUCT_ENTRY *)a);
+       tb = GET_TARGET((STRUCT_ENTRY *)b);
+       if (ta->u.target_size != tb->u.target_size)
+               return 0;
+       if (strcmp(ta->u.user.name, tb->u.user.name) != 0)
+               return 0;
+       mptr += sizeof(*ta);
+
+       if (target_different(ta->data, tb->data,
+                            ta->u.target_size - sizeof(*ta), mptr))
+               return 0;
+
+       return 1;
+}
+
+/* All zeroes == unconditional rule. */
+static inline int
+unconditional(const struct ip6t_ip6 *ipv6)
+{
+       unsigned int i;
+
+       for (i = 0; i < sizeof(*ipv6); i++)
+               if (((char *)ipv6)[i])
+                       break;
+
+       return (i == sizeof(*ipv6));
+}
+
+#ifdef IPTC_DEBUG
+/* Do every conceivable sanity check on the handle */
+static void
+do_check(TC_HANDLE_T h, unsigned int line)
+{
+       unsigned int i, n;
+       unsigned int user_offset; /* Offset of first user chain */
+       int was_return;
+
+       assert(h->changed == 0 || h->changed == 1);
+       if (strcmp(h->info.name, "filter") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_LOCAL_IN
+                          | 1 << NF_IP6_FORWARD
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == 0);
+
+               n = get_chain_end(h, 0);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else if (strcmp(h->info.name, "nat") == 0) {
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+               n = get_chain_end(h, n);
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_LOCAL_IN];
+               }
+
+       } else if (strcmp(h->info.name, "mangle") == 0) {
+               /* This code is getting ugly because linux < 2.4.18-pre6 had
+                * two mangle hooks, linux >= 2.4.18-pre6 has five mangle hooks
+                * */
+               assert((h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_OUT)) ||
+                      (h->info.valid_hooks
+                       == (1 << NF_IP6_PRE_ROUTING
+                           | 1 << NF_IP6_LOCAL_IN
+                           | 1 << NF_IP6_FORWARD
+                           | 1 << NF_IP6_LOCAL_OUT
+                           | 1 << NF_IP6_POST_ROUTING)));
+
+               /* Hooks should be first five */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, 0);
+
+               if (h->info.valid_hooks & (1 << NF_IP6_LOCAL_IN)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_LOCAL_IN] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               if (h->info.valid_hooks & (1 << NF_IP6_FORWARD)) {
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_FORWARD] == n);
+                       n = get_chain_end(h, n);
+               }
+
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+
+               if (h->info.valid_hooks & (1 << NF_IP6_POST_ROUTING)) {
+                       n = get_chain_end(h, n);
+                       n += get_entry(h, n)->next_offset;
+                       assert(h->info.hook_entry[NF_IP6_POST_ROUTING] == n);
+                       user_offset = h->info.hook_entry[NF_IP6_POST_ROUTING];
+               }
+       } else if (strcmp(h->info.name, "raw") == 0) {
+               assert(h->info.valid_hooks
+                      == (1 << NF_IP6_PRE_ROUTING
+                          | 1 << NF_IP6_LOCAL_OUT));
+
+               /* Hooks should be first three */
+               assert(h->info.hook_entry[NF_IP6_PRE_ROUTING] == 0);
+
+               n = get_chain_end(h, n);
+               n += get_entry(h, n)->next_offset;
+               assert(h->info.hook_entry[NF_IP6_LOCAL_OUT] == n);
+
+               user_offset = h->info.hook_entry[NF_IP6_LOCAL_OUT];
+       } else {
+                fprintf(stderr, "Unknown table `%s'\n", h->info.name);
+               abort();
+       }
+
+       /* User chain == end of last builtin + policy entry */
+       user_offset = get_chain_end(h, user_offset);
+       user_offset += get_entry(h, user_offset)->next_offset;
+
+       /* Overflows should be end of entry chains, and unconditional
+           policy nodes. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               STRUCT_ENTRY *e;
+               STRUCT_STANDARD_TARGET *t;
+
+               if (!(h->info.valid_hooks & (1 << i)))
+                       continue;
+               assert(h->info.underflow[i]
+                      == get_chain_end(h, h->info.hook_entry[i]));
+
+               e = get_entry(h, get_chain_end(h, h->info.hook_entry[i]));
+               assert(unconditional(&e->ipv6));
+               assert(e->target_offset == sizeof(*e));
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+               printf("target_size=%u, align=%u\n",
+                       t->target.u.target_size, ALIGN(sizeof(*t)));
+               assert(t->target.u.target_size == ALIGN(sizeof(*t)));
+               assert(e->next_offset == sizeof(*e) + ALIGN(sizeof(*t)));
+
+               assert(strcmp(t->target.u.user.name, STANDARD_TARGET)==0);
+               assert(t->verdict == -NF_DROP-1 || t->verdict == -NF_ACCEPT-1);
+
+               /* Hooks and underflows must be valid entries */
+               entry2index(h, get_entry(h, h->info.hook_entry[i]));
+               entry2index(h, get_entry(h, h->info.underflow[i]));
+       }
+
+       assert(h->info.size
+              >= h->info.num_entries * (sizeof(STRUCT_ENTRY)
+                                        +sizeof(STRUCT_STANDARD_TARGET)));
+
+       assert(h->entries.size
+              >= (h->new_number
+                  * (sizeof(STRUCT_ENTRY)
+                     + sizeof(STRUCT_STANDARD_TARGET))));
+       assert(strcmp(h->info.name, h->entries.name) == 0);
+
+       i = 0; n = 0;
+       was_return = 0;
+
+#if 0
+       /* Check all the entries. */
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     check_entry, &i, &n, user_offset, &was_return, h);
+
+       assert(i == h->new_number);
+       assert(n == h->entries.size);
+
+       /* Final entry must be error node */
+       assert(strcmp(GET_TARGET(index2entry(h, h->new_number-1))
+                     ->u.user.name,
+                     ERROR_TARGET) == 0);
+#endif
+}
+#endif /*IPTC_DEBUG*/
diff --git a/libiptc/libiptc.c b/libiptc/libiptc.c
new file mode 100644 (file)
index 0000000..3b2831f
--- /dev/null
@@ -0,0 +1,1924 @@
+/* Library which manipulates firewall rules.  Version $Revision: 1.41 $ */
+
+/* Architecture of firewall rules is as follows:
+ *
+ * Chains go INPUT, FORWARD, OUTPUT then user chains.
+ * Each user chain starts with an ERROR node.
+ * Every chain ends with an unconditional jump: a RETURN for user chains,
+ * and a POLICY for built-ins.
+ */
+
+/* (C) 1999 Paul ``Rusty'' Russell - Placed under the GNU GPL (See
+ * COPYING for details). 
+ * (C) 2000-2003 by the Netfilter Core Team <coreteam@netfilter.org>
+ *
+ * 2003-Jun-20: Harald Welte <laforge@netfilter.org>:
+ *     - Reimplementation of chain cache to use offsets instead of entries
+ * 2003-Jun-23: Harald Welte <laforge@netfilter.org>:
+ *     - performance optimization, sponsored by Astaro AG (http://www.astaro.com/)
+ *       don't rebuild the chain cache after every operation, instead fix it
+ *       up after a ruleset change.  
+ */
+
+#ifndef IPT_LIB_DIR
+#define IPT_LIB_DIR "/usr/lib/iptables"
+#endif
+
+#ifndef __OPTIMIZE__
+STRUCT_ENTRY_TARGET *
+GET_TARGET(STRUCT_ENTRY *e)
+{
+       return (void *)e + e->target_offset;
+}
+#endif
+
+static int sockfd = -1;
+static void *iptc_fn = NULL;
+
+static const char *hooknames[]
+= { [HOOK_PRE_ROUTING]  "PREROUTING",
+    [HOOK_LOCAL_IN]     "INPUT",
+    [HOOK_FORWARD]      "FORWARD",
+    [HOOK_LOCAL_OUT]    "OUTPUT",
+    [HOOK_POST_ROUTING] "POSTROUTING",
+#ifdef HOOK_DROPPING
+    [HOOK_DROPPING]    "DROPPING"
+#endif
+};
+
+struct counter_map
+{
+       enum {
+               COUNTER_MAP_NOMAP,
+               COUNTER_MAP_NORMAL_MAP,
+               COUNTER_MAP_ZEROED,
+               COUNTER_MAP_SET
+       } maptype;
+       unsigned int mappos;
+};
+
+/* Convenience structures */
+struct ipt_error_target
+{
+       STRUCT_ENTRY_TARGET t;
+       char error[TABLE_MAXNAMELEN];
+};
+
+struct chain_cache
+{
+       char name[TABLE_MAXNAMELEN];
+       /* This is the first rule in chain. */
+       unsigned int start_off;
+       /* Last rule in chain */
+       unsigned int end_off;
+};
+
+STRUCT_TC_HANDLE
+{
+       /* Have changes been made? */
+       int changed;
+       /* Size in here reflects original state. */
+       STRUCT_GETINFO info;
+
+       struct counter_map *counter_map;
+       /* Array of hook names */
+       const char **hooknames;
+
+       /* Cached position of chain heads (NULL = no cache). */
+       unsigned int cache_num_chains;
+       unsigned int cache_num_builtins;
+       struct chain_cache *cache_chain_heads;
+
+       /* Chain iterator: current chain cache entry. */
+       struct chain_cache *cache_chain_iteration;
+
+       /* Rule iterator: terminal rule */
+       STRUCT_ENTRY *cache_rule_end;
+
+       /* Number in here reflects current state. */
+       unsigned int new_number;
+       STRUCT_GET_ENTRIES entries;
+};
+
+static void
+set_changed(TC_HANDLE_T h)
+{
+       h->changed = 1;
+}
+
+#ifdef IPTC_DEBUG
+static void do_check(TC_HANDLE_T h, unsigned int line);
+#define CHECK(h) do { if (!getenv("IPTC_NO_CHECK")) do_check((h), __LINE__); } while(0)
+#else
+#define CHECK(h)
+#endif
+
+static inline int
+get_number(const STRUCT_ENTRY *i,
+          const STRUCT_ENTRY *seek,
+          unsigned int *pos)
+{
+       if (i == seek)
+               return 1;
+       (*pos)++;
+       return 0;
+}
+
+static unsigned int
+entry2index(const TC_HANDLE_T h, const STRUCT_ENTRY *seek)
+{
+       unsigned int pos = 0;
+
+       if (ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                         get_number, seek, &pos) == 0) {
+               fprintf(stderr, "ERROR: offset %i not an entry!\n",
+                       (char *)seek - (char *)h->entries.entrytable);
+               abort();
+       }
+       return pos;
+}
+
+static inline int
+get_entry_n(STRUCT_ENTRY *i,
+           unsigned int number,
+           unsigned int *pos,
+           STRUCT_ENTRY **pe)
+{
+       if (*pos == number) {
+               *pe = i;
+               return 1;
+       }
+       (*pos)++;
+       return 0;
+}
+
+static STRUCT_ENTRY *
+index2entry(TC_HANDLE_T h, unsigned int index)
+{
+       unsigned int pos = 0;
+       STRUCT_ENTRY *ret = NULL;
+
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     get_entry_n, index, &pos, &ret);
+
+       return ret;
+}
+
+static inline STRUCT_ENTRY *
+get_entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *)((char *)h->entries.entrytable + offset);
+}
+
+static inline unsigned long
+entry2offset(const TC_HANDLE_T h, const STRUCT_ENTRY *e)
+{
+       return (void *)e - (void *)h->entries.entrytable;
+}
+
+static inline unsigned long
+index2offset(TC_HANDLE_T h, unsigned int index)
+{
+       return entry2offset(h, index2entry(h, index));
+}
+
+static inline STRUCT_ENTRY *
+offset2entry(TC_HANDLE_T h, unsigned int offset)
+{
+       return (STRUCT_ENTRY *) ((void *)h->entries.entrytable+offset);
+}
+
+static inline unsigned int
+offset2index(const TC_HANDLE_T h, unsigned int offset)
+{
+       return entry2index(h, offset2entry(h, offset));
+}
+
+
+static const char *
+get_errorlabel(TC_HANDLE_T h, unsigned int offset)
+{
+       STRUCT_ENTRY *e;
+
+       e = get_entry(h, offset);
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) != 0) {
+               fprintf(stderr, "ERROR: offset %u not an error node!\n",
+                       offset);
+               abort();
+       }
+
+       return (const char *)GET_TARGET(e)->data;
+}
+
+/* Allocate handle of given size */
+static TC_HANDLE_T
+alloc_handle(const char *tablename, unsigned int size, unsigned int num_rules)
+{
+       size_t len;
+       TC_HANDLE_T h;
+
+       len = sizeof(STRUCT_TC_HANDLE)
+               + size
+               + num_rules * sizeof(struct counter_map);
+
+       if ((h = malloc(len)) == NULL) {
+               errno = ENOMEM;
+               return NULL;
+       }
+
+       h->changed = 0;
+       h->cache_num_chains = 0;
+       h->cache_chain_heads = NULL;
+       h->counter_map = (void *)h
+               + sizeof(STRUCT_TC_HANDLE)
+               + size;
+       strcpy(h->info.name, tablename);
+       strcpy(h->entries.name, tablename);
+
+       return h;
+}
+
+TC_HANDLE_T
+TC_INIT(const char *tablename)
+{
+       TC_HANDLE_T h;
+       STRUCT_GETINFO info;
+       unsigned int i;
+       int tmp;
+       socklen_t s;
+
+       iptc_fn = TC_INIT;
+
+       if (sockfd != -1) {
+               close(sockfd);
+               sockfd = -1;
+       }
+
+       if (strlen(tablename) >= TABLE_MAXNAMELEN) {
+               errno = EINVAL;
+               return NULL;
+       }
+       
+       sockfd = socket(TC_AF, SOCK_RAW, IPPROTO_RAW);
+       if (sockfd < 0)
+               return NULL;
+
+       s = sizeof(info);
+
+       strcpy(info.name, tablename);
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_INFO, &info, &s) < 0)
+               return NULL;
+
+       if ((h = alloc_handle(info.name, info.size, info.num_entries))
+           == NULL) {
+               close(sockfd);
+               sockfd = -1;
+               return NULL;
+       }
+
+/* Too hard --RR */
+#if 0
+       sprintf(pathname, "%s/%s", IPT_LIB_DIR, info.name);
+       dynlib = dlopen(pathname, RTLD_NOW);
+       if (!dynlib) {
+               errno = ENOENT;
+               return NULL;
+       }
+       h->hooknames = dlsym(dynlib, "hooknames");
+       if (!h->hooknames) {
+               errno = ENOENT;
+               return NULL;
+       }
+#else
+       h->hooknames = hooknames;
+#endif
+
+       /* Initialize current state */
+       h->info = info;
+       h->new_number = h->info.num_entries;
+       for (i = 0; i < h->info.num_entries; i++)
+               h->counter_map[i]
+                       = ((struct counter_map){COUNTER_MAP_NORMAL_MAP, i});
+
+       h->entries.size = h->info.size;
+
+       tmp = sizeof(STRUCT_GET_ENTRIES) + h->info.size;
+
+       if (getsockopt(sockfd, TC_IPPROTO, SO_GET_ENTRIES, &h->entries,
+                      &tmp) < 0) {
+               close(sockfd);
+               sockfd = -1;
+               free(h);
+               return NULL;
+       }
+
+       CHECK(h);
+       return h;
+}
+
+void
+TC_FREE(TC_HANDLE_T *h)
+{
+       close(sockfd);
+       sockfd = -1;
+       if ((*h)->cache_chain_heads)
+               free((*h)->cache_chain_heads);
+       free(*h);
+       *h = NULL;
+}
+
+static inline int
+print_match(const STRUCT_ENTRY_MATCH *m)
+{
+       printf("Match name: `%s'\n", m->u.user.name);
+       return 0;
+}
+
+static int dump_entry(STRUCT_ENTRY *e, const TC_HANDLE_T handle);
+void
+TC_DUMP_ENTRIES(const TC_HANDLE_T handle)
+{
+       CHECK(handle);
+
+       printf("libiptc v%s.  %u entries, %u bytes.\n",
+              IPTABLES_VERSION,
+              handle->new_number, handle->entries.size);
+       printf("Table `%s'\n", handle->info.name);
+       printf("Hooks: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.hook_entry[HOOK_PRE_ROUTING],
+              handle->info.hook_entry[HOOK_LOCAL_IN],
+              handle->info.hook_entry[HOOK_FORWARD],
+              handle->info.hook_entry[HOOK_LOCAL_OUT],
+              handle->info.hook_entry[HOOK_POST_ROUTING]);
+       printf("Underflows: pre/in/fwd/out/post = %u/%u/%u/%u/%u\n",
+              handle->info.underflow[HOOK_PRE_ROUTING],
+              handle->info.underflow[HOOK_LOCAL_IN],
+              handle->info.underflow[HOOK_FORWARD],
+              handle->info.underflow[HOOK_LOCAL_OUT],
+              handle->info.underflow[HOOK_POST_ROUTING]);
+
+       ENTRY_ITERATE(handle->entries.entrytable, handle->entries.size,
+                     dump_entry, handle);
+}
+
+/* Returns 0 if not hook entry, else hooknumber + 1 */
+static inline unsigned int
+is_hook_entry(STRUCT_ENTRY *e, TC_HANDLE_T h)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((h->info.valid_hooks & (1 << i))
+                   && get_entry(h, h->info.hook_entry[i]) == e)
+                       return i+1;
+       }
+       return 0;
+}
+
+static inline int
+add_chain(STRUCT_ENTRY *e, TC_HANDLE_T h, STRUCT_ENTRY **prev)
+{
+       unsigned int builtin;
+
+       /* Last entry.  End it. */
+       if (entry2offset(h, e) + e->next_offset == h->entries.size) {
+               /* This is the ERROR node at end of the table */
+               h->cache_chain_heads[h->cache_num_chains-1].end_off = 
+                       entry2offset(h, *prev);
+               return 0;
+       }
+
+       /* We know this is the start of a new chain if it's an ERROR
+          target, or a hook entry point */
+       if (strcmp(GET_TARGET(e)->u.user.name, ERROR_TARGET) == 0) {
+               /* prev was last entry in previous chain */
+               h->cache_chain_heads[h->cache_num_chains-1].end_off
+                       = entry2offset(h, *prev);
+
+               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+                      (const char *)GET_TARGET(e)->data);
+               h->cache_chain_heads[h->cache_num_chains].start_off
+                       = entry2offset(h, (void *)e + e->next_offset);
+               h->cache_num_chains++;
+       } else if ((builtin = is_hook_entry(e, h)) != 0) {
+               if (h->cache_num_chains > 0)
+                       /* prev was last entry in previous chain */
+                       h->cache_chain_heads[h->cache_num_chains-1].end_off
+                               = entry2offset(h, *prev);
+
+               strcpy(h->cache_chain_heads[h->cache_num_chains].name,
+                      h->hooknames[builtin-1]);
+               h->cache_chain_heads[h->cache_num_chains].start_off
+                       = entry2offset(h, (void *)e);
+               h->cache_num_chains++;
+       }
+
+       *prev = e;
+       return 0;
+}
+
+static int alphasort(const void *a, const void *b)
+{
+       return strcmp(((struct chain_cache *)a)->name,
+                     ((struct chain_cache *)b)->name);
+}
+
+static int populate_cache(TC_HANDLE_T h)
+{
+       unsigned int i;
+       STRUCT_ENTRY *prev;
+
+       /* # chains < # rules / 2 + num builtins - 1 */
+       h->cache_chain_heads = malloc((h->new_number / 2 + 4)
+                                     * sizeof(struct chain_cache));
+       if (!h->cache_chain_heads) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       h->cache_num_chains = 0;
+       h->cache_num_builtins = 0;
+
+       /* Count builtins */
+       for (i = 0; i < NUMHOOKS; i++) {
+               if (h->info.valid_hooks & (1 << i))
+                       h->cache_num_builtins++;
+       }
+
+       prev = NULL;
+       ENTRY_ITERATE(h->entries.entrytable, h->entries.size,
+                     add_chain, h, &prev);
+
+       qsort(h->cache_chain_heads + h->cache_num_builtins,
+             h->cache_num_chains - h->cache_num_builtins,
+             sizeof(struct chain_cache), alphasort);
+
+       return 1;
+}
+
+static int 
+correct_cache(TC_HANDLE_T h, unsigned int offset, int delta)
+{
+       int i;          /* needs to be signed because deleting first
+                          chain can make it drop to -1 */
+
+       if (!delta)
+               return 1;
+
+       for (i = 0; i < h->cache_num_chains; i++) {
+               struct chain_cache *cc = &h->cache_chain_heads[i];
+
+               if (delta < 0) {
+                       /* take care about deleted chains */
+                       if (cc->start_off > offset+delta
+                           && cc->end_off < offset) {
+                               /* this chain is within the deleted range,
+                                * let's remove it from the cache */
+                               void *start;
+                               unsigned int size;
+
+                               h->cache_num_chains--;
+
+                               /* no need for memmove since we are 
+                                * removing the last entry */
+                               if (i >= h->cache_num_chains)
+                                       continue;
+
+                               start = &h->cache_chain_heads[i+1];
+                               size = (h->cache_num_chains-i)
+                                       * sizeof(struct chain_cache);
+                               memmove(cc, start, size);
+
+                               /* iterate over same index again, since
+                                * it is now a different chain */
+                               i--;
+                               continue;
+                       }
+               }
+
+               if (cc->start_off > offset)
+                       cc->start_off += delta;
+
+               if (cc->end_off >= offset)
+                       cc->end_off += delta;
+       }
+       /* HW_FIXME: sorting might be needed, but just in case a new chain was
+        * added */
+
+       return 1;
+}
+
+static int
+add_chain_cache(TC_HANDLE_T h, const char *name, unsigned int start_off,
+               unsigned int end_off)
+{
+       struct chain_cache *ccs = realloc(h->cache_chain_heads, 
+                                         (h->new_number / 2 + 4 + 1)
+                                          * sizeof(struct chain_cache));
+       struct chain_cache *newcc;
+       
+       if (!ccs)
+               return 0;
+
+       h->cache_chain_heads = ccs;
+       newcc = &h->cache_chain_heads[h->cache_num_chains];
+       h->cache_num_chains++;
+
+       strncpy(newcc->name, name, TABLE_MAXNAMELEN-1);
+       newcc->start_off = start_off;
+       newcc->end_off = end_off;
+
+       return 1;
+}
+
+/* Returns cache ptr if found, otherwise NULL. */
+static struct chain_cache *
+find_label(const char *name, TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       if (handle->cache_chain_heads == NULL
+           && !populate_cache(handle))
+               return NULL;
+
+       /* FIXME: Linear search through builtins, then binary --RR */
+       for (i = 0; i < handle->cache_num_chains; i++) {
+               if (strcmp(handle->cache_chain_heads[i].name, name) == 0)
+                       return &handle->cache_chain_heads[i];
+       }
+
+       return NULL;
+}
+
+/* Does this chain exist? */
+int TC_IS_CHAIN(const char *chain, const TC_HANDLE_T handle)
+{
+       return find_label(chain, handle) != NULL;
+}
+
+/* Returns the position of the final (ie. unconditional) element. */
+static unsigned int
+get_chain_end(const TC_HANDLE_T handle, unsigned int start)
+{
+       unsigned int last_off, off;
+       STRUCT_ENTRY *e;
+
+       last_off = start;
+       e = get_entry(handle, start);
+
+       /* Terminate when we meet a error label or a hook entry. */
+       for (off = start + e->next_offset;
+            off < handle->entries.size;
+            last_off = off, off += e->next_offset) {
+               STRUCT_ENTRY_TARGET *t;
+               unsigned int i;
+
+               e = get_entry(handle, off);
+
+               /* We hit an entry point. */
+               for (i = 0; i < NUMHOOKS; i++) {
+                       if ((handle->info.valid_hooks & (1 << i))
+                           && off == handle->info.hook_entry[i])
+                               return last_off;
+               }
+
+               /* We hit a user chain label */
+               t = GET_TARGET(e);
+               if (strcmp(t->u.user.name, ERROR_TARGET) == 0)
+                       return last_off;
+       }
+       /* SHOULD NEVER HAPPEN */
+       fprintf(stderr, "ERROR: Off end (%u) of chain from %u!\n",
+               handle->entries.size, off);
+       abort();
+}
+
+/* Iterator functions to run through the chains. */
+const char *
+TC_FIRST_CHAIN(TC_HANDLE_T *handle)
+{
+       if ((*handle)->cache_chain_heads == NULL
+           && !populate_cache(*handle))
+               return NULL;
+
+       (*handle)->cache_chain_iteration
+               = &(*handle)->cache_chain_heads[0];
+
+       return (*handle)->cache_chain_iteration->name;
+}
+
+/* Iterator functions to run through the chains.  Returns NULL at end. */
+const char *
+TC_NEXT_CHAIN(TC_HANDLE_T *handle)
+{
+       (*handle)->cache_chain_iteration++;
+
+       if ((*handle)->cache_chain_iteration - (*handle)->cache_chain_heads
+           == (*handle)->cache_num_chains)
+               return NULL;
+
+       return (*handle)->cache_chain_iteration->name;
+}
+
+/* Get first rule in the given chain: NULL for empty chain. */
+const STRUCT_ENTRY *
+TC_FIRST_RULE(const char *chain, TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       c = find_label(chain, *handle);
+       if (!c) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       /* Empty chain: single return/policy rule */
+       if (c->start_off == c->end_off)
+               return NULL;
+
+       (*handle)->cache_rule_end = offset2entry(*handle, c->end_off);
+       return offset2entry(*handle, c->start_off);
+}
+
+/* Returns NULL when rules run out. */
+const STRUCT_ENTRY *
+TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle)
+{
+       if ((void *)prev + prev->next_offset
+           == (void *)(*handle)->cache_rule_end)
+               return NULL;
+
+       return (void *)prev + prev->next_offset;
+}
+
+#if 0
+/* How many rules in this chain? */
+unsigned int
+TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle)
+{
+       unsigned int off = 0;
+       STRUCT_ENTRY *start, *end;
+
+       CHECK(*handle);
+       if (!find_label(&off, chain, *handle)) {
+               errno = ENOENT;
+               return (unsigned int)-1;
+       }
+
+       start = get_entry(*handle, off);
+       end = get_entry(*handle, get_chain_end(*handle, off));
+
+       return entry2index(*handle, end) - entry2index(*handle, start);
+}
+
+/* Get n'th rule in this chain. */
+const STRUCT_ENTRY *TC_GET_RULE(const char *chain,
+                               unsigned int n,
+                               TC_HANDLE_T *handle)
+{
+       unsigned int pos = 0, chainindex;
+
+       CHECK(*handle);
+       if (!find_label(&pos, chain, *handle)) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = entry2index(*handle, get_entry(*handle, pos));
+
+       return index2entry(*handle, chainindex + n);
+}
+#endif
+
+static const char *
+target_name(TC_HANDLE_T handle, const STRUCT_ENTRY *ce)
+{
+       int spos;
+       unsigned int labelidx;
+       STRUCT_ENTRY *jumpto;
+
+       /* To avoid const warnings */
+       STRUCT_ENTRY *e = (STRUCT_ENTRY *)ce;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) != 0)
+               return GET_TARGET(e)->u.user.name;
+
+       /* Standard target: evaluate */
+       spos = *(int *)GET_TARGET(e)->data;
+       if (spos < 0) {
+               if (spos == RETURN)
+                       return LABEL_RETURN;
+               else if (spos == -NF_ACCEPT-1)
+                       return LABEL_ACCEPT;
+               else if (spos == -NF_DROP-1)
+                       return LABEL_DROP;
+               else if (spos == -NF_QUEUE-1)
+                       return LABEL_QUEUE;
+
+               fprintf(stderr, "ERROR: off %lu/%u not a valid target (%i)\n",
+                       entry2offset(handle, e), handle->entries.size,
+                       spos);
+               abort();
+       }
+
+       jumpto = get_entry(handle, spos);
+
+       /* Fall through rule */
+       if (jumpto == (void *)e + e->next_offset)
+               return "";
+
+       /* Must point to head of a chain: ie. after error rule */
+       labelidx = entry2index(handle, jumpto) - 1;
+       return get_errorlabel(handle, index2offset(handle, labelidx));
+}
+
+/* Returns a pointer to the target name of this position. */
+const char *TC_GET_TARGET(const STRUCT_ENTRY *e,
+                         TC_HANDLE_T *handle)
+{
+       return target_name(*handle, e);
+}
+
+/* Is this a built-in chain?  Actually returns hook + 1. */
+int
+TC_BUILTIN(const char *chain, const TC_HANDLE_T handle)
+{
+       unsigned int i;
+
+       for (i = 0; i < NUMHOOKS; i++) {
+               if ((handle->info.valid_hooks & (1 << i))
+                   && handle->hooknames[i]
+                   && strcmp(handle->hooknames[i], chain) == 0)
+                       return i+1;
+       }
+       return 0;
+}
+
+/* Get the policy of a given built-in chain */
+const char *
+TC_GET_POLICY(const char *chain,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int start;
+       STRUCT_ENTRY *e;
+       int hook;
+
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook != 0)
+               start = (*handle)->info.hook_entry[hook-1];
+       else
+               return NULL;
+
+       e = get_entry(*handle, get_chain_end(*handle, start));
+       *counters = e->counters;
+
+       return target_name(*handle, e);
+}
+
+static inline int
+correct_verdict(STRUCT_ENTRY *e,
+               char *base,
+               unsigned int offset, int delta_offset)
+{
+       STRUCT_STANDARD_TARGET *t = (void *)GET_TARGET(e);
+       unsigned int curr = (char *)e - base;
+
+       /* Trap: insert of fall-through rule.  Don't change fall-through
+          verdict to jump-over-next-rule. */
+       if (strcmp(t->target.u.user.name, STANDARD_TARGET) == 0
+           && t->verdict > (int)offset
+           && !(curr == offset &&
+                t->verdict == curr + e->next_offset)) {
+               t->verdict += delta_offset;
+       }
+
+       return 0;
+}
+
+/* Adjusts standard verdict jump positions after an insertion/deletion. */
+static int
+set_verdict(unsigned int offset, int delta_offset, TC_HANDLE_T *handle)
+{
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     correct_verdict, (char *)(*handle)->entries.entrytable,
+                     offset, delta_offset);
+
+       set_changed(*handle);
+       return 1;
+}
+
+/* If prepend is set, then we are prepending to a chain: if the
+ * insertion position is an entry point, keep the entry point. */
+static int
+insert_rules(unsigned int num_rules, unsigned int rules_size,
+            const STRUCT_ENTRY *insert,
+            unsigned int offset, unsigned int num_rules_offset,
+            int prepend,
+            TC_HANDLE_T *handle)
+{
+       TC_HANDLE_T newh;
+       STRUCT_GETINFO newinfo;
+       unsigned int i;
+
+       if (offset >= (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       newinfo = (*handle)->info;
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* Entry points to START of chain, so keep same if
+                   inserting on at that point. */
+               if ((*handle)->info.hook_entry[i] > offset)
+                       newinfo.hook_entry[i] += rules_size;
+
+               /* Underflow always points to END of chain (policy),
+                  so if something is inserted at same point, it
+                  should be advanced. */
+               if ((*handle)->info.underflow[i] >= offset)
+                       newinfo.underflow[i] += rules_size;
+       }
+
+       newh = alloc_handle((*handle)->info.name,
+                           (*handle)->entries.size + rules_size,
+                           (*handle)->new_number + num_rules);
+       if (!newh)
+               return 0;
+       newh->info = newinfo;
+
+       /* Copy pre... */
+       memcpy(newh->entries.entrytable, (*handle)->entries.entrytable,offset);
+       /* ... Insert new ... */
+       memcpy((char *)newh->entries.entrytable + offset, insert, rules_size);
+       /* ... copy post */
+       memcpy((char *)newh->entries.entrytable + offset + rules_size,
+              (char *)(*handle)->entries.entrytable + offset,
+              (*handle)->entries.size - offset);
+
+       /* Move counter map. */
+       /* Copy pre... */
+       memcpy(newh->counter_map, (*handle)->counter_map,
+              sizeof(struct counter_map) * num_rules_offset);
+       /* ... copy post */
+       memcpy(newh->counter_map + num_rules_offset + num_rules,
+              (*handle)->counter_map + num_rules_offset,
+              sizeof(struct counter_map) * ((*handle)->new_number
+                                            - num_rules_offset));
+       /* Set intermediates to no counter copy */
+       for (i = 0; i < num_rules; i++)
+               newh->counter_map[num_rules_offset+i]
+                       = ((struct counter_map){ COUNTER_MAP_SET, 0 });
+
+       newh->new_number = (*handle)->new_number + num_rules;
+       newh->entries.size = (*handle)->entries.size + rules_size;
+       newh->hooknames = (*handle)->hooknames;
+
+       newh->cache_chain_heads = (*handle)->cache_chain_heads;
+       newh->cache_num_builtins = (*handle)->cache_num_builtins;
+       newh->cache_num_chains = (*handle)->cache_num_chains;
+       newh->cache_rule_end = (*handle)->cache_rule_end;
+       newh->cache_chain_iteration = (*handle)->cache_chain_iteration;
+       if (!correct_cache(newh, offset, rules_size)) {
+               free(newh);
+               return 0;
+       }
+
+       free(*handle);
+       *handle = newh;
+
+       return set_verdict(offset, rules_size, handle);
+}
+
+static int
+delete_rules(unsigned int num_rules, unsigned int rules_size,
+            unsigned int offset, unsigned int num_rules_offset,
+            TC_HANDLE_T *handle)
+{
+       unsigned int i;
+
+       if (offset + rules_size > (*handle)->entries.size) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Fix up entry points. */
+       for (i = 0; i < NUMHOOKS; i++) {
+               /* In practice, we never delete up to a hook entry,
+                  since the built-in chains are always first,
+                  so these two are never equal */
+               if ((*handle)->info.hook_entry[i] >= offset + rules_size)
+                       (*handle)->info.hook_entry[i] -= rules_size;
+               else if ((*handle)->info.hook_entry[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting entry %u %u %u\n",
+                               i, (*handle)->info.hook_entry[i], offset);
+                       abort();
+               }
+
+               /* Underflow points to policy (terminal) rule in
+                   built-in, so sequality is valid here (when deleting
+                   the last rule). */
+               if ((*handle)->info.underflow[i] >= offset + rules_size)
+                       (*handle)->info.underflow[i] -= rules_size;
+               else if ((*handle)->info.underflow[i] > offset) {
+                       fprintf(stderr, "ERROR: Deleting uflow %u %u %u\n",
+                               i, (*handle)->info.underflow[i], offset);
+                       abort();
+               }
+       }
+
+       /* Move the rules down. */
+       memmove((char *)(*handle)->entries.entrytable + offset,
+               (char *)(*handle)->entries.entrytable + offset + rules_size,
+               (*handle)->entries.size - (offset + rules_size));
+
+       /* Move the counter map down. */
+       memmove(&(*handle)->counter_map[num_rules_offset],
+               &(*handle)->counter_map[num_rules_offset + num_rules],
+               sizeof(struct counter_map)
+               * ((*handle)->new_number - (num_rules + num_rules_offset)));
+
+       /* Fix numbers */
+       (*handle)->new_number -= num_rules;
+       (*handle)->entries.size -= rules_size;
+
+       /* Fix the chain cache */
+       if (!correct_cache(*handle, offset+rules_size, -(int)rules_size))
+               return 0;
+
+       return set_verdict(offset, -(int)rules_size, handle);
+}
+
+static int
+standard_map(STRUCT_ENTRY *e, int verdict)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (t->target.u.target_size
+           != ALIGN(sizeof(STRUCT_STANDARD_TARGET))) {
+               errno = EINVAL;
+               return 0;
+       }
+       /* memset for memcmp convenience on delete/replace */
+       memset(t->target.u.user.name, 0, FUNCTION_MAXNAMELEN);
+       strcpy(t->target.u.user.name, STANDARD_TARGET);
+       t->verdict = verdict;
+
+       return 1;
+}
+
+static int
+map_target(const TC_HANDLE_T handle,
+          STRUCT_ENTRY *e,
+          unsigned int offset,
+          STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *old = *t;
+
+       /* Maybe it's empty (=> fall through) */
+       if (strcmp(t->u.user.name, "") == 0)
+               return standard_map(e, offset + e->next_offset);
+       /* Maybe it's a standard target name... */
+       else if (strcmp(t->u.user.name, LABEL_ACCEPT) == 0)
+               return standard_map(e, -NF_ACCEPT - 1);
+       else if (strcmp(t->u.user.name, LABEL_DROP) == 0)
+               return standard_map(e, -NF_DROP - 1);
+       else if (strcmp(t->u.user.name, LABEL_QUEUE) == 0)
+               return standard_map(e, -NF_QUEUE - 1);
+       else if (strcmp(t->u.user.name, LABEL_RETURN) == 0)
+               return standard_map(e, RETURN);
+       else if (TC_BUILTIN(t->u.user.name, handle)) {
+               /* Can't jump to builtins. */
+               errno = EINVAL;
+               return 0;
+       } else {
+               /* Maybe it's an existing chain name. */
+               struct chain_cache *c;
+
+               c = find_label(t->u.user.name, handle);
+               if (c)
+                       return standard_map(e, c->start_off);
+       }
+
+       /* Must be a module?  If not, kernel will reject... */
+       /* memset to all 0 for your memcmp convenience. */
+       memset(t->u.user.name + strlen(t->u.user.name),
+              0,
+              FUNCTION_MAXNAMELEN - strlen(t->u.user.name));
+       return 1;
+}
+
+static void
+unmap_target(STRUCT_ENTRY *e, STRUCT_ENTRY_TARGET *old)
+{
+       STRUCT_ENTRY_TARGET *t = GET_TARGET(e);
+
+       /* Save old target (except data, which we don't change, except for
+          standard case, where we don't care). */
+       *t = *old;
+}
+
+/* Insert the entry `fw' in chain `chain' into position `rulenum'. */
+int
+TC_INSERT_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_INSERT_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp > offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+       offset = index2offset(*handle, chainindex + rulenum);
+
+       /* Mapping target actually alters entry, but that's
+           transparent to the caller. */
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, rulenum == 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Atomically replace rule `rulenum' in `chain' with `fw'. */
+int
+TC_REPLACE_ENTRY(const IPT_CHAINLABEL chain,
+                const STRUCT_ENTRY *e,
+                unsigned int rulenum,
+                TC_HANDLE_T *handle)
+{
+       unsigned int chainindex, offset;
+       STRUCT_ENTRY_TARGET old;
+       struct chain_cache *c;
+       STRUCT_ENTRY *tmp;
+       int ret;
+
+       iptc_fn = TC_REPLACE_ENTRY;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+
+       tmp = index2entry(*handle, chainindex + rulenum);
+       if (!tmp || tmp >= offset2entry(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       offset = index2offset(*handle, chainindex + rulenum);
+       /* Replace = delete and insert. */
+       if (!delete_rules(1, get_entry(*handle, offset)->next_offset,
+                         offset, chainindex + rulenum, handle))
+               return 0;
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e, offset, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, offset,
+                          chainindex + rulenum, 1, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+/* Append entry `fw' to chain `chain'.  Equivalent to insert with
+   rulenum = length of chain. */
+int
+TC_APPEND_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *e,
+               TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+       STRUCT_ENTRY_TARGET old;
+       int ret;
+
+       iptc_fn = TC_APPEND_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (!map_target(*handle, (STRUCT_ENTRY *)e,
+                       c->end_off, &old))
+               return 0;
+
+       ret = insert_rules(1, e->next_offset, e, c->end_off, 
+                          offset2index(*handle, c->end_off), 0, handle);
+       unmap_target((STRUCT_ENTRY *)e, &old);
+       return ret;
+}
+
+static inline int
+match_different(const STRUCT_ENTRY_MATCH *a,
+               const unsigned char *a_elems,
+               const unsigned char *b_elems,
+               unsigned char **maskptr)
+{
+       const STRUCT_ENTRY_MATCH *b;
+       unsigned int i;
+
+       /* Offset of b is the same as a. */
+       b = (void *)b_elems + ((unsigned char *)a - a_elems);
+
+       if (a->u.match_size != b->u.match_size)
+               return 1;
+
+       if (strcmp(a->u.user.name, b->u.user.name) != 0)
+               return 1;
+
+       *maskptr += ALIGN(sizeof(*a));
+
+       for (i = 0; i < a->u.match_size - ALIGN(sizeof(*a)); i++)
+               if (((a->data[i] ^ b->data[i]) & (*maskptr)[i]) != 0)
+                       return 1;
+       *maskptr += i;
+       return 0;
+}
+
+static inline int
+target_different(const unsigned char *a_targdata,
+                const unsigned char *b_targdata,
+                unsigned int tdatasize,
+                const unsigned char *mask)
+{
+       unsigned int i;
+       for (i = 0; i < tdatasize; i++)
+               if (((a_targdata[i] ^ b_targdata[i]) & mask[i]) != 0)
+                       return 1;
+
+       return 0;
+}
+
+static int
+is_same(const STRUCT_ENTRY *a,
+       const STRUCT_ENTRY *b,
+       unsigned char *matchmask);
+
+/* Delete the first rule in `chain' which matches `fw'. */
+int
+TC_DELETE_ENTRY(const IPT_CHAINLABEL chain,
+               const STRUCT_ENTRY *origfw,
+               unsigned char *matchmask,
+               TC_HANDLE_T *handle)
+{
+       unsigned int offset;
+       struct chain_cache *c;
+       STRUCT_ENTRY *e, *fw;
+
+       iptc_fn = TC_DELETE_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       fw = malloc(origfw->next_offset);
+       if (fw == NULL) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       for (offset = c->start_off; offset < c->end_off;
+            offset += e->next_offset) {
+               STRUCT_ENTRY_TARGET discard;
+
+               memcpy(fw, origfw, origfw->next_offset);
+
+               /* FIXME: handle this in is_same --RR */
+               if (!map_target(*handle, fw, offset, &discard)) {
+                       free(fw);
+                       return 0;
+               }
+               e = get_entry(*handle, offset);
+
+#if 0
+               printf("Deleting:\n");
+               dump_entry(newe);
+#endif
+               if (is_same(e, fw, matchmask)) {
+                       int ret;
+                       ret = delete_rules(1, e->next_offset,
+                                          offset, entry2index(*handle, e),
+                                          handle);
+                       free(fw);
+                       return ret;
+               }
+       }
+
+       free(fw);
+       errno = ENOENT;
+       return 0;
+}
+
+/* Delete the rule in position `rulenum' in `chain'. */
+int
+TC_DELETE_NUM_ENTRY(const IPT_CHAINLABEL chain,
+                   unsigned int rulenum,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int index;
+       int ret;
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+
+       iptc_fn = TC_DELETE_NUM_ENTRY;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       index = offset2index(*handle, c->start_off) + rulenum;
+
+       if (index >= offset2index(*handle, c->end_off)) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, index);
+       if (e == NULL) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ret = delete_rules(1, e->next_offset, entry2offset(*handle, e),
+                          index, handle);
+       return ret;
+}
+
+/* Check the packet `fw' on chain `chain'.  Returns the verdict, or
+   NULL and sets errno. */
+const char *
+TC_CHECK_PACKET(const IPT_CHAINLABEL chain,
+               STRUCT_ENTRY *entry,
+               TC_HANDLE_T *handle)
+{
+       errno = ENOSYS;
+       return NULL;
+}
+
+/* Flushes the entries in the given chain (ie. empties chain). */
+int
+TC_FLUSH_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int startindex, endindex;
+       STRUCT_ENTRY *startentry, *endentry;
+       struct chain_cache *c;
+       int ret;
+
+       iptc_fn = TC_FLUSH_ENTRIES;
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+       startindex = offset2index(*handle, c->start_off);
+       endindex = offset2index(*handle, c->end_off);
+       startentry = offset2entry(*handle, c->start_off);
+       endentry = offset2entry(*handle, c->end_off);
+
+       ret = delete_rules(endindex - startindex,
+                          (char *)endentry - (char *)startentry,
+                          c->start_off, startindex,
+                          handle);
+       return ret;
+}
+
+/* Zeroes the counters in a chain. */
+int
+TC_ZERO_ENTRIES(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int i, end;
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       i = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       for (; i <= end; i++) {
+               if ((*handle)->counter_map[i].maptype ==COUNTER_MAP_NORMAL_MAP)
+                       (*handle)->counter_map[i].maptype = COUNTER_MAP_ZEROED;
+       }
+       set_changed(*handle);
+
+       return 1;
+}
+
+STRUCT_COUNTERS *
+TC_READ_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_READ_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return NULL;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return NULL;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       return &e->counters;
+}
+
+int
+TC_ZERO_COUNTER(const IPT_CHAINLABEL chain,
+               unsigned int rulenum,
+               TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+       
+       iptc_fn = TC_ZERO_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       if ((*handle)->counter_map[chainindex + rulenum].maptype
+                       == COUNTER_MAP_NORMAL_MAP) {
+               (*handle)->counter_map[chainindex + rulenum].maptype
+                        = COUNTER_MAP_ZEROED;
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+int 
+TC_SET_COUNTER(const IPT_CHAINLABEL chain,
+              unsigned int rulenum,
+              STRUCT_COUNTERS *counters,
+              TC_HANDLE_T *handle)
+{
+       STRUCT_ENTRY *e;
+       struct chain_cache *c;
+       unsigned int chainindex, end;
+
+       iptc_fn = TC_SET_COUNTER;
+       CHECK(*handle);
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       chainindex = offset2index(*handle, c->start_off);
+       end = offset2index(*handle, c->end_off);
+
+       if (chainindex + rulenum > end) {
+               errno = E2BIG;
+               return 0;
+       }
+
+       e = index2entry(*handle, chainindex + rulenum);
+
+       (*handle)->counter_map[chainindex + rulenum].maptype
+               = COUNTER_MAP_SET;
+
+       memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Creates a new chain. */
+/* To create a chain, create two rules: error node and unconditional
+ * return. */
+int
+TC_CREATE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       int ret;
+       struct {
+               STRUCT_ENTRY head;
+               struct ipt_error_target name;
+               STRUCT_ENTRY ret;
+               STRUCT_STANDARD_TARGET target;
+       } newc;
+       unsigned int destination;
+
+       iptc_fn = TC_CREATE_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(chain, *handle)
+           || strcmp(chain, LABEL_DROP) == 0
+           || strcmp(chain, LABEL_ACCEPT) == 0
+           || strcmp(chain, LABEL_QUEUE) == 0
+           || strcmp(chain, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (strlen(chain)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       memset(&newc, 0, sizeof(newc));
+       newc.head.target_offset = sizeof(STRUCT_ENTRY);
+       newc.head.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.t.u.user.name, ERROR_TARGET);
+       newc.name.t.u.target_size = ALIGN(sizeof(struct ipt_error_target));
+       strcpy(newc.name.error, chain);
+
+       newc.ret.target_offset = sizeof(STRUCT_ENTRY);
+       newc.ret.next_offset
+               = sizeof(STRUCT_ENTRY)
+               + ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       strcpy(newc.target.target.u.user.name, STANDARD_TARGET);
+       newc.target.target.u.target_size
+               = ALIGN(sizeof(STRUCT_STANDARD_TARGET));
+       newc.target.verdict = RETURN;
+
+       destination = index2offset(*handle, (*handle)->new_number -1);
+
+       /* Add just before terminal entry */
+       ret = insert_rules(2, sizeof(newc), &newc.head,
+                          destination,
+                          (*handle)->new_number - 1,
+                          0, handle);
+
+       set_changed(*handle);
+
+       /* add chain cache info for this chain */
+       add_chain_cache(*handle, chain, 
+                       destination+newc.head.next_offset, 
+                       destination+newc.head.next_offset);
+
+       return ret;
+}
+
+static int
+count_ref(STRUCT_ENTRY *e, unsigned int offset, unsigned int *ref)
+{
+       STRUCT_STANDARD_TARGET *t;
+
+       if (strcmp(GET_TARGET(e)->u.user.name, STANDARD_TARGET) == 0) {
+               t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+               if (t->verdict == offset)
+                       (*ref)++;
+       }
+
+       return 0;
+}
+
+/* Get the number of references to this chain. */
+int
+TC_GET_REFERENCES(unsigned int *ref, const IPT_CHAINLABEL chain,
+                 TC_HANDLE_T *handle)
+{
+       struct chain_cache *c;
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       *ref = 0;
+       ENTRY_ITERATE((*handle)->entries.entrytable,
+                     (*handle)->entries.size,
+                     count_ref, c->start_off, ref);
+       return 1;
+}
+
+/* Deletes a chain. */
+int
+TC_DELETE_CHAIN(const IPT_CHAINLABEL chain, TC_HANDLE_T *handle)
+{
+       unsigned int labelidx, labeloff;
+       unsigned int references;
+       struct chain_cache *c;
+       int ret;
+       STRUCT_ENTRY *start;
+
+       if (!TC_GET_REFERENCES(&references, chain, handle))
+               return 0;
+
+       iptc_fn = TC_DELETE_CHAIN;
+
+       if (TC_BUILTIN(chain, *handle)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       if (references > 0) {
+               errno = EMLINK;
+               return 0;
+       }
+
+       if (!(c = find_label(chain, *handle))) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (c->start_off != c->end_off) {
+               errno = ENOTEMPTY;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       start = offset2entry(*handle, c->start_off);
+
+       ret = delete_rules(2,
+                          get_entry(*handle, labeloff)->next_offset
+                          + start->next_offset,
+                          labeloff, labelidx, handle);
+       return ret;
+}
+
+/* Renames a chain. */
+int TC_RENAME_CHAIN(const IPT_CHAINLABEL oldname,
+                   const IPT_CHAINLABEL newname,
+                   TC_HANDLE_T *handle)
+{
+       unsigned int labeloff, labelidx;
+       struct chain_cache *c;
+       struct ipt_error_target *t;
+
+       iptc_fn = TC_RENAME_CHAIN;
+
+       /* find_label doesn't cover built-in targets: DROP, ACCEPT,
+           QUEUE, RETURN. */
+       if (find_label(newname, *handle)
+           || strcmp(newname, LABEL_DROP) == 0
+           || strcmp(newname, LABEL_ACCEPT) == 0
+           || strcmp(newname, LABEL_QUEUE) == 0
+           || strcmp(newname, LABEL_RETURN) == 0) {
+               errno = EEXIST;
+               return 0;
+       }
+
+       if (!(c = find_label(oldname, *handle))
+           || TC_BUILTIN(oldname, *handle)) {
+               errno = ENOENT;
+               return 0;
+       }
+
+       if (strlen(newname)+1 > sizeof(IPT_CHAINLABEL)) {
+               errno = EINVAL;
+               return 0;
+       }
+
+       /* Need label index: preceeds chain start */
+       labelidx = offset2index(*handle, c->start_off) - 1;
+       labeloff = index2offset(*handle, labelidx);
+
+       t = (struct ipt_error_target *)
+               GET_TARGET(get_entry(*handle, labeloff));
+
+       memset(t->error, 0, sizeof(t->error));
+       strcpy(t->error, newname);
+
+       /* update chain cache */
+       memset(c->name, 0, sizeof(c->name));
+       strcpy(c->name, newname);
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Sets the policy on a built-in chain. */
+int
+TC_SET_POLICY(const IPT_CHAINLABEL chain,
+             const IPT_CHAINLABEL policy,
+             STRUCT_COUNTERS *counters,
+             TC_HANDLE_T *handle)
+{
+       unsigned int hook;
+       unsigned int policyoff, ctrindex;
+       STRUCT_ENTRY *e;
+       STRUCT_STANDARD_TARGET *t;
+
+       iptc_fn = TC_SET_POLICY;
+       /* Figure out which chain. */
+       hook = TC_BUILTIN(chain, *handle);
+       if (hook == 0) {
+               errno = ENOENT;
+               return 0;
+       } else
+               hook--;
+
+       policyoff = get_chain_end(*handle, (*handle)->info.hook_entry[hook]);
+       if (policyoff != (*handle)->info.underflow[hook]) {
+               printf("ERROR: Policy for `%s' offset %u != underflow %u\n",
+                      chain, policyoff, (*handle)->info.underflow[hook]);
+               return 0;
+       }
+
+       e = get_entry(*handle, policyoff);
+       t = (STRUCT_STANDARD_TARGET *)GET_TARGET(e);
+
+       if (strcmp(policy, LABEL_ACCEPT) == 0)
+               t->verdict = -NF_ACCEPT - 1;
+       else if (strcmp(policy, LABEL_DROP) == 0)
+               t->verdict = -NF_DROP - 1;
+       else {
+               errno = EINVAL;
+               return 0;
+       }
+
+       ctrindex = entry2index(*handle, e);
+
+       if (counters) {
+               /* set byte and packet counters */
+               memcpy(&e->counters, counters, sizeof(STRUCT_COUNTERS));
+
+               (*handle)->counter_map[ctrindex].maptype
+                       = COUNTER_MAP_SET;
+
+       } else {
+               (*handle)->counter_map[ctrindex]
+                       = ((struct counter_map){ COUNTER_MAP_NOMAP, 0 });
+       }
+
+       set_changed(*handle);
+
+       return 1;
+}
+
+/* Without this, on gcc 2.7.2.3, we get:
+   libiptc.c: In function `TC_COMMIT':
+   libiptc.c:833: fixed or forbidden register was spilled.
+   This may be due to a compiler bug or to impossible asm
+   statements or clauses.
+*/
+static void
+subtract_counters(STRUCT_COUNTERS *answer,
+                 const STRUCT_COUNTERS *a,
+                 const STRUCT_COUNTERS *b)
+{
+       answer->pcnt = a->pcnt - b->pcnt;
+       answer->bcnt = a->bcnt - b->bcnt;
+}
+
+int
+TC_COMMIT(TC_HANDLE_T *handle)
+{
+       /* Replace, then map back the counters. */
+       STRUCT_REPLACE *repl;
+       STRUCT_COUNTERS_INFO *newcounters;
+       unsigned int i;
+       size_t counterlen;
+
+       CHECK(*handle);
+
+       counterlen = sizeof(STRUCT_COUNTERS_INFO)
+                       + sizeof(STRUCT_COUNTERS) * (*handle)->new_number;
+
+#if 0
+       TC_DUMP_ENTRIES(*handle);
+#endif
+
+       /* Don't commit if nothing changed. */
+       if (!(*handle)->changed)
+               goto finished;
+
+       repl = malloc(sizeof(*repl) + (*handle)->entries.size);
+       if (!repl) {
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the old counters we will get from kernel */
+       repl->counters = malloc(sizeof(STRUCT_COUNTERS)
+                               * (*handle)->info.num_entries);
+       if (!repl->counters) {
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       /* These are the counters we're going to put back, later. */
+       newcounters = malloc(counterlen);
+       if (!newcounters) {
+               free(repl->counters);
+               free(repl);
+               errno = ENOMEM;
+               return 0;
+       }
+
+       strcpy(repl->name, (*handle)->info.name);
+       repl->num_entries = (*handle)->new_number;
+       repl->size = (*handle)->entries.size;
+       memcpy(repl->hook_entry, (*handle)->info.hook_entry,
+              sizeof(repl->hook_entry));
+       memcpy(repl->underflow, (*handle)->info.underflow,
+              sizeof(repl->underflow));
+       repl->num_counters = (*handle)->info.num_entries;
+       repl->valid_hooks = (*handle)->info.valid_hooks;
+       memcpy(repl->entries, (*handle)->entries.entrytable,
+              (*handle)->entries.size);
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_REPLACE, repl,
+                      sizeof(*repl) + (*handle)->entries.size) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       /* Put counters back. */
+       strcpy(newcounters->name, (*handle)->info.name);
+       newcounters->num_counters = (*handle)->new_number;
+       for (i = 0; i < (*handle)->new_number; i++) {
+               unsigned int mappos = (*handle)->counter_map[i].mappos;
+               switch ((*handle)->counter_map[i].maptype) {
+               case COUNTER_MAP_NOMAP:
+                       newcounters->counters[i]
+                               = ((STRUCT_COUNTERS){ 0, 0 });
+                       break;
+
+               case COUNTER_MAP_NORMAL_MAP:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: X + Y + Z.
+                        * => Add in X + Y
+                        * => Add in replacement read.
+                        */
+                       newcounters->counters[i] = repl->counters[mappos];
+                       break;
+
+               case COUNTER_MAP_ZEROED:
+                       /* Original read: X.
+                        * Atomic read on replacement: X + Y.
+                        * Currently in kernel: Z.
+                        * Want in kernel: Y + Z.
+                        * => Add in Y.
+                        * => Add in (replacement read - original read).
+                        */
+                       subtract_counters(&newcounters->counters[i],
+                                         &repl->counters[mappos],
+                                         &index2entry(*handle, i)->counters);
+                       break;
+
+               case COUNTER_MAP_SET:
+                       /* Want to set counter (iptables-restore) */
+
+                       memcpy(&newcounters->counters[i],
+                              &index2entry(*handle, i)->counters,
+                              sizeof(STRUCT_COUNTERS));
+
+                       break;
+               }
+       }
+
+#ifdef KERNEL_64_USERSPACE_32
+       {
+               /* Kernel will think that pointer should be 64-bits, and get
+                  padding.  So we accomodate here (assumption: alignment of
+                  `counters' is on 64-bit boundary). */
+               u_int64_t *kernptr = (u_int64_t *)&newcounters->counters;
+               if ((unsigned long)&newcounters->counters % 8 != 0) {
+                       fprintf(stderr,
+                               "counters alignment incorrect! Mail rusty!\n");
+                       abort();
+               }
+               *kernptr = newcounters->counters;
+       }
+#endif /* KERNEL_64_USERSPACE_32 */
+
+       if (setsockopt(sockfd, TC_IPPROTO, SO_SET_ADD_COUNTERS,
+                      newcounters, counterlen) < 0) {
+               free(repl->counters);
+               free(repl);
+               free(newcounters);
+               return 0;
+       }
+
+       free(repl->counters);
+       free(repl);
+       free(newcounters);
+
+ finished:
+       TC_FREE(handle);
+       return 1;
+}
+
+/* Get raw socket. */
+int
+TC_GET_RAW_SOCKET()
+{
+       return sockfd;
+}
+
+/* Translates errno numbers into more human-readable form than strerror. */
+const char *
+TC_STRERROR(int err)
+{
+       unsigned int i;
+       struct table_struct {
+               void *fn;
+               int err;
+               const char *message;
+       } table [] =
+         { { TC_INIT, EPERM, "Permission denied (you must be root)" },
+           { TC_INIT, EINVAL, "Module is wrong version" },
+           { TC_INIT, ENOENT, 
+                   "Table does not exist (do you need to insmod?)" },
+           { TC_DELETE_CHAIN, ENOTEMPTY, "Chain is not empty" },
+           { TC_DELETE_CHAIN, EINVAL, "Can't delete built-in chain" },
+           { TC_DELETE_CHAIN, EMLINK,
+             "Can't delete chain with references left" },
+           { TC_CREATE_CHAIN, EEXIST, "Chain already exists" },
+           { TC_INSERT_ENTRY, E2BIG, "Index of insertion too big" },
+           { TC_REPLACE_ENTRY, E2BIG, "Index of replacement too big" },
+           { TC_DELETE_NUM_ENTRY, E2BIG, "Index of deletion too big" },
+           { TC_READ_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_ZERO_COUNTER, E2BIG, "Index of counter too big" },
+           { TC_INSERT_ENTRY, ELOOP, "Loop found in table" },
+           { TC_INSERT_ENTRY, EINVAL, "Target problem" },
+           /* EINVAL for CHECK probably means bad interface. */
+           { TC_CHECK_PACKET, EINVAL,
+             "Bad arguments (does that interface exist?)" },
+           { TC_CHECK_PACKET, ENOSYS,
+             "Checking will most likely never get implemented" },
+           /* ENOENT for DELETE probably means no matching rule */
+           { TC_DELETE_ENTRY, ENOENT,
+             "Bad rule (does a matching rule exist in that chain?)" },
+           { TC_SET_POLICY, ENOENT,
+             "Bad built-in chain name" },
+           { TC_SET_POLICY, EINVAL,
+             "Bad policy name" },
+
+           { NULL, 0, "Incompatible with this kernel" },
+           { NULL, ENOPROTOOPT, "iptables who? (do you need to insmod?)" },
+           { NULL, ENOSYS, "Will be implemented real soon.  I promise ;)" },
+           { NULL, ENOMEM, "Memory allocation problem" },
+           { NULL, ENOENT, "No chain/target/match by that name" },
+         };
+
+       for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+               if ((!table[i].fn || table[i].fn == iptc_fn)
+                   && table[i].err == err)
+                       return table[i].message;
+       }
+
+       return strerror(err);
+}