summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
committerBart De Schuymer <bdschuym@pandora.be>2002-06-01 19:23:47 +0000
commit1abc55d3114378b4e73ec315eac6b122e55148c4 (patch)
tree2b4e1622873f7e6f8517b7862ec2b51a43a62d1a
Initial revision
-rw-r--r--COPYING339
-rw-r--r--ChangeLog51
-rw-r--r--INSTALL27
-rw-r--r--Makefile58
-rw-r--r--THANKS9
-rw-r--r--communication.c454
-rw-r--r--ebtables.8434
-rw-r--r--ebtables.c1655
-rw-r--r--ethertypes34
-rw-r--r--extensions/Makefile12
-rw-r--r--extensions/ebt_arp.c289
-rw-r--r--extensions/ebt_ip.c318
-rw-r--r--extensions/ebt_log.c197
-rw-r--r--extensions/ebt_nat.c222
-rw-r--r--extensions/ebt_redirect.c109
-rw-r--r--extensions/ebt_standard.c70
-rw-r--r--extensions/ebt_vlan.c231
-rw-r--r--extensions/ebtable_broute.c25
-rw-r--r--extensions/ebtable_filter.c32
-rw-r--r--extensions/ebtable_nat.c32
-rw-r--r--include/ebtables_u.h206
21 files changed, 4804 insertions, 0 deletions
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
+Loc 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 */