Initial revision
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    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..3a58b07
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,51 @@
+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..dc17499
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,27 @@
+FOLLOW THESE SIMPLE GUIDELINES:
+-------------------------------
+
+If /usr/src/linux contains the patched kernel for ebtables:
+	%make install
+Otherwise:
+	%make KERNEL_DIR=<<where-the-patched-kernel-is>> install
+
+optional:
+If your /usr/include/linux directory is a symbolic link, use the SYMLINK=y
+option. When unsure, don't use this. If the cp program produces errors
+when executing make install, use this option.
+
+WHAT GETS INSTALLED?
+--------------------
+
+- The needed kernel headers are placed in /usr/include/linux/netfilter_bridge/
+  That's why it needs the KERNEL_DIR. If the SYMLINK=y option is supplied,
+  /usr/include/linux will be a symbolic link.
+- The ebtables manual gets installed in /usr/local/man/man8
+  To put the manual somewhere else, include MANDIR=<<man-path/man>> as
+  option on the command line.
+  The Makefile will append /man8/ebtables.8.
+- ethertypes is placed in /etc/
+- the userspace program ebtables is compiled.
+
+That's all
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c16801e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,58 @@
+# ebtables Makefile
+
+KERNEL_DIR?=/usr/src/linux
+PROGNAME:=ebtables
+PROGVERSION:="2.0pre6 (May 2002)"
+
+MANDIR?=/usr/local/man
+CFLAGS:=-Wall -Wunused
+include extensions/Makefile
+
+# Some kernel testers prefer to use a symlink for /usr/include/linux
+ifeq ($(SYMLINK), y)
+KERNEL_INCLUDES=symlink
+else
+KERNEL_INCLUDES=headers
+endif
+
+all:	ebtables
+
+.PHONY: headers
+headers:
+	mkdir -p /usr/include/linux/netfilter_bridge
+	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge/* \
+		/usr/include/linux/netfilter_bridge/
+	cp -f $(KERNEL_DIR)/include/linux/br_db.h \
+		/usr/include/linux/br_db.h
+	cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \
+		/usr/include/linux/netfilter_bridge.h
+
+.PHONY: symlink
+symlink:
+	ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux
+
+communication.o: communication.c include/ebtables_u.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+ebtables.o: ebtables.c include/ebtables_u.h
+	$(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \
+	-DPROGNAME=\"$(PROGNAME)\" -c -o $@ $<
+
+ebtables: ebtables.o communication.o $(EXT_OBJS)
+	$(CC) $(CFLAGS) -o $@ $^
+
+$(MANDIR)/man8/ebtables.8: ebtables.8
+	mkdir -p $(@D)
+	install -m 0644 -o root -g root $< $@
+
+/etc/ethertypes: ethertypes
+	mkdir -p $(@D)
+	install -m 0644 -o root -g root $< $@
+
+install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \
+	ebtables /etc/ethertypes
+
+clean:
+	-rm -f ebtables
+	rm -f *.o *.c~
+	rm -f extensions/*.o extensions/*.c~
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..830ad5d
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,9 @@
+Thanks go out to:
+
+Lennert Buytenhek
+Rusty Russel
+Harald Welte
+Jason Lunz
+Tim Gardner
+Loïc Minier
+Nick Fedchik
diff --git a/communication.c b/communication.c
new file mode 100644
index 0000000..02aff3f
--- /dev/null
+++ b/communication.c
@@ -0,0 +1,454 @@
+/*
+ * communication.c, v2.0 April 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, lots of linked lists.
+// So, the translation is done here.
+
+#include <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h> // the database
+#include <netinet/in.h> // IPPROTO_IP
+#include <asm/types.h>
+#include "include/ebtables_u.h"
+
+extern char* hooknames[NF_BR_NUMHOOKS];
+
+int sockfd = -1;
+
+void get_sockfd()
+{
+	if (sockfd == -1) {
+		sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
+		if (sockfd < 0)
+			print_error("Problem getting a socket");
+	}
+}
+
+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;
+	char *p, *base;
+	int i, j;
+	unsigned int entries_size = 0;
+
+	new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
+	if (!new)
+		print_memory();
+	new->valid_hooks = u_repl->valid_hooks;
+	memcpy(new->name, u_repl->name, sizeof(new->name));
+	new->nentries = u_repl->nentries;
+	new->num_counters = u_repl->num_counters;
+	new->counters = u_repl->counters;
+	memcpy(new->counter_entry, u_repl->counter_entry,
+	   sizeof(new->counter_entry));
+	// determine size
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		if (!(new->valid_hooks & (1 << i)))
+			continue;
+		entries_size += sizeof(struct ebt_entries);
+		j = 0;
+		e = u_repl->hook_entry[i]->entries;
+		while (e) {
+			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 != u_repl->hook_entry[i]->nentries)
+			print_bug("Wrong nentries: %d != %d, hook = %s", j,
+			   u_repl->hook_entry[i]->nentries, hooknames[i]);
+	}
+
+	new->entries_size = entries_size;
+	new->entries = (char *)malloc(entries_size);
+	if (!new->entries)
+		print_memory();
+
+	// put everything in one block
+	p = new->entries;
+	for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+		struct ebt_entries *hlp;
+
+		if (!(new->valid_hooks & (1 << i)))
+			continue;
+		hlp = (struct ebt_entries *)p;
+		new->hook_entry[i] = hlp;
+		hlp->nentries = u_repl->hook_entry[i]->nentries;
+		hlp->policy = u_repl->hook_entry[i]->policy;
+		hlp->distinguisher = 0; // make the kernel see the light
+		p += sizeof(struct ebt_entries);
+		e = u_repl->hook_entry[i]->entries;
+		while (e) {
+			struct ebt_entry *tmp = (struct ebt_entry *)p;
+
+			tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
+			tmp->invflags = e->invflags;
+			tmp->ethproto = e->ethproto;
+			memcpy(tmp->in, e->in, sizeof(tmp->in));
+			memcpy(tmp->out, e->out, sizeof(tmp->out));
+			memcpy(tmp->logical_in, e->logical_in,
+			   sizeof(tmp->logical_in));
+			memcpy(tmp->logical_out, e->logical_out,
+			   sizeof(tmp->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));
+			p += e->t->target_size +
+			   sizeof(struct ebt_entry_target);
+			tmp->next_offset = p - base;
+			e = e->next;
+		}
+	}
+
+	// sanity check
+	if (p - new->entries != new->entries_size)
+		print_bug("Entries_size bug");
+	return new;
+}
+
+void 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);
+	get_sockfd();
+	// give the data to the kernel
+	optlen = sizeof(struct ebt_replace) + repl->entries_size;
+	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
+		print_error("Couldn't update kernel chains, you probably need "
+		   "to insmod an extension");	
+}
+
+// gets executed after deliver_table
+void
+deliver_counters(struct ebt_u_replace *u_repl, unsigned short *counterchanges)
+{
+	unsigned short *point;
+	struct ebt_counter *old, *new, *newcounters;
+	socklen_t optlen;
+	struct ebt_replace repl;
+
+	if (u_repl->nentries == 0)
+		return;
+
+	newcounters = (struct ebt_counter *)
+	   malloc(u_repl->nentries * sizeof(struct ebt_counter));
+	if (!newcounters)
+		print_memory();
+	memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
+	old = u_repl->counters;
+	new = newcounters;
+	point = counterchanges;
+	while (*point != CNT_END) {
+		if (*point == CNT_NORM) {
+			// 'normal' rule, meaning we didn't do anything to it
+			// So, we just copy
+			new->pcnt = old->pcnt;
+			// we've used an old counter
+			old++;
+			// we've set a new counter
+			new++;
+		} else
+		if (*point == CNT_DEL) {
+			// don't use this old counter
+			old++;
+		} else if (*point == CNT_ADD) {
+			// new counter, let it stay 0
+			new++;
+		} else {
+			// zero it
+			new->pcnt = 0;
+			old++;
+			new++;
+		}
+		point++;
+	}
+
+	free(u_repl->counters);
+	u_repl->counters = newcounters;
+	u_repl->num_counters = u_repl->nentries;
+	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 = u_repl->counters;
+	repl.num_counters = u_repl->num_counters;
+	memcpy(repl.name, u_repl->name, sizeof(repl.name));
+
+	get_sockfd();
+	if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
+		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;
+
+	new = (struct ebt_u_match_list *)
+	   malloc(sizeof(struct ebt_u_match_list));
+	if (!new)
+		print_memory();
+	new->m = (struct ebt_entry_match *)
+	   malloc(m->match_size + sizeof(struct ebt_entry_match));
+	if (!new->m)
+		print_memory();
+	memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (find_match(new->m->u.name) == NULL)
+		print_error("Kernel match %s unsupported by userspace tool",
+		   new->m->u.name);
+	return 0;
+}
+
+static int
+ebt_translate_watcher(struct ebt_entry_watcher *w,
+   struct ebt_u_watcher_list ***l)
+{
+	struct ebt_u_watcher_list *new;
+
+	new = (struct ebt_u_watcher_list *)
+	   malloc(sizeof(struct ebt_u_watcher_list));
+	if (!new)
+		print_memory();
+	new->w = (struct ebt_entry_watcher *)
+	   malloc(w->watcher_size + sizeof(struct ebt_entry_watcher));
+	if (!new->w)
+		print_memory();
+	memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher));
+	new->next = NULL;
+	**l = new;
+	*l = &new->next;
+	if (find_watcher(new->w->u.name) == NULL)
+		print_error("Kernel watcher %s unsupported by userspace tool",
+		   new->w->u.name);
+	return 0;
+}
+
+static int
+ebt_translate_entry(struct ebt_entry *e, unsigned int *hook, int *n, int *cnt,
+   int *totalcnt, struct ebt_u_entry ***u_e, struct ebt_u_replace *u_repl,
+   unsigned int valid_hooks)
+{
+	// 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)
+			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;
+		memcpy(new->in, e->in, sizeof(new->in));
+		memcpy(new->out, e->out, sizeof(new->out));
+		memcpy(new->logical_in, e->logical_in,
+		   sizeof(new->logical_in));
+		memcpy(new->logical_out, e->logical_out,
+		   sizeof(new->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));
+		new->m_list = NULL;
+		new->w_list = NULL;
+		new->next = NULL;
+		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)
+			print_memory();
+		if (find_target(t->u.name) == NULL)
+			print_error("Kernel target %s unsupported by "
+			            "userspace tool", t->u.name);
+		memcpy(new->t, t, t->target_size +
+		   sizeof(struct ebt_entry_target));
+
+		// I love pointers
+		**u_e = new;
+		*u_e = &new->next;
+		(*cnt)++;
+		(*totalcnt)++;
+		return 0;
+	} else { // a new chain
+		int i;
+		struct ebt_entries *entries = (struct ebt_entries *)e;
+		struct ebt_u_entries *new;
+
+		for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
+			if (valid_hooks & (1 << i))
+				break;
+		if (i >= NF_BR_NUMHOOKS)
+			print_bug("Not enough valid hooks");
+		*hook = i;
+		if (*n != *cnt)
+			print_bug("Nr of entries in the chain is wrong");
+		*n = entries->nentries;
+		*cnt = 0;
+		new = (struct ebt_u_entries *)
+		   malloc(sizeof(struct ebt_u_entries));
+		if (!new)
+			print_memory();
+		new->nentries = entries->nentries;
+		new->policy = entries->policy;
+		new->entries = NULL;
+		u_repl->hook_entry[*hook] = new;
+		*u_e = &new->entries;
+		return 0;
+	}
+}
+
+// talk with kernel to receive the kernel's table
+void get_table(struct ebt_u_replace *u_repl)
+{
+	int i, j, k, hook;
+	socklen_t optlen;
+	struct ebt_replace repl;
+	struct ebt_u_entry **u_e;
+
+	get_sockfd();
+
+	optlen = sizeof(struct ebt_replace);
+	strcpy(repl.name, u_repl->name);
+	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_INFO, &repl, &optlen))
+		print_error("A kernel module needed by your command is probably"
+		            " not loaded. Try insmod ebtables or"
+		            " insmod ebtable_%s", repl.name);
+
+	if ( !(repl.entries = (char *) malloc(repl.entries_size)) )
+		print_memory();
+	if (repl.nentries) {
+		if (!(repl.counters = (struct ebt_counter *)
+		   malloc(repl.nentries * sizeof(struct ebt_counter))) )
+			print_memory();
+	}
+	else
+		repl.counters = NULL;
+
+	// we want to receive the counters
+	repl.num_counters = repl.nentries;
+	optlen += repl.entries_size + repl.num_counters *
+	   sizeof(struct ebt_counter);
+	if (getsockopt(sockfd, IPPROTO_IP, EBT_SO_GET_ENTRIES, &repl, &optlen))
+		print_bug("hmm, what is wrong??? bug#1");
+
+	// translate the struct ebt_replace to a struct ebt_u_replace
+	memcpy(u_repl->name, repl.name, sizeof(u_repl->name));
+	u_repl->valid_hooks = repl.valid_hooks;
+	u_repl->nentries = repl.nentries;
+	u_repl->num_counters = repl.num_counters;
+	u_repl->counters = repl.counters;
+	memcpy(u_repl->counter_entry, repl.counter_entry,
+	   sizeof(repl.counter_entry));
+	hook = -1;
+	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
+	EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_entry,
+	   &hook, &i, &j, &k, &u_e, u_repl, u_repl->valid_hooks);
+	if (k != u_repl->nentries)
+		print_bug("Wrong total nentries");
+}
+
+void get_dbinfo(struct brdb_dbinfo *nr)
+{
+	socklen_t optlen = sizeof(struct brdb_dbinfo);
+
+	get_sockfd();
+	
+	if (getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DBINFO, nr, &optlen))
+		print_error("Sorry, br_db code probably not in kernel, "
+		            "try insmod br_db");
+}
+
+void get_db(int len, struct brdb_dbentry *db)
+{
+	socklen_t optlen = len;
+
+	get_sockfd();
+
+	if ( getsockopt(sockfd, IPPROTO_IP, BRDB_SO_GET_DB, db, &optlen) ) {
+		print_bug("hmm, what is wrong??? bug#2");
+	}
+}
+
+void deliver_allowdb(__u16 *decision)
+{
+	socklen_t optlen = sizeof(__u16);
+
+	get_sockfd();
+
+	if (setsockopt(sockfd, IPPROTO_IP, BRDB_SO_SET_ALLOWDB,
+	   decision, optlen))
+		print_error("Sorry, br_db code probably not in kernel, "
+		            "try insmod br_db");
+}
diff --git a/ebtables.8 b/ebtables.8
new file mode 100644
index 0000000..d0d7a18
--- /dev/null
+++ b/ebtables.8
@@ -0,0 +1,434 @@
+.TH EBTABLES 8  "01 May 2002"
+.\"
+.\" Man page written by Bart De Schuymer <bart.de.schuymer@pandora.be>
+.\" It is based on the iptables man page.
+.\"
+.\" 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(v.2.0) \- ethernet bridge packet table administration
+.SH SYNOPSIS
+.BR "ebtables -[ADI] " "chain rule-specification [options]"
+.br
+.BR "ebtables -P " "chain target"
+.br
+.BR "ebtables -[FLZ] [" "chain" "]"
+.br
+.B "ebtables -L DB"
+.br
+.BR "ebtables -[b] [" "y/n" "]"
+.br
+.SH DESCRIPTION
+.B ebtables
+is used to set up, maintain, and inspect the tables of Ethernet frame
+rules in the Linux kernel. It works analogous as iptables, but is less
+complicated. This man page is written with the man page of iptables
+next to it, so don't be surprised to see copied sentences and structure.
+
+There are three tables with built-in chains. Each chain is a list
+of rules which can match frames: each rule specifies what to do with a
+frame which matches. This is called a 'target'. The tables are used to
+divide functionality into different sets of chains.
+
+.SS TARGETS
+A firewall rule specifies criteria for a frame, and a target. If the
+frame does not match, the next rule in the chain is the examined one; if
+it does match, then the next thing to do is specified by the target.
+This target can be one of these values:
+.IR ACCEPT ,
+.IR DROP ,
+.IR CONTINUE ,
+an extention.
+.PP
+.I ACCEPT
+means to let the frame through.
+.I DROP
+means the frame has to be dropped.
+.I CONTINUE
+means the next rule has to be checked. This can be handy to know how many
+frames pass a certain point in the chain or to log those frames. For the
+other targets see the
+.B "TARGET EXTENSIONS"
+section.
+.SS TABLES
+There are three tables.
+.TP
+.B "-t, --table"
+This option specifies the frame matching table which the command should
+operate on. The tables are: 
+.BR filter ,
+this is the default table and contains three chains: 
+.B INPUT 
+(for frames destined for the bridge itself), 
+.B OUTPUT 
+(for locally-generated frames) and
+.B FORWARD 
+(for frames being bridged).
+.BR nat ,
+this table is used to change the mac addresses and contains three chains: 
+.B PREROUTING 
+(for altering frames as soon as they come in), 
+.B OUTPUT 
+(for altering locally generated 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 POSTROUTING and PREROUTING: it would be more accurate to call them
+PREFORWARDING and POSTFORWARDING, but for all those who come from the
+.BR iptables " world to " ebtables
+it is easier to have the same names.
+.BR broute ,
+this table is used to make a brouter, it has one chain:
+.BR BROUTING .
+The targets
+.BR DROP " and " ACCEPT
+have special meaning in this table.
+.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. It is only traversed by frames entering on
+a bridge enslaved nic 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 OPTIONS
+The options can be divided into several different groups.
+.SS COMMANDS
+These options specify the specific actions to perform; only one of them
+can be specified on the command line (the
+.B -Z
+command is an exception). All these options only apply to the selected
+(or default) table.
+.TP
+.B "-A, --append"
+Append a rule to the end of the selected chain.
+.TP
+.B "-D, --delete"
+Delete the specified rule from the selected chain. There are two versions
+of this command. A rule number (starting at 1) or the complete rule can be
+specified.
+.TP
+.B "-I, --insert"
+Insert the specified rule into the selected chain at the specified rule number (1 meaning
+the head of the chain).
+.TP
+.B "-L, --list"
+List all rules in the selected chain. If no chain is selected, all chains
+are listed. If the chainname equals 
+.BR DB ,
+.B ebtables
+will try to show the database. This database gives a survey of the kind of
+frames that pass the different bridge hooks. It uses the interfaces where
+the frame came in or will go out, the protocol field and the hook. This
+database is independent from the rest of
+.B ebtables
+and is in a different kernel module.
+.TP
+.B "-F, --flush"
+Flush the selected chain. If no chain is selected, every chain will be
+flushed. This does not change the policy of the chain.
+.TP
+.B "-Z, --zero"
+Put the counters of the selected chain on zero. If no chain is selected, all the counters
+are put on zero. This can be used in conjunction with the -L command (see above). 
+This will cause the rule counters to be printed on the screen before they are put on zero.
+.TP
+.B "-P, --policy"
+Set the policy for the chain to the given target. The policy is either
+.B ACCEPT
+, either
+.BR DROP .
+.SS PARAMETERS
+The following parameters make up a rule specification (as used in the add
+and delete commands). A "!" argument before the specification inverts the
+test for that specification. Apart from these standard parameters, there are others, see
+.BR "MATCH EXTENSIONS" .
+.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
+.B 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 via which a frame is received (for the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains). 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 (for the
+.BR INPUT ,
+.BR FORWARD ,
+.BR PREROUTING " and " BROUTING
+chains).
+.TP
+.BR "-o, --out-interface " "[!] \fIname\fP"
+The interface via which a frame is going to be sent (for the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains). 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 (for
+the
+.BR OUTPUT ,
+.B FORWARD
+and
+.B POSTROUTING
+chains).
+.TP
+.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]"
+The source mac address. Both mask and address are written as 6 hexadecimal
+numbers seperated by colons. Alternatively one can specify Unicast,
+Multicast or Broadcast.
+.br
+Unicast=00:00:00:00:00:00/01:00:00:00:00:00,
+Multicast=01:00:00:00:00:00/01:00:00:00:00:00 and
+Broadcast=ff:ff:ff:ff:ff:ff/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 -s (above) for more details. The flag
+.B --dst
+is an alias for this option.
+
+.SS OTHER OPTIONS
+.TP
+.B "-V, --version"
+Show the version of the userprogram.
+.TP
+.B "-h, --help"
+Give a brief description of the command syntax. Here you can also specify
+names of extensions and
+.B ebtables
+will try to write help about those extensions. E.g. ebtables -h snat log ip arp.
+.TP
+.BR "-b --db " "[\fIy/n\fP]"
+.IR "" "Enable (" y ") or disable (" n ") the database."
+.TP
+.BR "-j, --jump " "\fItarget\fP"
+The target of the rule. This is one of the following values:
+.BR ACCEPT ,
+.BR DROP ,
+.BR CONTINUE ,
+or a target extension, see
+.BR "TARGET EXTENSIONS" .
+.SH MATCH EXTENSIONS
+.B ebtables
+extensions are precompiled into the userspace tool. So there is no need
+to explicitly load them with a -m option like in iptables. However, these
+extensions deal with functionality supported by supplemental kernel modules.
+.SS ip
+Specify ip specific fields. These will only work if the protocol equals
+.BR 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.
+.SS arp
+Specify arp specific fields. These will only work if the protocol equals
+.BR ARP " or " RARP .
+.TP
+.BR "--arp-opcode " "[!] \fIopcode\fP"
+The (r)arp opcode (decimal or a string, for more details see ebtables -h arp).
+.TP
+.BR "--arp-htype " "[!] \fIhardware type\fP"
+The hardware type, this can be a decimal or the string "Ethernet". This
+is normally Ethernet (value 1).
+.TP
+.BR "--arp-ptype " "[!] \fIprotocol type\fP"
+The protocol type for which the (r)arp is used (hexadecimal or the string "IPv4").
+This is normally IPv4 (0x0800). 
+.TP
+.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]"
+The ARP IP source address specification.
+.TP
+.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]"
+The ARP IP destination address specification.
+.SS vlan
+Specify 802.1Q VLAN specific fields. These will only work if the protocol equals
+.BR 802_1Q .
+For more details see
+.BR "ebtables -h vlan" .
+.TP
+.BR "--vlan-id " "[!] \fIid\fP"
+The VLAN identifier (decimal number from 0 to 4095).
+.TP
+.BR "--vlan-prio " "[!] \fIprio\fP"
+The VLAN priority type, this can be a decimal number from 0 to 7. The default value is 0.
+.SH WATCHER EXTENSION(S)
+Watchers are things that only look at frames passing by. These watchers only see the
+frame if the frame passes all the matches of the rule.
+.SS log
+The fact that the log module is a watcher lets us log stuff while giving a target
+by choice. Note that the log module therefore is not a target.
+.TP
+.B "--log"
+.br
+Use this if you won't specify any other log options, so if you want to use the default
+settings: log-prefix="", no arp logging, no ip logging, log-level=info.
+.TP
+.B --log-level "\fIlevel\fP"
+.br
+defines the logging level. For the possible values: ebtables -h log.
+The default level is 
+.IR info .
+.TP
+.BR --log-prefix " \fItext\fP"
+.br
+defines the prefix to be printed before 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-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 TARGET EXTENSIONS
+.TP
+.B 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.
+.br
+.BR "--to-source " "\fIaddress\fP"
+.br
+The flag
+.B --to-src
+is an alias for this option.
+.br
+.BR "--snat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the snat, the rule still has 
+to give a standard target so
+.B ebtables
+knows what to do.
+The default target is ACCEPT. Making it CONTINUE could let you use
+multiple target extensions on the same frame. Making it DROP doesn't
+make sense, but you could do that too.
+.TP
+.B 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.
+.br
+.BR "--to-destination " "\fIaddress\fP"
+.br
+The flag
+.B --to-dst
+is an alias for this option.
+.br
+.BR "--dnat-target " "\fItarget\fP"
+.br
+Specifies the standard target. After doing the dnat, the rule still has to
+give a standard target so
+.B ebtables
+knows what to do.
+The default target is ACCEPT. Making it CONTINUE could let you use 
+multiple target extensions on the same frame. Making it DROP only makes
+sense in the BROUTING chain but using the redirect target is more logical
+there.
+.TP
+.B 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."
+.br
+.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
+.B ebtables
+knows what to do.
+The default target is ACCEPT. Making it CONTINUE could let you use 
+multiple target extensions on the same frame. Making it DROP in the
+BROUTING chain will let the frames be routed.
+.SH FILES
+.I /etc/ethertypes
+.SH BUGS
+This won't work on an architecture with a user32/kernel64 situation like the Sparc64.
+.SH AUTHOR
+.IR "" "Bart De Schuymer <" bart.de.schuymer@pandora.be >
+.SH SEE ALSO
+.BR iptables "(8), " brctl (8)
diff --git a/ebtables.c b/ebtables.c
new file mode 100644
index 0000000..e28fd96
--- /dev/null
+++ b/ebtables.c
@@ -0,0 +1,1655 @@
+/*
+ * ebtables.c, v2.0 April 2002
+ *
+ * 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 <getopt.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h> // the database
+#include <netinet/in.h>
+#include <asm/types.h>
+#include "include/ebtables_u.h"
+
+// here are the number-name correspondences kept for the ethernet
+// frame type field
+#define PROTOCOLFILE "/etc/ethertypes"
+
+#define DATABASEHOOKNR NF_BR_NUMHOOKS
+#define DATABASEHOOKNAME "DB"
+
+static char *prog_name = PROGNAME;
+static char *prog_version = PROGVERSION;
+char* 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"
+};
+
+// default command line options
+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' },
+	{ "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' },
+	{ "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' },
+	{ 0 }
+};
+
+static struct option *ebt_options = ebt_original_options;
+
+// yup, all the possible target names
+char* standard_targets[NUM_STANDARD_TARGETS] = {
+	"ACCEPT",
+	"DROP",
+	"CONTINUE",
+};
+
+unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0};
+unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0};
+unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255};
+
+// tells what happened to the old rules
+static unsigned short *counterchanges;
+// holds all the data
+static struct ebt_u_replace replace;
+
+// the chosen table
+static struct ebt_u_table *table = NULL;
+// the lists of supported tables, matches, watchers and targets
+static struct ebt_u_table *tables = NULL;
+static struct ebt_u_match *matches = NULL;
+static struct ebt_u_watcher *watchers = NULL;
+static struct ebt_u_target *targets = NULL;
+
+struct ebt_u_target *find_target(const char *name)
+{
+	struct ebt_u_target *t = targets;
+
+	while(t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+struct ebt_u_match *find_match(const char *name)
+{
+	struct ebt_u_match *m = matches;
+
+	while(m && strcmp(m->name, name))
+		m = m->next;
+	return m;
+}
+
+struct ebt_u_watcher *find_watcher(const char *name)
+{
+	struct ebt_u_watcher *w = watchers;
+
+	while(w && strcmp(w->name, name))
+		w = w->next;
+	return w;
+}
+
+struct ebt_u_table *find_table(char *name)
+{
+	struct ebt_u_table *t = tables;
+
+	while (t && strcmp(t->name, name))
+		t = t->next;
+	return t;
+}
+
+// The pointers in here are special:
+// The struct ebt_target * pointer is actually a struct ebt_u_target * pointer.
+// instead of making yet a few other structs, we just do a cast.
+// 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.
+// Same holds for the struct ebt_match and struct ebt_watcher pointers
+struct ebt_u_entry *new_entry;
+
+void 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;
+	// the init function of the standard target should have put the verdict
+	// on CONTINUE
+	e->t = (struct ebt_entry_target *)find_target(EBT_STANDARD_TARGET);
+	if (!e->t)
+		print_bug("Couldn't load standard target\n");
+}
+
+// this doesn't free e, becoz the calling function might need e->next
+void 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);
+}
+
+// the user will use the match, so put it in new_entry
+static void add_match(struct ebt_u_match *m)
+{
+	struct ebt_u_match_list **m_list, *new;
+
+	m->used = 1;
+	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)
+		print_memory();
+	*m_list = new;
+	new->next = NULL;
+	new->m = (struct ebt_entry_match *)m;
+}
+
+static void add_watcher(struct ebt_u_watcher *w)
+{
+	struct ebt_u_watcher_list **w_list;
+	struct ebt_u_watcher_list *new;
+
+	w->used = 1;
+	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)
+		print_memory();
+	*w_list = new;
+	new->next = NULL;
+	new->w = (struct ebt_entry_watcher *)w;
+}
+
+static int global_option_offset = 0;
+#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)
+		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)
+		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;
+}
+
+void register_match(struct ebt_u_match *m)
+{
+	int size = m->size + sizeof(struct ebt_entry_match);
+	struct ebt_u_match **i;
+
+	m->m = (struct ebt_entry_match *)malloc(size);
+	if (!m->m)
+		print_memory();
+	strcpy(m->m->u.name, m->name);
+	m->m->match_size = m->size;
+	ebt_options = merge_options
+	   (ebt_options, m->extra_ops, &(m->option_offset));
+	m->init(m->m);
+
+	for (i = &matches; *i; i = &((*i)->next));
+	m->next = NULL;
+	*i = m;
+}
+
+void register_watcher(struct ebt_u_watcher *w)
+{
+	int size = w->size + sizeof(struct ebt_entry_watcher);
+	struct ebt_u_watcher **i;
+
+	w->w = (struct ebt_entry_watcher *)malloc(size);
+	if (!w->w)
+		print_memory();
+	strcpy(w->w->u.name, w->name);
+	w->w->watcher_size = w->size;
+	ebt_options = merge_options
+	   (ebt_options, w->extra_ops, &(w->option_offset));
+	w->init(w->w);
+
+	for (i = &watchers; *i; i = &((*i)->next));
+	w->next = NULL;
+	*i = w;
+}
+
+void register_target(struct ebt_u_target *t)
+{
+	int size = t->size + sizeof(struct ebt_entry_target);
+	struct ebt_u_target **i;
+
+	t->t = (struct ebt_entry_target *)malloc(size);
+	if (!t->t)
+		print_memory();
+	strcpy(t->t->u.name, t->name);
+	t->t->target_size = t->size;
+	ebt_options = merge_options
+	   (ebt_options, t->extra_ops, &(t->option_offset));
+	t->init(t->t);
+	for (i = &targets; *i; i = &((*i)->next));
+	t->next = NULL;
+	*i = t;
+}
+
+void register_table(struct ebt_u_table *t)
+{
+	t->next = tables;
+	tables = t;
+}
+
+// used to parse /etc/etherproto
+int disregard_whitespace(char *buffer, FILE *ifp)
+{
+	int hlp;
+	buffer[0] = '\t';
+	while (buffer[0] == '\t' || buffer[0] == '\n' || buffer[0] == ' ') {
+		hlp = fscanf(ifp, "%c", buffer);
+		if (hlp == EOF || hlp == 0) return -1;
+	}
+	return 0;
+}
+
+// used to parse /etc/etherproto
+int disregard_tabspace(char *buffer, FILE *ifp)
+{
+	int hlp;
+	buffer[0] = '\t';
+	while (buffer[0] == '\t' || buffer[0] == ' ') {
+		hlp = fscanf(ifp, "%c", buffer);
+		if (hlp == EOF || hlp == 0) return -1;
+	}
+	return 0;
+}
+
+// helper function: processes a line of data from the file /etc/etherproto
+int get_a_line(char *buffer, char *value, FILE *ifp)
+{
+	int i, hlp;
+	char anotherhlp;
+
+	/* discard comment lines && whitespace*/
+	while (1) {
+		if (disregard_whitespace(buffer, ifp)) return -1;
+		if (buffer[0] == '#')
+			while (1) {
+				hlp = fscanf(ifp, "%c", &anotherhlp);
+				if (!hlp || hlp == EOF)
+					return -1;
+				if (anotherhlp == '\n')
+					break;
+			}
+		else break;
+	}
+
+	// buffer[0] already contains the first letter
+	for (i = 1; i < 21; i++) {
+		hlp = fscanf(ifp, "%c", buffer + i);
+		if (hlp == EOF || hlp == 0) return -1;
+		if (buffer[i] == '\t' || buffer[i] == ' ')
+			break;
+	}
+	if (i == 21) return -1;
+	buffer[i] = '\0';
+	if (disregard_tabspace(value, ifp))
+		return -1;
+	// maybe I should allow 0x0800 instead of 0800, but I'm feeling lazy
+	// buffer[0] already contains the first letter
+	for (i = 1; i < 5; i++) {
+		hlp = fscanf(ifp, "%c", value+i);
+		if (value[i] == '\n' || value[i] == '\t' ||
+		   value[i] == ' ' || hlp == EOF)
+			break;
+	}
+	if (i == 5) return -1;
+	/* discard comments at the end of a line */
+	if (value[i] == '\t' || value[i] == ' ')
+		while (1) {
+			hlp = fscanf(ifp, "%c", &anotherhlp);
+			if (!hlp || hlp == EOF || anotherhlp == '\n')
+				break;
+		}
+	value[i] = '\0';
+	return 0;
+}
+
+// helper function for list_em()
+int number_to_name(unsigned short proto, char *name)
+{
+	FILE *ifp;
+	char buffer[21], value[5], *bfr;
+	unsigned short i;
+
+	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+		return -1;
+	while (1) {
+		if (get_a_line(buffer, value, ifp)) {
+			fclose(ifp);
+			return -1;
+		}
+		i = (unsigned short) strtol(value, &bfr, 16);
+		if (*bfr != '\0' || i != proto)
+			continue;
+		strcpy(name, buffer);
+		fclose(ifp);
+		return 0;
+	}
+}
+
+// helper function for list_rules()
+static void list_em(int hooknr)
+{
+	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;
+	char name[21];
+
+	hlp = replace.hook_entry[hooknr]->entries;
+	printf("\nBridge chain: %s\nPolicy: %s\n", hooknames[hooknr],
+	   standard_targets[replace.hook_entry[hooknr]->policy]);
+	printf("nr. of entries: %d \n", replace.hook_entry[hooknr]->nentries);
+
+	i = replace.hook_entry[hooknr]->nentries;
+	while (i >9) {
+		space++;
+		i /= 10;
+	}
+
+	for (i = 0; i < replace.hook_entry[hooknr]->nentries; i++) {
+		digits = 0;
+		// A little work to get nice rule numbers.
+		while (j > 9) {
+			digits++;
+			j /= 10;
+		}
+		for (j = 0; j < space - digits; j++)
+			printf(" ");
+		printf("%d. ", i + 1);
+
+		// 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("eth proto: ");
+			if (hlp->invflags & EBT_IPROTO)
+				printf("! ");
+			if (hlp->bitmask & EBT_802_3)
+				printf("Length, ");
+			else {
+				if (number_to_name(ntohs(hlp->ethproto), name))
+					printf("0x%x, ", ntohs(hlp->ethproto));
+				else
+					printf("%s, ", name);
+			}
+		}
+		if (hlp->bitmask & EBT_SOURCEMAC) {
+			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+			printf("source mac: ");
+			if (hlp->invflags & EBT_ISOURCE)
+				printf("! ");
+			if (!memcmp(hlp->sourcemac, mac_type_unicast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_unicast, 6)) {
+				printf("Unicast");
+				goto endsrc;
+			}
+			if (!memcmp(hlp->sourcemac, mac_type_multicast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_multicast, 6)) {
+				printf("Multicast");
+				goto endsrc;
+			}
+			if (!memcmp(hlp->sourcemac, mac_type_broadcast, 6) &&
+			    !memcmp(hlp->sourcemsk, msk_type_broadcast, 6)) {
+				printf("Broadcast");
+				goto endsrc;
+			}
+			for (j = 0; j < ETH_ALEN; j++)
+				printf("%02x%s", hlp->sourcemac[j],
+				   (j == ETH_ALEN - 1) ? "" : ":");
+			if (memcmp(hlp->sourcemsk, hlpmsk, 6)) {
+				printf("/");
+				for (j = 0; j < ETH_ALEN; j++)
+					printf("%02x%s", hlp->sourcemsk[j],
+					   (j == ETH_ALEN - 1) ? "" : ":");
+			}
+endsrc:
+			printf(", ");
+		}
+		if (hlp->bitmask & EBT_DESTMAC) {
+			char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+			printf("dest mac: ");
+			if (hlp->invflags & EBT_IDEST)
+				printf("! ");
+			if (!memcmp(hlp->destmac, mac_type_unicast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_unicast, 6)) {
+				printf("Unicast");
+				goto enddst;
+			}
+			if (!memcmp(hlp->destmac, mac_type_multicast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_multicast, 6)) {
+				printf("Multicast");
+				goto enddst;
+			}
+			if (!memcmp(hlp->destmac, mac_type_broadcast, 6) &&
+			    !memcmp(hlp->destmsk, msk_type_broadcast, 6)) {
+				printf("Broadcast");
+				goto enddst;
+			}
+			for (j = 0; j < ETH_ALEN; j++)
+				printf("%02x%s", hlp->destmac[j],
+				   (j == ETH_ALEN - 1) ? "" : ":");
+			if (memcmp(hlp->destmsk, hlpmsk, 6)) {
+				printf("/");
+				for (j = 0; j < ETH_ALEN; j++)
+					printf("%02x%s", hlp->destmsk[j],
+					   (j == ETH_ALEN - 1) ? "" : ":");
+			}
+enddst:
+			printf(", ");
+		}
+		if (hlp->in[0] != '\0') {
+			if (hlp->invflags & EBT_IIN)
+				printf("! ");
+			printf("in-if: %s, ", hlp->in);
+		}
+		if (hlp->logical_in[0] != '\0') {
+			if (hlp->invflags & EBT_ILOGICALIN)
+				printf("! ");
+			printf("logical in-if: %s, ", hlp->logical_in);
+		}
+		if (hlp->logical_out[0] != '\0') {
+			if (hlp->invflags & EBT_ILOGICALOUT)
+				printf("! ");
+			printf("logical out-if: %s, ", hlp->logical_out);
+		}
+		if (hlp->out[0] != '\0') {
+			if (hlp->invflags & EBT_IOUT)
+				printf("! ");
+			printf("out-if: %s, ", hlp->out);
+		}
+
+		m_l = hlp->m_list;
+		while (m_l) {
+			m = find_match(m_l->m->u.name);
+			if (!m)
+				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 = find_watcher(w_l->w->u.name);
+			if (!w)
+				print_bug("Watcher not found");
+			w->print(hlp, w_l->w);
+			w_l = w_l->next;
+		}
+
+		printf("target: ");
+		t = find_target(hlp->t->u.name);
+		if (!t)
+			print_bug("Target not found");
+		t->print(hlp, hlp->t);
+		printf(", count = %llu",
+		   replace.counters[replace.counter_entry[hooknr] + i].pcnt);
+		printf("\n");
+		hlp = hlp->next;
+	}
+}
+
+// parse the chain name and return the corresponding nr
+int get_hooknr(char* arg)
+{
+	int i;
+
+	// database is special case (not really a chain)
+	if (!strcmp(arg, DATABASEHOOKNAME))
+		return DATABASEHOOKNR;
+
+	for (i = 0; i < NF_BR_NUMHOOKS; i++)
+		if (!strcmp(arg, hooknames[i]))
+			return i;
+	return -1;
+}
+
+// yup, print out help
+void print_help()
+{
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	printf(
+"%s v%s\n"
+"Usage:\n"
+"ebtables -[ADI] chain rule-specification [options]\n"
+"ebtables -P chain target\n"
+"ebtables -[LFZ] [chain]\n"
+"ebtables -[b] [y,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"
+"--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"
+"--list   -L "DATABASEHOOKNAME"                : List the database (if present)\n"
+"--flush  -F [chain]           : Delete all rules in chain or in all chains\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"
+"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"
+"--version -V                  : print package version\n"
+"\n" ,
+	prog_name,
+	prog_version);
+
+	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(hooknames);
+	exit(0);
+}
+
+// execute command L
+static void list_rules()
+{
+	int i;
+
+	printf("Bridge table: %s\n", table->name);
+	if (replace.selected_hook != -1) list_em(replace.selected_hook);
+	else
+		for (i = 0; i < NF_BR_NUMHOOKS; i++)
+			if (replace.valid_hooks & (1 << i))
+				list_em(i);
+	return;
+}
+
+// execute command P
+static void change_policy(int policy)
+{
+	int i;
+
+	// don't do anything if the policy is the same
+	if (replace.hook_entry[replace.selected_hook]->policy != policy) {
+		replace.hook_entry[replace.selected_hook]->policy = policy;
+		replace.num_counters = replace.nentries;
+		if (replace.nentries) {
+			// '+ 1' for the CNT_END
+			if (!(counterchanges = (unsigned short *) malloc(
+			   (replace.nentries + 1) * sizeof(unsigned short))))
+				print_memory();
+			// done nothing special to the rules
+			for (i = 0; i < replace.nentries; i++)
+				counterchanges[i] = CNT_NORM;
+			counterchanges[replace.nentries] = CNT_END;
+		}
+		else
+			counterchanges = NULL;
+	}
+	else
+		exit(0);
+}
+
+// flush one chain or the complete table
+static void flush_chains()
+{
+	int i, j, oldnentries;
+	unsigned short *cnt;
+	struct ebt_u_entry *u_e, *tmp;
+
+	// flush whole table
+	if (replace.selected_hook == -1) {
+		if (replace.nentries == 0)
+			exit(0);
+		replace.nentries = 0;
+		// no need for the kernel to give us counters back
+		replace.num_counters = 0;
+		// free everything and zero (n)entries
+		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			replace.hook_entry[i]->nentries = 0;
+			u_e = replace.hook_entry[i]->entries;
+			while (u_e) {
+				free_u_entry(u_e);
+				tmp = u_e->next;
+				free(u_e);
+				u_e = tmp;
+			}
+			replace.hook_entry[i]->entries = NULL;
+		}
+		return;
+	}
+
+	if (replace.hook_entry[replace.selected_hook]->nentries == 0)
+		exit(0);
+	oldnentries = replace.nentries;
+	replace.nentries = replace.nentries -
+	   replace.hook_entry[replace.selected_hook]->nentries;
+
+	// delete the counters belonging to the specified chain
+	if (replace.nentries) {
+		// +1 for CNT_END
+		if ( !(counterchanges = (unsigned short *)
+		   malloc((oldnentries + 1) * sizeof(unsigned short))) )
+			print_memory();
+		cnt = counterchanges;
+		for (i = 0; i < NF_BR_NUMHOOKS; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+				if (i != replace.selected_hook)
+					*cnt = CNT_NORM;
+				else
+					*cnt = CNT_DEL;
+				cnt++;
+			}
+		}
+		*cnt = CNT_END;
+		replace.num_counters = oldnentries;
+	}
+	else
+		replace.num_counters = 0;
+
+	replace.hook_entry[replace.selected_hook]->nentries = 0;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	while (u_e) {
+		free_u_entry(u_e);
+		tmp = u_e->next;
+		free(u_e);
+		u_e = tmp;
+	}
+	replace.hook_entry[replace.selected_hook]->entries = NULL;
+}	
+
+// -1 == no match
+static int check_rule_exists(int rule_nr)
+{
+	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;
+	int i, j, k;
+
+	// handle '-D chain rulenr' command
+	if (rule_nr != -1) {
+		if (rule_nr >
+		   replace.hook_entry[replace.selected_hook]->nentries)
+			return 0;
+		// user starts counting from 1
+		return rule_nr - 1;
+	}
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	// check for an existing rule (if there are duplicate rules,
+	// take the first occurance)
+	for (i = 0; i < replace.hook_entry[replace.selected_hook]->nentries;
+	   i++, u_e = u_e->next) {
+		if (!u_e)
+			print_bug("Hmm, trouble");
+		if ( u_e->ethproto == new_entry->ethproto
+		   && !strcmp(u_e->in, new_entry->in)
+		   && !strcmp(u_e->out, new_entry->out)
+		   && u_e->bitmask == new_entry->bitmask) {
+			if (new_entry->bitmask & EBT_SOURCEMAC &&
+			   strcmp(u_e->sourcemac, new_entry->sourcemac))
+				continue;
+			if (new_entry->bitmask & EBT_DESTMAC &&
+			   strcmp(u_e->destmac, new_entry->destmac))
+				continue;
+			if (new_entry->bitmask != u_e->bitmask ||
+			   new_entry->invflags != u_e->invflags)
+				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_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;
+}
+
+// execute command A
+static void add_rule(int rule_nr)
+{
+	int i, j;
+	struct ebt_u_entry *u_e, *u_e2;
+	unsigned short *cnt;
+	struct ebt_u_match_list *m_l;
+	struct ebt_u_watcher_list *w_l;
+
+	if (rule_nr != -1) { // command -I
+		if (--rule_nr >
+		   replace.hook_entry[replace.selected_hook]->nentries)
+			print_error("rule nr too high: %d > %d", rule_nr,
+			   replace.hook_entry[replace.selected_hook]->nentries);
+	} else
+		rule_nr = replace.hook_entry[replace.selected_hook]->nentries;
+	// we're adding one rule
+	replace.num_counters = replace.nentries;
+	replace.nentries++;
+	replace.hook_entry[replace.selected_hook]->nentries++;
+
+	// handle counter stuff
+	// +1 for CNT_END
+	if ( !(counterchanges = (unsigned short *)
+	   malloc((replace.nentries + 1) * sizeof(unsigned short))) )
+		print_memory();
+	cnt = counterchanges;
+	for (i = 0; i < replace.selected_hook; i++) {
+		if (!(replace.valid_hooks & (1 << i)))
+			continue;
+		for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+	}
+	for (i = 0; i < rule_nr; i++) {
+		*cnt = CNT_NORM;
+		cnt++;
+	}
+	*cnt = CNT_ADD;
+	cnt++;
+	while (cnt != counterchanges + replace.nentries) {
+		*cnt = CNT_NORM;
+		cnt++;
+	}
+	*cnt = CNT_END;
+
+	// go to the right position in the chain
+	u_e2 = NULL;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	for (i = 0; i < rule_nr; i++) {
+		u_e2 = u_e;
+		u_e = u_e->next;
+	}
+	// insert the rule
+	if (u_e2)
+		u_e2->next = new_entry;
+	else
+		replace.hook_entry[replace.selected_hook]->entries = new_entry;
+	new_entry->next = u_e;
+
+	// 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;
+}
+
+// execute command D
+static void delete_rule(int rule_nr)
+{
+	int i, j, lentmp = 0;
+	unsigned short *cnt;
+	struct ebt_u_entry *u_e, *u_e2;
+
+	if ( (i = check_rule_exists(rule_nr)) == -1 )
+		print_error("Sorry, rule does not exists");
+
+	// we're deleting a rule
+	replace.num_counters = replace.nentries;
+	replace.nentries--;
+
+	if (replace.nentries) {
+		for (j = 0; j < replace.selected_hook; j++) {
+			if (!(replace.valid_hooks & (1 << j)))
+				continue;
+			lentmp += replace.hook_entry[j]->nentries;
+		}
+		lentmp += i;
+		// +1 for CNT_END
+		if ( !(counterchanges = (unsigned short *)malloc(
+		   (replace.num_counters + 1) * sizeof(unsigned short))) )
+			print_memory();
+		cnt = counterchanges;
+		for (j = 0; j < lentmp; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_DEL;
+		cnt++;
+		for (j = 0; j < replace.num_counters - lentmp; j++) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_END;
+	}
+	else
+		replace.num_counters = 0;
+
+	// go to the right position in the chain
+	u_e2 = NULL;
+	u_e = replace.hook_entry[replace.selected_hook]->entries;
+	for (j = 0; j < i; j++) {
+		u_e2 = u_e;
+		u_e = u_e->next;
+	}
+
+	// remove from the chain
+	if (u_e2)
+		u_e2->next = u_e->next;
+	else
+		replace.hook_entry[replace.selected_hook]->entries = u_e->next;
+
+	replace.hook_entry[replace.selected_hook]->nentries--;
+	// free everything
+	free_u_entry(u_e);
+	free(u_e);
+}
+
+// execute command Z
+void zero_counters(int zerochain)
+{
+
+	if (zerochain == -1) {
+		// tell main() we don't update the counters
+		// this results in tricking the kernel to zero his counters,
+		// naively expecting userspace to update its counters. Muahahaha
+		counterchanges = NULL;
+		replace.num_counters = 0;
+	} else {
+		int i, j;
+		unsigned short *cnt;
+
+		if (replace.hook_entry[zerochain]->nentries == 0)
+			exit(0);
+		counterchanges = (unsigned short *)
+		   malloc((replace.nentries + 1) * sizeof(unsigned short));
+		if (!counterchanges)
+			print_memory();
+		cnt = counterchanges;
+		for (i = 0; i < zerochain; i++) {
+			if (!(replace.valid_hooks & (1 << i)))
+				continue;
+			for (j = 0; j < replace.hook_entry[i]->nentries; j++) {
+				*cnt = CNT_NORM;
+				cnt++;
+			}
+		}
+		for (i = 0; i < replace.hook_entry[zerochain]->nentries; i++) {
+			*cnt = CNT_ZERO;
+			cnt++;
+		}
+		while (cnt != counterchanges + replace.nentries) {
+			*cnt = CNT_NORM;
+			cnt++;
+		}
+		*cnt = CNT_END;
+	}
+}
+
+// list the database (optionally compiled into the kernel)
+static void list_db()
+{
+	struct brdb_dbinfo nr;
+	struct brdb_dbentry *db;
+	char name[21];
+	int i;
+
+	get_dbinfo(&nr);
+
+	// 0 : database disabled (-db n)
+	if (!(nr.nentries))
+		print_error("Database not present"
+		            " (disabled), try ebtables --db y");
+	nr.nentries--;
+	if (!nr.nentries) print_error("Database empty");
+	if ( !(db = (struct brdb_dbentry *)
+	   malloc(nr.nentries * sizeof(struct brdb_dbentry))) )
+		print_memory();
+
+	get_db(nr.nentries, db);
+	printf("number of entries: %d\n", nr.nentries);
+	for (i = 0; i < nr.nentries; i++) {
+		printf(
+		"%d:\n"
+		"hook    : %s\n"
+		"in-if   : %s\n"
+		"out-if  : %s\n"
+		"protocol: ", i + 1, hooknames[db->hook], db->in, db->out);
+		if (db->ethproto == IDENTIFY802_3)
+			printf("802.2/802.3 STYLE LENGTH FIELD\n");
+		else {
+			if (number_to_name(ntohs(db->ethproto), name))
+				printf("%x\n",ntohs(db->ethproto));
+			else
+				printf("%s\n", name);
+		}
+		db++;
+	}
+	exit(0);
+}
+
+// handle db [dis,en]abling
+static void allowdb(char yorn)
+{
+	__u16 decision;
+
+	if (yorn != 'y' && yorn != 'n')
+		print_error("Option [y] or [n] needed");
+
+	if (yorn == 'y')
+		decision = BRDB_DB;
+	else
+		decision = BRDB_NODB;
+
+	deliver_allowdb(&decision);
+
+	exit(0);
+}
+
+// set ethproto
+int name_to_protocol(char *name)
+{
+	FILE *ifp;
+	char buffer[21], value[5], *bfr;
+	unsigned short i;
+
+	if (!strcasecmp("LENGTH", name)) {
+		new_entry->ethproto = 0;
+		new_entry->bitmask |= EBT_802_3;
+		return 1;
+	}
+	if ( !(ifp = fopen(PROTOCOLFILE, "r")) )
+		return -1;
+	while (1) {
+		if (get_a_line(buffer, value, ifp)) return -1;
+		if (strcasecmp(buffer, name))
+			continue;
+		i = (unsigned short) strtol(value, &bfr, 16);
+		if (*bfr != '\0')
+			return -1;
+		new_entry->ethproto = i;
+		fclose(ifp);
+		return 0;
+	}
+	return -1;
+}
+
+// put the mac address into 6 (ETH_ALEN) bytes
+int getmac(char *from, char *to)
+{
+	int i, tmp;
+	char *buffer;
+
+	if (strlen(from) != 3 * ETH_ALEN - 1)
+		return -1;
+	for (i = 1; i < ETH_ALEN; i++) {
+		if (from[i*3 - 1] != ':')
+			return -1;
+		from[i*3 - 1] = '\0';
+	}
+	for (i = 0; i < ETH_ALEN; i++) {
+		tmp = strtol(from + i*3, &buffer, 16);
+		if (*buffer != '\0' || tmp > 255 || tmp < 0)
+			return -1;
+		to[i] = (unsigned char) tmp;
+	}
+	return 0;
+}
+
+int getmac_and_mask(char *from, char *to, char *mask)
+{
+	char *p;
+	int i;
+
+	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 ( (p = strrchr(from, '/')) != NULL) {
+		*p = '\0';
+		if (getmac(p + 1, mask))
+			return -1;
+	} else
+		memset(mask, 0xff, ETH_ALEN);
+	if (getmac(from, to))
+		return -1;
+	for (i = 0; i < ETH_ALEN; i++)
+		to[i] &= mask[i];
+	return 0;
+}
+
+int check_inverse(const char option[])
+{
+	if (strcmp(option, "!") == 0) {
+		optind++;
+		return 1;
+	}
+	return 0;
+}
+
+void check_option(unsigned int *flags, unsigned int mask)
+{
+	if (*flags & mask)
+		print_error("Multiple use of same option not allowed");
+	*flags |= mask;
+}
+
+#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
+// the main thing
+int main(int argc, char *argv[])
+{
+	char *buffer, allowbc = 'n';
+	int c, i;
+	// this special one for the -Z option (we can have -Z <this> -L <that>)
+	int zerochain = -1;
+	int policy = -1;
+	int rule_nr = -1;// used for -D chain number
+	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;
+
+	// initialize the table name, OPT_ flags, selected hook and command
+	strcpy(replace.name, "filter");
+	replace.flags = 0;
+	replace.selected_hook = -1;
+	replace.command = 'h';
+
+	new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
+	if (!new_entry)
+		print_memory();
+	// put some sane values in our new entry
+	initialize_entry(new_entry);
+
+	// getopt saves the day
+	while ((c = getopt_long(argc, argv,
+	   "-A:D:I:L::Z::F::P:Vhi:o:j:p:b:s:d:t:", ebt_options, NULL)) != -1) {
+		switch (c) {
+
+		case 'A': // add a rule
+		case 'D': // delete a rule
+		case 'P': // define policy
+		case 'I': // insert a rule
+			replace.command = c;
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.flags |= OPT_COMMAND;
+			if ((replace.selected_hook = get_hooknr(optarg)) == -1)
+				print_error("Bad chain");
+			if (c == 'D' && optind < argc &&
+			   argv[optind][0] != '-') {
+				rule_nr = strtol(argv[optind], &buffer, 10);
+				if (*buffer != '\0' || rule_nr < 0)
+					print_error("Problem with the "
+					            "specified rule number");
+				optind++;
+			}
+			if (c == 'P') {
+				if (optind >= argc)
+					print_error("No policy specified");
+				for (i = 0; i < 2; i++)
+					if (!strcmp(argv[optind],
+					   standard_targets[i])) {
+						policy = i;
+						break;
+					}
+				if (policy == -1)
+					print_error("Wrong policy");
+				optind++;
+			}
+			if (c == 'I') {
+				if (optind >= argc)
+					print_error("No rulenr for -I"
+					            " specified");
+				rule_nr = strtol(argv[optind], &buffer, 10);
+				if (*buffer != '\0' || rule_nr < 0)
+					print_error("Problem with the specified"
+					            " rule number");
+				optind++;
+			}
+			break;
+
+		case 'L': // list
+		case 'F': // flush
+		case 'Z': // zero counters
+			if (c == 'Z') {
+				if (replace.flags & OPT_ZERO)
+					print_error("Multiple commands"
+					            " not allowed");
+				if ( (replace.flags & OPT_COMMAND &&
+				   replace.command != 'L'))
+					print_error("command -Z only allowed "
+					            "together with command -L");
+				replace.flags |= OPT_ZERO;
+			} else {
+				replace.command = c;
+				if (replace.flags & OPT_COMMAND)
+					print_error("Multiple commands"
+					            " not allowed");
+				replace.flags |= OPT_COMMAND;
+			}
+			i = -1;
+			if (optarg) {
+				if ( (i = get_hooknr(optarg)) == -1 )
+					print_error("Bad chain");
+			} else
+				if (optind < argc && argv[optind][0] != '-') {
+					if ((i = get_hooknr(argv[optind]))
+					   == -1)
+						print_error("Bad chain");
+					optind++;
+				}
+			if (i != -1) {
+				if (c == 'Z')
+					zerochain = i;
+				else
+					replace.selected_hook = i;
+			}
+			break;
+
+		case 'V': // version
+			replace.command = 'V';
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			printf("%s, %s\n", prog_name, prog_version);
+			exit(0);
+
+		case 'h': // help
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands 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 ((m = find_match(argv[optind])))
+					add_match(m);
+				else if ((w = find_watcher(argv[optind])))
+					add_watcher(w);
+				else {
+					if (!(t = find_target(argv[optind])))
+						print_error("Extension %s "
+						   "not found", argv[optind]);
+					if (replace.flags & OPT_JUMP)
+						print_error("Sorry, you can "
+						 "only see help for one "
+						 "target extension each time");
+					replace.flags |= OPT_JUMP;
+					new_entry->t =
+					   (struct ebt_entry_target *)t;
+				}
+				optind++;
+			}
+			break;
+
+		case 't': // table
+			check_option(&replace.flags, OPT_TABLE);
+			if (strlen(optarg) > EBT_TABLE_MAXNAMELEN)
+				print_error("Table name too long");
+			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
+			if ((replace.flags & OPT_COMMAND) == 0)
+				print_error("No command specified");
+			if ( replace.command != 'A' &&
+			   replace.command != 'D' && replace.command != 'I')
+				print_error("Command and option do not match");
+			if (c == 'i') {
+				check_option(&replace.flags, OPT_IN);
+				if (replace.selected_hook > 2 &&
+				   replace.selected_hook < NF_BR_BROUTING)
+					print_error("Use in-interface only in "
+					   "INPUT, FORWARD, PREROUTING and"
+					   "BROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IIN;
+
+				if (optind > argc)
+					print_error("No in-interface "
+					            "specified");
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interfacelength");
+				strcpy(new_entry->in, argv[optind - 1]);
+				break;
+			}
+			if (c == 2) {
+				check_option(&replace.flags, OPT_LOGICALIN);
+				if (replace.selected_hook > 2 &&
+				   replace.selected_hook < NF_BR_BROUTING)
+					print_error("Use logical in-interface "
+					   "only in INPUT, FORWARD, "
+					   "PREROUTING and BROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ILOGICALIN;
+
+				if (optind > argc)
+					print_error("No logical in-interface "
+					            "specified");
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interfacelength");
+				strcpy(new_entry->logical_in, argv[optind - 1]);
+				break;
+			}
+			if (c == 'o') {
+				check_option(&replace.flags, OPT_OUT);
+				if (replace.selected_hook < 2)
+					print_error("Use out-interface only"
+					   " in OUTPUT, FORWARD and "
+					   "POSTROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IOUT;
+
+				if (optind > argc)
+					print_error("No out-interface "
+					            "specified");
+
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interface "
+					            "length");
+				strcpy(new_entry->out, argv[optind - 1]);
+				break;
+			}
+			if (c == 3) {
+				check_option(&replace.flags, OPT_LOGICALOUT);
+				if (replace.selected_hook < 2)
+					print_error("Use logical out-interface "
+					   "only in OUTPUT, FORWARD and "
+					   "POSTROUTING chains");
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ILOGICALOUT;
+
+				if (optind > argc)
+					print_error("No logical out-interface "
+					            "specified");
+
+				if (strlen(argv[optind - 1]) >= IFNAMSIZ)
+					print_error("Illegal interface "
+					            "length");
+				strcpy(new_entry->logical_out,
+				   argv[optind - 1]);
+				break;
+			}
+			if (c == 'j') {
+
+				check_option(&replace.flags, OPT_JUMP);
+				for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+					if (!strcmp(optarg,
+					   standard_targets[i])) {
+						t = find_target(
+						   EBT_STANDARD_TARGET);
+						((struct ebt_standard_target *)
+						   t->t)->verdict = i;
+						break;
+					}
+				// must be an extension then
+				if (i == NUM_STANDARD_TARGETS) {
+					struct ebt_u_target *t;
+					t = find_target(optarg);
+					// -j standard not allowed either
+					if (!t || t ==
+					   (struct ebt_u_target *)new_entry->t)
+						print_error("Illegal target "
+						            "name");
+					new_entry->t =
+					   (struct ebt_entry_target *)t;
+				}
+				break;
+			}
+			if (c == 's') {
+				check_option(&replace.flags, OPT_SOURCE);
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_ISOURCE;
+
+				if (optind > argc)
+					print_error("No source mac "
+					            "specified");
+				if (getmac_and_mask(argv[optind - 1],
+				   new_entry->sourcemac, new_entry->sourcemsk))
+					print_error("Problem with specified "
+					            "source mac");
+				new_entry->bitmask |= EBT_SOURCEMAC;
+				break;
+			}
+			if (c == 'd') {
+				check_option(&replace.flags, OPT_DEST);
+				if (check_inverse(optarg))
+					new_entry->invflags |= EBT_IDEST;
+
+				if (optind > argc)
+					print_error("No destination mac "
+					            "specified");
+				if (getmac_and_mask(argv[optind - 1],
+				   new_entry->destmac, new_entry->destmsk))
+					print_error("Problem with specified "
+					            "destination mac");
+				new_entry->bitmask |= EBT_DESTMAC;
+				break;
+			}
+			check_option(&replace.flags, OPT_PROTOCOL);
+			if (check_inverse(optarg))
+				new_entry->invflags |= EBT_IPROTO;
+
+			if (optind > argc)
+				print_error("No protocol specified");
+			new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
+			i = strtol(argv[optind - 1], &buffer, 16);
+			if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
+				print_error("Problem with the specified "
+				            "protocol");
+			new_entry->ethproto = i;
+			if (*buffer != '\0')
+				if (name_to_protocol(argv[optind - 1]) == -1)
+					print_error("Problem with the specified"
+					            " protocol");
+			if (new_entry->ethproto < 1536 &&
+			   !(new_entry->bitmask & EBT_802_3))
+				print_error("Sorry, protocols have values above"
+				            " or equal to 1536 (0x0600)");
+			break;
+
+		case 'b': // allow database?
+			if (replace.flags & OPT_COMMAND)
+				print_error("Multiple commands not allowed");
+			replace.command = c;
+			allowbc = *optarg;
+			break;
+
+		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)))
+				continue;
+
+			// is it a match_option?
+			for (m = 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 (m->used == 0)
+					add_match(m);
+				continue;
+			}
+
+			// is it a watcher option?
+			for (w = watchers; w; w = w->next)
+				if (w->parse(c-w->option_offset, argv,
+				   argc, new_entry, &w->flags, &w->w))
+					break;
+
+			if (w == NULL)
+				print_error("Unknown argument");
+			if (w->used == 0)
+				add_watcher(w);
+		}
+	}
+
+	// database stuff before ebtables stuff
+	if (replace.command == 'b')
+		allowdb(allowbc);
+	if (replace.command == 'L' && replace.selected_hook == DATABASEHOOKNR)
+		list_db();
+
+	if ( (replace.flags & OPT_COMMAND) && replace.command != 'L' &&
+	   replace.flags & OPT_ZERO )
+		print_error("Command -Z only allowed together with command -L");
+
+	if (replace.command == 'A' || replace.command == 'I' ||
+	   replace.command == 'D') {
+		if (replace.selected_hook == -1)
+			print_error("Not enough information");
+	}
+
+	if ( !(table = find_table(replace.name)) )
+		print_error("Bad table name");
+
+	// do this after parsing everything, so we can print specific info
+	if (replace.command == 'h' && !(replace.flags & OPT_ZERO))
+		print_help();
+
+	// do the final checks
+	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,
+		   replace.selected_hook);
+		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,
+		   replace.selected_hook);
+		w_l = w_l->next;
+	}
+	t->final_check(new_entry, t->t, replace.name, replace.selected_hook);
+	
+	// so, the extensions can work with the host endian
+	// the kernel does not have to do this ofcourse
+	new_entry->ethproto = htons(new_entry->ethproto);
+
+	// get the kernel's information
+	get_table(&replace);
+	// check if selected_hook is a valid_hook
+	if (replace.selected_hook >= 0 &&
+	   !(replace.valid_hooks & (1 << replace.selected_hook)))
+		print_error("Bad chain name");
+	if (replace.command == 'P')
+		change_policy(policy);
+	else if (replace.command == 'L') {
+		list_rules();
+		if (replace.flags & OPT_ZERO)
+			zero_counters(zerochain);
+		else
+			exit(0);
+	}
+	if (replace.flags & OPT_ZERO)
+		zero_counters(zerochain);
+	else if (replace.command == 'F')
+		flush_chains();
+	else if (replace.command == 'A' || replace.command == 'I')
+		add_rule(rule_nr);
+	else if (replace.command == 'D')
+		delete_rule(rule_nr);
+
+	if (table->check)
+		table->check(&replace);
+
+	deliver_table(&replace);
+
+	if (counterchanges)
+		deliver_counters(&replace, counterchanges);
+	return 0;
+}
diff --git a/ethertypes b/ethertypes
new file mode 100644
index 0000000..0123bf3
--- /dev/null
+++ b/ethertypes
@@ -0,0 +1,34 @@
+# all whitespace is ignored
+# comment lines must have a '#' as the first character
+# all protocol numbers are in hexadecimal form
+# maximum namesize = 20 characters
+# always put tabs or spaces between the name and the protocol number
+# don't use more than 4 digits for the protocol number
+# programs using this file should not be case sensitive
+# that's all :-))
+IPV4 	0800	put your comments behind, on the same line, after a tab
+X25	0800    or whitespace
+ARP	0806
+802_1Q	8100	802.1Q Virtual LAN tagged frame
+IPX	8137
+IPV6	86DD
+NetBEUI	8191
+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
+RARP	8035	Reverse Addr Res packet
+ATALK	809B	Appletalk DDP
+AARP	80F3	Appletalk AARP
+IPX	8137	IPX over DIX
+PPP_DISC	8863	PPPoE discovery messages
+PPP_SES	8864	PPPoE session messages
+ATMMPOA	884C	MultiProtocol over ATM
+ATMFATE	8884	Frame-based ATM Transport over Ethernet
+
+
diff --git a/extensions/Makefile b/extensions/Makefile
new file mode 100644
index 0000000..109af35
--- /dev/null
+++ b/extensions/Makefile
@@ -0,0 +1,12 @@
+#! /usr/bin/make
+
+EXT_FUNC+=nat arp ip standard log redirect vlan
+EXT_TABLES+=filter nat broute
+EXT_OBJS+=$(foreach T,$(EXT_FUNC), extensions/ebt_$(T).o)
+EXT_OBJS+=$(foreach T,$(EXT_TABLES), extensions/ebtable_$(T).o)
+
+extensions/ebt_%.o: extensions/ebt_%.c include/ebtables_u.h
+	$(CC) $(CFLAGS) -c -o $@ $<
+extensions/ebtable_%.o: extensions/ebtable_%.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
diff --git a/extensions/ebt_arp.c b/extensions/ebt_arp.c
new file mode 100644
index 0000000..0e22b0b
--- /dev/null
+++ b/extensions/ebt_arp.c
@@ -0,0 +1,289 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.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'
+static 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   },
+	{ 0 }
+};
+
+// 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 = 0;
+
+	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"
+" opcode strings: \n");
+	while (strcmp(opcodes[i], "")) {
+		printf("%d = %s\n", i + 1, opcodes[i]);
+		i++;
+	}
+	printf(
+" hardware type string: \n 1 = Ethernet\n"
+" protocol type string: \n 0x0800 = IPv4\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;
+}
+
+// defined in ebt_ip.c
+void parse_ip_address(char *address, __u32 *addr, __u32 *msk);
+
+#define OPT_OPCODE 0x01
+#define OPT_HTYPE  0x02
+#define OPT_PTYPE  0x04
+#define OPT_IP_S   0x08
+#define OPT_IP_D   0x10
+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;
+	int i;
+	char *end;
+	__u32 *addr;
+	__u32 *mask;
+
+	switch (c) {
+	case ARP_OPCODE:
+		check_option(flags, OPT_OPCODE);
+		if (check_inverse(optarg))
+			arpinfo->invflags |= EBT_ARP_OPCODE;
+
+		if (optind > argc)
+			print_error("Missing arp opcode argument");
+		i = strtol(argv[optind - 1], &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			i = 0;
+			while (strcmp(opcodes[i], "")) {
+				if (!strcasecmp(opcodes[i], optarg))
+					break;
+				i++;
+			}
+			if (!strcmp(opcodes[i], ""))
+				print_error("Problem with specified "
+				            "arp opcode");
+		}
+		arpinfo->opcode = htons(i);
+		arpinfo->bitmask |= EBT_ARP_OPCODE;
+		break;
+
+	case ARP_HTYPE:
+		check_option(flags, OPT_HTYPE);
+		if (check_inverse(optarg))
+			arpinfo->invflags |= EBT_ARP_HTYPE;
+
+		if (optind > argc)
+			print_error("Missing arp hardware type argument");
+		i = strtol(argv[optind - 1], &end, 10);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			if (!strcasecmp("Ethernet", argv[optind - 1]))
+				i = 1;
+			else
+				print_error("Problem with specified arp "
+				            "hardware type");
+		}
+		arpinfo->htype = htons(i);
+		arpinfo->bitmask |= EBT_ARP_HTYPE;
+		break;
+
+	case ARP_PTYPE:
+		check_option(flags, OPT_PTYPE);
+		if (check_inverse(optarg))
+			arpinfo->invflags |= EBT_ARP_PTYPE;
+
+		if (optind > argc)
+			print_error("Missing arp protocol type argument");
+		i = strtol(argv[optind - 1], &end, 16);
+		if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
+			if (!strcasecmp("IPv4", argv[optind - 1]))
+				i = 0x0800;
+			else
+				print_error("Problem with specified arp "
+				            "protocol type");
+		}
+		arpinfo->ptype = htons(i);
+		arpinfo->bitmask |= EBT_ARP_PTYPE;
+		break;
+
+	case ARP_IP_S:
+	case ARP_IP_D:
+		if (c == ARP_IP_S) {
+			check_option(flags, OPT_IP_S);
+			addr = &arpinfo->saddr;
+			mask = &arpinfo->smsk;
+			arpinfo->bitmask |= EBT_ARP_SRC_IP;
+		} else {
+			check_option(flags, OPT_IP_D);
+			addr = &arpinfo->daddr;
+			mask = &arpinfo->dmsk;
+			arpinfo->bitmask |= EBT_ARP_DST_IP;
+		}
+		if (check_inverse(optarg)) {
+			if (c == ARP_IP_S)
+				arpinfo->invflags |= EBT_ARP_SRC_IP;
+			else
+				arpinfo->invflags |= EBT_ARP_DST_IP;
+		}
+		if (optind > argc)
+			print_error("Missing ip address argument");
+		parse_ip_address(argv[optind - 1], addr, mask);
+		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 hook)
+{
+	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+	   (entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP))
+		print_error("For (R)ARP filtering the protocol must be "
+		            "specified as ARP or RARP");
+}
+
+// defined in the ebt_ip.c
+char *mask_to_dotted(__u32 mask);
+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) {
+		printf("arp opcode: ");
+		if (arpinfo->invflags & EBT_ARP_OPCODE)
+			printf("! ");
+		printf("%d ", ntohs(arpinfo->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) {
+		printf("arp ptype: ");
+		if (arpinfo->invflags & EBT_ARP_PTYPE)
+			printf("! ");
+		printf("0x%x ", ntohs(arpinfo->ptype));
+	}
+	if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
+		printf("arp src IP ");
+		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, ", mask_to_dotted(arpinfo->smsk));
+	}
+	if (arpinfo->bitmask & EBT_ARP_DST_IP) {
+		printf("arp dst IP ");
+		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, ", mask_to_dotted(arpinfo->dmsk));
+	}
+}
+
+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;
+	}
+	return 1;
+}
+
+static struct ebt_u_match arp_match =
+{
+	EBT_ARP_MATCH,
+	sizeof(struct ebt_arp_info),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_match(&arp_match);
+}
diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c
new file mode 100644
index 0000000..5d62d3a
--- /dev/null
+++ b/extensions/ebt_ip.c
@@ -0,0 +1,318 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.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'
+
+static 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  },
+	{ 0 }
+};
+
+// put the ip string into 4 bytes
+static int undot_ip(char *ip, unsigned char *ip2)
+{
+	char *p, *q, *end;
+	int onebyte, 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;
+	int bits;
+	__u32 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
+void parse_ip_address(char *address, __u32 *addr, __u32 *msk)
+{
+	char *p;
+	int i;
+
+	// first the mask
+	if ((p = strrchr(address, '/')) != NULL) {
+		*p = '\0';
+		i = ip_mask(p + 1, (unsigned char *)msk);
+		if (i)
+			print_error("Problem with the ip mask");
+	}
+	else
+		*msk = 0xFFFFFFFF;
+
+	i = undot_ip(address, (unsigned char *)addr);
+	if (i)
+		print_error("Problem with the ip address");
+	*addr = *addr & *msk;
+}
+
+// transform the ip mask into a string ready for output
+char *mask_to_dotted(__u32 mask)
+{
+	int i;
+	static char buf[20];
+	__u32 maskaddr, bits;
+
+	maskaddr = ntohl(mask);
+
+	// don't print /32
+	if (mask == 0xFFFFFFFFL)
+		return "";
+
+	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;
+}
+
+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");
+}
+
+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
+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, *buffer;
+	int i;
+
+	switch (c) {
+	case IP_SOURCE:
+		check_option(flags, OPT_SOURCE);
+		ipinfo->bitmask |= EBT_IP_SOURCE;
+
+	case IP_DEST:
+		if (c == IP_DEST) {
+			check_option(flags, OPT_DEST);
+			ipinfo->bitmask |= EBT_IP_DEST;
+		}
+		if (check_inverse(optarg)) {
+			if (c == IP_SOURCE)
+				ipinfo->invflags |= EBT_IP_SOURCE;
+			else
+				ipinfo->invflags |= EBT_IP_DEST;
+		}
+
+		if (optind > argc)
+			print_error("Missing ip address argument");
+		if (c == IP_SOURCE)
+			parse_ip_address(argv[optind - 1], &ipinfo->saddr,
+			   &ipinfo->smsk);
+		else
+			parse_ip_address(argv[optind - 1], &ipinfo->daddr,
+			   &ipinfo->dmsk);
+		break;
+
+	case IP_myTOS:
+		check_option(flags, OPT_TOS);
+		if (check_inverse(optarg))
+			ipinfo->invflags |= EBT_IP_TOS;
+
+		if (optind > argc)
+			print_error("Missing ip tos argument");
+		i = strtol(argv[optind - 1], &end, 16);
+		if (i < 0 || i > 255 || *buffer != '\0')
+			print_error("Problem with specified ip tos");
+		ipinfo->tos = i;
+		ipinfo->bitmask |= EBT_IP_TOS;
+		break;
+
+	case IP_PROTO:
+		check_option(flags, OPT_PROTO);
+		if (check_inverse(optarg))
+			ipinfo->invflags |= EBT_IP_PROTO;
+		if (optind > argc)
+			print_error("Missing ip protocol argument");
+		i = strtol(argv[optind - 1], &end, 10);
+		if (i < 0 || i > 255 || *end != '\0')
+			print_error("Problem with specified ip protocol");
+		ipinfo->protocol = 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 hook)
+{
+	if (entry->bitmask & EBT_NOPROTO || entry->bitmask & EBT_802_3 ||
+	   entry->ethproto != ETH_P_IP)
+		print_error("For IP filtering the protocol must be "
+		            "specified as IPv4");
+}
+
+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("source ip: ");
+		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, ", mask_to_dotted(ipinfo->smsk));
+	}
+	if (ipinfo->bitmask & EBT_IP_DEST) {
+		printf("dest ip: ");
+		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, ", 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) {
+		printf("ip proto: ");
+		if (ipinfo->invflags & EBT_IP_DEST)
+			printf("! ");
+		printf("%d, ", ipinfo->protocol);
+	}
+}
+
+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;
+	}
+	return 1;
+}
+
+static struct ebt_u_match ip_match =
+{
+	EBT_IP_MATCH,
+	sizeof(struct ebt_ip_info),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts,
+};
+
+static void _init(void) __attribute((constructor));
+static void _init(void)
+{
+	register_match(&ip_match);
+}
diff --git a/extensions/ebt_log.c b/extensions/ebt_log.c
new file mode 100644
index 0000000..6dff952
--- /dev/null
+++ b/extensions/ebt_log.c
@@ -0,0 +1,197 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.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 },
+	{ NULL, -1 }
+};
+
+static int name_to_loglevel(char* arg)
+{
+	int i = 0, c_val = eight_priority[0].c_val;
+	
+	while (c_val != -1) {
+		if (!strcmp(arg, eight_priority[i].c_name))
+			return c_val;
+		i++;
+		c_val = 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'
+static 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    },
+	{ 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"
+	, 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
+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;
+	int i;
+	char *end;
+
+	switch (c) {
+	case LOG_PREFIX:
+		check_option(flags, OPT_PREFIX);
+		if (strlen(optarg) > sizeof(loginfo->prefix) - 1)
+			print_error("Prefix too long");
+		strcpy(loginfo->prefix, optarg);
+		break;
+
+	case LOG_LEVEL:
+		check_option(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)
+			print_error("Problem with the log-level");
+		break;
+
+	case LOG_IP:
+		check_option(flags, OPT_IP);
+		loginfo->bitmask |= EBT_LOG_IP;
+		break;
+
+	case LOG_ARP:
+		check_option(flags, OPT_ARP);
+		loginfo->bitmask |= EBT_LOG_ARP;
+		break;
+
+	case LOG_LOG:
+		check_option(flags, OPT_LOG);
+		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 hook)
+{
+	return;
+}
+
+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: 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");
+	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(loginfo1->prefix, loginfo2->prefix);
+}
+
+static struct ebt_u_watcher log_watcher =
+{
+	EBT_LOG_WATCHER,
+	sizeof(struct ebt_log_info),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts,
+};
+
+#undef _init
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_watcher(&log_watcher);
+}
diff --git a/extensions/ebt_nat.c b/extensions/ebt_nat.c
new file mode 100644
index 0000000..dbdb5a4
--- /dev/null
+++ b/extensions/ebt_nat.c
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_nat.h>
+
+extern char *standard_targets[NUM_STANDARD_TARGETS];
+
+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'
+static 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 },
+	{ 0 }
+};
+
+static 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 or CONTINUE\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 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
+static int parse_s(int c, char **argv, int argc,
+   const struct ebt_u_entry *entry, unsigned int *flags,
+   struct ebt_entry_target **target)
+{
+	int i;
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_S:
+		check_option(flags, OPT_SNAT);
+		to_source_supplied = 1;
+		if (getmac(optarg, natinfo->mac))
+			print_error("Problem with specified to-source mac");
+		break;
+	case NAT_S_TARGET:
+		check_option(flags, OPT_SNAT_TARGET);
+		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+			if (!strcmp(optarg, standard_targets[i])) {
+				natinfo->target = i;
+				break;
+			}
+		if (i == NUM_STANDARD_TARGETS)
+			print_error("Illegal --snat-target target");
+		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)
+{
+	int i;
+	struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data;
+
+	switch (c) {
+	case NAT_D:
+		check_option(flags, OPT_DNAT);
+		to_dest_supplied = 1;
+		if (getmac(optarg, natinfo->mac))
+			print_error("Problem with specified "
+			            "to-destination mac");
+		break;
+	case NAT_D_TARGET:
+		check_option(flags, OPT_DNAT_TARGET);
+		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+			if (!strcmp(optarg, standard_targets[i])) {
+				natinfo->target = i;
+				break;
+			}
+		if (i == NUM_STANDARD_TARGETS)
+			print_error("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 hook)
+{
+	if (hook != NF_BR_POST_ROUTING || strcmp(name, "nat"))
+		print_error("Wrong chain for snat");
+	if (to_source_supplied == 0)
+		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 hook)
+{
+	if ( ((hook != NF_BR_PRE_ROUTING && hook != NF_BR_LOCAL_OUT) ||
+	   strcmp(name, "nat")) &&
+	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
+		print_error("Wrong chain for dnat");
+	if (to_dest_supplied == 0)
+		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;
+	int i;
+
+	printf("snat - to: ");
+	for (i = 0; i < ETH_ALEN; i++)
+		printf("%02x%s",
+		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
+	printf(" --snat-target %s", standard_targets[natinfo->target]);
+}
+
+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;
+	int i;
+
+	printf("dnat - to: ");
+	for (i = 0; i < ETH_ALEN; i++)
+		printf("%02x%s",
+		   natinfo->mac[i], (i == ETH_ALEN - 1) ? "" : ":");
+	printf(" --dnat-target %s", standard_targets[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 =
+{
+	EBT_SNAT_TARGET,
+	sizeof(struct ebt_nat_info),
+	print_help_s,
+	init_s,
+	parse_s,
+	final_check_s,
+	print_s,
+	compare,
+	opts_s,
+};
+
+static struct ebt_u_target dnat_target =
+{
+	EBT_DNAT_TARGET,
+	sizeof(struct ebt_nat_info),
+	print_help_d,
+	init_d,
+	parse_d,
+	final_check_d,
+	print_d,
+	compare,
+	opts_d,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&snat_target);
+	register_target(&dnat_target);
+}
diff --git a/extensions/ebt_redirect.c b/extensions/ebt_redirect.c
new file mode 100644
index 0000000..3dff790
--- /dev/null
+++ b/extensions/ebt_redirect.c
@@ -0,0 +1,109 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_redirect.h>
+
+extern char *standard_targets[NUM_STANDARD_TARGETS];
+
+#define REDIRECT_TARGET '1'
+static struct option opts[] =
+{
+	{ "redirect-target"    , required_argument, 0, REDIRECT_TARGET },
+	{ 0 }
+};
+
+static void print_help()
+{
+	printf(
+	"redirect option:\n"
+	" --redirect-target target   : ACCEPT, DROP 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)
+{
+	int i;
+	struct ebt_redirect_info *redirectinfo =
+	   (struct ebt_redirect_info *)(*target)->data;
+
+	switch (c) {
+	case REDIRECT_TARGET:
+		check_option(flags, OPT_REDIRECT_TARGET);
+		for (i = 0; i < NUM_STANDARD_TARGETS; i++)
+			if (!strcmp(optarg, standard_targets[i])) {
+				redirectinfo->target = i;
+				break;
+			}
+		if (i == NUM_STANDARD_TARGETS)
+			print_error("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 hook)
+{
+	if ( (hook != NF_BR_PRE_ROUTING || strcmp(name, "nat")) &&
+	   (hook != NF_BR_BROUTING || strcmp(name, "broute")) )
+		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;
+
+	printf("redirect");
+	printf(" --redirect-target %s", standard_targets[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 =
+{
+	EBT_REDIRECT_TARGET,
+	sizeof(struct ebt_redirect_info),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts,
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&redirect_target);
+}
diff --git a/extensions/ebt_standard.c b/extensions/ebt_standard.c
new file mode 100644
index 0000000..983d055
--- /dev/null
+++ b/extensions/ebt_standard.c
@@ -0,0 +1,70 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+
+static struct option opts[] =
+{
+	{0}
+};
+
+static void print_help()
+{
+	printf("Standard targets: DROP, ACCEPT and CONTINUE\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 hook)
+{
+}
+
+static void print(const struct ebt_u_entry *entry,
+   const struct ebt_entry_target *target)
+{
+	__u8 verdict = ((struct ebt_standard_target *)target)->verdict;
+
+	if (verdict == EBT_CONTINUE)
+		printf("Continue ");
+	else if (verdict ==  EBT_ACCEPT)
+		printf("Accept ");
+	else
+		printf("Drop ");
+}
+
+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 =
+{
+	EBT_STANDARD_TARGET,
+	sizeof(struct ebt_standard_target) - sizeof(struct ebt_entry_target),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_target(&standard);
+}
diff --git a/extensions/ebt_vlan.c b/extensions/ebt_vlan.c
new file mode 100644
index 0000000..ad3e6f7
--- /dev/null
+++ b/extensions/ebt_vlan.c
@@ -0,0 +1,231 @@
+/*
+ * Summary: ebt_vlan userspace module
+ * 
+ * Description: 802.1Q Virtual LAN match support module for ebtables project.
+ * Enable to match 802.1Q VLAN tagged frames by VLAN numeric 
+ * identifier (12-bites field) and frame priority (3-bites field)
+ * 
+ * Authors:
+ * Bart De Schuymer <bart.de.schuymer@pandora.be>
+ * Nick Fedchik <nick@fedchik.org.ua>
+ *  
+ *  May, 2002
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <linux/netfilter_bridge/ebtables.h>
+#include <getopt.h>
+#include "../include/ebtables_u.h"
+#include <linux/netfilter_bridge/ebt_vlan.h>
+
+#define VLAN_ID    '1'
+#define VLAN_PRIO  '2'
+
+static struct option opts[] = {
+	{"vlan-id", required_argument, 0, VLAN_ID},
+	{"vlan-prio", required_argument, 0, VLAN_PRIO},
+	{0}
+};
+
+/*
+ * Print out help for ebtables -h vlan 
+ */
+static void print_help ()
+{
+	printf ("802.1Q VLAN options:\n"
+		"--vlan-id [!] id        : VLAN ID 1-4095 (integer)\n"
+		"--vlan-prio [!] prio    : VLAN Priority 0-7 (integer)\n");
+}
+
+/*
+ * Initialization function 
+ */
+static void init (struct ebt_entry_match *match)
+{
+	struct ebt_vlan_info *vlaninfo =
+	    (struct ebt_vlan_info *) match->data;
+	/*
+	 * Just clean initial values 
+	 */
+	vlaninfo->id = 0;
+	vlaninfo->prio = 0;
+	vlaninfo->invflags = 0;
+	vlaninfo->bitmask = 0;
+}
+
+#define OPT_VLAN_ID     0x01
+#define OPT_VLAN_PRIO   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_vlan_info *vlaninfo =
+	    (struct ebt_vlan_info *) (*match)->data;
+	unsigned short i;
+	char *end;
+
+	switch (c) {
+	case VLAN_ID:
+		check_option (flags, OPT_VLAN_ID);
+		/*
+		 * Check If we got inversed arg for VID,
+		 * otherwise unset inversion flag 
+		 */
+		if (check_inverse (optarg))
+			vlaninfo->invflags |= EBT_VLAN_ID;
+		/*
+		 * Check arg value presense 
+		 */
+		if (optind > argc)
+			print_error ("Missing VLAN ID argument\n");
+		/*
+		 * Convert argv to long int,
+		 * set *end to end of argv string, 
+		 * base set 10 for decimal only 
+		 */
+		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
+		/*
+		 * Check arg val range 
+		 */
+		if (i < 1 || i >= 4096 || *end != '\0') {
+			i = 0;
+			print_error
+			    ("Problem with specified VLAN ID range\n");
+		}
+		vlaninfo->id = i;
+		vlaninfo->bitmask|=EBT_VLAN_ID;
+		break;
+
+	case VLAN_PRIO:
+		check_option (flags, OPT_VLAN_PRIO);
+		if (check_inverse (optarg))
+			vlaninfo->invflags |= EBT_VLAN_PRIO;
+		if (optind > argc)
+			print_error
+			    ("Missing VLAN Priority level argument\n");
+		/*
+		 * Convert argv to long int,
+		 * set *end to end of argv string, 
+		 * base set 10 for decimal only 
+		 */
+		(unsigned short) i = strtol (argv[optind - 1], &end, 10);
+		/*
+		 * Check arg val range 
+		 */
+		if (i >= 8 || *end != '\0') {
+			i = 0;
+			print_error
+			    ("Problem with specified VLAN Priority range\n");
+		}
+		vlaninfo->prio = i;
+		vlaninfo->bitmask|=EBT_VLAN_PRIO;
+		break;
+
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Final check 
+ */
+static void
+final_check (const struct ebt_u_entry *entry,
+	     const struct ebt_entry_match *match,
+	     const char *name, unsigned int hook)
+{
+	/*
+	 * Is any proto supplied there? Or specified proto isn't 802.1Q?
+	 */
+	if (entry->bitmask & EBT_NOPROTO || entry->ethproto != ETH_P_8021Q)
+		print_error
+		    ("For matching 802.1Q VLAN the protocol must be specified as 802_1Q\n");
+}
+
+/*
+ * Print line when listing rules by ebtables -L 
+ */
+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;
+
+	/*
+	 * Print VLAN ID if they are specified 
+	 */
+	if (vlaninfo->bitmask & EBT_VLAN_ID) {
+		printf ("vlan id: %s%d, ",
+			vlaninfo->invflags & EBT_VLAN_ID ? "!" : "",
+			vlaninfo->id);
+	}
+	/*
+	 * Print VLAN priority if they are specified 
+	 */
+	if (vlaninfo->bitmask & EBT_VLAN_PRIO) {
+		printf ("vlan prio: %s%d, ",
+			vlaninfo->invflags & EBT_VLAN_PRIO ? "!" : "",
+			vlaninfo->prio);
+	}
+}
+
+
+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;
+	/*
+	 * Compare argc 
+	 */
+	if (vlaninfo1->bitmask != vlaninfo2->bitmask)
+		return 0;
+	/*
+	 * Compare inv flags  
+	 */
+	if (vlaninfo1->invflags != vlaninfo2->invflags)
+		return 0;
+	/*
+	 * Compare VLAN ID if they are present 
+	 */
+	if (vlaninfo1->bitmask & EBT_VLAN_ID) {
+		if (vlaninfo1->id != vlaninfo2->id)
+			return 0;
+	};
+	/*
+	 * Compare VLAN Prio if they are present 
+	 */
+	if (vlaninfo1->bitmask & EBT_VLAN_PRIO) {
+		if (vlaninfo1->prio != vlaninfo2->prio)
+			return 0;
+	};
+	return 1;
+}
+
+static struct ebt_u_match vlan_match = {
+	EBT_VLAN_MATCH,
+	sizeof (struct ebt_vlan_info),
+	print_help,
+	init,
+	parse,
+	final_check,
+	print,
+	compare,
+	opts,
+};
+
+static void _init (void) __attribute__ ((constructor));
+static void _init (void)
+{
+	register_match (&vlan_match);
+}
diff --git a/extensions/ebtable_broute.c b/extensions/ebtable_broute.c
new file mode 100644
index 0000000..2abfcb6
--- /dev/null
+++ b/extensions/ebtable_broute.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include "../include/ebtables_u.h"
+
+
+static void print_help(char **hn)
+{
+	printf("Supported chain for the nat table:\n");
+	printf("%s\n",hn[NF_BR_BROUTING]);
+}
+
+static struct
+ebt_u_table table =
+{
+	"broute",
+	NULL,
+	print_help,
+	NULL
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_table(&table);
+}
diff --git a/extensions/ebtable_filter.c b/extensions/ebtable_filter.c
new file mode 100644
index 0000000..cf26983
--- /dev/null
+++ b/extensions/ebtable_filter.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <sys/socket.h>
+#include <linux/netfilter_bridge/ebtables.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(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 =
+{
+	"filter",
+	NULL,
+	print_help,
+	NULL
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_table(&table);
+}
diff --git a/extensions/ebtable_nat.c b/extensions/ebtable_nat.c
new file mode 100644
index 0000000..4b4ca48
--- /dev/null
+++ b/extensions/ebtable_nat.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <sys/socket.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(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 =
+{
+	"nat",
+	NULL,
+	print_help,
+	NULL
+};
+
+static void _init(void) __attribute__ ((constructor));
+static void _init(void)
+{
+	register_table(&table);
+}
diff --git a/include/ebtables_u.h b/include/ebtables_u.h
new file mode 100644
index 0000000..d31186c
--- /dev/null
+++ b/include/ebtables_u.h
@@ -0,0 +1,206 @@
+/*
+ * $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 <linux/netfilter_bridge/ebtables.h>
+#include <linux/br_db.h>
+
+struct ebt_u_entries
+{
+	__u8 policy;
+	__u32 nentries;
+	struct ebt_u_entry *entries;
+};
+
+
+struct ebt_u_replace
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	unsigned int valid_hooks;
+	// nr of rules in the table
+	unsigned int nentries;
+	struct ebt_u_entries *hook_entry[NF_BR_NUMHOOKS];
+	// how many counters in front of it?
+	unsigned int counter_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;
+	// 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 hook to do our thing on (can be -1 if unspecified)
+	int selected_hook;
+};
+
+struct ebt_u_table
+{
+	char name[EBT_TABLE_MAXNAMELEN];
+	int (*check)(struct ebt_u_replace *repl);
+	void (*help)(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
+{
+	__u32 bitmask;
+	__u32 invflags;
+	__u16 ethproto;
+	__u8 in[IFNAMSIZ];
+	__u8 logical_in[IFNAMSIZ];
+	__u8 out[IFNAMSIZ];
+	__u8 logical_out[IFNAMSIZ];
+	__u8 sourcemac[ETH_ALEN];
+	__u8 sourcemsk[ETH_ALEN];
+	__u8 destmac[ETH_ALEN];
+	__u8 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 *next;
+};
+
+struct ebt_u_match
+{
+	char name[EBT_FUNCTION_MAXNAMELEN];
+	// size of the real match data + sizeof struct ebt_match
+	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 hook);
+	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
+	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 hook);
+	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 hook);
+	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;
+};
+
+void register_table(struct ebt_u_table *);
+void register_match(struct ebt_u_match *);
+void register_watcher(struct ebt_u_watcher *);
+void register_target(struct ebt_u_target *t);
+void get_table(struct ebt_u_replace *repl);
+struct ebt_u_target *find_target(const char *name);
+struct ebt_u_match *find_match(const char *name);
+struct ebt_u_watcher *find_watcher(const char *name);
+void deliver_counters(struct ebt_u_replace *repl,
+   unsigned short * counterchanges);
+void deliver_table(struct ebt_u_replace *repl);
+void get_dbinfo(struct brdb_dbinfo *nr);
+void get_db(int len, struct brdb_dbentry *db);
+void deliver_allowdb(__u16 *decision);
+int getmac(char *from, char *to);
+void check_option(unsigned int *flags, unsigned int mask);
+int check_inverse(const char option[]);
+#define print_bug(format, args...) \
+   {printf("BUG: "format".\n", ##args); exit(-1);}
+#define print_error(format, args...) {printf(format".\n", ##args); exit(-1);}
+#define print_memory() {printf("Ebtables: " __FILE__ " " __FUNCTION__ \
+   " %d :Out of memory.\n", __LINE__); exit(-1);}
+
+
+
+// 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_END 3
+#define CNT_ZERO 4
+
+#endif /* EBTABLES_U_H */