From 1abc55d3114378b4e73ec315eac6b122e55148c4 Mon Sep 17 00:00:00 2001 From: Bart De Schuymer Date: Sat, 1 Jun 2002 19:23:47 +0000 Subject: Initial revision --- COPYING | 339 +++++++++ ChangeLog | 51 ++ INSTALL | 27 + Makefile | 58 ++ THANKS | 9 + communication.c | 454 ++++++++++++ ebtables.8 | 434 ++++++++++++ ebtables.c | 1655 +++++++++++++++++++++++++++++++++++++++++++ ethertypes | 34 + extensions/Makefile | 12 + extensions/ebt_arp.c | 289 ++++++++ extensions/ebt_ip.c | 318 +++++++++ extensions/ebt_log.c | 197 +++++ extensions/ebt_nat.c | 222 ++++++ extensions/ebt_redirect.c | 109 +++ extensions/ebt_standard.c | 70 ++ extensions/ebt_vlan.c | 231 ++++++ extensions/ebtable_broute.c | 25 + extensions/ebtable_filter.c | 32 + extensions/ebtable_nat.c | 32 + include/ebtables_u.h | 206 ++++++ 21 files changed, 4804 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 THANKS create mode 100644 communication.c create mode 100644 ebtables.8 create mode 100644 ebtables.c create mode 100644 ethertypes create mode 100644 extensions/Makefile create mode 100644 extensions/ebt_arp.c create mode 100644 extensions/ebt_ip.c create mode 100644 extensions/ebt_log.c create mode 100644 extensions/ebt_nat.c create mode 100644 extensions/ebt_redirect.c create mode 100644 extensions/ebt_standard.c create mode 100644 extensions/ebt_vlan.c create mode 100644 extensions/ebtable_broute.c create mode 100644 extensions/ebtable_filter.c create mode 100644 extensions/ebtable_nat.c create mode 100644 include/ebtables_u.h 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. + + + Copyright (C) 19yy + + 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. + + , 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=<> 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=<> as + option on the command line. + The Makefile will append /man8/ebtables.8. +- ethertypes is placed in /etc/ +- the userspace program ebtables is compiled. + +That's all diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c16801e --- /dev/null +++ b/Makefile @@ -0,0 +1,58 @@ +# ebtables Makefile + +KERNEL_DIR?=/usr/src/linux +PROGNAME:=ebtables +PROGVERSION:="2.0pre6 (May 2002)" + +MANDIR?=/usr/local/man +CFLAGS:=-Wall -Wunused +include extensions/Makefile + +# Some kernel testers prefer to use a symlink for /usr/include/linux +ifeq ($(SYMLINK), y) +KERNEL_INCLUDES=symlink +else +KERNEL_INCLUDES=headers +endif + +all: ebtables + +.PHONY: headers +headers: + mkdir -p /usr/include/linux/netfilter_bridge + cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge/* \ + /usr/include/linux/netfilter_bridge/ + cp -f $(KERNEL_DIR)/include/linux/br_db.h \ + /usr/include/linux/br_db.h + cp -f $(KERNEL_DIR)/include/linux/netfilter_bridge.h \ + /usr/include/linux/netfilter_bridge.h + +.PHONY: symlink +symlink: + ln -fs $(KERNEL_DIR)/include/linux /usr/include/linux + +communication.o: communication.c include/ebtables_u.h + $(CC) $(CFLAGS) -c -o $@ $< + +ebtables.o: ebtables.c include/ebtables_u.h + $(CC) $(CFLAGS) -DPROGVERSION=\"$(PROGVERSION)\" \ + -DPROGNAME=\"$(PROGNAME)\" -c -o $@ $< + +ebtables: ebtables.o communication.o $(EXT_OBJS) + $(CC) $(CFLAGS) -o $@ $^ + +$(MANDIR)/man8/ebtables.8: ebtables.8 + mkdir -p $(@D) + install -m 0644 -o root -g root $< $@ + +/etc/ethertypes: ethertypes + mkdir -p $(@D) + install -m 0644 -o root -g root $< $@ + +install: $(MANDIR)/man8/ebtables.8 $(KERNEL_INCLUDES) \ + ebtables /etc/ethertypes + +clean: + -rm -f ebtables + rm -f *.o *.c~ + rm -f extensions/*.o extensions/*.c~ diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..830ad5d --- /dev/null +++ b/THANKS @@ -0,0 +1,9 @@ +Thanks go out to: + +Lennert Buytenhek +Rusty Russel +Harald Welte +Jason Lunz +Tim Gardner +Loïc Minier +Nick Fedchik diff --git a/communication.c b/communication.c new file mode 100644 index 0000000..02aff3f --- /dev/null +++ b/communication.c @@ -0,0 +1,454 @@ +/* + * communication.c, v2.0 April 2002 + * + * Author: Bart De Schuymer + * + */ + +// All the userspace/kernel communication is in this file. +// The other code should not have to know anything about the way the +// kernel likes the structure of the table data. +// The other code works with linked lists, lots of linked lists. +// So, the translation is done here. + +#include +#include +#include +#include +#include +#include +#include +#include // the database +#include // IPPROTO_IP +#include +#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 +.\" 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 +#include +#include +#include +#include +#include +#include +#include +#include // the database +#include +#include +#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 -L ) + 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 +#include +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +#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 +#include +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +#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 +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +// 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 +#include +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +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 +#include +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +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 +#include +#include +#include +#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 + * Nick Fedchik + * + * May, 2002 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +#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 +#include +#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 +#include +#include +#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 +#include +#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 +#include + +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 */ -- cgit v1.2.3