Project import generated by Copybara.

GitOrigin-RevId: 0eb304f92590ea910048317acd02cc81c7b9104b
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..19ee962
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+*.a
+*.la
+*.lo
+*.so
+*.o
+.deps/
+.dirstamp
+.libs/
+Makefile
+Makefile.in
+
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
+/libtool
+/stamp-h1
+
+/ebtables-legacy
+/ebtables-legacy-restore
+/ebtables-legacy-save
+/ebtables-legacy.8
+/ebtablesd
+/ebtablesu
+/static
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..4ad2d49
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,62 @@
+LOCAL_PATH:= $(call my-dir)
+
+cflags := -O2 -g \
+	-DPROGNAME=\"ebtables\" \
+	-DPROGVERSION=\"2.0.11\" \
+	-DPROGDATE=\"December\ 2011\" \
+	-DLOCKFILE=\"/var/lib/ebtables/lock\" \
+	-Wno-sign-compare -Wno-missing-field-initializers \
+	-Wno-ignored-qualifiers -Wno-unused-parameter \
+	-Wno-#pragma-messages
+
+extensions_src_files := \
+	extensions/ebt_802_3.c \
+	extensions/ebt_AUDIT.c \
+	extensions/ebt_among.c \
+	extensions/ebt_arp.c \
+	extensions/ebt_arpreply.c \
+	extensions/ebt_ip.c \
+	extensions/ebt_ip6.c \
+	extensions/ebt_limit.c \
+	extensions/ebt_log.c \
+	extensions/ebt_mark.c \
+	extensions/ebt_mark_m.c \
+	extensions/ebt_nat.c \
+	extensions/ebt_nflog.c \
+	extensions/ebt_pkttype.c \
+	extensions/ebt_redirect.c \
+	extensions/ebt_standard.c \
+	extensions/ebt_stp.c \
+	extensions/ebt_string.c \
+	extensions/ebt_ulog.c \
+	extensions/ebt_vlan.c \
+	extensions/ebtable_broute.c \
+	extensions/ebtable_filter.c \
+	extensions/ebtable_nat.c \
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+	$(extensions_src_files) \
+	communication.c \
+	ebtables-standalone.c \
+	ebtables.c \
+	getethertype.c \
+	libebtc.c \
+	useful_functions.c \
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_CFLAGS := $(cflags)
+LOCAL_MODULE := ebtables
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ethertypes
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_SRC_FILES := ethertypes
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+include $(BUILD_PREBUILT)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..514754e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,342 @@
+All code in this package, including the code from the extensions,
+is released under the GPL license, which you find hereafter.
+
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..52de4db
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,209 @@
+20111215
+	Changelog for v2.0.10-4
+	* really fix counter setting bug (thanks to James' persistence)
+20111204
+	Changelog for v2.0.10-3
+	* fix counter setting bug (reported by James Sinclair)
+20110710
+	Changelog for v2.0.10-2
+	* enable compiler optimizations (-O3)
+	* small changes to remove the compiler warnings due to optimization being
+	  turned on (thanks to Peter Volkov)
+	* respect LDFLAGS in Makefiles (Peter Volkov)
+20110710
+	Changelog for v2.0.10-1
+	* fix --among-dst-file, which translated to --among-src
+	  (reported by Thierry Watelet)
+	* fix bug in test_ulog.c example
+	* Makefile: respect LDFLAGS during ebtables build (Peter Volkov)
+	* Makefile: create directories to avoid build failure when DESTDIR is
+	  supplied (Peter Volkov)
+	* incorporate fixes for possible issues found by Coverity analysis
+	  (thanks to Jiri Popelka)
+	* define __EXPORTED_HEADERS__ to get access to the Linux kernel headers
+	* extend ebt_ip6 to allow matching on ipv6-icmp types/codes (by Florian
+	  Westphal)
+	* Print a more useful error message when an update of the kernel table
+	  failed.
+	* Add --concurrent option, which enables using a file lock to support
+	  concurrent scripts updating the ebtables kernel tables
+20100203
+	Changelog for v2.0.9-2
+	* fix unwanted zeroing of counters in the last user-defined chain
+	  (reported by Jon Lewis)
+	* fix hidden symbol compilation error when using ld directly
+	* fix return value checking of creat to give a correct error
+	  message if the atomic file couldn't be created
+	* correct info in INSTALL about compilation of ulog
+20090621
+	Changelog for v2.0.9 vs v2.0.8-2
+	* added ip6 module for filtering IPv6 traffic (Kuo-Lang Tseng,
+	  Manohar Castelino)
+	* added --log-ip6 option for logging IPv6 traffic (Kuo-Lang Tseng,
+	  Manohar Castelino)
+	* added nflog watcher for logging packets to userspace (Peter Warasin)
+	* bugfix in ebtables.sysv (Michal Soltys)
+	* bugfix for among match on x86-64 (reported by Pavel Emelyanov)
+20061217
+	Since last entry:
+	* fixed a few reported bugs
+	* ebt_among --among-dst-file and --among-src-file: allow
+	  the list to be given in a file (circumvents command line max.
+	  line length
+	* ebt_nat --snat-arp: if it's an arp packet, also change the source
+	  address in the arp header
+	* ebt_mark --mark-or, --mark-xor, --mark-and
+20051020
+	Since last entry:
+	* ebtables modules are now located in /usr/lib/ebtables/
+	* added '/sbin/service ebtables' support
+	* added ebtables-save (thanks to Rok Papez <rok.papez@arnes.si>)
+	  and ebtables-restore (the first one a perl script, the second
+	  one written in c (fast))
+	* optimized the code for the '-A' command, making ebtables-restore
+	  very fast.
+	* ebtablesd/ebtablesu is deprecated and not compiled by default
+	  the ebtables-save/ebtables-restore scheme is much better
+20050117
+	Since last entry:
+	* added ulog watcher
+	* made the ebtables code modular (make library functions).
+	* added the ebtablesd/ebtablesu scheme to allow faster
+	  addition of rules (and to test the modular code).
+	* some small fixes
+	* added -c option (initialize counters)
+	* added -C option (change counters)
+20031102
+	Since last entry:
+	* <grzes_at_gnu.univ.gda.pl> added arpreply and among modules
+	* <tommy_at_home.tig-grr.com> added limit match
+20030724
+	* added (automatic) Sparc64 support, thanks to Michael Bellion and
+	  Thomas Heinz from hipac.org for providing a test-box.
+20030717
+	* added stp frames match type
+20030713
+	* added support for deleting all user-defined chains (-X option
+	  without specified chain)
+20030601
+	* added --Lmac2
+	* <csv_at_bluetail.com> Chris Vitale: basic 802.3/802.2 filtering
+	  (experimental, kernel files are in the CVS)
+
+20030503
+	* added negative rule counter support
+	* bugfix: bcnt was not updated correctly
+	* <blancher_at_cartel-securite.fr> Cedric Blancher: add ARP MAC
+	  matching support
+	* added pkttype match
+20030402
+	* fixed check bug in ebt_ip.c (report from
+	  joe_judge_at_guardium.com).
+20030111
+	* fixed problem when removing a chain (report from
+	  ykphuah_at_greenpacket.com).
+	* Added --help list_extensions which, well, lists the extensions
+20021203
+	* changed the way to use the atomic operations. It's now possible
+	  to use the EBTABLES_ATOMIC_FILE environment variable, so it's no
+	  longer necessary to explicitly state the file name. See the man.
+20021120
+	* changed the way of compiling. New releases will now contain their
+	  own set of kernel includes. No more copying of kernel includes to
+	  /usr/include/linux
+	* added getethertype.c (Nick) and use it. Removed name_to_number()
+	  and number_to_name().
+20021106
+	* added possibility to specify a rule number interval when deleting
+	  rules
+20021102
+	* added ! - option possibility, which is equivalent to - ! option
+20021102
+	* since last entry: added byte counters and udp/tcp port matching
+20020830
+	* updated the kernel files for 2.4.20-pre5 and 2.5.32
+	* last big cleanup of kernel and userspace code just finished
+20020820
+	* ARP module bugfix
+	* IP module bugfix
+	* nat module bugfix
+20020730
+	* other things done before 2.0-rc1 that I can think of,
+	  including kernel:
+	* cache align counters for better smp performance
+	* simplify snat code
+	* check for --xxxx-target RETURN on base chain
+	* cleanup code
+	* minor bugfixes
+20020724
+	* code cleanup
+	* bugfix for --atomic-commit
+20020720
+	* added mark target+match
+20020714
+	* added --atomic options
+20020710
+	* some unlogged changes (due to lazyness)
+	* added --Lc, --Ln, --Lx
+20020625
+	* user defined chains support: added -N, -X, -E options.
+20020621
+	* some unlogged changes (due to lazyness)
+	* change the output for -L to make it look like it would look when
+	  the user inputs the command.
+	* try to autoload modules
+	* some minor bugfixes
+	* add user defined chains support (without new commands yet,
+	  deliberately)
+	* comparing rules didn't take the logical devices into account
+20020520
+	* update help for -s and -d
+	* add VLAN in ethertypes
+	* add SYMLINK option for compiling
+20020501
+	* allow -i and --logical-in in BROUTING
+	* update the manual page
+	* rename /etc/etherproto into /etc/ethertypes (seems to be a more
+	  standard name)
+	* add MAC mask for -s and -d, also added Unicast, Multicast and
+	  Broadcast specification for specifying a (family of) MAC
+	  addresses.
+20020427
+	* added broute table.
+	* added redirect target.
+	* added --redirect-target, --snat-target and --dnat-target options.
+	* added logical_out and logical_in
+	* snat bugfix (->size)
+20020414
+	* fixed some things in the manual.
+	* fixed -P problem.
+20020411
+	* -j standard no longer works, is this cryptic? good :)
+	* lots of beautification.
+	  - made some code smaller
+	  - made everything fit within 80 columns
+	* fix problems with -i and -o option
+	* print_memory now prints useful info
+	* trying to see the tables when ebtables is not loaded in kernel
+	  no longer makes this be seen as a bug.
+20020403
+	ebtables v2.0 released, changes:
+	* A complete rewrite, made everything modular.
+	* Fixed a one year old bug in br_db.c. A similar bug was present
+	  in ebtables.c. It was visible when the number of rules got
+	  bigger (around 90).
+	* Removed the option to allow/disallow counters. Frames passing
+	  by are always counted now.
+	* Didn't really add any new functionality. However, it will be
+	  _alot_ easier and prettier to do so now. Feel free to add an
+	  extension yourself.
+	* There are 4 types of extensions:
+	  - Tables.
+	  - Matches: like iptables has.
+	  - Watchers: these only watch frames that passed all the matches
+	    of the rule. They don't change the frame, nor give a verdict.
+	    The log extension is a watcher.
+	  - Targets.
+	* user32/kernel64 architectures like the Sparc64 are unsupported.
+	  If you want me to change this, give me access to such a box,
+	  and don't pressure me.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..c43d95c
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,36 @@
+Installation instructions for iptables
+======================================
+
+ebtables uses the well-known configure(autotools) infrastructure.
+
+	$ ./configure
+	$ make
+	# make install
+
+
+Prerequisites
+=============
+
+	* no kernel-source required
+
+	* but obviously a compiler, glibc-devel and linux-kernel-headers
+	  (/usr/include/linux)
+
+
+Configuring and compiling
+=========================
+
+./configure [options]
+
+--prefix=
+
+	The prefix to put all installed files under. It defaults to
+	/usr/local, so the binaries will go into /usr/local/bin, sbin,
+	manpages into /usr/local/share/man, etc.
+
+If you want to enable debugging, use
+
+	./configure CFLAGS="-ggdb3 -O0" CPPFLAGS="-DEBT_DEBUG"
+
+(-O0 is used to turn off instruction reordering, which makes debugging
+much easier.)
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..6181003
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,69 @@
+# -*- Makefile -*-
+
+# For debugging, use ./configure CPPFLAGS=-DEBT_DEBUG CFLAGS="-O0 -ggdb3"
+
+PROGNAME = ${PACKAGE_NAME}
+PROGVERSION = ${PACKAGE_VERSION}
+PROGDATE = December\ 2011
+INITDIR = /etc/rc.d/init.d
+initddir = ${INITDIR}
+sysconfigdir = ${sysconfdir}/sysconfig
+EBTD_CMDLINE_MAXLN = 2048
+EBTD_ARGC_MAX = 50
+PIPE_DIR = /tmp/${PACKAGE_NAME}-v${PROGVERSION}
+PIPE = ${PIPE_DIR}/ebtablesd_pipe
+
+
+ACLOCAL_AMFLAGS = -I m4
+AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include \
+	-DPROGVERSION=\"${PACKAGE_VERSION}\" -DPROGNAME=\"${PACKAGE_NAME}\" \
+	-DPROGDATE=\"${PROGDATE}\" \
+	-D_PATH_ETHERTYPES=\"${sysconfdir}/ethertypes\" \
+	-DLOCKFILE=\"${LOCKFILE}\" \
+	-DEBTD_ARGC_MAX=${EBTD_ARGC_MAX} -DEBTD_CMDLINE_MAXLN=${EBTD_CMDLINE_MAXLN} \
+	-DEBTD_PIPE=\"${PIPE}\" -DEBTD_PIPE_DIR=\"${PIPE_DIR}\"
+AM_CFLAGS = ${regular_CFLAGS}
+
+sbin_PROGRAMS = ebtables-legacy ebtablesd ebtablesu ebtables-legacy-restore
+EXTRA_PROGRAMS = static examples/ulog/test_ulog
+sysconf_DATA = ethertypes
+sbin_SCRIPTS = ebtables-legacy-save
+man8_MANS = ebtables-legacy.8
+lib_LTLIBRARIES = libebtc.la
+
+libebtc_la_SOURCES = \
+	communication.c ebtables.c getethertype.c \
+	libebtc.c useful_functions.c \
+	extensions/ebt_802_3.c extensions/ebt_among.c extensions/ebt_arp.c \
+	extensions/ebt_arpreply.c extensions/ebt_ip.c extensions/ebt_ip6.c \
+	extensions/ebt_limit.c extensions/ebt_log.c extensions/ebt_mark.c \
+	extensions/ebt_mark_m.c extensions/ebt_nat.c extensions/ebt_nflog.c \
+	extensions/ebt_pkttype.c extensions/ebt_redirect.c \
+	extensions/ebt_standard.c extensions/ebt_stp.c extensions/ebt_string.c \
+	extensions/ebt_ulog.c extensions/ebt_vlan.c extensions/ebt_AUDIT.c \
+	extensions/ebtable_broute.c extensions/ebtable_filter.c \
+	extensions/ebtable_nat.c
+# Make sure ebtables.c can be built twice
+libebtc_la_CPPFLAGS = ${AM_CPPFLAGS}
+ebtables_legacy_SOURCES = ebtables-standalone.c
+ebtables_legacy_LDADD = libebtc.la
+ebtablesd_LDADD = libebtc.la
+ebtables_legacy_restore_SOURCES = ebtables-restore.c
+ebtables_legacy_restore_LDADD = libebtc.la
+static_SOURCES = ebtables.c
+static_LDFLAGS = -static
+static_LDADD = libebtc.la
+examples_ulog_test_ulog_SOURCES = examples/ulog/test_ulog.c getethertype.c
+
+daemon: ebtablesd ebtablesu
+exec: ebtables-legacy ebtables-legacy-restore
+
+CLEANFILES = ebtables-legacy-save ebtables-legacy.8
+
+ebtables-legacy-save: ebtables-save.in ${top_builddir}/config.status
+	${AM_V_GEN}sed -e 's![@]sbindir@!${sbindir}!g' <$< >$@
+
+ebtables-legacy.8: ebtables-legacy.8.in ${top_builddir}/config.status
+	${AM_V_GEN}sed -e 's![@]PACKAGE_VERSION@!${PACKAGE_VERSION}!g' \
+		-e 's![@]PACKAGE_DATE@!${PROGDATE}!g' \
+		-e 's![@]LOCKFILE@!${LOCKFILE}!g' <$< >$@
diff --git a/NOTICE b/NOTICE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..551cb69
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,5 @@
+tanglee@google.com
+xdsun@google.com
+eliribble@google.com
+allenlintwo@google.com
+jlevasseur@google.com
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..4b92ead
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,9 @@
+Special thanks go out to these early contributors:
+
+Lennert Buytenhek
+Rusty Russel
+Harald Welte
+Jason Lunz
+Tim Gardner
+Loïc Minier
+Nick Fedchik
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..a0c4395
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+
+autoreconf -fi;
+rm -Rf autom4te*.cache;
diff --git a/communication.c b/communication.c
new file mode 100644
index 0000000..ba058c0
--- /dev/null
+++ b/communication.c
@@ -0,0 +1,772 @@
+/*
+ * communication.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ */
+
+/*
+ * All the userspace/kernel communication is in this file.
+ * The other code should not have to know anything about the way the
+ * kernel likes the structure of the table data.
+ * The other code works with linked lists. So, the translation is done here.
+ */
+
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include "include/ebtables_u.h"
+
+extern char* hooknames[NF_BR_NUMHOOKS];
+
+#ifdef KERNEL_64_USERSPACE_32
+#define sparc_cast (uint64_t)
+#else
+#define sparc_cast
+#endif
+
+int sockfd = -1;
+
+static int get_sockfd()
+{
+	int ret = 0;
+	if (sockfd == -1) {
+		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+		if (sockfd < 0) {
+			ebt_print_error("Problem getting a socket, "
+					"you probably don't have the right "
+					"permissions");
+			ret = -1;
+		}
+	}
+	return ret;
+}
+
+static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl)
+{
+	struct ebt_replace *new;
+	struct ebt_u_entry *e;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries;
+	char *p, *base;
+	int i, j;
+	unsigned int entries_size = 0, *chain_offsets;
+
+	new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+	if (!new)
+		ebt_print_memory();
+	new->valid_hooks = u_repl->valid_hooks;
+	strcpy(new->name, u_repl->name);
+	new->nentries = u_repl->nentries;
+	new->num_counters = u_repl->num_counters;
+	new->counters = sparc_cast u_repl->counters;
+	chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int));
+	if (!chain_offsets)
+		ebt_print_memory();
+	/* Determine size */
+	for (i = 0; i < u_repl->num_chains; i++) {
+		if (!(entries = u_repl->chains[i]))
+			continue;
+		chain_offsets[i] = entries_size;
+		entries_size += sizeof(struct ebt_entries);
+		j = 0;
+		e = entries->entries->next;
+		while (e != entries->entries) {
+			j++;
+			entries_size += sizeof(struct ebt_entry);
+			m_l = e->m_list;
+			while (m_l) {
+				entries_size += m_l->m->match_size +
+				   sizeof(struct ebt_entry_match);
+				m_l = m_l->next;
+			}
+			w_l = e->w_list;
+			while (w_l) {
+				entries_size += w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher);
+				w_l = w_l->next;
+			}
+			entries_size += e->t->target_size +
+			   sizeof(struct ebt_entry_target);
+			e = e->next;
+		}
+		/* A little sanity check */
+		if (j != entries->nentries)
+			ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j,
+			   entries->nentries, entries->name);
+	}
+
+	new->entries_size = entries_size;
+	p = (char *)malloc(entries_size);
+	if (!p)
+		ebt_print_memory();
+
+	/* Put everything in one block */
+	new->entries = sparc_cast p;
+	for (i = 0; i < u_repl->num_chains; i++) {
+		struct ebt_entries *hlp;
+
+		hlp = (struct ebt_entries *)p;
+		if (!(entries = u_repl->chains[i]))
+			continue;
+		if (i < NF_BR_NUMHOOKS)
+			new->hook_entry[i] = sparc_cast hlp;
+		hlp->nentries = entries->nentries;
+		hlp->policy = entries->policy;
+		strcpy(hlp->name, entries->name);
+		hlp->counter_offset = entries->counter_offset;
+		hlp->distinguisher = 0; /* Make the kernel see the light */
+		p += sizeof(struct ebt_entries);
+		e = entries->entries->next;
+		while (e != entries->entries) {
+			struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+			tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
+			tmp->invflags = e->invflags;
+			tmp->ethproto = e->ethproto;
+			strcpy(tmp->in, e->in);
+			strcpy(tmp->out, e->out);
+			strcpy(tmp->logical_in, e->logical_in);
+			strcpy(tmp->logical_out, e->logical_out);
+			memcpy(tmp->sourcemac, e->sourcemac,
+			   sizeof(tmp->sourcemac));
+			memcpy(tmp->sourcemsk, e->sourcemsk,
+			   sizeof(tmp->sourcemsk));
+			memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
+			memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
+
+			base = p;
+			p += sizeof(struct ebt_entry);
+			m_l = e->m_list;
+			while (m_l) {
+				memcpy(p, m_l->m, m_l->m->match_size +
+				   sizeof(struct ebt_entry_match));
+				p += m_l->m->match_size +
+				   sizeof(struct ebt_entry_match);
+				m_l = m_l->next;
+			}
+			tmp->watchers_offset = p - base;
+			w_l = e->w_list;
+			while (w_l) {
+				memcpy(p, w_l->w, w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher));
+				p += w_l->w->watcher_size +
+				   sizeof(struct ebt_entry_watcher);
+				w_l = w_l->next;
+			}
+			tmp->target_offset = p - base;
+			memcpy(p, e->t, e->t->target_size +
+			   sizeof(struct ebt_entry_target));
+			if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+				struct ebt_standard_target *st =
+				   (struct ebt_standard_target *)p;
+				/* Translate the jump to a udc */
+				if (st->verdict >= 0)
+					st->verdict = chain_offsets
+					   [st->verdict + NF_BR_NUMHOOKS];
+			}
+			p += e->t->target_size +
+			   sizeof(struct ebt_entry_target);
+			tmp->next_offset = p - base;
+			e = e->next;
+		}
+	}
+
+	/* Sanity check */
+	if (p - (char *)new->entries != new->entries_size)
+		ebt_print_bug("Entries_size bug");
+	free(chain_offsets);
+	return new;
+}
+
+static void store_table_in_file(char *filename, struct ebt_replace *repl)
+{
+	char *data;
+	int size;
+	int fd;
+
+	/* Start from an empty file with the correct priviliges */
+	if ((fd = creat(filename, 0600)) == -1) {
+		ebt_print_error("Couldn't create file %s", filename);
+		return;
+	}
+
+	size = sizeof(struct ebt_replace) + repl->entries_size +
+	   repl->nentries * sizeof(struct ebt_counter);
+	data = (char *)malloc(size);
+	if (!data)
+		ebt_print_memory();
+	memcpy(data, repl, sizeof(struct ebt_replace));
+	memcpy(data + sizeof(struct ebt_replace), (char *)repl->entries,
+	   repl->entries_size);
+	/* Initialize counters to zero, deliver_counters() can update them */
+	memset(data + sizeof(struct ebt_replace) + repl->entries_size,
+	   0, repl->nentries * sizeof(struct ebt_counter));
+	if (write(fd, data, size) != size)
+		ebt_print_error("Couldn't write everything to file %s",
+				filename);
+	close(fd);
+	free(data);
+}
+
+void ebt_deliver_table(struct ebt_u_replace *u_repl)
+{
+	socklen_t optlen;
+	struct ebt_replace *repl;
+
+	/* Translate the struct ebt_u_replace to a struct ebt_replace */
+	repl = translate_user2kernel(u_repl);
+	if (u_repl->filename != NULL) {
+		store_table_in_file(u_repl->filename, repl);
+		goto free_repl;
+	}
+	/* Give the data to the kernel */
+	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+	if (get_sockfd())
+		goto free_repl;
+	if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+		goto free_repl;
+	if (u_repl->command == 8) { /* The ebtables module may not
+	                             * yet be loaded with --atomic-commit */
+		ebtables_insmod("ebtables");
+		if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES,
+		    repl, optlen))
+			goto free_repl;
+	}
+
+	ebt_print_error("Unable to update the kernel. Two possible causes:\n"
+			"1. Multiple ebtables programs were executing simultaneously. The ebtables\n"
+			"   userspace tool doesn't by default support multiple ebtables programs running\n"
+			"   concurrently. The ebtables option --concurrent or a tool like flock can be\n"
+			"   used to support concurrent scripts that update the ebtables kernel tables.\n"
+			"2. The kernel doesn't support a certain ebtables extension, consider\n"
+			"   recompiling your kernel or insmod the extension.\n");
+free_repl:
+	if (repl) {
+		free(repl->entries);
+		free(repl);
+	}
+}
+
+static int store_counters_in_file(char *filename, struct ebt_u_replace *repl)
+{
+	int size = repl->nentries * sizeof(struct ebt_counter), ret = 0;
+	unsigned int entries_size;
+	struct ebt_replace hlp;
+	FILE *file;
+
+	if (!(file = fopen(filename, "r+b"))) {
+		ebt_print_error("Could not open file %s", filename);
+		return -1;
+	}
+	/* Find out entries_size and then set the file pointer to the
+	 * counters */
+	if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET)
+	   || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) !=
+	   sizeof(unsigned int) ||
+	   fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) {
+		ebt_print_error("File %s is corrupt", filename);
+		ret = -1;
+		goto close_file;
+	}
+	if (fwrite(repl->counters, sizeof(char), size, file) != size) {
+		ebt_print_error("Could not write everything to file %s",
+				filename);
+		ret = -1;
+	}
+close_file:
+	fclose(file);
+	return ret;
+}
+
+/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel
+ * and resets the counterchanges to CNT_NORM */
+void ebt_deliver_counters(struct ebt_u_replace *u_repl)
+{
+	struct ebt_counter *old, *new, *newcounters;
+	socklen_t optlen;
+	struct ebt_replace repl;
+	struct ebt_cntchanges *cc = u_repl->cc->next, *cc2;
+	struct ebt_u_entries *entries = NULL;
+	struct ebt_u_entry *next = NULL;
+	int i, chainnr = -1;
+
+	if (u_repl->nentries == 0)
+		return;
+
+	newcounters = (struct ebt_counter *)
+	   malloc(u_repl->nentries * sizeof(struct ebt_counter));
+	if (!newcounters)
+		ebt_print_memory();
+	memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
+	old = u_repl->counters;
+	new = newcounters;
+	while (cc != u_repl->cc) {
+		if (!next || next == entries->entries) {
+			chainnr++;
+			while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) ||
+			       (next = entries->entries->next) == entries->entries))
+				chainnr++;
+			if (chainnr == u_repl->num_chains)
+				break;
+		}
+		if (next == NULL)
+			ebt_print_bug("next == NULL");
+		if (cc->type == CNT_NORM) {
+			/* 'Normal' rule, meaning we didn't do anything to it
+			 * So, we just copy */
+			*new = *old;
+			next->cnt = *new;
+			next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+			old++; /* We've used an old counter */
+			new++; /* We've set a new counter */
+			next = next->next;
+		} else if (cc->type == CNT_DEL) {
+			old++; /* Don't use this old counter */
+		} else {
+			if (cc->type == CNT_CHANGE) {
+				if (cc->change % 3 == 1)
+					new->pcnt = old->pcnt + next->cnt_surplus.pcnt;
+				else if (cc->change % 3 == 2)
+					new->pcnt = old->pcnt - next->cnt_surplus.pcnt;
+				else
+					new->pcnt = next->cnt.pcnt;
+				if (cc->change / 3 == 1)
+					new->bcnt = old->bcnt + next->cnt_surplus.bcnt;
+				else if (cc->change / 3 == 2)
+					new->bcnt = old->bcnt - next->cnt_surplus.bcnt;
+				else
+					new->bcnt = next->cnt.bcnt;
+			} else
+				*new = next->cnt;
+			next->cnt = *new;
+			next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
+			if (cc->type == CNT_ADD)
+				new++;
+			else {
+				old++;
+				new++;
+			}
+			next = next->next;
+		}
+		cc = cc->next;
+	}
+
+	free(u_repl->counters);
+	u_repl->counters = newcounters;
+	u_repl->num_counters = u_repl->nentries;
+	/* Reset the counterchanges to CNT_NORM and delete the unused cc */
+	i = 0;
+	cc = u_repl->cc->next;
+	while (cc != u_repl->cc) {
+		if (cc->type == CNT_DEL) {
+			cc->prev->next = cc->next;
+			cc->next->prev = cc->prev;
+			cc2 = cc->next;
+			free(cc);
+			cc = cc2;
+		} else {
+			cc->type = CNT_NORM;
+			cc->change = 0;
+			i++;
+			cc = cc->next;
+		}
+	}
+	if (i != u_repl->nentries)
+		ebt_print_bug("i != u_repl->nentries");
+	if (u_repl->filename != NULL) {
+		store_counters_in_file(u_repl->filename, u_repl);
+		return;
+	}
+	optlen = u_repl->nentries * sizeof(struct ebt_counter) +
+	   sizeof(struct ebt_replace);
+	/* Now put the stuff in the kernel's struct ebt_replace */
+	repl.counters = sparc_cast u_repl->counters;
+	repl.num_counters = u_repl->num_counters;
+	memcpy(repl.name, u_repl->name, sizeof(repl.name));
+
+	if (get_sockfd())
+		return;
+	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+		ebt_print_bug("Couldn't update kernel counters");
+}
+
+static int
+ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
+{
+	struct ebt_u_match_list *new;
+	int ret = 0;
+
+	new = (struct ebt_u_match_list *)
+	   malloc(sizeof(struct ebt_u_match_list));
+	if (!new)
+		ebt_print_memory();
+	new->m = (struct ebt_entry_match *)
+	   malloc(m->match_size + sizeof(struct ebt_entry_match));
+	if (!new->m)
+		ebt_print_memory();
+	memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (ebt_find_match(new->m->u.name) == NULL) {
+		ebt_print_error("Kernel match %s unsupported by userspace tool",
+				new->m->u.name);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int
+ebt_translate_watcher(struct ebt_entry_watcher *w,
+   struct ebt_u_watcher_list ***l)
+{
+	struct ebt_u_watcher_list *new;
+	int ret = 0;
+
+	new = (struct ebt_u_watcher_list *)
+	   malloc(sizeof(struct ebt_u_watcher_list));
+	if (!new)
+		ebt_print_memory();
+	new->w = (struct ebt_entry_watcher *)
+	   malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
+	if (!new->w)
+		ebt_print_memory();
+	memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (ebt_find_watcher(new->w->u.name) == NULL) {
+		ebt_print_error("Kernel watcher %s unsupported by userspace "
+				"tool", new->w->u.name);
+		ret = -1;
+	}
+	return ret;
+}
+
+static int
+ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt,
+   int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl,
+   unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc)
+{
+	/* An entry */
+	if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
+		struct ebt_u_entry *new;
+		struct ebt_u_match_list **m_l;
+		struct ebt_u_watcher_list **w_l;
+		struct ebt_entry_target *t;
+
+		new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new)
+			ebt_print_memory();
+		new->bitmask = e->bitmask;
+		/*
+		 * Plain userspace code doesn't know about
+		 * EBT_ENTRY_OR_ENTRIES
+		 */
+		new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
+		new->invflags = e->invflags;
+		new->ethproto = e->ethproto;
+		strcpy(new->in, e->in);
+		strcpy(new->out, e->out);
+		strcpy(new->logical_in, e->logical_in);
+		strcpy(new->logical_out, e->logical_out);
+		memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
+		memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
+		memcpy(new->destmac, e->destmac, sizeof(new->destmac));
+		memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
+		if (*totalcnt >= u_repl->nentries)
+			ebt_print_bug("*totalcnt >= u_repl->nentries");
+		new->cnt = u_repl->counters[*totalcnt];
+		new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0;
+		new->cc = *cc;
+		*cc = (*cc)->next;
+		new->m_list = NULL;
+		new->w_list = NULL;
+		new->next = (*u_e)->next;
+		new->next->prev = new;
+		(*u_e)->next = new;
+		new->prev = *u_e;
+		*u_e = new;
+		m_l = &new->m_list;
+		EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
+		w_l = &new->w_list;
+		EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l);
+
+		t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
+		new->t = (struct ebt_entry_target *)
+		   malloc(t->target_size + sizeof(struct ebt_entry_target));
+		if (!new->t)
+			ebt_print_memory();
+		if (ebt_find_target(t->u.name) == NULL) {
+			ebt_print_error("Kernel target %s unsupported by "
+					"userspace tool", t->u.name);
+			return -1;
+		}
+		memcpy(new->t, t, t->target_size +
+		   sizeof(struct ebt_entry_target));
+		/* Deal with jumps to udc */
+		if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
+			char *tmp = base;
+			int verdict = ((struct ebt_standard_target *)t)->verdict;
+			int i;
+
+			if (verdict >= 0) {
+				tmp += verdict;
+				for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++)
+					if (u_repl->chains[i]->kernel_start == tmp)
+						break;
+				if (i == u_repl->num_chains)
+					ebt_print_bug("Can't find udc for jump");
+				((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS;
+			}
+		}
+
+		(*cnt)++;
+		(*totalcnt)++;
+		return 0;
+	} else { /* A new chain */
+		int i;
+		struct ebt_entries *entries = (struct ebt_entries *)e;
+
+		if (*n != *cnt)
+			ebt_print_bug("Nr of entries in the chain is wrong");
+		*n = entries->nentries;
+		*cnt = 0;
+		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+			if (valid_hooks & (1 << i))
+				break;
+		*hook = i;
+		*u_e = u_repl->chains[*hook]->entries;
+		return 0;
+	}
+}
+
+/* Initialize all chain headers */
+static int
+ebt_translate_chains(struct ebt_entry *e, int *hook,
+   struct ebt_u_replace *u_repl, unsigned int valid_hooks)
+{
+	int i;
+	struct ebt_entries *entries = (struct ebt_entries *)e;
+	struct ebt_u_entries *new;
+
+	if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
+		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+			if (valid_hooks & (1 << i))
+				break;
+		new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
+		if (!new)
+			ebt_print_memory();
+		if (i == u_repl->max_chains)
+			ebt_double_chains(u_repl);
+		u_repl->chains[i] = new;
+		if (i >= NF_BR_NUMHOOKS)
+			new->kernel_start = (char *)e;
+		*hook = i;
+		new->nentries = entries->nentries;
+		new->policy = entries->policy;
+		new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new->entries)
+			ebt_print_memory();
+		new->entries->next = new->entries->prev = new->entries;
+		new->counter_offset = entries->counter_offset;
+		strcpy(new->name, entries->name);
+	}
+	return 0;
+}
+
+static int retrieve_from_file(char *filename, struct ebt_replace *repl,
+   char command)
+{
+	FILE *file;
+	char *hlp = NULL, *entries;
+	struct ebt_counter *counters;
+	int size, ret = 0;
+
+	if (!(file = fopen(filename, "r+b"))) {
+		ebt_print_error("Could not open file %s", filename);
+		return -1;
+	}
+	/* Make sure table name is right if command isn't -L or --atomic-commit */
+	if (command != 'L' && command != 8) {
+		hlp = (char *)malloc(strlen(repl->name) + 1);
+		if (!hlp)
+			ebt_print_memory();
+		strcpy(hlp, repl->name);
+	}
+	if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file)
+	   != sizeof(struct ebt_replace)) {
+		ebt_print_error("File %s is corrupt", filename);
+		ret = -1;
+		goto close_file;
+	}
+	if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) {
+		ebt_print_error("File %s contains wrong table name or is "
+				"corrupt", filename);
+		ret = -1;
+		goto close_file;
+	} else if (!ebt_find_table(repl->name)) {
+		ebt_print_error("File %s contains invalid table name",
+				filename);
+		ret = -1;
+		goto close_file;
+	}
+
+	size = sizeof(struct ebt_replace) +
+	   repl->nentries * sizeof(struct ebt_counter) + repl->entries_size;
+	fseek(file, 0, SEEK_END);
+	if (size != ftell(file)) {
+		ebt_print_error("File %s has wrong size", filename);
+		ret = -1;
+		goto close_file;
+	}
+	entries = (char *)malloc(repl->entries_size);
+	if (!entries)
+		ebt_print_memory();
+	repl->entries = sparc_cast entries;
+	if (repl->nentries) {
+		counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter));
+		repl->counters = sparc_cast counters;
+		if (!repl->counters)
+			ebt_print_memory();
+	} else
+		repl->counters = sparc_cast NULL;
+	/* Copy entries and counters */
+	if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) ||
+	   fread((char *)repl->entries, sizeof(char), repl->entries_size, file)
+	   != repl->entries_size ||
+	   fseek(file, sizeof(struct ebt_replace) + repl->entries_size,
+		 SEEK_SET)
+	   || (repl->counters && fread((char *)repl->counters, sizeof(char),
+	   repl->nentries * sizeof(struct ebt_counter), file)
+	   != repl->nentries * sizeof(struct ebt_counter))) {
+		ebt_print_error("File %s is corrupt", filename);
+		free(entries);
+		repl->entries = NULL;
+		ret = -1;
+	}
+close_file:
+	fclose(file);
+	free(hlp);
+	return ret;
+}
+
+static int retrieve_from_kernel(struct ebt_replace *repl, char command,
+				int init)
+{
+	socklen_t optlen;
+	int optname;
+	char *entries;
+
+	optlen = sizeof(struct ebt_replace);
+	if (get_sockfd())
+		return -1;
+	/* --atomic-init || --init-table */
+	if (init)
+		optname = EBT_SO_GET_INIT_INFO;
+	else
+		optname = EBT_SO_GET_INFO;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		return -1;
+
+	if ( !(entries = (char *)malloc(repl->entries_size)) )
+		ebt_print_memory();
+	repl->entries = sparc_cast entries;
+	if (repl->nentries) {
+		struct ebt_counter *counters;
+
+		if (!(counters = (struct ebt_counter *)
+		   malloc(repl->nentries * sizeof(struct ebt_counter))) )
+			ebt_print_memory();
+		repl->counters = sparc_cast counters;
+	}
+	else
+		repl->counters = sparc_cast NULL;
+
+	/* We want to receive the counters */
+	repl->num_counters = repl->nentries;
+	optlen += repl->entries_size + repl->num_counters *
+	   sizeof(struct ebt_counter);
+	if (init)
+		optname = EBT_SO_GET_INIT_ENTRIES;
+	else
+		optname = EBT_SO_GET_ENTRIES;
+	if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
+		ebt_print_bug("Hmm, what is wrong??? bug#1");
+
+	return 0;
+}
+
+int ebt_get_table(struct ebt_u_replace *u_repl, int init)
+{
+	int i, j, k, hook;
+	struct ebt_replace repl;
+	struct ebt_u_entry *u_e = NULL;
+	struct ebt_cntchanges *new_cc = NULL, *cc;
+
+	strcpy(repl.name, u_repl->name);
+	if (u_repl->filename != NULL) {
+		if (init)
+			ebt_print_bug("Getting initial table data from a file is impossible");
+		if (retrieve_from_file(u_repl->filename, &repl, u_repl->command))
+			return -1;
+		/* -L with a wrong table name should be dealt with silently */
+		strcpy(u_repl->name, repl.name);
+	} else if (retrieve_from_kernel(&repl, u_repl->command, init))
+		return -1;
+
+	/* Translate the struct ebt_replace to a struct ebt_u_replace */
+	u_repl->valid_hooks = repl.valid_hooks;
+	u_repl->nentries = repl.nentries;
+	u_repl->num_counters = repl.num_counters;
+	u_repl->counters = repl.counters;
+	u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+	if (!u_repl->cc)
+		ebt_print_memory();
+	u_repl->cc->next = u_repl->cc->prev = u_repl->cc;
+	cc = u_repl->cc;
+	for (i = 0; i < repl.nentries; i++) {
+		new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+		if (!new_cc)
+			ebt_print_memory();
+		new_cc->type = CNT_NORM;
+		new_cc->change = 0;
+		new_cc->prev = cc;
+		cc->next = new_cc;
+		cc = new_cc;
+	}
+	if (repl.nentries) {
+		new_cc->next = u_repl->cc;
+		u_repl->cc->prev = new_cc;
+	}
+	u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *));
+	u_repl->max_chains = EBT_ORI_MAX_CHAINS;
+	hook = -1;
+	/* FIXME: Clean up when an error is encountered */
+	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
+	   &hook, u_repl, u_repl->valid_hooks);
+	if (hook >= NF_BR_NUMHOOKS)
+		u_repl->num_chains = hook + 1;
+	else
+		u_repl->num_chains = NF_BR_NUMHOOKS;
+	i = 0; /* Holds the expected nr. of entries for the chain */
+	j = 0; /* Holds the up to now counted entries for the chain */
+	k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */
+	cc = u_repl->cc->next;
+	hook = -1;
+	EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size,
+	   ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl,
+	   u_repl->valid_hooks, (char *)repl.entries, &cc);
+	if (k != u_repl->nentries)
+		ebt_print_bug("Wrong total nentries");
+	free(repl.entries);
+	return 0;
+}
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..c24ede3
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,26 @@
+AC_INIT([ebtables], [2.0.11])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_MACRO_DIR([m4])
+AC_PROG_INSTALL
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax])
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_DISABLE_STATIC
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+AM_PROG_LIBTOOL
+
+AC_ARG_VAR([LOCKFILE], [Custom libebtc lockfile path (default: /var/lib/ebtables/lock)])
+AS_IF([test "x$LOCKFILE" = x], [LOCKFILE="/var/lib/ebtables/lock"])
+
+regular_CFLAGS="-Wall -Wunused"
+regular_CPPFLAGS=""
+case "$host" in
+	sparc64-*)
+		regular_CPPFLAGS="$regular_CPPFLAGS -DEBT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32";;
+esac
+
+AC_SUBST([regular_CFLAGS])
+AC_SUBST([regular_CPPFLAGS])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/ebtables-legacy.8.in b/ebtables-legacy.8.in
new file mode 100644
index 0000000..3417045
--- /dev/null
+++ b/ebtables-legacy.8.in
@@ -0,0 +1,1146 @@
+.TH EBTABLES 8  "@PACKAGE_DATE@"
+.\"
+.\" Man page written by Bart De Schuymer <bdschuym@pandora.be>
+.\" It is based on the iptables man page.
+.\"
+.\" The man page was edited, February 25th 2003, by 
+.\"      Greg Morgan <" dr_kludge_at_users_sourceforge_net >
+.\"
+.\" Iptables page by Herve Eychenne March 2000.
+.\"
+.\"     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
+ebtables-legacy (@PACKAGE_VERSION@) \- Ethernet bridge frame table administration (legacy)
+.SH SYNOPSIS
+.BR "ebtables " [ -t " table ] " - [ ACDI "] chain rule specification [match extensions] [watcher extensions] target"
+.br
+.BR "ebtables " [ -t " table ] " -P " chain " ACCEPT " | " DROP " | " RETURN
+.br
+.BR "ebtables " [ -t " table ] " -F " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -Z " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -L " [" -Z "] [chain] [ [" --Ln "] | [" --Lx "] ] [" --Lc "] [" --Lmac2 ]
+.br
+.BR "ebtables " [ -t " table ] " -N " chain [" "-P ACCEPT " | " DROP " | " RETURN" ]
+.br
+.BR "ebtables " [ -t " table ] " -X " [chain]"
+.br
+.BR "ebtables " [ -t " table ] " -E " old-chain-name new-chain-name"
+.br
+.BR "ebtables " [ -t " table ] " --init-table
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-commit
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-init
+.br
+.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-save
+.br
+
+.SH LEGACY
+This tool uses the old xtables/setsockopt framework, and is a legacy version
+of ebtables. That means that a new, more modern tool exists with the same
+functionality using the nf_tables framework and you are encouraged to migrate now.
+The new binaries (known as ebtables-nft and formerly known as ebtables-compat)
+uses the same syntax and semantics than this legacy one.
+
+You can still use this legacy tool. You should probably get some specific
+information from your Linux distribution or vendor.
+More docs are available at https://wiki.nftables.org
+
+.SH DESCRIPTION
+.B ebtables
+is an application program used to set up and maintain the
+tables of rules (inside the Linux kernel) that inspect
+Ethernet frames.
+It is analogous to the
+.B iptables
+application, but less complicated, due to the fact that the Ethernet protocol
+is much simpler than the IP protocol.
+.SS CHAINS
+There are three ebtables tables with built-in chains in the
+Linux kernel. These tables are used to divide functionality into
+different sets of rules. Each set of rules is called a chain.
+Each chain is an ordered list of rules that can match Ethernet frames. If a
+rule matches an Ethernet frame, then a processing specification tells
+what to do with that matching frame. The processing specification is
+called a 'target'. However, if the frame does not match the current
+rule in the chain, then the next rule in the chain is examined and so forth.
+The user can create new (user-defined) chains that can be used as the 'target'
+of a rule. User-defined chains are very useful to get better performance
+over the linear traversal of the rules and are also essential for structuring
+the filtering rules into well-organized and maintainable sets of rules.
+.SS TARGETS
+A firewall rule specifies criteria for an Ethernet frame and a frame
+processing specification called a target.  When a frame matches a rule,
+then the next action performed by the kernel is specified by the target.
+The target can be one of these values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+an 'extension' (see below) or a jump to a user-defined chain.
+.PP
+.B ACCEPT
+means to let the frame through.
+.B DROP
+means the frame has to be dropped. In the
+.BR BROUTING " chain however, the " ACCEPT " and " DROP " target have different"
+meanings (see the info provided for the
+.BR -t " option)."
+.B CONTINUE
+means the next rule has to be checked. This can be handy, f.e., to know how many
+frames pass a certain point in the chain, to log those frames or to apply multiple
+targets on a frame.
+.B RETURN
+means stop traversing this chain and resume at the next rule in the
+previous (calling) chain.
+For the extension targets please refer to the
+.B "TARGET EXTENSIONS"
+section of this man page.
+.SS TABLES
+As stated earlier, there are three ebtables tables in the Linux
+kernel.  The table names are
+.BR filter ", " nat " and " broute .
+Of these three tables,
+the filter table is the default table that the command operates on.
+If you are working with the filter table, then you can drop the '-t filter'
+argument to the ebtables command.  However, you will need to provide
+the -t argument for the other two tables.  Moreover, the -t argument must be the
+first argument on the ebtables command line, if used. 
+.TP
+.B "-t, --table"
+.br
+.B filter
+is the default table and contains three built-in chains:
+.B INPUT 
+(for frames destined for the bridge itself, on the level of the MAC destination address), 
+.B OUTPUT 
+(for locally-generated or (b)routed frames) and
+.B FORWARD 
+(for frames being forwarded by the bridge).
+.br
+.br
+.B nat
+is mostly used to change the mac addresses and contains three built-in chains:
+.B PREROUTING 
+(for altering frames as soon as they come in), 
+.B OUTPUT 
+(for altering locally generated or (b)routed frames before they are bridged) and 
+.B POSTROUTING
+(for altering frames as they are about to go out). A small note on the naming
+of chains PREROUTING and POSTROUTING: it would be more accurate to call them
+PREFORWARDING and POSTFORWARDING, but for all those who come from the
+iptables world to ebtables it is easier to have the same names. Note that you
+can change the name
+.BR "" ( -E )
+if you don't like the default.
+.br
+.br
+.B broute
+is used to make a brouter, it has one built-in chain:
+.BR BROUTING .
+The targets
+.BR DROP " and " ACCEPT
+have a special meaning in the broute table (these names are used instead of
+more descriptive names to keep the implementation generic).
+.B DROP
+actually means the frame has to be routed, while
+.B ACCEPT
+means the frame has to be bridged. The
+.B BROUTING
+chain is traversed very early. However, it is only traversed by frames entering on
+a bridge port that is in forwarding state. Normally those frames
+would be bridged, but you can decide otherwise here. The
+.B redirect
+target is very handy here.
+.SH EBTABLES COMMAND LINE ARGUMENTS
+After the initial ebtables '-t table' command line argument, the remaining
+arguments can be divided into several groups.  These groups
+are commands, miscellaneous commands, rule specifications, match extensions,
+watcher extensions and target extensions.
+.SS COMMANDS
+The ebtables command arguments specify the actions to perform on the table
+defined with the -t argument.  If you do not use the -t argument to name
+a table, the commands apply to the default filter table.
+Only one command may be used on the command line at a time, except when
+the commands
+.BR -L " and " -Z
+are combined, the commands
+.BR -N " and " -P
+are combined, or when
+.B --atomic-file
+is used.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to delete (directly after
+.BR -D ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). When \fIend_nr\fP is omitted, all rules starting
+from \fIstart_nr\fP are deleted. Using negative numbers is allowed, for more
+details about using negative numbers, see the
+.B -I
+command. The second usage is by
+specifying the complete rule as it would have been specified when it was added. Only
+the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, is deleted.
+.TP
+.B "-C, --change-counters"
+Change the counters of the specified rule or rules from the selected chain. There are two ways to
+use this command. The first is by specifying an interval of rule numbers
+to do the changes on (directly after
+.BR -C ).
+Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use
+.B -L --Ln
+to list the rules with their rule number). The details are the same as for the
+.BR -D " command. The second usage is by"
+specifying the complete rule as it would have been specified when it was added. Only
+the counters of the first encountered rule that is the same as this specified rule, in other
+words the matching rule with the lowest (positive) rule number, are changed.
+In the first usage, the counters are specified directly after the interval specification,
+in the second usage directly after
+.BR -C .
+First the packet counter is specified, then the byte counter. If the specified counters start
+with a '+', the counter values are added to the respective current counter values.
+If the specified counters start with a '-', the counter values are decreased from the respective
+current counter values. No bounds checking is done. If the counters don't start with '+' or '-',
+the current counters are changed to the specified counters.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number. If the
+rule number is not specified, the rule is added at the head of the chain.
+If the current number of rules equals
+.IR N ,
+then the specified number can be
+between
+.IR -N " and " N+1 .
+For a positive number
+.IR i ,
+it holds that
+.IR i " and " i-N-1
+specify the same place in the chain where the rule should be inserted. The rule number
+0 specifies the place past the last rule in the chain and using this number is therefore
+equivalent to using the
+.BR -A " command."
+Rule numbers structly smaller than 0 can be useful when more than one rule needs to be inserted
+in a chain.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy can be
+.BR ACCEPT ", " DROP " or " RETURN .
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, then every chain will be
+flushed. Flushing a chain does not change the policy of the
+chain, however.
+.TP
+.B "-Z, --zero"
+Set the counters of the selected chain to zero. If no chain is selected, all the counters
+are set to zero. The
+.B "-Z"
+command can be used in conjunction with the 
+.B "-L"
+command.
+When both the
+.B "-Z"
+and
+.B "-L"
+commands are used together in this way, the rule counters are printed on the screen
+before they are set to zero.
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed.
+.br
+The following options change the output of the
+.B "-L"
+command.
+.br
+.B "--Ln"
+.br
+Places the rule number in front of every rule. This option is incompatible with the
+.BR --Lx " option."
+.br
+.B "--Lc"
+.br
+Shows the counters at the end of each rule displayed by the
+.B "-L"
+command. Both a frame counter (pcnt) and a byte counter (bcnt) are displayed.
+The frame counter shows how many frames have matched the specific rule, the byte
+counter shows the sum of the frame sizes of these matching frames. Using this option
+.BR "" "in combination with the " --Lx " option causes the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lx"
+.br
+Changes the output so that it produces a set of ebtables commands that construct
+the contents of the chain, when specified.
+If no chain is specified, ebtables commands to construct the contents of the
+table are given, including commands for creating the user-defined chains (if any).
+You can use this set of commands in an ebtables boot or reload
+script.  For example the output could be used at system startup.
+The 
+.B "--Lx"
+option is incompatible with the
+.B "--Ln"
+listing option. Using the
+.BR --Lx " option together with the " --Lc " option will cause the counters to be written out"
+.BR "" "in the '" -c " <pcnt> <bcnt>' option format."
+.br
+.B "--Lmac2"
+.br
+Shows all MAC addresses with the same length, adding leading zeroes
+if necessary. The default representation omits leading zeroes in the addresses.
+.TP
+.B "-N, --new-chain"
+Create a new user-defined chain with the given name. The number of
+user-defined chains is limited only by the number of possible chain names.
+A user-defined chain name has a maximum
+length of 31 characters. The standard policy of the user-defined chain is
+ACCEPT. The policy of the new chain can be initialized to a different standard
+target by using the
+.B -P
+command together with the
+.B -N
+command. In this case, the chain name does not have to be specified for the
+.B -P
+command.
+.TP
+.B "-X, --delete-chain"
+Delete the specified user-defined chain. There must be no remaining references (jumps)
+to the specified chain, otherwise ebtables will refuse to delete it. If no chain is
+specified, all user-defined chains that aren't referenced will be removed.
+.TP
+.B "-E, --rename-chain"
+Rename the specified chain to a new name.  Besides renaming a user-defined
+chain, you can rename a standard chain to a name that suits your
+taste. For example, if you like PREFORWARDING more than PREROUTING,
+then you can use the -E command to rename the PREROUTING chain. If you do
+rename one of the standard ebtables chain names, please be sure to mention
+this fact should you post a question on the ebtables mailing lists.
+It would be wise to use the standard name in your post. Renaming a standard
+ebtables chain in this fashion has no effect on the structure or functioning
+of the ebtables kernel table.
+.TP
+.B "--init-table"
+Replace the current table data by the initial table data.
+.TP
+.B "--atomic-init"
+Copy the kernel's initial data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-save"
+Copy the kernel's current data of the table to the specified
+file. This can be used as the first action, after which rules are added
+to the file. The file can be specified using the
+.B --atomic-file
+command or through the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B "--atomic-commit"
+Replace the kernel table data with the data contained in the specified
+file. This is a useful command that allows you to load all your rules of a
+certain table into the kernel at once, saving the kernel a lot of precious
+time and allowing atomic updates of the tables. The file which contains
+the table data is constructed by using either the
+.B "--atomic-init"
+or the
+.B "--atomic-save"
+command to generate a starting file. After that, using the
+.B "--atomic-file"
+command when constructing rules or setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable"
+allows you to extend the file and build the complete table before
+committing it to the kernel. This command can be very useful in boot scripts
+to populate the ebtables tables in a fast way.
+.SS MISCELLANOUS COMMANDS
+.TP
+.B "-V, --version"
+Show the version of the ebtables userspace program.
+.TP
+.BR "-h, --help " "[\fIlist of module names\fP]"
+Give a brief description of the command syntax. Here you can also specify
+names of extensions and ebtables will try to write help about those
+extensions. E.g.
+.IR "ebtables -h snat log ip arp" .
+Specify
+.I list_extensions
+to list all extensions supported by the userspace
+utility.
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+.BR RETURN ,
+a target extension (see
+.BR "TARGET EXTENSIONS" ")"
+or a user-defined chain name.
+.TP
+.B --atomic-file "\fIfile\fP"
+Let the command operate on the specified
+.IR file .
+The data of the table to
+operate on will be extracted from the file and the result of the operation
+will be saved back into the file. If specified, this option should come
+before the command specification. An alternative that should be preferred,
+is setting the
+.IR EBTABLES_ATOMIC_FILE " environment variable."
+.TP
+.B -M, --modprobe "\fIprogram\fP"
+When talking to the kernel, use this
+.I program
+to try to automatically load missing kernel modules.
+.TP
+.B --concurrent
+Use a file lock to support concurrent scripts updating the ebtables kernel tables.
+
+.SS
+RULE SPECIFICATIONS
+The following command line arguments make up a rule specification (as used 
+in the add and delete commands). A "!" option before the specification 
+inverts the test for that specification. Apart from these standard rule 
+specifications there are some other command line arguments of interest.
+See both the 
+.BR "MATCH EXTENSIONS" 
+and the
+.BR "WATCHER EXTENSIONS" 
+below.
+.TP
+.BR "-p, --protocol " "[!] \fIprotocol\fP"
+The protocol that was responsible for creating the frame. This can be a
+hexadecimal number, above 
+.IR 0x0600 ,
+a name (e.g.
+.I ARP
+) or
+.BR LENGTH .
+The protocol field of the Ethernet frame can be used to denote the
+length of the header (802.2/802.3 networks). When the value of that field is
+below or equals
+.IR 0x0600 ,
+the value equals the size of the header and shouldn't be used as a
+protocol number. Instead, all frames where the protocol field is used as
+the length field are assumed to be of the same 'protocol'. The protocol
+name used in ebtables for these frames is
+.BR LENGTH .
+.br
+The file
+.B /etc/ethertypes
+can be used to show readable
+characters instead of hexadecimal numbers for the protocols. For example,
+.I 0x0800
+will be represented by 
+.IR IPV4 .
+The use of this file is not case sensitive. 
+See that file for more information. The flag 
+.B --proto
+is an alias for this option.
+.TP 
+.BR "-i, --in-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --in-if
+is an alias for this option.
+.TP
+.BR "--logical-in " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is received (this option is useful in the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface (bridge port) via which a frame is going to be sent (this option is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains). If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+The flag
+.B --out-if
+is an alias for this option.
+.TP
+.BR "--logical-out " "[!] \fIname\fP"
+The (logical) bridge interface via which a frame is going to be sent (this option
+is useful in the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains).
+If the interface name ends with '+', then
+any interface name that begins with this name (disregarding '+') will match.
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source MAC address. Both mask and address are written as 6 hexadecimal
+numbers separated by colons. Alternatively one can specify Unicast,
+Multicast, Broadcast or BGA (Bridge Group Address):
+.br
+.IR "Unicast" "=00:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Multicast" "=01:00:00:00:00:00/01:00:00:00:00:00,"
+.IR "Broadcast" "=ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff or"
+.IR "BGA" "=01:80:c2:00:00:00/ff:ff:ff:ff:ff:ff."
+Note that a broadcast
+address will also match the multicast specification. The flag
+.B --src
+is an alias for this option.
+.TP
+.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination MAC address. See
+.B -s
+(above) for more details on MAC addresses. The flag
+.B --dst
+is an alias for this option.
+.TP
+.BR "-c, --set-counter " "\fIpcnt bcnt\fP"
+If used with
+.BR -A " or " -I ", then the packet and byte counters of the new rule will be set to
+.IR pcnt ", resp. " bcnt ".
+If used with the
+.BR -C " or " -D " commands, only rules with a packet and byte count equal to"
+.IR pcnt ", resp. " bcnt " will match."
+
+.SS MATCH EXTENSIONS
+Ebtables extensions are dynamically loaded into the userspace tool,
+there is therefore no need to explicitly load them with a
+-m option like is done in iptables.
+These extensions deal with functionality supported by kernel modules supplemental to
+the core ebtables code.
+.SS 802_3
+Specify 802.3 DSAP/SSAP fields or SNAP type.  The protocol must be specified as
+.IR "LENGTH " "(see the option " " -p " above).
+.TP
+.BR "--802_3-sap " "[!] \fIsap\fP"
+DSAP and SSAP are two one byte 802.3 fields.  The bytes are always
+equal, so only one byte (hexadecimal) is needed as an argument.
+.TP
+.BR "--802_3-type " "[!] \fItype\fP"
+If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must
+be consulted to determine the payload protocol.  This is a two byte
+(hexadecimal) argument.  Only 802.3 frames with DSAP/SSAP 0xaa are
+checked for type.
+.SS among
+Match a MAC address or MAC/IP address pair versus a list of MAC addresses
+and MAC/IP address pairs.
+A list entry has the following format:
+.IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple"
+list entries are separated by a comma, specifying an IP address corresponding to
+the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address
+but different IP address (and vice versa) can be specified. If the MAC address doesn't
+match any entry from the list, the frame doesn't match the rule (unless "!" was used).
+.TP
+.BR "--among-dst " "[!] \fIlist\fP"
+Compare the MAC destination to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP destination address pairs from the
+list is possible.
+.TP
+.BR "--among-src " "[!] \fIlist\fP"
+Compare the MAC source to the given list. If the Ethernet frame has type
+.IR IPv4 " or " ARP ,
+then comparison with MAC/IP source address pairs from the list
+is possible.
+.TP
+.BR "--among-dst-file " "[!] \fIfile\fP"
+Same as
+.BR --among-dst " but the list is read in from the specified file."
+.TP
+.BR "--among-src-file " "[!] \fIfile\fP"
+Same as
+.BR --among-src " but the list is read in from the specified file."
+.SS arp
+Specify (R)ARP fields. The protocol must be specified as
+.IR ARP " or " RARP .
+.TP
+.BR "--arp-opcode " "[!] \fIopcode\fP"
+The (R)ARP opcode (decimal or a string, for more details see
+.BR "ebtables -h arp" ).
+.TP
+.BR "--arp-htype " "[!] \fIhardware type\fP"
+The hardware type, this can be a decimal or the string
+.I Ethernet
+(which sets
+.I type
+to 1). Most (R)ARP packets have Eternet as hardware type.
+.TP
+.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+The protocol type for which the (r)arp is used (hexadecimal or the string
+.IR IPv4 ,
+denoting 0x0800).
+Most (R)ARP packets have protocol type IPv4.
+.TP
+.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP source address specification.
+.TP
+.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP IP destination address specification.
+.TP
+.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC source address specification.
+.TP
+.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The (R)ARP MAC destination address specification.
+.TP
+.BR "" "[!]" " --arp-gratuitous"
+Checks for ARP gratuitous packets: checks equality of IPv4 source
+address and IPv4 destination address inside the ARP header.
+.SS ip
+Specify IPv4 fields. The protocol must be specified as
+.IR IPv4 .
+.TP
+.BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IP address.
+The flag
+.B --ip-src
+is an alias for this option.
+.TP
+.BR "--ip-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IP address.
+The flag
+.B --ip-dst
+is an alias for this option.
+.TP
+.BR "--ip-tos " "[!] \fItos\fP"
+The IP type of service, in hexadecimal numbers.
+.BR IPv4 .
+.TP
+.BR "--ip-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip-proto
+is an alias for this option.
+.TP
+.BR "--ip-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IP protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-sport
+is an alias for this option.
+.TP
+.BR "--ip-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for ip protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip-dport
+is an alias for this option.
+.SS ip6
+Specify IPv6 fields. The protocol must be specified as
+.IR IPv6 .
+.TP
+.BR "--ip6-source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source IPv6 address.
+The flag
+.B --ip6-src
+is an alias for this option.
+.TP
+.BR "--ip6-destination " "[!] \fIaddress\fP[/\fImask\fP]"
+The destination IPv6 address.
+The flag
+.B --ip6-dst
+is an alias for this option.
+.TP
+.BR "--ip6-tclass " "[!] \fItclass\fP"
+The IPv6 traffic class, in hexadecimal numbers.
+.TP
+.BR "--ip6-protocol " "[!] \fIprotocol\fP"
+The IP protocol.
+The flag
+.B --ip6-proto
+is an alias for this option.
+.TP
+.BR "--ip6-source-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The source port or port range for the IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-sport
+is an alias for this option.
+.TP
+.BR "--ip6-destination-port " "[!] \fIport1\fP[:\fIport2\fP]"
+The destination port or port range for IPv6 protocols 6 (TCP), 17
+(UDP), 33 (DCCP) or 132 (SCTP). The
+.B --ip6-protocol
+option must be specified as
+.IR TCP ", " UDP ", " DCCP " or " SCTP .
+If
+.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used."
+The flag
+.B --ip6-dport
+is an alias for this option.
+.TP
+.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}"
+Specify ipv6\-icmp type and code to match.
+Ranges for both type and code are supported. Type and code are
+separated by a slash. Valid numbers for type and range are 0 to 255.
+To match a single type including all valid codes, symbolic names can
+be used instead of numbers. The list of known type names is shown by the command
+.nf
+  ebtables \-\-help ip6
+.fi
+This option is only valid for \-\-ip6-prococol ipv6-icmp.
+.SS limit
+This module matches at a limited rate using a token bucket filter.
+A rule using this extension will match until this limit is reached.
+It can be used with the
+.B --log
+watcher to give limited logging, for example. Its use is the same
+as the limit match of iptables.
+.TP
+.BR "--limit " "[\fIvalue\fP]"
+Maximum average matching rate: specified as a number, with an optional
+.IR /second ", " /minute ", " /hour ", or " /day " suffix; the default is " 3/hour .
+.TP
+.BR "--limit-burst " "[\fInumber\fP]"
+Maximum initial number of packets to match: this number gets recharged by
+one every time the limit specified above is not reached, up to this
+number; the default is
+.IR 5 .
+.SS mark_m
+.TP
+.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]"
+Matches frames with the given unsigned mark value. If a
+.IR value " and " mask " are specified, the logical AND of the mark value of the frame and"
+the user-specified
+.IR mask " is taken before comparing it with the"
+user-specified mark
+.IR value ". When only a mark "
+.IR value " is specified, the packet"
+only matches when the mark value of the frame equals the user-specified
+mark
+.IR value .
+If only a
+.IR mask " is specified, the logical"
+AND of the mark value of the frame and the user-specified
+.IR mask " is taken and the frame matches when the result of this logical AND is"
+non-zero. Only specifying a
+.IR mask " is useful to match multiple mark values."
+.SS pkttype
+.TP
+.BR "--pkttype-type " "[!] \fItype\fP"
+Matches on the Ethernet "class" of the frame, which is determined by the
+generic networking code. Possible values:
+.IR broadcast " (MAC destination is the broadcast address),"
+.IR multicast " (MAC destination is a multicast address),"
+.IR host " (MAC destination is the receiving network device), or "
+.IR otherhost " (none of the above)."
+.SS stp
+Specify stp BPDU (bridge protocol data unit) fields. The destination
+address
+.BR "" ( -d ") must be specified as the bridge group address"
+.IR "" ( BGA ).
+For all options for which a range of values can be specified, it holds that
+if the lower bound is omitted (but the colon is not), then the lowest possible lower bound
+for that option is used, while if the upper bound is omitted (but the colon again is not), the
+highest possible upper bound for that option is used.
+.TP
+.BR "--stp-type " "[!] \fItype\fP"
+The BPDU type (0-255), recognized non-numerical types are
+.IR config ", denoting a configuration BPDU (=0), and"
+.IR tcn ", denothing a topology change notification BPDU (=128)."
+.TP
+.BR "--stp-flags " "[!] \fIflag\fP"
+The BPDU flag (0-255), recognized non-numerical flags are
+.IR topology-change ", denoting the topology change flag (=1), and"
+.IR topology-change-ack ", denoting the topology change acknowledgement flag (=128)."
+.TP
+.BR "--stp-root-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The root priority (0-65535) range.
+.TP
+.BR "--stp-root-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The root mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-root-cost " "[!] [\fIcost\fP][:\fIcost\fP]"
+The root path cost (0-4294967295) range.
+.TP
+.BR "--stp-sender-prio " "[!] [\fIprio\fP][:\fIprio\fP]"
+The BPDU's sender priority (0-65535) range.
+.TP
+.BR "--stp-sender-addr " "[!] [\fIaddress\fP][/\fImask\fP]"
+The BPDU's sender mac address, see the option
+.BR -s " for more details."
+.TP
+.BR "--stp-port " "[!] [\fIport\fP][:\fIport\fP]"
+The port identifier (0-65535) range.
+.TP
+.BR "--stp-msg-age " "[!] [\fIage\fP][:\fIage\fP]"
+The message age timer (0-65535) range.
+.TP
+.BR "--stp-max-age " "[!] [\fIage\fP][:\fIage\fP]"
+The max age timer (0-65535) range.
+.TP
+.BR "--stp-hello-time " "[!] [\fItime\fP][:\fItime\fP]"
+The hello time timer (0-65535) range.
+.TP
+.BR "--stp-forward-delay " "[!] [\fIdelay\fP][:\fIdelay\fP]"
+The forward delay timer (0-65535) range.
+.SS string
+This module matches on a given string using some pattern matching strategy.
+.TP
+.BR "--string-algo " "\fIalgorithm\fP"
+The pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)
+.TP
+.BR "--string-from " "\fIoffset\fP"
+The lowest offset from which a match can start. (default: 0)
+.TP
+.BR "--string-to " "\fIoffset\fP"
+The highest offset from which a match can start. (default: size of frame)
+.TP
+.BR "--string " "[!] \fIpattern\fP"
+Matches the given pattern.
+.TP
+.BR "--string-hex " "[!] \fIpattern\fP"
+Matches the given pattern in hex notation, e.g. '|0D 0A|', '|0D0A|', 'www|09|netfilter|03|org|00|'
+.TP
+.BR "--string-icase"
+Ignore case when searching.
+.SS vlan
+Specify 802.1Q Tag Control Information fields.
+The protocol must be specified as
+.IR 802_1Q " (0x8100)."
+.TP
+.BR "--vlan-id " "[!] \fIid\fP"
+The VLAN identifier field (VID). Decimal number from 0 to 4095.
+.TP
+.BR "--vlan-prio " "[!] \fIprio\fP"
+The user priority field, a decimal number from 0 to 7.
+The VID should be set to 0 ("null VID") or unspecified
+(in the latter case the VID is deliberately set to 0).
+.TP
+.BR "--vlan-encap " "[!] \fItype\fP"
+The encapsulated Ethernet frame type/length.
+Specified as a hexadecimal
+number from 0x0000 to 0xFFFF or as a symbolic name
+from
+.BR /etc/ethertypes .
+
+.SS WATCHER EXTENSIONS
+Watchers only look at frames passing by, they don't modify them nor decide
+to accept the frames or not. These watchers only
+see the frame if the frame matches the rule, and they see it before the
+target is executed.
+.SS log
+The log watcher writes descriptive data about a frame to the syslog.
+.TP
+.B "--log"
+.br
+Log with the default loggin options: log-level=
+.IR info ,
+log-prefix="", no ip logging, no arp logging.
+.TP
+.B --log-level "\fIlevel\fP"
+.br
+Defines the logging level. For the possible values, see
+.BR "ebtables -h log" .
+The default level is 
+.IR info .
+.TP
+.BR --log-prefix " \fItext\fP"
+.br
+Defines the prefix
+.I text
+to be printed at the beginning of the line with the logging information.
+.TP
+.B --log-ip 
+.br
+Will log the ip information when a frame made by the ip protocol matches 
+the rule. The default is no ip information logging.
+.TP
+.B --log-ip6 
+.br
+Will log the ipv6 information when a frame made by the ipv6 protocol matches 
+the rule. The default is no ipv6 information logging.
+.TP
+.B --log-arp
+.br
+Will log the (r)arp information when a frame made by the (r)arp protocols
+matches the rule. The default is no (r)arp information logging.
+.SS nflog
+The nflog watcher passes the packet to the loaded logging backend
+in order to log the packet. This is usually used in combination with
+nfnetlink_log as logging backend, which will multicast the packet
+through a
+.IR netlink
+socket to the specified multicast group. One or more userspace processes
+may subscribe to the group to receive the packets.
+.TP
+.B "--nflog"
+.br
+Log with the default logging options
+.TP
+.B --nflog-group "\fInlgroup\fP"
+.br
+The netlink group (1 - 2^32-1) to which packets are (only applicable for
+nfnetlink_log). The default value is 1.
+.TP
+.B --nflog-prefix "\fIprefix\fP"
+.br
+A prefix string to include in the log message, up to 30 characters
+long, useful for distinguishing messages in the logs.
+.TP
+.B --nflog-range "\fIsize\fP"
+.br
+The number of bytes to be copied to userspace (only applicable for
+nfnetlink_log). nfnetlink_log instances may specify their own
+range, this option overrides it.
+.TP
+.B --nflog-threshold "\fIsize\fP"
+.br
+Number of packets to queue inside the kernel before sending them
+to userspace (only applicable for nfnetlink_log). Higher values
+result in less overhead per packet, but increase delay until the
+packets reach userspace. The default value is 1.
+.SS ulog
+The ulog watcher passes the packet to a userspace
+logging daemon using netlink multicast sockets. This differs
+from the log watcher in the sense that the complete packet is
+sent to userspace instead of a descriptive text and that
+netlink multicast sockets are used instead of the syslog.
+This watcher enables parsing of packets with userspace programs, the
+physical bridge in and out ports are also included in the netlink messages.
+The ulog watcher module accepts 2 parameters when the module is loaded
+into the kernel (e.g. with modprobe):
+.B nlbufsiz
+specifies how big the buffer for each netlink multicast
+group is. If you say
+.IR nlbufsiz=8192 ,
+for example, up to eight kB of packets will
+get accumulated in the kernel until they are sent to userspace. It is
+not possible to allocate more than 128kB. Please also keep in mind that
+this buffer size is allocated for each nlgroup you are using, so the
+total kernel memory usage increases by that factor. The default is 4096.
+.B flushtimeout
+specifies after how many hundredths of a second the queue should be
+flushed, even if it is not full yet. The default is 10 (one tenth of
+a second).
+.TP
+.B "--ulog"
+.br
+Use the default settings: ulog-prefix="", ulog-nlgroup=1,
+ulog-cprange=4096, ulog-qthreshold=1.
+.TP
+.B --ulog-prefix "\fItext\fP"
+.br
+Defines the prefix included with the packets sent to userspace.
+.TP
+.BR --ulog-nlgroup " \fIgroup\fP"
+.br
+Defines which netlink group number to use (a number from 1 to 32).
+Make sure the netlink group numbers used for the iptables ULOG
+target differ from those used for the ebtables ulog watcher.
+The default group number is 1.
+.TP
+.BR --ulog-cprange " \fIrange\fP"
+.br
+Defines the maximum copy range to userspace, for packets matching the
+rule. The default range is 0, which means the maximum copy range is
+given by
+.BR nlbufsiz .
+A maximum copy range larger than
+128*1024 is meaningless as the packets sent to userspace have an upper
+size limit of 128*1024.
+.TP
+.BR --ulog-qthreshold " \fIthreshold\fP"
+.br
+Queue at most
+.I threshold
+number of packets before sending them to
+userspace with a netlink socket. Note that packets can be sent to
+userspace before the queue is full, this happens when the ulog
+kernel timer goes off (the frequency of this timer depends on
+.BR flushtimeout ).
+.SS TARGET EXTENSIONS
+.SS arpreply
+The
+.B arpreply
+target can be used in the
+.BR PREROUTING " chain of the " nat " table."
+If this target sees an ARP request it will automatically reply
+with an ARP reply. The used MAC address for the reply can be specified.
+The protocol must be specified as
+.IR ARP .
+When the ARP message is not an ARP request or when the ARP request isn't
+for an IP address on an Ethernet network, it is ignored by this target
+.BR "" ( CONTINUE ).
+When the ARP request is malformed, it is dropped
+.BR "" ( DROP ).
+.TP
+.BR "--arpreply-mac " "\fIaddress\fP"
+Specifies the MAC address to reply with: the Ethernet source MAC and the
+ARP payload source MAC will be filled in with this address.
+.TP
+.BR "--arpreply-target " "\fItarget\fP"
+Specifies the standard target. After sending the ARP reply, the rule still
+has to give a standard target so ebtables knows what to do with the ARP request.
+The default target
+.BR "" "is " DROP .
+.SS dnat
+The
+.B dnat
+target can only be used in the
+.BR BROUTING " chain of the " broute " table and the "
+.BR PREROUTING " and " OUTPUT " chains of the " nat " table."
+It specifies that the destination MAC address has to be changed.
+.TP
+.BR "--to-destination " "\fIaddress\fP"
+.br
+Change the destination MAC address to the specified
+.IR address .
+The flag
+.B --to-dst
+is an alias for this option.
+.TP
+.BR "--dnat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the dnat, the rule still has to
+give a standard target so ebtables knows what to do with the dnated frame.
+The default target is
+.BR ACCEPT .
+Making it
+.BR CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " only makes"
+sense in the
+.BR BROUTING " chain but using the " redirect " target is more logical there. " RETURN " is also allowed. Note that using " RETURN
+in a base chain is not allowed (for obvious reasons).
+.SS mark
+.BR "" "The " mark " target can be used in every chain of every table. It is possible"
+to use the marking of a frame/packet in both ebtables and iptables,
+if the bridge-nf code is compiled into the kernel. Both put the marking at the
+same place. This allows for a form of communication between ebtables and iptables.
+.TP
+.BR "--mark-set " "\fIvalue\fP"
+.br
+Mark the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-or " "\fIvalue\fP"
+.br
+Or the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-and " "\fIvalue\fP"
+.br
+And the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-xor " "\fIvalue\fP"
+.br
+Xor the frame with the specified non-negative
+.IR value .
+.TP
+.BR "--mark-target " "\fItarget\fP"
+.br
+Specifies the standard target. After marking the frame, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " can let you do other"
+things with the frame in subsequent rules of the chain.
+.SS redirect
+The
+.B redirect
+target will change the MAC target address to that of the bridge device the
+frame arrived on. This target can only be used in the
+.BR BROUTING " chain of the " broute " table and the "
+.BR PREROUTING " chain of the " nat " table."
+In the
+.BR BROUTING " chain, the MAC address of the bridge port is used as destination address,"
+.BR "" "in the " PREROUTING " chain, the MAC address of the bridge is used."
+.TP
+.BR "--redirect-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the MAC redirect, the rule
+still has to give a standard target so ebtables knows what to do.
+The default target is
+.BR ACCEPT ". Making it " CONTINUE " could let you use"
+multiple target extensions on the same frame. Making it
+.BR DROP " in the " BROUTING " chain will let the frames be routed. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.SS snat
+The
+.B snat
+target can only be used in the
+.BR POSTROUTING " chain of the " nat " table."
+It specifies that the source MAC address has to be changed.
+.TP
+.BR "--to-source " "\fIaddress\fP"
+.br
+Changes the source MAC address to the specified
+.IR address ". The flag"
+.B --to-src
+is an alias for this option.
+.TP
+.BR "--snat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the snat, the rule still has 
+to give a standard target so ebtables knows what to do.
+.BR "" "The default target is " ACCEPT ". Making it " CONTINUE " could let you use"
+.BR "" "multiple target extensions on the same frame. Making it " DROP " doesn't"
+.BR "" "make sense, but you could do that too. " RETURN " is also allowed. Note"
+.BR "" "that using " RETURN " in a base chain is not allowed."
+.br
+.TP
+.BR "--snat-arp "
+.br
+Also change the hardware source address inside the arp header if the packet is an
+arp message and the hardware address length in the arp header is 6 bytes.
+.br
+.SH FILES
+.I /etc/ethertypes
+.I @LOCKFILE@
+.SH ENVIRONMENT VARIABLES
+.I EBTABLES_ATOMIC_FILE
+.SH MAILINGLISTS
+.BR "" "See " http://netfilter.org/mailinglists.html
+.SH SEE ALSO
+.BR iptables "(8), " brctl "(8), " ifconfig "(8), " route (8)
+.PP
+.BR "" "See " http://ebtables.sf.net
diff --git a/ebtables-restore.c b/ebtables-restore.c
new file mode 100644
index 0000000..bb4d0cf
--- /dev/null
+++ b/ebtables-restore.c
@@ -0,0 +1,156 @@
+/*
+ * ebtables-restore.c, October 2005
+ *
+ * Author: Bart De Schuymer
+ *
+ * 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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "include/ebtables_u.h"
+
+static const struct option options[] = {
+	{.name = "noflush", .has_arg = 0, .val = 'n'},
+	{ 0 }
+};
+
+static struct ebt_u_replace replace[3];
+void ebt_early_init_once();
+
+#define OPT_KERNELDATA  0x800 /* Also defined in ebtables.c */
+
+static void print_usage()
+{
+	fprintf(stderr, "Usage: ebtables-restore [ --noflush ]\n");
+	exit(1);
+}
+
+static void copy_table_names()
+{
+	strcpy(replace[0].name, "filter");
+	strcpy(replace[1].name, "nat");
+	strcpy(replace[2].name, "broute");
+}
+
+#define ebtrest_print_error(format, args...) do {fprintf(stderr, "ebtables-restore: "\
+                                             "line %d: "format".\n", line, ##args); exit(-1);} while (0)
+int main(int argc_, char *argv_[])
+{
+	char *argv[EBTD_ARGC_MAX], cmdline[EBTD_CMDLINE_MAXLN];
+	int i, offset, quotemode = 0, argc, table_nr = -1, line = 0, whitespace, c, flush = 1;
+	char ebtables_str[] = "ebtables";
+
+	while ((c = getopt_long(argc_, argv_, "n", options, NULL)) != -1) {
+		switch(c) {
+			case 'n':
+				flush = 0;
+				break;
+			default:
+				print_usage();
+				break;
+		}
+	}
+
+	ebt_silent = 0;
+	copy_table_names();
+	ebt_early_init_once();
+	argv[0] = ebtables_str;
+
+	while (fgets(cmdline, EBTD_CMDLINE_MAXLN, stdin)) {
+		line++;
+		if (*cmdline == '#' || *cmdline == '\n')
+			continue;
+		*strchr(cmdline, '\n') = '\0';
+		if (*cmdline == '*') {
+			if (table_nr != -1) {
+				ebt_deliver_table(&replace[table_nr]);
+				ebt_deliver_counters(&replace[table_nr]);
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, cmdline+1))
+					break;
+			if (i == 3)
+				ebtrest_print_error("table '%s' was not recognized", cmdline+1);
+			table_nr = i;
+			replace[table_nr].command = 11;
+			ebt_get_kernel_table(&replace[table_nr], flush);
+			replace[table_nr].command = 0;
+			replace[table_nr].flags = OPT_KERNELDATA; /* Prevent do_command from initialising replace */
+			continue;
+		} else if (table_nr == -1)
+			ebtrest_print_error("no table specified");
+		if (*cmdline == ':') {
+			int policy, chain_nr;
+			char *ch;
+
+			if (!(ch = strchr(cmdline, ' ')))
+				ebtrest_print_error("no policy specified");
+			*ch = '\0';
+			for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+				if (!strcmp(ch+1, ebt_standard_targets[i])) {
+					policy = -i -1;
+					if (policy == EBT_CONTINUE)
+						i = NUM_STANDARD_TARGETS;
+					break;
+				}
+			if (i == NUM_STANDARD_TARGETS)
+				ebtrest_print_error("invalid policy specified");
+			/* No need to check chain name for consistency, since
+			 * we're supposed to be reading an automatically generated
+			 * file. */
+			if ((chain_nr = ebt_get_chainnr(&replace[table_nr], cmdline+1)) == -1)
+				ebt_new_chain(&replace[table_nr], cmdline+1, policy);
+			else
+				replace[table_nr].chains[chain_nr]->policy = policy;
+			continue;
+		}
+		argv[1] = cmdline;
+		offset = whitespace = 0;
+		argc = 2;
+		while (cmdline[offset] != '\0') {
+			if (cmdline[offset] == '\"') {
+				whitespace = 0;
+				quotemode ^= 1;
+				if (quotemode)
+					argv[argc++] = &cmdline[offset+1];
+				else if (cmdline[offset+1] != ' ' && cmdline[offset+1] != '\0')
+					ebtrest_print_error("syntax error at \"");
+				cmdline[offset] = '\0';
+			} else if (!quotemode && cmdline[offset] == ' ') {
+				whitespace = 1;
+				cmdline[offset] = '\0';
+			} else if (whitespace == 1) {
+				argv[argc++] = &cmdline[offset];
+				whitespace = 0;
+			}
+			offset++;
+		}
+		if (quotemode)
+			ebtrest_print_error("wrong use of '\"'");
+		optind = 0; /* Setting optind = 1 causes serious annoyances */
+		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
+		ebt_reinit_extensions();
+	}
+
+	if (table_nr != -1) {
+		ebt_deliver_table(&replace[table_nr]);
+		ebt_deliver_counters(&replace[table_nr]);
+	}
+	return 0;
+}
diff --git a/ebtables-save.in b/ebtables-save.in
new file mode 100644
index 0000000..17924a2
--- /dev/null
+++ b/ebtables-save.in
@@ -0,0 +1,61 @@
+#!/usr/bin/perl -w
+#
+#
+# A script that generates text output of the ebtables rules.
+# Similar to iptables-save.
+#
+# It can be used to store active configuration to /etc/sysconfig/ebtables
+
+use strict;
+my $table;
+my $ebtables = "@sbindir@/ebtables";
+my $cnt = "";
+my $version = "1.0";
+my $table_name;
+
+# ========================================================
+# Process filter table
+# ========================================================
+sub process_table {
+    my $chain = "";
+    my $rules = "";
+    my $chains = "";
+    my $line = "";
+
+    foreach $line (split("\n",$_[0])) {
+        if ($line =~ m/Bridge table: (.*)/) {
+            print "*$1\n";
+            next;
+        }
+        if ($line =~ m/Bridge chain: (.*?), entries:.* policy: (.*)/) {
+            $chains = $chains . ":$1 $2\n";
+            $chain = $1;
+            next;
+        }
+        if ($line =~ m/^$/) {
+            next;
+        }
+        if ($cnt eq "--Lc") {
+            $line =~ s/, pcnt = (.*) -- bcnt = (.*)/-c $1 $2/;
+        } else {
+            $line =~ s/ $//;
+        }
+        $rules = $rules . "-A $chain $line\n";
+    }
+
+    print $chains;
+    print $rules;
+    print "\n";
+}
+# ========================================================
+
+unless (-x $ebtables) { exit -1 };
+print "# Generated by ebtables-save v$version (legacy) on " . `date`;
+if (defined($ENV{'EBTABLES_SAVE_COUNTER'}) && $ENV{'EBTABLES_SAVE_COUNTER'} eq "yes") {
+    $cnt = "--Lc";
+}
+foreach $table_name (split("\n", `grep -E '^ebtable_' /proc/modules | cut -f1 -d' ' | sed s/ebtable_//`)) {
+    $table =`$ebtables -t $table_name -L $cnt`;
+    unless ($? == 0) { print $table; exit -1 };
+    &process_table($table);
+}
diff --git a/ebtables-standalone.c b/ebtables-standalone.c
new file mode 100644
index 0000000..d349d39
--- /dev/null
+++ b/ebtables-standalone.c
@@ -0,0 +1,14 @@
+#include <string.h>
+#include "include/ebtables_u.h"
+
+static struct ebt_u_replace replace;
+void ebt_early_init_once();
+
+int main(int argc, char *argv[])
+{
+	ebt_silent = 0;
+	ebt_early_init_once();
+	strcpy(replace.name, "filter");
+	do_command(argc, argv, EXEC_STYLE_PRG, &replace);
+	return 0;
+}
diff --git a/ebtables.c b/ebtables.c
new file mode 100644
index 0000000..c1f5c2b
--- /dev/null
+++ b/ebtables.c
@@ -0,0 +1,1241 @@
+/*
+ * ebtables.c, v2.0 July 2002
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code was stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <signal.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+
+/* Checks whether a command has already been specified */
+#define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->flags & OPT_ZERO)
+
+#define OPT_COMMAND	0x01
+#define OPT_TABLE	0x02
+#define OPT_IN		0x04
+#define OPT_OUT		0x08
+#define OPT_JUMP	0x10
+#define OPT_PROTOCOL	0x20
+#define OPT_SOURCE	0x40
+#define OPT_DEST	0x80
+#define OPT_ZERO	0x100
+#define OPT_LOGICALIN	0x200
+#define OPT_LOGICALOUT	0x400
+#define OPT_KERNELDATA	0x800 /* This value is also defined in ebtablesd.c */
+#define OPT_COUNT	0x1000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_INCR	0x2000 /* This value is also defined in libebtc.c */
+#define OPT_CNT_DECR	0x4000 /* This value is also defined in libebtc.c */
+
+/* Default command line options. Do not mess around with the already
+ * assigned numbers unless you know what you are doing */
+static struct option ebt_original_options[] =
+{
+	{ "append"         , required_argument, 0, 'A' },
+	{ "insert"         , required_argument, 0, 'I' },
+	{ "delete"         , required_argument, 0, 'D' },
+	{ "list"           , optional_argument, 0, 'L' },
+	{ "Lc"             , no_argument      , 0, 4   },
+	{ "Ln"             , no_argument      , 0, 5   },
+	{ "Lx"             , no_argument      , 0, 6   },
+	{ "Lmac2"          , no_argument      , 0, 12  },
+	{ "zero"           , optional_argument, 0, 'Z' },
+	{ "flush"          , optional_argument, 0, 'F' },
+	{ "policy"         , required_argument, 0, 'P' },
+	{ "in-interface"   , required_argument, 0, 'i' },
+	{ "in-if"          , required_argument, 0, 'i' },
+	{ "logical-in"     , required_argument, 0, 2   },
+	{ "logical-out"    , required_argument, 0, 3   },
+	{ "out-interface"  , required_argument, 0, 'o' },
+	{ "out-if"         , required_argument, 0, 'o' },
+	{ "version"        , no_argument      , 0, 'V' },
+	{ "help"           , no_argument      , 0, 'h' },
+	{ "jump"           , required_argument, 0, 'j' },
+	{ "set-counters"   , required_argument, 0, 'c' },
+	{ "change-counters", required_argument, 0, 'C' },
+	{ "proto"          , required_argument, 0, 'p' },
+	{ "protocol"       , required_argument, 0, 'p' },
+	{ "db"             , required_argument, 0, 'b' },
+	{ "source"         , required_argument, 0, 's' },
+	{ "src"            , required_argument, 0, 's' },
+	{ "destination"    , required_argument, 0, 'd' },
+	{ "dst"            , required_argument, 0, 'd' },
+	{ "table"          , required_argument, 0, 't' },
+	{ "modprobe"       , required_argument, 0, 'M' },
+	{ "new-chain"      , required_argument, 0, 'N' },
+	{ "rename-chain"   , required_argument, 0, 'E' },
+	{ "delete-chain"   , optional_argument, 0, 'X' },
+	{ "atomic-init"    , no_argument      , 0, 7   },
+	{ "atomic-commit"  , no_argument      , 0, 8   },
+	{ "atomic-file"    , required_argument, 0, 9   },
+	{ "atomic-save"    , no_argument      , 0, 10  },
+	{ "init-table"     , no_argument      , 0, 11  },
+	{ "concurrent"     , no_argument      , 0, 13  },
+	{ 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+/* Holds all the data */
+static struct ebt_u_replace *replace;
+
+/* The chosen table */
+static struct ebt_u_table *table;
+
+/* The pointers in here are special:
+ * The struct ebt_target pointer is actually a struct ebt_u_target pointer.
+ * I do not feel like using a union.
+ * We need a struct ebt_u_target pointer because we know the address of the data
+ * they point to won't change. We want to allow that the struct ebt_u_target.t
+ * member can change.
+ * The same holds for the struct ebt_match and struct ebt_watcher pointers */
+static struct ebt_u_entry *new_entry;
+
+
+static int global_option_offset;
+#define OPTION_OFFSET 256
+static struct option *merge_options(struct option *oldopts,
+   const struct option *newopts, unsigned int *options_offset)
+{
+	unsigned int num_old, num_new, i;
+	struct option *merge;
+
+	if (!newopts || !oldopts || !options_offset)
+		ebt_print_bug("merge wrong");
+	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;
+	*options_offset = global_option_offset;
+
+	merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+	if (!merge)
+		ebt_print_memory();
+	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 += *options_offset;
+	}
+	memset(merge + num_old + num_new, 0, sizeof(struct option));
+	/* Only free dynamically allocated stuff */
+	if (oldopts != ebt_original_options)
+		free(oldopts);
+
+	return merge;
+}
+
+static void merge_match(struct ebt_u_match *m)
+{
+	ebt_options = merge_options
+	   (ebt_options, m->extra_ops, &(m->option_offset));
+}
+
+static void merge_watcher(struct ebt_u_watcher *w)
+{
+	ebt_options = merge_options
+	   (ebt_options, w->extra_ops, &(w->option_offset));
+}
+
+static void merge_target(struct ebt_u_target *t)
+{
+	ebt_options = merge_options
+	   (ebt_options, t->extra_ops, &(t->option_offset));
+}
+
+/* Be backwards compatible, so don't use '+' in kernel */
+#define IF_WILDCARD 1
+static void print_iface(const char *iface)
+{
+	char *c;
+
+	if ((c = strchr(iface, IF_WILDCARD)))
+		*c = '+';
+	printf("%s ", iface);
+	if (c)
+		*c = IF_WILDCARD;
+}
+
+/* We use replace->flags, so we can't use the following values:
+ * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
+#define LIST_N    0x04
+#define LIST_C    0x08
+#define LIST_X    0x10
+#define LIST_MAC2 0x20
+
+/* Helper function for list_rules() */
+static void list_em(struct ebt_u_entries *entries)
+{
+	int i, j, space = 0, digits;
+	struct ebt_u_entry *hlp;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t;
+
+	if (replace->flags & LIST_MAC2)
+		ebt_printstyle_mac = 2;
+	else
+		ebt_printstyle_mac = 0;
+	hlp = entries->entries->next;
+	if (replace->flags & LIST_X && entries->policy != EBT_ACCEPT) {
+		printf("ebtables -t %s -P %s %s\n", replace->name,
+		   entries->name, ebt_standard_targets[-entries->policy - 1]);
+	} else if (!(replace->flags & LIST_X)) {
+		printf("\nBridge chain: %s, entries: %d, policy: %s\n",
+		   entries->name, entries->nentries,
+		   ebt_standard_targets[-entries->policy - 1]);
+	}
+
+	if (replace->flags & LIST_N) {
+		i = entries->nentries;
+		while (i > 9) {
+			space++;
+			i /= 10;
+		}
+	}
+
+	for (i = 0; i < entries->nentries; i++) {
+		if (replace->flags & LIST_N) {
+			digits = 0;
+			/* A little work to get nice rule numbers. */
+			j = i + 1;
+			while (j > 9) {
+				digits++;
+				j /= 10;
+			}
+			for (j = 0; j < space - digits; j++)
+				printf(" ");
+			printf("%d. ", i + 1);
+		}
+		if (replace->flags & LIST_X)
+			printf("ebtables -t %s -A %s ",
+			   replace->name, entries->name);
+
+		/* The standard target's print() uses this to find out
+		 * the name of a udc */
+		hlp->replace = replace;
+
+		/* Don't print anything about the protocol if no protocol was
+		 * specified, obviously this means any protocol will do. */
+		if (!(hlp->bitmask & EBT_NOPROTO)) {
+			printf("-p ");
+			if (hlp->invflags & EBT_IPROTO)
+				printf("! ");
+			if (hlp->bitmask & EBT_802_3)
+				printf("Length ");
+			else {
+				struct ethertypeent *ent;
+
+				ent = getethertypebynumber(ntohs(hlp->ethproto));
+				if (!ent)
+					printf("0x%x ", ntohs(hlp->ethproto));
+				else
+					printf("%s ", ent->e_name);
+			}
+		}
+		if (hlp->bitmask & EBT_SOURCEMAC) {
+			printf("-s ");
+			if (hlp->invflags & EBT_ISOURCE)
+				printf("! ");
+			ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk);
+			printf(" ");
+		}
+		if (hlp->bitmask & EBT_DESTMAC) {
+			printf("-d ");
+			if (hlp->invflags & EBT_IDEST)
+				printf("! ");
+			ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk);
+			printf(" ");
+		}
+		if (hlp->in[0] != '\0') {
+			printf("-i ");
+			if (hlp->invflags & EBT_IIN)
+				printf("! ");
+			print_iface(hlp->in);
+		}
+		if (hlp->logical_in[0] != '\0') {
+			printf("--logical-in ");
+			if (hlp->invflags & EBT_ILOGICALIN)
+				printf("! ");
+			print_iface(hlp->logical_in);
+		}
+		if (hlp->logical_out[0] != '\0') {
+			printf("--logical-out ");
+			if (hlp->invflags & EBT_ILOGICALOUT)
+				printf("! ");
+			print_iface(hlp->logical_out);
+		}
+		if (hlp->out[0] != '\0') {
+			printf("-o ");
+			if (hlp->invflags & EBT_IOUT)
+				printf("! ");
+			print_iface(hlp->out);
+		}
+
+		m_l = hlp->m_list;
+		while (m_l) {
+			m = ebt_find_match(m_l->m->u.name);
+			if (!m)
+				ebt_print_bug("Match not found");
+			m->print(hlp, m_l->m);
+			m_l = m_l->next;
+		}
+		w_l = hlp->w_list;
+		while (w_l) {
+			w = ebt_find_watcher(w_l->w->u.name);
+			if (!w)
+				ebt_print_bug("Watcher not found");
+			w->print(hlp, w_l->w);
+			w_l = w_l->next;
+		}
+
+		printf("-j ");
+		if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
+			printf("%s ", hlp->t->u.name);
+		t = ebt_find_target(hlp->t->u.name);
+		if (!t)
+			ebt_print_bug("Target '%s' not found", hlp->t->u.name);
+		t->print(hlp, hlp->t);
+		if (replace->flags & LIST_C) {
+			uint64_t pcnt = hlp->cnt.pcnt;
+			uint64_t bcnt = hlp->cnt.bcnt;
+
+			if (replace->flags & LIST_X)
+				printf("-c %"PRIu64" %"PRIu64, pcnt, bcnt);
+			else
+				printf(", pcnt = %"PRIu64" -- bcnt = %"PRIu64, pcnt, bcnt);
+		}
+		printf("\n");
+		hlp = hlp->next;
+	}
+}
+
+static void print_help()
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	PRINT_VERSION;
+	printf(
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[NX] [chain]\n"
+"ebtables -E old-chain-name new-chain-name\n\n"
+"Commands:\n"
+"--append -A chain             : append to chain\n"
+"--delete -D chain             : delete matching rule from chain\n"
+"--delete -D chain rulenum     : delete rule at position rulenum from chain\n"
+"--change-counters -C chain\n"
+"          [rulenum] pcnt bcnt : change counters of existing rule\n"
+"--insert -I chain rulenum     : insert rule at position rulenum in chain\n"
+"--list   -L [chain]           : list the rules in a chain or in all chains\n"
+"--flush  -F [chain]           : delete all rules in chain or in all chains\n"
+"--init-table                  : replace the kernel table with the initial table\n"
+"--zero   -Z [chain]           : put counters on zero in chain or in all chains\n"
+"--policy -P chain target      : change policy on chain to target\n"
+"--new-chain -N chain          : create a user defined chain\n"
+"--rename-chain -E old new     : rename a chain\n"
+"--delete-chain -X [chain]     : delete a user defined chain\n"
+"--atomic-commit               : update the kernel w/t table contained in <FILE>\n"
+"--atomic-init                 : put the initial kernel table into <FILE>\n"
+"--atomic-save                 : put the current kernel table into <FILE>\n"
+"--atomic-file file            : set <FILE> to file\n\n"
+"Options:\n"
+"--proto  -p [!] proto         : protocol hexadecimal, by name or LENGTH\n"
+"--src    -s [!] address[/mask]: source mac address\n"
+"--dst    -d [!] address[/mask]: destination mac address\n"
+"--in-if  -i [!] name[+]       : network input interface name\n"
+"--out-if -o [!] name[+]       : network output interface name\n"
+"--logical-in  [!] name[+]     : logical bridge input interface name\n"
+"--logical-out [!] name[+]     : logical bridge output interface name\n"
+"--set-counters -c chain\n"
+"          pcnt bcnt           : set the counters of the to be added rule\n"
+"--modprobe -M program         : try to insert modules using this program\n"
+"--concurrent                  : use a file lock to support concurrent scripts\n"
+"--version -V                  : print package version\n\n"
+"Environment variable:\n"
+ATOMIC_ENV_VARIABLE "          : if set <FILE> (see above) will equal its value"
+"\n\n");
+	m_l = new_entry->m_list;
+	while (m_l) {
+		((struct ebt_u_match *)m_l->m)->help();
+		printf("\n");
+		m_l = m_l->next;
+	}
+	w_l = new_entry->w_list;
+	while (w_l) {
+		((struct ebt_u_watcher *)w_l->w)->help();
+		printf("\n");
+		w_l = w_l->next;
+	}
+	((struct ebt_u_target *)new_entry->t)->help();
+	printf("\n");
+	if (table->help)
+		table->help(ebt_hooknames);
+}
+
+/* Execute command L */
+static void list_rules()
+{
+	int i;
+
+	if (!(replace->flags & LIST_X))
+		printf("Bridge table: %s\n", table->name);
+	if (replace->selected_chain != -1)
+		list_em(ebt_to_chain(replace));
+	else {
+		/* Create new chains and rename standard chains when necessary */
+		if (replace->flags & LIST_X && replace->num_chains > NF_BR_NUMHOOKS) {
+			for (i = NF_BR_NUMHOOKS; i < replace->num_chains; i++)
+				printf("ebtables -t %s -N %s\n", replace->name, replace->chains[i]->name);
+			for (i = 0; i < NF_BR_NUMHOOKS; i++)
+				if (replace->chains[i] && strcmp(replace->chains[i]->name, ebt_hooknames[i]))
+					printf("ebtables -t %s -E %s %s\n", replace->name, ebt_hooknames[i], replace->chains[i]->name);
+		}
+		for (i = 0; i < replace->num_chains; i++)
+			if (replace->chains[i])
+				list_em(replace->chains[i]);
+	}
+}
+
+static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
+{
+	char *colon = strchr(argv, ':'), *buffer;
+
+	if (colon) {
+		*colon = '\0';
+		if (*(colon + 1) == '\0')
+			*rule_nr_end = -1; /* Until the last rule */
+		else {
+			*rule_nr_end = strtol(colon + 1, &buffer, 10);
+			if (*buffer != '\0' || *rule_nr_end == 0)
+				return -1;
+		}
+	}
+	if (colon == argv)
+		*rule_nr = 1; /* Beginning with the first rule */
+	else {
+		*rule_nr = strtol(argv, &buffer, 10);
+		if (*buffer != '\0' || *rule_nr == 0)
+			return -1;
+	}
+	if (!colon)
+		*rule_nr_end = *rule_nr;
+	return 0;
+}
+
+/* Incrementing or decrementing rules in daemon mode is not supported as the
+ * involved code overload is not worth it (too annoying to take the increased
+ * counters in the kernel into account). */
+static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style)
+{
+	char *buffer;
+	int ret = 0;
+
+	if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) ||
+	    (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0'  && argv[optind + 1][1] > '9')))
+		ebt_print_error2("The command -C needs at least 2 arguments");
+	if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) {
+		if (optind + 3 != argc)
+			ebt_print_error2("No extra options allowed with -C start_nr[:end_nr] pcnt bcnt");
+		if (parse_rule_range(argv[optind], rule_nr, rule_nr_end))
+			ebt_print_error2("Something is wrong with the rule number specification '%s'", argv[optind]);
+		optind++;
+	}
+
+	if (argv[optind][0] == '+') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_incr:
+			ebt_print_error2("Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 1;
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else if (argv[optind][0] == '-') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+daemon_decr:
+			ebt_print_error2("Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]);
+		ret += 2;
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else
+		new_entry->cnt_surplus.pcnt = strtoull(argv[optind], &buffer, 10);
+
+	if (*buffer != '\0')
+		goto invalid;
+	optind++;
+	if (argv[optind][0] == '+') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_incr;
+		ret += 3;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else if (argv[optind][0] == '-') {
+		if (exec_style == EXEC_STYLE_DAEMON)
+			goto daemon_decr;
+		ret += 6;
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10);
+	} else
+		new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10);
+
+	if (*buffer != '\0')
+		goto invalid;
+	optind++;
+	return ret;
+invalid:
+	ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
+}
+
+static int parse_iface(char *iface, char *option)
+{
+	char *c;
+
+	if ((c = strchr(iface, '+'))) {
+		if (*(c + 1) != '\0') {
+			ebt_print_error("Spurious characters after '+' wildcard for '%s'", option);
+			return -1;
+		} else
+			*c = IF_WILDCARD;
+	}
+	return 0;
+}
+
+void ebt_early_init_once()
+{
+	ebt_iterate_matches(merge_match);
+	ebt_iterate_watchers(merge_watcher);
+	ebt_iterate_targets(merge_target);
+}
+
+/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_)
+{
+	char *buffer;
+	int c, i;
+	int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */
+	int chcounter = 0; /* Needed for -C */
+	int policy = 0;
+	int rule_nr = 0;
+	int rule_nr_end = 0;
+	struct ebt_u_target *t;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries;
+
+	opterr = 0;
+	ebt_modprobe = NULL;
+
+	replace = replace_;
+
+	/* The daemon doesn't use the environment variable */
+	if (exec_style == EXEC_STYLE_PRG) {
+		buffer = getenv(ATOMIC_ENV_VARIABLE);
+		if (buffer) {
+			replace->filename = malloc(strlen(buffer) + 1);
+			if (!replace->filename)
+				ebt_print_memory();
+			strcpy(replace->filename, buffer);
+			buffer = NULL;
+		}
+	}
+
+	replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */
+	replace->selected_chain = -1;
+	replace->command = 'h';
+
+	if (!new_entry) {
+		new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+		if (!new_entry)
+			ebt_print_memory();
+	}
+	/* Put some sane values in our new entry */
+	ebt_initialize_entry(new_entry);
+	new_entry->replace = replace;
+
+	/* The scenario induced by this loop makes that:
+	 * '-t'  ,'-M' and --atomic (if specified) have to come
+	 * before '-A' and the like */
+
+	/* Getopt saves the day */
+	while ((c = getopt_long(argc, argv,
+	   "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, NULL)) != -1) {
+		switch (c) {
+
+		case 'A': /* Add a rule */
+		case 'D': /* Delete a rule */
+		case 'C': /* Change counters */
+		case 'P': /* Define policy */
+		case 'I': /* Insert a rule */
+		case 'N': /* Make a user defined chain */
+		case 'E': /* Rename chain */
+		case 'X': /* Delete chain */
+			/* We allow -N chainname -P policy */
+			if (replace->command == 'N' && c == 'P') {
+				replace->command = c;
+				optind--; /* No table specified */
+				goto handle_P;
+			}
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+
+			replace->command = c;
+			replace->flags |= OPT_COMMAND;
+			if (!(replace->flags & OPT_KERNELDATA))
+				ebt_get_kernel_table(replace, 0);
+			if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
+				ebt_print_error2("No chain name specified");
+			if (c == 'N') {
+				if (ebt_get_chainnr(replace, optarg) != -1)
+					ebt_print_error2("Chain %s already exists", optarg);
+				else if (ebt_find_target(optarg))
+					ebt_print_error2("Target with name %s exists", optarg);
+				else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
+					ebt_print_error2("Chain name length can't exceed %d",
+							EBT_CHAIN_MAXNAMELEN - 1);
+				else if (strchr(optarg, ' ') != NULL)
+					ebt_print_error2("Use of ' ' not allowed in chain names");
+				ebt_new_chain(replace, optarg, EBT_ACCEPT);
+				/* This is needed to get -N x -P y working */
+				replace->selected_chain = ebt_get_chainnr(replace, optarg);
+				break;
+			} else if (c == 'X') {
+				if (optind >= argc) {
+					replace->selected_chain = -1;
+					ebt_delete_chain(replace);
+					break;
+				}
+
+				if (optind < argc - 1)
+					ebt_print_error2("No extra options allowed with -X");
+
+				if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1)
+					ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+				ebt_delete_chain(replace);
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				optind++;
+				break;
+			}
+
+			if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1)
+				ebt_print_error2("Chain '%s' doesn't exist", optarg);
+			if (c == 'E') {
+				if (optind >= argc)
+					ebt_print_error2("No new chain name specified");
+				else if (optind < argc - 1)
+					ebt_print_error2("No extra options allowed with -E");
+				else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
+					ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1);
+				else if (ebt_get_chainnr(replace, argv[optind]) != -1)
+					ebt_print_error2("Chain '%s' already exists", argv[optind]);
+				else if (ebt_find_target(argv[optind]))
+					ebt_print_error2("Target with name '%s' exists", argv[optind]);
+				else if (strchr(argv[optind], ' ') != NULL)
+					ebt_print_error2("Use of ' ' not allowed in chain names");
+				ebt_rename_chain(replace, argv[optind]);
+				optind++;
+				break;
+			} else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
+				if (optind != argc - 1)
+					ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]");
+				if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
+					ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]);
+				optind++;
+			} else if (c == 'C') {
+				if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style)) == -1)
+					return -1;
+			} else if (c == 'I') {
+				if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
+					rule_nr = 1;
+				else {
+					rule_nr = strtol(argv[optind], &buffer, 10);
+					if (*buffer != '\0')
+						ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]);
+					optind++;
+				}
+			} else if (c == 'P') {
+handle_P:
+				if (optind >= argc)
+					ebt_print_error2("No policy specified");
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(argv[optind], ebt_standard_targets[i])) {
+						policy = -i -1;
+						if (policy == EBT_CONTINUE)
+							ebt_print_error2("Wrong policy '%s'", argv[optind]);
+						break;
+					}
+				if (i == NUM_STANDARD_TARGETS)
+					ebt_print_error2("Unknown policy '%s'", argv[optind]);
+				optind++;
+			}
+			break;
+		case 'L': /* List */
+		case 'F': /* Flush */
+		case 'Z': /* Zero counters */
+			if (c == 'Z') {
+				if ((replace->flags & OPT_ZERO) || (replace->flags & OPT_COMMAND && replace->command != 'L'))
+print_zero:
+					ebt_print_error2("Command -Z only allowed together with command -L");
+				replace->flags |= OPT_ZERO;
+			} else {
+				if (replace->flags & OPT_COMMAND)
+					ebt_print_error2("Multiple commands are not allowed");
+				replace->command = c;
+				replace->flags |= OPT_COMMAND;
+				if (replace->flags & OPT_ZERO && c != 'L')
+					goto print_zero;
+			}
+
+#ifdef SILENT_DAEMON
+			if (c== 'L' && exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-L not supported in daemon mode");
+#endif
+
+			if (!(replace->flags & OPT_KERNELDATA))
+				ebt_get_kernel_table(replace, 0);
+			i = -1;
+			if (optind < argc && argv[optind][0] != '-') {
+				if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
+					ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
+				optind++;
+			}
+			if (i != -1) {
+				if (c == 'Z')
+					zerochain = i;
+				else
+					replace->selected_chain = i;
+			}
+			break;
+		case 'V': /* Version */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->command = 'V';
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2(PROGNAME" v"PROGVERSION" ("PROGDATE")\n");
+			PRINT_VERSION;
+			exit(0);
+		case 'M': /* Modprobe */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the -M option earlier");
+			free(ebt_modprobe);
+			ebt_modprobe = optarg;
+			break;
+		case 'h': /* Help */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("-h not supported in daemon mode");
+#endif
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->command = 'h';
+
+			/* All other arguments should be extension names */
+			while (optind < argc) {
+				struct ebt_u_match *m;
+				struct ebt_u_watcher *w;
+
+				if (!strcasecmp("list_extensions", argv[optind])) {
+					ebt_list_extensions();
+					exit(0);
+				}
+				if ((m = ebt_find_match(argv[optind])))
+					ebt_add_match(new_entry, m);
+				else if ((w = ebt_find_watcher(argv[optind])))
+					ebt_add_watcher(new_entry, w);
+				else {
+					if (!(t = ebt_find_target(argv[optind])))
+						ebt_print_error2("Extension '%s' not found", argv[optind]);
+					if (replace->flags & OPT_JUMP)
+						ebt_print_error2("Sorry, you can only see help for one target extension at a time");
+					replace->flags |= OPT_JUMP;
+					new_entry->t = (struct ebt_entry_target *)t;
+				}
+				optind++;
+			}
+			break;
+		case 't': /* Table */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the -t option first");
+			ebt_check_option2(&(replace->flags), OPT_TABLE);
+			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
+				ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1);
+			strcpy(replace->name, optarg);
+			break;
+		case 'i': /* Input interface */
+		case 2  : /* Logical input interface */
+		case 'o': /* Output interface */
+		case 3  : /* Logical output interface */
+		case 'j': /* Target */
+		case 'p': /* Net family protocol */
+		case 's': /* Source mac */
+		case 'd': /* Destination mac */
+		case 'c': /* Set counters */
+			if (!OPT_COMMANDS)
+				ebt_print_error2("No command specified");
+			if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I' && replace->command != 'C')
+				ebt_print_error2("Command and option do not match");
+			if (c == 'i') {
+				ebt_check_option2(&(replace->flags), OPT_IN);
+				if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
+					ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IIN;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+big_iface_length:
+					ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1);
+				strcpy(new_entry->in, optarg);
+				if (parse_iface(new_entry->in, "-i"))
+					return -1;
+				break;
+			} else if (c == 2) {
+				ebt_check_option2(&(replace->flags), OPT_LOGICALIN);
+				if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
+					ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ILOGICALIN;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->logical_in, optarg);
+				if (parse_iface(new_entry->logical_in, "--logical-in"))
+					return -1;
+				break;
+			} else if (c == 'o') {
+				ebt_check_option2(&(replace->flags), OPT_OUT);
+				if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
+					ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IOUT;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->out, optarg);
+				if (parse_iface(new_entry->out, "-o"))
+					return -1;
+				break;
+			} else if (c == 3) {
+				ebt_check_option2(&(replace->flags), OPT_LOGICALOUT);
+				if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
+					ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ILOGICALOUT;
+
+				if (strlen(optarg) >= IFNAMSIZ)
+					goto big_iface_length;
+				strcpy(new_entry->logical_out, optarg);
+				if (parse_iface(new_entry->logical_out, "--logical-out"))
+					return -1;    
+				break;
+			} else if (c == 'j') {
+				ebt_check_option2(&(replace->flags), OPT_JUMP);
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(optarg, ebt_standard_targets[i])) {
+						t = ebt_find_target(EBT_STANDARD_TARGET);
+						((struct ebt_standard_target *) t->t)->verdict = -i - 1;
+						break;
+					}
+				if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) {
+					ebt_print_error2("Return target only for user defined chains");
+				} else if (i != NUM_STANDARD_TARGETS)
+					break;
+
+				if ((i = ebt_get_chainnr(replace, optarg)) != -1) {
+					if (i < NF_BR_NUMHOOKS)
+						ebt_print_error2("Don't jump to a standard chain");
+					t = ebt_find_target(EBT_STANDARD_TARGET);
+					((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS;
+					break;
+				} else {
+					/* Must be an extension then */
+					struct ebt_u_target *t;
+
+					t = ebt_find_target(optarg);
+					/* -j standard not allowed either */
+					if (!t || t == (struct ebt_u_target *)new_entry->t)
+						ebt_print_error2("Illegal target name '%s'", optarg);
+					new_entry->t = (struct ebt_entry_target *)t;
+					ebt_find_target(EBT_STANDARD_TARGET)->used = 0;
+					t->used = 1;
+				}
+				break;
+			} else if (c == 's') {
+				ebt_check_option2(&(replace->flags), OPT_SOURCE);
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_ISOURCE;
+
+				if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk))
+					ebt_print_error2("Problem with specified source mac '%s'", optarg);
+				new_entry->bitmask |= EBT_SOURCEMAC;
+				break;
+			} else if (c == 'd') {
+				ebt_check_option2(&(replace->flags), OPT_DEST);
+				if (ebt_check_inverse2(optarg))
+					new_entry->invflags |= EBT_IDEST;
+
+				if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk))
+					ebt_print_error2("Problem with specified destination mac '%s'", optarg);
+				new_entry->bitmask |= EBT_DESTMAC;
+				break;
+			} else if (c == 'c') {
+				ebt_check_option2(&(replace->flags), OPT_COUNT);
+				if (ebt_check_inverse2(optarg))
+					ebt_print_error2("Unexpected '!' after -c");
+				if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-')
+					ebt_print_error2("Option -c needs 2 arguments");
+
+				new_entry->cnt.pcnt = strtoull(optarg, &buffer, 10);
+				if (*buffer != '\0')
+					ebt_print_error2("Packet counter '%s' invalid", optarg);
+				new_entry->cnt.bcnt = strtoull(argv[optind], &buffer, 10);
+				if (*buffer != '\0')
+					ebt_print_error2("Packet counter '%s' invalid", argv[optind]);
+				optind++;
+				break;
+			}
+			ebt_check_option2(&(replace->flags), OPT_PROTOCOL);
+			if (ebt_check_inverse2(optarg))
+				new_entry->invflags |= EBT_IPROTO;
+
+			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+			i = strtol(optarg, &buffer, 16);
+			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+				ebt_print_error2("Problem with the specified protocol");
+			if (*buffer != '\0') {
+				struct ethertypeent *ent;
+
+				if (!strcasecmp(optarg, "LENGTH")) {
+					new_entry->bitmask |= EBT_802_3;
+					break;
+				}
+				ent = getethertypebyname(optarg);
+				if (!ent)
+					ebt_print_error2("Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg);
+				new_entry->ethproto = ent->e_ethertype;
+			} else
+				new_entry->ethproto = i;
+
+			if (new_entry->ethproto < 0x0600)
+				ebt_print_error2("Sorry, protocols have values above or equal to 0x0600");
+			break;
+		case 4  : /* Lc */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lc is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_C);
+			if (replace->command != 'L')
+				ebt_print_error("Use --Lc with -L");
+			replace->flags |= LIST_C;
+			break;
+		case 5  : /* Ln */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Ln is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_N);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Ln with -L");
+			if (replace->flags & LIST_X)
+				ebt_print_error2("--Lx is not compatible with --Ln");
+			replace->flags |= LIST_N;
+			break;
+		case 6  : /* Lx */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--Lx is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_X);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Lx with -L");
+			if (replace->flags & LIST_N)
+				ebt_print_error2("--Lx is not compatible with --Ln");
+			replace->flags |= LIST_X;
+			break;
+		case 12 : /* Lmac2 */
+#ifdef SILENT_DAEMON
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error("--Lmac2 is not supported in daemon mode");
+#endif
+			ebt_check_option2(&(replace->flags), LIST_MAC2);
+			if (replace->command != 'L')
+				ebt_print_error2("Use --Lmac2 with -L");
+			replace->flags |= LIST_MAC2;
+			break;
+		case 8 : /* atomic-commit */
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--atomic-commit is not supported in daemon mode");
+			replace->command = c;
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			replace->flags |= OPT_COMMAND;
+			if (!replace->filename)
+				ebt_print_error2("No atomic file specified");
+			/* Get the information from the file */
+			ebt_get_table(replace, 0);
+			/* We don't want the kernel giving us its counters,
+			 * they would overwrite the counters extracted from
+			 * the file */
+			replace->num_counters = 0;
+			/* Make sure the table will be written to the kernel */
+			free(replace->filename);
+			replace->filename = NULL;
+			break;
+		case 7 : /* atomic-init */
+		case 10: /* atomic-save */
+		case 11: /* init-table */
+			if (exec_style == EXEC_STYLE_DAEMON) {
+				if (c == 7) {
+					ebt_print_error2("--atomic-init is not supported in daemon mode");
+				} else if (c == 10)
+					ebt_print_error2("--atomic-save is not supported in daemon mode");
+				ebt_print_error2("--init-table is not supported in daemon mode");
+			}
+			replace->command = c;
+			if (OPT_COMMANDS)
+				ebt_print_error2("Multiple commands are not allowed");
+			if (c != 11 && !replace->filename)
+				ebt_print_error2("No atomic file specified");
+			replace->flags |= OPT_COMMAND;
+			{
+				char *tmp = replace->filename;
+
+				/* Get the kernel table */
+				replace->filename = NULL;
+				ebt_get_kernel_table(replace, c == 10 ? 0 : 1);
+				replace->filename = tmp;
+			}
+			break;
+		case 9 : /* atomic */
+			if (exec_style == EXEC_STYLE_DAEMON)
+				ebt_print_error2("--atomic is not supported in daemon mode");
+			if (OPT_COMMANDS)
+				ebt_print_error2("--atomic has to come before the command");
+			/* A possible memory leak here, but this is not
+			 * executed in daemon mode */
+			replace->filename = (char *)malloc(strlen(optarg) + 1);
+			strcpy(replace->filename, optarg);
+			break;
+		case 13 : /* concurrent */
+			if (OPT_COMMANDS)
+				ebt_print_error2("Please put the --concurrent option first");
+			use_lockfd = 1;
+			break;
+		case 1 :
+			if (!strcmp(optarg, "!"))
+				ebt_check_inverse2(optarg);
+			else
+				ebt_print_error2("Bad argument : '%s'", optarg);
+			/* ebt_check_inverse() did optind++ */
+			optind--;
+			continue;
+		default:
+			/* Is it a target option? */
+			t = (struct ebt_u_target *)new_entry->t;
+			if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				goto check_extension;
+			}
+
+			/* Is it a match_option? */
+			for (m = ebt_matches; m; m = m->next)
+				if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
+					break;
+
+			if (m != NULL) {
+				if (ebt_errormsg[0] != '\0')
+					return -1;
+				if (m->used == 0) {
+					ebt_add_match(new_entry, m);
+					m->used = 1;
+				}
+				goto check_extension;
+			}
+
+			/* Is it a watcher option? */
+			for (w = ebt_watchers; w; w = w->next)
+				if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w))
+					break;
+
+			if (w == NULL && c == '?')
+				ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
+			else if (w == NULL) {
+				if (!strcmp(t->name, "standard"))
+					ebt_print_error2("Unknown argument: don't forget the -t option");
+				else
+					ebt_print_error2("Target-specific option does not correspond with specified target");
+			}
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			if (w->used == 0) {
+				ebt_add_watcher(new_entry, w);
+				w->used = 1;
+			}
+check_extension:
+			if (replace->command != 'A' && replace->command != 'I' &&
+			    replace->command != 'D' && replace->command != 'C')
+				ebt_print_error2("Extensions only for -A, -I, -D and -C");
+		}
+		ebt_invert = 0;
+	}
+
+	/* Just in case we didn't catch an error */
+	if (ebt_errormsg[0] != '\0')
+		return -1;
+
+	if (!(table = ebt_find_table(replace->name)))
+		ebt_print_error2("Bad table name");
+
+	if (replace->command == 'h' && !(replace->flags & OPT_ZERO)) {
+		print_help();
+		if (exec_style == EXEC_STYLE_PRG)
+			exit(0);
+	}
+
+	/* Do the final checks */
+	if (replace->command == 'A' || replace->command == 'I' ||
+	   replace->command == 'D' || replace->command == 'C') {
+		/* This will put the hook_mask right for the chains */
+		ebt_check_for_loops(replace);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		entries = ebt_to_chain(replace);
+		m_l = new_entry->m_list;
+		w_l = new_entry->w_list;
+		t = (struct ebt_u_target *)new_entry->t;
+		while (m_l) {
+			m = (struct ebt_u_match *)(m_l->m);
+			m->final_check(new_entry, m->m, replace->name,
+			   entries->hook_mask, 0);
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			m_l = m_l->next;
+		}
+		while (w_l) {
+			w = (struct ebt_u_watcher *)(w_l->w);
+			w->final_check(new_entry, w->w, replace->name,
+			   entries->hook_mask, 0);
+			if (ebt_errormsg[0] != '\0')
+				return -1;
+			w_l = w_l->next;
+		}
+		t->final_check(new_entry, t->t, replace->name,
+		   entries->hook_mask, 0);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	}
+	/* So, the extensions can work with the host endian.
+	 * The kernel does not have to do this of course */
+	new_entry->ethproto = htons(new_entry->ethproto);
+
+	if (replace->command == 'P') {
+		if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN)
+			ebt_print_error2("Policy RETURN only allowed for user defined chains");
+		ebt_change_policy(replace, policy);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	} else if (replace->command == 'L') {
+		list_rules();
+		if (!(replace->flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG)
+			exit(0);
+	}
+	if (replace->flags & OPT_ZERO) {
+		replace->selected_chain = zerochain;
+		ebt_zero_counters(replace);
+	} else if (replace->command == 'F') {
+		ebt_flush_chains(replace);
+	} else if (replace->command == 'A' || replace->command == 'I') {
+		ebt_add_rule(replace, new_entry, rule_nr);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		/* Makes undoing the add easier (jumps to delete_the_rule) */
+		if (rule_nr <= 0)
+			rule_nr--;
+		rule_nr_end = rule_nr;
+
+		/* a jump to a udc requires checking for loops */
+		if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) &&
+		((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) {
+			/* FIXME: this can be done faster */
+			ebt_check_for_loops(replace);
+			if (ebt_errormsg[0] != '\0')
+				goto delete_the_rule;
+		}
+
+		/* Do the final_check(), for all entries.
+		 * This is needed when adding a rule that has a chain target */
+		i = -1;
+		while (++i != replace->num_chains) {
+			struct ebt_u_entry *e;
+
+			entries = replace->chains[i];
+			if (!entries) {
+				if (i < NF_BR_NUMHOOKS)
+					continue;
+				else
+					ebt_print_bug("whoops\n");
+			}
+			e = entries->entries->next;
+			while (e != entries->entries) {
+				/* Userspace extensions use host endian */
+				e->ethproto = ntohs(e->ethproto);
+				ebt_do_final_checks(replace, e, entries);
+				if (ebt_errormsg[0] != '\0')
+					goto delete_the_rule;
+				e->ethproto = htons(e->ethproto);
+				e = e->next;
+			}
+		}
+		/* Don't reuse the added rule */
+		new_entry = NULL;
+	} else if (replace->command == 'D') {
+delete_the_rule:
+		ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	} else if (replace->command == 'C') {
+		ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter);
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+	}
+	/* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save,
+	 * --init-table fall through */
+
+	if (ebt_errormsg[0] != '\0')
+		return -1;
+	if (table->check)
+		table->check(replace);
+
+	if (exec_style == EXEC_STYLE_PRG) {/* Implies ebt_errormsg[0] == '\0' */
+		ebt_deliver_table(replace);
+
+		if (replace->nentries)
+			ebt_deliver_counters(replace);
+	}
+	return 0;
+}
diff --git a/ebtablesd.c b/ebtablesd.c
new file mode 100644
index 0000000..02d51fa
--- /dev/null
+++ b/ebtablesd.c
@@ -0,0 +1,371 @@
+/*
+ * ebtablesd.c, January 2005
+ *
+ * Author: Bart De Schuymer
+ *
+ * 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 <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "include/ebtables_u.h"
+
+#define OPT_KERNELDATA	0x800 /* Also defined in ebtables.c */
+
+static struct ebt_u_replace replace[3];
+#define OPEN_METHOD_FILE 1
+#define OPEN_METHOD_KERNEL 2
+static int open_method[3];
+void ebt_early_init_once();
+
+static void sigpipe_handler(int sig)
+{
+}
+static void copy_table_names()
+{
+	strcpy(replace[0].name, "filter");
+	strcpy(replace[1].name, "nat");
+	strcpy(replace[2].name, "broute");
+}
+
+int main(int argc_, char *argv_[])
+{
+	char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir",
+	     mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR,
+	     cmdline[EBTD_CMDLINE_MAXLN];
+	int readfd, base = 0, offset = 0, n = 0, quotemode = 0;
+
+	/* Make sure the pipe directory exists */
+	args[0] = name;
+	args[1] = mkdir_option;
+	args[2] = mkdir_dir;
+	args[3] = NULL;
+	switch (fork()) {
+	case 0:
+		execvp(args[0], args);
+
+		/* Not usually reached */
+		exit(0);
+	case -1:
+		return -1;
+
+	default: /* Parent */
+		wait(NULL);
+	}
+
+	if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) {
+		printf("Error creating FIFO " EBTD_PIPE "\n");
+		goto do_exit;
+	}
+
+	if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) {
+		perror("open");
+		goto do_exit;
+	}
+
+	if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) {
+		perror("signal");
+		goto do_exit;
+	}
+
+	ebt_silent = 1;
+
+	copy_table_names();
+	ebt_early_init_once();
+
+	while (1) {
+		int n2, i, argc, table_nr, ntot;
+
+		/* base == 0 */
+		ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1);
+		if (ntot <= 0)
+			continue;
+		ntot += offset;
+continue_read:
+		/* Put '\0' between arguments. */
+		for (; offset < ntot; n++, offset++) {
+			if (cmdline[offset] == '\"') {
+				quotemode ^= 1;
+				cmdline[offset] = '\0';
+			} else if (!quotemode && cmdline[offset] == ' ') {
+				cmdline[offset] = '\0';
+			} else if (cmdline[offset] == '\n') {
+				if (quotemode)
+					ebt_print_error("ebtablesd: wrong number of \" delimiters");
+				cmdline[offset] = '\0';
+				break;
+			}
+		}
+		if (n == 0) {
+			if (offset == ntot) {
+				/* The ntot bytes were parsed and ended with '\n' */
+				base = 0;
+				offset = 0;
+				continue;
+			}
+			offset++;
+			base = offset;
+			n = 0;
+			goto continue_read;
+		}
+		if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */
+			if (base == 0) {
+				ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1);
+				goto write_msg;
+			}
+			memmove(cmdline, cmdline+base+offset, ntot-offset);
+			offset -= base;
+			offset++;
+			base = 0;
+			continue;
+		}
+
+		table_nr = 0;
+		n2 = 0;
+		argc = 0;
+		while (n2 < n && argc < EBTD_ARGC_MAX) {
+			if (*(cmdline + base + n2) == '\0') {
+				n2++;
+				continue;
+			}
+			argv[argc++] = cmdline + base + n2;
+			n2 += strlen(cmdline + base + n2) + 1;
+		}
+		offset++; /* Move past the '\n' */
+		base = offset;
+
+		if (argc > EBTD_ARGC_MAX) {
+			ebt_print_error("ebtablesd: maximum %d arguments "
+			                "allowed", EBTD_ARGC_MAX - 1);
+			goto write_msg;
+		}
+		if (argc == 1) {
+			ebt_print_error("ebtablesd: no arguments");
+			goto write_msg;
+		}
+
+		/* Parse the options */
+		if (!strcmp(argv[1], "-t")) {
+			if (argc < 3) {
+				ebt_print_error("ebtablesd: -t but no table");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			table_nr = i;
+		} else if (!strcmp(argv[1], "free")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command free "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			ebt_cleanup_replace(&replace[i]);
+			copy_table_names();
+			replace[i].flags &= ~OPT_KERNELDATA;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "open")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command open "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			if (!ebt_get_kernel_table(&replace[i], 0)) {
+				replace[i].flags |= OPT_KERNELDATA;
+				open_method[i] = OPEN_METHOD_KERNEL;
+			}
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fopen")) {
+			struct ebt_u_replace tmp;
+
+			memset(&tmp, 0, sizeof(tmp));
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command fopen "
+				                "needs exactly two arguments");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (replace[i].flags & OPT_KERNELDATA) {
+				ebt_print_error("ebtablesd: table %s needs to "
+				                "be freed before it can be "
+				                "opened");
+				goto write_msg;
+			}
+			tmp.filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!tmp.filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(tmp.filename, argv[3]);
+			strcpy(tmp.name, "filter");
+			tmp.command = 'L'; /* Make sure retrieve_from_file()
+			                    * doesn't complain about wrong
+			                    * table name */
+
+			ebt_get_kernel_table(&tmp, 0);
+			free(tmp.filename);
+			tmp.filename = NULL;
+			if (ebt_errormsg[0] != '\0')
+				goto write_msg;
+
+			if (strcmp(tmp.name, argv[2])) {
+				ebt_print_error("ebtablesd: opened file with "
+				                "wrong table name '%s'", tmp.name);
+				ebt_cleanup_replace(&tmp);
+				goto write_msg;
+			}
+			replace[i] = tmp;
+			replace[i].command = '\0';
+			replace[i].flags |= OPT_KERNELDATA;
+			open_method[i] = OPEN_METHOD_FILE;
+			goto write_msg;
+		} else if (!strcmp(argv[1], "commit")) {
+			if (argc != 3) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly one argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			/* The counters from the kernel are useless if we 
+			 * didn't start from a kernel table */
+			if (open_method[i] == OPEN_METHOD_FILE)
+				replace[i].num_counters = 0;
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i]);
+			goto write_msg;
+		} else if (!strcmp(argv[1], "fcommit")) {
+			if (argc != 4) {
+				ebt_print_error("ebtablesd: command commit "
+				                "needs exactly two argument");
+				goto write_msg;
+			}
+
+			for (i = 0; i < 3; i++)
+				if (!strcmp(replace[i].name, argv[2]))
+					break;
+			if (i == 3) {
+				ebt_print_error("ebtablesd: table '%s' was "
+				                "not recognized", argv[2]);
+				goto write_msg;
+			}
+			if (!(replace[i].flags & OPT_KERNELDATA)) {
+				ebt_print_error("ebtablesd: table %s has not "
+				                "been opened");
+				goto write_msg;
+			}
+			replace[i].filename = (char *)malloc(strlen(argv[3]) + 1);
+			if (!replace[i].filename) {
+				ebt_print_error("Out of memory");
+				goto write_msg;
+			}
+			strcpy(replace[i].filename, argv[3]);
+			ebt_deliver_table(&replace[i]);
+			if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL)
+				ebt_deliver_counters(&replace[i]);
+			free(replace[i].filename);
+			replace[i].filename = NULL;
+			goto write_msg;
+		}else if (!strcmp(argv[1], "quit")) {
+			if (argc != 2) {
+				ebt_print_error("ebtablesd: command quit does "
+				                "not take any arguments");
+				goto write_msg;
+			}
+			break;
+		}
+		if (!(replace[table_nr].flags & OPT_KERNELDATA)) {
+			ebt_print_error("ebtablesd: table %s has not been "
+			                "opened", replace[table_nr].name);
+			goto write_msg;
+		}
+		optind = 0; /* Setting optind = 1 causes serious annoyances */
+		do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]);
+		ebt_reinit_extensions();
+write_msg:
+#ifndef SILENT_DAEMON
+		if (ebt_errormsg[0] != '\0')
+			printf("%s.\n", ebt_errormsg);
+#endif
+		ebt_errormsg[0]= '\0';
+		n = 0;
+		goto continue_read;
+	}
+do_exit:
+	unlink(EBTD_PIPE);
+	
+	return 0;
+}
diff --git a/ebtablesu.c b/ebtablesu.c
new file mode 100644
index 0000000..3cee145
--- /dev/null
+++ b/ebtablesu.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static void print_help()
+{
+	printf("ebtablesu v"PROGVERSION" ("PROGDATE")\n");
+	printf(
+"Usage:\n"
+"ebtablesu open table         : copy the kernel table\n"
+"ebtablesu fopen table file   : copy the table from the specified file\n"
+"ebtablesu free table         : remove the table from memory\n"
+"ebtablesu commit table       : commit the table to the kernel\n"
+"ebtablesu fcommit table file : commit the table to the specified file\n\n"
+"ebtablesu <ebtables options> : the ebtables specifications\n"
+"                               use spaces only to separate options and commands\n"
+"For the ebtables options, see\n# ebtables -h\nor\n# man ebtables\n"
+	);
+}
+int main(int argc, char *argv[])
+{
+	char *arguments, *pos;
+	int i, writefd, len = 0;
+
+	if (argc > EBTD_ARGC_MAX) {
+		fprintf(stderr, "ebtablesd accepts at most %d arguments, %d "
+		        "arguments were specified. If you need this many "
+		        "arguments, recompile this tool with a higher value for"
+		        " EBTD_ARGC_MAX.\n", EBTD_ARGC_MAX - 1, argc - 1);
+		return -1;
+	} else if (argc == 1) {
+		fprintf(stderr, "At least one argument is needed.\n");
+		print_help();
+		exit(0);
+	}
+
+	for (i = 0; i < argc; i++)
+		len += strlen(argv[i]);
+	/* Don't forget '\0' */
+	len += argc;
+	if (len > EBTD_CMDLINE_MAXLN) {
+		fprintf(stderr, "ebtablesd has a maximum command line argument "
+		       "length of %d, an argument length of %d was received. "
+		       "If a smaller length is unfeasible, recompile this tool "
+		       "with a higher value for EBTD_CMDLINE_MAXLN.\n",
+		       EBTD_CMDLINE_MAXLN, len);
+		return -1;
+	}
+
+	if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
+		if (argc != 2) {
+			fprintf(stderr, "%s does not accept options.\n", argv[1]);
+			return -1;
+		}
+		print_help();
+		exit(0);
+	}
+
+	if (!(arguments = (char *)malloc(len))) {
+		fprintf(stderr, "ebtablesu: out of memory.\n");
+		return -1;
+	}
+
+	if ((writefd = open(EBTD_PIPE, O_WRONLY, 0)) == -1) {
+		fprintf(stderr, "Could not open the pipe, perhaps ebtablesd is "
+		        "not running or you don't have write permission (try "
+		        "running as root).\n");
+		return -1;
+	}
+
+	pos = arguments;
+	for (i = 0; i < argc; i++) {
+		strcpy(pos, argv[i]);
+		pos += strlen(argv[i]);
+		*(pos++) = ' ';
+	}
+
+	*(pos-1) = '\n';
+	if (write(writefd, arguments, len) == -1) {
+		perror("write");
+		return -1;
+	}
+	return 0;
+}
diff --git a/ethertypes b/ethertypes
new file mode 100644
index 0000000..813177b
--- /dev/null
+++ b/ethertypes
@@ -0,0 +1,39 @@
+#
+# Ethernet frame types
+#		This file describes some of the various Ethernet
+#		protocol types that are used on Ethernet networks.
+#
+# This list could be found on:
+#         http://www.iana.org/assignments/ethernet-numbers
+#         http://www.iana.org/assignments/ieee-802-numbers
+#
+# <name>    <hexnumber> <alias1>...<alias35> #Comment
+#
+IPv4	 	0800  	ip ip4 		# Internet IP (IPv4)
+X25		0805
+ARP		0806	ether-arp	#
+FR_ARP		0808    		# Frame Relay ARP        [RFC1701]
+BPQ		08FF			# G8BPQ AX.25 Ethernet Packet
+DEC		6000			# DEC Assigned proto
+DNA_DL		6001			# DEC DNA Dump/Load
+DNA_RC		6002			# DEC DNA Remote Console
+DNA_RT		6003			# DEC DNA Routing
+LAT		6004			# DEC LAT
+DIAG		6005			# DEC Diagnostics
+CUST		6006			# DEC Customer use
+SCA		6007			# DEC Systems Comms Arch
+TEB		6558			# Trans Ether Bridging   [RFC1701]
+RAW_FR  	6559			# Raw Frame Relay        [RFC1701]
+RARP		8035			# Reverse ARP            [RFC903]
+AARP		80F3			# Appletalk AARP
+ATALK		809B			# Appletalk
+802_1Q		8100	8021q 1q 802.1q	dot1q # 802.1Q Virtual LAN tagged frame
+IPX		8137			# Novell IPX
+NetBEUI		8191			# NetBEUI
+IPv6		86DD	ip6 		# IP version 6
+PPP		880B			# PPP
+ATMMPOA		884C			# MultiProtocol over ATM
+PPP_DISC	8863			# PPPoE discovery messages
+PPP_SES		8864			# PPPoE session messages
+ATMFATE		8884			# Frame-based ATM Transport over Ethernet
+LOOP		9000	loopback 	# loop proto
diff --git a/examples/perf_test/perf_test b/examples/perf_test/perf_test
new file mode 100755
index 0000000..afda266
--- /dev/null
+++ b/examples/perf_test/perf_test
@@ -0,0 +1,261 @@
+#!/bin/bash
+#
+# The ebtablesu/ebtablesd programs are deprecated,
+# they're mostly useful for debugging ebtables in daemon
+# mode. For fast population of tables, use ebtables-restore.
+# You should probably just ignore this script.
+#
+# This script can be used to compare the speed of
+# the different methods for adding rules in ebtables
+# chains.
+#
+# Apart from the standard method of adding rules with
+# the ebtables tool, rules can be added (faster) with
+# ebtablesd running in the background and accepting
+# commands through a pipe. The pipe can be written to
+# with the standard shell tools. The only restriction is
+# that spaces are only used to separate options and
+# commands, i.e. spaces are not allowed in strings, even
+# if they are between "". E.g.
+# ebtablesu -A --log-prefix "a space"
+# is not allowed, however
+# ebtablesu -A --log-prefix "a_space"
+# is allowed.
+#
+# Author: Bart De Schuymer
+#
+
+export PIPE=/tmp/ebtables-v2.0.7/ebtablesd_pipe
+export EBTABLES=/usr/local/sbin/ebtables
+export EBTABLESD=/usr/local/sbin/ebtablesd
+export EBTABLESU=/usr/local/sbin/ebtablesu
+
+if [[ $# = 0 ]]
+then
+
+rm -f iets niets iets.out niets.out
+MAXLIMIT=10000
+for ((LIMIT=10; LIMIT <= MAXLIMIT; LIMIT *= 10)) do
+  killall ebtablesd 2>/dev/null
+  $EBTABLES --init-table
+  export LIMIT
+  time $0 1
+  echo "added" $LIMIT "rules with echo"
+  $EBTABLES --atomic-file iets --atomic-save
+  $EBTABLES -F
+  time $0 2
+  echo "added" $LIMIT "rules with ebtables"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file iets -L > iets.out
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  killall ebtablesd 2>/dev/null
+  $EBTABLES -F
+  time $0 3
+  echo "added" $LIMIT "rules with ebtablesu"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file iets -L > iets.out
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  time $0 4
+  echo "added" $LIMIT "rules with atomic-file"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f niets niets.out
+  time $0 5
+  echo "added" $LIMIT "rules with one atomic-commit"
+  $EBTABLES --atomic-file niets --atomic-save
+  $EBTABLES --atomic-file niets -L > niets.out
+  diff -purN iets.out niets.out
+  rm -f iets iets.out niets niets.out
+done
+
+elif [[ $1 = "1" ]]
+then
+
+$EBTABLESD &
+pid=`jobs -p '$EBTABLESD'`
+sleep 1
+$EBTABLESU open filter
+# Add rules with ebtablesd
+echo "$EBTABLESU -F" >>$PIPE
+for ((a=1; a <= LIMIT; a++)) do
+  echo "$EBTABLESU -A FORWARD" >>$PIPE
+done
+$EBTABLESU commit filter
+$EBTABLESU quit
+wait $pid
+
+elif [[ $1 = "2" ]]
+then
+
+# Add rules with ebtables
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLES -A FORWARD
+done
+
+elif [[ $1 = "3" ]]
+then
+
+$EBTABLESD &
+pid=`jobs -p '$EBTABLESD'`
+sleep 1
+$EBTABLESU open filter
+# Add rules with ebtablesu
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLESU -A FORWARD
+done
+$EBTABLESU commit filter
+$EBTABLESU quit
+wait $pid
+
+elif [[ $1 = "4" ]]
+then
+
+# Add rules with ebtables --atomic-file
+$EBTABLES --atomic-file niets --atomic-init
+for ((a=1; a <= LIMIT; a++))
+do
+  $EBTABLES --atomic-file niets -A FORWARD
+done
+$EBTABLES --atomic-file niets --atomic-commit
+
+else
+
+# Add rules taken from a binary file containing a saved table
+$EBTABLES --atomic-file iets --atomic-commit
+
+fi
+
+
+# From the results below, we can conclude the following about
+# table constructions when speed is an issue.
+# For first-time fast construction of tables, it's best to use the
+#   echo + ebtablesd method. If the echo method is unwanted, it
+#   is best to use the ebtablesu + ebtablesd method.
+#   The echo method is much faster because echo is a bash
+#   built-in command.
+#   Perhaps intialize the kernel tables to empty chains with
+#   policy DROP before constructing the table in userspace.
+# For construction of tables that were constructed earlier, it
+#   is best to save those constructed tables to a binary file
+#   and then use --atomic-commit to get the table in the kernel
+#   (this is lightning fast compared with the other methods).
+#
+# System specs:
+# processor       : 0
+# vendor_id       : AuthenticAMD
+# cpu family      : 6
+# model           : 4
+# model name      : AMD Athlon(tm) processor
+# stepping        : 4
+# cpu MHz         : 1000.592
+# cache size      : 256 KB
+# MemTotal        : 515780 kB
+#
+####10 rules####
+#
+# real    0m0.078s
+# user    0m0.030s
+# sys     0m0.045s
+# added 10 rules with echo
+#
+# real    0m0.098s
+# user    0m0.031s
+# sys     0m0.063s
+# added 10 rules with ebtables
+#
+# real    0m0.275s
+# user    0m0.083s
+# sys     0m0.188s
+# added 10 rules with ebtablesu
+#
+# real    0m0.279s
+# user    0m0.082s
+# sys     0m0.192s
+# added 10 rules with atomic-file
+#
+# real    0m0.017s
+# user    0m0.009s
+# sys     0m0.008s
+# added 10 rules with one atomic-commit
+#
+# real    0m0.095s
+# user    0m0.050s
+# sys     0m0.043s
+# added 100 rules with echo
+#
+# real    0m0.936s
+# user    0m0.315s
+# sys     0m0.587s
+# added 100 rules with ebtables
+#
+# real    0m1.967s
+# user    0m0.449s
+# sys     0m1.479s
+# added 100 rules with ebtablesu
+#
+# real    0m2.472s
+# user    0m0.745s
+# sys     0m1.660s
+# added 100 rules with atomic-file
+#
+# real    0m0.018s
+# user    0m0.006s
+# sys     0m0.012s
+# added 100 rules with one atomic-commit
+#
+# real    0m0.740s
+# user    0m0.461s
+# sys     0m0.265s
+# added 1000 rules with echo
+#
+# real    0m12.471s
+# user    0m4.423s
+# sys     0m7.828s
+# added 1000 rules with ebtables
+#
+# real    0m17.715s
+# user    0m6.814s
+# sys     0m10.604s
+# added 1000 rules with ebtablesu
+#
+# real    0m28.176s
+# user    0m8.875s
+# sys     0m18.704s
+# added 1000 rules with atomic-file
+#
+# real    0m0.025s
+# user    0m0.015s
+# sys     0m0.010s
+# added 1000 rules with one atomic-commit
+#
+# real    1m11.474s
+# user    1m9.662s
+# sys     0m1.035s
+# added 10000 rules with echo
+#
+# real    10m9.418s
+# user    4m19.163s
+# sys     5m37.548s
+# added 10000 rules with ebtables
+#
+# real    2m32.119s
+# user    1m38.085s
+# sys     0m52.298s
+# added 10000 rules with ebtablesu
+#
+# real    13m23.396s
+# user    5m29.770s
+# sys     6m44.598s
+# added 10000 rules with atomic-file
+#
+# real    0m0.319s
+# user    0m0.037s
+# sys     0m0.057s
+# added 10000 rules with one atomic-commit
diff --git a/examples/ulog/test_ulog.c b/examples/ulog/test_ulog.c
new file mode 100644
index 0000000..fbbd909
--- /dev/null
+++ b/examples/ulog/test_ulog.c
@@ -0,0 +1,292 @@
+
+/*
+ * Simple example program to log packets received with the ulog
+ * watcher of ebtables.
+ *
+ * usage:
+ * Add the appropriate ebtables ulog rule, e.g. (0 < NLGROUP < 33):
+ *   ebtables -A FORWARD --ulog-nlgroup NLGROUP
+ * Start this application somewhere:
+ *   test_ulog NLGROUP
+ *
+ * compile with make test_ulog KERNEL_INCLUDES=<path_to_kernel_include_dir>
+ *
+ * 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 <asm/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <time.h>
+#include <linux/netlink.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <netinet/if_ether.h>
+#include <netinet/ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include "../../include/ebtables_u.h"
+#include "../../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_ulog.h>
+
+/* <linux/if_vlan.h> doesn't hand this to userspace :-( */
+#define VLAN_HLEN 4
+struct vlan_hdr {
+	unsigned short TCI;
+	unsigned short encap;
+};
+
+static struct sockaddr_nl sa_local =
+{
+	.nl_family = AF_NETLINK,
+	.nl_groups = 1,
+};
+
+static struct sockaddr_nl sa_kernel =
+{
+	.nl_family = AF_NETLINK,
+	.nl_pid = 0,
+	.nl_groups = 1,
+};
+
+static const char *hookstr[NF_BR_NUMHOOKS] =
+{
+        [NF_BR_POST_ROUTING] "POSTROUTING",
+         [NF_BR_PRE_ROUTING] "PREROUTING",
+           [NF_BR_LOCAL_OUT] "OUTPUT",
+            [NF_BR_LOCAL_IN] "INPUT",
+            [NF_BR_BROUTING] "BROUTING",
+             [NF_BR_FORWARD] "FORWARD"
+};
+
+#define DEBUG_QUEUE 0
+#define BUFLEN 65536
+static char buf[BUFLEN];
+static int sfd;
+
+/* Get the next ebt_ulog packet, talk to the kernel if necessary */
+ebt_ulog_packet_msg_t *ulog_get_packet()
+{
+	static struct nlmsghdr *nlh = NULL;
+	static int len, remain_len;
+	static int pkts_per_msg = 0;
+	ebt_ulog_packet_msg_t *msg;
+	socklen_t addrlen = sizeof(sa_kernel);
+
+	if (!nlh) {
+recv_new:
+		if (pkts_per_msg && DEBUG_QUEUE)
+			printf("PACKETS IN LAST MSG: %d\n", pkts_per_msg);
+		pkts_per_msg = 0;
+		len = recvfrom(sfd, buf, BUFLEN, 0,
+		               (struct sockaddr *)&sa_kernel, &addrlen);
+		if (addrlen != sizeof(sa_kernel)) {
+			printf("addrlen %u != %u\n", addrlen,
+			       (uint32_t)sizeof(sa_kernel));
+			exit(-1);
+		}
+		if (len == -1) {
+			perror("recvmsg");
+			if (errno == EINTR)
+				goto recv_new;
+			exit(-1);
+		}
+		nlh = (struct nlmsghdr *)buf;
+		if (nlh->nlmsg_flags & MSG_TRUNC || len > BUFLEN) {
+			printf("Packet truncated");
+			exit(-1);
+		}
+		if (!NLMSG_OK(nlh, BUFLEN)) {
+			perror("Netlink message parse error\n");
+			return NULL;
+		}
+	}
+
+	msg = NLMSG_DATA(nlh);
+
+	remain_len = (len - ((char *)nlh - buf));
+	if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_type != NLMSG_DONE)
+		nlh = NLMSG_NEXT(nlh, remain_len);
+	else
+		nlh = NULL;
+
+	pkts_per_msg++;
+	return msg;
+}
+
+int main(int argc, char **argv)
+{
+	int i, curr_len, pktcnt = 0;
+	int rcvbufsize = BUFLEN;
+	ebt_ulog_packet_msg_t *msg;
+	struct ethhdr *ehdr;
+	struct ethertypeent *etype;
+	struct protoent *prototype;
+	struct iphdr *iph;
+	struct icmphdr *icmph;
+	struct tm* ptm;
+	char time_str[40], *ctmp;
+
+	if (argc == 2) {
+		i = strtoul(argv[1], &ctmp, 10);
+		if (*ctmp != '\0' || i < 1 || i > 32) {
+			printf("Usage: %s <group number>\nWith  0 < group "
+			       "number < 33\n", argv[0]);
+			exit(0);
+		}
+		sa_local.nl_groups = sa_kernel.nl_groups = 1 << (i - 1);
+	}
+
+	sa_local.nl_pid = getpid();
+	sfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG);
+	if (!sfd) {
+		perror("socket");
+		exit(-1);
+	}
+
+	if (bind(sfd, (struct sockaddr *)(&sa_local), sizeof(sa_local)) ==
+	    -1) {
+		perror("bind");
+		exit(-1);
+	}
+	i = setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbufsize,
+	               sizeof(rcvbufsize));
+
+	while (1) {
+		if (!(msg = ulog_get_packet()))
+			continue;
+
+		if (msg->version != EBT_ULOG_VERSION) {
+			printf("WRONG VERSION: %d INSTEAD OF %d\n",
+			       msg->version, EBT_ULOG_VERSION);
+			continue;
+		}
+		printf("PACKET NR: %d\n", ++pktcnt);
+		iph = NULL;
+		curr_len = ETH_HLEN;
+
+		printf("INDEV=%s\nOUTDEV=%s\nPHYSINDEV=%s\nPHYSOUTDEV=%s\n"
+		       "PREFIX='%s'", msg->indev, msg->outdev, msg->physindev,
+		       msg->physoutdev, msg->prefix);
+
+		ptm = localtime(&msg->stamp.tv_sec);
+		strftime (time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", ptm);
+		printf("\nARRIVAL TIME: %s\nMARK=%lu\nHOOK=%s\n", time_str,
+		       msg->mark, hookstr[msg->hook]);
+
+		if (msg->data_len < curr_len) {
+			printf("====>Packet smaller than Ethernet header "
+			       "length<====\n");
+			goto letscontinue;
+		}
+
+		printf("::::::::ETHERNET:HEADER::::::::\n");
+
+		ehdr = (struct ethhdr *)msg->data;
+		printf("MAC SRC=%s\n", ether_ntoa((const struct ether_addr *)
+		       ehdr->h_source));
+		printf("MAC DST=%s\nETHERNET PROTOCOL=", ether_ntoa(
+		       (const struct ether_addr *)ehdr->h_dest));
+		etype = getethertypebynumber(ntohs(ehdr->h_proto));
+		if (!etype)
+			printf("0x%x\n", ntohs(ehdr->h_proto));
+		else
+			printf("%s\n", etype->e_name);
+
+		if (ehdr->h_proto == htons(ETH_P_8021Q)) {
+			struct vlan_hdr *vlanh = (struct vlan_hdr *)
+			                          (((char *)ehdr) + curr_len);
+			printf("::::::::::VLAN:HEADER::::::::::\n");
+			curr_len += VLAN_HLEN;
+			if (msg->data_len < curr_len) {
+				printf("====>Packet only contains partial "
+				       "VLAN header<====\n");
+				goto letscontinue;
+			}
+
+			printf("VLAN TCI=%d\n", ntohs(vlanh->TCI));
+			printf("VLAN ENCAPS PROTOCOL=");
+			etype = getethertypebynumber(ntohs(vlanh->encap));
+			if (!etype)
+				printf("0x%x\n", ntohs(vlanh->encap));
+			else
+				printf("%s\n", etype->e_name);
+			if (ehdr->h_proto == htons(ETH_P_IP))
+				iph = (struct iphdr *)(vlanh + 1);
+		} else if (ehdr->h_proto == htons(ETH_P_IP))
+			iph = (struct iphdr *)(((char *)ehdr) + curr_len);
+		if (!iph)
+			goto letscontinue;
+
+		curr_len += sizeof(struct iphdr);
+		if (msg->data_len < curr_len || msg->data_len <
+		    (curr_len += iph->ihl * 4 - sizeof(struct iphdr))) {
+			printf("====>Packet only contains partial IP "
+			       "header<====\n");
+			goto letscontinue;
+		}
+
+		printf(":::::::::::IP:HEADER:::::::::::\n");
+		printf("IP SRC ADDR=");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&iph->saddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("\nIP DEST ADDR=");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&iph->daddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("\nIP PROTOCOL=");
+		if (!(prototype = getprotobynumber(iph->protocol)))
+			printf("%d\n", iph->protocol);
+		else
+			printf("%s\n", prototype->p_name);
+
+		if (iph->protocol != IPPROTO_ICMP)
+			goto letscontinue;
+
+		icmph = (struct icmphdr *)(((char *)ehdr) + curr_len);
+		curr_len += 4;
+		if (msg->data_len < curr_len) {
+truncated_icmp:
+			printf("====>Packet only contains partial ICMP "
+			       "header<====\n");
+			goto letscontinue;
+		}
+		if (icmph->type != ICMP_ECHO && icmph->type != ICMP_ECHOREPLY)
+			goto letscontinue;
+
+		curr_len += 4;
+		if (msg->data_len < curr_len)
+			goto truncated_icmp;
+		/* Normally the process id, it's sent out in machine
+		 * byte order */
+
+		printf("ICMP_ECHO IDENTIFIER=%u\n", icmph->un.echo.id);
+		printf("ICMP_ECHO SEQ NR=%u\n", ntohs(icmph->un.echo.sequence));
+
+letscontinue:
+		printf("===>Total Packet length: %ld, of which we examined "
+		       "%d bytes\n", msg->data_len, curr_len);
+		printf("###############################\n"
+		       "######END#OF##PACKET#DUMP######\n"
+		       "###############################\n");
+	}
+
+	return 0;
+}
diff --git a/extensions/ebt_802_3.c b/extensions/ebt_802_3.c
new file mode 100644
index 0000000..d70fd44
--- /dev/null
+++ b/extensions/ebt_802_3.c
@@ -0,0 +1,147 @@
+/* 802_3
+ *
+ * Author:
+ * Chris Vitale <csv@bluetail.com>
+ *
+ * May 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_802_3.h>
+
+#define _802_3_SAP '1'
+#define _802_3_TYPE '2'
+
+static const struct option opts[] =
+{
+	{ "802_3-sap"   , required_argument, 0, _802_3_SAP },
+	{ "802_3-type"  , required_argument, 0, _802_3_TYPE },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"802_3 options:\n"
+"--802_3-sap [!] protocol       : 802.3 DSAP/SSAP- 1 byte value (hex)\n"
+"  DSAP and SSAP are always the same.  One SAP applies to both fields\n"
+"--802_3-type [!] protocol      : 802.3 SNAP Type- 2 byte value (hex)\n"
+"  Type implies SAP value 0xaa\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+	info->invflags = 0;
+	info->bitmask = 0;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data;
+	unsigned int i;
+	char *end;
+
+	switch (c) {
+		case _802_3_SAP:
+			ebt_check_option2(flags, _802_3_SAP);
+			if (ebt_check_inverse2(optarg))
+				info->invflags |= EBT_802_3_SAP;
+			i = strtoul(optarg, &end, 16);
+			if (i > 255 || *end != '\0') 
+				ebt_print_error2("Problem with specified "
+						"sap hex value, %x",i);
+			info->sap = i; /* one byte, so no byte order worries */
+			info->bitmask |= EBT_802_3_SAP;
+			break;
+		case _802_3_TYPE:
+			ebt_check_option2(flags, _802_3_TYPE);
+			if (ebt_check_inverse2(optarg))
+				info->invflags |= EBT_802_3_TYPE;
+			i = strtoul(optarg, &end, 16);
+			if (i > 65535 || *end != '\0') {
+				ebt_print_error2("Problem with the specified "
+						"type hex value, %x",i);
+			}
+			info->type = htons(i);
+			info->bitmask |= EBT_802_3_TYPE;
+			break;
+		default:
+			return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	if (!(entry->bitmask & EBT_802_3))
+		ebt_print_error("For 802.3 DSAP/SSAP filtering the protocol "
+				"must be LENGTH");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data;
+
+	if (info->bitmask & EBT_802_3_SAP) {
+		printf("--802_3-sap ");
+		if (info->invflags & EBT_802_3_SAP)
+			printf("! ");
+		printf("0x%.2x ", info->sap);
+	}
+	if (info->bitmask & EBT_802_3_TYPE) {
+		printf("--802_3-type ");
+		if (info->invflags & EBT_802_3_TYPE)
+			printf("! ");
+		printf("0x%.4x ", ntohs(info->type));
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_802_3_info *info1 = (struct ebt_802_3_info *)m1->data;
+	struct ebt_802_3_info *info2 = (struct ebt_802_3_info *)m2->data;
+
+	if (info1->bitmask != info2->bitmask)
+		return 0;
+	if (info1->invflags != info2->invflags)
+		return 0;
+	if (info1->bitmask & EBT_802_3_SAP) {
+		if (info1->sap != info2->sap) 
+			return 0;
+	}
+	if (info1->bitmask & EBT_802_3_TYPE) {
+		if (info1->type != info2->type)
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match _802_3_match = 
+{
+	.name		= "802_3",
+	.size		= sizeof(struct ebt_802_3_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&_802_3_match);
+}
diff --git a/extensions/ebt_AUDIT.c b/extensions/ebt_AUDIT.c
new file mode 100644
index 0000000..c9befcc
--- /dev/null
+++ b/extensions/ebt_AUDIT.c
@@ -0,0 +1,110 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter/xt_AUDIT.h>
+
+#define AUDIT_TYPE  '1'
+static struct option opts[] =
+{
+	{ "audit-type" , required_argument, 0, AUDIT_TYPE },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"AUDIT target options:\n"
+	" --audit-type TYPE          : Set action type to record.\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct xt_AUDIT_info *info = (struct xt_AUDIT_info *) target->data;
+
+	info->type = 0;
+}
+
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct xt_AUDIT_info *info = (struct xt_AUDIT_info *) (*target)->data;
+
+	switch (c) {
+	case AUDIT_TYPE:
+		ebt_check_option2(flags, AUDIT_TYPE);
+
+		if (!strcasecmp(optarg, "accept"))
+			info->type = XT_AUDIT_TYPE_ACCEPT;
+		else if (!strcasecmp(optarg, "drop"))
+			info->type = XT_AUDIT_TYPE_DROP;
+		else if (!strcasecmp(optarg, "reject"))
+			info->type = XT_AUDIT_TYPE_REJECT;
+		else
+			ebt_print_error2("Bad action type value `%s'", optarg);
+
+		break;
+	 default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	const struct xt_AUDIT_info *info =
+		(const struct xt_AUDIT_info *) target->data;
+
+	printf("--audit-type ");
+
+	switch(info->type) {
+	case XT_AUDIT_TYPE_ACCEPT:
+		printf("accept");
+		break;
+	case XT_AUDIT_TYPE_DROP:
+		printf("drop");
+		break;
+	case XT_AUDIT_TYPE_REJECT:
+		printf("reject");
+		break;
+	}
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	const struct xt_AUDIT_info *info1 =
+		(const struct xt_AUDIT_info *) t1->data;
+	const struct xt_AUDIT_info *info2 =
+		(const struct xt_AUDIT_info *) t2->data;
+
+	return info1->type == info2->type;
+}
+
+static struct ebt_u_target AUDIT_target =
+{
+	.name		= "AUDIT",
+	.size		= sizeof(struct xt_AUDIT_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&AUDIT_target);
+}
diff --git a/extensions/ebt_among.c b/extensions/ebt_among.c
new file mode 100644
index 0000000..30c098c
--- /dev/null
+++ b/extensions/ebt_among.c
@@ -0,0 +1,497 @@
+/* ebt_among
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <ctype.h>
+#include <unistd.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include "../include/ethernetdb.h"
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge/ebt_among.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define AMONG_DST '1'
+#define AMONG_SRC '2'
+#define AMONG_DST_F '3'
+#define AMONG_SRC_F '4'
+
+static const struct option opts[] = {
+	{"among-dst", required_argument, 0, AMONG_DST},
+	{"among-src", required_argument, 0, AMONG_SRC},
+	{"among-dst-file", required_argument, 0, AMONG_DST_F},
+	{"among-src-file", required_argument, 0, AMONG_SRC_F},
+	{0}
+};
+
+#ifdef DEBUG
+static void hexdump(const void *mem, int howmany)
+{
+	printf("\n");
+	const unsigned char *p = mem;
+	int i;
+
+	for (i = 0; i < howmany; i++) {
+		if (i % 32 == 0) {
+			printf("\n%04x: ", i);
+		}
+		printf("%2.2x%c", p[i], ". "[i % 4 == 3]);
+	}
+	printf("\n");
+}
+#endif				/* DEBUG */
+
+static void print_help()
+{
+	printf(
+"`among' options:\n"
+"--among-dst      [!] list      : matches if ether dst is in list\n"
+"--among-src      [!] list      : matches if ether src is in list\n"
+"--among-dst-file [!] file      : obtain dst list from file\n"
+"--among-src-file [!] file      : obtain src list from file\n"
+"list has form:\n"
+" xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]"
+",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n"
+"Things in brackets are optional.\n"
+"If you want to allow two (or more) IP addresses to one MAC address, you\n"
+"can specify two (or more) pairs with the same MAC, e.g.\n"
+" 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n"
+	);
+}
+static int old_size;
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_among_info *amonginfo =
+	    (struct ebt_among_info *) match->data;
+
+	memset(amonginfo, 0, sizeof(struct ebt_among_info));
+	old_size = sizeof(struct ebt_among_info);
+}
+
+static struct ebt_mac_wormhash *new_wormhash(int n)
+{
+	int size =
+	    sizeof(struct ebt_mac_wormhash) +
+	    n * sizeof(struct ebt_mac_wormhash_tuple);
+	struct ebt_mac_wormhash *result =
+	    (struct ebt_mac_wormhash *) malloc(size);
+
+	if (!result)
+		ebt_print_memory();
+	memset(result, 0, size);
+	result->poolsize = n;
+	return result;
+}
+
+static void copy_wormhash(struct ebt_mac_wormhash *d,
+			  const struct ebt_mac_wormhash *s)
+{
+	int dpoolsize = d->poolsize;
+	int dsize, ssize, amount;
+
+	dsize = ebt_mac_wormhash_size(d);
+	ssize = ebt_mac_wormhash_size(s);
+	amount = dsize < ssize ? dsize : ssize;
+	memcpy(d, s, amount);
+	d->poolsize = dpoolsize;
+}
+
+/* Returns:
+ * -1 when '\0' reached
+ * -2 when `n' bytes read and no delimiter found
+ *  0 when no less than `n' bytes read and delimiter found
+ * if `destbuf' is not NULL, it is filled by read bytes and ended with '\0'
+ * *pp is set on the first byte not copied to `destbuf'
+ */
+static int read_until(const char **pp, const char *delimiters,
+		      char *destbuf, int n)
+{
+	int count = 0;
+	int ret = 0;
+	char c;
+
+	while (1) {
+		c = **pp;
+		if (!c) {
+			ret = -1;
+			break;
+		}
+		if (strchr(delimiters, c)) {
+			ret = 0;
+			break;
+		}
+		if (count == n) {
+			ret = -2;
+			break;
+		}
+		if (destbuf)
+			destbuf[count++] = c;
+		(*pp)++;
+	}
+	if (destbuf)
+		destbuf[count] = 0;
+	return ret;
+}
+
+static int fcmp(const void *va, const void *vb) {
+	const struct ebt_mac_wormhash_tuple *a = va;
+	const struct ebt_mac_wormhash_tuple *b = vb;
+	int ca = ((const unsigned char*)a->cmp)[7];
+	int cb = ((const unsigned char*)b->cmp)[7];
+
+	return ca - cb;
+}
+
+static void index_table(struct ebt_mac_wormhash *wh)
+{
+	int ipool, itable;
+	int c;
+
+	for (itable = 0; itable <= 256; itable++) {
+		wh->table[itable] = wh->poolsize;
+	}
+	ipool = 0;
+	itable = 0;
+	while (1) {
+		wh->table[itable] = ipool;
+		c = ((const unsigned char*)wh->pool[ipool].cmp)[7];
+		if (itable <= c) {
+			itable++;
+		} else {
+			ipool++;
+		}
+		if (ipool > wh->poolsize)
+			break;
+	}
+}
+
+static struct ebt_mac_wormhash *create_wormhash(const char *arg)
+{
+	const char *pc = arg;
+	const char *anchor;
+	char *endptr;
+	struct ebt_mac_wormhash *workcopy, *result, *h;
+	unsigned char mac[6];
+	unsigned char ip[4];
+	int nmacs = 0;
+	int i;
+	char token[4];
+
+	if (!(workcopy = new_wormhash(1024))) {
+		ebt_print_memory();
+	}
+	while (1) {
+		/* remember current position, we'll need it on error */
+		anchor = pc;
+
+		/* collect MAC; all its bytes are followed by ':' (colon),
+		 * except for the last one which can be followed by 
+		 * ',' (comma), '=' or '\0' */
+		for (i = 0; i < 5; i++) {
+			if (read_until(&pc, ":", token, 2) < 0
+			    || token[0] == 0) {
+				ebt_print_error("MAC parse error: %.20s", anchor);
+				free(workcopy);
+				return NULL;
+			}
+			mac[i] = strtol(token, &endptr, 16);
+			if (*endptr) {
+				ebt_print_error("MAC parse error: %.20s", anchor);
+				free(workcopy);
+				return NULL;
+			}
+			pc++;
+		}
+		if (read_until(&pc, "=,", token, 2) == -2 || token[0] == 0) {
+			ebt_print_error("MAC parse error: %.20s", anchor);
+			return NULL;
+		}
+		mac[i] = strtol(token, &endptr, 16);
+		if (*endptr) {
+			ebt_print_error("MAC parse error: %.20s", anchor);
+			return NULL;
+		}
+		if (*pc == '=') {
+			/* an IP follows the MAC; collect similarly to MAC */
+			pc++;
+			anchor = pc;
+			for (i = 0; i < 3; i++) {
+				if (read_until(&pc, ".", token, 3) < 0 || token[0] == 0) {
+					ebt_print_error("IP parse error: %.20s", anchor);
+					return NULL;
+				}
+				ip[i] = strtol(token, &endptr, 10);
+				if (*endptr) {
+					ebt_print_error("IP parse error: %.20s", anchor);
+					return NULL;
+				}
+				pc++;
+			}
+			if (read_until(&pc, ",", token, 3) == -2 || token[0] == 0) {
+				ebt_print_error("IP parse error: %.20s", anchor);
+				return NULL;
+			}
+			ip[3] = strtol(token, &endptr, 10);
+			if (*endptr) {
+				ebt_print_error("IP parse error: %.20s", anchor);
+				return NULL;
+			}
+			if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) {
+				ebt_print_error("Illegal IP 0.0.0.0");
+				return NULL;
+			}
+		} else {
+			/* no IP, we set it to 0.0.0.0 */
+			memset(ip, 0, 4);
+		}
+
+		/* we have collected MAC and IP, so we add an entry */
+		memcpy(((char *) workcopy->pool[nmacs].cmp) + 2, mac, 6);
+		memcpy(&(workcopy->pool[nmacs].ip), ip, 4);
+		nmacs++;
+
+		/* re-allocate memory if needed */
+		if (*pc && nmacs >= workcopy->poolsize) {
+			if (!(h = new_wormhash(nmacs * 2))) {
+				ebt_print_memory();
+			}
+			copy_wormhash(h, workcopy);
+			free(workcopy);
+			workcopy = h;
+		}
+
+		/* check if end of string was reached */
+		if (!*pc) {
+			break;
+		}
+
+		/* now `pc' points to comma if we are here; */
+		/* increment this to the next char */
+		/* but first assert :-> */
+		if (*pc != ',') {
+			ebt_print_error("Something went wrong; no comma...\n");
+			return NULL;
+		}
+		pc++;
+
+		/* again check if end of string was reached; */
+		/* we allow an ending comma */
+		if (!*pc) {
+			break;
+		}
+	}
+	if (!(result = new_wormhash(nmacs))) {
+		ebt_print_memory();
+	}
+	copy_wormhash(result, workcopy);
+	free(workcopy);
+	qsort(&result->pool, result->poolsize,
+			sizeof(struct ebt_mac_wormhash_tuple), fcmp);
+	index_table(result);
+	return result;
+}
+
+#define OPT_DST 0x01
+#define OPT_SRC 0x02
+static int parse(int c, char **argv, int argc,
+		 const struct ebt_u_entry *entry, unsigned int *flags,
+		 struct ebt_entry_match **match)
+{
+	struct ebt_among_info *info =
+	    (struct ebt_among_info *) (*match)->data;
+	struct ebt_mac_wormhash *wh;
+	struct ebt_entry_match *h;
+	int new_size;
+	long flen = 0;
+	int fd = -1;
+
+	switch (c) {
+	case AMONG_DST_F:
+	case AMONG_SRC_F:
+	case AMONG_DST:
+	case AMONG_SRC:
+		if (c == AMONG_DST || c == AMONG_DST_F) {
+			ebt_check_option2(flags, OPT_DST);
+		} else {
+			ebt_check_option2(flags, OPT_SRC);
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == AMONG_DST || c == AMONG_DST_F)
+				info->bitmask |= EBT_AMONG_DST_NEG;
+			else
+				info->bitmask |= EBT_AMONG_SRC_NEG;
+		}
+		if (c == AMONG_DST_F || c == AMONG_SRC_F) {
+			struct stat stats;
+
+			if ((fd = open(optarg, O_RDONLY)) == -1)
+				ebt_print_error("Couldn't open file '%s'", optarg);
+			fstat(fd, &stats);
+			flen = stats.st_size;
+			/* use mmap because the file will probably be big */
+			optarg = mmap(0, flen, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+			if (optarg == MAP_FAILED)
+				ebt_print_error("Couldn't map file to memory");
+			if (optarg[flen-1] != '\n')
+				ebt_print_error("File should end with a newline");
+			if (strchr(optarg, '\n') != optarg+flen-1)
+				ebt_print_error("File should only contain one line");
+			optarg[flen-1] = '\0';
+			if (ebt_errormsg[0] != '\0') {
+				munmap(argv, flen);
+				close(fd);
+				exit(-1);
+			}
+		}
+		wh = create_wormhash(optarg);
+		if (ebt_errormsg[0] != '\0')
+			break;
+
+		new_size = old_size+ebt_mac_wormhash_size(wh);
+		h = malloc(sizeof(struct ebt_entry_match)+EBT_ALIGN(new_size));
+		if (!h)
+			ebt_print_memory();
+		memcpy(h, *match, old_size+sizeof(struct ebt_entry_match));
+		memcpy((char *)h+old_size+sizeof(struct ebt_entry_match), wh,
+		       ebt_mac_wormhash_size(wh));
+		h->match_size = EBT_ALIGN(new_size);
+		info = (struct ebt_among_info *) h->data;
+		if (c == AMONG_DST || c == AMONG_DST_F) {
+			info->wh_dst_ofs = old_size;
+		} else {
+			info->wh_src_ofs = old_size;
+		}
+		old_size = new_size;
+		free(*match);
+		*match = h;
+		free(wh);
+		if (c == AMONG_DST_F || c == AMONG_SRC_F) {
+			munmap(argv, flen);
+			close(fd);
+		}
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+			const struct ebt_entry_match *match,
+			const char *name, unsigned int hookmask,
+			unsigned int time)
+{
+}
+
+#ifdef DEBUG
+static void wormhash_debug(const struct ebt_mac_wormhash *wh)
+{
+	int i;
+
+	printf("poolsize: %d\n", wh->poolsize);
+	for (i = 0; i <= 256; i++) {
+		printf("%02x ", wh->table[i]);
+		if (i % 16 == 15) {
+			printf("\n");
+		}
+	}
+	printf("\n");
+}
+#endif /* DEBUG */
+
+static void wormhash_printout(const struct ebt_mac_wormhash *wh)
+{
+	int i;
+	unsigned char *ip;
+
+	for (i = 0; i < wh->poolsize; i++) {
+		const struct ebt_mac_wormhash_tuple *p;
+
+		p = (const struct ebt_mac_wormhash_tuple *)(&wh->pool[i]);
+		ebt_print_mac(((const unsigned char *) &p->cmp[0]) + 2);
+		if (p->ip) {
+			ip = (unsigned char *) &p->ip;
+			printf("=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+		}
+		printf(",");
+	}
+	printf(" ");
+}
+
+static void print(const struct ebt_u_entry *entry,
+		  const struct ebt_entry_match *match)
+{
+	struct ebt_among_info *info = (struct ebt_among_info *)match->data;
+
+	if (info->wh_dst_ofs) {
+		printf("--among-dst ");
+		if (info->bitmask & EBT_AMONG_DST_NEG) {
+			printf("! ");
+		}
+		wormhash_printout(ebt_among_wh_dst(info));
+	}
+	if (info->wh_src_ofs) {
+		printf("--among-src ");
+		if (info->bitmask & EBT_AMONG_SRC_NEG) {
+			printf("! ");
+		}
+		wormhash_printout(ebt_among_wh_src(info));
+	}
+}
+
+static int compare_wh(const struct ebt_mac_wormhash *aw,
+		      const struct ebt_mac_wormhash *bw)
+{
+	int as, bs;
+
+	as = ebt_mac_wormhash_size(aw);
+	bs = ebt_mac_wormhash_size(bw);
+	if (as != bs)
+		return 0;
+	if (as && memcmp(aw, bw, as))
+		return 0;
+	return 1;
+}
+
+static int compare(const struct ebt_entry_match *m1,
+		   const struct ebt_entry_match *m2)
+{
+	struct ebt_among_info *a = (struct ebt_among_info *) m1->data;
+	struct ebt_among_info *b = (struct ebt_among_info *) m2->data;
+
+	if (!compare_wh(ebt_among_wh_dst(a), ebt_among_wh_dst(b)))
+		return 0;
+	if (!compare_wh(ebt_among_wh_src(a), ebt_among_wh_src(b)))
+		return 0;
+	if (a->bitmask != b->bitmask)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match among_match = {
+	.name 		= "among",
+	.size 		= sizeof(struct ebt_among_info),
+	.help 		= print_help,
+	.init 		= init,
+	.parse 		= parse,
+	.final_check 	= final_check,
+	.print 		= print,
+	.compare 	= compare,
+	.extra_ops 	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&among_match);
+}
diff --git a/extensions/ebt_arp.c b/extensions/ebt_arp.c
new file mode 100644
index 0000000..84b6e90
--- /dev/null
+++ b/extensions/ebt_arp.c
@@ -0,0 +1,368 @@
+/* ebt_arp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Tim Gardner <timg@tpi.com>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/if_ether.h>
+#include <linux/netfilter_bridge/ebt_arp.h>
+
+#define ARP_OPCODE '1'
+#define ARP_HTYPE  '2'
+#define ARP_PTYPE  '3'
+#define ARP_IP_S   '4'
+#define ARP_IP_D   '5'
+#define ARP_MAC_S  '6'
+#define ARP_MAC_D  '7'
+#define ARP_GRAT   '8'
+static const struct option opts[] =
+{
+	{ "arp-opcode"    , required_argument, 0, ARP_OPCODE },
+	{ "arp-op"        , required_argument, 0, ARP_OPCODE },
+	{ "arp-htype"     , required_argument, 0, ARP_HTYPE  },
+	{ "arp-ptype"     , required_argument, 0, ARP_PTYPE  },
+	{ "arp-ip-src"    , required_argument, 0, ARP_IP_S   },
+	{ "arp-ip-dst"    , required_argument, 0, ARP_IP_D   },
+	{ "arp-mac-src"   , required_argument, 0, ARP_MAC_S  },
+	{ "arp-mac-dst"   , required_argument, 0, ARP_MAC_D  },
+	{ "arp-gratuitous",       no_argument, 0, ARP_GRAT   },
+	{ 0 }
+};
+
+#define NUMOPCODES 9
+/* a few names */
+static char *opcodes[] =
+{
+	"Request",
+	"Reply",
+	"Request_Reverse",
+	"Reply_Reverse",
+	"DRARP_Request",
+	"DRARP_Reply",
+	"DRARP_Error",
+	"InARP_Request",
+	"ARP_NAK",
+};
+
+static void print_help()
+{
+	int i;
+
+	printf(
+"arp options:\n"
+"--arp-opcode  [!] opcode        : ARP opcode (integer or string)\n"
+"--arp-htype   [!] type          : ARP hardware type (integer or string)\n"
+"--arp-ptype   [!] type          : ARP protocol type (hexadecimal or string)\n"
+"--arp-ip-src  [!] address[/mask]: ARP IP source specification\n"
+"--arp-ip-dst  [!] address[/mask]: ARP IP target specification\n"
+"--arp-mac-src [!] address[/mask]: ARP MAC source specification\n"
+"--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n"
+"[!] --arp-gratuitous            : ARP gratuitous packet\n"
+" opcode strings: \n");
+	for (i = 0; i < NUMOPCODES; i++)
+		printf(" %d = %s\n", i + 1, opcodes[i]);
+	printf(
+" hardware type string: 1 = Ethernet\n"
+" protocol type string: see "_PATH_ETHERTYPES"\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+
+	arpinfo->invflags = 0;
+	arpinfo->bitmask = 0;
+}
+
+
+#define OPT_OPCODE 0x01
+#define OPT_HTYPE  0x02
+#define OPT_PTYPE  0x04
+#define OPT_IP_S   0x08
+#define OPT_IP_D   0x10
+#define OPT_MAC_S  0x20
+#define OPT_MAC_D  0x40
+#define OPT_GRAT   0x80
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
+	long int i;
+	char *end;
+	uint32_t *addr;
+	uint32_t *mask;
+	unsigned char *maddr;
+	unsigned char *mmask;
+
+	switch (c) {
+	case ARP_OPCODE:
+		ebt_check_option2(flags, OPT_OPCODE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_OPCODE;
+		i = strtol(optarg, &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			for (i = 0; i < NUMOPCODES; i++)
+				if (!strcasecmp(opcodes[i], optarg))
+					break;
+			if (i == NUMOPCODES)
+				ebt_print_error2("Problem with specified ARP opcode");
+			i++;
+		}
+		arpinfo->opcode = htons(i);
+		arpinfo->bitmask |= EBT_ARP_OPCODE;
+		break;
+
+	case ARP_HTYPE:
+		ebt_check_option2(flags, OPT_HTYPE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_HTYPE;
+		i = strtol(optarg, &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			if (!strcasecmp("Ethernet", argv[optind - 1]))
+				i = 1;
+			else
+				ebt_print_error2("Problem with specified ARP hardware type");
+		}
+		arpinfo->htype = htons(i);
+		arpinfo->bitmask |= EBT_ARP_HTYPE;
+		break;
+
+	case ARP_PTYPE:
+	{
+		uint16_t proto;
+
+		ebt_check_option2(flags, OPT_PTYPE);
+		if (ebt_check_inverse2(optarg))
+			arpinfo->invflags |= EBT_ARP_PTYPE;
+
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			struct ethertypeent *ent;
+
+			ent = getethertypebyname(argv[optind - 1]);
+			if (!ent)
+				ebt_print_error2("Problem with specified ARP "
+						"protocol type");
+			proto = ent->e_ethertype;
+
+		} else
+			proto = i;
+		arpinfo->ptype = htons(proto);
+		arpinfo->bitmask |= EBT_ARP_PTYPE;
+		break;
+	}
+
+	case ARP_IP_S:
+	case ARP_IP_D:
+		if (c == ARP_IP_S) {
+			ebt_check_option2(flags, OPT_IP_S);
+			addr = &arpinfo->saddr;
+			mask = &arpinfo->smsk;
+			arpinfo->bitmask |= EBT_ARP_SRC_IP;
+		} else {
+			ebt_check_option2(flags, OPT_IP_D);
+			addr = &arpinfo->daddr;
+			mask = &arpinfo->dmsk;
+			arpinfo->bitmask |= EBT_ARP_DST_IP;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == ARP_IP_S)
+				arpinfo->invflags |= EBT_ARP_SRC_IP;
+			else
+				arpinfo->invflags |= EBT_ARP_DST_IP;
+		}
+		ebt_parse_ip_address(optarg, addr, mask);
+		break;
+
+	case ARP_MAC_S:
+	case ARP_MAC_D:
+		if (c == ARP_MAC_S) {
+			ebt_check_option2(flags, OPT_MAC_S);
+			maddr = arpinfo->smaddr;
+			mmask = arpinfo->smmsk;
+			arpinfo->bitmask |= EBT_ARP_SRC_MAC;
+		} else {
+			ebt_check_option2(flags, OPT_MAC_D);
+			maddr = arpinfo->dmaddr;
+			mmask = arpinfo->dmmsk;
+			arpinfo->bitmask |= EBT_ARP_DST_MAC;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == ARP_MAC_S)
+				arpinfo->invflags |= EBT_ARP_SRC_MAC;
+			else
+				arpinfo->invflags |= EBT_ARP_DST_MAC;
+		}
+		if (ebt_get_mac_and_mask(optarg, maddr, mmask))
+			ebt_print_error2("Problem with ARP MAC address argument");
+		break;
+	case ARP_GRAT:
+		ebt_check_option2(flags, OPT_GRAT);
+		arpinfo->bitmask |= EBT_ARP_GRAT;
+		if (ebt_invert)
+			arpinfo->invflags |= EBT_ARP_GRAT;
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) ||
+	    entry->invflags & EBT_IPROTO)
+		ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
+	int i;
+
+	if (arpinfo->bitmask & EBT_ARP_OPCODE) {
+		int opcode = ntohs(arpinfo->opcode);
+		printf("--arp-op ");
+		if (arpinfo->invflags & EBT_ARP_OPCODE)
+			printf("! ");
+		if (opcode > 0 && opcode <= NUMOPCODES)
+			printf("%s ", opcodes[opcode - 1]);
+		else
+			printf("%d ", opcode);
+	}
+	if (arpinfo->bitmask & EBT_ARP_HTYPE) {
+		printf("--arp-htype ");
+		if (arpinfo->invflags & EBT_ARP_HTYPE)
+			printf("! ");
+		printf("%d ", ntohs(arpinfo->htype));
+	}
+	if (arpinfo->bitmask & EBT_ARP_PTYPE) {
+		struct ethertypeent *ent;
+
+		printf("--arp-ptype ");
+		if (arpinfo->invflags & EBT_ARP_PTYPE)
+			printf("! ");
+		ent = getethertypebynumber(ntohs(arpinfo->ptype));
+		if (!ent)
+			printf("0x%x ", ntohs(arpinfo->ptype));
+		else
+			printf("%s ", ent->e_name);
+	}
+	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+		printf("--arp-ip-src ");
+		if (arpinfo->invflags & EBT_ARP_SRC_IP)
+			printf("! ");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(arpinfo->smsk));
+	}
+	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+		printf("--arp-ip-dst ");
+		if (arpinfo->invflags & EBT_ARP_DST_IP)
+			printf("! ");
+		for (i = 0; i < 4; i++)
+			printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
+			   (i == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk));
+	}
+	if (arpinfo->bitmask & EBT_ARP_SRC_MAC) {
+		printf("--arp-mac-src ");
+		if (arpinfo->invflags & EBT_ARP_SRC_MAC)
+			printf("! ");
+		ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk);
+		printf(" ");
+	}
+	if (arpinfo->bitmask & EBT_ARP_DST_MAC) {
+		printf("--arp-mac-dst ");
+		if (arpinfo->invflags & EBT_ARP_DST_MAC)
+			printf("! ");
+		ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk);
+		printf(" ");
+	}
+	if (arpinfo->bitmask & EBT_ARP_GRAT) {
+		if (arpinfo->invflags & EBT_ARP_GRAT)
+			printf("! ");
+		printf("--arp-gratuitous ");
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data;
+	struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data;
+
+	if (arpinfo1->bitmask != arpinfo2->bitmask)
+		return 0;
+	if (arpinfo1->invflags != arpinfo2->invflags)
+		return 0;
+	if (arpinfo1->bitmask & EBT_ARP_OPCODE) {
+		if (arpinfo1->opcode != arpinfo2->opcode)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_HTYPE) {
+		if (arpinfo1->htype != arpinfo2->htype)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_PTYPE) {
+		if (arpinfo1->ptype != arpinfo2->ptype)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_SRC_IP) {
+		if (arpinfo1->saddr != arpinfo2->saddr)
+			return 0;
+		if (arpinfo1->smsk != arpinfo2->smsk)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_DST_IP) {
+		if (arpinfo1->daddr != arpinfo2->daddr)
+			return 0;
+		if (arpinfo1->dmsk != arpinfo2->dmsk)
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) {
+		if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN))
+			return 0;
+		if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN))
+			return 0;
+	}
+	if (arpinfo1->bitmask & EBT_ARP_DST_MAC) {
+		if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN))
+			return 0;
+		if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN))
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match arp_match =
+{
+	.name		= "arp",
+	.size		= sizeof(struct ebt_arp_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&arp_match);
+}
diff --git a/extensions/ebt_arpreply.c b/extensions/ebt_arpreply.c
new file mode 100644
index 0000000..399868b
--- /dev/null
+++ b/extensions/ebt_arpreply.c
@@ -0,0 +1,139 @@
+/* ebt_arpreply
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ *  August, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include <linux/netfilter_bridge/ebt_arpreply.h>
+
+static int mac_supplied;
+
+#define REPLY_MAC '1'
+#define REPLY_TARGET '2'
+static const struct option opts[] =
+{
+	{ "arpreply-mac" ,    required_argument, 0, REPLY_MAC    },
+	{ "arpreply-target" , required_argument, 0, REPLY_TARGET },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"arpreply target options:\n"
+	" --arpreply-mac address           : source MAC of generated reply\n"
+	" --arpreply-target target         : ACCEPT, DROP, RETURN or CONTINUE\n"
+	"                                    (standard target is DROP)\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	replyinfo->target = EBT_DROP;
+	memset(replyinfo->mac, 0, ETH_ALEN);
+	mac_supplied = 0;
+}
+
+#define OPT_REPLY_MAC     0x01
+#define OPT_REPLY_TARGET  0x02
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case REPLY_MAC:
+		ebt_check_option2(flags, OPT_REPLY_MAC);
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --arpreply-mac mac");
+		memcpy(replyinfo->mac, addr, ETH_ALEN);
+		mac_supplied = 1;
+		break;
+	case REPLY_TARGET:
+		ebt_check_option2(flags, OPT_REPLY_TARGET);
+		if (FILL_TARGET(optarg, replyinfo->target))
+			ebt_print_error2("Illegal --arpreply-target target");
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	if (entry->ethproto != ETH_P_ARP || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For ARP replying the protocol must be specified as ARP");
+	} else if (time == 0 && mac_supplied == 0) {
+		ebt_print_error("No arpreply mac supplied");
+	} else if (BASE_CHAIN && replyinfo->target == EBT_RETURN) {
+		ebt_print_error("--arpreply-target RETURN not allowed on base chain");
+	} else {
+		CLEAR_BASE_CHAIN_BIT;
+		if (strcmp(name, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING))
+			ebt_print_error("arpreply only allowed in PREROUTING");
+	}
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_arpreply_info *replyinfo =
+	   (struct ebt_arpreply_info *)target->data;
+
+	printf("--arpreply-mac ");
+	ebt_print_mac(replyinfo->mac);
+	if (replyinfo->target == EBT_DROP)
+		return;
+	printf(" --arpreply-target %s", TARGET_NAME(replyinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_arpreply_info *replyinfo1 =
+	   (struct ebt_arpreply_info *)t1->data;
+	struct ebt_arpreply_info *replyinfo2 =
+	   (struct ebt_arpreply_info *)t2->data;
+
+	return memcmp(replyinfo1->mac, replyinfo2->mac, ETH_ALEN) == 0
+		&& replyinfo1->target == replyinfo2->target;
+}
+
+static struct ebt_u_target arpreply_target =
+{
+	.name		= "arpreply",
+	.size		= sizeof(struct ebt_arpreply_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&arpreply_target);
+}
diff --git a/extensions/ebt_inat.c b/extensions/ebt_inat.c
new file mode 100644
index 0000000..48f65b4
--- /dev/null
+++ b/extensions/ebt_inat.c
@@ -0,0 +1,386 @@
+/* ebt_inat
+ *
+ * Authors:
+ * Grzegorz Borowiak <grzes@gnu.univ.gda.pl>
+ *
+ * August, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <getopt.h>
+#include <ctype.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_inat.h>
+
+static int s_sub_supplied, d_sub_supplied;
+
+#define NAT_S '1'
+#define NAT_D '1'
+#define NAT_S_SUB '2'
+#define NAT_D_SUB '2'
+#define NAT_S_TARGET '3'
+#define NAT_D_TARGET '3'
+static const struct option opts_s[] =
+{
+	{ "isnat-list"     , required_argument, 0, NAT_S },
+	{ "isnat-sub"      , required_argument, 0, NAT_S_SUB },
+	{ "isnat-default-target"   , required_argument, 0, NAT_S_TARGET },
+	{ 0 }
+};
+
+static const struct option opts_d[] =
+{
+	{ "idnat-list"     , required_argument, 0, NAT_D },
+	{ "idnat-sub"      , required_argument, 0, NAT_D_SUB },
+	{ "idnat-default-target"   , required_argument, 0, NAT_D_TARGET },
+	{ 0 }
+};
+
+static void print_help_common(const char *cas)
+{
+	printf(
+"isnat options:\n"
+" --i%1.1snat-list                    : indexed list of MAC addresses\n"
+" --i%1.1snat-sub                     : /24 subnet to which the rule apply\n"
+" --i%1.1snat-default-target target   : ACCEPT, DROP, RETURN or CONTINUE\n"
+"Indexed list of addresses is as follows:\n"
+"\tlist := chunk\n"
+"\tlist := list chunk\n"
+"\tchunk := pair ','\n"
+"\tpair := index '=' action\n"
+"\taction := mac_addr\n"
+"\taction := mac_addr '+'\n"
+"\taction := '_'\n"
+"where\n"
+"\tindex -- an integer [0..255]\n"
+"\tmac_addr -- a MAC address in format xx:xx:xx:xx:xx:xx\n"
+"If '_' at some index is specified, packets with last %s IP address byte\n"
+"equal to index are DROPped. If there is a MAC address, they are %1.1snatted\n"
+"to this and the target is CONTINUE. If this MAC is followed by '+', the\n"
+"target is ACCEPT.\n"
+"For example,\n"
+"--idnat-list 2=20:21:22:23:24:25,4=_,7=30:31:32:33:34:35,\n"
+"is valid.\n"
+"The subnet MUST be specified. Only packets with 3 first bytes of their\n"
+"%s address are considered. --i%1.1snat-sub parameter must begin with\n"
+"3 integers separated by dots. Only they are considered, the rest is ignored.\n"
+"No matter if you write '192.168.42.', '192.168.42.0', '192.168.42.12',\n"
+"'192.168.42.0/24', '192.168.42.0/23' or '192.168.42.i%1.1snat_sucks!!!',\n"
+"The numbers 192, 168 and 42 are taken and it behaves like a /24 IP subnet.\n"
+"--i%1.1snat-default-target affects only the packet not matching the subnet.\n",
+		cas, cas, cas, cas, cas, cas, cas, cas,
+		cas, cas, cas, cas, cas, cas, cas, cas
+	);
+}
+
+static void print_help_s()
+{
+	print_help_common("src");
+}
+
+static void print_help_d()
+{
+	print_help_common("dest");
+}
+
+static void init_s(struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	s_sub_supplied = 0;
+	memset(natinfo, 0, sizeof(struct ebt_inat_info));
+	natinfo->target = EBT_CONTINUE;
+	return;
+}
+
+static void init_d(struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	d_sub_supplied = 0;
+	memset(natinfo, 0, sizeof(struct ebt_inat_info));
+	natinfo->target = EBT_CONTINUE;
+	return;
+}
+
+static void parse_list(const char *arg, struct ebt_inat_info *info)
+{
+	int i;
+	char c;
+	int count = 0;
+	int now_index = 1;
+	int index;
+	char buf[4];
+	unsigned char mac[6];
+	int ibuf = 0;
+	int imac = 0;
+	int target;
+	memset(buf, 0, 4);
+	i = 0;
+	while (1) {
+		c = arg[i];
+		if (now_index) {
+			if (isdigit(c)) {
+				buf[ibuf++] = c;
+				if (ibuf > 3) {
+					print_error("Index too long at position %d", i);
+				}
+				goto next;
+			}
+			if (c == '=') {
+				if (ibuf == 0) {
+					print_error("Integer index expected before '=' at position %d", i);
+				}
+				buf[ibuf] = 0;
+				ibuf = 0;
+				index = atoi(buf);
+				if (index < 0 || 255 < index) {
+					print_error("Index out of range [0..255], namely %d", index);
+				}
+				now_index = 0;
+				memset(mac, 0, 6);
+				imac = 0;
+				target = EBT_CONTINUE;
+				goto next;
+			}
+			if (c == '\0') {
+				goto next;
+			}
+			print_error("Unexpected '%c' where integer or '=' expected", c);
+		}
+		else {
+			if (isxdigit(c)) {
+				buf[ibuf++] = c;
+				if (ibuf > 2) {
+					print_error("MAC address chunk too long at position %d", i);
+				}
+				goto next;
+			}
+			if (c == ':' || c == ',' || c == '\0') {
+				buf[ibuf] = 0;
+				ibuf = 0;
+				mac[imac++] = strtol(buf, 0, 16);
+				if (c == ',' || c == '\0') {
+					info->a[index].enabled = 1;
+					info->a[index].target = target;
+					memcpy(info->a[index].mac, mac, 6);
+					now_index = 1;
+					count++;
+					goto next;
+				}
+				if (c == ':' && imac >= 6) {
+					print_error("Too many MAC address chunks at position %d", i);
+				}
+				goto next;
+			}
+			if (c == '_') {
+				target = EBT_DROP;
+				goto next;
+			}
+			if (c == '+') {
+				target = EBT_ACCEPT;
+				goto next;
+			}
+			print_error("Unexpected '%c' where hex digit, '_', '+', ',' or end of string expected", c);
+		}
+	next:
+		if (!c) break;
+		i++;
+	}
+	if (count == 0) {
+		print_error("List empty");
+	}
+}
+
+static uint32_t parse_ip(const char *s)
+{
+	int a0, a1, a2;
+	char ip[4];
+	sscanf(s, "%d.%d.%d", &a0, &a1, &a2);
+	ip[0] = a0;
+	ip[1] = a1;
+	ip[2] = a2;
+	ip[3] = 0;
+	return *(uint32_t*)ip;
+}
+
+#define OPT_ISNAT         0x01
+#define OPT_ISNAT_SUB     0x02
+#define OPT_ISNAT_TARGET  0x04
+static int parse_s(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_S:
+		check_option(flags, OPT_ISNAT);
+		parse_list(optarg, natinfo);
+		break;
+	case NAT_S_TARGET:
+		check_option(flags, OPT_ISNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			print_error("Illegal --isnat-default-target target");
+		break;
+	case NAT_S_SUB:
+		natinfo->ip_subnet = parse_ip(optarg);
+		s_sub_supplied = 1;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+#define OPT_IDNAT        0x01
+#define OPT_IDNAT_SUB    0x02
+#define OPT_IDNAT_TARGET 0x04
+static int parse_d(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_D:
+		check_option(flags, OPT_IDNAT);
+		parse_list(optarg, natinfo);
+		break;
+	case NAT_D_TARGET:
+		check_option(flags, OPT_IDNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			print_error("Illegal --idnat-default-target target");
+		break;
+	case NAT_D_SUB:
+		natinfo->ip_subnet = parse_ip(optarg);
+		d_sub_supplied = 1;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+		print_error("--isnat-default-target RETURN not allowed on base chain");
+	CLEAR_BASE_CHAIN_BIT;
+	if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat"))
+		print_error("Wrong chain for isnat");
+	if (time == 0 && s_sub_supplied == 0)
+		print_error("No isnat subnet supplied");
+}
+
+static void final_check_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN)
+		print_error("--idnat-default-target RETURN not allowed on base chain");
+	CLEAR_BASE_CHAIN_BIT;
+	if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
+	   || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")))
+		print_error("Wrong chain for idnat");
+	if (time == 0 && d_sub_supplied == 0)
+		print_error("No idnat subnet supplied");
+}
+
+static void print_list(const struct ebt_inat_info *info)
+{
+	int i;
+	for (i = 0; i < 256; i++) {
+		if (info->a[i].enabled) {
+			printf("%d=", i);
+			if (info->a[i].target == EBT_DROP) {
+				printf("_");
+			}
+			else {
+				if (info->a[i].target == EBT_ACCEPT) {
+					printf("+");
+				}
+				print_mac(info->a[i].mac);
+			}
+			printf(",");
+		}
+	}
+}
+
+static void print_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
+
+	unsigned char sub[4];
+	*(uint32_t*)sub = info->ip_subnet;
+	printf("--isnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
+	printf(" --isnat-list ");
+	print_list(info);
+	printf(" --isnat-default-target %s", TARGET_NAME(info->target));
+}
+
+static void print_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_inat_info *info = (struct ebt_inat_info *)target->data;
+
+	unsigned char sub[4];
+	*(uint32_t*)sub = info->ip_subnet;
+	printf("--idnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]);
+	printf(" --idnat-list ");
+	print_list(info);
+	printf(" --idnat-default-target %s", TARGET_NAME(info->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_inat_info *natinfo1 = (struct ebt_inat_info *)t1->data;
+	struct ebt_inat_info *natinfo2 = (struct ebt_inat_info *)t2->data;
+
+
+	return !memcmp(natinfo1, natinfo2, sizeof(struct ebt_inat_info));
+}
+
+static struct ebt_u_target isnat_target =
+{
+	.name		= EBT_ISNAT_TARGET,
+	.size		= sizeof(struct ebt_inat_info),
+	.help		= print_help_s,
+	.init		= init_s,
+	.parse		= parse_s,
+	.final_check	= final_check_s,
+	.print		= print_s,
+	.compare	= compare,
+	.extra_ops	= opts_s,
+};
+
+static struct ebt_u_target idnat_target =
+{
+	.name		= EBT_IDNAT_TARGET,
+	.size		= sizeof(struct ebt_inat_info),
+	.help		= print_help_d,
+	.init		= init_d,
+	.parse		= parse_d,
+	.final_check	= final_check_d,
+	.print		= print_d,
+	.compare	= compare,
+	.extra_ops	= opts_d,
+};
+
+static void _INIT(void)
+{
+	register_target(&isnat_target);
+	register_target(&idnat_target);
+}
diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
new file mode 100644
index 0000000..faffade
--- /dev/null
+++ b/extensions/ebt_ip.c
@@ -0,0 +1,478 @@
+/* ebt_ip
+ * 
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * Changes:
+ *    added ip-sport and ip-dport; parsing of port arguments is
+ *    based on code from iptables-1.2.7a
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_ip.h>
+
+#define IP_SOURCE '1'
+#define IP_DEST   '2'
+#define IP_myTOS  '3' /* include/bits/in.h seems to already define IP_TOS */
+#define IP_PROTO  '4'
+#define IP_SPORT  '5'
+#define IP_DPORT  '6'
+#define IP_ICMP   '7'
+#define IP_IGMP   '8'
+
+static const struct option opts[] =
+{
+	{ "ip-source"           , required_argument, 0, IP_SOURCE },
+	{ "ip-src"              , required_argument, 0, IP_SOURCE },
+	{ "ip-destination"      , required_argument, 0, IP_DEST   },
+	{ "ip-dst"              , required_argument, 0, IP_DEST   },
+	{ "ip-tos"              , required_argument, 0, IP_myTOS  },
+	{ "ip-protocol"         , required_argument, 0, IP_PROTO  },
+	{ "ip-proto"            , required_argument, 0, IP_PROTO  },
+	{ "ip-source-port"      , required_argument, 0, IP_SPORT  },
+	{ "ip-sport"            , required_argument, 0, IP_SPORT  },
+	{ "ip-destination-port" , required_argument, 0, IP_DPORT  },
+	{ "ip-dport"            , required_argument, 0, IP_DPORT  },
+	{ "ip-icmp-type"        , required_argument, 0, IP_ICMP   },
+	{ "ip-igmp-type"        , required_argument, 0, IP_IGMP   },
+	{ 0 }
+};
+
+static const struct ebt_icmp_names icmp_codes[] = {
+	{ "echo-reply", 0, 0, 0xFF },
+	/* Alias */ { "pong", 0, 0, 0xFF },
+
+	{ "destination-unreachable", 3, 0, 0xFF },
+	{   "network-unreachable", 3, 0, 0 },
+	{   "host-unreachable", 3, 1, 1 },
+	{   "protocol-unreachable", 3, 2, 2 },
+	{   "port-unreachable", 3, 3, 3 },
+	{   "fragmentation-needed", 3, 4, 4 },
+	{   "source-route-failed", 3, 5, 5 },
+	{   "network-unknown", 3, 6, 6 },
+	{   "host-unknown", 3, 7, 7 },
+	{   "network-prohibited", 3, 9, 9 },
+	{   "host-prohibited", 3, 10, 10 },
+	{   "TOS-network-unreachable", 3, 11, 11 },
+	{   "TOS-host-unreachable", 3, 12, 12 },
+	{   "communication-prohibited", 3, 13, 13 },
+	{   "host-precedence-violation", 3, 14, 14 },
+	{   "precedence-cutoff", 3, 15, 15 },
+
+	{ "source-quench", 4, 0, 0xFF },
+
+	{ "redirect", 5, 0, 0xFF },
+	{   "network-redirect", 5, 0, 0 },
+	{   "host-redirect", 5, 1, 1 },
+	{   "TOS-network-redirect", 5, 2, 2 },
+	{   "TOS-host-redirect", 5, 3, 3 },
+
+	{ "echo-request", 8, 0, 0xFF },
+	/* Alias */ { "ping", 8, 0, 0xFF },
+
+	{ "router-advertisement", 9, 0, 0xFF },
+
+	{ "router-solicitation", 10, 0, 0xFF },
+
+	{ "time-exceeded", 11, 0, 0xFF },
+	/* Alias */ { "ttl-exceeded", 11, 0, 0xFF },
+	{   "ttl-zero-during-transit", 11, 0, 0 },
+	{   "ttl-zero-during-reassembly", 11, 1, 1 },
+
+	{ "parameter-problem", 12, 0, 0xFF },
+	{   "ip-header-bad", 12, 0, 0 },
+	{   "required-option-missing", 12, 1, 1 },
+
+	{ "timestamp-request", 13, 0, 0xFF },
+
+	{ "timestamp-reply", 14, 0, 0xFF },
+
+	{ "address-mask-request", 17, 0, 0xFF },
+
+	{ "address-mask-reply", 18, 0, 0xFF }
+};
+
+static const struct ebt_icmp_names igmp_types[] = {
+	{ "membership-query", 0x11 },
+	{ "membership-report-v1", 0x12 },
+	{ "membership-report-v2", 0x16 },
+	{ "leave-group", 0x17 },
+	{ "membership-report-v3", 0x22 },
+};
+
+/* put the mask into 4 bytes */
+/* transform a protocol and service name into a port number */
+static uint16_t parse_port(const char *protocol, const char *name)
+{
+	struct servent *service;
+	char *end;
+	int port;
+
+	port = strtol(name, &end, 10);
+	if (*end != '\0') {
+		if (protocol && 
+		    (service = getservbyname(name, protocol)) != NULL)
+			return ntohs(service->s_port);
+	}
+	else if (port >= 0 || port <= 0xFFFF) {
+		return port;
+	}
+	ebt_print_error("Problem with specified %s port '%s'", 
+			protocol?protocol:"", name);
+	return 0;
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+	
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = parse_port(protocol, buffer);
+	else {
+		*cp = '\0';
+		cp++;
+		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		
+		if (ports[0] > ports[1])
+			ebt_print_error("Invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+static void print_port_range(uint16_t *ports)
+{
+	if (ports[0] == ports[1])
+		printf("%d ", ports[0]);
+	else
+		printf("%d:%d ", ports[0], ports[1]);
+}
+
+static void print_help()
+{
+	printf(
+"ip options:\n"
+"--ip-src    [!] address[/mask]: ip source specification\n"
+"--ip-dst    [!] address[/mask]: ip destination specification\n"
+"--ip-tos    [!] tos           : ip tos specification\n"
+"--ip-proto  [!] protocol      : ip protocol specification\n"
+"--ip-sport  [!] port[:port]   : tcp/udp source port or port range\n"
+"--ip-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
+"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n"
+"--ip-igmp-type [!] type[:type]               : igmp type or type range\n");
+
+	printf("\nValid ICMP Types:\n");
+	ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
+	printf("\nValid IGMP Types:\n");
+	ebt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types));
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
+	ipinfo->invflags = 0;
+	ipinfo->bitmask = 0;
+}
+
+#define OPT_SOURCE 0x01
+#define OPT_DEST   0x02
+#define OPT_TOS    0x04
+#define OPT_PROTO  0x08
+#define OPT_SPORT  0x10
+#define OPT_DPORT  0x20
+#define OPT_ICMP   0x40
+#define OPT_IGMP   0x80
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		ebt_check_option2(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP_SOURCE;
+
+	case IP_DEST:
+		if (c == IP_DEST) {
+			ebt_check_option2(flags, OPT_DEST);
+			ipinfo->bitmask |= EBT_IP_DEST;
+		}
+		if (ebt_check_inverse2(optarg)) {
+			if (c == IP_SOURCE)
+				ipinfo->invflags |= EBT_IP_SOURCE;
+			else
+				ipinfo->invflags |= EBT_IP_DEST;
+		}
+		if (c == IP_SOURCE)
+			ebt_parse_ip_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
+		else
+			ebt_parse_ip_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
+		break;
+
+	case IP_SPORT:
+	case IP_DPORT:
+		if (c == IP_SPORT) {
+			ebt_check_option2(flags, OPT_SPORT);
+			ipinfo->bitmask |= EBT_IP_SPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP_SPORT;
+		} else {
+			ebt_check_option2(flags, OPT_DPORT);
+			ipinfo->bitmask |= EBT_IP_DPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP_DPORT;
+		}
+		if (c == IP_SPORT)
+			parse_port_range(NULL, optarg, ipinfo->sport);
+		else
+			parse_port_range(NULL, optarg, ipinfo->dport);
+		break;
+
+	case IP_ICMP:
+		ebt_check_option2(flags, OPT_ICMP);
+		ipinfo->bitmask |= EBT_IP_ICMP;
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_ICMP;
+		if (ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg,
+				   ipinfo->icmp_type, ipinfo->icmp_code))
+			return 0;
+		break;
+
+	case IP_IGMP:
+		ebt_check_option2(flags, OPT_IGMP);
+		ipinfo->bitmask |= EBT_IP_IGMP;
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_IGMP;
+		if (ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg,
+				   ipinfo->igmp_type, NULL))
+			return 0;
+		break;
+
+	case IP_myTOS:
+		ebt_check_option2(flags, OPT_TOS);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_TOS;
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i > 255 || *end != '\0')
+			ebt_print_error2("Problem with specified IP tos");
+		ipinfo->tos = i;
+		ipinfo->bitmask |= EBT_IP_TOS;
+		break;
+
+	case IP_PROTO:
+		ebt_check_option2(flags, OPT_PROTO);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP_PROTO;
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			struct protoent *pe;
+
+			pe = getprotobyname(optarg);
+			if (pe == NULL)
+				ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
+			ipinfo->protocol = pe->p_proto;
+		} else {
+			ipinfo->protocol = (unsigned char) i;
+		}
+		ipinfo->bitmask |= EBT_IP_PROTO;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+
+	if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For IP filtering the protocol must be "
+		            "specified as IPv4");
+	} else if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
+		(!(ipinfo->bitmask & EBT_IP_PROTO) ||
+		ipinfo->invflags & EBT_IP_PROTO ||
+		(ipinfo->protocol!=IPPROTO_TCP &&
+		 ipinfo->protocol!=IPPROTO_UDP &&
+		 ipinfo->protocol!=IPPROTO_SCTP &&
+		 ipinfo->protocol!=IPPROTO_DCCP))) {
+		ebt_print_error("For port filtering the IP protocol must be "
+				"either 6 (tcp), 17 (udp), 33 (dccp) or "
+				"132 (sctp)");
+	} else if ((ipinfo->bitmask & EBT_IP_ICMP) &&
+	         (!(ipinfo->bitmask & EBT_IP_PROTO) ||
+	            ipinfo->invflags & EBT_IP_PROTO ||
+	            ipinfo->protocol != IPPROTO_ICMP)) {
+		ebt_print_error("For ICMP filtering the IP protocol must be "
+				"1 (icmp)");
+	} else if ((ipinfo->bitmask & EBT_IP_IGMP) &&
+	         (!(ipinfo->bitmask & EBT_IP_PROTO) ||
+	            ipinfo->invflags & EBT_IP_PROTO ||
+	            ipinfo->protocol != IPPROTO_IGMP)) {
+		ebt_print_error("For IGMP filtering the IP protocol must be "
+				"2 (igmp)");
+	}
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
+	int j;
+
+	if (ipinfo->bitmask & EBT_IP_SOURCE) {
+		printf("--ip-src ");
+		if (ipinfo->invflags & EBT_IP_SOURCE)
+			printf("! ");
+		for (j = 0; j < 4; j++)
+			printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
+			   (j == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(ipinfo->smsk));
+	}
+	if (ipinfo->bitmask & EBT_IP_DEST) {
+		printf("--ip-dst ");
+		if (ipinfo->invflags & EBT_IP_DEST)
+			printf("! ");
+		for (j = 0; j < 4; j++)
+			printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
+			   (j == 3) ? "" : ".");
+		printf("%s ", ebt_mask_to_dotted(ipinfo->dmsk));
+	}
+	if (ipinfo->bitmask & EBT_IP_TOS) {
+		printf("--ip-tos ");
+		if (ipinfo->invflags & EBT_IP_TOS)
+			printf("! ");
+		printf("0x%02X ", ipinfo->tos);
+	}
+	if (ipinfo->bitmask & EBT_IP_PROTO) {
+		struct protoent *pe;
+
+		printf("--ip-proto ");
+		if (ipinfo->invflags & EBT_IP_PROTO)
+			printf("! ");
+		pe = getprotobynumber(ipinfo->protocol);
+		if (pe == NULL) {
+			printf("%d ", ipinfo->protocol);
+		} else {
+			printf("%s ", pe->p_name);
+		}
+	}
+	if (ipinfo->bitmask & EBT_IP_SPORT) {
+		printf("--ip-sport ");
+		if (ipinfo->invflags & EBT_IP_SPORT)
+			printf("! ");
+		print_port_range(ipinfo->sport);
+	}
+	if (ipinfo->bitmask & EBT_IP_DPORT) {
+		printf("--ip-dport ");
+		if (ipinfo->invflags & EBT_IP_DPORT)
+			printf("! ");
+		print_port_range(ipinfo->dport);
+	}
+	if (ipinfo->bitmask & EBT_IP_ICMP) {
+		printf("--ip-icmp-type ");
+		if (ipinfo->invflags & EBT_IP_ICMP)
+			printf("! ");
+		ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
+				    ipinfo->icmp_type, ipinfo->icmp_code);
+	}
+	if (ipinfo->bitmask & EBT_IP_IGMP) {
+		printf("--ip-igmp-type ");
+		if (ipinfo->invflags & EBT_IP_IGMP)
+			printf("! ");
+		ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
+				    ipinfo->igmp_type, NULL);
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
+	struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
+
+	if (ipinfo1->bitmask != ipinfo2->bitmask)
+		return 0;
+	if (ipinfo1->invflags != ipinfo2->invflags)
+		return 0;
+	if (ipinfo1->bitmask & EBT_IP_SOURCE) {
+		if (ipinfo1->saddr != ipinfo2->saddr)
+			return 0;
+		if (ipinfo1->smsk != ipinfo2->smsk)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_DEST) {
+		if (ipinfo1->daddr != ipinfo2->daddr)
+			return 0;
+		if (ipinfo1->dmsk != ipinfo2->dmsk)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_TOS) {
+		if (ipinfo1->tos != ipinfo2->tos)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_PROTO) {
+		if (ipinfo1->protocol != ipinfo2->protocol)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_SPORT) {
+		if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
+		   ipinfo1->sport[1] != ipinfo2->sport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_DPORT) {
+		if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
+		   ipinfo1->dport[1] != ipinfo2->dport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_ICMP) {
+		if (ipinfo1->icmp_type[0] != ipinfo2->icmp_type[0] ||
+		    ipinfo1->icmp_type[1] != ipinfo2->icmp_type[1] ||
+		    ipinfo1->icmp_code[0] != ipinfo2->icmp_code[0] ||
+		    ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP_IGMP) {
+		if (ipinfo1->igmp_type[0] != ipinfo2->igmp_type[0] ||
+		    ipinfo1->igmp_type[1] != ipinfo2->igmp_type[1])
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match ip_match =
+{
+	.name		= "ip",
+	.size		= sizeof(struct ebt_ip_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&ip_match);
+}
diff --git a/extensions/ebt_ip6.c b/extensions/ebt_ip6.c
new file mode 100644
index 0000000..17a4303
--- /dev/null
+++ b/extensions/ebt_ip6.c
@@ -0,0 +1,419 @@
+/* ebt_ip6
+ * 
+ * Authors:
+ * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
+ * Manohar Castelino <manohar.castelino@intel.com>
+ *
+ * Summary:
+ * This is just a modification of the IPv4 code written by 
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * with the changes required to support IPv6
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_ip6.h>
+
+
+
+#define IP_SOURCE '1'
+#define IP_DEST   '2'
+#define IP_TCLASS '3'
+#define IP_PROTO  '4'
+#define IP_SPORT  '5'
+#define IP_DPORT  '6'
+#define IP_ICMP6  '7'
+
+static const struct option opts[] =
+{
+	{ "ip6-source"           , required_argument, 0, IP_SOURCE },
+	{ "ip6-src"              , required_argument, 0, IP_SOURCE },
+	{ "ip6-destination"      , required_argument, 0, IP_DEST   },
+	{ "ip6-dst"              , required_argument, 0, IP_DEST   },
+	{ "ip6-traffic-class"    , required_argument, 0, IP_TCLASS },
+	{ "ip6-tclass"           , required_argument, 0, IP_TCLASS },
+	{ "ip6-protocol"         , required_argument, 0, IP_PROTO  },
+	{ "ip6-proto"            , required_argument, 0, IP_PROTO  },
+	{ "ip6-source-port"      , required_argument, 0, IP_SPORT  },
+	{ "ip6-sport"            , required_argument, 0, IP_SPORT  },
+	{ "ip6-destination-port" , required_argument, 0, IP_DPORT  },
+	{ "ip6-dport"            , required_argument, 0, IP_DPORT  },
+	{ "ip6-icmp-type"	 , required_argument, 0, IP_ICMP6  },
+	{ 0 }
+};
+
+
+static const struct ebt_icmp_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 },
+};
+
+/* transform a protocol and service name into a port number */
+static uint16_t parse_port(const char *protocol, const char *name)
+{
+	struct servent *service;
+	char *end;
+	int port;
+
+	port = strtol(name, &end, 10);
+	if (*end != '\0') {
+		if (protocol && 
+		    (service = getservbyname(name, protocol)) != NULL)
+			return ntohs(service->s_port);
+	}
+	else if (port >= 0 || port <= 0xFFFF) {
+		return port;
+	}
+	ebt_print_error("Problem with specified %s port '%s'", 
+			protocol?protocol:"", name);
+	return 0;
+}
+
+static void
+parse_port_range(const char *protocol, const char *portstring, uint16_t *ports)
+{
+	char *buffer;
+	char *cp;
+	
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL)
+		ports[0] = ports[1] = parse_port(protocol, buffer);
+	else {
+		*cp = '\0';
+		cp++;
+		ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
+		if (ebt_errormsg[0] != '\0')
+			return;
+		
+		if (ports[0] > ports[1])
+			ebt_print_error("Invalid portrange (min > max)");
+	}
+	free(buffer);
+}
+
+static void print_port_range(uint16_t *ports)
+{
+	if (ports[0] == ports[1])
+		printf("%d ", ports[0]);
+	else
+		printf("%d:%d ", ports[0], ports[1]);
+}
+
+static void print_help()
+{
+	printf(
+"ip6 options:\n"
+"--ip6-src    [!] address[/mask]: ipv6 source specification\n"
+"--ip6-dst    [!] address[/mask]: ipv6 destination specification\n"
+"--ip6-tclass [!] tclass        : ipv6 traffic class specification\n"
+"--ip6-proto  [!] protocol      : ipv6 protocol specification\n"
+"--ip6-sport  [!] port[:port]   : tcp/udp source port or port range\n"
+"--ip6-dport  [!] port[:port]   : tcp/udp destination port or port range\n"
+"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
+
+	printf("\nValid ICMPv6 Types:\n");
+	ebt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	ipinfo->invflags = 0;
+	ipinfo->bitmask = 0;
+	memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
+	memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
+	memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
+	memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
+}
+
+#define OPT_SOURCE 0x01
+#define OPT_DEST   0x02
+#define OPT_TCLASS 0x04
+#define OPT_PROTO  0x08
+#define OPT_SPORT  0x10
+#define OPT_DPORT  0x20
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		ebt_check_option2(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP6_SOURCE;
+		if (ebt_check_inverse2(optarg)) {
+		    ipinfo->invflags |= EBT_IP6_SOURCE;
+		}
+		ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
+		break;
+
+	case IP_DEST:
+		ebt_check_option2(flags, OPT_DEST);
+		ipinfo->bitmask |= EBT_IP6_DEST;
+		if (ebt_check_inverse2(optarg)) {
+			ipinfo->invflags |= EBT_IP6_DEST;
+		}
+		ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
+		break;
+
+	case IP_SPORT:
+	case IP_DPORT:
+		if (c == IP_SPORT) {
+			ebt_check_option2(flags, OPT_SPORT);
+			ipinfo->bitmask |= EBT_IP6_SPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP6_SPORT;
+		} else {
+			ebt_check_option2(flags, OPT_DPORT);
+			ipinfo->bitmask |= EBT_IP6_DPORT;
+			if (ebt_check_inverse2(optarg))
+				ipinfo->invflags |= EBT_IP6_DPORT;
+		}
+		if (c == IP_SPORT)
+			parse_port_range(NULL, optarg, ipinfo->sport);
+		else
+			parse_port_range(NULL, optarg, ipinfo->dport);
+		break;
+
+	case IP_ICMP6:
+		ebt_check_option2(flags, EBT_IP6_ICMP6);
+		ipinfo->bitmask |= EBT_IP6_ICMP6;
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_ICMP6;
+		if (ebt_parse_icmp(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
+				   optarg, ipinfo->icmpv6_type,
+				   ipinfo->icmpv6_code))
+			return 0;
+		break;
+
+	case IP_TCLASS:
+		ebt_check_option2(flags, OPT_TCLASS);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_TCLASS;
+		i = strtol(optarg, &end, 16);
+		if (i < 0 || i > 255 || *end != '\0')
+			ebt_print_error2("Problem with specified IPv6 traffic class");
+		ipinfo->tclass = i;
+		ipinfo->bitmask |= EBT_IP6_TCLASS;
+		break;
+
+	case IP_PROTO:
+		ebt_check_option2(flags, OPT_PROTO);
+		if (ebt_check_inverse2(optarg))
+			ipinfo->invflags |= EBT_IP6_PROTO;
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			struct protoent *pe;
+
+			pe = getprotobyname(optarg);
+			if (pe == NULL)
+				ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
+			ipinfo->protocol = pe->p_proto;
+		} else {
+			ipinfo->protocol = (unsigned char) i;
+		}
+		ipinfo->bitmask |= EBT_IP6_PROTO;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) {
+		ebt_print_error("For IPv6 filtering the protocol must be "
+		            "specified as IPv6");
+	} else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) &&
+		(!(ipinfo->bitmask & EBT_IP6_PROTO) ||
+		ipinfo->invflags & EBT_IP6_PROTO ||
+		(ipinfo->protocol!=IPPROTO_TCP &&
+		 ipinfo->protocol!=IPPROTO_UDP &&
+		 ipinfo->protocol!=IPPROTO_SCTP &&
+		 ipinfo->protocol!=IPPROTO_DCCP)))
+		ebt_print_error("For port filtering the IP protocol must be "
+				"either 6 (tcp), 17 (udp), 33 (dccp) or "
+				"132 (sctp)");
+	if ((ipinfo->bitmask & EBT_IP6_ICMP6) &&
+	  (!(ipinfo->bitmask & EBT_IP6_PROTO) ||
+	     ipinfo->invflags & EBT_IP6_PROTO ||
+	     ipinfo->protocol != IPPROTO_ICMPV6))
+		ebt_print_error("For ipv6-icmp filtering the IP protocol must be "
+				"58 (ipv6-icmp)");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
+
+	if (ipinfo->bitmask & EBT_IP6_SOURCE) {
+		printf("--ip6-src ");
+		if (ipinfo->invflags & EBT_IP6_SOURCE)
+			printf("! ");
+		printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr));
+		printf("%s ", ebt_ip6_mask_to_string(&ipinfo->smsk));
+	}
+	if (ipinfo->bitmask & EBT_IP6_DEST) {
+		printf("--ip6-dst ");
+		if (ipinfo->invflags & EBT_IP6_DEST)
+			printf("! ");
+		printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr));
+		printf("%s ", ebt_ip6_mask_to_string(&ipinfo->dmsk));
+	}
+	if (ipinfo->bitmask & EBT_IP6_TCLASS) {
+		printf("--ip6-tclass ");
+		if (ipinfo->invflags & EBT_IP6_TCLASS)
+			printf("! ");
+		printf("0x%02X ", ipinfo->tclass);
+	}
+	if (ipinfo->bitmask & EBT_IP6_PROTO) {
+		struct protoent *pe;
+
+		printf("--ip6-proto ");
+		if (ipinfo->invflags & EBT_IP6_PROTO)
+			printf("! ");
+		pe = getprotobynumber(ipinfo->protocol);
+		if (pe == NULL) {
+			printf("%d ", ipinfo->protocol);
+		} else {
+			printf("%s ", pe->p_name);
+		}
+	}
+	if (ipinfo->bitmask & EBT_IP6_SPORT) {
+		printf("--ip6-sport ");
+		if (ipinfo->invflags & EBT_IP6_SPORT)
+			printf("! ");
+		print_port_range(ipinfo->sport);
+	}
+	if (ipinfo->bitmask & EBT_IP6_DPORT) {
+		printf("--ip6-dport ");
+		if (ipinfo->invflags & EBT_IP6_DPORT)
+			printf("! ");
+		print_port_range(ipinfo->dport);
+	}
+	if (ipinfo->bitmask & EBT_IP6_ICMP6) {
+		printf("--ip6-icmp-type ");
+		if (ipinfo->invflags & EBT_IP6_ICMP6)
+			printf("! ");
+		ebt_print_icmp_type(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
+				    ipinfo->icmpv6_type, ipinfo->icmpv6_code);
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data;
+	struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data;
+
+	if (ipinfo1->bitmask != ipinfo2->bitmask)
+		return 0;
+	if (ipinfo1->invflags != ipinfo2->invflags)
+		return 0;
+	if (ipinfo1->bitmask & EBT_IP6_SOURCE) {
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr))
+			return 0;
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk))
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_DEST) {
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr))
+			return 0;
+		if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk))
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_TCLASS) {
+		if (ipinfo1->tclass != ipinfo2->tclass)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_PROTO) {
+		if (ipinfo1->protocol != ipinfo2->protocol)
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_SPORT) {
+		if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
+		   ipinfo1->sport[1] != ipinfo2->sport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_DPORT) {
+		if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
+		   ipinfo1->dport[1] != ipinfo2->dport[1])
+			return 0;
+	}
+	if (ipinfo1->bitmask & EBT_IP6_ICMP6) {
+		if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] ||
+		    ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] ||
+		    ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] ||
+		    ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1])
+			return 0;
+	}
+	return 1;
+}
+
+static struct ebt_u_match ip6_match =
+{
+	.name		= EBT_IP6_MATCH,
+	.size		= sizeof(struct ebt_ip6_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&ip6_match);
+}
diff --git a/extensions/ebt_limit.c b/extensions/ebt_limit.c
new file mode 100644
index 0000000..1fe9d84
--- /dev/null
+++ b/extensions/ebt_limit.c
@@ -0,0 +1,218 @@
+/* ebt_limit
+ *
+ * Authors:
+ * Tom Marshall <tommy@home.tig-grr.com>
+ *
+ * Mostly copied from iptables' limit match.
+ *
+ * September, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_limit.h>
+
+#define EBT_LIMIT_AVG	"3/hour"
+#define EBT_LIMIT_BURST	5
+
+static int string_to_number(const char *s, unsigned int min, unsigned int max,
+   unsigned int *ret)
+{
+	long number;
+	char *end;
+
+	errno = 0;
+	number = strtol(s, &end, 0);
+	if (*end == '\0' && end != s) {
+		if (errno != ERANGE && min <= number && number <= max) {
+			*ret = number;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+#define FLAG_LIMIT		0x01
+#define FLAG_LIMIT_BURST	0x02
+#define ARG_LIMIT		'1'
+#define ARG_LIMIT_BURST		'2'
+
+static const struct option opts[] =
+{
+	{ "limit",		required_argument, 0, ARG_LIMIT },
+	{ "limit-burst",	required_argument, 0, ARG_LIMIT_BURST },
+	{ 0 }
+};
+
+static void print_help(void)
+{
+	printf(
+"limit options:\n"
+"--limit avg                   : max average match rate: default "EBT_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, -1 < number < 10001,\n"
+"                                default %u\n", EBT_LIMIT_BURST);
+}
+
+static int parse_rate(const char *rate, uint32_t *val)
+{
+	const char *delim;
+	uint32_t r;
+	uint32_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 > EBT_LIMIT_SCALE)
+		return 0;
+
+	*val = EBT_LIMIT_SCALE * mult / r;
+	return 1;
+}
+
+/* Initialize the match. */
+static void init(struct ebt_entry_match *m)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)m->data;
+
+	parse_rate(EBT_LIMIT_AVG, &r->avg);
+	r->burst = EBT_LIMIT_BURST;
+}
+
+/* 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");
+*/
+
+static int parse(int c, char **argv, int argc,
+      const struct ebt_u_entry *entry,
+      unsigned int *flags,
+      struct ebt_entry_match **match)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data;
+	unsigned int num;
+
+	switch(c) {
+	case ARG_LIMIT:
+		ebt_check_option2(flags, FLAG_LIMIT);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --limit");
+		if (!parse_rate(optarg, &r->avg))
+			ebt_print_error2("bad rate `%s'", optarg);
+		break;
+
+	case ARG_LIMIT_BURST:
+		ebt_check_option2(flags, FLAG_LIMIT_BURST);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --limit-burst");
+		if (string_to_number(optarg, 0, 10000, &num) == -1)
+			ebt_print_error2("bad --limit-burst `%s'", optarg);
+		r->burst = num;
+		break;
+
+	default:
+		return 0;
+	}
+
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+struct rates
+{
+	const char *name;
+	uint32_t mult;
+};
+
+static struct rates g_rates[] =
+{
+	{ "day", EBT_LIMIT_SCALE*24*60*60 },
+	{ "hour", EBT_LIMIT_SCALE*60*60 },
+	{ "min", EBT_LIMIT_SCALE*60 },
+	{ "sec", EBT_LIMIT_SCALE }
+};
+
+static void print_rate(uint32_t period)
+{
+	unsigned int i;
+
+	for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++)
+		if (period > g_rates[i].mult ||
+		    g_rates[i].mult/period < g_rates[i].mult%period)
+			break;
+
+	printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name);
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_limit_info *r = (struct ebt_limit_info *)match->data;
+
+	printf("--limit ");
+	print_rate(r->avg);
+	printf("--limit-burst %u ", r->burst);
+}
+
+static int compare(const struct ebt_entry_match* m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_limit_info* li1 = (struct ebt_limit_info*)m1->data;
+	struct ebt_limit_info* li2 = (struct ebt_limit_info*)m2->data;
+
+	if (li1->avg != li2->avg)
+		return 0;
+
+	if (li1->burst != li2->burst)
+		return 0;
+
+	return 1;
+}
+
+static struct ebt_u_match limit_match =
+{
+	.name		= "limit",
+	.size		= sizeof(struct ebt_limit_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&limit_match);
+}
diff --git a/extensions/ebt_log.c b/extensions/ebt_log.c
new file mode 100644
index 0000000..b5d3232
--- /dev/null
+++ b/extensions/ebt_log.c
@@ -0,0 +1,223 @@
+/* ebt_log
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_log.h>
+
+/*
+ * copied from syslog.h
+ * used for the LOG target
+ */
+#define	LOG_EMERG	0 /* system is unusable               */
+#define	LOG_ALERT	1 /* action must be taken immediately */
+#define	LOG_CRIT	2 /* critical conditions              */
+#define	LOG_ERR		3 /* error conditions                 */
+#define	LOG_WARNING	4 /* warning conditions               */
+#define	LOG_NOTICE	5 /* normal but significant condition */
+#define	LOG_INFO	6 /* informational                    */
+#define	LOG_DEBUG	7 /* debug-level messages             */
+
+#define LOG_DEFAULT_LEVEL LOG_INFO
+
+typedef struct _code {
+	char *c_name;
+	int c_val;
+} CODE;
+
+static CODE eight_priority[] = {
+	{ "emerg", LOG_EMERG },
+	{ "alert", LOG_ALERT },
+	{ "crit", LOG_CRIT },
+	{ "error", LOG_ERR },
+	{ "warning", LOG_WARNING },
+	{ "notice", LOG_NOTICE },
+	{ "info", LOG_INFO },
+	{ "debug", LOG_DEBUG }
+};
+
+static int name_to_loglevel(char* arg)
+{
+	int i;
+	
+	for (i = 0; i < 8; i++)
+		if (!strcmp(arg, eight_priority[i].c_name))
+			return eight_priority[i].c_val;
+	/* return bad loglevel */
+	return 9;
+}
+
+#define LOG_PREFIX '1'
+#define LOG_LEVEL  '2'
+#define LOG_ARP    '3'
+#define LOG_IP     '4'
+#define LOG_LOG    '5'
+#define LOG_IP6    '6'
+static const struct option opts[] =
+{
+	{ "log-prefix", required_argument, 0, LOG_PREFIX },
+	{ "log-level" , required_argument, 0, LOG_LEVEL  },
+	{ "log-arp"   , no_argument      , 0, LOG_ARP    },
+	{ "log-ip"    , no_argument      , 0, LOG_IP     },
+	{ "log"       , no_argument      , 0, LOG_LOG    },
+	{ "log-ip6"   , no_argument      , 0, LOG_IP6    },
+	{ 0 }
+};
+
+static void print_help()
+{
+	int i;
+
+	printf(
+"log options:\n"
+"--log               : use this if you're not specifying anything\n"
+"--log-level level   : level = [1-8] or a string\n"
+"--log-prefix prefix : max. %d chars.\n"
+"--log-ip            : put ip info. in the log for ip packets\n"
+"--log-arp           : put (r)arp info. in the log for (r)arp packets\n"
+"--log-ip6           : put ip6 info. in the log for ip6 packets\n"
+	, EBT_LOG_PREFIX_SIZE - 1);
+	printf("levels:\n");
+	for (i = 0; i < 8; i++)
+		printf("%d = %s\n", eight_priority[i].c_val,
+		   eight_priority[i].c_name);
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+
+	loginfo->bitmask = 0;
+	loginfo->prefix[0] = '\0';
+	loginfo->loglevel = LOG_NOTICE;
+}
+
+#define OPT_PREFIX 0x01
+#define OPT_LEVEL  0x02
+#define OPT_ARP    0x04
+#define OPT_IP     0x08
+#define OPT_LOG    0x10
+#define OPT_IP6    0x20
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_watcher **watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)(*watcher)->data;
+	long int i;
+	char *end;
+
+	switch (c) {
+	case LOG_PREFIX:
+		ebt_check_option2(flags, OPT_PREFIX);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-prefix");
+		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+			ebt_print_error2("Prefix too long");
+		if (strchr(optarg, '\"'))
+			ebt_print_error2("Use of \\\" is not allowed in the prefix");
+		strcpy((char *)loginfo->prefix, (char *)optarg);
+		break;
+
+	case LOG_LEVEL:
+		ebt_check_option2(flags, OPT_LEVEL);
+		i = strtol(optarg, &end, 16);
+		if (*end != '\0' || i < 0 || i > 7)
+			loginfo->loglevel = name_to_loglevel(optarg);
+		else
+			loginfo->loglevel = i;
+		if (loginfo->loglevel == 9)
+			ebt_print_error2("Problem with the log-level");
+		break;
+
+	case LOG_IP:
+		ebt_check_option2(flags, OPT_IP);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-ip");
+		loginfo->bitmask |= EBT_LOG_IP;
+		break;
+
+	case LOG_ARP:
+		ebt_check_option2(flags, OPT_ARP);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-arp");
+		loginfo->bitmask |= EBT_LOG_ARP;
+		break;
+
+	case LOG_LOG:
+		ebt_check_option2(flags, OPT_LOG);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log");
+		break;
+
+	case LOG_IP6:
+		ebt_check_option2(flags, OPT_IP6);
+		if (ebt_check_inverse(optarg))
+			ebt_print_error2("Unexpected `!' after --log-ip6");
+		loginfo->bitmask |= EBT_LOG_IP6;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data;
+
+	printf("--log-level %s --log-prefix \"%s\"",
+		eight_priority[loginfo->loglevel].c_name,
+		loginfo->prefix);
+	if (loginfo->bitmask & EBT_LOG_IP)
+		printf(" --log-ip");
+	if (loginfo->bitmask & EBT_LOG_ARP)
+		printf(" --log-arp");
+	if (loginfo->bitmask & EBT_LOG_IP6)
+		printf(" --log-ip6");
+	printf(" ");
+}
+
+static int compare(const struct ebt_entry_watcher *w1,
+   const struct ebt_entry_watcher *w2)
+{
+	struct ebt_log_info *loginfo1 = (struct ebt_log_info *)w1->data;
+	struct ebt_log_info *loginfo2 = (struct ebt_log_info *)w2->data;
+
+	if (loginfo1->loglevel != loginfo2->loglevel)
+		return 0;
+	if (loginfo1->bitmask != loginfo2->bitmask)
+		return 0;
+	return !strcmp((char *)loginfo1->prefix, (char *)loginfo2->prefix);
+}
+
+static struct ebt_u_watcher log_watcher =
+{
+	.name		= "log",
+	.size		= sizeof(struct ebt_log_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_watcher(&log_watcher);
+}
diff --git a/extensions/ebt_mark.c b/extensions/ebt_mark.c
new file mode 100644
index 0000000..b4f93b5
--- /dev/null
+++ b/extensions/ebt_mark.c
@@ -0,0 +1,178 @@
+/* ebt_mark
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002, September 2006
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_mark_t.h>
+
+static int mark_supplied;
+
+#define MARK_TARGET  '1'
+#define MARK_SETMARK '2'
+#define MARK_ORMARK  '3'
+#define MARK_ANDMARK '4'
+#define MARK_XORMARK '5'
+static const struct option opts[] =
+{
+	{ "mark-target" , required_argument, 0, MARK_TARGET },
+	/* an oldtime messup, we should have always used the scheme
+	 * <extension-name>-<option> */
+	{ "set-mark"    , required_argument, 0, MARK_SETMARK },
+	{ "mark-set"    , required_argument, 0, MARK_SETMARK },
+	{ "mark-or"     , required_argument, 0, MARK_ORMARK  },
+	{ "mark-and"    , required_argument, 0, MARK_ANDMARK },
+	{ "mark-xor"    , required_argument, 0, MARK_XORMARK },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"mark target options:\n"
+	" --mark-set value     : Set nfmark value\n"
+	" --mark-or  value     : Or nfmark with value (nfmark |= value)\n"
+	" --mark-and value     : And nfmark with value (nfmark &= value)\n"
+	" --mark-xor value     : Xor nfmark with value (nfmark ^= value)\n"
+	" --mark-target target : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+
+	markinfo->target = EBT_ACCEPT;
+	markinfo->mark = 0;
+	mark_supplied = 0;
+}
+
+#define OPT_MARK_TARGET   0x01
+#define OPT_MARK_SETMARK  0x02
+#define OPT_MARK_ORMARK   0x04
+#define OPT_MARK_ANDMARK  0x08
+#define OPT_MARK_XORMARK  0x10
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)(*target)->data;
+	char *end;
+
+	switch (c) {
+	case MARK_TARGET:
+		{ int tmp;
+		ebt_check_option2(flags, OPT_MARK_TARGET);
+		if (FILL_TARGET(optarg, tmp))
+			ebt_print_error2("Illegal --mark-target target");
+		/* the 4 lsb are left to designate the target */
+		markinfo->target = (markinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS);
+		}
+		return 1;
+	case MARK_SETMARK:
+		ebt_check_option2(flags, OPT_MARK_SETMARK);
+		if (*flags & (OPT_MARK_ORMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-set cannot be used together with specific --mark option");
+                break;
+	case MARK_ORMARK:
+		ebt_check_option2(flags, OPT_MARK_ORMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-or cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_OR_VALUE;
+                break;
+	case MARK_ANDMARK:
+		ebt_check_option2(flags, OPT_MARK_ANDMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ORMARK|OPT_MARK_XORMARK))
+			ebt_print_error2("--mark-and cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_AND_VALUE;
+                break;
+	case MARK_XORMARK:
+		ebt_check_option2(flags, OPT_MARK_XORMARK);
+		if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_ORMARK))
+			ebt_print_error2("--mark-xor cannot be used together with specific --mark option");
+		markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_XOR_VALUE;
+                break;
+	 default:
+		return 0;
+	}
+	/* mutual code */
+	markinfo->mark = strtoul(optarg, &end, 0);
+	if (*end != '\0' || end == optarg)
+		ebt_print_error2("Bad MARK value '%s'", optarg);
+	mark_supplied = 1;
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+
+	if (time == 0 && mark_supplied == 0) {
+		ebt_print_error("No mark value supplied");
+	} else if (BASE_CHAIN && (markinfo->target|~EBT_VERDICT_BITS) == EBT_RETURN)
+		ebt_print_error("--mark-target RETURN not allowed on base chain");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_mark_t_info *markinfo =
+	   (struct ebt_mark_t_info *)target->data;
+	int tmp;
+
+	tmp = markinfo->target & ~EBT_VERDICT_BITS;
+	if (tmp == MARK_SET_VALUE)
+		printf("--mark-set");
+	else if (tmp == MARK_OR_VALUE)
+		printf("--mark-or");
+	else if (tmp == MARK_XOR_VALUE)
+		printf("--mark-xor");
+	else if (tmp == MARK_AND_VALUE)
+		printf("--mark-and");
+	else
+		ebt_print_error("oops, unknown mark action, try a later version of ebtables");
+	printf(" 0x%lx", markinfo->mark);
+	tmp = markinfo->target | ~EBT_VERDICT_BITS;
+	printf(" --mark-target %s", TARGET_NAME(tmp));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_mark_t_info *markinfo1 =
+	   (struct ebt_mark_t_info *)t1->data;
+	struct ebt_mark_t_info *markinfo2 =
+	   (struct ebt_mark_t_info *)t2->data;
+
+	return markinfo1->target == markinfo2->target &&
+	   markinfo1->mark == markinfo2->mark;
+}
+
+static struct ebt_u_target mark_target =
+{
+	.name		= "mark",
+	.size		= sizeof(struct ebt_mark_t_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&mark_target);
+}
diff --git a/extensions/ebt_mark_m.c b/extensions/ebt_mark_m.c
new file mode 100644
index 0000000..b6d11a2
--- /dev/null
+++ b/extensions/ebt_mark_m.c
@@ -0,0 +1,127 @@
+/* ebt_mark_m
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_mark_m.h>
+
+#define MARK '1'
+
+static const struct option opts[] =
+{
+	{ "mark", required_argument, 0, MARK },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"mark option:\n"
+"--mark    [!] [value][/mask]: Match nfmask value (see man page)\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)match->data;
+
+	markinfo->mark    = 0;
+	markinfo->mask    = 0;
+	markinfo->invert  = 0;
+	markinfo->bitmask = 0;
+}
+
+#define OPT_MARK 0x01
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)
+	   (*match)->data;
+	char *end;
+
+	switch (c) {
+	case MARK:
+		ebt_check_option2(flags, MARK);
+		if (ebt_check_inverse2(optarg))
+			markinfo->invert = 1;
+		markinfo->mark = strtoul(optarg, &end, 0);
+		markinfo->bitmask = EBT_MARK_AND;
+		if (*end == '/') {
+			if (end == optarg)
+				markinfo->bitmask = EBT_MARK_OR;
+			markinfo->mask = strtoul(end+1, &end, 0);
+		} else
+			markinfo->mask = 0xffffffff;
+		if ( *end != '\0' || end == optarg)
+			ebt_print_error2("Bad mark value '%s'", optarg);
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_mark_m_info *markinfo =
+	   (struct ebt_mark_m_info *)match->data;
+
+	printf("--mark ");
+	if (markinfo->invert)
+		printf("! ");
+	if (markinfo->bitmask == EBT_MARK_OR)
+		printf("/0x%lx ", markinfo->mask);
+	else if(markinfo->mask != 0xffffffff)
+		printf("0x%lx/0x%lx ", markinfo->mark, markinfo->mask);
+	else
+		printf("0x%lx ", markinfo->mark);
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_mark_m_info *markinfo1 = (struct ebt_mark_m_info *)m1->data;
+	struct ebt_mark_m_info *markinfo2 = (struct ebt_mark_m_info *)m2->data;
+
+	if (markinfo1->invert != markinfo2->invert)
+		return 0;
+	if (markinfo1->mark != markinfo2->mark)
+		return 0;
+	if (markinfo1->mask != markinfo2->mask)
+		return 0;
+	if (markinfo1->bitmask != markinfo2->bitmask)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match mark_match =
+{
+	.name		= "mark_m",
+	.size		= sizeof(struct ebt_mark_m_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&mark_match);
+}
diff --git a/extensions/ebt_nat.c b/extensions/ebt_nat.c
new file mode 100644
index 0000000..fe7e987
--- /dev/null
+++ b/extensions/ebt_nat.c
@@ -0,0 +1,238 @@
+/* ebt_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * June, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <netinet/ether.h>
+#include <linux/netfilter_bridge/ebt_nat.h>
+
+static int to_source_supplied, to_dest_supplied;
+
+#define NAT_S '1'
+#define NAT_D '1'
+#define NAT_S_TARGET '2'
+#define NAT_D_TARGET '2'
+#define NAT_S_ARP '3'
+static const struct option opts_s[] =
+{
+	{ "to-source"     , required_argument, 0, NAT_S },
+	{ "to-src"        , required_argument, 0, NAT_S },
+	{ "snat-target"   , required_argument, 0, NAT_S_TARGET },
+	{ "snat-arp"      ,       no_argument, 0, NAT_S_ARP },
+	{ 0 }
+};
+
+static const struct option opts_d[] =
+{
+	{ "to-destination", required_argument, 0, NAT_D },
+	{ "to-dst"        , required_argument, 0, NAT_D },
+	{ "dnat-target"   , required_argument, 0, NAT_D_TARGET },
+	{ 0 }
+};
+
+static void print_help_s()
+{
+	printf(
+	"snat options:\n"
+	" --to-src address       : MAC address to map source to\n"
+	" --snat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n"
+	" --snat-arp             : also change src address in arp msg\n");
+}
+
+static void print_help_d()
+{
+	printf(
+	"dnat options:\n"
+	" --to-dst address       : MAC address to map destination to\n"
+	" --dnat-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init_s(struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	to_source_supplied = 0;
+	natinfo->target = EBT_ACCEPT;
+	return;
+}
+
+static void init_d(struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	to_dest_supplied = 0;
+	natinfo->target = EBT_ACCEPT;
+	return;
+}
+
+#define OPT_SNAT         0x01
+#define OPT_SNAT_TARGET  0x02
+#define OPT_SNAT_ARP     0x04
+static int parse_s(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case NAT_S:
+		ebt_check_option2(flags, OPT_SNAT);
+		to_source_supplied = 1;
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --to-source mac");
+		memcpy(natinfo->mac, addr, ETH_ALEN);
+		break;
+	case NAT_S_TARGET:
+		{ int tmp;
+		ebt_check_option2(flags, OPT_SNAT_TARGET);
+		if (FILL_TARGET(optarg, tmp))
+			ebt_print_error2("Illegal --snat-target target");
+		natinfo->target = (natinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS);
+		}
+		break;
+	case NAT_S_ARP:
+		ebt_check_option2(flags, OPT_SNAT_ARP);
+		natinfo->target ^= NAT_ARP_BIT;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+#define OPT_DNAT        0x01
+#define OPT_DNAT_TARGET 0x02
+static int parse_d(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+	struct ether_addr *addr;
+
+	switch (c) {
+	case NAT_D:
+		ebt_check_option2(flags, OPT_DNAT);
+		to_dest_supplied = 1;
+		if (!(addr = ether_aton(optarg)))
+			ebt_print_error2("Problem with specified --to-destination mac");
+		memcpy(natinfo->mac, addr, ETH_ALEN);
+		break;
+	case NAT_D_TARGET:
+		ebt_check_option2(flags, OPT_DNAT_TARGET);
+		if (FILL_TARGET(optarg, natinfo->target))
+			ebt_print_error2("Illegal --dnat-target target");
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	if (BASE_CHAIN && (natinfo->target | ~EBT_VERDICT_BITS) == EBT_RETURN) {
+		ebt_print_error("--snat-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat")) {
+		ebt_print_error("Wrong chain for snat");
+	} else if (time == 0 && to_source_supplied == 0)
+		ebt_print_error("No snat address supplied");
+}
+
+static void final_check_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	if (BASE_CHAIN && natinfo->target == EBT_RETURN) {
+		ebt_print_error("--dnat-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))
+	   || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute"))) {
+		ebt_print_error("Wrong chain for dnat");
+	} else if (time == 0 && to_dest_supplied == 0)
+		ebt_print_error("No dnat address supplied");
+}
+
+static void print_s(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	printf("--to-src ");
+	ebt_print_mac(natinfo->mac);
+	if (!(natinfo->target&NAT_ARP_BIT))
+		printf(" --snat-arp");
+	printf(" --snat-target %s", TARGET_NAME((natinfo->target|~EBT_VERDICT_BITS)));
+}
+
+static void print_d(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data;
+
+	printf("--to-dst ");
+	ebt_print_mac(natinfo->mac);
+	printf(" --dnat-target %s", TARGET_NAME(natinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data;
+	struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data;
+
+	return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac)) &&
+	   natinfo1->target == natinfo2->target;
+}
+
+static struct ebt_u_target snat_target =
+{
+	.name		= "snat",
+	.size		= sizeof(struct ebt_nat_info),
+	.help		= print_help_s,
+	.init		= init_s,
+	.parse		= parse_s,
+	.final_check	= final_check_s,
+	.print		= print_s,
+	.compare	= compare,
+	.extra_ops	= opts_s,
+};
+
+static struct ebt_u_target dnat_target =
+{
+	.name		= "dnat",
+	.size		= sizeof(struct ebt_nat_info),
+	.help		= print_help_d,
+	.init		= init_d,
+	.parse		= parse_d,
+	.final_check	= final_check_d,
+	.print		= print_d,
+	.compare	= compare,
+	.extra_ops	= opts_d,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&snat_target);
+	ebt_register_target(&dnat_target);
+}
diff --git a/extensions/ebt_nflog.c b/extensions/ebt_nflog.c
new file mode 100644
index 0000000..04c547d
--- /dev/null
+++ b/extensions/ebt_nflog.c
@@ -0,0 +1,172 @@
+/* ebt_nflog
+ *
+ * Authors:
+ * Peter Warasin <peter@endian.com>
+ *
+ *  February, 2008
+ *
+ * Based on:
+ *  ebt_ulog.c, (C) 2004, Bart De Schuymer <bdschuym@pandora.be>
+ *  libxt_NFLOG.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_nflog.h>
+
+enum {
+	NFLOG_GROUP = 0x1,
+	NFLOG_PREFIX = 0x2,
+	NFLOG_RANGE = 0x4,
+	NFLOG_THRESHOLD = 0x8,
+	NFLOG_NFLOG = 0x16,
+};
+
+static const struct option nflog_opts[] = {
+	{"nflog-group", required_argument, NULL, NFLOG_GROUP},
+	{"nflog-prefix", required_argument, NULL, NFLOG_PREFIX},
+	{"nflog-range", required_argument, NULL, NFLOG_RANGE},
+	{"nflog-threshold", required_argument, NULL, NFLOG_THRESHOLD},
+	{"nflog", no_argument, NULL, NFLOG_NFLOG},
+	{.name = NULL}
+};
+
+static void nflog_help()
+{
+	printf("nflog options:\n"
+	       "--nflog               : use the default nflog parameters\n"
+	       "--nflog-prefix prefix : Prefix string for log message\n"
+	       "--nflog-group group   : NETLINK group used for logging\n"
+	       "--nflog-range range   : Number of byte to copy\n"
+	       "--nflog-threshold     : Message threshold of"
+	       "in-kernel queue\n");
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data;
+
+	info->prefix[0] = '\0';
+	info->group = EBT_NFLOG_DEFAULT_GROUP;
+	info->threshold = EBT_NFLOG_DEFAULT_THRESHOLD;
+}
+
+static int nflog_parse(int c, char **argv, int argc,
+		       const struct ebt_u_entry *entry, unsigned int *flags,
+		       struct ebt_entry_watcher **watcher)
+{
+	struct ebt_nflog_info *info;
+	unsigned int i;
+	char *end;
+
+	info = (struct ebt_nflog_info *)(*watcher)->data;
+	switch (c) {
+	case NFLOG_PREFIX:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_PREFIX);
+		if (strlen(optarg) > EBT_NFLOG_PREFIX_SIZE - 1)
+			ebt_print_error("Prefix too long for nflog-prefix");
+		strcpy(info->prefix, optarg);
+		break;
+
+	case NFLOG_GROUP:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_GROUP);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-group must be a number!");
+		info->group = i;
+		break;
+
+	case NFLOG_RANGE:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_RANGE);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-range must be a number!");
+		info->len = i;
+		break;
+
+	case NFLOG_THRESHOLD:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_THRESHOLD);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("--nflog-threshold must be a number!");
+		info->threshold = i;
+		break;
+	case NFLOG_NFLOG:
+		if (ebt_check_inverse(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, NFLOG_NFLOG);
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+
+ inverse_invalid:
+	ebt_print_error("The use of '!' makes no sense for the nflog watcher");
+	return 1;
+}
+
+static void nflog_final_check(const struct ebt_u_entry *entry,
+			      const struct ebt_entry_watcher *watcher,
+			      const char *name, unsigned int hookmask,
+			      unsigned int time)
+{
+}
+
+static void nflog_print(const struct ebt_u_entry *entry,
+			const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data;
+
+	if (info->prefix[0] != '\0')
+		printf("--nflog-prefix \"%s\"", info->prefix);
+	if (info->group)
+		printf("--nflog-group %d ", info->group);
+	if (info->len)
+		printf("--nflog-range %d", info->len);
+	if (info->threshold != EBT_NFLOG_DEFAULT_THRESHOLD)
+		printf(" --nflog-threshold %d ", info->threshold);
+}
+
+static int nflog_compare(const struct ebt_entry_watcher *w1,
+			 const struct ebt_entry_watcher *w2)
+{
+	struct ebt_nflog_info *info1 = (struct ebt_nflog_info *)w1->data;
+	struct ebt_nflog_info *info2 = (struct ebt_nflog_info *)w2->data;
+
+	if (info1->group != info2->group ||
+	    info1->len != info2->len ||
+	    info1->threshold != info2->threshold ||
+	    strcmp(info1->prefix, info2->prefix))
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_watcher nflog_watcher = {
+	.name = "nflog",
+	.size = sizeof(struct ebt_nflog_info),
+	.help = nflog_help,
+	.init = init,
+	.parse = nflog_parse,
+	.final_check = nflog_final_check,
+	.print = nflog_print,
+	.compare = nflog_compare,
+	.extra_ops = nflog_opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_watcher(&nflog_watcher);
+}
diff --git a/extensions/ebt_pkttype.c b/extensions/ebt_pkttype.c
new file mode 100644
index 0000000..bf578fc
--- /dev/null
+++ b/extensions/ebt_pkttype.c
@@ -0,0 +1,131 @@
+/* ebt_pkttype
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2003
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <netdb.h>
+#include "../include/ebtables_u.h"
+#include <linux/if_packet.h>
+#include <linux/netfilter_bridge/ebt_pkttype.h>
+
+char *classes[] =
+{
+	"host",
+	"broadcast",
+	"multicast",
+	"otherhost",
+	"outgoing",
+	"loopback",
+	"fastroute",
+	"\0"
+};
+
+static const struct option opts[] =
+{
+	{ "pkttype-type"        , required_argument, 0, '1' },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"pkttype options:\n"
+"--pkttype-type    [!] type: class the packet belongs to\n"
+"Possible values: broadcast, multicast, host, otherhost, or any other byte value (which would be pretty useless).\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
+
+	pt->invert = 0;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_pkttype_info *ptinfo = (struct ebt_pkttype_info *)(*match)->data;
+	char *end;
+	long int i;
+
+	switch (c) {
+	case '1':
+		ebt_check_option2(flags, 1);
+		if (ebt_check_inverse2(optarg))
+			ptinfo->invert = 1;
+		i = strtol(optarg, &end, 16);
+		if (*end != '\0') {
+			int j = 0;
+			i = -1;
+			while (classes[j][0])
+				if (!strcasecmp(optarg, classes[j++])) {
+					i = j - 1;
+					break;
+				}
+		}
+		if (i < 0 || i > 255)
+			ebt_print_error2("Problem with specified pkttype class");
+		ptinfo->pkt_type = (uint8_t)i;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data;
+	int i = 0;
+
+	printf("--pkttype-type %s", pt->invert ? "! " : "");
+	while (classes[i++][0]);
+	if (pt->pkt_type < i - 1)
+		printf("%s ", classes[pt->pkt_type]);
+	else
+		printf("%d ", pt->pkt_type);
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	struct ebt_pkttype_info *pt1 = (struct ebt_pkttype_info *)m1->data;
+	struct ebt_pkttype_info *pt2 = (struct ebt_pkttype_info *)m2->data;
+
+	if (pt1->invert != pt2->invert ||
+	    pt1->pkt_type != pt2->pkt_type)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match pkttype_match =
+{
+	.name		= "pkttype",
+	.size		= sizeof(struct ebt_pkttype_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&pkttype_match);
+}
diff --git a/extensions/ebt_redirect.c b/extensions/ebt_redirect.c
new file mode 100644
index 0000000..59fe818
--- /dev/null
+++ b/extensions/ebt_redirect.c
@@ -0,0 +1,114 @@
+/* ebt_redirect
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_redirect.h>
+
+#define REDIRECT_TARGET '1'
+static const struct option opts[] =
+{
+	{ "redirect-target", required_argument, 0, REDIRECT_TARGET },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"redirect option:\n"
+	" --redirect-target target   : ACCEPT, DROP, RETURN or CONTINUE\n");
+}
+
+static void init(struct ebt_entry_target *target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	redirectinfo->target = EBT_ACCEPT;
+	return;
+}
+
+#define OPT_REDIRECT_TARGET  0x01
+static int parse(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)(*target)->data;
+
+	switch (c) {
+	case REDIRECT_TARGET:
+		ebt_check_option2(flags, OPT_REDIRECT_TARGET);
+		if (FILL_TARGET(optarg, redirectinfo->target))
+			ebt_print_error2("Illegal --redirect-target target");
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	if (BASE_CHAIN && redirectinfo->target == EBT_RETURN) {
+		ebt_print_error("--redirect-target RETURN not allowed on base chain");
+		return;
+	}
+	CLEAR_BASE_CHAIN_BIT;
+	if ( ((hookmask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) &&
+	   ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) )
+		ebt_print_error("Wrong chain for redirect");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)target->data;
+
+	if (redirectinfo->target == EBT_ACCEPT)
+		return;
+	printf(" --redirect-target %s", TARGET_NAME(redirectinfo->target));
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	struct ebt_redirect_info *redirectinfo1 =
+	   (struct ebt_redirect_info *)t1->data;
+	struct ebt_redirect_info *redirectinfo2 =
+	   (struct ebt_redirect_info *)t2->data;
+
+	return redirectinfo1->target == redirectinfo2->target;
+}
+
+static struct ebt_u_target redirect_target =
+{
+	.name		= "redirect",
+	.size		= sizeof(struct ebt_redirect_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&redirect_target);
+}
diff --git a/extensions/ebt_standard.c b/extensions/ebt_standard.c
new file mode 100644
index 0000000..f3c3308
--- /dev/null
+++ b/extensions/ebt_standard.c
@@ -0,0 +1,90 @@
+/* ebt_standard
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+
+static const struct option opts[] =
+{
+	{0}
+};
+
+static void print_help()
+{
+	printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n"
+	       "The target can also be a user defined chain.\n");
+}
+
+static void init(struct ebt_entry_target *t)
+{
+	((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_target **target)
+{
+	return 0;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	int verdict = ((struct ebt_standard_target *)target)->verdict;
+
+	if (verdict >= 0) {
+		struct ebt_u_entries *entries;
+
+		entries = entry->replace->chains[verdict + NF_BR_NUMHOOKS];
+		printf("%s", entries->name);
+		return;
+	}
+	if (verdict == EBT_CONTINUE)
+		printf("CONTINUE ");
+	else if (verdict == EBT_ACCEPT)
+		printf("ACCEPT ");
+	else if (verdict == EBT_DROP)
+		printf("DROP ");
+	else if (verdict == EBT_RETURN)
+		printf("RETURN ");
+	else
+		ebt_print_bug("Bad standard target");
+}
+
+static int compare(const struct ebt_entry_target *t1,
+   const struct ebt_entry_target *t2)
+{
+	return ((struct ebt_standard_target *)t1)->verdict ==
+	   ((struct ebt_standard_target *)t2)->verdict;
+}
+
+static struct ebt_u_target standard =
+{
+	.name		= "standard",
+	.size		= sizeof(struct ebt_standard_target) -
+			  sizeof(struct ebt_entry_target),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_target(&standard);
+}
diff --git a/extensions/ebt_stp.c b/extensions/ebt_stp.c
new file mode 100644
index 0000000..311bc63
--- /dev/null
+++ b/extensions/ebt_stp.c
@@ -0,0 +1,343 @@
+/* ebt_stp
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * July, 2003
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_stp.h>
+
+#define STP_TYPE	'a'
+#define STP_FLAGS	'b'
+#define STP_ROOTPRIO	'c'
+#define STP_ROOTADDR	'd'
+#define STP_ROOTCOST	'e'
+#define STP_SENDERPRIO	'f'
+#define STP_SENDERADDR	'g'
+#define STP_PORT	'h'
+#define STP_MSGAGE	'i'
+#define STP_MAXAGE	'j'
+#define STP_HELLOTIME	'k'
+#define STP_FWDD	'l'
+#define STP_NUMOPS 12
+
+static const struct option opts[] =
+{
+	{ "stp-type"         , required_argument, 0, STP_TYPE},
+	{ "stp-flags"        , required_argument, 0, STP_FLAGS},
+	{ "stp-root-prio"    , required_argument, 0, STP_ROOTPRIO},
+	{ "stp-root-addr"    , required_argument, 0, STP_ROOTADDR},
+	{ "stp-root-cost"    , required_argument, 0, STP_ROOTCOST},
+	{ "stp-sender-prio"  , required_argument, 0, STP_SENDERPRIO},
+	{ "stp-sender-addr"  , required_argument, 0, STP_SENDERADDR},
+	{ "stp-port"         , required_argument, 0, STP_PORT},
+	{ "stp-msg-age"      , required_argument, 0, STP_MSGAGE},
+	{ "stp-max-age"      , required_argument, 0, STP_MAXAGE},
+	{ "stp-hello-time"   , required_argument, 0, STP_HELLOTIME},
+	{ "stp-forward-delay", required_argument, 0, STP_FWDD},
+	{ 0 }
+};
+
+#define BPDU_TYPE_CONFIG 0
+#define BPDU_TYPE_TCN 0x80
+#define BPDU_TYPE_CONFIG_STRING "config"
+#define BPDU_TYPE_TCN_STRING "tcn"
+
+#define FLAG_TC 0x01
+#define FLAG_TC_ACK 0x80
+#define FLAG_TC_STRING "topology-change"
+#define FLAG_TC_ACK_STRING "topology-change-ack"
+
+static void print_help()
+{
+	printf(
+"stp options:\n"
+"--stp-type type                  : BPDU type\n"
+"--stp-flags flag                 : control flag\n"
+"--stp-root-prio prio[:prio]      : root priority (16-bit) range\n"
+"--stp-root-addr address[/mask]   : MAC address of root\n"
+"--stp-root-cost cost[:cost]      : root cost (32-bit) range\n"
+"--stp-sender-prio prio[:prio]    : sender priority (16-bit) range\n"
+"--stp-sender-addr address[/mask] : MAC address of sender\n"
+"--stp-port port[:port]           : port id (16-bit) range\n"
+"--stp-msg-age age[:age]          : message age timer (16-bit) range\n"
+"--stp-max-age age[:age]          : maximum age timer (16-bit) range\n"
+"--stp-hello-time time[:time]     : hello time timer (16-bit) range\n"
+"--stp-forward-delay delay[:delay]: forward delay timer (16-bit) range\n"
+" Recognized BPDU type strings:\n"
+"   \"config\": configuration BPDU (=0)\n"
+"   \"tcn\"   : topology change notification BPDU (=0x80)\n"
+" Recognized control flag strings:\n"
+"   \"topology-change\"    : topology change flag (0x01)\n"
+"   \"topology-change-ack\": topology change acknowledgement flag (0x80)");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data;
+
+	stpinfo->invflags = 0;
+	stpinfo->bitmask = 0;
+}
+
+static int parse_range(const char *portstring, void *lower, void *upper,
+   int bits, uint32_t min, uint32_t max)
+{
+	char *buffer;
+	char *cp, *end;
+	uint32_t low_nr, upp_nr;
+	int ret = 0;
+
+	buffer = strdup(portstring);
+	if ((cp = strchr(buffer, ':')) == NULL) {
+		low_nr = strtoul(buffer, &end, 10);
+		if (*end || low_nr < min || low_nr > max) {
+			ret = -1;
+			goto out;
+		}
+		if (bits == 2) {
+			*(uint16_t *)lower =  low_nr;
+			*(uint16_t *)upper =  low_nr;
+		} else {
+			*(uint32_t *)lower =  low_nr;
+			*(uint32_t *)upper =  low_nr;
+		}
+	} else {
+		*cp = '\0';
+		cp++;
+		if (!*buffer)
+			low_nr = min;
+		else {
+			low_nr = strtoul(buffer, &end, 10);
+			if (*end || low_nr < min) {
+				ret = -1;
+				goto out;
+			}
+		}
+		if (!*cp)
+			upp_nr = max;
+		else {
+			upp_nr = strtoul(cp, &end, 10);
+			if (*end || upp_nr > max) {
+				ret = -1;
+				goto out;
+			}
+		}
+		if (upp_nr < low_nr) {
+			ret = -1;
+			goto out;
+		}
+		if (bits == 2) {
+			*(uint16_t *)lower = low_nr;
+			*(uint16_t *)upper = upp_nr;
+		} else {
+			*(uint32_t *)lower = low_nr;
+			*(uint32_t *)upper = upp_nr;
+		}
+	}
+out:
+	free(buffer);
+	return ret;
+}
+
+static void print_range(unsigned int l, unsigned int u)
+{
+	if (l == u)
+		printf("%u ", l);
+	else
+		printf("%u:%u ", l, u);
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)(*match)->data;
+	unsigned int flag;
+	long int i;
+	char *end = NULL;
+
+	if (c < 'a' || c > ('a' + STP_NUMOPS - 1))
+		return 0;
+	flag = 1 << (c - 'a');
+	ebt_check_option2(flags, flag);
+	if (ebt_check_inverse2(optarg))
+		stpinfo->invflags |= flag;
+	stpinfo->bitmask |= flag;
+	switch (flag) {
+	case EBT_STP_TYPE:
+		i = strtol(optarg, &end, 0);
+		if (i < 0 || i > 255 || *end != '\0') {
+			if (!strcasecmp(optarg, BPDU_TYPE_CONFIG_STRING))
+				stpinfo->type = BPDU_TYPE_CONFIG;
+			else if (!strcasecmp(optarg, BPDU_TYPE_TCN_STRING))
+				stpinfo->type = BPDU_TYPE_TCN;
+			else
+				ebt_print_error2("Bad --stp-type argument");
+		} else
+			stpinfo->type = i;
+		break;
+	case EBT_STP_FLAGS:
+		i = strtol(optarg, &end, 0);
+		if (i < 0 || i > 255 || *end != '\0') {
+			if (!strcasecmp(optarg, FLAG_TC_STRING))
+				stpinfo->config.flags = FLAG_TC;
+			else if (!strcasecmp(optarg, FLAG_TC_ACK_STRING))
+				stpinfo->config.flags = FLAG_TC_ACK;
+			else
+				ebt_print_error2("Bad --stp-flags argument");
+		} else
+			stpinfo->config.flags = i;
+		break;
+	case EBT_STP_ROOTPRIO:
+		if (parse_range(argv[optind-1], &(stpinfo->config.root_priol),
+		    &(stpinfo->config.root_priou), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-root-prio range");
+		break;
+	case EBT_STP_ROOTCOST:
+		if (parse_range(argv[optind-1], &(stpinfo->config.root_costl),
+		    &(stpinfo->config.root_costu), 4, 0, 0xffffffff))
+			ebt_print_error("Bad --stp-root-cost range");
+		break;
+	case EBT_STP_SENDERPRIO:
+		if (parse_range(argv[optind-1], &(stpinfo->config.sender_priol),
+		    &(stpinfo->config.sender_priou), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-sender-prio range");
+		break;
+	case EBT_STP_PORT:
+		if (parse_range(argv[optind-1], &(stpinfo->config.portl),
+		    &(stpinfo->config.portu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-port range");
+		break;
+	case EBT_STP_MSGAGE:
+		if (parse_range(argv[optind-1], &(stpinfo->config.msg_agel),
+		    &(stpinfo->config.msg_ageu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-msg-age range");
+		break;
+	case EBT_STP_MAXAGE:
+		if (parse_range(argv[optind-1], &(stpinfo->config.max_agel),
+		    &(stpinfo->config.max_ageu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-max-age range");
+		break;
+	case EBT_STP_HELLOTIME:
+		if (parse_range(argv[optind-1], &(stpinfo->config.hello_timel),
+		    &(stpinfo->config.hello_timeu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-hello-time range");
+		break;
+	case EBT_STP_FWDD:
+		if (parse_range(argv[optind-1], &(stpinfo->config.forward_delayl),
+		    &(stpinfo->config.forward_delayu), 2, 0, 0xffff))
+			ebt_print_error("Bad --stp-forward-delay range");
+		break;
+	case EBT_STP_ROOTADDR:
+		if (ebt_get_mac_and_mask(argv[optind-1],
+		    (unsigned char *)stpinfo->config.root_addr,
+		    (unsigned char *)stpinfo->config.root_addrmsk))
+			ebt_print_error("Bad --stp-root-addr address");
+		break;
+	case EBT_STP_SENDERADDR:
+		if (ebt_get_mac_and_mask(argv[optind-1],
+		    (unsigned char *)stpinfo->config.sender_addr,
+		    (unsigned char *)stpinfo->config.sender_addrmsk))
+			ebt_print_error("Bad --stp-sender-addr address");
+		break;
+	default:
+		ebt_print_error("stp match: this shouldn't happen");
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+	uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+	uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+	if (memcmp(entry->destmac, bridge_ula, 6) ||
+	    memcmp(entry->destmsk, msk, 6))
+		ebt_print_error("STP matching is only valid when the "
+				"destination MAC address is the bridge group "
+				"address (BGA) 01:80:c2:00:00:00");
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data;
+	struct ebt_stp_config_info *c = &(stpinfo->config);
+	int i;
+
+	for (i = 0; i < STP_NUMOPS; i++) {
+		if (!(stpinfo->bitmask & (1 << i)))
+			continue;
+		printf("--%s %s", opts[i].name,
+		       (stpinfo->invflags & (1 << i)) ? "! " : "");
+		if (EBT_STP_TYPE == (1 << i)) {
+			if (stpinfo->type == BPDU_TYPE_CONFIG)
+				printf("%s", BPDU_TYPE_CONFIG_STRING);
+			else if (stpinfo->type == BPDU_TYPE_TCN)
+				printf("%s", BPDU_TYPE_TCN_STRING);
+			else
+				printf("%d", stpinfo->type);
+		} else if (EBT_STP_FLAGS == (1 << i)) {
+			if (c->flags == FLAG_TC)
+				printf("%s", FLAG_TC_STRING);
+			else if (c->flags == FLAG_TC_ACK)
+				printf("%s", FLAG_TC_ACK_STRING);
+			else
+				printf("%d", c->flags);
+		} else if (EBT_STP_ROOTPRIO == (1 << i))
+			print_range(c->root_priol, c->root_priou);
+		else if (EBT_STP_ROOTADDR == (1 << i))
+			ebt_print_mac_and_mask((unsigned char *)c->root_addr,
+			   (unsigned char*)c->root_addrmsk);
+		else if (EBT_STP_ROOTCOST == (1 << i))
+			print_range(c->root_costl, c->root_costu);
+		else if (EBT_STP_SENDERPRIO == (1 << i))
+			print_range(c->sender_priol, c->sender_priou);
+		else if (EBT_STP_SENDERADDR == (1 << i))
+			ebt_print_mac_and_mask((unsigned char *)c->sender_addr,
+			   (unsigned char *)c->sender_addrmsk);
+		else if (EBT_STP_PORT == (1 << i))
+			print_range(c->portl, c->portu);
+		else if (EBT_STP_MSGAGE == (1 << i))
+			print_range(c->msg_agel, c->msg_ageu);
+		else if (EBT_STP_MAXAGE == (1 << i))
+			print_range(c->max_agel, c->max_ageu);
+		else if (EBT_STP_HELLOTIME == (1 << i))
+			print_range(c->hello_timel, c->hello_timeu);
+		else if (EBT_STP_FWDD == (1 << i))
+			print_range(c->forward_delayl, c->forward_delayu);
+		printf(" ");
+	}
+}
+
+static int compare(const struct ebt_entry_match *m1,
+   const struct ebt_entry_match *m2)
+{
+	return (!memcmp(m1->data, m2->data, sizeof(struct ebt_stp_info)));
+}
+
+static struct ebt_u_match stp_match =
+{
+	.name		= "stp",
+	.size		= sizeof(struct ebt_stp_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&stp_match);
+}
diff --git a/extensions/ebt_string.c b/extensions/ebt_string.c
new file mode 100644
index 0000000..97fbe19
--- /dev/null
+++ b/extensions/ebt_string.c
@@ -0,0 +1,318 @@
+/* ebt_string
+ *
+ * Author:
+ * Bernie Harris <bernie.harris@alliedtelesis.co.nz>
+ *
+ * February, 2018
+ *
+ * Based on:
+ *  libxt_string.c, Copyright (C) 2000 Emmanuel Roger  <winfield@freegates.be>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <netdb.h>
+#include <ctype.h>
+#include "../include/ebtables_u.h"
+#include <linux/if_packet.h>
+#include <linux/netfilter/xt_string.h>
+
+#define STRING_FROM  '1'
+#define STRING_TO    '2'
+#define STRING_ALGO  '3'
+#define STRING_ICASE '4'
+#define STRING       '5'
+#define STRING_HEX   '6'
+#define OPT_STRING_FROM  (1 << 0)
+#define OPT_STRING_TO    (1 << 1)
+#define OPT_STRING_ALGO  (1 << 2)
+#define OPT_STRING_ICASE (1 << 3)
+#define OPT_STRING       (1 << 4)
+#define OPT_STRING_HEX   (1 << 5)
+
+static const struct option opts[] =
+{
+	{ "string-from"             , required_argument, 0, STRING_FROM },
+	{ "string-to"               , required_argument, 0, STRING_TO },
+	{ "string-algo"             , required_argument, 0, STRING_ALGO },
+	{ "string-icase"            , no_argument,       0, STRING_ICASE },
+	{ "string"                  , required_argument, 0, STRING },
+	{ "string-hex"              , required_argument, 0, STRING_HEX },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"string options:\n"
+"--string-from offset    : Offset to start searching from (default: 0)\n"
+"--string-to   offset    : Offset to stop searching (default: packet size)\n"
+"--string-algo algorithm : Algorithm (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)\n"
+"--string-icase          : Ignore case when searching\n"
+"--string     [!] string : Match a string in a packet\n"
+"--string-hex [!] string : Match a hex string in a packet, e.g. |0D 0A|, |0D0A|, netfilter|03|org\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct xt_string_info *info = (struct xt_string_info *)match->data;
+
+	info->to_offset = UINT16_MAX;
+}
+
+static void parse_string(const char *s, struct xt_string_info *info)
+{
+	/* xt_string does not need \0 at the end of the pattern */
+	if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) {
+		strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE);
+		info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE);
+		return;
+	}
+	ebt_print_error3("STRING too long \"%s\"", s);
+}
+
+static void parse_hex_string(const char *s, struct xt_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) {
+		ebt_print_error3("STRING must contain at least one char");
+	}
+
+	while (i < slen) {
+		if (s[i] == '\\' && !hex_f) {
+			literal_f = 1;
+		} else if (s[i] == '\\') {
+			ebt_print_error3("Cannot include literals in hex data");
+		} else if (s[i] == '|') {
+			if (hex_f)
+				hex_f = 0;
+			else {
+				hex_f = 1;
+				/* get past any initial whitespace just after the '|' */
+				while (s[i+1] == ' ')
+					i++;
+			}
+			if (i+1 >= slen)
+				break;
+			else
+				i++;  /* advance to the next character */
+		}
+
+		if (literal_f) {
+			if (i+1 >= slen) {
+				ebt_print_error3("Bad literal placement at end of string");
+			}
+			info->pattern[sindex] = s[i+1];
+			i += 2;  /* skip over literal char */
+			literal_f = 0;
+		} else if (hex_f) {
+			if (i+1 >= slen) {
+				ebt_print_error3("Odd number of hex digits");
+			}
+			if (i+2 >= slen) {
+				/* must end with a "|" */
+				ebt_print_error3("Invalid hex block");
+			}
+			if (! isxdigit(s[i])) /* check for valid hex char */
+				ebt_print_error3("Invalid hex char '%c'", s[i]);
+			if (! isxdigit(s[i+1])) /* check for valid hex char */
+				ebt_print_error3("Invalid hex char '%c'", s[i+1]);
+			hextmp[0] = s[i];
+			hextmp[1] = s[i+1];
+			hextmp[2] = '\0';
+			if (! sscanf(hextmp, "%x", &schar))
+				ebt_print_error3("Invalid hex char `%c'", s[i]);
+			info->pattern[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->pattern[sindex] = s[i];
+			i++;
+		}
+		if (sindex > XT_STRING_MAX_PATTERN_SIZE)
+			ebt_print_error3("STRING too long \"%s\"", s);
+		sindex++;
+	}
+	info->patlen = sindex;
+}
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+		 unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct xt_string_info *info = (struct xt_string_info *)(*match)->data;
+
+	switch (c) {
+	case STRING_FROM:
+		ebt_check_option2(flags, OPT_STRING_FROM);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --string-from");
+		info->from_offset = (__u16)strtoul(optarg, NULL, 10);
+		break;
+	case STRING_TO:
+		ebt_check_option2(flags, OPT_STRING_TO);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --string-to");
+		info->to_offset = (__u16)strtoul(optarg, NULL, 10);
+		break;
+	case STRING_ALGO:
+		ebt_check_option2(flags, OPT_STRING_ALGO);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --string-algo");
+		if (snprintf(info->algo, sizeof(info->algo), "%s", optarg) >=
+				sizeof(info->algo))
+			ebt_print_error2("\"%s\" is truncated", info->algo);
+		break;
+	case STRING_ICASE:
+		ebt_check_option2(flags, OPT_STRING_ICASE);
+		if (ebt_check_inverse2(optarg))
+			ebt_print_error2("Unexpected `!' after --string-icase");
+		info->u.v1.flags |= XT_STRING_FLAG_IGNORECASE;
+		break;
+	case STRING:
+		ebt_check_option2(flags, OPT_STRING);
+		parse_string(optarg, info);
+		if (ebt_check_inverse2(optarg)) {
+			info->u.v1.flags |= XT_STRING_FLAG_INVERT;
+		}
+		break;
+	case STRING_HEX:
+		ebt_check_option2(flags, OPT_STRING_HEX);
+		parse_hex_string(optarg, info);
+		if (ebt_check_inverse2(optarg)) {
+			info->u.v1.flags |= XT_STRING_FLAG_INVERT;
+		}
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+			const struct ebt_entry_match *match, const char *name,
+			unsigned int hookmask, unsigned int time)
+{
+	struct xt_string_info *info = (struct xt_string_info *)match->data;
+
+	if (info->to_offset < info->from_offset) {
+		ebt_print_error3("'to' offset should not be less than 'from' "
+				 "offset");
+	}
+}
+
+/* Test to see if the string contains non-printable chars or quotes */
+static unsigned short int is_hex_string(const char *str,
+					const unsigned short int len)
+{
+	unsigned int i;
+	for (i=0; i < len; i++) {
+		if (! isprint(str[i])) {
+			/* string contains at least one non-printable char */
+			return 1;
+		}
+	}
+	/* use hex output if the last char is a "\" */
+	if (str[len-1] == '\\')
+		return 1;
+	return 0;
+}
+
+/* Print string with "|" chars included as one would pass to --string-hex */
+static void print_hex_string(const char *str, const unsigned short int len)
+{
+	unsigned int i;
+	/* start hex block */
+	printf("\"|");
+	for (i=0; i < len; i++)
+		printf("%02x", (unsigned char)str[i]);
+	/* close hex block */
+	printf("|\" ");
+}
+
+static void print_string(const char *str, const unsigned short int len)
+{
+	unsigned int i;
+	printf("\"");
+	for (i=0; i < len; i++) {
+		if (str[i] == '\"' || str[i] == '\\')
+			putchar('\\');
+		printf("%c", (unsigned char) str[i]);
+	}
+	printf("\" ");  /* closing quote */
+}
+
+static void print(const struct ebt_u_entry *entry,
+		  const struct ebt_entry_match *match)
+{
+	const struct xt_string_info *info =
+		(const struct xt_string_info *) match->data;
+	int invert = info->u.v1.flags & XT_STRING_FLAG_INVERT;
+
+	if (is_hex_string(info->pattern, info->patlen)) {
+		printf("--string-hex %s", invert ? "! " : "");
+		print_hex_string(info->pattern, info->patlen);
+	} else {
+		printf("--string %s", invert ? "! " : "");
+		print_string(info->pattern, info->patlen);
+	}
+	printf("--string-algo %s ", info->algo);
+	if (info->from_offset != 0)
+		printf("--string-from %u ", info->from_offset);
+	if (info->to_offset != 0)
+		printf("--string-to %u ", info->to_offset);
+	if (info->u.v1.flags & XT_STRING_FLAG_IGNORECASE)
+		printf("--string-icase ");
+}
+
+static int compare(const struct ebt_entry_match *m1,
+		   const struct ebt_entry_match *m2)
+{
+	const struct xt_string_info *info1 =
+		(const struct xt_string_info *) m1->data;
+	const struct xt_string_info *info2 =
+		(const struct xt_string_info *) m2->data;
+
+	if (info1->from_offset != info2->from_offset)
+		return 0;
+	if (info1->to_offset != info2->to_offset)
+		return 0;
+	if (info1->u.v1.flags != info2->u.v1.flags)
+		return 0;
+	if (info1->patlen != info2->patlen)
+		return 0;
+	if (strncmp (info1->algo, info2->algo, XT_STRING_MAX_ALGO_NAME_SIZE) != 0)
+		return 0;
+	if (strncmp (info1->pattern, info2->pattern, info1->patlen) != 0)
+		return 0;
+
+	return 1;
+}
+
+static struct ebt_u_match string_match =
+{
+	.name		= "string",
+	.revision	= 1,
+	.size		= sizeof(struct xt_string_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&string_match);
+}
diff --git a/extensions/ebt_ulog.c b/extensions/ebt_ulog.c
new file mode 100644
index 0000000..72a6c8b
--- /dev/null
+++ b/extensions/ebt_ulog.c
@@ -0,0 +1,186 @@
+/* ebt_ulog
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * November, 2004
+ */
+
+#define __need_time_t
+#define __need_suseconds_t
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <sys/time.h>
+#include <linux/netfilter_bridge/ebt_ulog.h>
+
+#define CP_NO_LIMIT_S "default_cprange"
+#define CP_NO_LIMIT_N 0
+
+#define ULOG_PREFIX     '1'
+#define ULOG_NLGROUP    '2'
+#define ULOG_CPRANGE    '3'
+#define ULOG_QTHRESHOLD '4'
+#define ULOG_ULOG       '5'
+static const struct option opts[] =
+{
+	{ "ulog-prefix"    , required_argument, 0, ULOG_PREFIX     },
+	{ "ulog-nlgroup"   , required_argument, 0, ULOG_NLGROUP    },
+	{ "ulog-cprange"   , required_argument, 0, ULOG_CPRANGE    },
+	{ "ulog-qthreshold", required_argument, 0, ULOG_QTHRESHOLD },
+	{ "ulog"           , no_argument      , 0, ULOG_ULOG       },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+"ulog options:\n"
+"--ulog                : use the default ulog parameters\n"
+"--ulog-prefix prefix  : max %d characters (default is no prefix)\n"
+"--ulog-nlgroup group  : 0 < group number < %d (default = %d)\n"
+"--ulog-cprange range  : max copy range (default is " CP_NO_LIMIT_S ")\n"
+"--ulog-qthreshold     : 0 < queueing threshold < %d (default = %d)\n",
+	EBT_ULOG_PREFIX_LEN - 1, EBT_ULOG_MAXNLGROUPS + 1,
+	EBT_ULOG_DEFAULT_NLGROUP + 1, EBT_ULOG_MAX_QLEN + 1,
+	EBT_ULOG_DEFAULT_QTHRESHOLD);
+}
+
+static void init(struct ebt_entry_watcher *watcher)
+{
+	struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data;
+
+	uloginfo->prefix[0] = '\0';
+	uloginfo->nlgroup = EBT_ULOG_DEFAULT_NLGROUP;
+	uloginfo->cprange = CP_NO_LIMIT_N; /* Use default netlink buffer size */
+	uloginfo->qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD;
+}
+
+#define OPT_PREFIX     0x01
+#define OPT_NLGROUP    0x02
+#define OPT_CPRANGE    0x04
+#define OPT_QTHRESHOLD 0x08
+#define OPT_ULOG       0x10
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_watcher **watcher)
+{
+	struct ebt_ulog_info *uloginfo;
+	unsigned int i;
+	char *end;
+
+	uloginfo = (struct ebt_ulog_info *)(*watcher)->data;
+	switch (c) {
+	case ULOG_PREFIX:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_PREFIX);
+		if (strlen(optarg) > EBT_ULOG_PREFIX_LEN - 1)
+			ebt_print_error("Prefix too long for ulog-prefix");
+		strcpy(uloginfo->prefix, optarg);
+		break;
+
+	case ULOG_NLGROUP:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_NLGROUP);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("Problem with ulog-nlgroup: %s", optarg);
+		if (i < 1 || i > EBT_ULOG_MAXNLGROUPS)
+			ebt_print_error2("the ulog-nlgroup number must be between 1 and 32");
+		uloginfo->nlgroup = i - 1;
+		break;
+
+	case ULOG_CPRANGE:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_CPRANGE);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0') {
+			if (strcasecmp(optarg, CP_NO_LIMIT_S))
+				ebt_print_error2("Problem with ulog-cprange: %s", optarg);
+			i = CP_NO_LIMIT_N;
+		}
+		uloginfo->cprange = i;
+		break;
+
+	case ULOG_QTHRESHOLD:
+		if (ebt_check_inverse2(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_QTHRESHOLD);
+		i = strtoul(optarg, &end, 10);
+		if (*end != '\0')
+			ebt_print_error2("Problem with ulog-qthreshold: %s", optarg);
+		if (i > EBT_ULOG_MAX_QLEN)
+			ebt_print_error2("ulog-qthreshold argument %d exceeds the maximum of %d", i, EBT_ULOG_MAX_QLEN);
+		uloginfo->qthreshold = i;
+		break;
+	case ULOG_ULOG:
+		if (ebt_check_inverse(optarg))
+			goto inverse_invalid;
+		ebt_check_option2(flags, OPT_ULOG);
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+
+inverse_invalid:
+	ebt_print_error("The use of '!' makes no sense for the ulog watcher");
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher, const char *name,
+   unsigned int hookmask, unsigned int time)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_watcher *watcher)
+{
+	struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data;
+
+	printf("--ulog-prefix \"%s\" --ulog-nlgroup %d --ulog-cprange ",
+	       uloginfo->prefix, uloginfo->nlgroup + 1);
+	if (uloginfo->cprange == CP_NO_LIMIT_N)
+		printf(CP_NO_LIMIT_S);
+	else
+		printf("%d", uloginfo->cprange);
+	printf(" --ulog-qthreshold %d ", uloginfo->qthreshold);
+}
+
+static int compare(const struct ebt_entry_watcher *w1,
+   const struct ebt_entry_watcher *w2)
+{
+	struct ebt_ulog_info *uloginfo1 = (struct ebt_ulog_info *)w1->data;
+	struct ebt_ulog_info *uloginfo2 = (struct ebt_ulog_info *)w2->data;
+
+	if (uloginfo1->nlgroup != uloginfo2->nlgroup ||
+	    uloginfo1->cprange != uloginfo2->cprange ||
+	    uloginfo1->qthreshold != uloginfo2->qthreshold ||
+	    strcmp(uloginfo1->prefix, uloginfo2->prefix))
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_watcher ulog_watcher =
+{
+	.name		= "ulog",
+	.size		= sizeof(struct ebt_ulog_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_watcher(&ulog_watcher);
+}
diff --git a/extensions/ebt_vlan.c b/extensions/ebt_vlan.c
new file mode 100644
index 0000000..0818d48
--- /dev/null
+++ b/extensions/ebt_vlan.c
@@ -0,0 +1,187 @@
+/* ebt_vlan
+ * 
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ * Nick Fedchik <nick@fedchik.org.ua> 
+ *
+ * June, 2002
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <ctype.h>
+#include "../include/ebtables_u.h"
+#include "../include/ethernetdb.h"
+#include <linux/netfilter_bridge/ebt_vlan.h>
+#include <linux/if_ether.h>
+
+#define NAME_VLAN_ID    "id"
+#define NAME_VLAN_PRIO  "prio"
+#define NAME_VLAN_ENCAP "encap"
+
+#define VLAN_ID    '1'
+#define VLAN_PRIO  '2'
+#define VLAN_ENCAP '3'
+
+static const struct option opts[] = {
+	{"vlan-id"   , required_argument, NULL, VLAN_ID},
+	{"vlan-prio" , required_argument, NULL, VLAN_PRIO},
+	{"vlan-encap", required_argument, NULL, VLAN_ENCAP},
+	{ 0 }
+};
+
+/*
+ * option inverse flags definition 
+ */
+#define OPT_VLAN_ID     0x01
+#define OPT_VLAN_PRIO   0x02
+#define OPT_VLAN_ENCAP  0x04
+#define OPT_VLAN_FLAGS	(OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP)
+
+struct ethertypeent *ethent;
+
+static void print_help()
+{
+	printf(
+"vlan options:\n"
+"--vlan-id [!] id       : vlan-tagged frame identifier, 0,1-4096 (integer)\n"
+"--vlan-prio [!] prio   : Priority-tagged frame's user priority, 0-7 (integer)\n"
+"--vlan-encap [!] encap : Encapsulated frame protocol (hexadecimal or name)\n");
+}
+
+static void init(struct ebt_entry_match *match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data;
+	vlaninfo->invflags = 0;
+	vlaninfo->bitmask = 0;
+}
+
+
+static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
+   unsigned int *flags, struct ebt_entry_match **match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) (*match)->data;
+	char *end;
+	struct ebt_vlan_info local;
+
+	switch (c) {
+	case VLAN_ID:
+		ebt_check_option2(flags, OPT_VLAN_ID);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_ID;
+		local.id = strtoul(optarg, &end, 10);
+		if (local.id > 4094 || *end != '\0')
+			ebt_print_error2("Invalid --vlan-id range ('%s')", optarg);
+		vlaninfo->id = local.id;
+		vlaninfo->bitmask |= EBT_VLAN_ID;
+		break;
+	case VLAN_PRIO:
+		ebt_check_option2(flags, OPT_VLAN_PRIO);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_PRIO;
+		local.prio = strtoul(optarg, &end, 10);
+		if (local.prio >= 8 || *end != '\0')
+			ebt_print_error2("Invalid --vlan-prio range ('%s')", optarg);
+		vlaninfo->prio = local.prio;
+		vlaninfo->bitmask |= EBT_VLAN_PRIO;
+		break;
+	case VLAN_ENCAP:
+		ebt_check_option2(flags, OPT_VLAN_ENCAP);
+		if (ebt_check_inverse2(optarg))
+			vlaninfo->invflags |= EBT_VLAN_ENCAP;
+		local.encap = strtoul(optarg, &end, 16);
+		if (*end != '\0') {
+			ethent = getethertypebyname(optarg);
+			if (ethent == NULL)
+				ebt_print_error("Unknown --vlan-encap value ('%s')", optarg);
+			local.encap = ethent->e_ethertype;
+		}
+		if (local.encap < ETH_ZLEN)
+			ebt_print_error2("Invalid --vlan-encap range ('%s')", optarg);
+		vlaninfo->encap = htons(local.encap);
+		vlaninfo->bitmask |= EBT_VLAN_ENCAP;
+		break;
+	default:
+		return 0;
+
+	}
+	return 1;
+}
+
+static void final_check(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match,
+   const char *name, unsigned int hookmask, unsigned int time)
+{
+	if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO)
+		ebt_print_error("For vlan filtering the protocol must be specified as 802_1Q");
+
+	/* Check if specified vlan-id=0 (priority-tagged frame condition) 
+	 * when vlan-prio was specified. */
+	/* I see no reason why a user should be prohibited to match on a perhaps impossible situation <BDS>
+	if (vlaninfo->bitmask & EBT_VLAN_PRIO &&
+	    vlaninfo->id && vlaninfo->bitmask & EBT_VLAN_ID)
+		ebt_print_error("When setting --vlan-prio the specified --vlan-id must be 0");*/
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_match *match)
+{
+	struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data;
+
+	if (vlaninfo->bitmask & EBT_VLAN_ID) {
+		printf("--vlan-id %s%d ", (vlaninfo->invflags & EBT_VLAN_ID) ? "! " : "", vlaninfo->id);
+	}
+	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+		printf("--vlan-prio %s%d ", (vlaninfo->invflags & EBT_VLAN_PRIO) ? "! " : "", vlaninfo->prio);
+	}
+	if (vlaninfo->bitmask & EBT_VLAN_ENCAP) {
+		printf("--vlan-encap %s", (vlaninfo->invflags & EBT_VLAN_ENCAP) ? "! " : "");
+		ethent = getethertypebynumber(ntohs(vlaninfo->encap));
+		if (ethent != NULL) {
+			printf("%s ", ethent->e_name);
+		} else {
+			printf("%4.4X ", ntohs(vlaninfo->encap));
+		}
+	}
+}
+
+static int compare(const struct ebt_entry_match *vlan1,
+   const struct ebt_entry_match *vlan2)
+{
+	struct ebt_vlan_info *vlaninfo1 = (struct ebt_vlan_info *) vlan1->data;
+	struct ebt_vlan_info *vlaninfo2 = (struct ebt_vlan_info *) vlan2->data;
+
+	if (vlaninfo1->bitmask != vlaninfo2->bitmask)
+		return 0;
+	if (vlaninfo1->invflags != vlaninfo2->invflags)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_ID &&
+	    vlaninfo1->id != vlaninfo2->id)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_PRIO &&
+	    vlaninfo1->prio != vlaninfo2->prio)
+		return 0;
+	if (vlaninfo1->bitmask & EBT_VLAN_ENCAP &&
+	    vlaninfo1->encap != vlaninfo2->encap)
+		return 0;
+	return 1;
+}
+
+static struct ebt_u_match vlan_match = {
+	.name		= "vlan",
+	.size		= sizeof(struct ebt_vlan_info),
+	.help		= print_help,
+	.init		= init,
+	.parse		= parse,
+	.final_check	= final_check,
+	.print		= print,
+	.compare	= compare,
+	.extra_ops	= opts,
+};
+
+static void _INIT(void)
+{
+	ebt_register_match(&vlan_match);
+}
diff --git a/extensions/ebtable_broute.c b/extensions/ebtable_broute.c
new file mode 100644
index 0000000..c106f08
--- /dev/null
+++ b/extensions/ebtable_broute.c
@@ -0,0 +1,29 @@
+/* ebtable_broute
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+
+static void print_help(const char **hn)
+{
+	printf("Supported chain for the broute table:\n");
+	printf("%s\n",hn[NF_BR_BROUTING]);
+}
+
+static struct
+ebt_u_table table =
+{
+	.name		= "broute",
+	.help		= print_help,
+};
+
+static void _INIT(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/extensions/ebtable_filter.c b/extensions/ebtable_filter.c
new file mode 100644
index 0000000..c0bf105
--- /dev/null
+++ b/extensions/ebtable_filter.c
@@ -0,0 +1,35 @@
+/* ebtable_filter
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \
+   (1 << NF_BR_LOCAL_OUT))
+
+static void print_help(const char **hn)
+{
+	int i;
+
+	printf("Supported chains for the filter table:\n");
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (FILTER_VALID_HOOKS & (1 << i))
+			printf("%s ", hn[i]);
+	printf("\n");
+}
+
+static struct ebt_u_table table =
+{
+	.name		= "filter",
+	.help		= print_help,
+};
+
+static void _INIT(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/extensions/ebtable_nat.c b/extensions/ebtable_nat.c
new file mode 100644
index 0000000..ee04482
--- /dev/null
+++ b/extensions/ebtable_nat.c
@@ -0,0 +1,36 @@
+/* ebtable_nat
+ *
+ * Authors:
+ * Bart De Schuymer <bdschuym@pandora.be>
+ *
+ * April, 2002
+ */
+
+#include <stdio.h>
+#include "../include/ebtables_u.h"
+
+#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \
+   (1 << NF_BR_POST_ROUTING))
+
+static void print_help(const char **hn)
+{
+	int i;
+
+	printf("Supported chains for the nat table:\n");
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (NAT_VALID_HOOKS & (1 << i))
+			printf("%s ", hn[i]);
+	printf("\n");
+}
+
+static struct
+ebt_u_table table =
+{
+	.name		= "nat",
+	.help		= print_help,
+};
+
+static void _INIT(void)
+{
+	ebt_register_table(&table);
+}
diff --git a/getethertype.c b/getethertype.c
new file mode 100644
index 0000000..e4e4812
--- /dev/null
+++ b/getethertype.c
@@ -0,0 +1,161 @@
+/*
+* getethertype.c
+*
+* This file was part of the NYS Library.
+*
+** The NYS Library is free software; you can redistribute it and/or
+** modify it under the terms of the GNU Library 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 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
+*/
+
+/********************************************************************
+* Description: Ethertype name service switch and the ethertypes 
+* database access functions
+* Author: Nick Fedchik <fnm@ukrsat.com>
+* Checker: Bart De Schuymer <bdschuym@pandora.be>
+* Origin: uClibc-0.9.16/libc/inet/getproto.c
+* Created at: Mon Nov 11 12:20:11 EET 2002
+********************************************************************/
+
+#include <ctype.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+
+#include "ethernetdb.h"
+
+#define	MAXALIASES	35
+
+static FILE *etherf = NULL;
+static char line[BUFSIZ + 1];
+static struct ethertypeent et_ent;
+static char *ethertype_aliases[MAXALIASES];
+static int ethertype_stayopen;
+
+void setethertypeent(int f)
+{
+	if (etherf == NULL)
+		etherf = fopen(_PATH_ETHERTYPES, "r");
+	else
+		rewind(etherf);
+	ethertype_stayopen |= f;
+}
+
+void endethertypeent(void)
+{
+	if (etherf) {
+		fclose(etherf);
+		etherf = NULL;
+	}
+	ethertype_stayopen = 0;
+}
+
+struct ethertypeent *getethertypeent(void)
+{
+	char *e;
+	char *endptr;
+	register char *cp, **q;
+
+	if (etherf == NULL
+	    && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) {
+		return (NULL);
+	}
+
+again:
+	if ((e = fgets(line, BUFSIZ, etherf)) == NULL) {
+		return (NULL);
+	}
+	if (*e == '#')
+		goto again;
+	cp = strpbrk(e, "#\n");
+	if (cp == NULL)
+		goto again;
+	*cp = '\0';
+	et_ent.e_name = e;
+	cp = strpbrk(e, " \t");
+	if (cp == NULL)
+		goto again;
+	*cp++ = '\0';
+	while (*cp == ' ' || *cp == '\t')
+		cp++;
+	e = strpbrk(cp, " \t");
+	if (e != NULL)
+		*e++ = '\0';
+// Check point
+	et_ent.e_ethertype = strtol(cp, &endptr, 16);
+	if (*endptr != '\0'
+	    || (et_ent.e_ethertype < ETH_ZLEN
+		|| et_ent.e_ethertype > 0xFFFF))
+		goto again;	// Skip invalid etherproto type entry
+	q = et_ent.e_aliases = ethertype_aliases;
+	if (e != NULL) {
+		cp = e;
+		while (cp && *cp) {
+			if (*cp == ' ' || *cp == '\t') {
+				cp++;
+				continue;
+			}
+			if (q < &ethertype_aliases[MAXALIASES - 1])
+				*q++ = cp;
+			cp = strpbrk(cp, " \t");
+			if (cp != NULL)
+				*cp++ = '\0';
+		}
+	}
+	*q = NULL;
+	return (&et_ent);
+}
+
+
+struct ethertypeent *getethertypebyname(const char *name)
+{
+	register struct ethertypeent *e;
+	register char **cp;
+
+	setethertypeent(ethertype_stayopen);
+	while ((e = getethertypeent()) != NULL) {
+		if (strcasecmp(e->e_name, name) == 0)
+			break;
+		for (cp = e->e_aliases; *cp != 0; cp++)
+			if (strcasecmp(*cp, name) == 0)
+				goto found;
+	}
+found:
+	if (!ethertype_stayopen)
+		endethertypeent();
+	return (e);
+}
+
+struct ethertypeent *getethertypebynumber(int type)
+{
+	register struct ethertypeent *e;
+
+	setethertypeent(ethertype_stayopen);
+	while ((e = getethertypeent()) != NULL)
+		if (e->e_ethertype == type)
+			break;
+	if (!ethertype_stayopen)
+		endethertypeent();
+	return (e);
+}
diff --git a/include/ebtables_u.h b/include/ebtables_u.h
new file mode 100644
index 0000000..901b282
--- /dev/null
+++ b/include/ebtables_u.h
@@ -0,0 +1,407 @@
+/*
+ * $Id: ebtables.c,v 1.03 2002/01/19
+ *
+ * Copyright (C) 2001-2002 Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * 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.
+ */
+
+#ifndef EBTABLES_U_H
+#define EBTABLES_U_H
+#include <netinet/in.h>
+#include <netinet/ether.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/netfilter/x_tables.h>
+
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP		132
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP		33
+#endif
+
+#define EXEC_STYLE_PRG		0
+#define EXEC_STYLE_DAEMON	1
+
+#ifndef EBT_MIN_ALIGN
+#define EBT_MIN_ALIGN (__alignof__(struct _xt_align))
+#endif
+#define EBT_ALIGN(s) (((s) + (EBT_MIN_ALIGN-1)) & ~(EBT_MIN_ALIGN-1))
+#define ERRORMSG_MAXLEN 128
+
+#define _INIT __attribute__((constructor)) _init
+
+struct ebt_u_entries
+{
+	int policy;
+	unsigned int nentries;
+	/* counter offset for this chain */
+	unsigned int counter_offset;
+	/* used for udc */
+	unsigned int hook_mask;
+	char *kernel_start;
+	char name[EBT_CHAIN_MAXNAMELEN];
+	struct ebt_u_entry *entries;
+};
+
+struct ebt_cntchanges
+{
+	unsigned short type;
+	unsigned short change; /* determines incremental/decremental/change */
+	struct ebt_cntchanges *prev;
+	struct ebt_cntchanges *next;
+};
+
+#define EBT_ORI_MAX_CHAINS 10
+struct ebt_u_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	unsigned int num_chains;
+	unsigned int max_chains;
+	struct ebt_u_entries **chains;
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+	struct ebt_counter *counters;
+	/*
+	 * can be used e.g. to know if a standard option
+	 * has been specified twice
+	 */
+	unsigned int flags;
+	/* we stick the specified command (e.g. -A) in here */
+	char command;
+	/*
+	 * here we stick the chain to do our thing on (can be -1 if unspecified)
+	 */
+	int selected_chain;
+	/* used for the atomic option */
+	char *filename;
+	/* tells what happened to the old rules (counter changes) */
+	struct ebt_cntchanges *cc;
+};
+
+struct ebt_u_table
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	void (*check)(struct ebt_u_replace *repl);
+	void (*help)(const char **);
+	struct ebt_u_table *next;
+};
+
+struct ebt_u_match_list
+{
+	struct ebt_u_match_list *next;
+	struct ebt_entry_match *m;
+};
+
+struct ebt_u_watcher_list
+{
+	struct ebt_u_watcher_list *next;
+	struct ebt_entry_watcher *w;
+};
+
+struct ebt_u_entry
+{
+	unsigned int bitmask;
+	unsigned int invflags;
+	uint16_t ethproto;
+	char in[IFNAMSIZ];
+	char logical_in[IFNAMSIZ];
+	char out[IFNAMSIZ];
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	struct ebt_u_match_list *m_list;
+	struct ebt_u_watcher_list *w_list;
+	struct ebt_entry_target *t;
+	struct ebt_u_entry *prev;
+	struct ebt_u_entry *next;
+	struct ebt_counter cnt;
+	struct ebt_counter cnt_surplus; /* for increasing/decreasing a counter and for option 'C' */
+	struct ebt_cntchanges *cc;
+	/* the standard target needs this to know the name of a udc when
+	 * printing out rules. */
+	struct ebt_u_replace *replace;
+};
+
+struct ebt_u_match
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	uint8_t revision;
+	/* size of the real match data */
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_match *m);
+	int (*parse)(int c, char **argv, int argc,
+	        const struct ebt_u_entry *entry, unsigned int *flags,
+	        struct ebt_entry_match **match);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_match *match,
+	   const char *name, unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_match *match);
+	int (*compare)(const struct ebt_entry_match *m1,
+	   const struct ebt_entry_match *m2);
+	const struct option *extra_ops;
+	/*
+	 * can be used e.g. to check for multiple occurance of the same option
+	 */
+	unsigned int flags;
+	unsigned int option_offset;
+	struct ebt_entry_match *m;
+	/*
+	 * if used == 1 we no longer have to add it to
+	 * the match chain of the new entry
+	 * be sure to put it back on 0 when finished
+	 */
+	unsigned int used;
+	struct ebt_u_match *next;
+};
+
+struct ebt_u_watcher
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_watcher *w);
+	int (*parse)(int c, char **argv, int argc,
+	   const struct ebt_u_entry *entry, unsigned int *flags,
+	   struct ebt_entry_watcher **watcher);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_watcher *watch, const char *name,
+	   unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_watcher *watcher);
+	int (*compare)(const struct ebt_entry_watcher *w1,
+	   const struct ebt_entry_watcher *w2);
+	const struct option *extra_ops;
+	unsigned int flags;
+	unsigned int option_offset;
+	struct ebt_entry_watcher *w;
+	unsigned int used;
+	struct ebt_u_watcher *next;
+};
+
+struct ebt_u_target
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	unsigned int size;
+	void (*help)(void);
+	void (*init)(struct ebt_entry_target *t);
+	int (*parse)(int c, char **argv, int argc,
+	   const struct ebt_u_entry *entry, unsigned int *flags,
+	   struct ebt_entry_target **target);
+	void (*final_check)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_target *target, const char *name,
+	   unsigned int hookmask, unsigned int time);
+	void (*print)(const struct ebt_u_entry *entry,
+	   const struct ebt_entry_target *target);
+	int (*compare)(const struct ebt_entry_target *t1,
+	   const struct ebt_entry_target *t2);
+	const struct option *extra_ops;
+	unsigned int option_offset;
+	unsigned int flags;
+	struct ebt_entry_target *t;
+	unsigned int used;
+	struct ebt_u_target *next;
+};
+
+
+struct ebt_icmp_names {
+	const char *name;
+	uint8_t type;
+	uint8_t code_min, code_max;
+};
+
+
+
+/* libebtc.c */
+
+extern struct ebt_u_table *ebt_tables;
+extern struct ebt_u_match *ebt_matches;
+extern struct ebt_u_watcher *ebt_watchers;
+extern struct ebt_u_target *ebt_targets;
+
+extern int use_lockfd;
+
+void ebt_register_table(struct ebt_u_table *);
+void ebt_register_match(struct ebt_u_match *);
+void ebt_register_watcher(struct ebt_u_watcher *);
+void ebt_register_target(struct ebt_u_target *t);
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init);
+struct ebt_u_target *ebt_find_target(const char *name);
+struct ebt_u_match *ebt_find_match(const char *name);
+struct ebt_u_watcher *ebt_find_watcher(const char *name);
+struct ebt_u_table *ebt_find_table(const char *name);
+int ebtables_insmod(const char *modname);
+void ebt_list_extensions();
+void ebt_initialize_entry(struct ebt_u_entry *e);
+void ebt_cleanup_replace(struct ebt_u_replace *replace);
+void ebt_reinit_extensions();
+void ebt_double_chains(struct ebt_u_replace *replace);
+void ebt_free_u_entry(struct ebt_u_entry *e);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+				    const char* arg);
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace,
+				    const char* arg);
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg);
+/**/
+void ebt_change_policy(struct ebt_u_replace *replace, int policy);
+void ebt_flush_chains(struct ebt_u_replace *replace);
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+			  struct ebt_u_entry *new_entry);
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry,
+		  int rule_nr);
+void ebt_delete_rule(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end);
+void ebt_zero_counters(struct ebt_u_replace *replace);
+void ebt_change_counters(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end,
+		     struct ebt_counter *cnt, int mask);
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy);
+void ebt_delete_chain(struct ebt_u_replace *replace);
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name);
+/**/
+void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
+			 struct ebt_u_entries *entries);
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err);
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+			      int print_err);
+void ebt_check_for_loops(struct ebt_u_replace *replace);
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m);
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w);
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *));
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *));
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *));
+void __ebt_print_bug(char *file, int line, char *format, ...);
+void __ebt_print_error(char *format, ...);
+
+/* communication.c */
+
+int ebt_get_table(struct ebt_u_replace *repl, int init);
+void ebt_deliver_counters(struct ebt_u_replace *repl);
+void ebt_deliver_table(struct ebt_u_replace *repl);
+
+/* useful_functions.c */
+
+extern int ebt_invert;
+void ebt_check_option(unsigned int *flags, unsigned int mask);
+#define ebt_check_inverse(arg) _ebt_check_inverse(arg, argc, argv)
+int _ebt_check_inverse(const char option[], int argc, char **argv);
+void ebt_print_mac(const unsigned char *mac);
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask);
+int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk);
+char *ebt_mask_to_dotted(uint32_t mask);
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr,
+						   struct in6_addr *msk);
+char *ebt_ip6_to_numeric(const struct in6_addr *addrp);
+char *ebt_ip6_mask_to_string(const struct in6_addr *msk);
+
+int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
+		   const char *icmptype, uint8_t type[], uint8_t code[]);
+void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes,
+			 size_t n_codes, uint8_t *type, uint8_t *code);
+void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes,
+			  size_t n_codes);
+
+int do_command(int argc, char *argv[], int exec_style,
+               struct ebt_u_replace *replace_);
+
+struct ethertypeent *parseethertypebynumber(int type);
+
+#define ebt_to_chain(repl)				\
+({struct ebt_u_entries *_ch = NULL;			\
+if (repl->selected_chain != -1)				\
+	_ch = repl->chains[repl->selected_chain];	\
+_ch;})
+#define ebt_print_bug(format, args...) \
+   __ebt_print_bug(__FILE__, __LINE__, format, ##args)
+#define ebt_print_error(format,args...) __ebt_print_error(format, ##args);
+#define ebt_print_error2(format, args...) do {__ebt_print_error(format, ##args); \
+   return -1;} while (0)
+#define ebt_print_error3(format, args...) do {__ebt_print_error(format, ##args); \
+   return;} while (0)
+#define ebt_check_option2(flags,mask)	\
+({ebt_check_option(flags,mask);		\
+ if (ebt_errormsg[0] != '\0')		\
+	return -1;})
+#define ebt_check_inverse2(option)					\
+({int __ret = ebt_check_inverse(option);				\
+if (ebt_errormsg[0] != '\0')						\
+	return -1;							\
+if (!optarg) {								\
+	__ebt_print_error("Option without (mandatory) argument");	\
+	return -1;							\
+}									\
+__ret;})
+#define ebt_print_memory() do {printf("Ebtables: " __FILE__ \
+   " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);} while (0)
+
+/* used for keeping the rule counters right during rule adds or deletes */
+#define CNT_NORM 	0
+#define CNT_DEL 	1
+#define CNT_ADD 	2
+#define CNT_CHANGE 	3
+
+extern const char *ebt_hooknames[NF_BR_NUMHOOKS];
+extern const char *ebt_standard_targets[NUM_STANDARD_TARGETS];
+extern char ebt_errormsg[ERRORMSG_MAXLEN];
+extern char *ebt_modprobe;
+extern int ebt_silent;
+extern int ebt_printstyle_mac;
+
+/*
+ * Transforms a target string into the right integer,
+ * returns 0 on success.
+ */
+#define FILL_TARGET(_str, _pos) ({                            \
+	int _i, _ret = 0;                                     \
+	for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++)         \
+		if (!strcmp(_str, ebt_standard_targets[_i])) {\
+			_pos = -_i - 1;                       \
+			break;                                \
+		}                                             \
+	if (_i == NUM_STANDARD_TARGETS)                       \
+		_ret = 1;                                     \
+	_ret;                                                 \
+})
+
+/* Transforms the target value to an index into standard_targets[] */
+#define TARGET_INDEX(_value) (-_value - 1)
+/* Returns a target string corresponding to the value */
+#define TARGET_NAME(_value) (ebt_standard_targets[TARGET_INDEX(_value)])
+/* True if the hook mask denotes that the rule is in a base chain */
+#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS))
+/* Clear the bit in the hook_mask that tells if the rule is on a base chain */
+#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS))
+#define PRINT_VERSION printf(PROGNAME" v"PROGVERSION" (legacy) ("PROGDATE")\n")
+#ifndef PROC_SYS_MODPROBE
+#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
+#endif
+#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(x)	(sizeof(x) / sizeof((x)[0]))
+#endif
+#endif /* EBTABLES_U_H */
diff --git a/include/ethernetdb.h b/include/ethernetdb.h
new file mode 100644
index 0000000..1683abe
--- /dev/null
+++ b/include/ethernetdb.h
@@ -0,0 +1,57 @@
+/*
+* 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
+*/
+
+/* All data returned by the network data base library are supplied in
+   host order and returned in network order (suitable for use in
+   system calls).  */
+
+#ifndef	_ETHERNETDB_H
+#define	_ETHERNETDB_H	1
+
+#include <features.h>
+#include <netinet/in.h>
+#include <stdint.h>
+
+/* Absolute file name for network data base files.  */
+#ifndef	_PATH_ETHERTYPES
+#define	_PATH_ETHERTYPES	"/etc/ethertypes"
+#endif				/* _PATH_ETHERTYPES */
+
+struct ethertypeent {
+	char *e_name;		/* Official ethernet type name.  */
+	char **e_aliases;	/* Alias list.  */
+	int e_ethertype;	/* Ethernet type number.  */
+};
+
+/* Open ethertype data base files and mark them as staying open even
+   after a later search if STAY_OPEN is non-zero.  */
+extern void setethertypeent(int __stay_open);
+
+/* Close ethertype data base files and clear `stay open' flag.  */
+extern void endethertypeent(void);
+
+/* Get next entry from ethertype data base file.  Open data base if
+   necessary.  */
+extern struct ethertypeent *getethertypeent(void);
+
+/* Return entry from ethertype data base for network with NAME.  */
+extern struct ethertypeent *getethertypebyname(__const char *__name);
+
+/* Return entry from ethertype data base which number is PROTO.  */
+extern struct ethertypeent *getethertypebynumber(int __ethertype);
+
+
+#endif				/* ethernetdb.h */
diff --git a/include/linux/netfilter/xt_AUDIT.h b/include/linux/netfilter/xt_AUDIT.h
new file mode 100644
index 0000000..44111b2
--- /dev/null
+++ b/include/linux/netfilter/xt_AUDIT.h
@@ -0,0 +1,30 @@
+/*
+ * Header file for iptables xt_AUDIT target
+ *
+ * (C) 2010-2011 Thomas Graf <tgraf@redhat.com>
+ * (C) 2010-2011 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _XT_AUDIT_TARGET_H
+#define _XT_AUDIT_TARGET_H
+
+#include <linux/types.h>
+
+enum {
+	XT_AUDIT_TYPE_ACCEPT = 0,
+	XT_AUDIT_TYPE_DROP,
+	XT_AUDIT_TYPE_REJECT,
+	__XT_AUDIT_TYPE_MAX,
+};
+
+#define XT_AUDIT_TYPE_MAX (__XT_AUDIT_TYPE_MAX - 1)
+
+struct xt_AUDIT_info {
+	__u8 type; /* XT_AUDIT_TYPE_* */
+};
+
+#endif /* _XT_AUDIT_TARGET_H */
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
new file mode 100644
index 0000000..c4dbfd9
--- /dev/null
+++ b/include/linux/netfilter_bridge.h
@@ -0,0 +1,27 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter. 
+ */
+
+#include <linux/netfilter.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING	0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN		1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD		2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT		3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING	4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING		5
+#define NF_BR_NUMHOOKS		6
+
+#endif /* __LINUX_BRIDGE_NETFILTER_H */
diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h
new file mode 100644
index 0000000..70028c1
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_802_3.h
@@ -0,0 +1,63 @@
+#ifndef __LINUX_BRIDGE_EBT_802_3_H
+#define __LINUX_BRIDGE_EBT_802_3_H
+
+#include <linux/types.h>
+#include <linux/if_ether.h>
+
+#define EBT_802_3_SAP 0x01
+#define EBT_802_3_TYPE 0x02
+
+#define EBT_802_3_MATCH "802_3"
+
+/*
+ * If frame has DSAP/SSAP value 0xaa you must check the SNAP type
+ * to discover what kind of packet we're carrying. 
+ */
+#define CHECK_TYPE 0xaa
+
+/*
+ * Control field may be one or two bytes.  If the first byte has
+ * the value 0x03 then the entire length is one byte, otherwise it is two.
+ * One byte controls are used in Unnumbered Information frames.
+ * Two byte controls are used in Numbered Information frames.
+ */
+#define IS_UI 0x03
+
+#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3)
+
+/* ui has one byte ctrl, ni has two */
+struct hdr_ui {
+	__u8 dsap;
+	__u8 ssap;
+	__u8 ctrl;
+	__u8 orig[3];
+	__be16 type;
+};
+
+struct hdr_ni {
+	__u8 dsap;
+	__u8 ssap;
+	__be16 ctrl;
+	__u8  orig[3];
+	__be16 type;
+};
+
+struct ebt_802_3_hdr {
+	__u8  daddr[ETH_ALEN];
+	__u8  saddr[ETH_ALEN];
+	__be16 len;
+	union {
+		struct hdr_ui ui;
+		struct hdr_ni ni;
+	} llc;
+};
+
+
+struct ebt_802_3_info {
+	__u8  sap;
+	__be16 type;
+	__u8  bitmask;
+	__u8  invflags;
+};
+
+#endif /* __LINUX_BRIDGE_EBT_802_3_H */
diff --git a/include/linux/netfilter_bridge/ebt_among.h b/include/linux/netfilter_bridge/ebt_among.h
new file mode 100644
index 0000000..bd4e3ad
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_among.h
@@ -0,0 +1,64 @@
+#ifndef __LINUX_BRIDGE_EBT_AMONG_H
+#define __LINUX_BRIDGE_EBT_AMONG_H
+
+#include <linux/types.h>
+
+#define EBT_AMONG_DST 0x01
+#define EBT_AMONG_SRC 0x02
+
+/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003
+ * 
+ * Write-once-read-many hash table, used for checking if a given
+ * MAC address belongs to a set or not and possibly for checking
+ * if it is related with a given IPv4 address.
+ *
+ * The hash value of an address is its last byte.
+ * 
+ * In real-world ethernet addresses, values of the last byte are
+ * evenly distributed and there is no need to consider other bytes.
+ * It would only slow the routines down.
+ *
+ * For MAC address comparison speedup reasons, we introduce a trick.
+ * MAC address is mapped onto an array of two 32-bit integers.
+ * This pair of integers is compared with MAC addresses in the
+ * hash table, which are stored also in form of pairs of integers
+ * (in `cmp' array). This is quick as it requires only two elementary
+ * number comparisons in worst case. Further, we take advantage of
+ * fact that entropy of 3 last bytes of address is larger than entropy
+ * of 3 first bytes. So first we compare 4 last bytes of addresses and
+ * if they are the same we compare 2 first.
+ *
+ * Yes, it is a memory overhead, but in 2003 AD, who cares?
+ */
+
+struct ebt_mac_wormhash_tuple {
+	__u32 cmp[2];
+	__be32 ip;
+};
+
+struct ebt_mac_wormhash {
+	int table[257];
+	int poolsize;
+	struct ebt_mac_wormhash_tuple pool[0];
+};
+
+#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \
+		+ (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0)
+
+struct ebt_among_info {
+	int wh_dst_ofs;
+	int wh_src_ofs;
+	int bitmask;
+};
+
+#define EBT_AMONG_DST_NEG 0x1
+#define EBT_AMONG_SRC_NEG 0x2
+
+#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL)
+#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \
+	(struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL)
+
+#define EBT_AMONG_MATCH "among"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h
new file mode 100644
index 0000000..522f3e4
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_arp.h
@@ -0,0 +1,36 @@
+#ifndef __LINUX_BRIDGE_EBT_ARP_H
+#define __LINUX_BRIDGE_EBT_ARP_H
+
+#include <linux/types.h>
+
+#define EBT_ARP_OPCODE 0x01
+#define EBT_ARP_HTYPE 0x02
+#define EBT_ARP_PTYPE 0x04
+#define EBT_ARP_SRC_IP 0x08
+#define EBT_ARP_DST_IP 0x10
+#define EBT_ARP_SRC_MAC 0x20
+#define EBT_ARP_DST_MAC 0x40
+#define EBT_ARP_GRAT 0x80
+#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \
+   EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC | \
+   EBT_ARP_GRAT)
+#define EBT_ARP_MATCH "arp"
+
+struct ebt_arp_info
+{
+	__be16 htype;
+	__be16 ptype;
+	__be16 opcode;
+	__be32 saddr;
+	__be32 smsk;
+	__be32 daddr;
+	__be32 dmsk;
+	unsigned char smaddr[ETH_ALEN];
+	unsigned char smmsk[ETH_ALEN];
+	unsigned char dmaddr[ETH_ALEN];
+	unsigned char dmmsk[ETH_ALEN];
+	__u8  bitmask;
+	__u8  invflags;
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_arpreply.h b/include/linux/netfilter_bridge/ebt_arpreply.h
new file mode 100644
index 0000000..7e77896
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_arpreply.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H
+#define __LINUX_BRIDGE_EBT_ARPREPLY_H
+
+struct ebt_arpreply_info {
+	unsigned char mac[ETH_ALEN];
+	int target;
+};
+#define EBT_ARPREPLY_TARGET "arpreply"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h
new file mode 100644
index 0000000..46d6261
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_ip.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ *  ebt_ip
+ *
+ *	Authors:
+ *	Bart De Schuymer <bart.de.schuymer@pandora.be>
+ *
+ *  April, 2002
+ *
+ *  Changes:
+ *    added ip-sport and ip-dport
+ *    Innominate Security Technologies AG <mhopf@innominate.com>
+ *    September, 2002
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP_H
+#define __LINUX_BRIDGE_EBT_IP_H
+
+#include <linux/types.h>
+
+#define EBT_IP_SOURCE 0x01
+#define EBT_IP_DEST 0x02
+#define EBT_IP_TOS 0x04
+#define EBT_IP_PROTO 0x08
+#define EBT_IP_SPORT 0x10
+#define EBT_IP_DPORT 0x20
+#define EBT_IP_ICMP 0x40
+#define EBT_IP_IGMP 0x80
+#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\
+		     EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP)
+#define EBT_IP_MATCH "ip"
+
+/* the same values are used for the invflags */
+struct ebt_ip_info {
+	__be32 saddr;
+	__be32 daddr;
+	__be32 smsk;
+	__be32 dmsk;
+	__u8  tos;
+	__u8  protocol;
+	__u8  bitmask;
+	__u8  invflags;
+	union {
+		__u16 sport[2];
+		__u8 icmp_type[2];
+		__u8 igmp_type[2];
+	};
+	union {
+		__u16 dport[2];
+		__u8 icmp_code[2];
+	};
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h
new file mode 100644
index 0000000..42b8896
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_ip6.h
@@ -0,0 +1,50 @@
+/*
+ *  ebt_ip6
+ *
+ *	Authors:
+ * Kuo-Lang Tseng <kuo-lang.tseng@intel.com>
+ * Manohar Castelino <manohar.r.castelino@intel.com>
+ *
+ *  Jan 11, 2008
+ *
+ */
+
+#ifndef __LINUX_BRIDGE_EBT_IP6_H
+#define __LINUX_BRIDGE_EBT_IP6_H
+
+#include <linux/types.h>
+
+#define EBT_IP6_SOURCE 0x01
+#define EBT_IP6_DEST 0x02
+#define EBT_IP6_TCLASS 0x04
+#define EBT_IP6_PROTO 0x08
+#define EBT_IP6_SPORT 0x10
+#define EBT_IP6_DPORT 0x20
+#define EBT_IP6_ICMP6 0x40
+
+#define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\
+		      EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT | \
+		      EBT_IP6_ICMP6)
+#define EBT_IP6_MATCH "ip6"
+
+/* the same values are used for the invflags */
+struct ebt_ip6_info {
+	struct in6_addr saddr;
+	struct in6_addr daddr;
+	struct in6_addr smsk;
+	struct in6_addr dmsk;
+	__u8  tclass;
+	__u8  protocol;
+	__u8  bitmask;
+	__u8  invflags;
+	union {
+		__u16 sport[2];
+		__u8 icmpv6_type[2];
+	};
+	union {
+		__u16 dport[2];
+		__u8 icmpv6_code[2];
+	};
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_limit.h b/include/linux/netfilter_bridge/ebt_limit.h
new file mode 100644
index 0000000..66d80b3
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_limit.h
@@ -0,0 +1,24 @@
+#ifndef __LINUX_BRIDGE_EBT_LIMIT_H
+#define __LINUX_BRIDGE_EBT_LIMIT_H
+
+#include <linux/types.h>
+
+#define EBT_LIMIT_MATCH "limit"
+
+/* timings are in milliseconds. */
+#define EBT_LIMIT_SCALE 10000
+
+/* 1/10,000 sec period => max of 10,000/sec.  Min rate is then 429490
+   seconds, or one every 59 hours. */
+
+struct ebt_limit_info {
+	__u32 avg;    /* Average secs between packets * scale */
+	__u32 burst;  /* Period multiplier for upper limit. */
+
+	/* Used internally by the kernel */
+	unsigned long prev;
+	__u32 credit;
+	__u32 credit_cap, cost;
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h
new file mode 100644
index 0000000..7e7f1d1
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_log.h
@@ -0,0 +1,20 @@
+#ifndef __LINUX_BRIDGE_EBT_LOG_H
+#define __LINUX_BRIDGE_EBT_LOG_H
+
+#include <linux/types.h>
+
+#define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */
+#define EBT_LOG_ARP 0x02
+#define EBT_LOG_NFLOG 0x04
+#define EBT_LOG_IP6 0x08
+#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP | EBT_LOG_IP6)
+#define EBT_LOG_PREFIX_SIZE 30
+#define EBT_LOG_WATCHER "log"
+
+struct ebt_log_info {
+	__u8 loglevel;
+	__u8 prefix[EBT_LOG_PREFIX_SIZE];
+	__u32 bitmask;
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_m.h b/include/linux/netfilter_bridge/ebt_mark_m.h
new file mode 100644
index 0000000..410f9e5
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_mark_m.h
@@ -0,0 +1,16 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_M_H
+#define __LINUX_BRIDGE_EBT_MARK_M_H
+
+#include <linux/types.h>
+
+#define EBT_MARK_AND 0x01
+#define EBT_MARK_OR 0x02
+#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR)
+struct ebt_mark_m_info {
+	unsigned long mark, mask;
+	__u8 invert;
+	__u8 bitmask;
+};
+#define EBT_MARK_MATCH "mark_m"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_t.h b/include/linux/netfilter_bridge/ebt_mark_t.h
new file mode 100644
index 0000000..7d5a268
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_mark_t.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_MARK_T_H
+#define __LINUX_BRIDGE_EBT_MARK_T_H
+
+/* The target member is reused for adding new actions, the
+ * value of the real target is -1 to -NUM_STANDARD_TARGETS.
+ * For backward compatibility, the 4 lsb (2 would be enough,
+ * but let's play it safe) are kept to designate this target.
+ * The remaining bits designate the action. By making the set
+ * action 0xfffffff0, the result will look ok for older
+ * versions. [September 2006] */
+#define MARK_SET_VALUE (0xfffffff0)
+#define MARK_OR_VALUE  (0xffffffe0)
+#define MARK_AND_VALUE (0xffffffd0)
+#define MARK_XOR_VALUE (0xffffffc0)
+
+struct ebt_mark_t_info {
+	unsigned long mark;
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_MARK_TARGET "mark"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_nat.h b/include/linux/netfilter_bridge/ebt_nat.h
new file mode 100644
index 0000000..5e74e3b
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_nat.h
@@ -0,0 +1,13 @@
+#ifndef __LINUX_BRIDGE_EBT_NAT_H
+#define __LINUX_BRIDGE_EBT_NAT_H
+
+#define NAT_ARP_BIT  (0x00000010)
+struct ebt_nat_info {
+	unsigned char mac[ETH_ALEN];
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_SNAT_TARGET "snat"
+#define EBT_DNAT_TARGET "dnat"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_nflog.h b/include/linux/netfilter_bridge/ebt_nflog.h
new file mode 100644
index 0000000..df829fc
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_nflog.h
@@ -0,0 +1,23 @@
+#ifndef __LINUX_BRIDGE_EBT_NFLOG_H
+#define __LINUX_BRIDGE_EBT_NFLOG_H
+
+#include <linux/types.h>
+
+#define EBT_NFLOG_MASK 0x0
+
+#define EBT_NFLOG_PREFIX_SIZE 64
+#define EBT_NFLOG_WATCHER "nflog"
+
+#define EBT_NFLOG_DEFAULT_GROUP		0x1
+#define EBT_NFLOG_DEFAULT_THRESHOLD	1
+
+struct ebt_nflog_info {
+	__u32 len;
+	__u16 group;
+	__u16 threshold;
+	__u16 flags;
+	__u16 pad;
+	char prefix[EBT_NFLOG_PREFIX_SIZE];
+};
+
+#endif				/* __LINUX_BRIDGE_EBT_NFLOG_H */
diff --git a/include/linux/netfilter_bridge/ebt_pkttype.h b/include/linux/netfilter_bridge/ebt_pkttype.h
new file mode 100644
index 0000000..c241bad
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_pkttype.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H
+#define __LINUX_BRIDGE_EBT_PKTTYPE_H
+
+#include <linux/types.h>
+
+struct ebt_pkttype_info {
+	__u8 pkt_type;
+	__u8 invert;
+};
+#define EBT_PKTTYPE_MATCH "pkttype"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_redirect.h b/include/linux/netfilter_bridge/ebt_redirect.h
new file mode 100644
index 0000000..dd9622c
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_redirect.h
@@ -0,0 +1,10 @@
+#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H
+#define __LINUX_BRIDGE_EBT_REDIRECT_H
+
+struct ebt_redirect_info {
+	/* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */
+	int target;
+};
+#define EBT_REDIRECT_TARGET "redirect"
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_stp.h b/include/linux/netfilter_bridge/ebt_stp.h
new file mode 100644
index 0000000..1025b9f
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_stp.h
@@ -0,0 +1,46 @@
+#ifndef __LINUX_BRIDGE_EBT_STP_H
+#define __LINUX_BRIDGE_EBT_STP_H
+
+#include <linux/types.h>
+
+#define EBT_STP_TYPE		0x0001
+
+#define EBT_STP_FLAGS		0x0002
+#define EBT_STP_ROOTPRIO	0x0004
+#define EBT_STP_ROOTADDR	0x0008
+#define EBT_STP_ROOTCOST	0x0010
+#define EBT_STP_SENDERPRIO	0x0020
+#define EBT_STP_SENDERADDR	0x0040
+#define EBT_STP_PORT		0x0080
+#define EBT_STP_MSGAGE		0x0100
+#define EBT_STP_MAXAGE		0x0200
+#define EBT_STP_HELLOTIME	0x0400
+#define EBT_STP_FWDD		0x0800
+
+#define EBT_STP_MASK		0x0fff
+#define EBT_STP_CONFIG_MASK	0x0ffe
+
+#define EBT_STP_MATCH "stp"
+
+struct ebt_stp_config_info {
+	__u8 flags;
+	__u16 root_priol, root_priou;
+	char root_addr[6], root_addrmsk[6];
+	__u32 root_costl, root_costu;
+	__u16 sender_priol, sender_priou;
+	char sender_addr[6], sender_addrmsk[6];
+	__u16 portl, portu;
+	__u16 msg_agel, msg_ageu;
+	__u16 max_agel, max_ageu;
+	__u16 hello_timel, hello_timeu;
+	__u16 forward_delayl, forward_delayu;
+};
+
+struct ebt_stp_info {
+	__u8 type;
+	struct ebt_stp_config_info config;
+	__u16 bitmask;
+	__u16 invflags;
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebt_ulog.h b/include/linux/netfilter_bridge/ebt_ulog.h
new file mode 100644
index 0000000..89a6bec
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_ulog.h
@@ -0,0 +1,38 @@
+#ifndef _EBT_ULOG_H
+#define _EBT_ULOG_H
+
+#include <linux/types.h>
+
+#define EBT_ULOG_DEFAULT_NLGROUP 0
+#define EBT_ULOG_DEFAULT_QTHRESHOLD 1
+#define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */
+#define EBT_ULOG_PREFIX_LEN 32
+#define EBT_ULOG_MAX_QLEN 50
+#define EBT_ULOG_WATCHER "ulog"
+#define EBT_ULOG_VERSION 1
+
+struct ebt_ulog_info {
+	__u32 nlgroup;
+	unsigned int cprange;
+	unsigned int qthreshold;
+	char prefix[EBT_ULOG_PREFIX_LEN];
+};
+
+typedef struct ebt_ulog_packet_msg {
+	int version;
+	char indev[IFNAMSIZ];
+	char outdev[IFNAMSIZ];
+	char physindev[IFNAMSIZ];
+	char physoutdev[IFNAMSIZ];
+	char prefix[EBT_ULOG_PREFIX_LEN];
+	struct timeval stamp;
+	unsigned long mark;
+	unsigned int hook;
+	size_t data_len;
+	/* The complete packet, including Ethernet header and perhaps
+	 * the VLAN header is appended */
+	unsigned char data[0] __attribute__
+	                      ((aligned (__alignof__(struct ebt_ulog_info))));
+} ebt_ulog_packet_msg_t;
+
+#endif /* _EBT_ULOG_H */
diff --git a/include/linux/netfilter_bridge/ebt_vlan.h b/include/linux/netfilter_bridge/ebt_vlan.h
new file mode 100644
index 0000000..967d1d5
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebt_vlan.h
@@ -0,0 +1,22 @@
+#ifndef __LINUX_BRIDGE_EBT_VLAN_H
+#define __LINUX_BRIDGE_EBT_VLAN_H
+
+#include <linux/types.h>
+
+#define EBT_VLAN_ID	0x01
+#define EBT_VLAN_PRIO	0x02
+#define EBT_VLAN_ENCAP	0x04
+#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP)
+#define EBT_VLAN_MATCH "vlan"
+
+struct ebt_vlan_info {
+	__u16 id;		/* VLAN ID {1-4095} */
+	__u8 prio;		/* VLAN User Priority {0-7} */
+	__be16 encap;		/* VLAN Encapsulated frame code {0-65535} */
+	__u8 bitmask;		/* Args bitmask bit 1=1 - ID arg,
+				   bit 2=1 User-Priority arg, bit 3=1 encap*/
+	__u8 invflags;		/* Inverse bitmask  bit 1=1 - inversed ID arg, 
+				   bit 2=1 - inversed Pirority arg */
+};
+
+#endif
diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
new file mode 100644
index 0000000..5be75f2
--- /dev/null
+++ b/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ *  ebtables
+ *
+ *	Authors:
+ *	Bart De Schuymer		<bdschuym@pandora.be>
+ *
+ *  ebtables.c,v 2.0, April, 2002
+ *
+ *  This code is strongly inspired by the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ */
+
+#ifndef __LINUX_BRIDGE_EFF_H
+#define __LINUX_BRIDGE_EFF_H
+#include <linux/types.h>
+#include <linux/if.h>
+#include <linux/netfilter_bridge.h>
+
+#define EBT_TABLE_MAXNAMELEN 32
+#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
+#define EBT_EXTENSION_MAXNAMELEN 31
+
+/* verdicts >0 are "branches" */
+#define EBT_ACCEPT   -1
+#define EBT_DROP     -2
+#define EBT_CONTINUE -3
+#define EBT_RETURN   -4
+#define NUM_STANDARD_TARGETS   4
+/* ebtables target modules store the verdict inside an int. We can
+ * reclaim a part of this int for backwards compatible extensions.
+ * The 4 lsb are more than enough to store the verdict. */
+#define EBT_VERDICT_BITS 0x0000000F
+
+struct xt_match;
+struct xt_target;
+
+struct ebt_counter {
+	__u64 pcnt;
+	__u64 bcnt;
+};
+
+struct ebt_replace {
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	/* total size of the entries */
+	unsigned int entries_size;
+	/* start of the chains */
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+	struct ebt_counter *counters;
+	char *entries;
+};
+
+struct ebt_replace_kernel {
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	/* nr of rules in the table */
+	unsigned int nentries;
+	/* total size of the entries */
+	unsigned int entries_size;
+	/* start of the chains */
+	struct ebt_entries *hook_entry[NF_BR_NUMHOOKS];
+	/* nr of counters userspace expects back */
+	unsigned int num_counters;
+	/* where the kernel will put the old counters */
+	struct ebt_counter *counters;
+	char *entries;
+};
+
+struct ebt_entries {
+	/* this field is always set to zero
+	 * See EBT_ENTRY_OR_ENTRIES.
+	 * Must be same size as ebt_entry.bitmask */
+	unsigned int distinguisher;
+	/* the chain name */
+	char name[EBT_CHAIN_MAXNAMELEN];
+	/* counter offset for this chain */
+	unsigned int counter_offset;
+	/* one standard (accept, drop, return) per hook */
+	int policy;
+	/* nr. of entries */
+	unsigned int nentries;
+	/* entry list */
+	char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* used for the bitmask of struct ebt_entry */
+
+/* This is a hack to make a difference between an ebt_entry struct and an
+ * ebt_entries struct when traversing the entries from start to end.
+ * Using this simplifies the code a lot, while still being able to use
+ * ebt_entries.
+ * Contrary, iptables doesn't use something like ebt_entries and therefore uses
+ * different techniques for naming the policy and such. So, iptables doesn't
+ * need a hack like this.
+ */
+#define EBT_ENTRY_OR_ENTRIES 0x01
+/* these are the normal masks */
+#define EBT_NOPROTO 0x02
+#define EBT_802_3 0x04
+#define EBT_SOURCEMAC 0x08
+#define EBT_DESTMAC 0x10
+#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
+   | EBT_ENTRY_OR_ENTRIES)
+
+#define EBT_IPROTO 0x01
+#define EBT_IIN 0x02
+#define EBT_IOUT 0x04
+#define EBT_ISOURCE 0x8
+#define EBT_IDEST 0x10
+#define EBT_ILOGICALIN 0x20
+#define EBT_ILOGICALOUT 0x40
+#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
+   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)
+
+struct ebt_entry_match {
+	union {
+		struct {
+			char name[EBT_EXTENSION_MAXNAMELEN];
+			uint8_t revision;
+		};
+		struct xt_match *match;
+	} u;
+	/* size of data */
+	unsigned int match_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_watcher {
+	union {
+		struct {
+			char name[EBT_EXTENSION_MAXNAMELEN];
+			uint8_t revision;
+		};
+		struct xt_target *watcher;
+	} u;
+	/* size of data */
+	unsigned int watcher_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+struct ebt_entry_target {
+	union {
+		struct {
+			char name[EBT_EXTENSION_MAXNAMELEN];
+			uint8_t revision;
+		};
+		struct xt_target *target;
+	} u;
+	/* size of data */
+	unsigned int target_size;
+	unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+#define EBT_STANDARD_TARGET "standard"
+struct ebt_standard_target {
+	struct ebt_entry_target target;
+	int verdict;
+};
+
+/* one entry */
+struct ebt_entry {
+	/* this needs to be the first field */
+	unsigned int bitmask;
+	unsigned int invflags;
+	__be16 ethproto;
+	/* the physical in-dev */
+	char in[IFNAMSIZ];
+	/* the logical in-dev */
+	char logical_in[IFNAMSIZ];
+	/* the physical out-dev */
+	char out[IFNAMSIZ];
+	/* the logical out-dev */
+	char logical_out[IFNAMSIZ];
+	unsigned char sourcemac[ETH_ALEN];
+	unsigned char sourcemsk[ETH_ALEN];
+	unsigned char destmac[ETH_ALEN];
+	unsigned char destmsk[ETH_ALEN];
+	/* sizeof ebt_entry + matches */
+	unsigned int watchers_offset;
+	/* sizeof ebt_entry + matches + watchers */
+	unsigned int target_offset;
+	/* sizeof ebt_entry + matches + watchers + target */
+	unsigned int next_offset;
+	unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
+};
+
+/* {g,s}etsockopt numbers */
+#define EBT_BASE_CTL            128
+
+#define EBT_SO_SET_ENTRIES      (EBT_BASE_CTL)
+#define EBT_SO_SET_COUNTERS     (EBT_SO_SET_ENTRIES+1)
+#define EBT_SO_SET_MAX          (EBT_SO_SET_COUNTERS+1)
+
+#define EBT_SO_GET_INFO         (EBT_BASE_CTL)
+#define EBT_SO_GET_ENTRIES      (EBT_SO_GET_INFO+1)
+#define EBT_SO_GET_INIT_INFO    (EBT_SO_GET_ENTRIES+1)
+#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1)
+#define EBT_SO_GET_MAX          (EBT_SO_GET_INIT_ENTRIES+1)
+
+
+/* blatently stolen from ip_tables.h
+ * fn returns 0 to continue iteration */
+#define EBT_MATCH_ITERATE(e, fn, args...)                   \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_match *__match;                    \
+	                                                    \
+	for (__i = sizeof(struct ebt_entry);                \
+	     __i < (e)->watchers_offset;                    \
+	     __i += __match->match_size +                   \
+	     sizeof(struct ebt_entry_match)) {              \
+		__match = (void *)(e) + __i;                \
+		                                            \
+		__ret = fn(__match , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->watchers_offset)            \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_WATCHER_ITERATE(e, fn, args...)                 \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry_watcher *__watcher;                \
+	                                                    \
+	for (__i = e->watchers_offset;                      \
+	     __i < (e)->target_offset;                      \
+	     __i += __watcher->watcher_size +               \
+	     sizeof(struct ebt_entry_watcher)) {            \
+		__watcher = (void *)(e) + __i;              \
+		                                            \
+		__ret = fn(__watcher , ## args);            \
+		if (__ret != 0)                             \
+			break;                              \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (e)->target_offset)              \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#define EBT_ENTRY_ITERATE(entries, size, fn, args...)       \
+({                                                          \
+	unsigned int __i;                                   \
+	int __ret = 0;                                      \
+	struct ebt_entry *__entry;                          \
+	                                                    \
+	for (__i = 0; __i < (size);) {                      \
+		__entry = (void *)(entries) + __i;          \
+		__ret = fn(__entry , ## args);              \
+		if (__ret != 0)                             \
+			break;                              \
+		if (__entry->bitmask != 0)                  \
+			__i += __entry->next_offset;        \
+		else                                        \
+			__i += sizeof(struct ebt_entries);  \
+	}                                                   \
+	if (__ret == 0) {                                   \
+		if (__i != (size))                          \
+			__ret = -EINVAL;                    \
+	}                                                   \
+	__ret;                                              \
+})
+
+#endif /* __LINUX_BRIDGE_EFF_H */
diff --git a/include/linux/types.h b/include/linux/types.h
new file mode 100644
index 0000000..23ea78f
--- /dev/null
+++ b/include/linux/types.h
@@ -0,0 +1,51 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+#include <asm/types.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/posix_types.h>
+
+
+/*
+ * Below are truly Linux-specific types that should never collide with
+ * any application/library that wants linux/types.h.
+ */
+
+#ifdef __CHECKER__
+#define __bitwise__ __attribute__((bitwise))
+#else
+#define __bitwise__
+#endif
+#ifdef __CHECK_ENDIAN__
+#define __bitwise __bitwise__
+#else
+#define __bitwise
+#endif
+
+typedef __u16 __bitwise __le16;
+typedef __u16 __bitwise __be16;
+typedef __u32 __bitwise __le32;
+typedef __u32 __bitwise __be32;
+typedef __u64 __bitwise __le64;
+typedef __u64 __bitwise __be64;
+
+typedef __u16 __bitwise __sum16;
+typedef __u32 __bitwise __wsum;
+
+/*
+ * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid
+ * common 32/64-bit compat problems.
+ * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other
+ * architectures) and to 8-byte boundaries on 64-bit architectures.  The new
+ * aligned_64 type enforces 8-byte alignment so that structs containing
+ * aligned_64 values have the same alignment on 32-bit and 64-bit architectures.
+ * No conversions are necessary between 32-bit user-space and a 64-bit kernel.
+ */
+#define __aligned_u64 __u64 __attribute__((aligned(8)))
+#define __aligned_be64 __be64 __attribute__((aligned(8)))
+#define __aligned_le64 __le64 __attribute__((aligned(8)))
+
+#endif /*  __ASSEMBLY__ */
+#endif /* _LINUX_TYPES_H */
diff --git a/libebtc.c b/libebtc.c
new file mode 100644
index 0000000..112c307
--- /dev/null
+++ b/libebtc.c
@@ -0,0 +1,1320 @@
+/*
+ * libebtc.c, January 2004
+ *
+ * Contains the functions with which to make a table in userspace.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * 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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <libgen.h>
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace);
+static int iterate_entries(struct ebt_u_replace *replace, int type);
+
+/* The standard names */
+const char *ebt_hooknames[NF_BR_NUMHOOKS] =
+{
+	[NF_BR_PRE_ROUTING]"PREROUTING",
+	[NF_BR_LOCAL_IN]"INPUT",
+	[NF_BR_FORWARD]"FORWARD",
+	[NF_BR_LOCAL_OUT]"OUTPUT",
+	[NF_BR_POST_ROUTING]"POSTROUTING",
+	[NF_BR_BROUTING]"BROUTING"
+};
+
+/* The four target names */
+const char* ebt_standard_targets[NUM_STANDARD_TARGETS] =
+{
+	"ACCEPT",
+	"DROP",
+	"CONTINUE",
+	"RETURN",
+};
+
+/* The lists of supported tables, matches, watchers and targets */
+struct ebt_u_table *ebt_tables;
+struct ebt_u_match *ebt_matches;
+struct ebt_u_watcher *ebt_watchers;
+struct ebt_u_target *ebt_targets;
+
+/* Find the right structure belonging to a name */
+struct ebt_u_target *ebt_find_target(const char *name)
+{
+	struct ebt_u_target *t = ebt_targets;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+struct ebt_u_match *ebt_find_match(const char *name)
+{
+	struct ebt_u_match *m = ebt_matches;
+
+	while (m && strcmp(m->name, name))
+		m = m->next;
+	return m;
+}
+
+struct ebt_u_watcher *ebt_find_watcher(const char *name)
+{
+	struct ebt_u_watcher *w = ebt_watchers;
+
+	while (w && strcmp(w->name, name))
+		w = w->next;
+	return w;
+}
+
+struct ebt_u_table *ebt_find_table(const char *name)
+{
+	struct ebt_u_table *t = ebt_tables;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+/* Prints all registered extensions */
+void ebt_list_extensions()
+{
+	struct ebt_u_table *tbl = ebt_tables;
+        struct ebt_u_target *t = ebt_targets;
+        struct ebt_u_match *m = ebt_matches;
+        struct ebt_u_watcher *w = ebt_watchers;
+
+	PRINT_VERSION;
+	printf("Loaded userspace extensions:\n\nLoaded tables:\n");
+        while (tbl) {
+		printf("%s\n", tbl->name);
+                tbl = tbl->next;
+	}
+	printf("\nLoaded targets:\n");
+        while (t) {
+		printf("%s\n", t->name);
+                t = t->next;
+	}
+	printf("\nLoaded matches:\n");
+        while (m) {
+		printf("%s\n", m->name);
+                m = m->next;
+	}
+	printf("\nLoaded watchers:\n");
+        while (w) {
+		printf("%s\n", w->name);
+                w = w->next;
+	}
+}
+
+int use_lockfd;
+/* Returns 0 on success, -1 when the file is locked by another process
+ * or -2 on any other error. */
+static int lock_file()
+{
+	char pathbuf[] = LOCKFILE;
+	int fd, try = 0;
+
+retry:
+	fd = open(LOCKFILE, O_CREAT|O_WRONLY|O_CLOEXEC, 00600);
+	if (fd < 0) {
+		if (try == 1 || mkdir(dirname(pathbuf), 00700))
+			return -2;
+		try = 1;
+		goto retry;
+	}
+	return flock(fd, LOCK_EX);
+}
+
+/* Get the table from the kernel or from a binary file
+ * init: 1 = ask the kernel for the initial contents of a table, i.e. the
+ *           way it looks when the table is insmod'ed
+ *       0 = get the current data in the table */
+int ebt_get_kernel_table(struct ebt_u_replace *replace, int init)
+{
+	int ret;
+
+	if (!ebt_find_table(replace->name)) {
+		ebt_print_error("Bad table name '%s'", replace->name);
+		return -1;
+	}
+	while (use_lockfd && (ret = lock_file())) {
+		if (ret == -2) {
+			/* if we get an error we can't handle, we exit. This
+			 * doesn't break backwards compatibility since using
+			 * this file locking is disabled by default. */
+			ebt_print_error2("Unable to create lock file "LOCKFILE);
+		}
+		fprintf(stderr, "Trying to obtain lock %s\n", LOCKFILE);
+		sleep(1);
+	}
+	/* Get the kernel's information */
+	if (ebt_get_table(replace, init)) {
+		if (ebt_errormsg[0] != '\0')
+			return -1;
+		ebtables_insmod("ebtables");
+		if (ebt_get_table(replace, init)) {
+			ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Put sane values into a new entry */
+void ebt_initialize_entry(struct ebt_u_entry *e)
+{
+	e->bitmask = EBT_NOPROTO;
+	e->invflags = 0;
+	e->ethproto = 0;
+	strcpy(e->in, "");
+	strcpy(e->out, "");
+	strcpy(e->logical_in, "");
+	strcpy(e->logical_out, "");
+	e->m_list = NULL;
+	e->w_list = NULL;
+	e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET);
+	ebt_find_target(EBT_STANDARD_TARGET)->used = 1;
+	e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0;
+
+	if (!e->t)
+		ebt_print_bug("Couldn't load standard target");
+	((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE;
+}
+
+/* Free up the memory of the table held in userspace, *replace can be reused */
+void ebt_cleanup_replace(struct ebt_u_replace *replace)
+{
+	int i;
+	struct ebt_u_entries *entries;
+	struct ebt_cntchanges *cc1, *cc2;
+	struct ebt_u_entry *u_e1, *u_e2;
+
+	replace->name[0] = '\0';
+	replace->valid_hooks = 0;
+	replace->nentries = 0;
+	replace->num_counters = 0;
+	replace->flags = 0;
+	replace->command = 0;
+	replace->selected_chain = -1;
+	free(replace->filename);
+	replace->filename = NULL;
+	free(replace->counters);
+	replace->counters = NULL;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		u_e1 = entries->entries->next;
+		while (u_e1 != entries->entries) {
+			ebt_free_u_entry(u_e1);
+			u_e2 = u_e1->next;
+			free(u_e1);
+			u_e1 = u_e2;
+		}
+		free(entries->entries);
+		free(entries);
+		replace->chains[i] = NULL;
+	}
+	cc1 = replace->cc->next;
+	while (cc1 != replace->cc) {
+		cc2 = cc1->next;
+		free(cc1);
+		cc1 = cc2;
+	}
+	replace->cc->next = replace->cc->prev = replace->cc;
+}
+
+/* Should be called, e.g., between 2 rule adds */
+void ebt_reinit_extensions()
+{
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t;
+	int size;
+
+	/* The init functions should determine by themselves whether they are
+	 * called for the first time or not (when necessary). */
+	for (m = ebt_matches; m; m = m->next) {
+		if (m->used) {
+			size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
+			m->m = (struct ebt_entry_match *)malloc(size);
+			if (!m->m)
+				ebt_print_memory();
+			strcpy(m->m->u.name, m->name);
+			m->m->u.revision = m->revision;
+			m->m->match_size = EBT_ALIGN(m->size);
+			m->used = 0;
+		}
+		m->flags = 0; /* An error can occur before used is set, while flags is changed. */
+		m->init(m->m);
+	}
+	for (w = ebt_watchers; w; w = w->next) {
+		if (w->used) {
+			size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
+			w->w = (struct ebt_entry_watcher *)malloc(size);
+			if (!w->w)
+				ebt_print_memory();
+			strcpy(w->w->u.name, w->name);
+			w->w->watcher_size = EBT_ALIGN(w->size);
+			w->used = 0;
+		}
+		w->flags = 0;
+		w->init(w->w);
+	}
+	for (t = ebt_targets; t; t = t->next) {
+		if (t->used) {
+			size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
+			t->t = (struct ebt_entry_target *)malloc(size);
+			if (!t->t)
+				ebt_print_memory();
+			strcpy(t->t->u.name, t->name);
+			t->t->target_size = EBT_ALIGN(t->size);
+			t->used = 0;
+		}
+		t->flags = 0;
+		t->init(t->t);
+	}
+}
+
+/* This doesn't free e, because the calling function might need e->next */
+void ebt_free_u_entry(struct ebt_u_entry *e)
+{
+	struct ebt_u_match_list *m_l, *m_l2;
+	struct ebt_u_watcher_list *w_l, *w_l2;
+
+	m_l = e->m_list;
+	while (m_l) {
+		m_l2 = m_l->next;
+		free(m_l->m);
+		free(m_l);
+		m_l = m_l2;
+	}
+	w_l = e->w_list;
+	while (w_l) {
+		w_l2 = w_l->next;
+		free(w_l->w);
+		free(w_l);
+		w_l = w_l2;
+	}
+	free(e->t);
+}
+
+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) {
+		if (read(procfile, ret, 1024) == -1)
+			goto fail;
+		/* The kernel adds a '\n' */
+		ret[1023] = '\n';
+		*strchr(ret, '\n') = '\0';
+		close(procfile);
+		return ret;
+	}
+ fail:
+	free(ret);
+	close(procfile);
+	return NULL;
+}
+
+char *ebt_modprobe;
+/* Try to load the kernel module, analogous to ip_tables.c */
+int ebtables_insmod(const char *modname)
+{
+	char *buf = NULL;
+	char *argv[3];
+
+	/* If they don't explicitly set it, read out of /proc */
+	if (!ebt_modprobe) {
+		buf = get_modprobe();
+		if (!buf)
+			return -1;
+		ebt_modprobe = buf; /* Keep the value for possible later use */
+	}
+
+	switch (fork()) {
+	case 0:
+		argv[0] = (char *)ebt_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);
+	}
+
+	return 0;
+}
+
+/* Parse the chain name and return a pointer to the chain base.
+ * Returns NULL on failure. */
+struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, const char* arg)
+{
+	int i;
+	struct ebt_u_entries *chain;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(chain = replace->chains[i]))
+			continue;
+		if (!strcmp(arg, chain->name))
+			return chain;
+	}
+	return NULL;
+}
+
+/* Parse the chain name and return the corresponding chain nr
+ * returns -1 on failure */
+int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg)
+{
+	int i;
+
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!replace->chains[i])
+			continue;
+		if (!strcmp(arg, replace->chains[i]->name))
+			return i;
+	}
+	return -1;
+}
+
+     /*
+************
+************
+**COMMANDS**
+************
+************
+     */
+
+/* Change the policy of selected_chain.
+ * Handing a bad policy to this function is a bug. */
+void ebt_change_policy(struct ebt_u_replace *replace, int policy)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE)
+		ebt_print_bug("Wrong policy: %d", policy);
+	entries->policy = policy;
+}
+
+void ebt_delete_cc(struct ebt_cntchanges *cc)
+{
+	if (cc->type == CNT_ADD) {
+		cc->prev->next = cc->next;
+		cc->next->prev = cc->prev;
+		free(cc);
+	} else
+		cc->type = CNT_DEL;
+}
+
+void ebt_empty_chain(struct ebt_u_entries *entries)
+{
+	struct ebt_u_entry *u_e = entries->entries->next, *tmp;
+	while (u_e != entries->entries) {
+		ebt_delete_cc(u_e->cc);
+		ebt_free_u_entry(u_e);
+		tmp = u_e->next;
+		free(u_e);
+		u_e = tmp;
+	}
+	entries->entries->next = entries->entries->prev = entries->entries;
+	entries->nentries = 0;
+}
+
+/* Flush one chain or the complete table
+ * If selected_chain == -1 then flush the complete table */
+void ebt_flush_chains(struct ebt_u_replace *replace)
+{
+	int i, numdel;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	/* Flush whole table */
+	if (!entries) {
+		if (replace->nentries == 0)
+			return;
+		replace->nentries = 0;
+
+		/* Free everything and zero (n)entries */
+		for (i = 0; i < replace->num_chains; i++) {
+			if (!(entries = replace->chains[i]))
+				continue;
+			entries->counter_offset = 0;
+			ebt_empty_chain(entries);
+		}
+		return;
+	}
+
+	if (entries->nentries == 0)
+		return;
+	replace->nentries -= entries->nentries;
+	numdel = entries->nentries;
+
+	/* Update counter_offset */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset -= numdel;
+	}
+
+	entries = ebt_to_chain(replace);
+	ebt_empty_chain(entries);
+}
+
+#define OPT_COUNT	0x1000 /* This value is also defined in ebtables.c */
+/* Returns the rule number on success (starting from 0), -1 on failure
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} */
+int ebt_check_rule_exists(struct ebt_u_replace *replace,
+			  struct ebt_u_entry *new_entry)
+{
+	struct ebt_u_entry *u_e;
+	struct ebt_u_match_list *m_l, *m_l2;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher_list *w_l, *w_l2;
+	struct ebt_u_watcher *w;
+	struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	int i, j, k;
+
+	u_e = entries->entries->next;
+	/* Check for an existing rule (if there are duplicate rules,
+	 * take the first occurance) */
+	for (i = 0; i < entries->nentries; i++, u_e = u_e->next) {
+		if (u_e->ethproto != new_entry->ethproto)
+			continue;
+		if (strcmp(u_e->in, new_entry->in))
+			continue;
+		if (strcmp(u_e->out, new_entry->out))
+			continue;
+		if (strcmp(u_e->logical_in, new_entry->logical_in))
+			continue;
+		if (strcmp(u_e->logical_out, new_entry->logical_out))
+			continue;
+		if (new_entry->bitmask & EBT_SOURCEMAC &&
+		    memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN))
+			continue;
+		if (new_entry->bitmask & EBT_DESTMAC &&
+		    memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN))
+			continue;
+		if (new_entry->bitmask != u_e->bitmask ||
+		    new_entry->invflags != u_e->invflags)
+			continue;
+		if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt !=
+		    u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt))
+			continue;
+		/* Compare all matches */
+		m_l = new_entry->m_list;
+		j = 0;
+		while (m_l) {
+			m = (struct ebt_u_match *)(m_l->m);
+			m_l2 = u_e->m_list;
+			while (m_l2 && (strcmp(m_l2->m->u.name, m->m->u.name) ||
+			       m_l2->m->u.revision != m->m->u.revision)) {
+				m_l2 = m_l2->next;
+			}
+			if (!m_l2 || !m->compare(m->m, m_l2->m))
+				goto letscontinue;
+			j++;
+			m_l = m_l->next;
+		}
+		/* Now be sure they have the same nr of matches */
+		k = 0;
+		m_l = u_e->m_list;
+		while (m_l) {
+			k++;
+			m_l = m_l->next;
+		}
+		if (j != k)
+			continue;
+
+		/* Compare all watchers */
+		w_l = new_entry->w_list;
+		j = 0;
+		while (w_l) {
+			w = (struct ebt_u_watcher *)(w_l->w);
+			w_l2 = u_e->w_list;
+			while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name))
+				w_l2 = w_l2->next;
+			if (!w_l2 || !w->compare(w->w, w_l2->w))
+				goto letscontinue;
+			j++;
+			w_l = w_l->next;
+		}
+		k = 0;
+		w_l = u_e->w_list;
+		while (w_l) {
+			k++;
+			w_l = w_l->next;
+		}
+		if (j != k)
+			continue;
+		if (strcmp(t->t->u.name, u_e->t->u.name))
+			continue;
+		if (!t->compare(t->t, u_e->t))
+			continue;
+		return i;
+letscontinue:;
+	}
+	return -1;
+}
+
+/* Add a rule, rule_nr is the rule to update
+ * rule_nr specifies where the rule should be inserted
+ * rule_nr > 0 : insert the rule right before the rule_nr'th rule
+ *               (the first rule is rule 1)
+ * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule,
+ *               where n denotes the number of rules in the chain
+ * rule_nr == 0: add a new rule at the end of the chain
+ *
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target} and updates these
+ * pointers so that they point to ebt_{match,watcher,target}, before adding
+ * the rule to the chain. Don't free() the ebt_{match,watcher,target} and
+ * don't reuse the new_entry after a successful call to ebt_add_rule() */
+void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr)
+{
+	int i;
+	struct ebt_u_entry *u_e;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	struct ebt_cntchanges *cc, *new_cc;
+
+	if (rule_nr <= 0)
+		rule_nr += entries->nentries;
+	else
+		rule_nr--;
+	if (rule_nr > entries->nentries || rule_nr < 0) {
+		ebt_print_error("The specified rule number is incorrect");
+		return;
+	}
+	/* Go to the right position in the chain */
+	if (rule_nr == entries->nentries)
+		u_e = entries->entries;
+	else {
+		u_e = entries->entries->next;
+		for (i = 0; i < rule_nr; i++)
+			u_e = u_e->next;
+	}
+	/* We're adding one rule */
+	replace->nentries++;
+	entries->nentries++;
+	/* Insert the rule */
+	new_entry->next = u_e;
+	new_entry->prev = u_e->prev;
+	u_e->prev->next = new_entry;
+	u_e->prev = new_entry;
+	new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
+	if (!new_cc)
+		ebt_print_memory();
+	new_cc->type = CNT_ADD;
+	new_cc->change = 0;
+	if (new_entry->next == entries->entries) {
+		for (i = replace->selected_chain+1; i < replace->num_chains; i++)
+			if (!replace->chains[i] || replace->chains[i]->nentries == 0)
+				continue;
+			else
+				break;
+		if (i == replace->num_chains)
+			cc = replace->cc;
+		else
+			cc = replace->chains[i]->entries->next->cc;
+	} else
+		cc = new_entry->next->cc;
+	new_cc->next = cc;
+	new_cc->prev = cc->prev;
+	cc->prev->next = new_cc;
+	cc->prev = new_cc;
+	new_entry->cc = new_cc;
+
+	/* Put the ebt_{match, watcher, target} pointers in place */
+	m_l = new_entry->m_list;
+	while (m_l) {
+		m_l->m = ((struct ebt_u_match *)m_l->m)->m;
+		m_l = m_l->next;
+	}
+	w_l = new_entry->w_list;
+	while (w_l) {
+		w_l->w = ((struct ebt_u_watcher *)w_l->w)->w;
+		w_l = w_l->next;
+	}
+	new_entry->t = ((struct ebt_u_target *)new_entry->t)->t;
+	/* Update the counter_offset of chains behind this one */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		entries = replace->chains[i];
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset++;
+	}
+}
+
+/* If *begin==*end==0 then find the rule corresponding to new_entry,
+ * else make the rule numbers positive (starting from 0) and check
+ * for bad rule numbers. */
+static int check_and_change_rule_number(struct ebt_u_replace *replace,
+   struct ebt_u_entry *new_entry, int *begin, int *end)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (*begin < 0)
+		*begin += entries->nentries + 1;
+	if (*end < 0)
+		*end += entries->nentries + 1;
+
+	if (*begin < 0 || *begin > *end || *end > entries->nentries) {
+		ebt_print_error("Sorry, wrong rule numbers");
+		return -1;
+	}
+
+	if ((*begin * *end == 0) && (*begin + *end != 0))
+		ebt_print_bug("begin and end should be either both zero, "
+			      "either both non-zero");
+	if (*begin != 0) {
+		(*begin)--;
+		(*end)--;
+	} else {
+		*begin = ebt_check_rule_exists(replace, new_entry);
+		*end = *begin;
+		if (*begin == -1) {
+			ebt_print_error("Sorry, rule does not exist");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/* Delete a rule or rules
+ * begin == end == 0: delete the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}. */
+void ebt_delete_rule(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end)
+{
+	int i,  nr_deletes;
+	struct ebt_u_entry *u_e, *u_e2, *u_e3;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+		return;
+	/* We're deleting rules */
+	nr_deletes = end - begin + 1;
+	replace->nentries -= nr_deletes;
+	entries->nentries -= nr_deletes;
+	/* Go to the right position in the chain */
+	u_e = entries->entries->next;
+	for (i = 0; i < begin; i++)
+		u_e = u_e->next;
+	u_e3 = u_e->prev;
+	/* Remove the rules */
+	for (i = 0; i < nr_deletes; i++) {
+		u_e2 = u_e;
+		ebt_delete_cc(u_e2->cc);
+		u_e = u_e->next;
+		/* Free everything */
+		ebt_free_u_entry(u_e2);
+		free(u_e2);
+	}
+	u_e3->next = u_e;
+	u_e->prev = u_e3;
+	/* Update the counter_offset of chains behind this one */
+	for (i = replace->selected_chain+1; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		entries->counter_offset -= nr_deletes;
+	}
+}
+
+/* Change the counters of a rule or rules
+ * begin == end == 0: change counters of the rule corresponding to new_entry
+ *
+ * The first rule has rule nr 1, the last rule has rule nr -1, etc.
+ * This function expects the ebt_{match,watcher,target} members of new_entry
+ * to contain pointers to ebt_u_{match,watcher,target}.
+ * The mask denotes the following:
+ *    pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement
+ *    bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment
+ * In daemon mode, mask==0 must hold */
+void ebt_change_counters(struct ebt_u_replace *replace,
+		     struct ebt_u_entry *new_entry, int begin, int end,
+		     struct ebt_counter *cnt, int mask)
+{
+	int i;
+	struct ebt_u_entry *u_e;
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (check_and_change_rule_number(replace, new_entry, &begin, &end))
+		return;
+	u_e = entries->entries->next;
+	for (i = 0; i < begin; i++)
+		u_e = u_e->next;
+	for (i = end-begin+1; i > 0; i--) {
+		if (mask % 3 == 0) {
+			u_e->cnt.pcnt = (*cnt).pcnt;
+			u_e->cnt_surplus.pcnt = 0;
+		} else {
+#ifdef EBT_DEBUG
+			if (u_e->cc->type != CNT_NORM)
+				ebt_print_bug("cc->type != CNT_NORM");
+#endif
+			u_e->cnt_surplus.pcnt = (*cnt).pcnt;
+		}
+
+		if (mask / 3 == 0) {
+			u_e->cnt.bcnt = (*cnt).bcnt;
+			u_e->cnt_surplus.bcnt = 0;
+		} else {
+#ifdef EBT_DEBUG
+			if (u_e->cc->type != CNT_NORM)
+				ebt_print_bug("cc->type != CNT_NORM");
+#endif
+			u_e->cnt_surplus.bcnt = (*cnt).bcnt;
+		}
+		if (u_e->cc->type != CNT_ADD)
+			u_e->cc->type = CNT_CHANGE;
+		u_e->cc->change = mask;
+		u_e = u_e->next;
+	}
+}
+
+/* If selected_chain == -1 then zero all counters,
+ * otherwise, zero the counters of selected_chain */
+void ebt_zero_counters(struct ebt_u_replace *replace)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+	struct ebt_u_entry *next;
+	int i;
+
+	if (!entries) {
+		for (i = 0; i < replace->num_chains; i++) {
+			if (!(entries = replace->chains[i]))
+				continue;
+			next = entries->entries->next;
+			while (next != entries->entries) {
+				if (next->cc->type == CNT_NORM)
+					next->cc->type = CNT_CHANGE;
+				next->cnt.bcnt = next->cnt.pcnt = 0;
+				next->cc->change = 0;
+				next = next->next;
+			}
+		}
+	} else {
+		if (entries->nentries == 0)
+			return;
+
+		next = entries->entries->next;
+		while (next != entries->entries) {
+			if (next->cc->type == CNT_NORM)
+				next->cc->type = CNT_CHANGE;
+			next->cnt.bcnt = next->cnt.pcnt = 0;
+			next = next->next;
+		}
+	}
+}
+
+/* Add a new chain and specify its policy */
+void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy)
+{
+	struct ebt_u_entries *new;
+
+	if (replace->num_chains == replace->max_chains)
+		ebt_double_chains(replace);
+	new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
+	if (!new)
+		ebt_print_memory();
+	replace->chains[replace->num_chains++] = new;
+	new->nentries = 0;
+	new->policy = policy;
+	new->counter_offset = replace->nentries;
+	new->hook_mask = 0;
+	strcpy(new->name, name);
+	new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+	if (!new->entries)
+		ebt_print_memory();
+	new->entries->next = new->entries->prev = new->entries;
+	new->kernel_start = NULL;
+}
+
+/* returns -1 if the chain is referenced, 0 on success */
+static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err)
+{
+	int tmp = replace->selected_chain;
+	/* If the chain is referenced, don't delete it,
+	 * also decrement jumps to a chain behind the
+	 * one we're deleting */
+	replace->selected_chain = chain;
+	if (ebt_check_for_references(replace, print_err))
+		return -1;
+	decrease_chain_jumps(replace);
+	ebt_flush_chains(replace);
+	replace->selected_chain = tmp;
+	free(replace->chains[chain]->entries);
+	free(replace->chains[chain]);
+	memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *));
+	replace->num_chains--;
+	return 0;
+}
+
+/* Selected_chain == -1: delete all non-referenced udc
+ * selected_chain < NF_BR_NUMHOOKS is illegal */
+void ebt_delete_chain(struct ebt_u_replace *replace)
+{
+	if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS)
+		ebt_print_bug("You can't remove a standard chain");
+	if (replace->selected_chain == -1) {
+		int i = NF_BR_NUMHOOKS;
+
+		while (i < replace->num_chains)
+			if (ebt_delete_a_chain(replace, i, 0))
+				i++;
+	} else
+		ebt_delete_a_chain(replace, replace->selected_chain, 1);
+}
+
+/* Rename an existing chain. */
+void ebt_rename_chain(struct ebt_u_replace *replace, const char *name)
+{
+	struct ebt_u_entries *entries = ebt_to_chain(replace);
+
+	if (!entries)
+		ebt_print_bug("ebt_rename_chain: entries == NULL");
+	strcpy(entries->name, name);
+}
+
+
+           /*
+*************************
+*************************
+**SPECIALIZED*FUNCTIONS**
+*************************
+*************************
+            */
+
+
+void ebt_double_chains(struct ebt_u_replace *replace)
+{
+	struct ebt_u_entries **new;
+
+	replace->max_chains *= 2;
+	new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *));
+	if (!new)
+		ebt_print_memory();
+	memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *));
+	free(replace->chains);
+	replace->chains = new;
+}
+
+/* Executes the final_check() function for all extensions used by the rule
+ * ebt_check_for_loops should have been executed earlier, to make sure the
+ * hook_mask is correct. The time argument to final_check() is set to 1,
+ * meaning it's the second time the final_check() function is executed. */
+void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e,
+			 struct ebt_u_entries *entries)
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+	struct ebt_u_target *t;
+	struct ebt_u_match *m;
+	struct ebt_u_watcher *w;
+
+	m_l = e->m_list;
+	w_l = e->w_list;
+	while (m_l) {
+		m = ebt_find_match(m_l->m->u.name);
+		m->final_check(e, m_l->m, replace->name,
+		   entries->hook_mask, 1);
+		if (ebt_errormsg[0] != '\0')
+			return;
+		m_l = m_l->next;
+	}
+	while (w_l) {
+		w = ebt_find_watcher(w_l->w->u.name);
+		w->final_check(e, w_l->w, replace->name,
+		   entries->hook_mask, 1);
+		if (ebt_errormsg[0] != '\0')
+			return;
+		w_l = w_l->next;
+	}
+	t = ebt_find_target(e->t->u.name);
+	t->final_check(e, e->t, replace->name,
+	   entries->hook_mask, 1);
+}
+
+/* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references(struct ebt_u_replace *replace, int print_err)
+{
+	if (print_err)
+		return iterate_entries(replace, 1);
+	else
+		return iterate_entries(replace, 2);
+}
+
+/* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS)
+ * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't.
+ * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */
+int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr,
+                              int print_err)
+{
+	int tmp = replace->selected_chain, ret;
+
+	replace->selected_chain = chain_nr;
+	if (print_err)
+		ret = iterate_entries(replace, 1);
+	else
+		ret = iterate_entries(replace, 2);
+	replace->selected_chain = tmp;
+	return ret;
+}
+
+struct ebt_u_stack
+{
+	int chain_nr;
+	int n;
+	struct ebt_u_entry *e;
+	struct ebt_u_entries *entries;
+};
+
+/* Checks for loops
+ * As a by-product, the hook_mask member of each chain is filled in
+ * correctly. The check functions of the extensions need this hook_mask
+ * to know from which standard chains they can be called. */
+void ebt_check_for_loops(struct ebt_u_replace *replace)
+{
+	int chain_nr , i, j , k, sp = 0, verdict;
+	struct ebt_u_entries *entries, *entries2;
+	struct ebt_u_stack *stack = NULL;
+	struct ebt_u_entry *e;
+
+	/* Initialize hook_mask to 0 */
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		if (i < NF_BR_NUMHOOKS)
+			/* (1 << NF_BR_NUMHOOKS) implies it's a standard chain
+			 * (usefull in the final_check() funtions) */
+			entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS);
+		else
+			entries->hook_mask = 0;
+	}
+	if (replace->num_chains == NF_BR_NUMHOOKS)
+		return;
+	stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack));
+	if (!stack)
+		ebt_print_memory();
+
+	/* Check for loops, starting from every base chain */
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		chain_nr = i;
+
+		e = entries->entries->next;
+		for (j = 0; j < entries->nentries; j++) {
+			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET))
+				goto letscontinue;
+			verdict = ((struct ebt_standard_target *)(e->t))->verdict;
+			if (verdict < 0)
+				goto letscontinue;
+			/* Now see if we've been here before */
+			for (k = 0; k < sp; k++)
+				if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) {
+					ebt_print_error("Loop from chain '%s' to chain '%s'",
+					   replace->chains[chain_nr]->name,
+					   replace->chains[stack[k].chain_nr]->name);
+					goto free_stack;
+				}
+			entries2 = replace->chains[verdict + NF_BR_NUMHOOKS];
+			/* check if we've dealt with this chain already */
+			if (entries2->hook_mask & (1<<i))
+				goto letscontinue;
+			entries2->hook_mask |= entries->hook_mask & ~(1 << NF_BR_NUMHOOKS);
+			/* Jump to the chain, make sure we know how to get back */
+			stack[sp].chain_nr = chain_nr;
+			stack[sp].n = j;
+			stack[sp].entries = entries;
+			stack[sp].e = e;
+			sp++;
+			j = -1;
+			e = entries2->entries->next;
+			chain_nr = verdict + NF_BR_NUMHOOKS;
+			entries = entries2;
+			continue;
+letscontinue:
+			e = e->next;
+		}
+		/* We are at the end of a standard chain */
+		if (sp == 0)
+			continue;
+		/* Go back to the chain one level higher */
+		sp--;
+		j = stack[sp].n;
+		chain_nr = stack[sp].chain_nr;
+		e = stack[sp].e;
+		entries = stack[sp].entries;
+		goto letscontinue;
+	}
+free_stack:
+	free(stack);
+	return;
+}
+
+/* The user will use the match, so put it in new_entry. The ebt_u_match
+ * pointer is put in the ebt_entry_match pointer. ebt_add_rule will
+ * fill in the final value for new->m. Unless the rule is added to a chain,
+ * the pointer will keep pointing to the ebt_u_match (until the new_entry
+ * is freed). I know, I should use a union for these 2 pointer types... */
+void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m)
+{
+	struct ebt_u_match_list **m_list, *new;
+
+	for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next);
+	new = (struct ebt_u_match_list *)
+	   malloc(sizeof(struct ebt_u_match_list));
+	if (!new)
+		ebt_print_memory();
+	*m_list = new;
+	new->next = NULL;
+	new->m = (struct ebt_entry_match *)m;
+}
+
+void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w)
+{
+	struct ebt_u_watcher_list **w_list;
+	struct ebt_u_watcher_list *new;
+
+	for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next);
+	new = (struct ebt_u_watcher_list *)
+	   malloc(sizeof(struct ebt_u_watcher_list));
+	if (!new)
+		ebt_print_memory();
+	*w_list = new;
+	new->next = NULL;
+	new->w = (struct ebt_entry_watcher *)w;
+}
+
+
+        /*
+*******************
+*******************
+**OTHER*FUNCTIONS**
+*******************
+*******************
+         */
+
+
+/* type = 0 => update chain jumps
+ * type = 1 => check for reference, print error when referenced
+ * type = 2 => check for reference, don't print error when referenced
+ *
+ * Returns 1 when type == 1 and the chain is referenced
+ * returns 0 otherwise */
+static int iterate_entries(struct ebt_u_replace *replace, int type)
+{
+	int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS;
+	struct ebt_u_entries *entries;
+	struct ebt_u_entry *e;
+
+	if (chain_nr < 0)
+		ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr);
+	for (i = 0; i < replace->num_chains; i++) {
+		if (!(entries = replace->chains[i]))
+			continue;
+		e = entries->entries->next;
+		for (j = 0; j < entries->nentries; j++) {
+			int chain_jmp;
+
+			if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
+				e = e->next;
+				continue;
+			}
+			chain_jmp = ((struct ebt_standard_target *)e->t)->
+				    verdict;
+			switch (type) {
+			case 1:
+			case 2:
+			if (chain_jmp == chain_nr) {
+				if (type == 2)
+					return 1;
+				ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d",
+				                replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, entries->name, j);
+				return 1;
+			}
+			break;
+			case 0:
+			/* Adjust the chain jumps when necessary */
+			if (chain_jmp > chain_nr)
+				((struct ebt_standard_target *)e->t)->verdict--;
+			break;
+			} /* End switch */
+			e = e->next;
+		}
+	}
+	return 0;
+}
+
+static void decrease_chain_jumps(struct ebt_u_replace *replace)
+{
+	iterate_entries(replace, 0);
+}
+
+/* Used in initialization code of modules */
+void ebt_register_match(struct ebt_u_match *m)
+{
+	int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match);
+	struct ebt_u_match **i;
+
+	m->m = (struct ebt_entry_match *)malloc(size);
+	if (!m->m)
+		ebt_print_memory();
+	strcpy(m->m->u.name, m->name);
+	m->m->u.revision = m->revision;
+	m->m->match_size = EBT_ALIGN(m->size);
+	m->init(m->m);
+
+	for (i = &ebt_matches; *i; i = &((*i)->next));
+	m->next = NULL;
+	*i = m;
+}
+
+void ebt_register_watcher(struct ebt_u_watcher *w)
+{
+	int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher);
+	struct ebt_u_watcher **i;
+
+	w->w = (struct ebt_entry_watcher *)malloc(size);
+	if (!w->w)
+		ebt_print_memory();
+	strcpy(w->w->u.name, w->name);
+	w->w->watcher_size = EBT_ALIGN(w->size);
+	w->init(w->w);
+
+	for (i = &ebt_watchers; *i; i = &((*i)->next));
+	w->next = NULL;
+	*i = w;
+}
+
+void ebt_register_target(struct ebt_u_target *t)
+{
+	int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target);
+	struct ebt_u_target **i;
+
+	t->t = (struct ebt_entry_target *)malloc(size);
+	if (!t->t)
+		ebt_print_memory();
+	strcpy(t->t->u.name, t->name);
+	t->t->target_size = EBT_ALIGN(t->size);
+	t->init(t->t);
+
+	for (i = &ebt_targets; *i; i = &((*i)->next));
+	t->next = NULL;
+	*i = t;
+}
+
+void ebt_register_table(struct ebt_u_table *t)
+{
+	t->next = ebt_tables;
+	ebt_tables = t;
+}
+
+void ebt_iterate_matches(void (*f)(struct ebt_u_match *))
+{
+	struct ebt_u_match *i;
+
+	for (i = ebt_matches; i; i = i->next)
+		f(i);
+}
+
+void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *))
+{
+	struct ebt_u_watcher *i;
+
+	for (i = ebt_watchers; i; i = i->next)
+		f(i);
+}
+
+void ebt_iterate_targets(void (*f)(struct ebt_u_target *))
+{
+	struct ebt_u_target *i;
+
+	for (i = ebt_targets; i; i = i->next)
+		f(i);
+}
+
+/* Don't use this function, use ebt_print_bug() */
+void __ebt_print_bug(char *file, int line, char *format, ...)
+{
+	va_list l;
+
+	va_start(l, format);
+	fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line);
+	vfprintf(stderr, format, l);
+	fprintf(stderr, "\n");
+	va_end(l);
+	exit (-1);
+}
+
+/* The error messages are put in here when ebt_silent == 1
+ * ebt_errormsg[0] == '\0' implies there was no error */
+char ebt_errormsg[ERRORMSG_MAXLEN];
+/* When error messages should not be printed on the screen, after which
+ * the program exit()s, set ebt_silent to 1. */
+int ebt_silent;
+/* Don't use this function, use ebt_print_error() */
+void __ebt_print_error(char *format, ...)
+{
+	va_list l;
+
+	va_start(l, format);
+	if (ebt_silent && ebt_errormsg[0] == '\0') {
+		vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l);
+		va_end(l);
+	} else {
+		vfprintf(stderr, format, l);
+		fprintf(stderr, ".\n");
+		va_end(l);
+		exit (-1);
+	}
+}
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644
index 0000000..64d9bbc
--- /dev/null
+++ b/m4/.gitignore
@@ -0,0 +1,2 @@
+/libtool.m4
+/lt*.m4
diff --git a/useful_functions.c b/useful_functions.c
new file mode 100644
index 0000000..133ae2f
--- /dev/null
+++ b/useful_functions.c
@@ -0,0 +1,607 @@
+/*
+ * useful_functions.c, January 2004
+ *
+ * Random collection of functions that can be used by extensions.
+ *
+ * Author: Bart De Schuymer
+ *
+ *  This code is stongly inspired on the iptables code which is
+ *  Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "include/ebtables_u.h"
+#include "include/ethernetdb.h"
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <netinet/ether.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+
+const unsigned char mac_type_unicast[ETH_ALEN] =   {0,0,0,0,0,0};
+const unsigned char msk_type_unicast[ETH_ALEN] =   {1,0,0,0,0,0};
+const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0};
+const unsigned char msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255};
+
+/* 0: default, print only 2 digits if necessary
+ * 2: always print 2 digits, a printed mac address
+ * then always has the same length */
+int ebt_printstyle_mac;
+
+void ebt_print_mac(const unsigned char *mac)
+{
+	if (ebt_printstyle_mac == 2) {
+		int j;
+		for (j = 0; j < ETH_ALEN; j++)
+			printf("%02x%s", mac[j],
+				(j==ETH_ALEN-1) ? "" : ":");
+	} else
+		printf("%s", ether_ntoa((struct ether_addr *) mac));
+}
+
+void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask)
+{
+	char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+	if (!memcmp(mac, mac_type_unicast, 6) &&
+	    !memcmp(mask, msk_type_unicast, 6))
+		printf("Unicast");
+	else if (!memcmp(mac, mac_type_multicast, 6) &&
+	         !memcmp(mask, msk_type_multicast, 6))
+		printf("Multicast");
+	else if (!memcmp(mac, mac_type_broadcast, 6) &&
+	         !memcmp(mask, msk_type_broadcast, 6))
+		printf("Broadcast");
+	else if (!memcmp(mac, mac_type_bridge_group, 6) &&
+	         !memcmp(mask, msk_type_bridge_group, 6))
+		printf("BGA");
+	else {
+		ebt_print_mac(mac);
+		if (memcmp(mask, hlpmsk, 6)) {
+			printf("/");
+			ebt_print_mac(mask);
+		}
+	}
+}
+
+/* Checks the type for validity and calls getethertypebynumber(). */
+struct ethertypeent *parseethertypebynumber(int type)
+{
+	if (type < 1536)
+		ebt_print_error("Ethernet protocols have values >= 0x0600");
+	if (type > 0xffff)
+		ebt_print_error("Ethernet protocols have values <= 0xffff");
+	return getethertypebynumber(type);
+}
+
+/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */
+int ebt_get_mac_and_mask(const char *from, unsigned char *to,
+  unsigned char *mask)
+{
+	char *p;
+	int i;
+	struct ether_addr *addr;
+
+	if (strcasecmp(from, "Unicast") == 0) {
+		memcpy(to, mac_type_unicast, ETH_ALEN);
+		memcpy(mask, msk_type_unicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Multicast") == 0) {
+		memcpy(to, mac_type_multicast, ETH_ALEN);
+		memcpy(mask, msk_type_multicast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "Broadcast") == 0) {
+		memcpy(to, mac_type_broadcast, ETH_ALEN);
+		memcpy(mask, msk_type_broadcast, ETH_ALEN);
+		return 0;
+	}
+	if (strcasecmp(from, "BGA") == 0) {
+		memcpy(to, mac_type_bridge_group, ETH_ALEN);
+		memcpy(mask, msk_type_bridge_group, ETH_ALEN);
+		return 0;
+	}
+	if ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		if (!(addr = ether_aton(p + 1)))
+			return -1;
+		memcpy(mask, addr, ETH_ALEN);
+	} else
+		memset(mask, 0xff, ETH_ALEN);
+	if (!(addr = ether_aton(from)))
+		return -1;
+	memcpy(to, addr, ETH_ALEN);
+	for (i = 0; i < ETH_ALEN; i++)
+		to[i] &= mask[i];
+	return 0;
+}
+
+/* 0: default
+ * 1: the inverse '!' of the option has already been specified */
+int ebt_invert = 0;
+
+/*
+ * Check if the inverse of the option is specified. This is used
+ * in the parse functions of the extensions and ebtables.c
+ */
+int _ebt_check_inverse(const char option[], int argc, char **argv)
+{
+	if (!option)
+		return ebt_invert;
+	if (strcmp(option, "!") == 0) {
+		if (ebt_invert == 1)
+			ebt_print_error("Double use of '!' not allowed");
+		if (optind >= argc)
+			optarg = NULL;
+		else
+			optarg = argv[optind];
+		optind++;
+		ebt_invert = 1;
+		return 1;
+	}
+	return ebt_invert;
+}
+
+/* Make sure the same option wasn't specified twice. This is used
+ * in the parse functions of the extensions and ebtables.c */
+void ebt_check_option(unsigned int *flags, unsigned int mask)
+{
+	if (*flags & mask)
+		ebt_print_error("Multiple use of same option not allowed");
+	*flags |= mask;
+}
+
+/* Put the ip string into 4 bytes. */
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+	char *p, *q, *end;
+	long int onebyte;
+	int i;
+	char buf[20];
+
+	strncpy(buf, ip, sizeof(buf) - 1);
+
+	p = buf;
+	for (i = 0; i < 3; i++) {
+		if ((q = strchr(p, '.')) == NULL)
+			return -1;
+		*q = '\0';
+		onebyte = strtol(p, &end, 10);
+		if (*end != '\0' || onebyte > 255 || onebyte < 0)
+			return -1;
+		ip2[i] = (unsigned char)onebyte;
+		p = q + 1;
+	}
+
+	onebyte = strtol(p, &end, 10);
+	if (*end != '\0' || onebyte > 255 || onebyte < 0)
+		return -1;
+	ip2[3] = (unsigned char)onebyte;
+
+	return 0;
+}
+
+/* Put the mask into 4 bytes. */
+static int ip_mask(char *mask, unsigned char *mask2)
+{
+	char *end;
+	long int bits;
+	uint32_t mask22;
+
+	if (undot_ip(mask, mask2)) {
+		/* not the /a.b.c.e format, maybe the /x format */
+		bits = strtol(mask, &end, 10);
+		if (*end != '\0' || bits > 32 || bits < 0)
+			return -1;
+		if (bits != 0) {
+			mask22 = htonl(0xFFFFFFFF << (32 - bits));
+			memcpy(mask2, &mask22, 4);
+		} else {
+			mask22 = 0xFFFFFFFF;
+			memcpy(mask2, &mask22, 4);
+		}
+	}
+	return 0;
+}
+
+/* Set the ip mask and ip address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk)
+{
+	char *p;
+
+	/* first the mask */
+	if ((p = strrchr(address, '/')) != NULL) {
+		*p = '\0';
+		if (ip_mask(p + 1, (unsigned char *)msk)) {
+			ebt_print_error("Problem with the IP mask '%s'", p + 1);
+			return;
+		}
+	} else
+		*msk = 0xFFFFFFFF;
+
+	if (undot_ip(address, (unsigned char *)addr)) {
+		ebt_print_error("Problem with the IP address '%s'", address);
+		return;
+	}
+	*addr = *addr & *msk;
+}
+
+
+/* Transform the ip mask into a string ready for output. */
+char *ebt_mask_to_dotted(uint32_t mask)
+{
+	int i;
+	static char buf[20];
+	uint32_t maskaddr, bits;
+
+	maskaddr = ntohl(mask);
+
+	/* don't print /32 */
+	if (mask == 0xFFFFFFFFL) {
+		*buf = '\0';
+		return buf;
+	}
+
+	i = 32;
+	bits = 0xFFFFFFFEL; /* Case 0xFFFFFFFF has just been dealt with */
+	while (--i >= 0 && maskaddr != bits)
+		bits <<= 1;
+
+	if (i > 0)
+		sprintf(buf, "/%d", i);
+	else if (!i)
+		*buf = '\0';
+	else
+		/* Mask was not a decent combination of 1's and 0's */
+		sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0],
+		   ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2],
+		   ((unsigned char *)&mask)[3]);
+
+	return buf;
+}
+
+/* Most of the following code is derived from iptables */
+static void
+in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
+{
+	memcpy(dst, src, sizeof(struct in6_addr));
+}
+
+int string_to_number_ll(const char *s, unsigned long long min,
+            unsigned long long max, unsigned long long *ret)
+{
+	unsigned long long number;
+	char *end;
+
+	/* Handle hex, octal, etc. */
+	errno = 0;
+	number = strtoull(s, &end, 0);
+	if (*end == '\0' && end != s) {
+		/* we parsed a number, let's see if we want this */
+		if (errno != ERANGE && min <= number && (!max || number <= max)) {
+			*ret = number;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int string_to_number_l(const char *s, unsigned long min, unsigned long max,
+                       unsigned long *ret)
+{
+	int result;
+	unsigned long long number;
+
+	result = string_to_number_ll(s, min, max, &number);
+	*ret = (unsigned long)number;
+
+	return result;
+}
+
+int string_to_number(const char *s, unsigned int min, unsigned int max,
+                     unsigned int *ret)
+{
+	int result;
+	unsigned long number;
+
+	result = string_to_number_l(s, min, max, &number);
+	*ret = (unsigned int)number;
+
+	return result;
+}
+
+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;
+	return (struct in6_addr *)NULL;
+}
+
+static struct in6_addr *parse_ip6_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)
+		ebt_print_error("Invalid IPv6 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;
+}
+
+/* Set the ipv6 mask and address. Callers should check ebt_errormsg[0].
+ * The string pointed to by address can be altered. */
+void ebt_parse_ip6_address(char *address, struct in6_addr *addr,
+                           struct in6_addr *msk)
+{
+	struct in6_addr *tmp_addr;
+	char buf[256];
+	char *p;
+	int i;
+	int err;
+
+	strncpy(buf, address, sizeof(buf) - 1);
+	/* first the mask */
+	buf[sizeof(buf) - 1] = '\0';
+	if ((p = strrchr(buf, '/')) != NULL) {
+		*p = '\0';
+		tmp_addr = parse_ip6_mask(p + 1);
+	} else
+		tmp_addr = parse_ip6_mask(NULL);
+	in6addrcpy(msk, tmp_addr);
+
+	/* if a null mask is given, the name is ignored, like in "any/0" */
+	if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any)))
+		strcpy(buf, "::");
+
+	if ((err=inet_pton(AF_INET6, buf, addr)) < 1) {
+		ebt_print_error("Invalid IPv6 Address '%s' specified", buf);
+		return;
+	}
+
+	for (i = 0; i < 4; i++)
+		addr->s6_addr32[i] &= msk->s6_addr32[i];
+}
+
+/* Transform the ip6 addr into a string ready for output. */
+char *ebt_ip6_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));
+}
+
+int ebt_ip6mask_to_cidr(const struct in6_addr *k)
+{
+	unsigned int bits = 0;
+	uint32_t a, b, c, d;
+
+	a = ntohl(k->s6_addr32[0]);
+	b = ntohl(k->s6_addr32[1]);
+	c = ntohl(k->s6_addr32[2]);
+	d = ntohl(k->s6_addr32[3]);
+	while (a & 0x80000000U) {
+		++bits;
+		a <<= 1;
+		a  |= (b >> 31) & 1;
+		b <<= 1;
+		b  |= (c >> 31) & 1;
+		c <<= 1;
+		c  |= (d >> 31) & 1;
+		d <<= 1;
+	}
+	if (a != 0 || b != 0 || c != 0 || d != 0)
+		return -1;
+	return bits;
+}
+
+char *ebt_ip6_mask_to_string(const struct in6_addr *msk)
+{
+	int l = ebt_ip6mask_to_cidr(msk);
+	static char buf[51+1];
+
+	if (l == 128)
+		*buf = '\0';
+	else if (l == -1)
+		sprintf(buf, "/%s", ebt_ip6_to_numeric(msk));
+	else
+		sprintf(buf, "/%d", l);
+	return buf;
+}
+
+static char*
+parse_num(const char *str, long min, long max, long *num)
+{
+	char *end;
+
+	errno = 0;
+	*num = strtol(str, &end, 10);
+	if (errno && (*num == LONG_MIN || *num == LONG_MAX)) {
+		ebt_print_error("Invalid number %s: %s", str, strerror(errno));
+		return NULL;
+	}
+	if (min <= max) {
+		if (*num > max || *num < min) {
+			ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max);
+			return NULL;
+		}
+	}
+	if (*num == 0 && str == end)
+		return NULL;
+	return end;
+}
+
+static char *
+parse_range(const char *str, long min, long max, long num[])
+{
+	char *next;
+
+	next = parse_num(str, min, max, num);
+	if (next == NULL)
+		return NULL;
+	if (next && *next == ':')
+		next = parse_num(next+1, min, max, &num[1]);
+	else
+		num[1] = num[0];
+	return next;
+}
+
+int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes,
+		   const char *icmptype, uint8_t type[], uint8_t code[])
+{
+	unsigned int match = n_codes;
+	unsigned int i;
+	long number[2];
+
+	for (i = 0; i < n_codes; i++) {
+		if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype)))
+			continue;
+		if (match != n_codes)
+			ebt_print_error("Ambiguous ICMP type `%s':"
+					" `%s' or `%s'?",
+					icmptype, icmp_codes[match].name,
+					icmp_codes[i].name);
+		match = i;
+	}
+
+	if (match < n_codes) {
+		type[0] = type[1] = icmp_codes[match].type;
+		if (code) {
+			code[0] = icmp_codes[match].code_min;
+			code[1] = icmp_codes[match].code_max;
+		}
+	} else {
+		char *next = parse_range(icmptype, 0, 255, number);
+		if (!next) {
+			ebt_print_error("Unknown ICMP type `%s'",
+							icmptype);
+			return -1;
+		}
+		type[0] = (uint8_t) number[0];
+		type[1] = (uint8_t) number[1];
+		switch (*next) {
+		case 0:
+			if (code) {
+				code[0] = 0;
+				code[1] = 255;
+			}
+			return 0;
+		case '/':
+			if (code) {
+				next = parse_range(next+1, 0, 255, number);
+				code[0] = (uint8_t) number[0];
+				code[1] = (uint8_t) number[1];
+				if (next == NULL)
+					return -1;
+				if (next && *next == 0)
+					return 0;
+			}
+		/* fallthrough */
+		default:
+			ebt_print_error("unknown character %c", *next);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static void print_icmp_code(uint8_t *code)
+{
+	if (!code)
+		return;
+
+	if (code[0] == code[1])
+		printf("/%"PRIu8 " ", code[0]);
+	else
+		printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]);
+}
+
+void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes,
+			 size_t n_codes, uint8_t *type, uint8_t *code)
+{
+	unsigned int i;
+
+	if (type[0] != type[1]) {
+		printf("%"PRIu8 ":%" PRIu8, type[0], type[1]);
+		print_icmp_code(code);
+		return;
+	}
+
+	for (i = 0; i < n_codes; i++) {
+		if (icmp_codes[i].type != type[0])
+			continue;
+
+		if (!code || (icmp_codes[i].code_min == code[0] &&
+			      icmp_codes[i].code_max == code[1])) {
+			printf("%s ", icmp_codes[i].name);
+			return;
+		}
+	}
+	printf("%"PRIu8, type[0]);
+	print_icmp_code(code);
+}
+
+void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes,
+			  size_t n_codes)
+{
+	unsigned int i;
+
+	for (i = 0; i < n_codes; 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");
+}