From 73d9ff871b9046018e251aa5060d9d8c15ab23a7 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Mon, 9 Apr 2018 13:35:28 +0200 Subject: [PATCH] ebtables-tiny: introduce stripped-down ebtables variant --- net/ebtables-tiny/Makefile | 25 + net/ebtables-tiny/patches/100-musl_fix.patch | 10 + net/ebtables-tiny/src/COPYING | 342 +++++++ net/ebtables-tiny/src/Makefile | 32 + net/ebtables-tiny/src/THANKS | 9 + net/ebtables-tiny/src/communication.c | 563 +++++++++++ net/ebtables-tiny/src/ebtables-standalone.c | 13 + net/ebtables-tiny/src/ebtables.c | 829 ++++++++++++++++ net/ebtables-tiny/src/extensions/ebt_arp.c | 367 +++++++ net/ebtables-tiny/src/extensions/ebt_ip.c | 474 +++++++++ net/ebtables-tiny/src/extensions/ebt_ip6.c | 415 ++++++++ net/ebtables-tiny/src/extensions/ebt_limit.c | 218 +++++ net/ebtables-tiny/src/extensions/ebt_mark.c | 178 ++++ net/ebtables-tiny/src/extensions/ebt_mark_m.c | 127 +++ .../src/extensions/ebt_standard.c | 90 ++ .../src/extensions/ebtable_broute.c | 29 + .../src/extensions/ebtable_filter.c | 35 + .../src/extensions/ebtable_nat.c | 36 + net/ebtables-tiny/src/getethertype.c | 92 ++ net/ebtables-tiny/src/include/ebtables.h | 255 +++++ net/ebtables-tiny/src/include/ebtables_u.h | 329 +++++++ net/ebtables-tiny/src/include/ethernetdb.h | 40 + .../src/include/linux/netfilter_bridge.h | 27 + .../include/linux/netfilter_bridge/ebt_arp.h | 36 + .../include/linux/netfilter_bridge/ebt_ip.h | 54 ++ .../include/linux/netfilter_bridge/ebt_ip6.h | 50 + .../linux/netfilter_bridge/ebt_limit.h | 24 + .../linux/netfilter_bridge/ebt_mark_m.h | 16 + .../linux/netfilter_bridge/ebt_mark_t.h | 23 + .../include/linux/netfilter_bridge/ebtables.h | 268 +++++ net/ebtables-tiny/src/include/linux/types.h | 51 + net/ebtables-tiny/src/libebtc.c | 913 ++++++++++++++++++ net/ebtables-tiny/src/useful_functions.c | 564 +++++++++++ 33 files changed, 6534 insertions(+) create mode 100644 net/ebtables-tiny/Makefile create mode 100644 net/ebtables-tiny/patches/100-musl_fix.patch create mode 100644 net/ebtables-tiny/src/COPYING create mode 100644 net/ebtables-tiny/src/Makefile create mode 100644 net/ebtables-tiny/src/THANKS create mode 100644 net/ebtables-tiny/src/communication.c create mode 100644 net/ebtables-tiny/src/ebtables-standalone.c create mode 100644 net/ebtables-tiny/src/ebtables.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_arp.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_ip.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_ip6.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_limit.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_mark.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_mark_m.c create mode 100644 net/ebtables-tiny/src/extensions/ebt_standard.c create mode 100644 net/ebtables-tiny/src/extensions/ebtable_broute.c create mode 100644 net/ebtables-tiny/src/extensions/ebtable_filter.c create mode 100644 net/ebtables-tiny/src/extensions/ebtable_nat.c create mode 100644 net/ebtables-tiny/src/getethertype.c create mode 100644 net/ebtables-tiny/src/include/ebtables.h create mode 100644 net/ebtables-tiny/src/include/ebtables_u.h create mode 100644 net/ebtables-tiny/src/include/ethernetdb.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_arp.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_ip.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_ip6.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_limit.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_mark_m.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebt_mark_t.h create mode 100644 net/ebtables-tiny/src/include/linux/netfilter_bridge/ebtables.h create mode 100644 net/ebtables-tiny/src/include/linux/types.h create mode 100644 net/ebtables-tiny/src/libebtc.c create mode 100644 net/ebtables-tiny/src/useful_functions.c diff --git a/net/ebtables-tiny/Makefile b/net/ebtables-tiny/Makefile new file mode 100644 index 0000000..7073901 --- /dev/null +++ b/net/ebtables-tiny/Makefile @@ -0,0 +1,25 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ebtables-tiny +PKG_RELEASE:=1 + +PKG_LICENSE:=GPL-2.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/ebtables-tiny + SECTION:=net + CATEGORY:=Network + SUBMENU:=Firewall + CONFLICTS:=ebtables + TITLE:=Ethernet bridge firewall administration utility (tiny) +endef + +TARGET_CFLAGS += -fvisibility=hidden + +define Package/ebtables-tiny/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/ebtables $(1)/usr/sbin/ebtables-tiny +endef + +$(eval $(call BuildPackage,ebtables-tiny)) diff --git a/net/ebtables-tiny/patches/100-musl_fix.patch b/net/ebtables-tiny/patches/100-musl_fix.patch new file mode 100644 index 0000000..3fe5845 --- /dev/null +++ b/net/ebtables-tiny/patches/100-musl_fix.patch @@ -0,0 +1,10 @@ +--- a/include/ebtables_u.h ++++ b/include/ebtables_u.h +@@ -23,6 +23,7 @@ + + #ifndef EBTABLES_U_H + #define EBTABLES_U_H ++#define _NETINET_IF_ETHER_H + #include + #include + #include diff --git a/net/ebtables-tiny/src/COPYING b/net/ebtables-tiny/src/COPYING new file mode 100644 index 0000000..514754e --- /dev/null +++ b/net/ebtables-tiny/src/COPYING @@ -0,0 +1,342 @@ +All code in this package, including the code from the extensions, +is released under the GPL license, which you find hereafter. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + 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/net/ebtables-tiny/src/Makefile b/net/ebtables-tiny/src/Makefile new file mode 100644 index 0000000..c55895d --- /dev/null +++ b/net/ebtables-tiny/src/Makefile @@ -0,0 +1,32 @@ +LOCKFILE?=/var/lib/ebtables/lock +LOCKDIR:=$(shell echo $(LOCKFILE) | sed 's/\(.*\)\/.*/\1/')/ + + +CFLAGS += -Iinclude +CFLAGS += -Wall + +EXT_TABLES:=filter nat broute +EXT_FUNC:=arp ip6 ip limit mark mark_m standard +EXT_OBJS:=$(foreach T,$(EXT_FUNC),extensions/ebt_$(T).o) $(foreach T,$(EXT_TABLES),extensions/ebtable_$(T).o) + +OBJECTS2:=getethertype.o communication.o libebtc.o useful_functions.o ebtables.o + +OBJECTS:=$(OBJECTS2) $(EXT_OBJS) + +PROGSPECS:= \ + -DLOCKFILE=\"$(LOCKFILE)\" \ + -DLOCKDIR=\"$(LOCKDIR)\" + +all: ebtables + +%.o: %.c include/ebtables_u.h include/ethernetdb.h + $(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $< + +ebtables: ebtables-standalone.o $(OBJECTS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ + +.PHONY: clean +clean: + rm -f ebtables + rm -f *.o + rm -f extensions/*.o diff --git a/net/ebtables-tiny/src/THANKS b/net/ebtables-tiny/src/THANKS new file mode 100644 index 0000000..4b92ead --- /dev/null +++ b/net/ebtables-tiny/src/THANKS @@ -0,0 +1,9 @@ +Special thanks go out to these early contributors: + +Lennert Buytenhek +Rusty Russel +Harald Welte +Jason Lunz +Tim Gardner +Loïc Minier +Nick Fedchik diff --git a/net/ebtables-tiny/src/communication.c b/net/ebtables-tiny/src/communication.c new file mode 100644 index 0000000..3a75b42 --- /dev/null +++ b/net/ebtables-tiny/src/communication.c @@ -0,0 +1,563 @@ +/* + * communication.c, v2.0 July 2002 + * + * Author: Bart De Schuymer + * + */ + +/* + * All the userspace/kernel communication is in this file. + * The other code should not have to know anything about the way the + * kernel likes the structure of the table data. + * The other code works with linked lists. So, the translation is done here. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/ebtables_u.h" + +extern char* hooknames[NF_BR_NUMHOOKS]; + +int sockfd = -1; + +static int get_sockfd() +{ + int ret = 0; + if (sockfd == -1) { + sockfd = socket(AF_INET, SOCK_RAW, PF_INET); + if (sockfd < 0) { + ebt_print_error("Problem getting a socket, " + "you probably don't have the right " + "permissions"); + ret = -1; + } + } + return ret; +} + +static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl) +{ + struct ebt_replace *new; + struct ebt_u_entry *e; + struct ebt_u_match_list *m_l; + struct ebt_u_entries *entries; + char *p, *base; + int i, j; + unsigned int entries_size = 0, *chain_offsets; + + new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace)); + if (!new) + ebt_print_memory(); + new->valid_hooks = u_repl->valid_hooks; + strcpy(new->name, u_repl->name); + new->nentries = u_repl->nentries; + new->num_counters = u_repl->num_counters; + new->counters = u_repl->counters; + chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int)); + if (!chain_offsets) + ebt_print_memory(); + /* Determine size */ + for (i = 0; i < u_repl->num_chains; i++) { + if (!(entries = u_repl->chains[i])) + continue; + chain_offsets[i] = entries_size; + entries_size += sizeof(struct ebt_entries); + j = 0; + e = entries->entries->next; + while (e != entries->entries) { + j++; + entries_size += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + entries_size += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + entries_size += e->t->target_size + + sizeof(struct ebt_entry_target); + e = e->next; + } + /* A little sanity check */ + if (j != entries->nentries) + ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j, + entries->nentries, entries->name); + } + + new->entries_size = entries_size; + p = (char *)malloc(entries_size); + if (!p) + ebt_print_memory(); + + /* Put everything in one block */ + new->entries = p; + for (i = 0; i < u_repl->num_chains; i++) { + struct ebt_entries *hlp; + + hlp = (struct ebt_entries *)p; + if (!(entries = u_repl->chains[i])) + continue; + if (i < NF_BR_NUMHOOKS) + new->hook_entry[i] = hlp; + hlp->nentries = entries->nentries; + hlp->policy = entries->policy; + strcpy(hlp->name, entries->name); + hlp->counter_offset = entries->counter_offset; + hlp->distinguisher = 0; /* Make the kernel see the light */ + p += sizeof(struct ebt_entries); + e = entries->entries->next; + while (e != entries->entries) { + struct ebt_entry *tmp = (struct ebt_entry *)p; + + tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES; + tmp->invflags = e->invflags; + tmp->ethproto = e->ethproto; + strcpy(tmp->in, e->in); + strcpy(tmp->out, e->out); + strcpy(tmp->logical_in, e->logical_in); + strcpy(tmp->logical_out, e->logical_out); + memcpy(tmp->sourcemac, e->sourcemac, + sizeof(tmp->sourcemac)); + memcpy(tmp->sourcemsk, e->sourcemsk, + sizeof(tmp->sourcemsk)); + memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac)); + memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk)); + + base = p; + p += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + memcpy(p, m_l->m, m_l->m->match_size + + sizeof(struct ebt_entry_match)); + p += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + tmp->watchers_offset = p - base; + tmp->target_offset = p - base; + memcpy(p, e->t, e->t->target_size + + sizeof(struct ebt_entry_target)); + if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { + struct ebt_standard_target *st = + (struct ebt_standard_target *)p; + /* Translate the jump to a udc */ + if (st->verdict >= 0) + st->verdict = chain_offsets + [st->verdict + NF_BR_NUMHOOKS]; + } + p += e->t->target_size + + sizeof(struct ebt_entry_target); + tmp->next_offset = p - base; + e = e->next; + } + } + + /* Sanity check */ + if (p - (char *)new->entries != new->entries_size) + ebt_print_bug("Entries_size bug"); + free(chain_offsets); + return new; +} + +void ebt_deliver_table(struct ebt_u_replace *u_repl) +{ + socklen_t optlen; + struct ebt_replace *repl; + + /* Translate the struct ebt_u_replace to a struct ebt_replace */ + repl = translate_user2kernel(u_repl); + /* Give the data to the kernel */ + optlen = sizeof(struct ebt_replace) + repl->entries_size; + if (get_sockfd()) + goto free_repl; + if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) + goto free_repl; + if (u_repl->command == 8) + goto free_repl; + + ebt_print_error("Unable to update the kernel. Two possible causes:\n" + "1. Multiple ebtables programs were executing simultaneously. The ebtables\n" + " userspace tool doesn't by default support multiple ebtables programs running\n" + " concurrently. The ebtables option --concurrent or a tool like flock can be\n" + " used to support concurrent scripts that update the ebtables kernel tables.\n" + "2. The kernel doesn't support a certain ebtables extension, consider\n" + " recompiling your kernel or insmod the extension.\n"); +free_repl: + if (repl) { + free(repl->entries); + free(repl); + } +} + +/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel + * and resets the counterchanges to CNT_NORM */ +void ebt_deliver_counters(struct ebt_u_replace *u_repl) +{ + struct ebt_counter *old, *new, *newcounters; + socklen_t optlen; + struct ebt_replace repl; + struct ebt_cntchanges *cc = u_repl->cc->next, *cc2; + struct ebt_u_entries *entries = NULL; + struct ebt_u_entry *next = NULL; + int i, chainnr = -1; + + if (u_repl->nentries == 0) + return; + + newcounters = (struct ebt_counter *) + malloc(u_repl->nentries * sizeof(struct ebt_counter)); + if (!newcounters) + ebt_print_memory(); + memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter)); + old = u_repl->counters; + new = newcounters; + while (cc != u_repl->cc) { + if (!next || next == entries->entries) { + chainnr++; + while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) || + (next = entries->entries->next) == entries->entries)) + chainnr++; + if (chainnr == u_repl->num_chains) + break; + } + if (next == NULL) + ebt_print_bug("next == NULL"); + if (cc->type == CNT_NORM) { + /* 'Normal' rule, meaning we didn't do anything to it + * So, we just copy */ + *new = *old; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + old++; /* We've used an old counter */ + new++; /* We've set a new counter */ + next = next->next; + } else if (cc->type == CNT_DEL) { + old++; /* Don't use this old counter */ + } else { + if (cc->type == CNT_CHANGE) { + if (cc->change % 3 == 1) + new->pcnt = old->pcnt + next->cnt_surplus.pcnt; + else if (cc->change % 3 == 2) + new->pcnt = old->pcnt - next->cnt_surplus.pcnt; + else + new->pcnt = next->cnt.pcnt; + if (cc->change / 3 == 1) + new->bcnt = old->bcnt + next->cnt_surplus.bcnt; + else if (cc->change / 3 == 2) + new->bcnt = old->bcnt - next->cnt_surplus.bcnt; + else + new->bcnt = next->cnt.bcnt; + } else + *new = next->cnt; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + if (cc->type == CNT_ADD) + new++; + else { + old++; + new++; + } + next = next->next; + } + cc = cc->next; + } + + free(u_repl->counters); + u_repl->counters = newcounters; + u_repl->num_counters = u_repl->nentries; + /* Reset the counterchanges to CNT_NORM and delete the unused cc */ + i = 0; + cc = u_repl->cc->next; + while (cc != u_repl->cc) { + if (cc->type == CNT_DEL) { + cc->prev->next = cc->next; + cc->next->prev = cc->prev; + cc2 = cc->next; + free(cc); + cc = cc2; + } else { + cc->type = CNT_NORM; + cc->change = 0; + i++; + cc = cc->next; + } + } + if (i != u_repl->nentries) + ebt_print_bug("i != u_repl->nentries"); + 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)); + + if (get_sockfd()) + return; + if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen)) + ebt_print_bug("Couldn't update kernel counters"); +} + +static int +ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l) +{ + struct ebt_u_match_list *new; + int ret = 0; + + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + ebt_print_memory(); + new->m = (struct ebt_entry_match *) + malloc(m->match_size + sizeof(struct ebt_entry_match)); + if (!new->m) + ebt_print_memory(); + memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match)); + new->next = NULL; + **l = new; + *l = &new->next; + if (ebt_find_match(new->m->u.name) == NULL) { + ebt_print_error("Kernel match %s unsupported by userspace tool", + new->m->u.name); + ret = -1; + } + return ret; +} + +static int +ebt_translate_watcher(struct ebt_entry_watcher *w) +{ + ebt_print_error("Kernel watcher %s unsupported by userspace " + "tool", w->u.name); + return -1; +} + +static int +ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt, + int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl, + unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc) +{ + /* An entry */ + if (e->bitmask & EBT_ENTRY_OR_ENTRIES) { + struct ebt_u_entry *new; + struct ebt_u_match_list **m_l; + struct ebt_entry_target *t; + + new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new) + ebt_print_memory(); + new->bitmask = e->bitmask; + /* + * Plain userspace code doesn't know about + * EBT_ENTRY_OR_ENTRIES + */ + new->bitmask &= ~EBT_ENTRY_OR_ENTRIES; + new->invflags = e->invflags; + new->ethproto = e->ethproto; + strcpy(new->in, e->in); + strcpy(new->out, e->out); + strcpy(new->logical_in, e->logical_in); + strcpy(new->logical_out, e->logical_out); + memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac)); + memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk)); + memcpy(new->destmac, e->destmac, sizeof(new->destmac)); + memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk)); + if (*totalcnt >= u_repl->nentries) + ebt_print_bug("*totalcnt >= u_repl->nentries"); + new->cnt = u_repl->counters[*totalcnt]; + new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0; + new->cc = *cc; + *cc = (*cc)->next; + new->m_list = NULL; + new->next = (*u_e)->next; + new->next->prev = new; + (*u_e)->next = new; + new->prev = *u_e; + *u_e = new; + m_l = &new->m_list; + EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l); + EBT_WATCHER_ITERATE(e, ebt_translate_watcher); + + t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); + new->t = (struct ebt_entry_target *) + malloc(t->target_size + sizeof(struct ebt_entry_target)); + if (!new->t) + ebt_print_memory(); + if (ebt_find_target(t->u.name) == NULL) { + ebt_print_error("Kernel target %s unsupported by " + "userspace tool", t->u.name); + return -1; + } + memcpy(new->t, t, t->target_size + + sizeof(struct ebt_entry_target)); + /* Deal with jumps to udc */ + if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) { + char *tmp = base; + int verdict = ((struct ebt_standard_target *)t)->verdict; + int i; + + if (verdict >= 0) { + tmp += verdict; + for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++) + if (u_repl->chains[i]->kernel_start == tmp) + break; + if (i == u_repl->num_chains) + ebt_print_bug("Can't find udc for jump"); + ((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS; + } + } + + (*cnt)++; + (*totalcnt)++; + return 0; + } else { /* A new chain */ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + + if (*n != *cnt) + ebt_print_bug("Nr of entries in the chain is wrong"); + *n = entries->nentries; + *cnt = 0; + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + *hook = i; + *u_e = u_repl->chains[*hook]->entries; + return 0; + } +} + +/* Initialize all chain headers */ +static int +ebt_translate_chains(struct ebt_entry *e, int *hook, + struct ebt_u_replace *u_repl, unsigned int valid_hooks) +{ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + struct ebt_u_entries *new; + + if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) { + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); + if (!new) + ebt_print_memory(); + if (i == u_repl->max_chains) + ebt_double_chains(u_repl); + u_repl->chains[i] = new; + if (i >= NF_BR_NUMHOOKS) + new->kernel_start = (char *)e; + *hook = i; + new->nentries = entries->nentries; + new->policy = entries->policy; + new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new->entries) + ebt_print_memory(); + new->entries->next = new->entries->prev = new->entries; + new->counter_offset = entries->counter_offset; + strcpy(new->name, entries->name); + } + return 0; +} + +static int retrieve_from_kernel(struct ebt_replace *repl, char command) +{ + socklen_t optlen; + int optname; + char *entries; + + optlen = sizeof(struct ebt_replace); + if (get_sockfd()) + return -1; + optname = EBT_SO_GET_INFO; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + return -1; + + if ( !(entries = (char *)malloc(repl->entries_size)) ) + ebt_print_memory(); + repl->entries = entries; + if (repl->nentries) { + struct ebt_counter *counters; + + if (!(counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter))) ) + ebt_print_memory(); + repl->counters = counters; + } + 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); + optname = EBT_SO_GET_ENTRIES; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + ebt_print_bug("Hmm, what is wrong??? bug#1"); + + return 0; +} + +int ebt_get_table(struct ebt_u_replace *u_repl) +{ + int i, j, k, hook; + struct ebt_replace repl; + struct ebt_u_entry *u_e = NULL; + struct ebt_cntchanges *new_cc = NULL, *cc; + + strcpy(repl.name, u_repl->name); + if (retrieve_from_kernel(&repl, u_repl->command)) + return -1; + + /* Translate the struct ebt_replace to a struct ebt_u_replace */ + u_repl->valid_hooks = repl.valid_hooks; + u_repl->nentries = repl.nentries; + u_repl->num_counters = repl.num_counters; + u_repl->counters = repl.counters; + u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!u_repl->cc) + ebt_print_memory(); + u_repl->cc->next = u_repl->cc->prev = u_repl->cc; + cc = u_repl->cc; + for (i = 0; i < repl.nentries; i++) { + new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!new_cc) + ebt_print_memory(); + new_cc->type = CNT_NORM; + new_cc->change = 0; + new_cc->prev = cc; + cc->next = new_cc; + cc = new_cc; + } + if (repl.nentries) { + new_cc->next = u_repl->cc; + u_repl->cc->prev = new_cc; + } + u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *)); + u_repl->max_chains = EBT_ORI_MAX_CHAINS; + hook = -1; + /* FIXME: Clean up when an error is encountered */ + EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains, + &hook, u_repl, u_repl->valid_hooks); + if (hook >= NF_BR_NUMHOOKS) + u_repl->num_chains = hook + 1; + else + u_repl->num_chains = NF_BR_NUMHOOKS; + i = 0; /* Holds the expected nr. of entries for the chain */ + j = 0; /* Holds the up to now counted entries for the chain */ + k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */ + cc = u_repl->cc->next; + hook = -1; + EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size, + ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl, + u_repl->valid_hooks, (char *)repl.entries, &cc); + if (k != u_repl->nentries) + ebt_print_bug("Wrong total nentries"); + free(repl.entries); + return 0; +} diff --git a/net/ebtables-tiny/src/ebtables-standalone.c b/net/ebtables-tiny/src/ebtables-standalone.c new file mode 100644 index 0000000..f9f62d2 --- /dev/null +++ b/net/ebtables-tiny/src/ebtables-standalone.c @@ -0,0 +1,13 @@ +#include +#include "include/ebtables_u.h" + +static struct ebt_u_replace replace; +void ebt_early_init_once(); + +int main(int argc, char *argv[]) +{ + ebt_early_init_once(); + strcpy(replace.name, "filter"); + do_command(argc, argv, &replace); + return 0; +} diff --git a/net/ebtables-tiny/src/ebtables.c b/net/ebtables-tiny/src/ebtables.c new file mode 100644 index 0000000..44e0eda --- /dev/null +++ b/net/ebtables-tiny/src/ebtables.c @@ -0,0 +1,829 @@ +/* + * ebtables.c, v2.0 July 2002 + * + * Author: Bart De Schuymer + * + * This code was stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" + +/* Checks whether a command has already been specified */ +#define OPT_COMMANDS (replace->flags & OPT_COMMAND) + +#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_LOGICALIN 0x200 +#define OPT_LOGICALOUT 0x400 + +/* Default command line options. Do not mess around with the already + * assigned numbers unless you know what you are doing */ +static struct option ebt_original_options[] = +{ + { "append" , required_argument, 0, 'A' }, + { "insert" , required_argument, 0, 'I' }, + { "delete" , required_argument, 0, 'D' }, + { "list" , optional_argument, 0, 'L' }, + { "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' }, + { "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' }, + { "new-chain" , required_argument, 0, 'N' }, + { "rename-chain" , required_argument, 0, 'E' }, + { "delete-chain" , optional_argument, 0, 'X' }, + { 0 } +}; + +static struct option *ebt_options = ebt_original_options; + +/* Holds all the data */ +static struct ebt_u_replace *replace; + +/* The chosen table */ +static struct ebt_u_table *table; + +/* The pointers in here are special: + * The struct ebt_target pointer is actually a struct ebt_u_target pointer. + * I do not feel like using a union. + * We need a struct ebt_u_target pointer because we know the address of the data + * they point to won't change. We want to allow that the struct ebt_u_target.t + * member can change. + * The same holds for the struct ebt_match and struct ebt_watcher pointers */ +static struct ebt_u_entry *new_entry; + + +static int global_option_offset; +#define OPTION_OFFSET 256 +static struct option *merge_options(struct option *oldopts, + const struct option *newopts, unsigned int *options_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + if (!newopts || !oldopts || !options_offset) + ebt_print_bug("merge wrong"); + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *options_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + if (!merge) + ebt_print_memory(); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *options_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + /* Only free dynamically allocated stuff */ + if (oldopts != ebt_original_options) + free(oldopts); + + return merge; +} + +static void merge_match(struct ebt_u_match *m) +{ + ebt_options = merge_options + (ebt_options, m->extra_ops, &(m->option_offset)); +} + +static void merge_target(struct ebt_u_target *t) +{ + ebt_options = merge_options + (ebt_options, t->extra_ops, &(t->option_offset)); +} + +/* Be backwards compatible, so don't use '+' in kernel */ +#define IF_WILDCARD 1 +static void print_iface(const char *iface) +{ + char *c; + + if ((c = strchr(iface, IF_WILDCARD))) + *c = '+'; + printf("%s ", iface); + if (c) + *c = IF_WILDCARD; +} + +/* Helper function for list_rules() */ +static void list_em(struct ebt_u_entries *entries) +{ + int i; + struct ebt_u_entry *hlp; + struct ebt_u_match_list *m_l; + struct ebt_u_match *m; + struct ebt_u_target *t; + + hlp = entries->entries->next; + printf("\nBridge chain: %s, entries: %d, policy: %s\n", + entries->name, entries->nentries, + ebt_standard_targets[-entries->policy - 1]); + + for (i = 0; i < entries->nentries; i++) { + /* The standard target's print() uses this to find out + * the name of a udc */ + hlp->replace = replace; + + /* Don't print anything about the protocol if no protocol was + * specified, obviously this means any protocol will do. */ + if (!(hlp->bitmask & EBT_NOPROTO)) { + printf("-p "); + if (hlp->invflags & EBT_IPROTO) + printf("! "); + if (hlp->bitmask & EBT_802_3) + printf("Length "); + else { + const struct ethertypeent *ent; + + ent = getethertypebynumber(ntohs(hlp->ethproto)); + if (!ent) + printf("0x%x ", ntohs(hlp->ethproto)); + else + printf("%s ", ent->e_name); + } + } + if (hlp->bitmask & EBT_SOURCEMAC) { + printf("-s "); + if (hlp->invflags & EBT_ISOURCE) + printf("! "); + ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk); + printf(" "); + } + if (hlp->bitmask & EBT_DESTMAC) { + printf("-d "); + if (hlp->invflags & EBT_IDEST) + printf("! "); + ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk); + printf(" "); + } + if (hlp->in[0] != '\0') { + printf("-i "); + if (hlp->invflags & EBT_IIN) + printf("! "); + print_iface(hlp->in); + } + if (hlp->logical_in[0] != '\0') { + printf("--logical-in "); + if (hlp->invflags & EBT_ILOGICALIN) + printf("! "); + print_iface(hlp->logical_in); + } + if (hlp->logical_out[0] != '\0') { + printf("--logical-out "); + if (hlp->invflags & EBT_ILOGICALOUT) + printf("! "); + print_iface(hlp->logical_out); + } + if (hlp->out[0] != '\0') { + printf("-o "); + if (hlp->invflags & EBT_IOUT) + printf("! "); + print_iface(hlp->out); + } + + m_l = hlp->m_list; + while (m_l) { + m = ebt_find_match(m_l->m->u.name); + if (!m) + ebt_print_bug("Match not found"); + m->print(hlp, m_l->m); + m_l = m_l->next; + } + + printf("-j "); + if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET)) + printf("%s ", hlp->t->u.name); + t = ebt_find_target(hlp->t->u.name); + if (!t) + ebt_print_bug("Target '%s' not found", hlp->t->u.name); + t->print(hlp, hlp->t); + printf("\n"); + hlp = hlp->next; + } +} + +static void print_help() +{ + struct ebt_u_match_list *m_l; + + PRINT_VERSION; + printf( +"Usage:\n" +"ebtables -[ADI] chain rule-specification [options]\n" +"ebtables -P chain target\n" +"ebtables -[LFZ] [chain]\n" +"ebtables -[NX] [chain]\n" +"ebtables -E old-chain-name new-chain-name\n\n" +"Commands:\n" +"--append -A chain : append to chain\n" +"--delete -D chain : delete matching rule from chain\n" +"--delete -D chain rulenum : delete rule at position rulenum from chain\n" +"--insert -I chain rulenum : insert rule at position rulenum in chain\n" +"--list -L [chain] : list the rules in a chain or in all chains\n" +"--flush -F [chain] : delete all rules in chain or in all chains\n" +"--policy -P chain target : change policy on chain to target\n" +"--new-chain -N chain : create a user defined chain\n" +"--rename-chain -E old new : rename a chain\n" +"--delete-chain -X [chain] : delete a user defined chain\n" +"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"); + m_l = new_entry->m_list; + while (m_l) { + ((struct ebt_u_match *)m_l->m)->help(); + printf("\n"); + m_l = m_l->next; + } + ((struct ebt_u_target *)new_entry->t)->help(); + printf("\n"); + if (table->help) + table->help(ebt_hooknames); +} + +/* Execute command L */ +static void list_rules() +{ + int i; + + printf("Bridge table: %s\n", table->name); + if (replace->selected_chain != -1) + list_em(ebt_to_chain(replace)); + else { + for (i = 0; i < replace->num_chains; i++) + if (replace->chains[i]) + list_em(replace->chains[i]); + } +} + +static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end) +{ + char *colon = strchr(argv, ':'), *buffer; + + if (colon) { + *colon = '\0'; + if (*(colon + 1) == '\0') + *rule_nr_end = -1; /* Until the last rule */ + else { + *rule_nr_end = strtol(colon + 1, &buffer, 10); + if (*buffer != '\0' || *rule_nr_end == 0) + return -1; + } + } + if (colon == argv) + *rule_nr = 1; /* Beginning with the first rule */ + else { + *rule_nr = strtol(argv, &buffer, 10); + if (*buffer != '\0' || *rule_nr == 0) + return -1; + } + if (!colon) + *rule_nr_end = *rule_nr; + return 0; +} + +static int parse_iface(char *iface, char *option) +{ + char *c; + + if ((c = strchr(iface, '+'))) { + if (*(c + 1) != '\0') { + ebt_print_error("Spurious characters after '+' wildcard for '%s'", option); + return -1; + } else + *c = IF_WILDCARD; + } + return 0; +} + +void ebt_early_init_once() +{ + ebt_iterate_matches(merge_match); + ebt_iterate_targets(merge_target); +} + +/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */ +int do_command(int argc, char *argv[], struct ebt_u_replace *replace_) +{ + char *buffer; + int c, i; + int policy = 0; + int rule_nr = 0; + int rule_nr_end = 0; + struct ebt_u_target *t; + struct ebt_u_match *m; + struct ebt_u_match_list *m_l; + struct ebt_u_entries *entries; + + opterr = 0; + + replace = replace_; + + replace->flags = 0; + replace->selected_chain = -1; + replace->command = 'h'; + + if (!new_entry) { + new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new_entry) + ebt_print_memory(); + } + /* Put some sane values in our new entry */ + ebt_initialize_entry(new_entry); + new_entry->replace = replace; + + /* The scenario induced by this loop makes that: + * '-t' and '-M' (if specified) have to come + * before '-A' and the like */ + + /* Getopt saves the day */ + while ((c = getopt_long(argc, argv, + "-A:D:I:N:E:X::L::F::P:Vhi:o:j:p: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 */ + case 'N': /* Make a user defined chain */ + case 'E': /* Rename chain */ + case 'X': /* Delete chain */ + /* We allow -N chainname -P policy */ + if (replace->command == 'N' && c == 'P') { + replace->command = c; + optind--; /* No table specified */ + goto handle_P; + } + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + + replace->command = c; + replace->flags |= OPT_COMMAND; + ebt_get_kernel_table(replace); + if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!"))) + ebt_print_error2("No chain name specified"); + if (c == 'N') { + if (ebt_get_chainnr(replace, optarg) != -1) + ebt_print_error2("Chain %s already exists", optarg); + else if (ebt_find_target(optarg)) + ebt_print_error2("Target with name %s exists", optarg); + else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN) + ebt_print_error2("Chain name length can't exceed %d", + EBT_CHAIN_MAXNAMELEN - 1); + else if (strchr(optarg, ' ') != NULL) + ebt_print_error2("Use of ' ' not allowed in chain names"); + ebt_new_chain(replace, optarg, EBT_ACCEPT); + /* This is needed to get -N x -P y working */ + replace->selected_chain = ebt_get_chainnr(replace, optarg); + break; + } else if (c == 'X') { + if (optind >= argc) { + replace->selected_chain = -1; + ebt_delete_chain(replace); + break; + } + + if (optind < argc - 1) + ebt_print_error2("No extra options allowed with -X"); + + if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1) + ebt_print_error2("Chain '%s' doesn't exist", argv[optind]); + ebt_delete_chain(replace); + optind++; + break; + } + + if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1) + ebt_print_error2("Chain '%s' doesn't exist", optarg); + if (c == 'E') { + if (optind >= argc) + ebt_print_error2("No new chain name specified"); + else if (optind < argc - 1) + ebt_print_error2("No extra options allowed with -E"); + else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN) + ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1); + else if (ebt_get_chainnr(replace, argv[optind]) != -1) + ebt_print_error2("Chain '%s' already exists", argv[optind]); + else if (ebt_find_target(argv[optind])) + ebt_print_error2("Target with name '%s' exists", argv[optind]); + else if (strchr(argv[optind], ' ') != NULL) + ebt_print_error2("Use of ' ' not allowed in chain names"); + ebt_rename_chain(replace, argv[optind]); + optind++; + break; + } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) { + if (optind != argc - 1) + ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]"); + if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end)) + ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]); + optind++; + } else if (c == 'I') { + if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9'))) + rule_nr = 1; + else { + rule_nr = strtol(argv[optind], &buffer, 10); + if (*buffer != '\0') + ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]); + optind++; + } + } else if (c == 'P') { +handle_P: + if (optind >= argc) + ebt_print_error2("No policy specified"); + for (i = 0; i < NUM_STANDARD_TARGETS; i++) + if (!strcmp(argv[optind], ebt_standard_targets[i])) { + policy = -i -1; + if (policy == EBT_CONTINUE) + ebt_print_error2("Wrong policy '%s'", argv[optind]); + break; + } + if (i == NUM_STANDARD_TARGETS) + ebt_print_error2("Unknown policy '%s'", argv[optind]); + optind++; + } + break; + case 'L': /* List */ + case 'F': /* Flush */ + if (replace->flags & OPT_COMMAND) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = c; + replace->flags |= OPT_COMMAND; + + ebt_get_kernel_table(replace); + i = -1; + if (optind < argc && argv[optind][0] != '-') { + if ((i = ebt_get_chainnr(replace, argv[optind])) == -1) + ebt_print_error2("Chain '%s' doesn't exist", argv[optind]); + optind++; + } + if (i != -1) + replace->selected_chain = i; + break; + case 'V': /* Version */ + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = 'V'; + PRINT_VERSION; + exit(0); + case 'h': /* Help */ + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = 'h'; + + /* All other arguments should be extension names */ + while (optind < argc) { + struct ebt_u_match *m; + + if (!strcasecmp("list_extensions", argv[optind])) { + ebt_list_extensions(); + exit(0); + } + if ((m = ebt_find_match(argv[optind]))) + ebt_add_match(new_entry, m); + else { + if (!(t = ebt_find_target(argv[optind]))) + ebt_print_error2("Extension '%s' not found", argv[optind]); + if (replace->flags & OPT_JUMP) + ebt_print_error2("Sorry, you can only see help for one target extension at a time"); + replace->flags |= OPT_JUMP; + new_entry->t = (struct ebt_entry_target *)t; + } + optind++; + } + break; + case 't': /* Table */ + if (OPT_COMMANDS) + ebt_print_error2("Please put the -t option first"); + ebt_check_option2(&(replace->flags), OPT_TABLE); + if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1) + ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1); + strcpy(replace->name, optarg); + break; + case 'i': /* Input interface */ + case 2 : /* Logical input interface */ + case 'o': /* Output interface */ + case 3 : /* Logical output interface */ + case 'j': /* Target */ + case 'p': /* Net family protocol */ + case 's': /* Source mac */ + case 'd': /* Destination mac */ + if (!OPT_COMMANDS) + ebt_print_error2("No command specified"); + if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I') + ebt_print_error2("Command and option do not match"); + if (c == 'i') { + ebt_check_option2(&(replace->flags), OPT_IN); + if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) + ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IIN; + + if (strlen(optarg) >= IFNAMSIZ) +big_iface_length: + ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1); + strcpy(new_entry->in, optarg); + if (parse_iface(new_entry->in, "-i")) + return -1; + break; + } else if (c == 2) { + ebt_check_option2(&(replace->flags), OPT_LOGICALIN); + if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) + ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ILOGICALIN; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->logical_in, optarg); + if (parse_iface(new_entry->logical_in, "--logical-in")) + return -1; + break; + } else if (c == 'o') { + ebt_check_option2(&(replace->flags), OPT_OUT); + if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) + ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IOUT; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->out, optarg); + if (parse_iface(new_entry->out, "-o")) + return -1; + break; + } else if (c == 3) { + ebt_check_option2(&(replace->flags), OPT_LOGICALOUT); + if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) + ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ILOGICALOUT; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->logical_out, optarg); + if (parse_iface(new_entry->logical_out, "--logical-out")) + return -1; + break; + } else if (c == 'j') { + ebt_check_option2(&(replace->flags), OPT_JUMP); + for (i = 0; i < NUM_STANDARD_TARGETS; i++) + if (!strcmp(optarg, ebt_standard_targets[i])) { + t = ebt_find_target(EBT_STANDARD_TARGET); + ((struct ebt_standard_target *) t->t)->verdict = -i - 1; + break; + } + if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) { + ebt_print_error2("Return target only for user defined chains"); + } else if (i != NUM_STANDARD_TARGETS) + break; + + if ((i = ebt_get_chainnr(replace, optarg)) != -1) { + if (i < NF_BR_NUMHOOKS) + ebt_print_error2("Don't jump to a standard chain"); + t = ebt_find_target(EBT_STANDARD_TARGET); + ((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS; + break; + } else { + /* Must be an extension then */ + struct ebt_u_target *t; + + t = ebt_find_target(optarg); + /* -j standard not allowed either */ + if (!t || t == (struct ebt_u_target *)new_entry->t) + ebt_print_error2("Illegal target name '%s'", optarg); + new_entry->t = (struct ebt_entry_target *)t; + ebt_find_target(EBT_STANDARD_TARGET)->used = 0; + t->used = 1; + } + break; + } else if (c == 's') { + ebt_check_option2(&(replace->flags), OPT_SOURCE); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ISOURCE; + + if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk)) + ebt_print_error2("Problem with specified source mac '%s'", optarg); + new_entry->bitmask |= EBT_SOURCEMAC; + break; + } else if (c == 'd') { + ebt_check_option2(&(replace->flags), OPT_DEST); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IDEST; + + if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk)) + ebt_print_error2("Problem with specified destination mac '%s'", optarg); + new_entry->bitmask |= EBT_DESTMAC; + break; + } + ebt_check_option2(&(replace->flags), OPT_PROTOCOL); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IPROTO; + + new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO); + i = strtol(optarg, &buffer, 16); + if (*buffer == '\0' && (i < 0 || i > 0xFFFF)) + ebt_print_error2("Problem with the specified protocol"); + if (*buffer != '\0') { + const struct ethertypeent *ent; + + if (!strcasecmp(optarg, "LENGTH")) { + new_entry->bitmask |= EBT_802_3; + break; + } + ent = getethertypebyname(optarg); + if (!ent) + ebt_print_error2("Problem with the specified Ethernet protocol '%s'", optarg); + new_entry->ethproto = ent->e_ethertype; + } else + new_entry->ethproto = i; + + if (new_entry->ethproto < 0x0600) + ebt_print_error2("Sorry, protocols have values above or equal to 0x0600"); + break; + case 1 : + if (!strcmp(optarg, "!")) + ebt_check_inverse2(optarg); + else + ebt_print_error2("Bad argument : '%s'", optarg); + /* ebt_check_inverse() did optind++ */ + optind--; + continue; + default: + /* Is it a target option? */ + t = (struct ebt_u_target *)new_entry->t; + if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) { + goto check_extension; + } + + /* Is it a match_option? */ + for (m = ebt_matches; m; m = m->next) + if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m)) + break; + + if (m != NULL) { + if (m->used == 0) { + ebt_add_match(new_entry, m); + m->used = 1; + } + goto check_extension; + } + + if (c == '?') + ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c); + else { + if (!strcmp(t->name, "standard")) + ebt_print_error2("Unknown argument: don't forget the -t option"); + else + ebt_print_error2("Target-specific option does not correspond with specified target"); + } +check_extension: + if (replace->command != 'A' && replace->command != 'I' && + replace->command != 'D') + ebt_print_error2("Extensions only for -A, -I and -D"); + } + ebt_invert = 0; + } + + if (!(table = ebt_find_table(replace->name))) + ebt_print_error2("Bad table name"); + + if (replace->command == 'h') { + print_help(); + exit(0); + } + + /* Do the final checks */ + if (replace->command == 'A' || replace->command == 'I' || + replace->command == 'D') { + /* This will put the hook_mask right for the chains */ + ebt_check_for_loops(replace); + entries = ebt_to_chain(replace); + m_l = new_entry->m_list; + t = (struct ebt_u_target *)new_entry->t; + while (m_l) { + m = (struct ebt_u_match *)(m_l->m); + m->final_check(new_entry, m->m, replace->name, + entries->hook_mask, 0); + m_l = m_l->next; + } + t->final_check(new_entry, t->t, replace->name, + entries->hook_mask, 0); + } + /* So, the extensions can work with the host endian. + * The kernel does not have to do this of course */ + new_entry->ethproto = htons(new_entry->ethproto); + + if (replace->command == 'P') { + if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN) + ebt_print_error2("Policy RETURN only allowed for user defined chains"); + ebt_change_policy(replace, policy); + } else if (replace->command == 'L') { + list_rules(); + exit(0); + } + if (replace->command == 'F') { + ebt_flush_chains(replace); + } else if (replace->command == 'A' || replace->command == 'I') { + ebt_add_rule(replace, new_entry, rule_nr); + if (rule_nr <= 0) + rule_nr--; + rule_nr_end = rule_nr; + + /* a jump to a udc requires checking for loops */ + if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) && + ((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) { + /* FIXME: this can be done faster */ + ebt_check_for_loops(replace); + } + + /* Do the final_check(), for all entries. + * This is needed when adding a rule that has a chain target */ + i = -1; + while (++i != replace->num_chains) { + struct ebt_u_entry *e; + + entries = replace->chains[i]; + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + ebt_print_bug("whoops\n"); + } + e = entries->entries->next; + while (e != entries->entries) { + /* Userspace extensions use host endian */ + e->ethproto = ntohs(e->ethproto); + ebt_do_final_checks(replace, e, entries); + e->ethproto = htons(e->ethproto); + e = e->next; + } + } + /* Don't reuse the added rule */ + new_entry = NULL; + } else if (replace->command == 'D') { + ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end); + } + /* Commands -N, -E, -X fall through */ + + if (table->check) + table->check(replace); + + ebt_deliver_table(replace); + + if (replace->nentries) + ebt_deliver_counters(replace); + + return 0; +} diff --git a/net/ebtables-tiny/src/extensions/ebt_arp.c b/net/ebtables-tiny/src/extensions/ebt_arp.c new file mode 100644 index 0000000..bb6c5fd --- /dev/null +++ b/net/ebtables-tiny/src/extensions/ebt_arp.c @@ -0,0 +1,367 @@ +/* ebt_arp + * + * Authors: + * Bart De Schuymer + * Tim Gardner + * + * April, 2002 + */ + +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include "../include/ethernetdb.h" +#include +#include + +#define ARP_OPCODE '1' +#define ARP_HTYPE '2' +#define ARP_PTYPE '3' +#define ARP_IP_S '4' +#define ARP_IP_D '5' +#define ARP_MAC_S '6' +#define ARP_MAC_D '7' +#define ARP_GRAT '8' +static const struct option opts[] = +{ + { "arp-opcode" , required_argument, 0, ARP_OPCODE }, + { "arp-op" , required_argument, 0, ARP_OPCODE }, + { "arp-htype" , required_argument, 0, ARP_HTYPE }, + { "arp-ptype" , required_argument, 0, ARP_PTYPE }, + { "arp-ip-src" , required_argument, 0, ARP_IP_S }, + { "arp-ip-dst" , required_argument, 0, ARP_IP_D }, + { "arp-mac-src" , required_argument, 0, ARP_MAC_S }, + { "arp-mac-dst" , required_argument, 0, ARP_MAC_D }, + { "arp-gratuitous", no_argument, 0, ARP_GRAT }, + { 0 } +}; + +#define NUMOPCODES 9 +/* a few names */ +static char *opcodes[] = +{ + "Request", + "Reply", + "Request_Reverse", + "Reply_Reverse", + "DRARP_Request", + "DRARP_Reply", + "DRARP_Error", + "InARP_Request", + "ARP_NAK", +}; + +static void print_help() +{ + int i; + + printf( +"arp options:\n" +"--arp-opcode [!] opcode : ARP opcode (integer or string)\n" +"--arp-htype [!] type : ARP hardware type (integer or string)\n" +"--arp-ptype [!] type : ARP protocol type (hexadecimal or string)\n" +"--arp-ip-src [!] address[/mask]: ARP IP source specification\n" +"--arp-ip-dst [!] address[/mask]: ARP IP target specification\n" +"--arp-mac-src [!] address[/mask]: ARP MAC source specification\n" +"--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n" +"[!] --arp-gratuitous : ARP gratuitous packet\n" +" opcode strings: \n"); + for (i = 0; i < NUMOPCODES; i++) + printf(" %d = %s\n", i + 1, opcodes[i]); + printf( +" hardware type string: 1 = Ethernet\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; + + arpinfo->invflags = 0; + arpinfo->bitmask = 0; +} + + +#define OPT_OPCODE 0x01 +#define OPT_HTYPE 0x02 +#define OPT_PTYPE 0x04 +#define OPT_IP_S 0x08 +#define OPT_IP_D 0x10 +#define OPT_MAC_S 0x20 +#define OPT_MAC_D 0x40 +#define OPT_GRAT 0x80 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data; + long int i; + char *end; + uint32_t *addr; + uint32_t *mask; + unsigned char *maddr; + unsigned char *mmask; + + switch (c) { + case ARP_OPCODE: + ebt_check_option2(flags, OPT_OPCODE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_OPCODE; + i = strtol(optarg, &end, 10); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + for (i = 0; i < NUMOPCODES; i++) + if (!strcasecmp(opcodes[i], optarg)) + break; + if (i == NUMOPCODES) + ebt_print_error2("Problem with specified ARP opcode"); + i++; + } + arpinfo->opcode = htons(i); + arpinfo->bitmask |= EBT_ARP_OPCODE; + break; + + case ARP_HTYPE: + ebt_check_option2(flags, OPT_HTYPE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_HTYPE; + i = strtol(optarg, &end, 10); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + if (!strcasecmp("Ethernet", argv[optind - 1])) + i = 1; + else + ebt_print_error2("Problem with specified ARP hardware type"); + } + arpinfo->htype = htons(i); + arpinfo->bitmask |= EBT_ARP_HTYPE; + break; + + case ARP_PTYPE: + { + uint16_t proto; + + ebt_check_option2(flags, OPT_PTYPE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_PTYPE; + + i = strtol(optarg, &end, 16); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + const struct ethertypeent *ent; + + ent = getethertypebyname(argv[optind - 1]); + if (!ent) + ebt_print_error2("Problem with specified ARP " + "protocol type"); + proto = ent->e_ethertype; + + } else + proto = i; + arpinfo->ptype = htons(proto); + arpinfo->bitmask |= EBT_ARP_PTYPE; + break; + } + + case ARP_IP_S: + case ARP_IP_D: + if (c == ARP_IP_S) { + ebt_check_option2(flags, OPT_IP_S); + addr = &arpinfo->saddr; + mask = &arpinfo->smsk; + arpinfo->bitmask |= EBT_ARP_SRC_IP; + } else { + ebt_check_option2(flags, OPT_IP_D); + addr = &arpinfo->daddr; + mask = &arpinfo->dmsk; + arpinfo->bitmask |= EBT_ARP_DST_IP; + } + if (ebt_check_inverse2(optarg)) { + if (c == ARP_IP_S) + arpinfo->invflags |= EBT_ARP_SRC_IP; + else + arpinfo->invflags |= EBT_ARP_DST_IP; + } + ebt_parse_ip_address(optarg, addr, mask); + break; + + case ARP_MAC_S: + case ARP_MAC_D: + if (c == ARP_MAC_S) { + ebt_check_option2(flags, OPT_MAC_S); + maddr = arpinfo->smaddr; + mmask = arpinfo->smmsk; + arpinfo->bitmask |= EBT_ARP_SRC_MAC; + } else { + ebt_check_option2(flags, OPT_MAC_D); + maddr = arpinfo->dmaddr; + mmask = arpinfo->dmmsk; + arpinfo->bitmask |= EBT_ARP_DST_MAC; + } + if (ebt_check_inverse2(optarg)) { + if (c == ARP_MAC_S) + arpinfo->invflags |= EBT_ARP_SRC_MAC; + else + arpinfo->invflags |= EBT_ARP_DST_MAC; + } + if (ebt_get_mac_and_mask(optarg, maddr, mmask)) + ebt_print_error2("Problem with ARP MAC address argument"); + break; + case ARP_GRAT: + ebt_check_option2(flags, OPT_GRAT); + arpinfo->bitmask |= EBT_ARP_GRAT; + if (ebt_invert) + arpinfo->invflags |= EBT_ARP_GRAT; + break; + + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) || + entry->invflags & EBT_IPROTO) + ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; + int i; + + if (arpinfo->bitmask & EBT_ARP_OPCODE) { + int opcode = ntohs(arpinfo->opcode); + printf("--arp-op "); + if (arpinfo->invflags & EBT_ARP_OPCODE) + printf("! "); + if (opcode > 0 && opcode <= NUMOPCODES) + printf("%s ", opcodes[opcode - 1]); + else + printf("%d ", opcode); + } + if (arpinfo->bitmask & EBT_ARP_HTYPE) { + printf("--arp-htype "); + if (arpinfo->invflags & EBT_ARP_HTYPE) + printf("! "); + printf("%d ", ntohs(arpinfo->htype)); + } + if (arpinfo->bitmask & EBT_ARP_PTYPE) { + const struct ethertypeent *ent; + + printf("--arp-ptype "); + if (arpinfo->invflags & EBT_ARP_PTYPE) + printf("! "); + ent = getethertypebynumber(ntohs(arpinfo->ptype)); + if (!ent) + printf("0x%x ", ntohs(arpinfo->ptype)); + else + printf("%s ", ent->e_name); + } + if (arpinfo->bitmask & EBT_ARP_SRC_IP) { + printf("--arp-ip-src "); + if (arpinfo->invflags & EBT_ARP_SRC_IP) + printf("! "); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i], + (i == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(arpinfo->smsk)); + } + if (arpinfo->bitmask & EBT_ARP_DST_IP) { + printf("--arp-ip-dst "); + if (arpinfo->invflags & EBT_ARP_DST_IP) + printf("! "); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i], + (i == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk)); + } + if (arpinfo->bitmask & EBT_ARP_SRC_MAC) { + printf("--arp-mac-src "); + if (arpinfo->invflags & EBT_ARP_SRC_MAC) + printf("! "); + ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk); + printf(" "); + } + if (arpinfo->bitmask & EBT_ARP_DST_MAC) { + printf("--arp-mac-dst "); + if (arpinfo->invflags & EBT_ARP_DST_MAC) + printf("! "); + ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk); + printf(" "); + } + if (arpinfo->bitmask & EBT_ARP_GRAT) { + if (arpinfo->invflags & EBT_ARP_GRAT) + printf("! "); + printf("--arp-gratuitous "); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data; + struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data; + + if (arpinfo1->bitmask != arpinfo2->bitmask) + return 0; + if (arpinfo1->invflags != arpinfo2->invflags) + return 0; + if (arpinfo1->bitmask & EBT_ARP_OPCODE) { + if (arpinfo1->opcode != arpinfo2->opcode) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_HTYPE) { + if (arpinfo1->htype != arpinfo2->htype) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_PTYPE) { + if (arpinfo1->ptype != arpinfo2->ptype) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_SRC_IP) { + if (arpinfo1->saddr != arpinfo2->saddr) + return 0; + if (arpinfo1->smsk != arpinfo2->smsk) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_DST_IP) { + if (arpinfo1->daddr != arpinfo2->daddr) + return 0; + if (arpinfo1->dmsk != arpinfo2->dmsk) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) { + if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN)) + return 0; + if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN)) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_DST_MAC) { + if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN)) + return 0; + if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN)) + return 0; + } + return 1; +} + +static struct ebt_u_match arp_match = +{ + .name = "arp", + .size = sizeof(struct ebt_arp_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +__attribute__((constructor)) static void extension_init(void) +{ + ebt_register_match(&arp_match); +} diff --git a/net/ebtables-tiny/src/extensions/ebt_ip.c b/net/ebtables-tiny/src/extensions/ebt_ip.c new file mode 100644 index 0000000..f6e716a --- /dev/null +++ b/net/ebtables-tiny/src/extensions/ebt_ip.c @@ -0,0 +1,474 @@ +/* ebt_ip + * + * Authors: + * Bart De Schuymer + * + * Changes: + * added ip-sport and ip-dport; parsing of port arguments is + * based on code from iptables-1.2.7a + * Innominate Security Technologies AG + * September, 2002 + */ + +#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' +#define IP_SPORT '5' +#define IP_DPORT '6' +#define IP_ICMP '7' +#define IP_IGMP '8' + +static const struct option opts[] = +{ + { "ip-source" , required_argument, 0, IP_SOURCE }, + { "ip-src" , required_argument, 0, IP_SOURCE }, + { "ip-destination" , required_argument, 0, IP_DEST }, + { "ip-dst" , required_argument, 0, IP_DEST }, + { "ip-tos" , required_argument, 0, IP_myTOS }, + { "ip-protocol" , required_argument, 0, IP_PROTO }, + { "ip-proto" , required_argument, 0, IP_PROTO }, + { "ip-source-port" , required_argument, 0, IP_SPORT }, + { "ip-sport" , required_argument, 0, IP_SPORT }, + { "ip-destination-port" , required_argument, 0, IP_DPORT }, + { "ip-dport" , required_argument, 0, IP_DPORT }, + { "ip-icmp-type" , required_argument, 0, IP_ICMP }, + { "ip-igmp-type" , required_argument, 0, IP_IGMP }, + { 0 } +}; + +static const struct ebt_icmp_names icmp_codes[] = { + { "echo-reply", 0, 0, 0xFF }, + /* Alias */ { "pong", 0, 0, 0xFF }, + + { "destination-unreachable", 3, 0, 0xFF }, + { "network-unreachable", 3, 0, 0 }, + { "host-unreachable", 3, 1, 1 }, + { "protocol-unreachable", 3, 2, 2 }, + { "port-unreachable", 3, 3, 3 }, + { "fragmentation-needed", 3, 4, 4 }, + { "source-route-failed", 3, 5, 5 }, + { "network-unknown", 3, 6, 6 }, + { "host-unknown", 3, 7, 7 }, + { "network-prohibited", 3, 9, 9 }, + { "host-prohibited", 3, 10, 10 }, + { "TOS-network-unreachable", 3, 11, 11 }, + { "TOS-host-unreachable", 3, 12, 12 }, + { "communication-prohibited", 3, 13, 13 }, + { "host-precedence-violation", 3, 14, 14 }, + { "precedence-cutoff", 3, 15, 15 }, + + { "source-quench", 4, 0, 0xFF }, + + { "redirect", 5, 0, 0xFF }, + { "network-redirect", 5, 0, 0 }, + { "host-redirect", 5, 1, 1 }, + { "TOS-network-redirect", 5, 2, 2 }, + { "TOS-host-redirect", 5, 3, 3 }, + + { "echo-request", 8, 0, 0xFF }, + /* Alias */ { "ping", 8, 0, 0xFF }, + + { "router-advertisement", 9, 0, 0xFF }, + + { "router-solicitation", 10, 0, 0xFF }, + + { "time-exceeded", 11, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, + { "ttl-zero-during-transit", 11, 0, 0 }, + { "ttl-zero-during-reassembly", 11, 1, 1 }, + + { "parameter-problem", 12, 0, 0xFF }, + { "ip-header-bad", 12, 0, 0 }, + { "required-option-missing", 12, 1, 1 }, + + { "timestamp-request", 13, 0, 0xFF }, + + { "timestamp-reply", 14, 0, 0xFF }, + + { "address-mask-request", 17, 0, 0xFF }, + + { "address-mask-reply", 18, 0, 0xFF } +}; + +static const struct ebt_icmp_names igmp_types[] = { + { "membership-query", 0x11 }, + { "membership-report-v1", 0x12 }, + { "membership-report-v2", 0x16 }, + { "leave-group", 0x17 }, + { "membership-report-v3", 0x22 }, +}; + +/* put the mask into 4 bytes */ +/* transform a protocol and service name into a port number */ +static uint16_t parse_port(const char *protocol, const char *name) +{ + struct servent *service; + char *end; + int port; + + port = strtol(name, &end, 10); + if (*end != '\0') { + if (protocol && + (service = getservbyname(name, protocol)) != NULL) + return ntohs(service->s_port); + } + else if (port >= 0 || port <= 0xFFFF) { + return port; + } + ebt_print_error("Problem with specified %s port '%s'", + protocol?protocol:"", name); + return 0; +} + +static void +parse_port_range(const char *protocol, const char *portstring, uint16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = parse_port(protocol, buffer); + else { + *cp = '\0'; + cp++; + ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0; + ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF; + + if (ports[0] > ports[1]) + ebt_print_error("Invalid portrange (min > max)"); + } + free(buffer); +} + +static void print_port_range(uint16_t *ports) +{ + if (ports[0] == ports[1]) + printf("%d ", ports[0]); + else + printf("%d:%d ", ports[0], ports[1]); +} + +static void print_help() +{ + printf( +"ip options:\n" +"--ip-src [!] address[/mask]: ip source specification\n" +"--ip-dst [!] address[/mask]: ip destination specification\n" +"--ip-tos [!] tos : ip tos specification\n" +"--ip-proto [!] protocol : ip protocol specification\n" +"--ip-sport [!] port[:port] : tcp/udp source port or port range\n" +"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n" +"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n" +"--ip-igmp-type [!] type[:type] : igmp type or type range\n"); + + printf("\nValid ICMP Types:\n"); + ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes)); + printf("\nValid IGMP Types:\n"); + ebt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types)); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + + ipinfo->invflags = 0; + ipinfo->bitmask = 0; +} + +#define OPT_SOURCE 0x01 +#define OPT_DEST 0x02 +#define OPT_TOS 0x04 +#define OPT_PROTO 0x08 +#define OPT_SPORT 0x10 +#define OPT_DPORT 0x20 +#define OPT_ICMP 0x40 +#define OPT_IGMP 0x80 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data; + char *end; + long int i; + + switch (c) { + case IP_SOURCE: + ebt_check_option2(flags, OPT_SOURCE); + ipinfo->bitmask |= EBT_IP_SOURCE; + + case IP_DEST: + if (c == IP_DEST) { + ebt_check_option2(flags, OPT_DEST); + ipinfo->bitmask |= EBT_IP_DEST; + } + if (ebt_check_inverse2(optarg)) { + if (c == IP_SOURCE) + ipinfo->invflags |= EBT_IP_SOURCE; + else + ipinfo->invflags |= EBT_IP_DEST; + } + if (c == IP_SOURCE) + ebt_parse_ip_address(optarg, &ipinfo->saddr, &ipinfo->smsk); + else + ebt_parse_ip_address(optarg, &ipinfo->daddr, &ipinfo->dmsk); + break; + + case IP_SPORT: + case IP_DPORT: + if (c == IP_SPORT) { + ebt_check_option2(flags, OPT_SPORT); + ipinfo->bitmask |= EBT_IP_SPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_SPORT; + } else { + ebt_check_option2(flags, OPT_DPORT); + ipinfo->bitmask |= EBT_IP_DPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_DPORT; + } + if (c == IP_SPORT) + parse_port_range(NULL, optarg, ipinfo->sport); + else + parse_port_range(NULL, optarg, ipinfo->dport); + break; + + case IP_ICMP: + ebt_check_option2(flags, OPT_ICMP); + ipinfo->bitmask |= EBT_IP_ICMP; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_ICMP; + if (ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg, + ipinfo->icmp_type, ipinfo->icmp_code)) + return 0; + break; + + case IP_IGMP: + ebt_check_option2(flags, OPT_IGMP); + ipinfo->bitmask |= EBT_IP_IGMP; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_IGMP; + if (ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg, + ipinfo->igmp_type, NULL)) + return 0; + break; + + case IP_myTOS: + ebt_check_option2(flags, OPT_TOS); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_TOS; + i = strtol(optarg, &end, 16); + if (i < 0 || i > 255 || *end != '\0') + ebt_print_error2("Problem with specified IP tos"); + ipinfo->tos = i; + ipinfo->bitmask |= EBT_IP_TOS; + break; + + case IP_PROTO: + ebt_check_option2(flags, OPT_PROTO); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_PROTO; + i = strtoul(optarg, &end, 10); + if (*end != '\0') { + struct protoent *pe; + + pe = getprotobyname(optarg); + if (pe == NULL) + ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]); + ipinfo->protocol = pe->p_proto; + } else { + ipinfo->protocol = (unsigned char) i; + } + ipinfo->bitmask |= EBT_IP_PROTO; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + + if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO) { + ebt_print_error("For IP filtering the protocol must be " + "specified as IPv4"); + } else if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + (ipinfo->protocol!=IPPROTO_TCP && + ipinfo->protocol!=IPPROTO_UDP && + ipinfo->protocol!=IPPROTO_SCTP && + ipinfo->protocol!=IPPROTO_DCCP))) { + ebt_print_error("For port filtering the IP protocol must be " + "either 6 (tcp), 17 (udp), 33 (dccp) or " + "132 (sctp)"); + } else if ((ipinfo->bitmask & EBT_IP_ICMP) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + ipinfo->protocol != IPPROTO_ICMP)) { + ebt_print_error("For ICMP filtering the IP protocol must be " + "1 (icmp)"); + } else if ((ipinfo->bitmask & EBT_IP_IGMP) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + ipinfo->protocol != IPPROTO_IGMP)) { + ebt_print_error("For IGMP filtering the IP protocol must be " + "2 (igmp)"); + } +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + int j; + + if (ipinfo->bitmask & EBT_IP_SOURCE) { + printf("--ip-src "); + if (ipinfo->invflags & EBT_IP_SOURCE) + printf("! "); + for (j = 0; j < 4; j++) + printf("%d%s",((unsigned char *)&ipinfo->saddr)[j], + (j == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(ipinfo->smsk)); + } + if (ipinfo->bitmask & EBT_IP_DEST) { + printf("--ip-dst "); + if (ipinfo->invflags & EBT_IP_DEST) + printf("! "); + for (j = 0; j < 4; j++) + printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j], + (j == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(ipinfo->dmsk)); + } + if (ipinfo->bitmask & EBT_IP_TOS) { + printf("--ip-tos "); + if (ipinfo->invflags & EBT_IP_TOS) + printf("! "); + printf("0x%02X ", ipinfo->tos); + } + if (ipinfo->bitmask & EBT_IP_PROTO) { + struct protoent *pe; + + printf("--ip-proto "); + if (ipinfo->invflags & EBT_IP_PROTO) + printf("! "); + pe = getprotobynumber(ipinfo->protocol); + if (pe == NULL) { + printf("%d ", ipinfo->protocol); + } else { + printf("%s ", pe->p_name); + } + } + if (ipinfo->bitmask & EBT_IP_SPORT) { + printf("--ip-sport "); + if (ipinfo->invflags & EBT_IP_SPORT) + printf("! "); + print_port_range(ipinfo->sport); + } + if (ipinfo->bitmask & EBT_IP_DPORT) { + printf("--ip-dport "); + if (ipinfo->invflags & EBT_IP_DPORT) + printf("! "); + print_port_range(ipinfo->dport); + } + if (ipinfo->bitmask & EBT_IP_ICMP) { + printf("--ip-icmp-type "); + if (ipinfo->invflags & EBT_IP_ICMP) + printf("! "); + ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes), + ipinfo->icmp_type, ipinfo->icmp_code); + } + if (ipinfo->bitmask & EBT_IP_IGMP) { + printf("--ip-igmp-type "); + if (ipinfo->invflags & EBT_IP_IGMP) + printf("! "); + ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types), + ipinfo->igmp_type, NULL); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data; + struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data; + + if (ipinfo1->bitmask != ipinfo2->bitmask) + return 0; + if (ipinfo1->invflags != ipinfo2->invflags) + return 0; + if (ipinfo1->bitmask & EBT_IP_SOURCE) { + if (ipinfo1->saddr != ipinfo2->saddr) + return 0; + if (ipinfo1->smsk != ipinfo2->smsk) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_DEST) { + if (ipinfo1->daddr != ipinfo2->daddr) + return 0; + if (ipinfo1->dmsk != ipinfo2->dmsk) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_TOS) { + if (ipinfo1->tos != ipinfo2->tos) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_PROTO) { + if (ipinfo1->protocol != ipinfo2->protocol) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_SPORT) { + if (ipinfo1->sport[0] != ipinfo2->sport[0] || + ipinfo1->sport[1] != ipinfo2->sport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_DPORT) { + if (ipinfo1->dport[0] != ipinfo2->dport[0] || + ipinfo1->dport[1] != ipinfo2->dport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_ICMP) { + if (ipinfo1->icmp_type[0] != ipinfo2->icmp_type[0] || + ipinfo1->icmp_type[1] != ipinfo2->icmp_type[1] || + ipinfo1->icmp_code[0] != ipinfo2->icmp_code[0] || + ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_IGMP) { + if (ipinfo1->igmp_type[0] != ipinfo2->igmp_type[0] || + ipinfo1->igmp_type[1] != ipinfo2->igmp_type[1]) + return 0; + } + return 1; +} + +static struct ebt_u_match ip_match = +{ + .name = "ip", + .size = sizeof(struct ebt_ip_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +__attribute__((constructor)) static void extension_init(void) +{ + ebt_register_match(&ip_match); +} diff --git a/net/ebtables-tiny/src/extensions/ebt_ip6.c b/net/ebtables-tiny/src/extensions/ebt_ip6.c new file mode 100644 index 0000000..8333c4b --- /dev/null +++ b/net/ebtables-tiny/src/extensions/ebt_ip6.c @@ -0,0 +1,415 @@ +/* ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng + * Manohar Castelino + * + * Summary: + * This is just a modification of the IPv4 code written by + * Bart De Schuymer + * with the changes required to support IPv6 + * + */ + +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + + + +#define IP_SOURCE '1' +#define IP_DEST '2' +#define IP_TCLASS '3' +#define IP_PROTO '4' +#define IP_SPORT '5' +#define IP_DPORT '6' +#define IP_ICMP6 '7' + +static const struct option opts[] = +{ + { "ip6-source" , required_argument, 0, IP_SOURCE }, + { "ip6-src" , required_argument, 0, IP_SOURCE }, + { "ip6-destination" , required_argument, 0, IP_DEST }, + { "ip6-dst" , required_argument, 0, IP_DEST }, + { "ip6-traffic-class" , required_argument, 0, IP_TCLASS }, + { "ip6-tclass" , required_argument, 0, IP_TCLASS }, + { "ip6-protocol" , required_argument, 0, IP_PROTO }, + { "ip6-proto" , required_argument, 0, IP_PROTO }, + { "ip6-source-port" , required_argument, 0, IP_SPORT }, + { "ip6-sport" , required_argument, 0, IP_SPORT }, + { "ip6-destination-port" , required_argument, 0, IP_DPORT }, + { "ip6-dport" , required_argument, 0, IP_DPORT }, + { "ip6-icmp-type" , required_argument, 0, IP_ICMP6 }, + { 0 } +}; + + +static const struct ebt_icmp_names icmpv6_codes[] = { + { "destination-unreachable", 1, 0, 0xFF }, + { "no-route", 1, 0, 0 }, + { "communication-prohibited", 1, 1, 1 }, + { "address-unreachable", 1, 3, 3 }, + { "port-unreachable", 1, 4, 4 }, + + { "packet-too-big", 2, 0, 0xFF }, + + { "time-exceeded", 3, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, + { "ttl-zero-during-transit", 3, 0, 0 }, + { "ttl-zero-during-reassembly", 3, 1, 1 }, + + { "parameter-problem", 4, 0, 0xFF }, + { "bad-header", 4, 0, 0 }, + { "unknown-header-type", 4, 1, 1 }, + { "unknown-option", 4, 2, 2 }, + + { "echo-request", 128, 0, 0xFF }, + /* Alias */ { "ping", 128, 0, 0xFF }, + + { "echo-reply", 129, 0, 0xFF }, + /* Alias */ { "pong", 129, 0, 0xFF }, + + { "router-solicitation", 133, 0, 0xFF }, + + { "router-advertisement", 134, 0, 0xFF }, + + { "neighbour-solicitation", 135, 0, 0xFF }, + /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, + + { "neighbour-advertisement", 136, 0, 0xFF }, + /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, + + { "redirect", 137, 0, 0xFF }, +}; + +/* transform a protocol and service name into a port number */ +static uint16_t parse_port(const char *protocol, const char *name) +{ + struct servent *service; + char *end; + int port; + + port = strtol(name, &end, 10); + if (*end != '\0') { + if (protocol && + (service = getservbyname(name, protocol)) != NULL) + return ntohs(service->s_port); + } + else if (port >= 0 || port <= 0xFFFF) { + return port; + } + ebt_print_error("Problem with specified %s port '%s'", + protocol?protocol:"", name); + return 0; +} + +static void +parse_port_range(const char *protocol, const char *portstring, uint16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = parse_port(protocol, buffer); + else { + *cp = '\0'; + cp++; + ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0; + ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF; + + if (ports[0] > ports[1]) + ebt_print_error("Invalid portrange (min > max)"); + } + free(buffer); +} + +static void print_port_range(uint16_t *ports) +{ + if (ports[0] == ports[1]) + printf("%d ", ports[0]); + else + printf("%d:%d ", ports[0], ports[1]); +} + +static void print_help() +{ + printf( +"ip6 options:\n" +"--ip6-src [!] address[/mask]: ipv6 source specification\n" +"--ip6-dst [!] address[/mask]: ipv6 destination specification\n" +"--ip6-tclass [!] tclass : ipv6 traffic class specification\n" +"--ip6-proto [!] protocol : ipv6 protocol specification\n" +"--ip6-sport [!] port[:port] : tcp/udp source port or port range\n" +"--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n" +"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n"); + + printf("\nValid ICMPv6 Types:\n"); + ebt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes)); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + ipinfo->invflags = 0; + ipinfo->bitmask = 0; + memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr)); + memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr)); + memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr)); + memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr)); +} + +#define OPT_SOURCE 0x01 +#define OPT_DEST 0x02 +#define OPT_TCLASS 0x04 +#define OPT_PROTO 0x08 +#define OPT_SPORT 0x10 +#define OPT_DPORT 0x20 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data; + char *end; + long int i; + + switch (c) { + case IP_SOURCE: + ebt_check_option2(flags, OPT_SOURCE); + ipinfo->bitmask |= EBT_IP6_SOURCE; + if (ebt_check_inverse2(optarg)) { + ipinfo->invflags |= EBT_IP6_SOURCE; + } + ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk); + break; + + case IP_DEST: + ebt_check_option2(flags, OPT_DEST); + ipinfo->bitmask |= EBT_IP6_DEST; + if (ebt_check_inverse2(optarg)) { + ipinfo->invflags |= EBT_IP6_DEST; + } + ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk); + break; + + case IP_SPORT: + case IP_DPORT: + if (c == IP_SPORT) { + ebt_check_option2(flags, OPT_SPORT); + ipinfo->bitmask |= EBT_IP6_SPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_SPORT; + } else { + ebt_check_option2(flags, OPT_DPORT); + ipinfo->bitmask |= EBT_IP6_DPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_DPORT; + } + if (c == IP_SPORT) + parse_port_range(NULL, optarg, ipinfo->sport); + else + parse_port_range(NULL, optarg, ipinfo->dport); + break; + + case IP_ICMP6: + ebt_check_option2(flags, EBT_IP6_ICMP6); + ipinfo->bitmask |= EBT_IP6_ICMP6; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_ICMP6; + if (ebt_parse_icmp(icmpv6_codes, ARRAY_SIZE(icmpv6_codes), + optarg, ipinfo->icmpv6_type, + ipinfo->icmpv6_code)) + return 0; + break; + + case IP_TCLASS: + ebt_check_option2(flags, OPT_TCLASS); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_TCLASS; + i = strtol(optarg, &end, 16); + if (i < 0 || i > 255 || *end != '\0') + ebt_print_error2("Problem with specified IPv6 traffic class"); + ipinfo->tclass = i; + ipinfo->bitmask |= EBT_IP6_TCLASS; + break; + + case IP_PROTO: + ebt_check_option2(flags, OPT_PROTO); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_PROTO; + i = strtoul(optarg, &end, 10); + if (*end != '\0') { + struct protoent *pe; + + pe = getprotobyname(optarg); + if (pe == NULL) + ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]); + ipinfo->protocol = pe->p_proto; + } else { + ipinfo->protocol = (unsigned char) i; + } + ipinfo->bitmask |= EBT_IP6_PROTO; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) { + ebt_print_error("For IPv6 filtering the protocol must be " + "specified as IPv6"); + } else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) && + (!(ipinfo->bitmask & EBT_IP6_PROTO) || + ipinfo->invflags & EBT_IP6_PROTO || + (ipinfo->protocol!=IPPROTO_TCP && + ipinfo->protocol!=IPPROTO_UDP && + ipinfo->protocol!=IPPROTO_SCTP && + ipinfo->protocol!=IPPROTO_DCCP))) + ebt_print_error("For port filtering the IP protocol must be " + "either 6 (tcp), 17 (udp), 33 (dccp) or " + "132 (sctp)"); + if ((ipinfo->bitmask & EBT_IP6_ICMP6) && + (!(ipinfo->bitmask & EBT_IP6_PROTO) || + ipinfo->invflags & EBT_IP6_PROTO || + ipinfo->protocol != IPPROTO_ICMPV6)) + ebt_print_error("For ipv6-icmp filtering the IP protocol must be " + "58 (ipv6-icmp)"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + if (ipinfo->bitmask & EBT_IP6_SOURCE) { + printf("--ip6-src "); + if (ipinfo->invflags & EBT_IP6_SOURCE) + printf("! "); + printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr)); + printf("%s ", ebt_ip6_mask_to_string(&ipinfo->smsk)); + } + if (ipinfo->bitmask & EBT_IP6_DEST) { + printf("--ip6-dst "); + if (ipinfo->invflags & EBT_IP6_DEST) + printf("! "); + printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr)); + printf("%s ", ebt_ip6_mask_to_string(&ipinfo->dmsk)); + } + if (ipinfo->bitmask & EBT_IP6_TCLASS) { + printf("--ip6-tclass "); + if (ipinfo->invflags & EBT_IP6_TCLASS) + printf("! "); + printf("0x%02X ", ipinfo->tclass); + } + if (ipinfo->bitmask & EBT_IP6_PROTO) { + struct protoent *pe; + + printf("--ip6-proto "); + if (ipinfo->invflags & EBT_IP6_PROTO) + printf("! "); + pe = getprotobynumber(ipinfo->protocol); + if (pe == NULL) { + printf("%d ", ipinfo->protocol); + } else { + printf("%s ", pe->p_name); + } + } + if (ipinfo->bitmask & EBT_IP6_SPORT) { + printf("--ip6-sport "); + if (ipinfo->invflags & EBT_IP6_SPORT) + printf("! "); + print_port_range(ipinfo->sport); + } + if (ipinfo->bitmask & EBT_IP6_DPORT) { + printf("--ip6-dport "); + if (ipinfo->invflags & EBT_IP6_DPORT) + printf("! "); + print_port_range(ipinfo->dport); + } + if (ipinfo->bitmask & EBT_IP6_ICMP6) { + printf("--ip6-icmp-type "); + if (ipinfo->invflags & EBT_IP6_ICMP6) + printf("! "); + ebt_print_icmp_type(icmpv6_codes, ARRAY_SIZE(icmpv6_codes), + ipinfo->icmpv6_type, ipinfo->icmpv6_code); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data; + struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data; + + if (ipinfo1->bitmask != ipinfo2->bitmask) + return 0; + if (ipinfo1->invflags != ipinfo2->invflags) + return 0; + if (ipinfo1->bitmask & EBT_IP6_SOURCE) { + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr)) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk)) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_DEST) { + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr)) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk)) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_TCLASS) { + if (ipinfo1->tclass != ipinfo2->tclass) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_PROTO) { + if (ipinfo1->protocol != ipinfo2->protocol) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_SPORT) { + if (ipinfo1->sport[0] != ipinfo2->sport[0] || + ipinfo1->sport[1] != ipinfo2->sport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_DPORT) { + if (ipinfo1->dport[0] != ipinfo2->dport[0] || + ipinfo1->dport[1] != ipinfo2->dport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_ICMP6) { + if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] || + ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] || + ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] || + ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1]) + return 0; + } + return 1; +} + +static struct ebt_u_match ip6_match = +{ + .name = EBT_IP6_MATCH, + .size = sizeof(struct ebt_ip6_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +__attribute__((constructor)) static void extension_init(void) +{ + ebt_register_match(&ip6_match); +} diff --git a/net/ebtables-tiny/src/extensions/ebt_limit.c b/net/ebtables-tiny/src/extensions/ebt_limit.c new file mode 100644 index 0000000..e21bdac --- /dev/null +++ b/net/ebtables-tiny/src/extensions/ebt_limit.c @@ -0,0 +1,218 @@ +/* ebt_limit + * + * Authors: + * Tom Marshall + * + * Mostly copied from iptables' limit match. + * + * September, 2003 + */ + +#include +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +#define EBT_LIMIT_AVG "3/hour" +#define EBT_LIMIT_BURST 5 + +static int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +#define FLAG_LIMIT 0x01 +#define FLAG_LIMIT_BURST 0x02 +#define ARG_LIMIT '1' +#define ARG_LIMIT_BURST '2' + +static const struct option opts[] = +{ + { "limit", required_argument, 0, ARG_LIMIT }, + { "limit-burst", required_argument, 0, ARG_LIMIT_BURST }, + { 0 } +}; + +static void print_help(void) +{ + printf( +"limit options:\n" +"--limit avg : max average match rate: default "EBT_LIMIT_AVG"\n" +" [Packets per second unless followed by \n" +" /sec /minute /hour /day postfixes]\n" +"--limit-burst number : number to match in a burst, -1 < number < 10001,\n" +" default %u\n", EBT_LIMIT_BURST); +} + +static int parse_rate(const char *rate, uint32_t *val) +{ + const char *delim; + uint32_t r; + uint32_t mult = 1; /* Seconds by default. */ + + delim = strchr(rate, '/'); + if (delim) { + if (strlen(delim+1) == 0) + return 0; + + if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) + mult = 1; + else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) + mult = 60; + else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) + mult = 60*60; + else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) + mult = 24*60*60; + else + return 0; + } + r = atoi(rate); + if (!r) + return 0; + + /* This would get mapped to infinite (1/day is minimum they + can specify, so we're ok at that end). */ + if (r / mult > EBT_LIMIT_SCALE) + return 0; + + *val = EBT_LIMIT_SCALE * mult / r; + return 1; +} + +/* Initialize the match. */ +static void init(struct ebt_entry_match *m) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)m->data; + + parse_rate(EBT_LIMIT_AVG, &r->avg); + r->burst = EBT_LIMIT_BURST; +} + +/* FIXME: handle overflow: + if (r->avg*r->burst/r->burst != r->avg) + exit_error(PARAMETER_PROBLEM, + "Sorry: burst too large for that avg rate.\n"); +*/ + +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, + unsigned int *flags, + struct ebt_entry_match **match) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data; + unsigned int num; + + switch(c) { + case ARG_LIMIT: + ebt_check_option2(flags, FLAG_LIMIT); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --limit"); + if (!parse_rate(optarg, &r->avg)) + ebt_print_error2("bad rate `%s'", optarg); + break; + + case ARG_LIMIT_BURST: + ebt_check_option2(flags, FLAG_LIMIT_BURST); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --limit-burst"); + if (string_to_number(optarg, 0, 10000, &num) == -1) + ebt_print_error2("bad --limit-burst `%s'", optarg); + r->burst = num; + break; + + default: + return 0; + } + + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +struct rates +{ + const char *name; + uint32_t mult; +}; + +static struct rates g_rates[] = +{ + { "day", EBT_LIMIT_SCALE*24*60*60 }, + { "hour", EBT_LIMIT_SCALE*60*60 }, + { "min", EBT_LIMIT_SCALE*60 }, + { "sec", EBT_LIMIT_SCALE } +}; + +static void print_rate(uint32_t period) +{ + unsigned int i; + + for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++) + if (period > g_rates[i].mult || + g_rates[i].mult/period < g_rates[i].mult%period) + break; + + printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)match->data; + + printf("--limit "); + print_rate(r->avg); + printf("--limit-burst %u ", r->burst); +} + +static int compare(const struct ebt_entry_match* m1, + const struct ebt_entry_match *m2) +{ + struct ebt_limit_info* li1 = (struct ebt_limit_info*)m1->data; + struct ebt_limit_info* li2 = (struct ebt_limit_info*)m2->data; + + if (li1->avg != li2->avg) + return 0; + + if (li1->burst != li2->burst) + return 0; + + return 1; +} + +static struct ebt_u_match limit_match = +{ + .name = "limit", + .size = sizeof(struct ebt_limit_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +__attribute__((constructor)) static void extension_init(void) +{ + ebt_register_match(&limit_match); +} diff --git a/net/ebtables-tiny/src/extensions/ebt_mark.c b/net/ebtables-tiny/src/extensions/ebt_mark.c new file mode 100644 index 0000000..f7184cb --- /dev/null +++ b/net/ebtables-tiny/src/extensions/ebt_mark.c @@ -0,0 +1,178 @@ +/* ebt_mark + * + * Authors: + * Bart De Schuymer + * + * July, 2002, September 2006 + */ + +#include +#include +#include +#include +#include "../include/ebtables_u.h" +#include + +static int mark_supplied; + +#define MARK_TARGET '1' +#define MARK_SETMARK '2' +#define MARK_ORMARK '3' +#define MARK_ANDMARK '4' +#define MARK_XORMARK '5' +static const struct option opts[] = +{ + { "mark-target" , required_argument, 0, MARK_TARGET }, + /* an oldtime messup, we should have always used the scheme + * -