Project import generated by Copybara. GitOrigin-RevId: 0eb304f92590ea910048317acd02cc81c7b9104b
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19ee962 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,26 @@ +*.a +*.la +*.lo +*.so +*.o +.deps/ +.dirstamp +.libs/ +Makefile +Makefile.in + +/aclocal.m4 +/autom4te.cache/ +/build-aux/ +/config.* +/configure +/libtool +/stamp-h1 + +/ebtables-legacy +/ebtables-legacy-restore +/ebtables-legacy-save +/ebtables-legacy.8 +/ebtablesd +/ebtablesu +/static
diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..4ad2d49 --- /dev/null +++ b/Android.mk
@@ -0,0 +1,62 @@ +LOCAL_PATH:= $(call my-dir) + +cflags := -O2 -g \ + -DPROGNAME=\"ebtables\" \ + -DPROGVERSION=\"2.0.11\" \ + -DPROGDATE=\"December\ 2011\" \ + -DLOCKFILE=\"/var/lib/ebtables/lock\" \ + -Wno-sign-compare -Wno-missing-field-initializers \ + -Wno-ignored-qualifiers -Wno-unused-parameter \ + -Wno-#pragma-messages + +extensions_src_files := \ + extensions/ebt_802_3.c \ + extensions/ebt_AUDIT.c \ + extensions/ebt_among.c \ + extensions/ebt_arp.c \ + extensions/ebt_arpreply.c \ + extensions/ebt_ip.c \ + extensions/ebt_ip6.c \ + extensions/ebt_limit.c \ + extensions/ebt_log.c \ + extensions/ebt_mark.c \ + extensions/ebt_mark_m.c \ + extensions/ebt_nat.c \ + extensions/ebt_nflog.c \ + extensions/ebt_pkttype.c \ + extensions/ebt_redirect.c \ + extensions/ebt_standard.c \ + extensions/ebt_stp.c \ + extensions/ebt_string.c \ + extensions/ebt_ulog.c \ + extensions/ebt_vlan.c \ + extensions/ebtable_broute.c \ + extensions/ebtable_filter.c \ + extensions/ebtable_nat.c \ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + $(extensions_src_files) \ + communication.c \ + ebtables-standalone.c \ + ebtables.c \ + getethertype.c \ + libebtc.c \ + useful_functions.c \ + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/include +LOCAL_CFLAGS := $(cflags) +LOCAL_MODULE := ebtables +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) + + +include $(CLEAR_VARS) +LOCAL_MODULE := ethertypes +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_SRC_FILES := ethertypes +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +include $(BUILD_PREBUILT)
diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..514754e --- /dev/null +++ b/COPYING
@@ -0,0 +1,342 @@ +All code in this package, including the code from the extensions, +is released under the GPL license, which you find hereafter. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..52de4db --- /dev/null +++ b/ChangeLog
@@ -0,0 +1,209 @@ +20111215 + Changelog for v2.0.10-4 + * really fix counter setting bug (thanks to James' persistence) +20111204 + Changelog for v2.0.10-3 + * fix counter setting bug (reported by James Sinclair) +20110710 + Changelog for v2.0.10-2 + * enable compiler optimizations (-O3) + * small changes to remove the compiler warnings due to optimization being + turned on (thanks to Peter Volkov) + * respect LDFLAGS in Makefiles (Peter Volkov) +20110710 + Changelog for v2.0.10-1 + * fix --among-dst-file, which translated to --among-src + (reported by Thierry Watelet) + * fix bug in test_ulog.c example + * Makefile: respect LDFLAGS during ebtables build (Peter Volkov) + * Makefile: create directories to avoid build failure when DESTDIR is + supplied (Peter Volkov) + * incorporate fixes for possible issues found by Coverity analysis + (thanks to Jiri Popelka) + * define __EXPORTED_HEADERS__ to get access to the Linux kernel headers + * extend ebt_ip6 to allow matching on ipv6-icmp types/codes (by Florian + Westphal) + * Print a more useful error message when an update of the kernel table + failed. + * Add --concurrent option, which enables using a file lock to support + concurrent scripts updating the ebtables kernel tables +20100203 + Changelog for v2.0.9-2 + * fix unwanted zeroing of counters in the last user-defined chain + (reported by Jon Lewis) + * fix hidden symbol compilation error when using ld directly + * fix return value checking of creat to give a correct error + message if the atomic file couldn't be created + * correct info in INSTALL about compilation of ulog +20090621 + Changelog for v2.0.9 vs v2.0.8-2 + * added ip6 module for filtering IPv6 traffic (Kuo-Lang Tseng, + Manohar Castelino) + * added --log-ip6 option for logging IPv6 traffic (Kuo-Lang Tseng, + Manohar Castelino) + * added nflog watcher for logging packets to userspace (Peter Warasin) + * bugfix in ebtables.sysv (Michal Soltys) + * bugfix for among match on x86-64 (reported by Pavel Emelyanov) +20061217 + Since last entry: + * fixed a few reported bugs + * ebt_among --among-dst-file and --among-src-file: allow + the list to be given in a file (circumvents command line max. + line length + * ebt_nat --snat-arp: if it's an arp packet, also change the source + address in the arp header + * ebt_mark --mark-or, --mark-xor, --mark-and +20051020 + Since last entry: + * ebtables modules are now located in /usr/lib/ebtables/ + * added '/sbin/service ebtables' support + * added ebtables-save (thanks to Rok Papez <rok.papez@arnes.si>) + and ebtables-restore (the first one a perl script, the second + one written in c (fast)) + * optimized the code for the '-A' command, making ebtables-restore + very fast. + * ebtablesd/ebtablesu is deprecated and not compiled by default + the ebtables-save/ebtables-restore scheme is much better +20050117 + Since last entry: + * added ulog watcher + * made the ebtables code modular (make library functions). + * added the ebtablesd/ebtablesu scheme to allow faster + addition of rules (and to test the modular code). + * some small fixes + * added -c option (initialize counters) + * added -C option (change counters) +20031102 + Since last entry: + * <grzes_at_gnu.univ.gda.pl> added arpreply and among modules + * <tommy_at_home.tig-grr.com> added limit match +20030724 + * added (automatic) Sparc64 support, thanks to Michael Bellion and + Thomas Heinz from hipac.org for providing a test-box. +20030717 + * added stp frames match type +20030713 + * added support for deleting all user-defined chains (-X option + without specified chain) +20030601 + * added --Lmac2 + * <csv_at_bluetail.com> Chris Vitale: basic 802.3/802.2 filtering + (experimental, kernel files are in the CVS) + +20030503 + * added negative rule counter support + * bugfix: bcnt was not updated correctly + * <blancher_at_cartel-securite.fr> Cedric Blancher: add ARP MAC + matching support + * added pkttype match +20030402 + * fixed check bug in ebt_ip.c (report from + joe_judge_at_guardium.com). +20030111 + * fixed problem when removing a chain (report from + ykphuah_at_greenpacket.com). + * Added --help list_extensions which, well, lists the extensions +20021203 + * changed the way to use the atomic operations. It's now possible + to use the EBTABLES_ATOMIC_FILE environment variable, so it's no + longer necessary to explicitly state the file name. See the man. +20021120 + * changed the way of compiling. New releases will now contain their + own set of kernel includes. No more copying of kernel includes to + /usr/include/linux + * added getethertype.c (Nick) and use it. Removed name_to_number() + and number_to_name(). +20021106 + * added possibility to specify a rule number interval when deleting + rules +20021102 + * added ! - option possibility, which is equivalent to - ! option +20021102 + * since last entry: added byte counters and udp/tcp port matching +20020830 + * updated the kernel files for 2.4.20-pre5 and 2.5.32 + * last big cleanup of kernel and userspace code just finished +20020820 + * ARP module bugfix + * IP module bugfix + * nat module bugfix +20020730 + * other things done before 2.0-rc1 that I can think of, + including kernel: + * cache align counters for better smp performance + * simplify snat code + * check for --xxxx-target RETURN on base chain + * cleanup code + * minor bugfixes +20020724 + * code cleanup + * bugfix for --atomic-commit +20020720 + * added mark target+match +20020714 + * added --atomic options +20020710 + * some unlogged changes (due to lazyness) + * added --Lc, --Ln, --Lx +20020625 + * user defined chains support: added -N, -X, -E options. +20020621 + * some unlogged changes (due to lazyness) + * change the output for -L to make it look like it would look when + the user inputs the command. + * try to autoload modules + * some minor bugfixes + * add user defined chains support (without new commands yet, + deliberately) + * comparing rules didn't take the logical devices into account +20020520 + * update help for -s and -d + * add VLAN in ethertypes + * add SYMLINK option for compiling +20020501 + * allow -i and --logical-in in BROUTING + * update the manual page + * rename /etc/etherproto into /etc/ethertypes (seems to be a more + standard name) + * add MAC mask for -s and -d, also added Unicast, Multicast and + Broadcast specification for specifying a (family of) MAC + addresses. +20020427 + * added broute table. + * added redirect target. + * added --redirect-target, --snat-target and --dnat-target options. + * added logical_out and logical_in + * snat bugfix (->size) +20020414 + * fixed some things in the manual. + * fixed -P problem. +20020411 + * -j standard no longer works, is this cryptic? good :) + * lots of beautification. + - made some code smaller + - made everything fit within 80 columns + * fix problems with -i and -o option + * print_memory now prints useful info + * trying to see the tables when ebtables is not loaded in kernel + no longer makes this be seen as a bug. +20020403 + ebtables v2.0 released, changes: + * A complete rewrite, made everything modular. + * Fixed a one year old bug in br_db.c. A similar bug was present + in ebtables.c. It was visible when the number of rules got + bigger (around 90). + * Removed the option to allow/disallow counters. Frames passing + by are always counted now. + * Didn't really add any new functionality. However, it will be + _alot_ easier and prettier to do so now. Feel free to add an + extension yourself. + * There are 4 types of extensions: + - Tables. + - Matches: like iptables has. + - Watchers: these only watch frames that passed all the matches + of the rule. They don't change the frame, nor give a verdict. + The log extension is a watcher. + - Targets. + * user32/kernel64 architectures like the Sparc64 are unsupported. + If you want me to change this, give me access to such a box, + and don't pressure me.
diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c43d95c --- /dev/null +++ b/INSTALL
@@ -0,0 +1,36 @@ +Installation instructions for iptables +====================================== + +ebtables uses the well-known configure(autotools) infrastructure. + + $ ./configure + $ make + # make install + + +Prerequisites +============= + + * no kernel-source required + + * but obviously a compiler, glibc-devel and linux-kernel-headers + (/usr/include/linux) + + +Configuring and compiling +========================= + +./configure [options] + +--prefix= + + The prefix to put all installed files under. It defaults to + /usr/local, so the binaries will go into /usr/local/bin, sbin, + manpages into /usr/local/share/man, etc. + +If you want to enable debugging, use + + ./configure CFLAGS="-ggdb3 -O0" CPPFLAGS="-DEBT_DEBUG" + +(-O0 is used to turn off instruction reordering, which makes debugging +much easier.)
diff --git a/LICENSE b/LICENSE new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/LICENSE
@@ -0,0 +1 @@ +COPYING \ No newline at end of file
diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..6181003 --- /dev/null +++ b/Makefile.am
@@ -0,0 +1,69 @@ +# -*- Makefile -*- + +# For debugging, use ./configure CPPFLAGS=-DEBT_DEBUG CFLAGS="-O0 -ggdb3" + +PROGNAME = ${PACKAGE_NAME} +PROGVERSION = ${PACKAGE_VERSION} +PROGDATE = December\ 2011 +INITDIR = /etc/rc.d/init.d +initddir = ${INITDIR} +sysconfigdir = ${sysconfdir}/sysconfig +EBTD_CMDLINE_MAXLN = 2048 +EBTD_ARGC_MAX = 50 +PIPE_DIR = /tmp/${PACKAGE_NAME}-v${PROGVERSION} +PIPE = ${PIPE_DIR}/ebtablesd_pipe + + +ACLOCAL_AMFLAGS = -I m4 +AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include \ + -DPROGVERSION=\"${PACKAGE_VERSION}\" -DPROGNAME=\"${PACKAGE_NAME}\" \ + -DPROGDATE=\"${PROGDATE}\" \ + -D_PATH_ETHERTYPES=\"${sysconfdir}/ethertypes\" \ + -DLOCKFILE=\"${LOCKFILE}\" \ + -DEBTD_ARGC_MAX=${EBTD_ARGC_MAX} -DEBTD_CMDLINE_MAXLN=${EBTD_CMDLINE_MAXLN} \ + -DEBTD_PIPE=\"${PIPE}\" -DEBTD_PIPE_DIR=\"${PIPE_DIR}\" +AM_CFLAGS = ${regular_CFLAGS} + +sbin_PROGRAMS = ebtables-legacy ebtablesd ebtablesu ebtables-legacy-restore +EXTRA_PROGRAMS = static examples/ulog/test_ulog +sysconf_DATA = ethertypes +sbin_SCRIPTS = ebtables-legacy-save +man8_MANS = ebtables-legacy.8 +lib_LTLIBRARIES = libebtc.la + +libebtc_la_SOURCES = \ + communication.c ebtables.c getethertype.c \ + libebtc.c useful_functions.c \ + extensions/ebt_802_3.c extensions/ebt_among.c extensions/ebt_arp.c \ + extensions/ebt_arpreply.c extensions/ebt_ip.c extensions/ebt_ip6.c \ + extensions/ebt_limit.c extensions/ebt_log.c extensions/ebt_mark.c \ + extensions/ebt_mark_m.c extensions/ebt_nat.c extensions/ebt_nflog.c \ + extensions/ebt_pkttype.c extensions/ebt_redirect.c \ + extensions/ebt_standard.c extensions/ebt_stp.c extensions/ebt_string.c \ + extensions/ebt_ulog.c extensions/ebt_vlan.c extensions/ebt_AUDIT.c \ + extensions/ebtable_broute.c extensions/ebtable_filter.c \ + extensions/ebtable_nat.c +# Make sure ebtables.c can be built twice +libebtc_la_CPPFLAGS = ${AM_CPPFLAGS} +ebtables_legacy_SOURCES = ebtables-standalone.c +ebtables_legacy_LDADD = libebtc.la +ebtablesd_LDADD = libebtc.la +ebtables_legacy_restore_SOURCES = ebtables-restore.c +ebtables_legacy_restore_LDADD = libebtc.la +static_SOURCES = ebtables.c +static_LDFLAGS = -static +static_LDADD = libebtc.la +examples_ulog_test_ulog_SOURCES = examples/ulog/test_ulog.c getethertype.c + +daemon: ebtablesd ebtablesu +exec: ebtables-legacy ebtables-legacy-restore + +CLEANFILES = ebtables-legacy-save ebtables-legacy.8 + +ebtables-legacy-save: ebtables-save.in ${top_builddir}/config.status + ${AM_V_GEN}sed -e 's![@]sbindir@!${sbindir}!g' <$< >$@ + +ebtables-legacy.8: ebtables-legacy.8.in ${top_builddir}/config.status + ${AM_V_GEN}sed -e 's![@]PACKAGE_VERSION@!${PACKAGE_VERSION}!g' \ + -e 's![@]PACKAGE_DATE@!${PROGDATE}!g' \ + -e 's![@]LOCKFILE@!${LOCKFILE}!g' <$< >$@
diff --git a/NOTICE b/NOTICE new file mode 120000 index 0000000..d24842f --- /dev/null +++ b/NOTICE
@@ -0,0 +1 @@ +COPYING \ No newline at end of file
diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..551cb69 --- /dev/null +++ b/OWNERS
@@ -0,0 +1,5 @@ +tanglee@google.com +xdsun@google.com +eliribble@google.com +allenlintwo@google.com +jlevasseur@google.com
diff --git a/THANKS b/THANKS new file mode 100644 index 0000000..4b92ead --- /dev/null +++ b/THANKS
@@ -0,0 +1,9 @@ +Special thanks go out to these early contributors: + +Lennert Buytenhek +Rusty Russel +Harald Welte +Jason Lunz +Tim Gardner +Loïc Minier +Nick Fedchik
diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..a0c4395 --- /dev/null +++ b/autogen.sh
@@ -0,0 +1,4 @@ +#!/bin/sh -e + +autoreconf -fi; +rm -Rf autom4te*.cache;
diff --git a/communication.c b/communication.c new file mode 100644 index 0000000..ba058c0 --- /dev/null +++ b/communication.c
@@ -0,0 +1,772 @@ +/* + * communication.c, v2.0 July 2002 + * + * Author: Bart De Schuymer + * + */ + +/* + * All the userspace/kernel communication is in this file. + * The other code should not have to know anything about the way the + * kernel likes the structure of the table data. + * The other code works with linked lists. So, the translation is done here. + */ + +#include <getopt.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include "include/ebtables_u.h" + +extern char* hooknames[NF_BR_NUMHOOKS]; + +#ifdef KERNEL_64_USERSPACE_32 +#define sparc_cast (uint64_t) +#else +#define sparc_cast +#endif + +int sockfd = -1; + +static int get_sockfd() +{ + int ret = 0; + if (sockfd == -1) { + sockfd = socket(AF_INET, SOCK_RAW, PF_INET); + if (sockfd < 0) { + ebt_print_error("Problem getting a socket, " + "you probably don't have the right " + "permissions"); + ret = -1; + } + } + return ret; +} + +static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl) +{ + struct ebt_replace *new; + struct ebt_u_entry *e; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_entries *entries; + char *p, *base; + int i, j; + unsigned int entries_size = 0, *chain_offsets; + + new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace)); + if (!new) + ebt_print_memory(); + new->valid_hooks = u_repl->valid_hooks; + strcpy(new->name, u_repl->name); + new->nentries = u_repl->nentries; + new->num_counters = u_repl->num_counters; + new->counters = sparc_cast u_repl->counters; + chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int)); + if (!chain_offsets) + ebt_print_memory(); + /* Determine size */ + for (i = 0; i < u_repl->num_chains; i++) { + if (!(entries = u_repl->chains[i])) + continue; + chain_offsets[i] = entries_size; + entries_size += sizeof(struct ebt_entries); + j = 0; + e = entries->entries->next; + while (e != entries->entries) { + j++; + entries_size += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + entries_size += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + w_l = e->w_list; + while (w_l) { + entries_size += w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher); + w_l = w_l->next; + } + entries_size += e->t->target_size + + sizeof(struct ebt_entry_target); + e = e->next; + } + /* A little sanity check */ + if (j != entries->nentries) + ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j, + entries->nentries, entries->name); + } + + new->entries_size = entries_size; + p = (char *)malloc(entries_size); + if (!p) + ebt_print_memory(); + + /* Put everything in one block */ + new->entries = sparc_cast p; + for (i = 0; i < u_repl->num_chains; i++) { + struct ebt_entries *hlp; + + hlp = (struct ebt_entries *)p; + if (!(entries = u_repl->chains[i])) + continue; + if (i < NF_BR_NUMHOOKS) + new->hook_entry[i] = sparc_cast hlp; + hlp->nentries = entries->nentries; + hlp->policy = entries->policy; + strcpy(hlp->name, entries->name); + hlp->counter_offset = entries->counter_offset; + hlp->distinguisher = 0; /* Make the kernel see the light */ + p += sizeof(struct ebt_entries); + e = entries->entries->next; + while (e != entries->entries) { + struct ebt_entry *tmp = (struct ebt_entry *)p; + + tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES; + tmp->invflags = e->invflags; + tmp->ethproto = e->ethproto; + strcpy(tmp->in, e->in); + strcpy(tmp->out, e->out); + strcpy(tmp->logical_in, e->logical_in); + strcpy(tmp->logical_out, e->logical_out); + memcpy(tmp->sourcemac, e->sourcemac, + sizeof(tmp->sourcemac)); + memcpy(tmp->sourcemsk, e->sourcemsk, + sizeof(tmp->sourcemsk)); + memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac)); + memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk)); + + base = p; + p += sizeof(struct ebt_entry); + m_l = e->m_list; + while (m_l) { + memcpy(p, m_l->m, m_l->m->match_size + + sizeof(struct ebt_entry_match)); + p += m_l->m->match_size + + sizeof(struct ebt_entry_match); + m_l = m_l->next; + } + tmp->watchers_offset = p - base; + w_l = e->w_list; + while (w_l) { + memcpy(p, w_l->w, w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher)); + p += w_l->w->watcher_size + + sizeof(struct ebt_entry_watcher); + w_l = w_l->next; + } + tmp->target_offset = p - base; + memcpy(p, e->t, e->t->target_size + + sizeof(struct ebt_entry_target)); + if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { + struct ebt_standard_target *st = + (struct ebt_standard_target *)p; + /* Translate the jump to a udc */ + if (st->verdict >= 0) + st->verdict = chain_offsets + [st->verdict + NF_BR_NUMHOOKS]; + } + p += e->t->target_size + + sizeof(struct ebt_entry_target); + tmp->next_offset = p - base; + e = e->next; + } + } + + /* Sanity check */ + if (p - (char *)new->entries != new->entries_size) + ebt_print_bug("Entries_size bug"); + free(chain_offsets); + return new; +} + +static void store_table_in_file(char *filename, struct ebt_replace *repl) +{ + char *data; + int size; + int fd; + + /* Start from an empty file with the correct priviliges */ + if ((fd = creat(filename, 0600)) == -1) { + ebt_print_error("Couldn't create file %s", filename); + return; + } + + size = sizeof(struct ebt_replace) + repl->entries_size + + repl->nentries * sizeof(struct ebt_counter); + data = (char *)malloc(size); + if (!data) + ebt_print_memory(); + memcpy(data, repl, sizeof(struct ebt_replace)); + memcpy(data + sizeof(struct ebt_replace), (char *)repl->entries, + repl->entries_size); + /* Initialize counters to zero, deliver_counters() can update them */ + memset(data + sizeof(struct ebt_replace) + repl->entries_size, + 0, repl->nentries * sizeof(struct ebt_counter)); + if (write(fd, data, size) != size) + ebt_print_error("Couldn't write everything to file %s", + filename); + close(fd); + free(data); +} + +void ebt_deliver_table(struct ebt_u_replace *u_repl) +{ + socklen_t optlen; + struct ebt_replace *repl; + + /* Translate the struct ebt_u_replace to a struct ebt_replace */ + repl = translate_user2kernel(u_repl); + if (u_repl->filename != NULL) { + store_table_in_file(u_repl->filename, repl); + goto free_repl; + } + /* Give the data to the kernel */ + optlen = sizeof(struct ebt_replace) + repl->entries_size; + if (get_sockfd()) + goto free_repl; + if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen)) + goto free_repl; + if (u_repl->command == 8) { /* The ebtables module may not + * yet be loaded with --atomic-commit */ + ebtables_insmod("ebtables"); + if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, + repl, optlen)) + goto free_repl; + } + + ebt_print_error("Unable to update the kernel. Two possible causes:\n" + "1. Multiple ebtables programs were executing simultaneously. The ebtables\n" + " userspace tool doesn't by default support multiple ebtables programs running\n" + " concurrently. The ebtables option --concurrent or a tool like flock can be\n" + " used to support concurrent scripts that update the ebtables kernel tables.\n" + "2. The kernel doesn't support a certain ebtables extension, consider\n" + " recompiling your kernel or insmod the extension.\n"); +free_repl: + if (repl) { + free(repl->entries); + free(repl); + } +} + +static int store_counters_in_file(char *filename, struct ebt_u_replace *repl) +{ + int size = repl->nentries * sizeof(struct ebt_counter), ret = 0; + unsigned int entries_size; + struct ebt_replace hlp; + FILE *file; + + if (!(file = fopen(filename, "r+b"))) { + ebt_print_error("Could not open file %s", filename); + return -1; + } + /* Find out entries_size and then set the file pointer to the + * counters */ + if (fseek(file, (char *)(&hlp.entries_size) - (char *)(&hlp), SEEK_SET) + || fread(&entries_size, sizeof(char), sizeof(unsigned int), file) != + sizeof(unsigned int) || + fseek(file, entries_size + sizeof(struct ebt_replace), SEEK_SET)) { + ebt_print_error("File %s is corrupt", filename); + ret = -1; + goto close_file; + } + if (fwrite(repl->counters, sizeof(char), size, file) != size) { + ebt_print_error("Could not write everything to file %s", + filename); + ret = -1; + } +close_file: + fclose(file); + return ret; +} + +/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel + * and resets the counterchanges to CNT_NORM */ +void ebt_deliver_counters(struct ebt_u_replace *u_repl) +{ + struct ebt_counter *old, *new, *newcounters; + socklen_t optlen; + struct ebt_replace repl; + struct ebt_cntchanges *cc = u_repl->cc->next, *cc2; + struct ebt_u_entries *entries = NULL; + struct ebt_u_entry *next = NULL; + int i, chainnr = -1; + + if (u_repl->nentries == 0) + return; + + newcounters = (struct ebt_counter *) + malloc(u_repl->nentries * sizeof(struct ebt_counter)); + if (!newcounters) + ebt_print_memory(); + memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter)); + old = u_repl->counters; + new = newcounters; + while (cc != u_repl->cc) { + if (!next || next == entries->entries) { + chainnr++; + while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) || + (next = entries->entries->next) == entries->entries)) + chainnr++; + if (chainnr == u_repl->num_chains) + break; + } + if (next == NULL) + ebt_print_bug("next == NULL"); + if (cc->type == CNT_NORM) { + /* 'Normal' rule, meaning we didn't do anything to it + * So, we just copy */ + *new = *old; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + old++; /* We've used an old counter */ + new++; /* We've set a new counter */ + next = next->next; + } else if (cc->type == CNT_DEL) { + old++; /* Don't use this old counter */ + } else { + if (cc->type == CNT_CHANGE) { + if (cc->change % 3 == 1) + new->pcnt = old->pcnt + next->cnt_surplus.pcnt; + else if (cc->change % 3 == 2) + new->pcnt = old->pcnt - next->cnt_surplus.pcnt; + else + new->pcnt = next->cnt.pcnt; + if (cc->change / 3 == 1) + new->bcnt = old->bcnt + next->cnt_surplus.bcnt; + else if (cc->change / 3 == 2) + new->bcnt = old->bcnt - next->cnt_surplus.bcnt; + else + new->bcnt = next->cnt.bcnt; + } else + *new = next->cnt; + next->cnt = *new; + next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0; + if (cc->type == CNT_ADD) + new++; + else { + old++; + new++; + } + next = next->next; + } + cc = cc->next; + } + + free(u_repl->counters); + u_repl->counters = newcounters; + u_repl->num_counters = u_repl->nentries; + /* Reset the counterchanges to CNT_NORM and delete the unused cc */ + i = 0; + cc = u_repl->cc->next; + while (cc != u_repl->cc) { + if (cc->type == CNT_DEL) { + cc->prev->next = cc->next; + cc->next->prev = cc->prev; + cc2 = cc->next; + free(cc); + cc = cc2; + } else { + cc->type = CNT_NORM; + cc->change = 0; + i++; + cc = cc->next; + } + } + if (i != u_repl->nentries) + ebt_print_bug("i != u_repl->nentries"); + if (u_repl->filename != NULL) { + store_counters_in_file(u_repl->filename, u_repl); + return; + } + optlen = u_repl->nentries * sizeof(struct ebt_counter) + + sizeof(struct ebt_replace); + /* Now put the stuff in the kernel's struct ebt_replace */ + repl.counters = sparc_cast u_repl->counters; + repl.num_counters = u_repl->num_counters; + memcpy(repl.name, u_repl->name, sizeof(repl.name)); + + if (get_sockfd()) + return; + if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen)) + ebt_print_bug("Couldn't update kernel counters"); +} + +static int +ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l) +{ + struct ebt_u_match_list *new; + int ret = 0; + + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + ebt_print_memory(); + new->m = (struct ebt_entry_match *) + malloc(m->match_size + sizeof(struct ebt_entry_match)); + if (!new->m) + ebt_print_memory(); + memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match)); + new->next = NULL; + **l = new; + *l = &new->next; + if (ebt_find_match(new->m->u.name) == NULL) { + ebt_print_error("Kernel match %s unsupported by userspace tool", + new->m->u.name); + ret = -1; + } + return ret; +} + +static int +ebt_translate_watcher(struct ebt_entry_watcher *w, + struct ebt_u_watcher_list ***l) +{ + struct ebt_u_watcher_list *new; + int ret = 0; + + new = (struct ebt_u_watcher_list *) + malloc(sizeof(struct ebt_u_watcher_list)); + if (!new) + ebt_print_memory(); + new->w = (struct ebt_entry_watcher *) + malloc(w->watcher_size + sizeof(struct ebt_entry_watcher)); + if (!new->w) + ebt_print_memory(); + memcpy(new->w, w, w->watcher_size + sizeof(struct ebt_entry_watcher)); + new->next = NULL; + **l = new; + *l = &new->next; + if (ebt_find_watcher(new->w->u.name) == NULL) { + ebt_print_error("Kernel watcher %s unsupported by userspace " + "tool", new->w->u.name); + ret = -1; + } + return ret; +} + +static int +ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt, + int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl, + unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc) +{ + /* An entry */ + if (e->bitmask & EBT_ENTRY_OR_ENTRIES) { + struct ebt_u_entry *new; + struct ebt_u_match_list **m_l; + struct ebt_u_watcher_list **w_l; + struct ebt_entry_target *t; + + new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new) + ebt_print_memory(); + new->bitmask = e->bitmask; + /* + * Plain userspace code doesn't know about + * EBT_ENTRY_OR_ENTRIES + */ + new->bitmask &= ~EBT_ENTRY_OR_ENTRIES; + new->invflags = e->invflags; + new->ethproto = e->ethproto; + strcpy(new->in, e->in); + strcpy(new->out, e->out); + strcpy(new->logical_in, e->logical_in); + strcpy(new->logical_out, e->logical_out); + memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac)); + memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk)); + memcpy(new->destmac, e->destmac, sizeof(new->destmac)); + memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk)); + if (*totalcnt >= u_repl->nentries) + ebt_print_bug("*totalcnt >= u_repl->nentries"); + new->cnt = u_repl->counters[*totalcnt]; + new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0; + new->cc = *cc; + *cc = (*cc)->next; + new->m_list = NULL; + new->w_list = NULL; + new->next = (*u_e)->next; + new->next->prev = new; + (*u_e)->next = new; + new->prev = *u_e; + *u_e = new; + m_l = &new->m_list; + EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l); + w_l = &new->w_list; + EBT_WATCHER_ITERATE(e, ebt_translate_watcher, &w_l); + + t = (struct ebt_entry_target *)(((char *)e) + e->target_offset); + new->t = (struct ebt_entry_target *) + malloc(t->target_size + sizeof(struct ebt_entry_target)); + if (!new->t) + ebt_print_memory(); + if (ebt_find_target(t->u.name) == NULL) { + ebt_print_error("Kernel target %s unsupported by " + "userspace tool", t->u.name); + return -1; + } + memcpy(new->t, t, t->target_size + + sizeof(struct ebt_entry_target)); + /* Deal with jumps to udc */ + if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) { + char *tmp = base; + int verdict = ((struct ebt_standard_target *)t)->verdict; + int i; + + if (verdict >= 0) { + tmp += verdict; + for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++) + if (u_repl->chains[i]->kernel_start == tmp) + break; + if (i == u_repl->num_chains) + ebt_print_bug("Can't find udc for jump"); + ((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS; + } + } + + (*cnt)++; + (*totalcnt)++; + return 0; + } else { /* A new chain */ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + + if (*n != *cnt) + ebt_print_bug("Nr of entries in the chain is wrong"); + *n = entries->nentries; + *cnt = 0; + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + *hook = i; + *u_e = u_repl->chains[*hook]->entries; + return 0; + } +} + +/* Initialize all chain headers */ +static int +ebt_translate_chains(struct ebt_entry *e, int *hook, + struct ebt_u_replace *u_repl, unsigned int valid_hooks) +{ + int i; + struct ebt_entries *entries = (struct ebt_entries *)e; + struct ebt_u_entries *new; + + if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) { + for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++) + if (valid_hooks & (1 << i)) + break; + new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); + if (!new) + ebt_print_memory(); + if (i == u_repl->max_chains) + ebt_double_chains(u_repl); + u_repl->chains[i] = new; + if (i >= NF_BR_NUMHOOKS) + new->kernel_start = (char *)e; + *hook = i; + new->nentries = entries->nentries; + new->policy = entries->policy; + new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new->entries) + ebt_print_memory(); + new->entries->next = new->entries->prev = new->entries; + new->counter_offset = entries->counter_offset; + strcpy(new->name, entries->name); + } + return 0; +} + +static int retrieve_from_file(char *filename, struct ebt_replace *repl, + char command) +{ + FILE *file; + char *hlp = NULL, *entries; + struct ebt_counter *counters; + int size, ret = 0; + + if (!(file = fopen(filename, "r+b"))) { + ebt_print_error("Could not open file %s", filename); + return -1; + } + /* Make sure table name is right if command isn't -L or --atomic-commit */ + if (command != 'L' && command != 8) { + hlp = (char *)malloc(strlen(repl->name) + 1); + if (!hlp) + ebt_print_memory(); + strcpy(hlp, repl->name); + } + if (fread(repl, sizeof(char), sizeof(struct ebt_replace), file) + != sizeof(struct ebt_replace)) { + ebt_print_error("File %s is corrupt", filename); + ret = -1; + goto close_file; + } + if (command != 'L' && command != 8 && strcmp(hlp, repl->name)) { + ebt_print_error("File %s contains wrong table name or is " + "corrupt", filename); + ret = -1; + goto close_file; + } else if (!ebt_find_table(repl->name)) { + ebt_print_error("File %s contains invalid table name", + filename); + ret = -1; + goto close_file; + } + + size = sizeof(struct ebt_replace) + + repl->nentries * sizeof(struct ebt_counter) + repl->entries_size; + fseek(file, 0, SEEK_END); + if (size != ftell(file)) { + ebt_print_error("File %s has wrong size", filename); + ret = -1; + goto close_file; + } + entries = (char *)malloc(repl->entries_size); + if (!entries) + ebt_print_memory(); + repl->entries = sparc_cast entries; + if (repl->nentries) { + counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter)); + repl->counters = sparc_cast counters; + if (!repl->counters) + ebt_print_memory(); + } else + repl->counters = sparc_cast NULL; + /* Copy entries and counters */ + if (fseek(file, sizeof(struct ebt_replace), SEEK_SET) || + fread((char *)repl->entries, sizeof(char), repl->entries_size, file) + != repl->entries_size || + fseek(file, sizeof(struct ebt_replace) + repl->entries_size, + SEEK_SET) + || (repl->counters && fread((char *)repl->counters, sizeof(char), + repl->nentries * sizeof(struct ebt_counter), file) + != repl->nentries * sizeof(struct ebt_counter))) { + ebt_print_error("File %s is corrupt", filename); + free(entries); + repl->entries = NULL; + ret = -1; + } +close_file: + fclose(file); + free(hlp); + return ret; +} + +static int retrieve_from_kernel(struct ebt_replace *repl, char command, + int init) +{ + socklen_t optlen; + int optname; + char *entries; + + optlen = sizeof(struct ebt_replace); + if (get_sockfd()) + return -1; + /* --atomic-init || --init-table */ + if (init) + optname = EBT_SO_GET_INIT_INFO; + else + optname = EBT_SO_GET_INFO; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + return -1; + + if ( !(entries = (char *)malloc(repl->entries_size)) ) + ebt_print_memory(); + repl->entries = sparc_cast entries; + if (repl->nentries) { + struct ebt_counter *counters; + + if (!(counters = (struct ebt_counter *) + malloc(repl->nentries * sizeof(struct ebt_counter))) ) + ebt_print_memory(); + repl->counters = sparc_cast counters; + } + else + repl->counters = sparc_cast NULL; + + /* We want to receive the counters */ + repl->num_counters = repl->nentries; + optlen += repl->entries_size + repl->num_counters * + sizeof(struct ebt_counter); + if (init) + optname = EBT_SO_GET_INIT_ENTRIES; + else + optname = EBT_SO_GET_ENTRIES; + if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen)) + ebt_print_bug("Hmm, what is wrong??? bug#1"); + + return 0; +} + +int ebt_get_table(struct ebt_u_replace *u_repl, int init) +{ + int i, j, k, hook; + struct ebt_replace repl; + struct ebt_u_entry *u_e = NULL; + struct ebt_cntchanges *new_cc = NULL, *cc; + + strcpy(repl.name, u_repl->name); + if (u_repl->filename != NULL) { + if (init) + ebt_print_bug("Getting initial table data from a file is impossible"); + if (retrieve_from_file(u_repl->filename, &repl, u_repl->command)) + return -1; + /* -L with a wrong table name should be dealt with silently */ + strcpy(u_repl->name, repl.name); + } else if (retrieve_from_kernel(&repl, u_repl->command, init)) + return -1; + + /* Translate the struct ebt_replace to a struct ebt_u_replace */ + u_repl->valid_hooks = repl.valid_hooks; + u_repl->nentries = repl.nentries; + u_repl->num_counters = repl.num_counters; + u_repl->counters = repl.counters; + u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!u_repl->cc) + ebt_print_memory(); + u_repl->cc->next = u_repl->cc->prev = u_repl->cc; + cc = u_repl->cc; + for (i = 0; i < repl.nentries; i++) { + new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!new_cc) + ebt_print_memory(); + new_cc->type = CNT_NORM; + new_cc->change = 0; + new_cc->prev = cc; + cc->next = new_cc; + cc = new_cc; + } + if (repl.nentries) { + new_cc->next = u_repl->cc; + u_repl->cc->prev = new_cc; + } + u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *)); + u_repl->max_chains = EBT_ORI_MAX_CHAINS; + hook = -1; + /* FIXME: Clean up when an error is encountered */ + EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains, + &hook, u_repl, u_repl->valid_hooks); + if (hook >= NF_BR_NUMHOOKS) + u_repl->num_chains = hook + 1; + else + u_repl->num_chains = NF_BR_NUMHOOKS; + i = 0; /* Holds the expected nr. of entries for the chain */ + j = 0; /* Holds the up to now counted entries for the chain */ + k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */ + cc = u_repl->cc->next; + hook = -1; + EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size, + ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl, + u_repl->valid_hooks, (char *)repl.entries, &cc); + if (k != u_repl->nentries) + ebt_print_bug("Wrong total nentries"); + free(repl.entries); + return 0; +}
diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..c24ede3 --- /dev/null +++ b/configure.ac
@@ -0,0 +1,26 @@ +AC_INIT([ebtables], [2.0.11]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_MACRO_DIR([m4]) +AC_PROG_INSTALL +AM_INIT_AUTOMAKE([-Wall foreign subdir-objects tar-pax]) +AC_PROG_CC +AM_PROG_CC_C_O +AC_DISABLE_STATIC +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +AM_PROG_LIBTOOL + +AC_ARG_VAR([LOCKFILE], [Custom libebtc lockfile path (default: /var/lib/ebtables/lock)]) +AS_IF([test "x$LOCKFILE" = x], [LOCKFILE="/var/lib/ebtables/lock"]) + +regular_CFLAGS="-Wall -Wunused" +regular_CPPFLAGS="" +case "$host" in + sparc64-*) + regular_CPPFLAGS="$regular_CPPFLAGS -DEBT_MIN_ALIGN=8 -DKERNEL_64_USERSPACE_32";; +esac + +AC_SUBST([regular_CFLAGS]) +AC_SUBST([regular_CPPFLAGS]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT
diff --git a/ebtables-legacy.8.in b/ebtables-legacy.8.in new file mode 100644 index 0000000..3417045 --- /dev/null +++ b/ebtables-legacy.8.in
@@ -0,0 +1,1146 @@ +.TH EBTABLES 8 "@PACKAGE_DATE@" +.\" +.\" Man page written by Bart De Schuymer <bdschuym@pandora.be> +.\" It is based on the iptables man page. +.\" +.\" The man page was edited, February 25th 2003, by +.\" Greg Morgan <" dr_kludge_at_users_sourceforge_net > +.\" +.\" Iptables page by Herve Eychenne March 2000. +.\" +.\" This program is free software; you can redistribute it and/or modify +.\" it under the terms of the GNU General Public License as published by +.\" the Free Software Foundation; either version 2 of the License, or +.\" (at your option) any later version. +.\" +.\" This program is distributed in the hope that it will be useful, +.\" but WITHOUT ANY WARRANTY; without even the implied warranty of +.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +.\" GNU General Public License for more details. +.\" +.\" You should have received a copy of the GNU General Public License +.\" along with this program; if not, write to the Free Software +.\" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.\" +.SH NAME +ebtables-legacy (@PACKAGE_VERSION@) \- Ethernet bridge frame table administration (legacy) +.SH SYNOPSIS +.BR "ebtables " [ -t " table ] " - [ ACDI "] chain rule specification [match extensions] [watcher extensions] target" +.br +.BR "ebtables " [ -t " table ] " -P " chain " ACCEPT " | " DROP " | " RETURN +.br +.BR "ebtables " [ -t " table ] " -F " [chain]" +.br +.BR "ebtables " [ -t " table ] " -Z " [chain]" +.br +.BR "ebtables " [ -t " table ] " -L " [" -Z "] [chain] [ [" --Ln "] | [" --Lx "] ] [" --Lc "] [" --Lmac2 ] +.br +.BR "ebtables " [ -t " table ] " -N " chain [" "-P ACCEPT " | " DROP " | " RETURN" ] +.br +.BR "ebtables " [ -t " table ] " -X " [chain]" +.br +.BR "ebtables " [ -t " table ] " -E " old-chain-name new-chain-name" +.br +.BR "ebtables " [ -t " table ] " --init-table +.br +.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-commit +.br +.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-init +.br +.BR "ebtables " [ -t " table ] [" --atomic-file " file] " --atomic-save +.br + +.SH LEGACY +This tool uses the old xtables/setsockopt framework, and is a legacy version +of ebtables. That means that a new, more modern tool exists with the same +functionality using the nf_tables framework and you are encouraged to migrate now. +The new binaries (known as ebtables-nft and formerly known as ebtables-compat) +uses the same syntax and semantics than this legacy one. + +You can still use this legacy tool. You should probably get some specific +information from your Linux distribution or vendor. +More docs are available at https://wiki.nftables.org + +.SH DESCRIPTION +.B ebtables +is an application program used to set up and maintain the +tables of rules (inside the Linux kernel) that inspect +Ethernet frames. +It is analogous to the +.B iptables +application, but less complicated, due to the fact that the Ethernet protocol +is much simpler than the IP protocol. +.SS CHAINS +There are three ebtables tables with built-in chains in the +Linux kernel. These tables are used to divide functionality into +different sets of rules. Each set of rules is called a chain. +Each chain is an ordered list of rules that can match Ethernet frames. If a +rule matches an Ethernet frame, then a processing specification tells +what to do with that matching frame. The processing specification is +called a 'target'. However, if the frame does not match the current +rule in the chain, then the next rule in the chain is examined and so forth. +The user can create new (user-defined) chains that can be used as the 'target' +of a rule. User-defined chains are very useful to get better performance +over the linear traversal of the rules and are also essential for structuring +the filtering rules into well-organized and maintainable sets of rules. +.SS TARGETS +A firewall rule specifies criteria for an Ethernet frame and a frame +processing specification called a target. When a frame matches a rule, +then the next action performed by the kernel is specified by the target. +The target can be one of these values: +.BR ACCEPT , +.BR DROP , +.BR CONTINUE , +.BR RETURN , +an 'extension' (see below) or a jump to a user-defined chain. +.PP +.B ACCEPT +means to let the frame through. +.B DROP +means the frame has to be dropped. In the +.BR BROUTING " chain however, the " ACCEPT " and " DROP " target have different" +meanings (see the info provided for the +.BR -t " option)." +.B CONTINUE +means the next rule has to be checked. This can be handy, f.e., to know how many +frames pass a certain point in the chain, to log those frames or to apply multiple +targets on a frame. +.B RETURN +means stop traversing this chain and resume at the next rule in the +previous (calling) chain. +For the extension targets please refer to the +.B "TARGET EXTENSIONS" +section of this man page. +.SS TABLES +As stated earlier, there are three ebtables tables in the Linux +kernel. The table names are +.BR filter ", " nat " and " broute . +Of these three tables, +the filter table is the default table that the command operates on. +If you are working with the filter table, then you can drop the '-t filter' +argument to the ebtables command. However, you will need to provide +the -t argument for the other two tables. Moreover, the -t argument must be the +first argument on the ebtables command line, if used. +.TP +.B "-t, --table" +.br +.B filter +is the default table and contains three built-in chains: +.B INPUT +(for frames destined for the bridge itself, on the level of the MAC destination address), +.B OUTPUT +(for locally-generated or (b)routed frames) and +.B FORWARD +(for frames being forwarded by the bridge). +.br +.br +.B nat +is mostly used to change the mac addresses and contains three built-in chains: +.B PREROUTING +(for altering frames as soon as they come in), +.B OUTPUT +(for altering locally generated or (b)routed frames before they are bridged) and +.B POSTROUTING +(for altering frames as they are about to go out). A small note on the naming +of chains PREROUTING and POSTROUTING: it would be more accurate to call them +PREFORWARDING and POSTFORWARDING, but for all those who come from the +iptables world to ebtables it is easier to have the same names. Note that you +can change the name +.BR "" ( -E ) +if you don't like the default. +.br +.br +.B broute +is used to make a brouter, it has one built-in chain: +.BR BROUTING . +The targets +.BR DROP " and " ACCEPT +have a special meaning in the broute table (these names are used instead of +more descriptive names to keep the implementation generic). +.B DROP +actually means the frame has to be routed, while +.B ACCEPT +means the frame has to be bridged. The +.B BROUTING +chain is traversed very early. However, it is only traversed by frames entering on +a bridge port that is in forwarding state. Normally those frames +would be bridged, but you can decide otherwise here. The +.B redirect +target is very handy here. +.SH EBTABLES COMMAND LINE ARGUMENTS +After the initial ebtables '-t table' command line argument, the remaining +arguments can be divided into several groups. These groups +are commands, miscellaneous commands, rule specifications, match extensions, +watcher extensions and target extensions. +.SS COMMANDS +The ebtables command arguments specify the actions to perform on the table +defined with the -t argument. If you do not use the -t argument to name +a table, the commands apply to the default filter table. +Only one command may be used on the command line at a time, except when +the commands +.BR -L " and " -Z +are combined, the commands +.BR -N " and " -P +are combined, or when +.B --atomic-file +is used. +.TP +.B "-A, --append" +Append a rule to the end of the selected chain. +.TP +.B "-D, --delete" +Delete the specified rule or rules from the selected chain. There are two ways to +use this command. The first is by specifying an interval of rule numbers +to delete (directly after +.BR -D ). +Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use +.B -L --Ln +to list the rules with their rule number). When \fIend_nr\fP is omitted, all rules starting +from \fIstart_nr\fP are deleted. Using negative numbers is allowed, for more +details about using negative numbers, see the +.B -I +command. The second usage is by +specifying the complete rule as it would have been specified when it was added. Only +the first encountered rule that is the same as this specified rule, in other +words the matching rule with the lowest (positive) rule number, is deleted. +.TP +.B "-C, --change-counters" +Change the counters of the specified rule or rules from the selected chain. There are two ways to +use this command. The first is by specifying an interval of rule numbers +to do the changes on (directly after +.BR -C ). +Syntax: \fIstart_nr\fP[\fI:end_nr\fP] (use +.B -L --Ln +to list the rules with their rule number). The details are the same as for the +.BR -D " command. The second usage is by" +specifying the complete rule as it would have been specified when it was added. Only +the counters of the first encountered rule that is the same as this specified rule, in other +words the matching rule with the lowest (positive) rule number, are changed. +In the first usage, the counters are specified directly after the interval specification, +in the second usage directly after +.BR -C . +First the packet counter is specified, then the byte counter. If the specified counters start +with a '+', the counter values are added to the respective current counter values. +If the specified counters start with a '-', the counter values are decreased from the respective +current counter values. No bounds checking is done. If the counters don't start with '+' or '-', +the current counters are changed to the specified counters. +.TP +.B "-I, --insert" +Insert the specified rule into the selected chain at the specified rule number. If the +rule number is not specified, the rule is added at the head of the chain. +If the current number of rules equals +.IR N , +then the specified number can be +between +.IR -N " and " N+1 . +For a positive number +.IR i , +it holds that +.IR i " and " i-N-1 +specify the same place in the chain where the rule should be inserted. The rule number +0 specifies the place past the last rule in the chain and using this number is therefore +equivalent to using the +.BR -A " command." +Rule numbers structly smaller than 0 can be useful when more than one rule needs to be inserted +in a chain. +.TP +.B "-P, --policy" +Set the policy for the chain to the given target. The policy can be +.BR ACCEPT ", " DROP " or " RETURN . +.TP +.B "-F, --flush" +Flush the selected chain. If no chain is selected, then every chain will be +flushed. Flushing a chain does not change the policy of the +chain, however. +.TP +.B "-Z, --zero" +Set the counters of the selected chain to zero. If no chain is selected, all the counters +are set to zero. The +.B "-Z" +command can be used in conjunction with the +.B "-L" +command. +When both the +.B "-Z" +and +.B "-L" +commands are used together in this way, the rule counters are printed on the screen +before they are set to zero. +.TP +.B "-L, --list" +List all rules in the selected chain. If no chain is selected, all chains +are listed. +.br +The following options change the output of the +.B "-L" +command. +.br +.B "--Ln" +.br +Places the rule number in front of every rule. This option is incompatible with the +.BR --Lx " option." +.br +.B "--Lc" +.br +Shows the counters at the end of each rule displayed by the +.B "-L" +command. Both a frame counter (pcnt) and a byte counter (bcnt) are displayed. +The frame counter shows how many frames have matched the specific rule, the byte +counter shows the sum of the frame sizes of these matching frames. Using this option +.BR "" "in combination with the " --Lx " option causes the counters to be written out" +.BR "" "in the '" -c " <pcnt> <bcnt>' option format." +.br +.B "--Lx" +.br +Changes the output so that it produces a set of ebtables commands that construct +the contents of the chain, when specified. +If no chain is specified, ebtables commands to construct the contents of the +table are given, including commands for creating the user-defined chains (if any). +You can use this set of commands in an ebtables boot or reload +script. For example the output could be used at system startup. +The +.B "--Lx" +option is incompatible with the +.B "--Ln" +listing option. Using the +.BR --Lx " option together with the " --Lc " option will cause the counters to be written out" +.BR "" "in the '" -c " <pcnt> <bcnt>' option format." +.br +.B "--Lmac2" +.br +Shows all MAC addresses with the same length, adding leading zeroes +if necessary. The default representation omits leading zeroes in the addresses. +.TP +.B "-N, --new-chain" +Create a new user-defined chain with the given name. The number of +user-defined chains is limited only by the number of possible chain names. +A user-defined chain name has a maximum +length of 31 characters. The standard policy of the user-defined chain is +ACCEPT. The policy of the new chain can be initialized to a different standard +target by using the +.B -P +command together with the +.B -N +command. In this case, the chain name does not have to be specified for the +.B -P +command. +.TP +.B "-X, --delete-chain" +Delete the specified user-defined chain. There must be no remaining references (jumps) +to the specified chain, otherwise ebtables will refuse to delete it. If no chain is +specified, all user-defined chains that aren't referenced will be removed. +.TP +.B "-E, --rename-chain" +Rename the specified chain to a new name. Besides renaming a user-defined +chain, you can rename a standard chain to a name that suits your +taste. For example, if you like PREFORWARDING more than PREROUTING, +then you can use the -E command to rename the PREROUTING chain. If you do +rename one of the standard ebtables chain names, please be sure to mention +this fact should you post a question on the ebtables mailing lists. +It would be wise to use the standard name in your post. Renaming a standard +ebtables chain in this fashion has no effect on the structure or functioning +of the ebtables kernel table. +.TP +.B "--init-table" +Replace the current table data by the initial table data. +.TP +.B "--atomic-init" +Copy the kernel's initial data of the table to the specified +file. This can be used as the first action, after which rules are added +to the file. The file can be specified using the +.B --atomic-file +command or through the +.IR EBTABLES_ATOMIC_FILE " environment variable." +.TP +.B "--atomic-save" +Copy the kernel's current data of the table to the specified +file. This can be used as the first action, after which rules are added +to the file. The file can be specified using the +.B --atomic-file +command or through the +.IR EBTABLES_ATOMIC_FILE " environment variable." +.TP +.B "--atomic-commit" +Replace the kernel table data with the data contained in the specified +file. This is a useful command that allows you to load all your rules of a +certain table into the kernel at once, saving the kernel a lot of precious +time and allowing atomic updates of the tables. The file which contains +the table data is constructed by using either the +.B "--atomic-init" +or the +.B "--atomic-save" +command to generate a starting file. After that, using the +.B "--atomic-file" +command when constructing rules or setting the +.IR EBTABLES_ATOMIC_FILE " environment variable" +allows you to extend the file and build the complete table before +committing it to the kernel. This command can be very useful in boot scripts +to populate the ebtables tables in a fast way. +.SS MISCELLANOUS COMMANDS +.TP +.B "-V, --version" +Show the version of the ebtables userspace program. +.TP +.BR "-h, --help " "[\fIlist of module names\fP]" +Give a brief description of the command syntax. Here you can also specify +names of extensions and ebtables will try to write help about those +extensions. E.g. +.IR "ebtables -h snat log ip arp" . +Specify +.I list_extensions +to list all extensions supported by the userspace +utility. +.TP +.BR "-j, --jump " "\fItarget\fP" +The target of the rule. This is one of the following values: +.BR ACCEPT , +.BR DROP , +.BR CONTINUE , +.BR RETURN , +a target extension (see +.BR "TARGET EXTENSIONS" ")" +or a user-defined chain name. +.TP +.B --atomic-file "\fIfile\fP" +Let the command operate on the specified +.IR file . +The data of the table to +operate on will be extracted from the file and the result of the operation +will be saved back into the file. If specified, this option should come +before the command specification. An alternative that should be preferred, +is setting the +.IR EBTABLES_ATOMIC_FILE " environment variable." +.TP +.B -M, --modprobe "\fIprogram\fP" +When talking to the kernel, use this +.I program +to try to automatically load missing kernel modules. +.TP +.B --concurrent +Use a file lock to support concurrent scripts updating the ebtables kernel tables. + +.SS +RULE SPECIFICATIONS +The following command line arguments make up a rule specification (as used +in the add and delete commands). A "!" option before the specification +inverts the test for that specification. Apart from these standard rule +specifications there are some other command line arguments of interest. +See both the +.BR "MATCH EXTENSIONS" +and the +.BR "WATCHER EXTENSIONS" +below. +.TP +.BR "-p, --protocol " "[!] \fIprotocol\fP" +The protocol that was responsible for creating the frame. This can be a +hexadecimal number, above +.IR 0x0600 , +a name (e.g. +.I ARP +) or +.BR LENGTH . +The protocol field of the Ethernet frame can be used to denote the +length of the header (802.2/802.3 networks). When the value of that field is +below or equals +.IR 0x0600 , +the value equals the size of the header and shouldn't be used as a +protocol number. Instead, all frames where the protocol field is used as +the length field are assumed to be of the same 'protocol'. The protocol +name used in ebtables for these frames is +.BR LENGTH . +.br +The file +.B /etc/ethertypes +can be used to show readable +characters instead of hexadecimal numbers for the protocols. For example, +.I 0x0800 +will be represented by +.IR IPV4 . +The use of this file is not case sensitive. +See that file for more information. The flag +.B --proto +is an alias for this option. +.TP +.BR "-i, --in-interface " "[!] \fIname\fP" +The interface (bridge port) via which a frame is received (this option is useful in the +.BR INPUT , +.BR FORWARD , +.BR PREROUTING " and " BROUTING +chains). If the interface name ends with '+', then +any interface name that begins with this name (disregarding '+') will match. +The flag +.B --in-if +is an alias for this option. +.TP +.BR "--logical-in " "[!] \fIname\fP" +The (logical) bridge interface via which a frame is received (this option is useful in the +.BR INPUT , +.BR FORWARD , +.BR PREROUTING " and " BROUTING +chains). +If the interface name ends with '+', then +any interface name that begins with this name (disregarding '+') will match. +.TP +.BR "-o, --out-interface " "[!] \fIname\fP" +The interface (bridge port) via which a frame is going to be sent (this option is useful in the +.BR OUTPUT , +.B FORWARD +and +.B POSTROUTING +chains). If the interface name ends with '+', then +any interface name that begins with this name (disregarding '+') will match. +The flag +.B --out-if +is an alias for this option. +.TP +.BR "--logical-out " "[!] \fIname\fP" +The (logical) bridge interface via which a frame is going to be sent (this option +is useful in the +.BR OUTPUT , +.B FORWARD +and +.B POSTROUTING +chains). +If the interface name ends with '+', then +any interface name that begins with this name (disregarding '+') will match. +.TP +.BR "-s, --source " "[!] \fIaddress\fP[/\fImask\fP]" +The source MAC address. Both mask and address are written as 6 hexadecimal +numbers separated by colons. Alternatively one can specify Unicast, +Multicast, Broadcast or BGA (Bridge Group Address): +.br +.IR "Unicast" "=00:00:00:00:00:00/01:00:00:00:00:00," +.IR "Multicast" "=01:00:00:00:00:00/01:00:00:00:00:00," +.IR "Broadcast" "=ff:ff:ff:ff:ff:ff/ff:ff:ff:ff:ff:ff or" +.IR "BGA" "=01:80:c2:00:00:00/ff:ff:ff:ff:ff:ff." +Note that a broadcast +address will also match the multicast specification. The flag +.B --src +is an alias for this option. +.TP +.BR "-d, --destination " "[!] \fIaddress\fP[/\fImask\fP]" +The destination MAC address. See +.B -s +(above) for more details on MAC addresses. The flag +.B --dst +is an alias for this option. +.TP +.BR "-c, --set-counter " "\fIpcnt bcnt\fP" +If used with +.BR -A " or " -I ", then the packet and byte counters of the new rule will be set to +.IR pcnt ", resp. " bcnt ". +If used with the +.BR -C " or " -D " commands, only rules with a packet and byte count equal to" +.IR pcnt ", resp. " bcnt " will match." + +.SS MATCH EXTENSIONS +Ebtables extensions are dynamically loaded into the userspace tool, +there is therefore no need to explicitly load them with a +-m option like is done in iptables. +These extensions deal with functionality supported by kernel modules supplemental to +the core ebtables code. +.SS 802_3 +Specify 802.3 DSAP/SSAP fields or SNAP type. The protocol must be specified as +.IR "LENGTH " "(see the option " " -p " above). +.TP +.BR "--802_3-sap " "[!] \fIsap\fP" +DSAP and SSAP are two one byte 802.3 fields. The bytes are always +equal, so only one byte (hexadecimal) is needed as an argument. +.TP +.BR "--802_3-type " "[!] \fItype\fP" +If the 802.3 DSAP and SSAP values are 0xaa then the SNAP type field must +be consulted to determine the payload protocol. This is a two byte +(hexadecimal) argument. Only 802.3 frames with DSAP/SSAP 0xaa are +checked for type. +.SS among +Match a MAC address or MAC/IP address pair versus a list of MAC addresses +and MAC/IP address pairs. +A list entry has the following format: +.IR xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip][,] ". Multiple" +list entries are separated by a comma, specifying an IP address corresponding to +the MAC address is optional. Multiple MAC/IP address pairs with the same MAC address +but different IP address (and vice versa) can be specified. If the MAC address doesn't +match any entry from the list, the frame doesn't match the rule (unless "!" was used). +.TP +.BR "--among-dst " "[!] \fIlist\fP" +Compare the MAC destination to the given list. If the Ethernet frame has type +.IR IPv4 " or " ARP , +then comparison with MAC/IP destination address pairs from the +list is possible. +.TP +.BR "--among-src " "[!] \fIlist\fP" +Compare the MAC source to the given list. If the Ethernet frame has type +.IR IPv4 " or " ARP , +then comparison with MAC/IP source address pairs from the list +is possible. +.TP +.BR "--among-dst-file " "[!] \fIfile\fP" +Same as +.BR --among-dst " but the list is read in from the specified file." +.TP +.BR "--among-src-file " "[!] \fIfile\fP" +Same as +.BR --among-src " but the list is read in from the specified file." +.SS arp +Specify (R)ARP fields. The protocol must be specified as +.IR ARP " or " RARP . +.TP +.BR "--arp-opcode " "[!] \fIopcode\fP" +The (R)ARP opcode (decimal or a string, for more details see +.BR "ebtables -h arp" ). +.TP +.BR "--arp-htype " "[!] \fIhardware type\fP" +The hardware type, this can be a decimal or the string +.I Ethernet +(which sets +.I type +to 1). Most (R)ARP packets have Eternet as hardware type. +.TP +.BR "--arp-ptype " "[!] \fIprotocol type\fP" +The protocol type for which the (r)arp is used (hexadecimal or the string +.IR IPv4 , +denoting 0x0800). +Most (R)ARP packets have protocol type IPv4. +.TP +.BR "--arp-ip-src " "[!] \fIaddress\fP[/\fImask\fP]" +The (R)ARP IP source address specification. +.TP +.BR "--arp-ip-dst " "[!] \fIaddress\fP[/\fImask\fP]" +The (R)ARP IP destination address specification. +.TP +.BR "--arp-mac-src " "[!] \fIaddress\fP[/\fImask\fP]" +The (R)ARP MAC source address specification. +.TP +.BR "--arp-mac-dst " "[!] \fIaddress\fP[/\fImask\fP]" +The (R)ARP MAC destination address specification. +.TP +.BR "" "[!]" " --arp-gratuitous" +Checks for ARP gratuitous packets: checks equality of IPv4 source +address and IPv4 destination address inside the ARP header. +.SS ip +Specify IPv4 fields. The protocol must be specified as +.IR IPv4 . +.TP +.BR "--ip-source " "[!] \fIaddress\fP[/\fImask\fP]" +The source IP address. +The flag +.B --ip-src +is an alias for this option. +.TP +.BR "--ip-destination " "[!] \fIaddress\fP[/\fImask\fP]" +The destination IP address. +The flag +.B --ip-dst +is an alias for this option. +.TP +.BR "--ip-tos " "[!] \fItos\fP" +The IP type of service, in hexadecimal numbers. +.BR IPv4 . +.TP +.BR "--ip-protocol " "[!] \fIprotocol\fP" +The IP protocol. +The flag +.B --ip-proto +is an alias for this option. +.TP +.BR "--ip-source-port " "[!] \fIport1\fP[:\fIport2\fP]" +The source port or port range for the IP protocols 6 (TCP), 17 +(UDP), 33 (DCCP) or 132 (SCTP). The +.B --ip-protocol +option must be specified as +.IR TCP ", " UDP ", " DCCP " or " SCTP . +If +.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used." +The flag +.B --ip-sport +is an alias for this option. +.TP +.BR "--ip-destination-port " "[!] \fIport1\fP[:\fIport2\fP]" +The destination port or port range for ip protocols 6 (TCP), 17 +(UDP), 33 (DCCP) or 132 (SCTP). The +.B --ip-protocol +option must be specified as +.IR TCP ", " UDP ", " DCCP " or " SCTP . +If +.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used." +The flag +.B --ip-dport +is an alias for this option. +.SS ip6 +Specify IPv6 fields. The protocol must be specified as +.IR IPv6 . +.TP +.BR "--ip6-source " "[!] \fIaddress\fP[/\fImask\fP]" +The source IPv6 address. +The flag +.B --ip6-src +is an alias for this option. +.TP +.BR "--ip6-destination " "[!] \fIaddress\fP[/\fImask\fP]" +The destination IPv6 address. +The flag +.B --ip6-dst +is an alias for this option. +.TP +.BR "--ip6-tclass " "[!] \fItclass\fP" +The IPv6 traffic class, in hexadecimal numbers. +.TP +.BR "--ip6-protocol " "[!] \fIprotocol\fP" +The IP protocol. +The flag +.B --ip6-proto +is an alias for this option. +.TP +.BR "--ip6-source-port " "[!] \fIport1\fP[:\fIport2\fP]" +The source port or port range for the IPv6 protocols 6 (TCP), 17 +(UDP), 33 (DCCP) or 132 (SCTP). The +.B --ip6-protocol +option must be specified as +.IR TCP ", " UDP ", " DCCP " or " SCTP . +If +.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used." +The flag +.B --ip6-sport +is an alias for this option. +.TP +.BR "--ip6-destination-port " "[!] \fIport1\fP[:\fIport2\fP]" +The destination port or port range for IPv6 protocols 6 (TCP), 17 +(UDP), 33 (DCCP) or 132 (SCTP). The +.B --ip6-protocol +option must be specified as +.IR TCP ", " UDP ", " DCCP " or " SCTP . +If +.IR port1 " is omitted, " 0:port2 " is used; if " port2 " is omitted but a colon is specified, " port1:65535 " is used." +The flag +.B --ip6-dport +is an alias for this option. +.TP +.BR "--ip6-icmp-type " "[!] {\fItype\fP[:\fItype\fP]/\fIcode\fP[:\fIcode\fP]|\fItypename\fP}" +Specify ipv6\-icmp type and code to match. +Ranges for both type and code are supported. Type and code are +separated by a slash. Valid numbers for type and range are 0 to 255. +To match a single type including all valid codes, symbolic names can +be used instead of numbers. The list of known type names is shown by the command +.nf + ebtables \-\-help ip6 +.fi +This option is only valid for \-\-ip6-prococol ipv6-icmp. +.SS limit +This module matches at a limited rate using a token bucket filter. +A rule using this extension will match until this limit is reached. +It can be used with the +.B --log +watcher to give limited logging, for example. Its use is the same +as the limit match of iptables. +.TP +.BR "--limit " "[\fIvalue\fP]" +Maximum average matching rate: specified as a number, with an optional +.IR /second ", " /minute ", " /hour ", or " /day " suffix; the default is " 3/hour . +.TP +.BR "--limit-burst " "[\fInumber\fP]" +Maximum initial number of packets to match: this number gets recharged by +one every time the limit specified above is not reached, up to this +number; the default is +.IR 5 . +.SS mark_m +.TP +.BR "--mark " "[!] [\fIvalue\fP][/\fImask\fP]" +Matches frames with the given unsigned mark value. If a +.IR value " and " mask " are specified, the logical AND of the mark value of the frame and" +the user-specified +.IR mask " is taken before comparing it with the" +user-specified mark +.IR value ". When only a mark " +.IR value " is specified, the packet" +only matches when the mark value of the frame equals the user-specified +mark +.IR value . +If only a +.IR mask " is specified, the logical" +AND of the mark value of the frame and the user-specified +.IR mask " is taken and the frame matches when the result of this logical AND is" +non-zero. Only specifying a +.IR mask " is useful to match multiple mark values." +.SS pkttype +.TP +.BR "--pkttype-type " "[!] \fItype\fP" +Matches on the Ethernet "class" of the frame, which is determined by the +generic networking code. Possible values: +.IR broadcast " (MAC destination is the broadcast address)," +.IR multicast " (MAC destination is a multicast address)," +.IR host " (MAC destination is the receiving network device), or " +.IR otherhost " (none of the above)." +.SS stp +Specify stp BPDU (bridge protocol data unit) fields. The destination +address +.BR "" ( -d ") must be specified as the bridge group address" +.IR "" ( BGA ). +For all options for which a range of values can be specified, it holds that +if the lower bound is omitted (but the colon is not), then the lowest possible lower bound +for that option is used, while if the upper bound is omitted (but the colon again is not), the +highest possible upper bound for that option is used. +.TP +.BR "--stp-type " "[!] \fItype\fP" +The BPDU type (0-255), recognized non-numerical types are +.IR config ", denoting a configuration BPDU (=0), and" +.IR tcn ", denothing a topology change notification BPDU (=128)." +.TP +.BR "--stp-flags " "[!] \fIflag\fP" +The BPDU flag (0-255), recognized non-numerical flags are +.IR topology-change ", denoting the topology change flag (=1), and" +.IR topology-change-ack ", denoting the topology change acknowledgement flag (=128)." +.TP +.BR "--stp-root-prio " "[!] [\fIprio\fP][:\fIprio\fP]" +The root priority (0-65535) range. +.TP +.BR "--stp-root-addr " "[!] [\fIaddress\fP][/\fImask\fP]" +The root mac address, see the option +.BR -s " for more details." +.TP +.BR "--stp-root-cost " "[!] [\fIcost\fP][:\fIcost\fP]" +The root path cost (0-4294967295) range. +.TP +.BR "--stp-sender-prio " "[!] [\fIprio\fP][:\fIprio\fP]" +The BPDU's sender priority (0-65535) range. +.TP +.BR "--stp-sender-addr " "[!] [\fIaddress\fP][/\fImask\fP]" +The BPDU's sender mac address, see the option +.BR -s " for more details." +.TP +.BR "--stp-port " "[!] [\fIport\fP][:\fIport\fP]" +The port identifier (0-65535) range. +.TP +.BR "--stp-msg-age " "[!] [\fIage\fP][:\fIage\fP]" +The message age timer (0-65535) range. +.TP +.BR "--stp-max-age " "[!] [\fIage\fP][:\fIage\fP]" +The max age timer (0-65535) range. +.TP +.BR "--stp-hello-time " "[!] [\fItime\fP][:\fItime\fP]" +The hello time timer (0-65535) range. +.TP +.BR "--stp-forward-delay " "[!] [\fIdelay\fP][:\fIdelay\fP]" +The forward delay timer (0-65535) range. +.SS string +This module matches on a given string using some pattern matching strategy. +.TP +.BR "--string-algo " "\fIalgorithm\fP" +The pattern matching strategy. (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris) +.TP +.BR "--string-from " "\fIoffset\fP" +The lowest offset from which a match can start. (default: 0) +.TP +.BR "--string-to " "\fIoffset\fP" +The highest offset from which a match can start. (default: size of frame) +.TP +.BR "--string " "[!] \fIpattern\fP" +Matches the given pattern. +.TP +.BR "--string-hex " "[!] \fIpattern\fP" +Matches the given pattern in hex notation, e.g. '|0D 0A|', '|0D0A|', 'www|09|netfilter|03|org|00|' +.TP +.BR "--string-icase" +Ignore case when searching. +.SS vlan +Specify 802.1Q Tag Control Information fields. +The protocol must be specified as +.IR 802_1Q " (0x8100)." +.TP +.BR "--vlan-id " "[!] \fIid\fP" +The VLAN identifier field (VID). Decimal number from 0 to 4095. +.TP +.BR "--vlan-prio " "[!] \fIprio\fP" +The user priority field, a decimal number from 0 to 7. +The VID should be set to 0 ("null VID") or unspecified +(in the latter case the VID is deliberately set to 0). +.TP +.BR "--vlan-encap " "[!] \fItype\fP" +The encapsulated Ethernet frame type/length. +Specified as a hexadecimal +number from 0x0000 to 0xFFFF or as a symbolic name +from +.BR /etc/ethertypes . + +.SS WATCHER EXTENSIONS +Watchers only look at frames passing by, they don't modify them nor decide +to accept the frames or not. These watchers only +see the frame if the frame matches the rule, and they see it before the +target is executed. +.SS log +The log watcher writes descriptive data about a frame to the syslog. +.TP +.B "--log" +.br +Log with the default loggin options: log-level= +.IR info , +log-prefix="", no ip logging, no arp logging. +.TP +.B --log-level "\fIlevel\fP" +.br +Defines the logging level. For the possible values, see +.BR "ebtables -h log" . +The default level is +.IR info . +.TP +.BR --log-prefix " \fItext\fP" +.br +Defines the prefix +.I text +to be printed at the beginning of the line with the logging information. +.TP +.B --log-ip +.br +Will log the ip information when a frame made by the ip protocol matches +the rule. The default is no ip information logging. +.TP +.B --log-ip6 +.br +Will log the ipv6 information when a frame made by the ipv6 protocol matches +the rule. The default is no ipv6 information logging. +.TP +.B --log-arp +.br +Will log the (r)arp information when a frame made by the (r)arp protocols +matches the rule. The default is no (r)arp information logging. +.SS nflog +The nflog watcher passes the packet to the loaded logging backend +in order to log the packet. This is usually used in combination with +nfnetlink_log as logging backend, which will multicast the packet +through a +.IR netlink +socket to the specified multicast group. One or more userspace processes +may subscribe to the group to receive the packets. +.TP +.B "--nflog" +.br +Log with the default logging options +.TP +.B --nflog-group "\fInlgroup\fP" +.br +The netlink group (1 - 2^32-1) to which packets are (only applicable for +nfnetlink_log). The default value is 1. +.TP +.B --nflog-prefix "\fIprefix\fP" +.br +A prefix string to include in the log message, up to 30 characters +long, useful for distinguishing messages in the logs. +.TP +.B --nflog-range "\fIsize\fP" +.br +The number of bytes to be copied to userspace (only applicable for +nfnetlink_log). nfnetlink_log instances may specify their own +range, this option overrides it. +.TP +.B --nflog-threshold "\fIsize\fP" +.br +Number of packets to queue inside the kernel before sending them +to userspace (only applicable for nfnetlink_log). Higher values +result in less overhead per packet, but increase delay until the +packets reach userspace. The default value is 1. +.SS ulog +The ulog watcher passes the packet to a userspace +logging daemon using netlink multicast sockets. This differs +from the log watcher in the sense that the complete packet is +sent to userspace instead of a descriptive text and that +netlink multicast sockets are used instead of the syslog. +This watcher enables parsing of packets with userspace programs, the +physical bridge in and out ports are also included in the netlink messages. +The ulog watcher module accepts 2 parameters when the module is loaded +into the kernel (e.g. with modprobe): +.B nlbufsiz +specifies how big the buffer for each netlink multicast +group is. If you say +.IR nlbufsiz=8192 , +for example, up to eight kB of packets will +get accumulated in the kernel until they are sent to userspace. It is +not possible to allocate more than 128kB. Please also keep in mind that +this buffer size is allocated for each nlgroup you are using, so the +total kernel memory usage increases by that factor. The default is 4096. +.B flushtimeout +specifies after how many hundredths of a second the queue should be +flushed, even if it is not full yet. The default is 10 (one tenth of +a second). +.TP +.B "--ulog" +.br +Use the default settings: ulog-prefix="", ulog-nlgroup=1, +ulog-cprange=4096, ulog-qthreshold=1. +.TP +.B --ulog-prefix "\fItext\fP" +.br +Defines the prefix included with the packets sent to userspace. +.TP +.BR --ulog-nlgroup " \fIgroup\fP" +.br +Defines which netlink group number to use (a number from 1 to 32). +Make sure the netlink group numbers used for the iptables ULOG +target differ from those used for the ebtables ulog watcher. +The default group number is 1. +.TP +.BR --ulog-cprange " \fIrange\fP" +.br +Defines the maximum copy range to userspace, for packets matching the +rule. The default range is 0, which means the maximum copy range is +given by +.BR nlbufsiz . +A maximum copy range larger than +128*1024 is meaningless as the packets sent to userspace have an upper +size limit of 128*1024. +.TP +.BR --ulog-qthreshold " \fIthreshold\fP" +.br +Queue at most +.I threshold +number of packets before sending them to +userspace with a netlink socket. Note that packets can be sent to +userspace before the queue is full, this happens when the ulog +kernel timer goes off (the frequency of this timer depends on +.BR flushtimeout ). +.SS TARGET EXTENSIONS +.SS arpreply +The +.B arpreply +target can be used in the +.BR PREROUTING " chain of the " nat " table." +If this target sees an ARP request it will automatically reply +with an ARP reply. The used MAC address for the reply can be specified. +The protocol must be specified as +.IR ARP . +When the ARP message is not an ARP request or when the ARP request isn't +for an IP address on an Ethernet network, it is ignored by this target +.BR "" ( CONTINUE ). +When the ARP request is malformed, it is dropped +.BR "" ( DROP ). +.TP +.BR "--arpreply-mac " "\fIaddress\fP" +Specifies the MAC address to reply with: the Ethernet source MAC and the +ARP payload source MAC will be filled in with this address. +.TP +.BR "--arpreply-target " "\fItarget\fP" +Specifies the standard target. After sending the ARP reply, the rule still +has to give a standard target so ebtables knows what to do with the ARP request. +The default target +.BR "" "is " DROP . +.SS dnat +The +.B dnat +target can only be used in the +.BR BROUTING " chain of the " broute " table and the " +.BR PREROUTING " and " OUTPUT " chains of the " nat " table." +It specifies that the destination MAC address has to be changed. +.TP +.BR "--to-destination " "\fIaddress\fP" +.br +Change the destination MAC address to the specified +.IR address . +The flag +.B --to-dst +is an alias for this option. +.TP +.BR "--dnat-target " "\fItarget\fP" +.br +Specifies the standard target. After doing the dnat, the rule still has to +give a standard target so ebtables knows what to do with the dnated frame. +The default target is +.BR ACCEPT . +Making it +.BR CONTINUE " could let you use" +multiple target extensions on the same frame. Making it +.BR DROP " only makes" +sense in the +.BR BROUTING " chain but using the " redirect " target is more logical there. " RETURN " is also allowed. Note that using " RETURN +in a base chain is not allowed (for obvious reasons). +.SS mark +.BR "" "The " mark " target can be used in every chain of every table. It is possible" +to use the marking of a frame/packet in both ebtables and iptables, +if the bridge-nf code is compiled into the kernel. Both put the marking at the +same place. This allows for a form of communication between ebtables and iptables. +.TP +.BR "--mark-set " "\fIvalue\fP" +.br +Mark the frame with the specified non-negative +.IR value . +.TP +.BR "--mark-or " "\fIvalue\fP" +.br +Or the frame with the specified non-negative +.IR value . +.TP +.BR "--mark-and " "\fIvalue\fP" +.br +And the frame with the specified non-negative +.IR value . +.TP +.BR "--mark-xor " "\fIvalue\fP" +.br +Xor the frame with the specified non-negative +.IR value . +.TP +.BR "--mark-target " "\fItarget\fP" +.br +Specifies the standard target. After marking the frame, the rule +still has to give a standard target so ebtables knows what to do. +The default target is +.BR ACCEPT ". Making it " CONTINUE " can let you do other" +things with the frame in subsequent rules of the chain. +.SS redirect +The +.B redirect +target will change the MAC target address to that of the bridge device the +frame arrived on. This target can only be used in the +.BR BROUTING " chain of the " broute " table and the " +.BR PREROUTING " chain of the " nat " table." +In the +.BR BROUTING " chain, the MAC address of the bridge port is used as destination address," +.BR "" "in the " PREROUTING " chain, the MAC address of the bridge is used." +.TP +.BR "--redirect-target " "\fItarget\fP" +.br +Specifies the standard target. After doing the MAC redirect, the rule +still has to give a standard target so ebtables knows what to do. +The default target is +.BR ACCEPT ". Making it " CONTINUE " could let you use" +multiple target extensions on the same frame. Making it +.BR DROP " in the " BROUTING " chain will let the frames be routed. " RETURN " is also allowed. Note" +.BR "" "that using " RETURN " in a base chain is not allowed." +.SS snat +The +.B snat +target can only be used in the +.BR POSTROUTING " chain of the " nat " table." +It specifies that the source MAC address has to be changed. +.TP +.BR "--to-source " "\fIaddress\fP" +.br +Changes the source MAC address to the specified +.IR address ". The flag" +.B --to-src +is an alias for this option. +.TP +.BR "--snat-target " "\fItarget\fP" +.br +Specifies the standard target. After doing the snat, the rule still has +to give a standard target so ebtables knows what to do. +.BR "" "The default target is " ACCEPT ". Making it " CONTINUE " could let you use" +.BR "" "multiple target extensions on the same frame. Making it " DROP " doesn't" +.BR "" "make sense, but you could do that too. " RETURN " is also allowed. Note" +.BR "" "that using " RETURN " in a base chain is not allowed." +.br +.TP +.BR "--snat-arp " +.br +Also change the hardware source address inside the arp header if the packet is an +arp message and the hardware address length in the arp header is 6 bytes. +.br +.SH FILES +.I /etc/ethertypes +.I @LOCKFILE@ +.SH ENVIRONMENT VARIABLES +.I EBTABLES_ATOMIC_FILE +.SH MAILINGLISTS +.BR "" "See " http://netfilter.org/mailinglists.html +.SH SEE ALSO +.BR iptables "(8), " brctl "(8), " ifconfig "(8), " route (8) +.PP +.BR "" "See " http://ebtables.sf.net
diff --git a/ebtables-restore.c b/ebtables-restore.c new file mode 100644 index 0000000..bb4d0cf --- /dev/null +++ b/ebtables-restore.c
@@ -0,0 +1,156 @@ +/* + * ebtables-restore.c, October 2005 + * + * Author: Bart De Schuymer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <getopt.h> +#include "include/ebtables_u.h" + +static const struct option options[] = { + {.name = "noflush", .has_arg = 0, .val = 'n'}, + { 0 } +}; + +static struct ebt_u_replace replace[3]; +void ebt_early_init_once(); + +#define OPT_KERNELDATA 0x800 /* Also defined in ebtables.c */ + +static void print_usage() +{ + fprintf(stderr, "Usage: ebtables-restore [ --noflush ]\n"); + exit(1); +} + +static void copy_table_names() +{ + strcpy(replace[0].name, "filter"); + strcpy(replace[1].name, "nat"); + strcpy(replace[2].name, "broute"); +} + +#define ebtrest_print_error(format, args...) do {fprintf(stderr, "ebtables-restore: "\ + "line %d: "format".\n", line, ##args); exit(-1);} while (0) +int main(int argc_, char *argv_[]) +{ + char *argv[EBTD_ARGC_MAX], cmdline[EBTD_CMDLINE_MAXLN]; + int i, offset, quotemode = 0, argc, table_nr = -1, line = 0, whitespace, c, flush = 1; + char ebtables_str[] = "ebtables"; + + while ((c = getopt_long(argc_, argv_, "n", options, NULL)) != -1) { + switch(c) { + case 'n': + flush = 0; + break; + default: + print_usage(); + break; + } + } + + ebt_silent = 0; + copy_table_names(); + ebt_early_init_once(); + argv[0] = ebtables_str; + + while (fgets(cmdline, EBTD_CMDLINE_MAXLN, stdin)) { + line++; + if (*cmdline == '#' || *cmdline == '\n') + continue; + *strchr(cmdline, '\n') = '\0'; + if (*cmdline == '*') { + if (table_nr != -1) { + ebt_deliver_table(&replace[table_nr]); + ebt_deliver_counters(&replace[table_nr]); + } + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, cmdline+1)) + break; + if (i == 3) + ebtrest_print_error("table '%s' was not recognized", cmdline+1); + table_nr = i; + replace[table_nr].command = 11; + ebt_get_kernel_table(&replace[table_nr], flush); + replace[table_nr].command = 0; + replace[table_nr].flags = OPT_KERNELDATA; /* Prevent do_command from initialising replace */ + continue; + } else if (table_nr == -1) + ebtrest_print_error("no table specified"); + if (*cmdline == ':') { + int policy, chain_nr; + char *ch; + + if (!(ch = strchr(cmdline, ' '))) + ebtrest_print_error("no policy specified"); + *ch = '\0'; + for (i = 0; i < NUM_STANDARD_TARGETS; i++) + if (!strcmp(ch+1, ebt_standard_targets[i])) { + policy = -i -1; + if (policy == EBT_CONTINUE) + i = NUM_STANDARD_TARGETS; + break; + } + if (i == NUM_STANDARD_TARGETS) + ebtrest_print_error("invalid policy specified"); + /* No need to check chain name for consistency, since + * we're supposed to be reading an automatically generated + * file. */ + if ((chain_nr = ebt_get_chainnr(&replace[table_nr], cmdline+1)) == -1) + ebt_new_chain(&replace[table_nr], cmdline+1, policy); + else + replace[table_nr].chains[chain_nr]->policy = policy; + continue; + } + argv[1] = cmdline; + offset = whitespace = 0; + argc = 2; + while (cmdline[offset] != '\0') { + if (cmdline[offset] == '\"') { + whitespace = 0; + quotemode ^= 1; + if (quotemode) + argv[argc++] = &cmdline[offset+1]; + else if (cmdline[offset+1] != ' ' && cmdline[offset+1] != '\0') + ebtrest_print_error("syntax error at \""); + cmdline[offset] = '\0'; + } else if (!quotemode && cmdline[offset] == ' ') { + whitespace = 1; + cmdline[offset] = '\0'; + } else if (whitespace == 1) { + argv[argc++] = &cmdline[offset]; + whitespace = 0; + } + offset++; + } + if (quotemode) + ebtrest_print_error("wrong use of '\"'"); + optind = 0; /* Setting optind = 1 causes serious annoyances */ + do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]); + ebt_reinit_extensions(); + } + + if (table_nr != -1) { + ebt_deliver_table(&replace[table_nr]); + ebt_deliver_counters(&replace[table_nr]); + } + return 0; +}
diff --git a/ebtables-save.in b/ebtables-save.in new file mode 100644 index 0000000..17924a2 --- /dev/null +++ b/ebtables-save.in
@@ -0,0 +1,61 @@ +#!/usr/bin/perl -w +# +# +# A script that generates text output of the ebtables rules. +# Similar to iptables-save. +# +# It can be used to store active configuration to /etc/sysconfig/ebtables + +use strict; +my $table; +my $ebtables = "@sbindir@/ebtables"; +my $cnt = ""; +my $version = "1.0"; +my $table_name; + +# ======================================================== +# Process filter table +# ======================================================== +sub process_table { + my $chain = ""; + my $rules = ""; + my $chains = ""; + my $line = ""; + + foreach $line (split("\n",$_[0])) { + if ($line =~ m/Bridge table: (.*)/) { + print "*$1\n"; + next; + } + if ($line =~ m/Bridge chain: (.*?), entries:.* policy: (.*)/) { + $chains = $chains . ":$1 $2\n"; + $chain = $1; + next; + } + if ($line =~ m/^$/) { + next; + } + if ($cnt eq "--Lc") { + $line =~ s/, pcnt = (.*) -- bcnt = (.*)/-c $1 $2/; + } else { + $line =~ s/ $//; + } + $rules = $rules . "-A $chain $line\n"; + } + + print $chains; + print $rules; + print "\n"; +} +# ======================================================== + +unless (-x $ebtables) { exit -1 }; +print "# Generated by ebtables-save v$version (legacy) on " . `date`; +if (defined($ENV{'EBTABLES_SAVE_COUNTER'}) && $ENV{'EBTABLES_SAVE_COUNTER'} eq "yes") { + $cnt = "--Lc"; +} +foreach $table_name (split("\n", `grep -E '^ebtable_' /proc/modules | cut -f1 -d' ' | sed s/ebtable_//`)) { + $table =`$ebtables -t $table_name -L $cnt`; + unless ($? == 0) { print $table; exit -1 }; + &process_table($table); +}
diff --git a/ebtables-standalone.c b/ebtables-standalone.c new file mode 100644 index 0000000..d349d39 --- /dev/null +++ b/ebtables-standalone.c
@@ -0,0 +1,14 @@ +#include <string.h> +#include "include/ebtables_u.h" + +static struct ebt_u_replace replace; +void ebt_early_init_once(); + +int main(int argc, char *argv[]) +{ + ebt_silent = 0; + ebt_early_init_once(); + strcpy(replace.name, "filter"); + do_command(argc, argv, EXEC_STYLE_PRG, &replace); + return 0; +}
diff --git a/ebtables.c b/ebtables.c new file mode 100644 index 0000000..c1f5c2b --- /dev/null +++ b/ebtables.c
@@ -0,0 +1,1241 @@ +/* + * ebtables.c, v2.0 July 2002 + * + * Author: Bart De Schuymer + * + * This code was stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <getopt.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <signal.h> +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" + +/* Checks whether a command has already been specified */ +#define OPT_COMMANDS (replace->flags & OPT_COMMAND || replace->flags & OPT_ZERO) + +#define OPT_COMMAND 0x01 +#define OPT_TABLE 0x02 +#define OPT_IN 0x04 +#define OPT_OUT 0x08 +#define OPT_JUMP 0x10 +#define OPT_PROTOCOL 0x20 +#define OPT_SOURCE 0x40 +#define OPT_DEST 0x80 +#define OPT_ZERO 0x100 +#define OPT_LOGICALIN 0x200 +#define OPT_LOGICALOUT 0x400 +#define OPT_KERNELDATA 0x800 /* This value is also defined in ebtablesd.c */ +#define OPT_COUNT 0x1000 /* This value is also defined in libebtc.c */ +#define OPT_CNT_INCR 0x2000 /* This value is also defined in libebtc.c */ +#define OPT_CNT_DECR 0x4000 /* This value is also defined in libebtc.c */ + +/* Default command line options. Do not mess around with the already + * assigned numbers unless you know what you are doing */ +static struct option ebt_original_options[] = +{ + { "append" , required_argument, 0, 'A' }, + { "insert" , required_argument, 0, 'I' }, + { "delete" , required_argument, 0, 'D' }, + { "list" , optional_argument, 0, 'L' }, + { "Lc" , no_argument , 0, 4 }, + { "Ln" , no_argument , 0, 5 }, + { "Lx" , no_argument , 0, 6 }, + { "Lmac2" , no_argument , 0, 12 }, + { "zero" , optional_argument, 0, 'Z' }, + { "flush" , optional_argument, 0, 'F' }, + { "policy" , required_argument, 0, 'P' }, + { "in-interface" , required_argument, 0, 'i' }, + { "in-if" , required_argument, 0, 'i' }, + { "logical-in" , required_argument, 0, 2 }, + { "logical-out" , required_argument, 0, 3 }, + { "out-interface" , required_argument, 0, 'o' }, + { "out-if" , required_argument, 0, 'o' }, + { "version" , no_argument , 0, 'V' }, + { "help" , no_argument , 0, 'h' }, + { "jump" , required_argument, 0, 'j' }, + { "set-counters" , required_argument, 0, 'c' }, + { "change-counters", required_argument, 0, 'C' }, + { "proto" , required_argument, 0, 'p' }, + { "protocol" , required_argument, 0, 'p' }, + { "db" , required_argument, 0, 'b' }, + { "source" , required_argument, 0, 's' }, + { "src" , required_argument, 0, 's' }, + { "destination" , required_argument, 0, 'd' }, + { "dst" , required_argument, 0, 'd' }, + { "table" , required_argument, 0, 't' }, + { "modprobe" , required_argument, 0, 'M' }, + { "new-chain" , required_argument, 0, 'N' }, + { "rename-chain" , required_argument, 0, 'E' }, + { "delete-chain" , optional_argument, 0, 'X' }, + { "atomic-init" , no_argument , 0, 7 }, + { "atomic-commit" , no_argument , 0, 8 }, + { "atomic-file" , required_argument, 0, 9 }, + { "atomic-save" , no_argument , 0, 10 }, + { "init-table" , no_argument , 0, 11 }, + { "concurrent" , no_argument , 0, 13 }, + { 0 } +}; + +static struct option *ebt_options = ebt_original_options; + +/* Holds all the data */ +static struct ebt_u_replace *replace; + +/* The chosen table */ +static struct ebt_u_table *table; + +/* The pointers in here are special: + * The struct ebt_target pointer is actually a struct ebt_u_target pointer. + * I do not feel like using a union. + * We need a struct ebt_u_target pointer because we know the address of the data + * they point to won't change. We want to allow that the struct ebt_u_target.t + * member can change. + * The same holds for the struct ebt_match and struct ebt_watcher pointers */ +static struct ebt_u_entry *new_entry; + + +static int global_option_offset; +#define OPTION_OFFSET 256 +static struct option *merge_options(struct option *oldopts, + const struct option *newopts, unsigned int *options_offset) +{ + unsigned int num_old, num_new, i; + struct option *merge; + + if (!newopts || !oldopts || !options_offset) + ebt_print_bug("merge wrong"); + for (num_old = 0; oldopts[num_old].name; num_old++); + for (num_new = 0; newopts[num_new].name; num_new++); + + global_option_offset += OPTION_OFFSET; + *options_offset = global_option_offset; + + merge = malloc(sizeof(struct option) * (num_new + num_old + 1)); + if (!merge) + ebt_print_memory(); + memcpy(merge, oldopts, num_old * sizeof(struct option)); + for (i = 0; i < num_new; i++) { + merge[num_old + i] = newopts[i]; + merge[num_old + i].val += *options_offset; + } + memset(merge + num_old + num_new, 0, sizeof(struct option)); + /* Only free dynamically allocated stuff */ + if (oldopts != ebt_original_options) + free(oldopts); + + return merge; +} + +static void merge_match(struct ebt_u_match *m) +{ + ebt_options = merge_options + (ebt_options, m->extra_ops, &(m->option_offset)); +} + +static void merge_watcher(struct ebt_u_watcher *w) +{ + ebt_options = merge_options + (ebt_options, w->extra_ops, &(w->option_offset)); +} + +static void merge_target(struct ebt_u_target *t) +{ + ebt_options = merge_options + (ebt_options, t->extra_ops, &(t->option_offset)); +} + +/* Be backwards compatible, so don't use '+' in kernel */ +#define IF_WILDCARD 1 +static void print_iface(const char *iface) +{ + char *c; + + if ((c = strchr(iface, IF_WILDCARD))) + *c = '+'; + printf("%s ", iface); + if (c) + *c = IF_WILDCARD; +} + +/* We use replace->flags, so we can't use the following values: + * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */ +#define LIST_N 0x04 +#define LIST_C 0x08 +#define LIST_X 0x10 +#define LIST_MAC2 0x20 + +/* Helper function for list_rules() */ +static void list_em(struct ebt_u_entries *entries) +{ + int i, j, space = 0, digits; + struct ebt_u_entry *hlp; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_match *m; + struct ebt_u_watcher *w; + struct ebt_u_target *t; + + if (replace->flags & LIST_MAC2) + ebt_printstyle_mac = 2; + else + ebt_printstyle_mac = 0; + hlp = entries->entries->next; + if (replace->flags & LIST_X && entries->policy != EBT_ACCEPT) { + printf("ebtables -t %s -P %s %s\n", replace->name, + entries->name, ebt_standard_targets[-entries->policy - 1]); + } else if (!(replace->flags & LIST_X)) { + printf("\nBridge chain: %s, entries: %d, policy: %s\n", + entries->name, entries->nentries, + ebt_standard_targets[-entries->policy - 1]); + } + + if (replace->flags & LIST_N) { + i = entries->nentries; + while (i > 9) { + space++; + i /= 10; + } + } + + for (i = 0; i < entries->nentries; i++) { + if (replace->flags & LIST_N) { + digits = 0; + /* A little work to get nice rule numbers. */ + j = i + 1; + while (j > 9) { + digits++; + j /= 10; + } + for (j = 0; j < space - digits; j++) + printf(" "); + printf("%d. ", i + 1); + } + if (replace->flags & LIST_X) + printf("ebtables -t %s -A %s ", + replace->name, entries->name); + + /* The standard target's print() uses this to find out + * the name of a udc */ + hlp->replace = replace; + + /* Don't print anything about the protocol if no protocol was + * specified, obviously this means any protocol will do. */ + if (!(hlp->bitmask & EBT_NOPROTO)) { + printf("-p "); + if (hlp->invflags & EBT_IPROTO) + printf("! "); + if (hlp->bitmask & EBT_802_3) + printf("Length "); + else { + struct ethertypeent *ent; + + ent = getethertypebynumber(ntohs(hlp->ethproto)); + if (!ent) + printf("0x%x ", ntohs(hlp->ethproto)); + else + printf("%s ", ent->e_name); + } + } + if (hlp->bitmask & EBT_SOURCEMAC) { + printf("-s "); + if (hlp->invflags & EBT_ISOURCE) + printf("! "); + ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk); + printf(" "); + } + if (hlp->bitmask & EBT_DESTMAC) { + printf("-d "); + if (hlp->invflags & EBT_IDEST) + printf("! "); + ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk); + printf(" "); + } + if (hlp->in[0] != '\0') { + printf("-i "); + if (hlp->invflags & EBT_IIN) + printf("! "); + print_iface(hlp->in); + } + if (hlp->logical_in[0] != '\0') { + printf("--logical-in "); + if (hlp->invflags & EBT_ILOGICALIN) + printf("! "); + print_iface(hlp->logical_in); + } + if (hlp->logical_out[0] != '\0') { + printf("--logical-out "); + if (hlp->invflags & EBT_ILOGICALOUT) + printf("! "); + print_iface(hlp->logical_out); + } + if (hlp->out[0] != '\0') { + printf("-o "); + if (hlp->invflags & EBT_IOUT) + printf("! "); + print_iface(hlp->out); + } + + m_l = hlp->m_list; + while (m_l) { + m = ebt_find_match(m_l->m->u.name); + if (!m) + ebt_print_bug("Match not found"); + m->print(hlp, m_l->m); + m_l = m_l->next; + } + w_l = hlp->w_list; + while (w_l) { + w = ebt_find_watcher(w_l->w->u.name); + if (!w) + ebt_print_bug("Watcher not found"); + w->print(hlp, w_l->w); + w_l = w_l->next; + } + + printf("-j "); + if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET)) + printf("%s ", hlp->t->u.name); + t = ebt_find_target(hlp->t->u.name); + if (!t) + ebt_print_bug("Target '%s' not found", hlp->t->u.name); + t->print(hlp, hlp->t); + if (replace->flags & LIST_C) { + uint64_t pcnt = hlp->cnt.pcnt; + uint64_t bcnt = hlp->cnt.bcnt; + + if (replace->flags & LIST_X) + printf("-c %"PRIu64" %"PRIu64, pcnt, bcnt); + else + printf(", pcnt = %"PRIu64" -- bcnt = %"PRIu64, pcnt, bcnt); + } + printf("\n"); + hlp = hlp->next; + } +} + +static void print_help() +{ + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + + PRINT_VERSION; + printf( +"Usage:\n" +"ebtables -[ADI] chain rule-specification [options]\n" +"ebtables -P chain target\n" +"ebtables -[LFZ] [chain]\n" +"ebtables -[NX] [chain]\n" +"ebtables -E old-chain-name new-chain-name\n\n" +"Commands:\n" +"--append -A chain : append to chain\n" +"--delete -D chain : delete matching rule from chain\n" +"--delete -D chain rulenum : delete rule at position rulenum from chain\n" +"--change-counters -C chain\n" +" [rulenum] pcnt bcnt : change counters of existing rule\n" +"--insert -I chain rulenum : insert rule at position rulenum in chain\n" +"--list -L [chain] : list the rules in a chain or in all chains\n" +"--flush -F [chain] : delete all rules in chain or in all chains\n" +"--init-table : replace the kernel table with the initial table\n" +"--zero -Z [chain] : put counters on zero in chain or in all chains\n" +"--policy -P chain target : change policy on chain to target\n" +"--new-chain -N chain : create a user defined chain\n" +"--rename-chain -E old new : rename a chain\n" +"--delete-chain -X [chain] : delete a user defined chain\n" +"--atomic-commit : update the kernel w/t table contained in <FILE>\n" +"--atomic-init : put the initial kernel table into <FILE>\n" +"--atomic-save : put the current kernel table into <FILE>\n" +"--atomic-file file : set <FILE> to file\n\n" +"Options:\n" +"--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n" +"--src -s [!] address[/mask]: source mac address\n" +"--dst -d [!] address[/mask]: destination mac address\n" +"--in-if -i [!] name[+] : network input interface name\n" +"--out-if -o [!] name[+] : network output interface name\n" +"--logical-in [!] name[+] : logical bridge input interface name\n" +"--logical-out [!] name[+] : logical bridge output interface name\n" +"--set-counters -c chain\n" +" pcnt bcnt : set the counters of the to be added rule\n" +"--modprobe -M program : try to insert modules using this program\n" +"--concurrent : use a file lock to support concurrent scripts\n" +"--version -V : print package version\n\n" +"Environment variable:\n" +ATOMIC_ENV_VARIABLE " : if set <FILE> (see above) will equal its value" +"\n\n"); + m_l = new_entry->m_list; + while (m_l) { + ((struct ebt_u_match *)m_l->m)->help(); + printf("\n"); + m_l = m_l->next; + } + w_l = new_entry->w_list; + while (w_l) { + ((struct ebt_u_watcher *)w_l->w)->help(); + printf("\n"); + w_l = w_l->next; + } + ((struct ebt_u_target *)new_entry->t)->help(); + printf("\n"); + if (table->help) + table->help(ebt_hooknames); +} + +/* Execute command L */ +static void list_rules() +{ + int i; + + if (!(replace->flags & LIST_X)) + printf("Bridge table: %s\n", table->name); + if (replace->selected_chain != -1) + list_em(ebt_to_chain(replace)); + else { + /* Create new chains and rename standard chains when necessary */ + if (replace->flags & LIST_X && replace->num_chains > NF_BR_NUMHOOKS) { + for (i = NF_BR_NUMHOOKS; i < replace->num_chains; i++) + printf("ebtables -t %s -N %s\n", replace->name, replace->chains[i]->name); + for (i = 0; i < NF_BR_NUMHOOKS; i++) + if (replace->chains[i] && strcmp(replace->chains[i]->name, ebt_hooknames[i])) + printf("ebtables -t %s -E %s %s\n", replace->name, ebt_hooknames[i], replace->chains[i]->name); + } + for (i = 0; i < replace->num_chains; i++) + if (replace->chains[i]) + list_em(replace->chains[i]); + } +} + +static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end) +{ + char *colon = strchr(argv, ':'), *buffer; + + if (colon) { + *colon = '\0'; + if (*(colon + 1) == '\0') + *rule_nr_end = -1; /* Until the last rule */ + else { + *rule_nr_end = strtol(colon + 1, &buffer, 10); + if (*buffer != '\0' || *rule_nr_end == 0) + return -1; + } + } + if (colon == argv) + *rule_nr = 1; /* Beginning with the first rule */ + else { + *rule_nr = strtol(argv, &buffer, 10); + if (*buffer != '\0' || *rule_nr == 0) + return -1; + } + if (!colon) + *rule_nr_end = *rule_nr; + return 0; +} + +/* Incrementing or decrementing rules in daemon mode is not supported as the + * involved code overload is not worth it (too annoying to take the increased + * counters in the kernel into account). */ +static int parse_change_counters_rule(int argc, char **argv, int *rule_nr, int *rule_nr_end, int exec_style) +{ + char *buffer; + int ret = 0; + + if (optind + 1 >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')) || + (argv[optind + 1][0] == '-' && (argv[optind + 1][1] < '0' && argv[optind + 1][1] > '9'))) + ebt_print_error2("The command -C needs at least 2 arguments"); + if (optind + 2 < argc && (argv[optind + 2][0] != '-' || (argv[optind + 2][1] >= '0' && argv[optind + 2][1] <= '9'))) { + if (optind + 3 != argc) + ebt_print_error2("No extra options allowed with -C start_nr[:end_nr] pcnt bcnt"); + if (parse_rule_range(argv[optind], rule_nr, rule_nr_end)) + ebt_print_error2("Something is wrong with the rule number specification '%s'", argv[optind]); + optind++; + } + + if (argv[optind][0] == '+') { + if (exec_style == EXEC_STYLE_DAEMON) +daemon_incr: + ebt_print_error2("Incrementing rule counters (%s) not allowed in daemon mode", argv[optind]); + ret += 1; + new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10); + } else if (argv[optind][0] == '-') { + if (exec_style == EXEC_STYLE_DAEMON) +daemon_decr: + ebt_print_error2("Decrementing rule counters (%s) not allowed in daemon mode", argv[optind]); + ret += 2; + new_entry->cnt_surplus.pcnt = strtoull(argv[optind] + 1, &buffer, 10); + } else + new_entry->cnt_surplus.pcnt = strtoull(argv[optind], &buffer, 10); + + if (*buffer != '\0') + goto invalid; + optind++; + if (argv[optind][0] == '+') { + if (exec_style == EXEC_STYLE_DAEMON) + goto daemon_incr; + ret += 3; + new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10); + } else if (argv[optind][0] == '-') { + if (exec_style == EXEC_STYLE_DAEMON) + goto daemon_decr; + ret += 6; + new_entry->cnt_surplus.bcnt = strtoull(argv[optind] + 1, &buffer, 10); + } else + new_entry->cnt_surplus.bcnt = strtoull(argv[optind], &buffer, 10); + + if (*buffer != '\0') + goto invalid; + optind++; + return ret; +invalid: + ebt_print_error2("Packet counter '%s' invalid", argv[optind]); +} + +static int parse_iface(char *iface, char *option) +{ + char *c; + + if ((c = strchr(iface, '+'))) { + if (*(c + 1) != '\0') { + ebt_print_error("Spurious characters after '+' wildcard for '%s'", option); + return -1; + } else + *c = IF_WILDCARD; + } + return 0; +} + +void ebt_early_init_once() +{ + ebt_iterate_matches(merge_match); + ebt_iterate_watchers(merge_watcher); + ebt_iterate_targets(merge_target); +} + +/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */ +int do_command(int argc, char *argv[], int exec_style, + struct ebt_u_replace *replace_) +{ + char *buffer; + int c, i; + int zerochain = -1; /* Needed for the -Z option (we can have -Z <this> -L <that>) */ + int chcounter = 0; /* Needed for -C */ + int policy = 0; + int rule_nr = 0; + int rule_nr_end = 0; + struct ebt_u_target *t; + struct ebt_u_match *m; + struct ebt_u_watcher *w; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_entries *entries; + + opterr = 0; + ebt_modprobe = NULL; + + replace = replace_; + + /* The daemon doesn't use the environment variable */ + if (exec_style == EXEC_STYLE_PRG) { + buffer = getenv(ATOMIC_ENV_VARIABLE); + if (buffer) { + replace->filename = malloc(strlen(buffer) + 1); + if (!replace->filename) + ebt_print_memory(); + strcpy(replace->filename, buffer); + buffer = NULL; + } + } + + replace->flags &= OPT_KERNELDATA; /* ebtablesd needs OPT_KERNELDATA */ + replace->selected_chain = -1; + replace->command = 'h'; + + if (!new_entry) { + new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new_entry) + ebt_print_memory(); + } + /* Put some sane values in our new entry */ + ebt_initialize_entry(new_entry); + new_entry->replace = replace; + + /* The scenario induced by this loop makes that: + * '-t' ,'-M' and --atomic (if specified) have to come + * before '-A' and the like */ + + /* Getopt saves the day */ + while ((c = getopt_long(argc, argv, + "-A:D:C:I:N:E:X::L::Z::F::P:Vhi:o:j:c:p:s:d:t:M:", ebt_options, NULL)) != -1) { + switch (c) { + + case 'A': /* Add a rule */ + case 'D': /* Delete a rule */ + case 'C': /* Change counters */ + case 'P': /* Define policy */ + case 'I': /* Insert a rule */ + case 'N': /* Make a user defined chain */ + case 'E': /* Rename chain */ + case 'X': /* Delete chain */ + /* We allow -N chainname -P policy */ + if (replace->command == 'N' && c == 'P') { + replace->command = c; + optind--; /* No table specified */ + goto handle_P; + } + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + + replace->command = c; + replace->flags |= OPT_COMMAND; + if (!(replace->flags & OPT_KERNELDATA)) + ebt_get_kernel_table(replace, 0); + if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!"))) + ebt_print_error2("No chain name specified"); + if (c == 'N') { + if (ebt_get_chainnr(replace, optarg) != -1) + ebt_print_error2("Chain %s already exists", optarg); + else if (ebt_find_target(optarg)) + ebt_print_error2("Target with name %s exists", optarg); + else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN) + ebt_print_error2("Chain name length can't exceed %d", + EBT_CHAIN_MAXNAMELEN - 1); + else if (strchr(optarg, ' ') != NULL) + ebt_print_error2("Use of ' ' not allowed in chain names"); + ebt_new_chain(replace, optarg, EBT_ACCEPT); + /* This is needed to get -N x -P y working */ + replace->selected_chain = ebt_get_chainnr(replace, optarg); + break; + } else if (c == 'X') { + if (optind >= argc) { + replace->selected_chain = -1; + ebt_delete_chain(replace); + break; + } + + if (optind < argc - 1) + ebt_print_error2("No extra options allowed with -X"); + + if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1) + ebt_print_error2("Chain '%s' doesn't exist", argv[optind]); + ebt_delete_chain(replace); + if (ebt_errormsg[0] != '\0') + return -1; + optind++; + break; + } + + if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1) + ebt_print_error2("Chain '%s' doesn't exist", optarg); + if (c == 'E') { + if (optind >= argc) + ebt_print_error2("No new chain name specified"); + else if (optind < argc - 1) + ebt_print_error2("No extra options allowed with -E"); + else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN) + ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1); + else if (ebt_get_chainnr(replace, argv[optind]) != -1) + ebt_print_error2("Chain '%s' already exists", argv[optind]); + else if (ebt_find_target(argv[optind])) + ebt_print_error2("Target with name '%s' exists", argv[optind]); + else if (strchr(argv[optind], ' ') != NULL) + ebt_print_error2("Use of ' ' not allowed in chain names"); + ebt_rename_chain(replace, argv[optind]); + optind++; + break; + } else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) { + if (optind != argc - 1) + ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]"); + if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end)) + ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]); + optind++; + } else if (c == 'C') { + if ((chcounter = parse_change_counters_rule(argc, argv, &rule_nr, &rule_nr_end, exec_style)) == -1) + return -1; + } else if (c == 'I') { + if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9'))) + rule_nr = 1; + else { + rule_nr = strtol(argv[optind], &buffer, 10); + if (*buffer != '\0') + ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]); + optind++; + } + } else if (c == 'P') { +handle_P: + if (optind >= argc) + ebt_print_error2("No policy specified"); + for (i = 0; i < NUM_STANDARD_TARGETS; i++) + if (!strcmp(argv[optind], ebt_standard_targets[i])) { + policy = -i -1; + if (policy == EBT_CONTINUE) + ebt_print_error2("Wrong policy '%s'", argv[optind]); + break; + } + if (i == NUM_STANDARD_TARGETS) + ebt_print_error2("Unknown policy '%s'", argv[optind]); + optind++; + } + break; + case 'L': /* List */ + case 'F': /* Flush */ + case 'Z': /* Zero counters */ + if (c == 'Z') { + if ((replace->flags & OPT_ZERO) || (replace->flags & OPT_COMMAND && replace->command != 'L')) +print_zero: + ebt_print_error2("Command -Z only allowed together with command -L"); + replace->flags |= OPT_ZERO; + } else { + if (replace->flags & OPT_COMMAND) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = c; + replace->flags |= OPT_COMMAND; + if (replace->flags & OPT_ZERO && c != 'L') + goto print_zero; + } + +#ifdef SILENT_DAEMON + if (c== 'L' && exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("-L not supported in daemon mode"); +#endif + + if (!(replace->flags & OPT_KERNELDATA)) + ebt_get_kernel_table(replace, 0); + i = -1; + if (optind < argc && argv[optind][0] != '-') { + if ((i = ebt_get_chainnr(replace, argv[optind])) == -1) + ebt_print_error2("Chain '%s' doesn't exist", argv[optind]); + optind++; + } + if (i != -1) { + if (c == 'Z') + zerochain = i; + else + replace->selected_chain = i; + } + break; + case 'V': /* Version */ + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = 'V'; + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2(PROGNAME" v"PROGVERSION" ("PROGDATE")\n"); + PRINT_VERSION; + exit(0); + case 'M': /* Modprobe */ + if (OPT_COMMANDS) + ebt_print_error2("Please put the -M option earlier"); + free(ebt_modprobe); + ebt_modprobe = optarg; + break; + case 'h': /* Help */ +#ifdef SILENT_DAEMON + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("-h not supported in daemon mode"); +#endif + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + replace->command = 'h'; + + /* All other arguments should be extension names */ + while (optind < argc) { + struct ebt_u_match *m; + struct ebt_u_watcher *w; + + if (!strcasecmp("list_extensions", argv[optind])) { + ebt_list_extensions(); + exit(0); + } + if ((m = ebt_find_match(argv[optind]))) + ebt_add_match(new_entry, m); + else if ((w = ebt_find_watcher(argv[optind]))) + ebt_add_watcher(new_entry, w); + else { + if (!(t = ebt_find_target(argv[optind]))) + ebt_print_error2("Extension '%s' not found", argv[optind]); + if (replace->flags & OPT_JUMP) + ebt_print_error2("Sorry, you can only see help for one target extension at a time"); + replace->flags |= OPT_JUMP; + new_entry->t = (struct ebt_entry_target *)t; + } + optind++; + } + break; + case 't': /* Table */ + if (OPT_COMMANDS) + ebt_print_error2("Please put the -t option first"); + ebt_check_option2(&(replace->flags), OPT_TABLE); + if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1) + ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1); + strcpy(replace->name, optarg); + break; + case 'i': /* Input interface */ + case 2 : /* Logical input interface */ + case 'o': /* Output interface */ + case 3 : /* Logical output interface */ + case 'j': /* Target */ + case 'p': /* Net family protocol */ + case 's': /* Source mac */ + case 'd': /* Destination mac */ + case 'c': /* Set counters */ + if (!OPT_COMMANDS) + ebt_print_error2("No command specified"); + if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I' && replace->command != 'C') + ebt_print_error2("Command and option do not match"); + if (c == 'i') { + ebt_check_option2(&(replace->flags), OPT_IN); + if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) + ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IIN; + + if (strlen(optarg) >= IFNAMSIZ) +big_iface_length: + ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1); + strcpy(new_entry->in, optarg); + if (parse_iface(new_entry->in, "-i")) + return -1; + break; + } else if (c == 2) { + ebt_check_option2(&(replace->flags), OPT_LOGICALIN); + if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING) + ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ILOGICALIN; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->logical_in, optarg); + if (parse_iface(new_entry->logical_in, "--logical-in")) + return -1; + break; + } else if (c == 'o') { + ebt_check_option2(&(replace->flags), OPT_OUT); + if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) + ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IOUT; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->out, optarg); + if (parse_iface(new_entry->out, "-o")) + return -1; + break; + } else if (c == 3) { + ebt_check_option2(&(replace->flags), OPT_LOGICALOUT); + if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING) + ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains"); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ILOGICALOUT; + + if (strlen(optarg) >= IFNAMSIZ) + goto big_iface_length; + strcpy(new_entry->logical_out, optarg); + if (parse_iface(new_entry->logical_out, "--logical-out")) + return -1; + break; + } else if (c == 'j') { + ebt_check_option2(&(replace->flags), OPT_JUMP); + for (i = 0; i < NUM_STANDARD_TARGETS; i++) + if (!strcmp(optarg, ebt_standard_targets[i])) { + t = ebt_find_target(EBT_STANDARD_TARGET); + ((struct ebt_standard_target *) t->t)->verdict = -i - 1; + break; + } + if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) { + ebt_print_error2("Return target only for user defined chains"); + } else if (i != NUM_STANDARD_TARGETS) + break; + + if ((i = ebt_get_chainnr(replace, optarg)) != -1) { + if (i < NF_BR_NUMHOOKS) + ebt_print_error2("Don't jump to a standard chain"); + t = ebt_find_target(EBT_STANDARD_TARGET); + ((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS; + break; + } else { + /* Must be an extension then */ + struct ebt_u_target *t; + + t = ebt_find_target(optarg); + /* -j standard not allowed either */ + if (!t || t == (struct ebt_u_target *)new_entry->t) + ebt_print_error2("Illegal target name '%s'", optarg); + new_entry->t = (struct ebt_entry_target *)t; + ebt_find_target(EBT_STANDARD_TARGET)->used = 0; + t->used = 1; + } + break; + } else if (c == 's') { + ebt_check_option2(&(replace->flags), OPT_SOURCE); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_ISOURCE; + + if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk)) + ebt_print_error2("Problem with specified source mac '%s'", optarg); + new_entry->bitmask |= EBT_SOURCEMAC; + break; + } else if (c == 'd') { + ebt_check_option2(&(replace->flags), OPT_DEST); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IDEST; + + if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk)) + ebt_print_error2("Problem with specified destination mac '%s'", optarg); + new_entry->bitmask |= EBT_DESTMAC; + break; + } else if (c == 'c') { + ebt_check_option2(&(replace->flags), OPT_COUNT); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected '!' after -c"); + if (optind >= argc || optarg[0] == '-' || argv[optind][0] == '-') + ebt_print_error2("Option -c needs 2 arguments"); + + new_entry->cnt.pcnt = strtoull(optarg, &buffer, 10); + if (*buffer != '\0') + ebt_print_error2("Packet counter '%s' invalid", optarg); + new_entry->cnt.bcnt = strtoull(argv[optind], &buffer, 10); + if (*buffer != '\0') + ebt_print_error2("Packet counter '%s' invalid", argv[optind]); + optind++; + break; + } + ebt_check_option2(&(replace->flags), OPT_PROTOCOL); + if (ebt_check_inverse2(optarg)) + new_entry->invflags |= EBT_IPROTO; + + new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO); + i = strtol(optarg, &buffer, 16); + if (*buffer == '\0' && (i < 0 || i > 0xFFFF)) + ebt_print_error2("Problem with the specified protocol"); + if (*buffer != '\0') { + struct ethertypeent *ent; + + if (!strcasecmp(optarg, "LENGTH")) { + new_entry->bitmask |= EBT_802_3; + break; + } + ent = getethertypebyname(optarg); + if (!ent) + ebt_print_error2("Problem with the specified Ethernet protocol '%s', perhaps "_PATH_ETHERTYPES " is missing", optarg); + new_entry->ethproto = ent->e_ethertype; + } else + new_entry->ethproto = i; + + if (new_entry->ethproto < 0x0600) + ebt_print_error2("Sorry, protocols have values above or equal to 0x0600"); + break; + case 4 : /* Lc */ +#ifdef SILENT_DAEMON + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("--Lc is not supported in daemon mode"); +#endif + ebt_check_option2(&(replace->flags), LIST_C); + if (replace->command != 'L') + ebt_print_error("Use --Lc with -L"); + replace->flags |= LIST_C; + break; + case 5 : /* Ln */ +#ifdef SILENT_DAEMON + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("--Ln is not supported in daemon mode"); +#endif + ebt_check_option2(&(replace->flags), LIST_N); + if (replace->command != 'L') + ebt_print_error2("Use --Ln with -L"); + if (replace->flags & LIST_X) + ebt_print_error2("--Lx is not compatible with --Ln"); + replace->flags |= LIST_N; + break; + case 6 : /* Lx */ +#ifdef SILENT_DAEMON + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("--Lx is not supported in daemon mode"); +#endif + ebt_check_option2(&(replace->flags), LIST_X); + if (replace->command != 'L') + ebt_print_error2("Use --Lx with -L"); + if (replace->flags & LIST_N) + ebt_print_error2("--Lx is not compatible with --Ln"); + replace->flags |= LIST_X; + break; + case 12 : /* Lmac2 */ +#ifdef SILENT_DAEMON + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error("--Lmac2 is not supported in daemon mode"); +#endif + ebt_check_option2(&(replace->flags), LIST_MAC2); + if (replace->command != 'L') + ebt_print_error2("Use --Lmac2 with -L"); + replace->flags |= LIST_MAC2; + break; + case 8 : /* atomic-commit */ + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("--atomic-commit is not supported in daemon mode"); + replace->command = c; + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + replace->flags |= OPT_COMMAND; + if (!replace->filename) + ebt_print_error2("No atomic file specified"); + /* Get the information from the file */ + ebt_get_table(replace, 0); + /* We don't want the kernel giving us its counters, + * they would overwrite the counters extracted from + * the file */ + replace->num_counters = 0; + /* Make sure the table will be written to the kernel */ + free(replace->filename); + replace->filename = NULL; + break; + case 7 : /* atomic-init */ + case 10: /* atomic-save */ + case 11: /* init-table */ + if (exec_style == EXEC_STYLE_DAEMON) { + if (c == 7) { + ebt_print_error2("--atomic-init is not supported in daemon mode"); + } else if (c == 10) + ebt_print_error2("--atomic-save is not supported in daemon mode"); + ebt_print_error2("--init-table is not supported in daemon mode"); + } + replace->command = c; + if (OPT_COMMANDS) + ebt_print_error2("Multiple commands are not allowed"); + if (c != 11 && !replace->filename) + ebt_print_error2("No atomic file specified"); + replace->flags |= OPT_COMMAND; + { + char *tmp = replace->filename; + + /* Get the kernel table */ + replace->filename = NULL; + ebt_get_kernel_table(replace, c == 10 ? 0 : 1); + replace->filename = tmp; + } + break; + case 9 : /* atomic */ + if (exec_style == EXEC_STYLE_DAEMON) + ebt_print_error2("--atomic is not supported in daemon mode"); + if (OPT_COMMANDS) + ebt_print_error2("--atomic has to come before the command"); + /* A possible memory leak here, but this is not + * executed in daemon mode */ + replace->filename = (char *)malloc(strlen(optarg) + 1); + strcpy(replace->filename, optarg); + break; + case 13 : /* concurrent */ + if (OPT_COMMANDS) + ebt_print_error2("Please put the --concurrent option first"); + use_lockfd = 1; + break; + case 1 : + if (!strcmp(optarg, "!")) + ebt_check_inverse2(optarg); + else + ebt_print_error2("Bad argument : '%s'", optarg); + /* ebt_check_inverse() did optind++ */ + optind--; + continue; + default: + /* Is it a target option? */ + t = (struct ebt_u_target *)new_entry->t; + if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) { + if (ebt_errormsg[0] != '\0') + return -1; + goto check_extension; + } + + /* Is it a match_option? */ + for (m = ebt_matches; m; m = m->next) + if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m)) + break; + + if (m != NULL) { + if (ebt_errormsg[0] != '\0') + return -1; + if (m->used == 0) { + ebt_add_match(new_entry, m); + m->used = 1; + } + goto check_extension; + } + + /* Is it a watcher option? */ + for (w = ebt_watchers; w; w = w->next) + if (w->parse(c - w->option_offset, argv, argc, new_entry, &w->flags, &w->w)) + break; + + if (w == NULL && c == '?') + ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c); + else if (w == NULL) { + if (!strcmp(t->name, "standard")) + ebt_print_error2("Unknown argument: don't forget the -t option"); + else + ebt_print_error2("Target-specific option does not correspond with specified target"); + } + if (ebt_errormsg[0] != '\0') + return -1; + if (w->used == 0) { + ebt_add_watcher(new_entry, w); + w->used = 1; + } +check_extension: + if (replace->command != 'A' && replace->command != 'I' && + replace->command != 'D' && replace->command != 'C') + ebt_print_error2("Extensions only for -A, -I, -D and -C"); + } + ebt_invert = 0; + } + + /* Just in case we didn't catch an error */ + if (ebt_errormsg[0] != '\0') + return -1; + + if (!(table = ebt_find_table(replace->name))) + ebt_print_error2("Bad table name"); + + if (replace->command == 'h' && !(replace->flags & OPT_ZERO)) { + print_help(); + if (exec_style == EXEC_STYLE_PRG) + exit(0); + } + + /* Do the final checks */ + if (replace->command == 'A' || replace->command == 'I' || + replace->command == 'D' || replace->command == 'C') { + /* This will put the hook_mask right for the chains */ + ebt_check_for_loops(replace); + if (ebt_errormsg[0] != '\0') + return -1; + entries = ebt_to_chain(replace); + m_l = new_entry->m_list; + w_l = new_entry->w_list; + t = (struct ebt_u_target *)new_entry->t; + while (m_l) { + m = (struct ebt_u_match *)(m_l->m); + m->final_check(new_entry, m->m, replace->name, + entries->hook_mask, 0); + if (ebt_errormsg[0] != '\0') + return -1; + m_l = m_l->next; + } + while (w_l) { + w = (struct ebt_u_watcher *)(w_l->w); + w->final_check(new_entry, w->w, replace->name, + entries->hook_mask, 0); + if (ebt_errormsg[0] != '\0') + return -1; + w_l = w_l->next; + } + t->final_check(new_entry, t->t, replace->name, + entries->hook_mask, 0); + if (ebt_errormsg[0] != '\0') + return -1; + } + /* So, the extensions can work with the host endian. + * The kernel does not have to do this of course */ + new_entry->ethproto = htons(new_entry->ethproto); + + if (replace->command == 'P') { + if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN) + ebt_print_error2("Policy RETURN only allowed for user defined chains"); + ebt_change_policy(replace, policy); + if (ebt_errormsg[0] != '\0') + return -1; + } else if (replace->command == 'L') { + list_rules(); + if (!(replace->flags & OPT_ZERO) && exec_style == EXEC_STYLE_PRG) + exit(0); + } + if (replace->flags & OPT_ZERO) { + replace->selected_chain = zerochain; + ebt_zero_counters(replace); + } else if (replace->command == 'F') { + ebt_flush_chains(replace); + } else if (replace->command == 'A' || replace->command == 'I') { + ebt_add_rule(replace, new_entry, rule_nr); + if (ebt_errormsg[0] != '\0') + return -1; + /* Makes undoing the add easier (jumps to delete_the_rule) */ + if (rule_nr <= 0) + rule_nr--; + rule_nr_end = rule_nr; + + /* a jump to a udc requires checking for loops */ + if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) && + ((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) { + /* FIXME: this can be done faster */ + ebt_check_for_loops(replace); + if (ebt_errormsg[0] != '\0') + goto delete_the_rule; + } + + /* Do the final_check(), for all entries. + * This is needed when adding a rule that has a chain target */ + i = -1; + while (++i != replace->num_chains) { + struct ebt_u_entry *e; + + entries = replace->chains[i]; + if (!entries) { + if (i < NF_BR_NUMHOOKS) + continue; + else + ebt_print_bug("whoops\n"); + } + e = entries->entries->next; + while (e != entries->entries) { + /* Userspace extensions use host endian */ + e->ethproto = ntohs(e->ethproto); + ebt_do_final_checks(replace, e, entries); + if (ebt_errormsg[0] != '\0') + goto delete_the_rule; + e->ethproto = htons(e->ethproto); + e = e->next; + } + } + /* Don't reuse the added rule */ + new_entry = NULL; + } else if (replace->command == 'D') { +delete_the_rule: + ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end); + if (ebt_errormsg[0] != '\0') + return -1; + } else if (replace->command == 'C') { + ebt_change_counters(replace, new_entry, rule_nr, rule_nr_end, &(new_entry->cnt_surplus), chcounter); + if (ebt_errormsg[0] != '\0') + return -1; + } + /* Commands -N, -E, -X, --atomic-commit, --atomic-commit, --atomic-save, + * --init-table fall through */ + + if (ebt_errormsg[0] != '\0') + return -1; + if (table->check) + table->check(replace); + + if (exec_style == EXEC_STYLE_PRG) {/* Implies ebt_errormsg[0] == '\0' */ + ebt_deliver_table(replace); + + if (replace->nentries) + ebt_deliver_counters(replace); + } + return 0; +}
diff --git a/ebtablesd.c b/ebtablesd.c new file mode 100644 index 0000000..02d51fa --- /dev/null +++ b/ebtablesd.c
@@ -0,0 +1,371 @@ +/* + * ebtablesd.c, January 2005 + * + * Author: Bart De Schuymer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <errno.h> +#include "include/ebtables_u.h" + +#define OPT_KERNELDATA 0x800 /* Also defined in ebtables.c */ + +static struct ebt_u_replace replace[3]; +#define OPEN_METHOD_FILE 1 +#define OPEN_METHOD_KERNEL 2 +static int open_method[3]; +void ebt_early_init_once(); + +static void sigpipe_handler(int sig) +{ +} +static void copy_table_names() +{ + strcpy(replace[0].name, "filter"); + strcpy(replace[1].name, "nat"); + strcpy(replace[2].name, "broute"); +} + +int main(int argc_, char *argv_[]) +{ + char *argv[EBTD_ARGC_MAX], *args[4], name[] = "mkdir", + mkdir_option[] = "-p", mkdir_dir[] = EBTD_PIPE_DIR, + cmdline[EBTD_CMDLINE_MAXLN]; + int readfd, base = 0, offset = 0, n = 0, quotemode = 0; + + /* Make sure the pipe directory exists */ + args[0] = name; + args[1] = mkdir_option; + args[2] = mkdir_dir; + args[3] = NULL; + switch (fork()) { + case 0: + execvp(args[0], args); + + /* Not usually reached */ + exit(0); + case -1: + return -1; + + default: /* Parent */ + wait(NULL); + } + + if (mkfifo(EBTD_PIPE, 0600) < 0 && errno != EEXIST) { + printf("Error creating FIFO " EBTD_PIPE "\n"); + goto do_exit; + } + + if ((readfd = open(EBTD_PIPE, O_RDONLY | O_NONBLOCK, 0)) == -1) { + perror("open"); + goto do_exit; + } + + if (signal(SIGPIPE, sigpipe_handler) == SIG_ERR) { + perror("signal"); + goto do_exit; + } + + ebt_silent = 1; + + copy_table_names(); + ebt_early_init_once(); + + while (1) { + int n2, i, argc, table_nr, ntot; + + /* base == 0 */ + ntot = read(readfd, cmdline+offset, EBTD_CMDLINE_MAXLN-offset-1); + if (ntot <= 0) + continue; + ntot += offset; +continue_read: + /* Put '\0' between arguments. */ + for (; offset < ntot; n++, offset++) { + if (cmdline[offset] == '\"') { + quotemode ^= 1; + cmdline[offset] = '\0'; + } else if (!quotemode && cmdline[offset] == ' ') { + cmdline[offset] = '\0'; + } else if (cmdline[offset] == '\n') { + if (quotemode) + ebt_print_error("ebtablesd: wrong number of \" delimiters"); + cmdline[offset] = '\0'; + break; + } + } + if (n == 0) { + if (offset == ntot) { + /* The ntot bytes were parsed and ended with '\n' */ + base = 0; + offset = 0; + continue; + } + offset++; + base = offset; + n = 0; + goto continue_read; + } + if (offset == ntot) { /* The ntot bytes were parsed but no complete rule is yet specified */ + if (base == 0) { + ebt_print_error("ebtablesd: the maximum command line length is %d", EBTD_CMDLINE_MAXLN-1); + goto write_msg; + } + memmove(cmdline, cmdline+base+offset, ntot-offset); + offset -= base; + offset++; + base = 0; + continue; + } + + table_nr = 0; + n2 = 0; + argc = 0; + while (n2 < n && argc < EBTD_ARGC_MAX) { + if (*(cmdline + base + n2) == '\0') { + n2++; + continue; + } + argv[argc++] = cmdline + base + n2; + n2 += strlen(cmdline + base + n2) + 1; + } + offset++; /* Move past the '\n' */ + base = offset; + + if (argc > EBTD_ARGC_MAX) { + ebt_print_error("ebtablesd: maximum %d arguments " + "allowed", EBTD_ARGC_MAX - 1); + goto write_msg; + } + if (argc == 1) { + ebt_print_error("ebtablesd: no arguments"); + goto write_msg; + } + + /* Parse the options */ + if (!strcmp(argv[1], "-t")) { + if (argc < 3) { + ebt_print_error("ebtablesd: -t but no table"); + goto write_msg; + } + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + table_nr = i; + } else if (!strcmp(argv[1], "free")) { + if (argc != 3) { + ebt_print_error("ebtablesd: command free " + "needs exactly one argument"); + goto write_msg; + } + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + if (!(replace[i].flags & OPT_KERNELDATA)) { + ebt_print_error("ebtablesd: table %s has not " + "been opened"); + goto write_msg; + } + ebt_cleanup_replace(&replace[i]); + copy_table_names(); + replace[i].flags &= ~OPT_KERNELDATA; + goto write_msg; + } else if (!strcmp(argv[1], "open")) { + if (argc != 3) { + ebt_print_error("ebtablesd: command open " + "needs exactly one argument"); + goto write_msg; + } + + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + if (replace[i].flags & OPT_KERNELDATA) { + ebt_print_error("ebtablesd: table %s needs to " + "be freed before it can be " + "opened"); + goto write_msg; + } + if (!ebt_get_kernel_table(&replace[i], 0)) { + replace[i].flags |= OPT_KERNELDATA; + open_method[i] = OPEN_METHOD_KERNEL; + } + goto write_msg; + } else if (!strcmp(argv[1], "fopen")) { + struct ebt_u_replace tmp; + + memset(&tmp, 0, sizeof(tmp)); + if (argc != 4) { + ebt_print_error("ebtablesd: command fopen " + "needs exactly two arguments"); + goto write_msg; + } + + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + if (replace[i].flags & OPT_KERNELDATA) { + ebt_print_error("ebtablesd: table %s needs to " + "be freed before it can be " + "opened"); + goto write_msg; + } + tmp.filename = (char *)malloc(strlen(argv[3]) + 1); + if (!tmp.filename) { + ebt_print_error("Out of memory"); + goto write_msg; + } + strcpy(tmp.filename, argv[3]); + strcpy(tmp.name, "filter"); + tmp.command = 'L'; /* Make sure retrieve_from_file() + * doesn't complain about wrong + * table name */ + + ebt_get_kernel_table(&tmp, 0); + free(tmp.filename); + tmp.filename = NULL; + if (ebt_errormsg[0] != '\0') + goto write_msg; + + if (strcmp(tmp.name, argv[2])) { + ebt_print_error("ebtablesd: opened file with " + "wrong table name '%s'", tmp.name); + ebt_cleanup_replace(&tmp); + goto write_msg; + } + replace[i] = tmp; + replace[i].command = '\0'; + replace[i].flags |= OPT_KERNELDATA; + open_method[i] = OPEN_METHOD_FILE; + goto write_msg; + } else if (!strcmp(argv[1], "commit")) { + if (argc != 3) { + ebt_print_error("ebtablesd: command commit " + "needs exactly one argument"); + goto write_msg; + } + + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + if (!(replace[i].flags & OPT_KERNELDATA)) { + ebt_print_error("ebtablesd: table %s has not " + "been opened"); + goto write_msg; + } + /* The counters from the kernel are useless if we + * didn't start from a kernel table */ + if (open_method[i] == OPEN_METHOD_FILE) + replace[i].num_counters = 0; + ebt_deliver_table(&replace[i]); + if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL) + ebt_deliver_counters(&replace[i]); + goto write_msg; + } else if (!strcmp(argv[1], "fcommit")) { + if (argc != 4) { + ebt_print_error("ebtablesd: command commit " + "needs exactly two argument"); + goto write_msg; + } + + for (i = 0; i < 3; i++) + if (!strcmp(replace[i].name, argv[2])) + break; + if (i == 3) { + ebt_print_error("ebtablesd: table '%s' was " + "not recognized", argv[2]); + goto write_msg; + } + if (!(replace[i].flags & OPT_KERNELDATA)) { + ebt_print_error("ebtablesd: table %s has not " + "been opened"); + goto write_msg; + } + replace[i].filename = (char *)malloc(strlen(argv[3]) + 1); + if (!replace[i].filename) { + ebt_print_error("Out of memory"); + goto write_msg; + } + strcpy(replace[i].filename, argv[3]); + ebt_deliver_table(&replace[i]); + if (ebt_errormsg[0] == '\0' && open_method[i] == OPEN_METHOD_KERNEL) + ebt_deliver_counters(&replace[i]); + free(replace[i].filename); + replace[i].filename = NULL; + goto write_msg; + }else if (!strcmp(argv[1], "quit")) { + if (argc != 2) { + ebt_print_error("ebtablesd: command quit does " + "not take any arguments"); + goto write_msg; + } + break; + } + if (!(replace[table_nr].flags & OPT_KERNELDATA)) { + ebt_print_error("ebtablesd: table %s has not been " + "opened", replace[table_nr].name); + goto write_msg; + } + optind = 0; /* Setting optind = 1 causes serious annoyances */ + do_command(argc, argv, EXEC_STYLE_DAEMON, &replace[table_nr]); + ebt_reinit_extensions(); +write_msg: +#ifndef SILENT_DAEMON + if (ebt_errormsg[0] != '\0') + printf("%s.\n", ebt_errormsg); +#endif + ebt_errormsg[0]= '\0'; + n = 0; + goto continue_read; + } +do_exit: + unlink(EBTD_PIPE); + + return 0; +}
diff --git a/ebtablesu.c b/ebtablesu.c new file mode 100644 index 0000000..3cee145 --- /dev/null +++ b/ebtablesu.c
@@ -0,0 +1,89 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +static void print_help() +{ + printf("ebtablesu v"PROGVERSION" ("PROGDATE")\n"); + printf( +"Usage:\n" +"ebtablesu open table : copy the kernel table\n" +"ebtablesu fopen table file : copy the table from the specified file\n" +"ebtablesu free table : remove the table from memory\n" +"ebtablesu commit table : commit the table to the kernel\n" +"ebtablesu fcommit table file : commit the table to the specified file\n\n" +"ebtablesu <ebtables options> : the ebtables specifications\n" +" use spaces only to separate options and commands\n" +"For the ebtables options, see\n# ebtables -h\nor\n# man ebtables\n" + ); +} +int main(int argc, char *argv[]) +{ + char *arguments, *pos; + int i, writefd, len = 0; + + if (argc > EBTD_ARGC_MAX) { + fprintf(stderr, "ebtablesd accepts at most %d arguments, %d " + "arguments were specified. If you need this many " + "arguments, recompile this tool with a higher value for" + " EBTD_ARGC_MAX.\n", EBTD_ARGC_MAX - 1, argc - 1); + return -1; + } else if (argc == 1) { + fprintf(stderr, "At least one argument is needed.\n"); + print_help(); + exit(0); + } + + for (i = 0; i < argc; i++) + len += strlen(argv[i]); + /* Don't forget '\0' */ + len += argc; + if (len > EBTD_CMDLINE_MAXLN) { + fprintf(stderr, "ebtablesd has a maximum command line argument " + "length of %d, an argument length of %d was received. " + "If a smaller length is unfeasible, recompile this tool " + "with a higher value for EBTD_CMDLINE_MAXLN.\n", + EBTD_CMDLINE_MAXLN, len); + return -1; + } + + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + if (argc != 2) { + fprintf(stderr, "%s does not accept options.\n", argv[1]); + return -1; + } + print_help(); + exit(0); + } + + if (!(arguments = (char *)malloc(len))) { + fprintf(stderr, "ebtablesu: out of memory.\n"); + return -1; + } + + if ((writefd = open(EBTD_PIPE, O_WRONLY, 0)) == -1) { + fprintf(stderr, "Could not open the pipe, perhaps ebtablesd is " + "not running or you don't have write permission (try " + "running as root).\n"); + return -1; + } + + pos = arguments; + for (i = 0; i < argc; i++) { + strcpy(pos, argv[i]); + pos += strlen(argv[i]); + *(pos++) = ' '; + } + + *(pos-1) = '\n'; + if (write(writefd, arguments, len) == -1) { + perror("write"); + return -1; + } + return 0; +}
diff --git a/ethertypes b/ethertypes new file mode 100644 index 0000000..813177b --- /dev/null +++ b/ethertypes
@@ -0,0 +1,39 @@ +# +# Ethernet frame types +# This file describes some of the various Ethernet +# protocol types that are used on Ethernet networks. +# +# This list could be found on: +# http://www.iana.org/assignments/ethernet-numbers +# http://www.iana.org/assignments/ieee-802-numbers +# +# <name> <hexnumber> <alias1>...<alias35> #Comment +# +IPv4 0800 ip ip4 # Internet IP (IPv4) +X25 0805 +ARP 0806 ether-arp # +FR_ARP 0808 # Frame Relay ARP [RFC1701] +BPQ 08FF # G8BPQ AX.25 Ethernet Packet +DEC 6000 # DEC Assigned proto +DNA_DL 6001 # DEC DNA Dump/Load +DNA_RC 6002 # DEC DNA Remote Console +DNA_RT 6003 # DEC DNA Routing +LAT 6004 # DEC LAT +DIAG 6005 # DEC Diagnostics +CUST 6006 # DEC Customer use +SCA 6007 # DEC Systems Comms Arch +TEB 6558 # Trans Ether Bridging [RFC1701] +RAW_FR 6559 # Raw Frame Relay [RFC1701] +RARP 8035 # Reverse ARP [RFC903] +AARP 80F3 # Appletalk AARP +ATALK 809B # Appletalk +802_1Q 8100 8021q 1q 802.1q dot1q # 802.1Q Virtual LAN tagged frame +IPX 8137 # Novell IPX +NetBEUI 8191 # NetBEUI +IPv6 86DD ip6 # IP version 6 +PPP 880B # PPP +ATMMPOA 884C # MultiProtocol over ATM +PPP_DISC 8863 # PPPoE discovery messages +PPP_SES 8864 # PPPoE session messages +ATMFATE 8884 # Frame-based ATM Transport over Ethernet +LOOP 9000 loopback # loop proto
diff --git a/examples/perf_test/perf_test b/examples/perf_test/perf_test new file mode 100755 index 0000000..afda266 --- /dev/null +++ b/examples/perf_test/perf_test
@@ -0,0 +1,261 @@ +#!/bin/bash +# +# The ebtablesu/ebtablesd programs are deprecated, +# they're mostly useful for debugging ebtables in daemon +# mode. For fast population of tables, use ebtables-restore. +# You should probably just ignore this script. +# +# This script can be used to compare the speed of +# the different methods for adding rules in ebtables +# chains. +# +# Apart from the standard method of adding rules with +# the ebtables tool, rules can be added (faster) with +# ebtablesd running in the background and accepting +# commands through a pipe. The pipe can be written to +# with the standard shell tools. The only restriction is +# that spaces are only used to separate options and +# commands, i.e. spaces are not allowed in strings, even +# if they are between "". E.g. +# ebtablesu -A --log-prefix "a space" +# is not allowed, however +# ebtablesu -A --log-prefix "a_space" +# is allowed. +# +# Author: Bart De Schuymer +# + +export PIPE=/tmp/ebtables-v2.0.7/ebtablesd_pipe +export EBTABLES=/usr/local/sbin/ebtables +export EBTABLESD=/usr/local/sbin/ebtablesd +export EBTABLESU=/usr/local/sbin/ebtablesu + +if [[ $# = 0 ]] +then + +rm -f iets niets iets.out niets.out +MAXLIMIT=10000 +for ((LIMIT=10; LIMIT <= MAXLIMIT; LIMIT *= 10)) do + killall ebtablesd 2>/dev/null + $EBTABLES --init-table + export LIMIT + time $0 1 + echo "added" $LIMIT "rules with echo" + $EBTABLES --atomic-file iets --atomic-save + $EBTABLES -F + time $0 2 + echo "added" $LIMIT "rules with ebtables" + $EBTABLES --atomic-file niets --atomic-save + $EBTABLES --atomic-file iets -L > iets.out + $EBTABLES --atomic-file niets -L > niets.out + diff -purN iets.out niets.out + rm -f niets niets.out + killall ebtablesd 2>/dev/null + $EBTABLES -F + time $0 3 + echo "added" $LIMIT "rules with ebtablesu" + $EBTABLES --atomic-file niets --atomic-save + $EBTABLES --atomic-file iets -L > iets.out + $EBTABLES --atomic-file niets -L > niets.out + diff -purN iets.out niets.out + rm -f niets niets.out + time $0 4 + echo "added" $LIMIT "rules with atomic-file" + $EBTABLES --atomic-file niets --atomic-save + $EBTABLES --atomic-file niets -L > niets.out + diff -purN iets.out niets.out + rm -f niets niets.out + time $0 5 + echo "added" $LIMIT "rules with one atomic-commit" + $EBTABLES --atomic-file niets --atomic-save + $EBTABLES --atomic-file niets -L > niets.out + diff -purN iets.out niets.out + rm -f iets iets.out niets niets.out +done + +elif [[ $1 = "1" ]] +then + +$EBTABLESD & +pid=`jobs -p '$EBTABLESD'` +sleep 1 +$EBTABLESU open filter +# Add rules with ebtablesd +echo "$EBTABLESU -F" >>$PIPE +for ((a=1; a <= LIMIT; a++)) do + echo "$EBTABLESU -A FORWARD" >>$PIPE +done +$EBTABLESU commit filter +$EBTABLESU quit +wait $pid + +elif [[ $1 = "2" ]] +then + +# Add rules with ebtables +for ((a=1; a <= LIMIT; a++)) +do + $EBTABLES -A FORWARD +done + +elif [[ $1 = "3" ]] +then + +$EBTABLESD & +pid=`jobs -p '$EBTABLESD'` +sleep 1 +$EBTABLESU open filter +# Add rules with ebtablesu +for ((a=1; a <= LIMIT; a++)) +do + $EBTABLESU -A FORWARD +done +$EBTABLESU commit filter +$EBTABLESU quit +wait $pid + +elif [[ $1 = "4" ]] +then + +# Add rules with ebtables --atomic-file +$EBTABLES --atomic-file niets --atomic-init +for ((a=1; a <= LIMIT; a++)) +do + $EBTABLES --atomic-file niets -A FORWARD +done +$EBTABLES --atomic-file niets --atomic-commit + +else + +# Add rules taken from a binary file containing a saved table +$EBTABLES --atomic-file iets --atomic-commit + +fi + + +# From the results below, we can conclude the following about +# table constructions when speed is an issue. +# For first-time fast construction of tables, it's best to use the +# echo + ebtablesd method. If the echo method is unwanted, it +# is best to use the ebtablesu + ebtablesd method. +# The echo method is much faster because echo is a bash +# built-in command. +# Perhaps intialize the kernel tables to empty chains with +# policy DROP before constructing the table in userspace. +# For construction of tables that were constructed earlier, it +# is best to save those constructed tables to a binary file +# and then use --atomic-commit to get the table in the kernel +# (this is lightning fast compared with the other methods). +# +# System specs: +# processor : 0 +# vendor_id : AuthenticAMD +# cpu family : 6 +# model : 4 +# model name : AMD Athlon(tm) processor +# stepping : 4 +# cpu MHz : 1000.592 +# cache size : 256 KB +# MemTotal : 515780 kB +# +####10 rules#### +# +# real 0m0.078s +# user 0m0.030s +# sys 0m0.045s +# added 10 rules with echo +# +# real 0m0.098s +# user 0m0.031s +# sys 0m0.063s +# added 10 rules with ebtables +# +# real 0m0.275s +# user 0m0.083s +# sys 0m0.188s +# added 10 rules with ebtablesu +# +# real 0m0.279s +# user 0m0.082s +# sys 0m0.192s +# added 10 rules with atomic-file +# +# real 0m0.017s +# user 0m0.009s +# sys 0m0.008s +# added 10 rules with one atomic-commit +# +# real 0m0.095s +# user 0m0.050s +# sys 0m0.043s +# added 100 rules with echo +# +# real 0m0.936s +# user 0m0.315s +# sys 0m0.587s +# added 100 rules with ebtables +# +# real 0m1.967s +# user 0m0.449s +# sys 0m1.479s +# added 100 rules with ebtablesu +# +# real 0m2.472s +# user 0m0.745s +# sys 0m1.660s +# added 100 rules with atomic-file +# +# real 0m0.018s +# user 0m0.006s +# sys 0m0.012s +# added 100 rules with one atomic-commit +# +# real 0m0.740s +# user 0m0.461s +# sys 0m0.265s +# added 1000 rules with echo +# +# real 0m12.471s +# user 0m4.423s +# sys 0m7.828s +# added 1000 rules with ebtables +# +# real 0m17.715s +# user 0m6.814s +# sys 0m10.604s +# added 1000 rules with ebtablesu +# +# real 0m28.176s +# user 0m8.875s +# sys 0m18.704s +# added 1000 rules with atomic-file +# +# real 0m0.025s +# user 0m0.015s +# sys 0m0.010s +# added 1000 rules with one atomic-commit +# +# real 1m11.474s +# user 1m9.662s +# sys 0m1.035s +# added 10000 rules with echo +# +# real 10m9.418s +# user 4m19.163s +# sys 5m37.548s +# added 10000 rules with ebtables +# +# real 2m32.119s +# user 1m38.085s +# sys 0m52.298s +# added 10000 rules with ebtablesu +# +# real 13m23.396s +# user 5m29.770s +# sys 6m44.598s +# added 10000 rules with atomic-file +# +# real 0m0.319s +# user 0m0.037s +# sys 0m0.057s +# added 10000 rules with one atomic-commit
diff --git a/examples/ulog/test_ulog.c b/examples/ulog/test_ulog.c new file mode 100644 index 0000000..fbbd909 --- /dev/null +++ b/examples/ulog/test_ulog.c
@@ -0,0 +1,292 @@ + +/* + * Simple example program to log packets received with the ulog + * watcher of ebtables. + * + * usage: + * Add the appropriate ebtables ulog rule, e.g. (0 < NLGROUP < 33): + * ebtables -A FORWARD --ulog-nlgroup NLGROUP + * Start this application somewhere: + * test_ulog NLGROUP + * + * compile with make test_ulog KERNEL_INCLUDES=<path_to_kernel_include_dir> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <asm/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <time.h> +#include <linux/netlink.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <netdb.h> +#include <errno.h> +#include <netinet/if_ether.h> +#include <netinet/ether.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include "../../include/ebtables_u.h" +#include "../../include/ethernetdb.h" +#include <linux/netfilter_bridge/ebt_ulog.h> + +/* <linux/if_vlan.h> doesn't hand this to userspace :-( */ +#define VLAN_HLEN 4 +struct vlan_hdr { + unsigned short TCI; + unsigned short encap; +}; + +static struct sockaddr_nl sa_local = +{ + .nl_family = AF_NETLINK, + .nl_groups = 1, +}; + +static struct sockaddr_nl sa_kernel = +{ + .nl_family = AF_NETLINK, + .nl_pid = 0, + .nl_groups = 1, +}; + +static const char *hookstr[NF_BR_NUMHOOKS] = +{ + [NF_BR_POST_ROUTING] "POSTROUTING", + [NF_BR_PRE_ROUTING] "PREROUTING", + [NF_BR_LOCAL_OUT] "OUTPUT", + [NF_BR_LOCAL_IN] "INPUT", + [NF_BR_BROUTING] "BROUTING", + [NF_BR_FORWARD] "FORWARD" +}; + +#define DEBUG_QUEUE 0 +#define BUFLEN 65536 +static char buf[BUFLEN]; +static int sfd; + +/* Get the next ebt_ulog packet, talk to the kernel if necessary */ +ebt_ulog_packet_msg_t *ulog_get_packet() +{ + static struct nlmsghdr *nlh = NULL; + static int len, remain_len; + static int pkts_per_msg = 0; + ebt_ulog_packet_msg_t *msg; + socklen_t addrlen = sizeof(sa_kernel); + + if (!nlh) { +recv_new: + if (pkts_per_msg && DEBUG_QUEUE) + printf("PACKETS IN LAST MSG: %d\n", pkts_per_msg); + pkts_per_msg = 0; + len = recvfrom(sfd, buf, BUFLEN, 0, + (struct sockaddr *)&sa_kernel, &addrlen); + if (addrlen != sizeof(sa_kernel)) { + printf("addrlen %u != %u\n", addrlen, + (uint32_t)sizeof(sa_kernel)); + exit(-1); + } + if (len == -1) { + perror("recvmsg"); + if (errno == EINTR) + goto recv_new; + exit(-1); + } + nlh = (struct nlmsghdr *)buf; + if (nlh->nlmsg_flags & MSG_TRUNC || len > BUFLEN) { + printf("Packet truncated"); + exit(-1); + } + if (!NLMSG_OK(nlh, BUFLEN)) { + perror("Netlink message parse error\n"); + return NULL; + } + } + + msg = NLMSG_DATA(nlh); + + remain_len = (len - ((char *)nlh - buf)); + if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_type != NLMSG_DONE) + nlh = NLMSG_NEXT(nlh, remain_len); + else + nlh = NULL; + + pkts_per_msg++; + return msg; +} + +int main(int argc, char **argv) +{ + int i, curr_len, pktcnt = 0; + int rcvbufsize = BUFLEN; + ebt_ulog_packet_msg_t *msg; + struct ethhdr *ehdr; + struct ethertypeent *etype; + struct protoent *prototype; + struct iphdr *iph; + struct icmphdr *icmph; + struct tm* ptm; + char time_str[40], *ctmp; + + if (argc == 2) { + i = strtoul(argv[1], &ctmp, 10); + if (*ctmp != '\0' || i < 1 || i > 32) { + printf("Usage: %s <group number>\nWith 0 < group " + "number < 33\n", argv[0]); + exit(0); + } + sa_local.nl_groups = sa_kernel.nl_groups = 1 << (i - 1); + } + + sa_local.nl_pid = getpid(); + sfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG); + if (!sfd) { + perror("socket"); + exit(-1); + } + + if (bind(sfd, (struct sockaddr *)(&sa_local), sizeof(sa_local)) == + -1) { + perror("bind"); + exit(-1); + } + i = setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize)); + + while (1) { + if (!(msg = ulog_get_packet())) + continue; + + if (msg->version != EBT_ULOG_VERSION) { + printf("WRONG VERSION: %d INSTEAD OF %d\n", + msg->version, EBT_ULOG_VERSION); + continue; + } + printf("PACKET NR: %d\n", ++pktcnt); + iph = NULL; + curr_len = ETH_HLEN; + + printf("INDEV=%s\nOUTDEV=%s\nPHYSINDEV=%s\nPHYSOUTDEV=%s\n" + "PREFIX='%s'", msg->indev, msg->outdev, msg->physindev, + msg->physoutdev, msg->prefix); + + ptm = localtime(&msg->stamp.tv_sec); + strftime (time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", ptm); + printf("\nARRIVAL TIME: %s\nMARK=%lu\nHOOK=%s\n", time_str, + msg->mark, hookstr[msg->hook]); + + if (msg->data_len < curr_len) { + printf("====>Packet smaller than Ethernet header " + "length<====\n"); + goto letscontinue; + } + + printf("::::::::ETHERNET:HEADER::::::::\n"); + + ehdr = (struct ethhdr *)msg->data; + printf("MAC SRC=%s\n", ether_ntoa((const struct ether_addr *) + ehdr->h_source)); + printf("MAC DST=%s\nETHERNET PROTOCOL=", ether_ntoa( + (const struct ether_addr *)ehdr->h_dest)); + etype = getethertypebynumber(ntohs(ehdr->h_proto)); + if (!etype) + printf("0x%x\n", ntohs(ehdr->h_proto)); + else + printf("%s\n", etype->e_name); + + if (ehdr->h_proto == htons(ETH_P_8021Q)) { + struct vlan_hdr *vlanh = (struct vlan_hdr *) + (((char *)ehdr) + curr_len); + printf("::::::::::VLAN:HEADER::::::::::\n"); + curr_len += VLAN_HLEN; + if (msg->data_len < curr_len) { + printf("====>Packet only contains partial " + "VLAN header<====\n"); + goto letscontinue; + } + + printf("VLAN TCI=%d\n", ntohs(vlanh->TCI)); + printf("VLAN ENCAPS PROTOCOL="); + etype = getethertypebynumber(ntohs(vlanh->encap)); + if (!etype) + printf("0x%x\n", ntohs(vlanh->encap)); + else + printf("%s\n", etype->e_name); + if (ehdr->h_proto == htons(ETH_P_IP)) + iph = (struct iphdr *)(vlanh + 1); + } else if (ehdr->h_proto == htons(ETH_P_IP)) + iph = (struct iphdr *)(((char *)ehdr) + curr_len); + if (!iph) + goto letscontinue; + + curr_len += sizeof(struct iphdr); + if (msg->data_len < curr_len || msg->data_len < + (curr_len += iph->ihl * 4 - sizeof(struct iphdr))) { + printf("====>Packet only contains partial IP " + "header<====\n"); + goto letscontinue; + } + + printf(":::::::::::IP:HEADER:::::::::::\n"); + printf("IP SRC ADDR="); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&iph->saddr)[i], + (i == 3) ? "" : "."); + printf("\nIP DEST ADDR="); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&iph->daddr)[i], + (i == 3) ? "" : "."); + printf("\nIP PROTOCOL="); + if (!(prototype = getprotobynumber(iph->protocol))) + printf("%d\n", iph->protocol); + else + printf("%s\n", prototype->p_name); + + if (iph->protocol != IPPROTO_ICMP) + goto letscontinue; + + icmph = (struct icmphdr *)(((char *)ehdr) + curr_len); + curr_len += 4; + if (msg->data_len < curr_len) { +truncated_icmp: + printf("====>Packet only contains partial ICMP " + "header<====\n"); + goto letscontinue; + } + if (icmph->type != ICMP_ECHO && icmph->type != ICMP_ECHOREPLY) + goto letscontinue; + + curr_len += 4; + if (msg->data_len < curr_len) + goto truncated_icmp; + /* Normally the process id, it's sent out in machine + * byte order */ + + printf("ICMP_ECHO IDENTIFIER=%u\n", icmph->un.echo.id); + printf("ICMP_ECHO SEQ NR=%u\n", ntohs(icmph->un.echo.sequence)); + +letscontinue: + printf("===>Total Packet length: %ld, of which we examined " + "%d bytes\n", msg->data_len, curr_len); + printf("###############################\n" + "######END#OF##PACKET#DUMP######\n" + "###############################\n"); + } + + return 0; +}
diff --git a/extensions/ebt_802_3.c b/extensions/ebt_802_3.c new file mode 100644 index 0000000..d70fd44 --- /dev/null +++ b/extensions/ebt_802_3.c
@@ -0,0 +1,147 @@ +/* 802_3 + * + * Author: + * Chris Vitale <csv@bluetail.com> + * + * May 2003 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include "../include/ethernetdb.h" +#include <linux/netfilter_bridge/ebt_802_3.h> + +#define _802_3_SAP '1' +#define _802_3_TYPE '2' + +static const struct option opts[] = +{ + { "802_3-sap" , required_argument, 0, _802_3_SAP }, + { "802_3-type" , required_argument, 0, _802_3_TYPE }, + { 0 } +}; + +static void print_help() +{ + printf( +"802_3 options:\n" +"--802_3-sap [!] protocol : 802.3 DSAP/SSAP- 1 byte value (hex)\n" +" DSAP and SSAP are always the same. One SAP applies to both fields\n" +"--802_3-type [!] protocol : 802.3 SNAP Type- 2 byte value (hex)\n" +" Type implies SAP value 0xaa\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data; + + info->invflags = 0; + info->bitmask = 0; +} + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_802_3_info *info = (struct ebt_802_3_info *) (*match)->data; + unsigned int i; + char *end; + + switch (c) { + case _802_3_SAP: + ebt_check_option2(flags, _802_3_SAP); + if (ebt_check_inverse2(optarg)) + info->invflags |= EBT_802_3_SAP; + i = strtoul(optarg, &end, 16); + if (i > 255 || *end != '\0') + ebt_print_error2("Problem with specified " + "sap hex value, %x",i); + info->sap = i; /* one byte, so no byte order worries */ + info->bitmask |= EBT_802_3_SAP; + break; + case _802_3_TYPE: + ebt_check_option2(flags, _802_3_TYPE); + if (ebt_check_inverse2(optarg)) + info->invflags |= EBT_802_3_TYPE; + i = strtoul(optarg, &end, 16); + if (i > 65535 || *end != '\0') { + ebt_print_error2("Problem with the specified " + "type hex value, %x",i); + } + info->type = htons(i); + info->bitmask |= EBT_802_3_TYPE; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + if (!(entry->bitmask & EBT_802_3)) + ebt_print_error("For 802.3 DSAP/SSAP filtering the protocol " + "must be LENGTH"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_802_3_info *info = (struct ebt_802_3_info *)match->data; + + if (info->bitmask & EBT_802_3_SAP) { + printf("--802_3-sap "); + if (info->invflags & EBT_802_3_SAP) + printf("! "); + printf("0x%.2x ", info->sap); + } + if (info->bitmask & EBT_802_3_TYPE) { + printf("--802_3-type "); + if (info->invflags & EBT_802_3_TYPE) + printf("! "); + printf("0x%.4x ", ntohs(info->type)); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_802_3_info *info1 = (struct ebt_802_3_info *)m1->data; + struct ebt_802_3_info *info2 = (struct ebt_802_3_info *)m2->data; + + if (info1->bitmask != info2->bitmask) + return 0; + if (info1->invflags != info2->invflags) + return 0; + if (info1->bitmask & EBT_802_3_SAP) { + if (info1->sap != info2->sap) + return 0; + } + if (info1->bitmask & EBT_802_3_TYPE) { + if (info1->type != info2->type) + return 0; + } + return 1; +} + +static struct ebt_u_match _802_3_match = +{ + .name = "802_3", + .size = sizeof(struct ebt_802_3_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&_802_3_match); +}
diff --git a/extensions/ebt_AUDIT.c b/extensions/ebt_AUDIT.c new file mode 100644 index 0000000..c9befcc --- /dev/null +++ b/extensions/ebt_AUDIT.c
@@ -0,0 +1,110 @@ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter/xt_AUDIT.h> + +#define AUDIT_TYPE '1' +static struct option opts[] = +{ + { "audit-type" , required_argument, 0, AUDIT_TYPE }, + { 0 } +}; + +static void print_help() +{ + printf( + "AUDIT target options:\n" + " --audit-type TYPE : Set action type to record.\n"); +} + +static void init(struct ebt_entry_target *target) +{ + struct xt_AUDIT_info *info = (struct xt_AUDIT_info *) target->data; + + info->type = 0; +} + +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct xt_AUDIT_info *info = (struct xt_AUDIT_info *) (*target)->data; + + switch (c) { + case AUDIT_TYPE: + ebt_check_option2(flags, AUDIT_TYPE); + + if (!strcasecmp(optarg, "accept")) + info->type = XT_AUDIT_TYPE_ACCEPT; + else if (!strcasecmp(optarg, "drop")) + info->type = XT_AUDIT_TYPE_DROP; + else if (!strcasecmp(optarg, "reject")) + info->type = XT_AUDIT_TYPE_REJECT; + else + ebt_print_error2("Bad action type value `%s'", optarg); + + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + const struct xt_AUDIT_info *info = + (const struct xt_AUDIT_info *) target->data; + + printf("--audit-type "); + + switch(info->type) { + case XT_AUDIT_TYPE_ACCEPT: + printf("accept"); + break; + case XT_AUDIT_TYPE_DROP: + printf("drop"); + break; + case XT_AUDIT_TYPE_REJECT: + printf("reject"); + break; + } +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + const struct xt_AUDIT_info *info1 = + (const struct xt_AUDIT_info *) t1->data; + const struct xt_AUDIT_info *info2 = + (const struct xt_AUDIT_info *) t2->data; + + return info1->type == info2->type; +} + +static struct ebt_u_target AUDIT_target = +{ + .name = "AUDIT", + .size = sizeof(struct xt_AUDIT_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_target(&AUDIT_target); +}
diff --git a/extensions/ebt_among.c b/extensions/ebt_among.c new file mode 100644 index 0000000..30c098c --- /dev/null +++ b/extensions/ebt_among.c
@@ -0,0 +1,497 @@ +/* ebt_among + * + * Authors: + * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> + * + * August, 2003 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <ctype.h> +#include <unistd.h> +#include "../include/ebtables_u.h" +#include <netinet/ether.h> +#include "../include/ethernetdb.h" +#include <linux/if_ether.h> +#include <linux/netfilter_bridge/ebt_among.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define AMONG_DST '1' +#define AMONG_SRC '2' +#define AMONG_DST_F '3' +#define AMONG_SRC_F '4' + +static const struct option opts[] = { + {"among-dst", required_argument, 0, AMONG_DST}, + {"among-src", required_argument, 0, AMONG_SRC}, + {"among-dst-file", required_argument, 0, AMONG_DST_F}, + {"among-src-file", required_argument, 0, AMONG_SRC_F}, + {0} +}; + +#ifdef DEBUG +static void hexdump(const void *mem, int howmany) +{ + printf("\n"); + const unsigned char *p = mem; + int i; + + for (i = 0; i < howmany; i++) { + if (i % 32 == 0) { + printf("\n%04x: ", i); + } + printf("%2.2x%c", p[i], ". "[i % 4 == 3]); + } + printf("\n"); +} +#endif /* DEBUG */ + +static void print_help() +{ + printf( +"`among' options:\n" +"--among-dst [!] list : matches if ether dst is in list\n" +"--among-src [!] list : matches if ether src is in list\n" +"--among-dst-file [!] file : obtain dst list from file\n" +"--among-src-file [!] file : obtain src list from file\n" +"list has form:\n" +" xx:xx:xx:xx:xx:xx[=ip.ip.ip.ip],yy:yy:yy:yy:yy:yy[=ip.ip.ip.ip]" +",...,zz:zz:zz:zz:zz:zz[=ip.ip.ip.ip][,]\n" +"Things in brackets are optional.\n" +"If you want to allow two (or more) IP addresses to one MAC address, you\n" +"can specify two (or more) pairs with the same MAC, e.g.\n" +" 00:00:00:fa:eb:fe=153.19.120.250,00:00:00:fa:eb:fe=192.168.0.1\n" + ); +} +static int old_size; + +static void init(struct ebt_entry_match *match) +{ + struct ebt_among_info *amonginfo = + (struct ebt_among_info *) match->data; + + memset(amonginfo, 0, sizeof(struct ebt_among_info)); + old_size = sizeof(struct ebt_among_info); +} + +static struct ebt_mac_wormhash *new_wormhash(int n) +{ + int size = + sizeof(struct ebt_mac_wormhash) + + n * sizeof(struct ebt_mac_wormhash_tuple); + struct ebt_mac_wormhash *result = + (struct ebt_mac_wormhash *) malloc(size); + + if (!result) + ebt_print_memory(); + memset(result, 0, size); + result->poolsize = n; + return result; +} + +static void copy_wormhash(struct ebt_mac_wormhash *d, + const struct ebt_mac_wormhash *s) +{ + int dpoolsize = d->poolsize; + int dsize, ssize, amount; + + dsize = ebt_mac_wormhash_size(d); + ssize = ebt_mac_wormhash_size(s); + amount = dsize < ssize ? dsize : ssize; + memcpy(d, s, amount); + d->poolsize = dpoolsize; +} + +/* Returns: + * -1 when '\0' reached + * -2 when `n' bytes read and no delimiter found + * 0 when no less than `n' bytes read and delimiter found + * if `destbuf' is not NULL, it is filled by read bytes and ended with '\0' + * *pp is set on the first byte not copied to `destbuf' + */ +static int read_until(const char **pp, const char *delimiters, + char *destbuf, int n) +{ + int count = 0; + int ret = 0; + char c; + + while (1) { + c = **pp; + if (!c) { + ret = -1; + break; + } + if (strchr(delimiters, c)) { + ret = 0; + break; + } + if (count == n) { + ret = -2; + break; + } + if (destbuf) + destbuf[count++] = c; + (*pp)++; + } + if (destbuf) + destbuf[count] = 0; + return ret; +} + +static int fcmp(const void *va, const void *vb) { + const struct ebt_mac_wormhash_tuple *a = va; + const struct ebt_mac_wormhash_tuple *b = vb; + int ca = ((const unsigned char*)a->cmp)[7]; + int cb = ((const unsigned char*)b->cmp)[7]; + + return ca - cb; +} + +static void index_table(struct ebt_mac_wormhash *wh) +{ + int ipool, itable; + int c; + + for (itable = 0; itable <= 256; itable++) { + wh->table[itable] = wh->poolsize; + } + ipool = 0; + itable = 0; + while (1) { + wh->table[itable] = ipool; + c = ((const unsigned char*)wh->pool[ipool].cmp)[7]; + if (itable <= c) { + itable++; + } else { + ipool++; + } + if (ipool > wh->poolsize) + break; + } +} + +static struct ebt_mac_wormhash *create_wormhash(const char *arg) +{ + const char *pc = arg; + const char *anchor; + char *endptr; + struct ebt_mac_wormhash *workcopy, *result, *h; + unsigned char mac[6]; + unsigned char ip[4]; + int nmacs = 0; + int i; + char token[4]; + + if (!(workcopy = new_wormhash(1024))) { + ebt_print_memory(); + } + while (1) { + /* remember current position, we'll need it on error */ + anchor = pc; + + /* collect MAC; all its bytes are followed by ':' (colon), + * except for the last one which can be followed by + * ',' (comma), '=' or '\0' */ + for (i = 0; i < 5; i++) { + if (read_until(&pc, ":", token, 2) < 0 + || token[0] == 0) { + ebt_print_error("MAC parse error: %.20s", anchor); + free(workcopy); + return NULL; + } + mac[i] = strtol(token, &endptr, 16); + if (*endptr) { + ebt_print_error("MAC parse error: %.20s", anchor); + free(workcopy); + return NULL; + } + pc++; + } + if (read_until(&pc, "=,", token, 2) == -2 || token[0] == 0) { + ebt_print_error("MAC parse error: %.20s", anchor); + return NULL; + } + mac[i] = strtol(token, &endptr, 16); + if (*endptr) { + ebt_print_error("MAC parse error: %.20s", anchor); + return NULL; + } + if (*pc == '=') { + /* an IP follows the MAC; collect similarly to MAC */ + pc++; + anchor = pc; + for (i = 0; i < 3; i++) { + if (read_until(&pc, ".", token, 3) < 0 || token[0] == 0) { + ebt_print_error("IP parse error: %.20s", anchor); + return NULL; + } + ip[i] = strtol(token, &endptr, 10); + if (*endptr) { + ebt_print_error("IP parse error: %.20s", anchor); + return NULL; + } + pc++; + } + if (read_until(&pc, ",", token, 3) == -2 || token[0] == 0) { + ebt_print_error("IP parse error: %.20s", anchor); + return NULL; + } + ip[3] = strtol(token, &endptr, 10); + if (*endptr) { + ebt_print_error("IP parse error: %.20s", anchor); + return NULL; + } + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + ebt_print_error("Illegal IP 0.0.0.0"); + return NULL; + } + } else { + /* no IP, we set it to 0.0.0.0 */ + memset(ip, 0, 4); + } + + /* we have collected MAC and IP, so we add an entry */ + memcpy(((char *) workcopy->pool[nmacs].cmp) + 2, mac, 6); + memcpy(&(workcopy->pool[nmacs].ip), ip, 4); + nmacs++; + + /* re-allocate memory if needed */ + if (*pc && nmacs >= workcopy->poolsize) { + if (!(h = new_wormhash(nmacs * 2))) { + ebt_print_memory(); + } + copy_wormhash(h, workcopy); + free(workcopy); + workcopy = h; + } + + /* check if end of string was reached */ + if (!*pc) { + break; + } + + /* now `pc' points to comma if we are here; */ + /* increment this to the next char */ + /* but first assert :-> */ + if (*pc != ',') { + ebt_print_error("Something went wrong; no comma...\n"); + return NULL; + } + pc++; + + /* again check if end of string was reached; */ + /* we allow an ending comma */ + if (!*pc) { + break; + } + } + if (!(result = new_wormhash(nmacs))) { + ebt_print_memory(); + } + copy_wormhash(result, workcopy); + free(workcopy); + qsort(&result->pool, result->poolsize, + sizeof(struct ebt_mac_wormhash_tuple), fcmp); + index_table(result); + return result; +} + +#define OPT_DST 0x01 +#define OPT_SRC 0x02 +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_match **match) +{ + struct ebt_among_info *info = + (struct ebt_among_info *) (*match)->data; + struct ebt_mac_wormhash *wh; + struct ebt_entry_match *h; + int new_size; + long flen = 0; + int fd = -1; + + switch (c) { + case AMONG_DST_F: + case AMONG_SRC_F: + case AMONG_DST: + case AMONG_SRC: + if (c == AMONG_DST || c == AMONG_DST_F) { + ebt_check_option2(flags, OPT_DST); + } else { + ebt_check_option2(flags, OPT_SRC); + } + if (ebt_check_inverse2(optarg)) { + if (c == AMONG_DST || c == AMONG_DST_F) + info->bitmask |= EBT_AMONG_DST_NEG; + else + info->bitmask |= EBT_AMONG_SRC_NEG; + } + if (c == AMONG_DST_F || c == AMONG_SRC_F) { + struct stat stats; + + if ((fd = open(optarg, O_RDONLY)) == -1) + ebt_print_error("Couldn't open file '%s'", optarg); + fstat(fd, &stats); + flen = stats.st_size; + /* use mmap because the file will probably be big */ + optarg = mmap(0, flen, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + if (optarg == MAP_FAILED) + ebt_print_error("Couldn't map file to memory"); + if (optarg[flen-1] != '\n') + ebt_print_error("File should end with a newline"); + if (strchr(optarg, '\n') != optarg+flen-1) + ebt_print_error("File should only contain one line"); + optarg[flen-1] = '\0'; + if (ebt_errormsg[0] != '\0') { + munmap(argv, flen); + close(fd); + exit(-1); + } + } + wh = create_wormhash(optarg); + if (ebt_errormsg[0] != '\0') + break; + + new_size = old_size+ebt_mac_wormhash_size(wh); + h = malloc(sizeof(struct ebt_entry_match)+EBT_ALIGN(new_size)); + if (!h) + ebt_print_memory(); + memcpy(h, *match, old_size+sizeof(struct ebt_entry_match)); + memcpy((char *)h+old_size+sizeof(struct ebt_entry_match), wh, + ebt_mac_wormhash_size(wh)); + h->match_size = EBT_ALIGN(new_size); + info = (struct ebt_among_info *) h->data; + if (c == AMONG_DST || c == AMONG_DST_F) { + info->wh_dst_ofs = old_size; + } else { + info->wh_src_ofs = old_size; + } + old_size = new_size; + free(*match); + *match = h; + free(wh); + if (c == AMONG_DST_F || c == AMONG_SRC_F) { + munmap(argv, flen); + close(fd); + } + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, + const char *name, unsigned int hookmask, + unsigned int time) +{ +} + +#ifdef DEBUG +static void wormhash_debug(const struct ebt_mac_wormhash *wh) +{ + int i; + + printf("poolsize: %d\n", wh->poolsize); + for (i = 0; i <= 256; i++) { + printf("%02x ", wh->table[i]); + if (i % 16 == 15) { + printf("\n"); + } + } + printf("\n"); +} +#endif /* DEBUG */ + +static void wormhash_printout(const struct ebt_mac_wormhash *wh) +{ + int i; + unsigned char *ip; + + for (i = 0; i < wh->poolsize; i++) { + const struct ebt_mac_wormhash_tuple *p; + + p = (const struct ebt_mac_wormhash_tuple *)(&wh->pool[i]); + ebt_print_mac(((const unsigned char *) &p->cmp[0]) + 2); + if (p->ip) { + ip = (unsigned char *) &p->ip; + printf("=%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + } + printf(","); + } + printf(" "); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_among_info *info = (struct ebt_among_info *)match->data; + + if (info->wh_dst_ofs) { + printf("--among-dst "); + if (info->bitmask & EBT_AMONG_DST_NEG) { + printf("! "); + } + wormhash_printout(ebt_among_wh_dst(info)); + } + if (info->wh_src_ofs) { + printf("--among-src "); + if (info->bitmask & EBT_AMONG_SRC_NEG) { + printf("! "); + } + wormhash_printout(ebt_among_wh_src(info)); + } +} + +static int compare_wh(const struct ebt_mac_wormhash *aw, + const struct ebt_mac_wormhash *bw) +{ + int as, bs; + + as = ebt_mac_wormhash_size(aw); + bs = ebt_mac_wormhash_size(bw); + if (as != bs) + return 0; + if (as && memcmp(aw, bw, as)) + return 0; + return 1; +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_among_info *a = (struct ebt_among_info *) m1->data; + struct ebt_among_info *b = (struct ebt_among_info *) m2->data; + + if (!compare_wh(ebt_among_wh_dst(a), ebt_among_wh_dst(b))) + return 0; + if (!compare_wh(ebt_among_wh_src(a), ebt_among_wh_src(b))) + return 0; + if (a->bitmask != b->bitmask) + return 0; + return 1; +} + +static struct ebt_u_match among_match = { + .name = "among", + .size = sizeof(struct ebt_among_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&among_match); +}
diff --git a/extensions/ebt_arp.c b/extensions/ebt_arp.c new file mode 100644 index 0000000..84b6e90 --- /dev/null +++ b/extensions/ebt_arp.c
@@ -0,0 +1,368 @@ +/* ebt_arp + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * Tim Gardner <timg@tpi.com> + * + * April, 2002 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include "../include/ethernetdb.h" +#include <linux/if_ether.h> +#include <linux/netfilter_bridge/ebt_arp.h> + +#define ARP_OPCODE '1' +#define ARP_HTYPE '2' +#define ARP_PTYPE '3' +#define ARP_IP_S '4' +#define ARP_IP_D '5' +#define ARP_MAC_S '6' +#define ARP_MAC_D '7' +#define ARP_GRAT '8' +static const struct option opts[] = +{ + { "arp-opcode" , required_argument, 0, ARP_OPCODE }, + { "arp-op" , required_argument, 0, ARP_OPCODE }, + { "arp-htype" , required_argument, 0, ARP_HTYPE }, + { "arp-ptype" , required_argument, 0, ARP_PTYPE }, + { "arp-ip-src" , required_argument, 0, ARP_IP_S }, + { "arp-ip-dst" , required_argument, 0, ARP_IP_D }, + { "arp-mac-src" , required_argument, 0, ARP_MAC_S }, + { "arp-mac-dst" , required_argument, 0, ARP_MAC_D }, + { "arp-gratuitous", no_argument, 0, ARP_GRAT }, + { 0 } +}; + +#define NUMOPCODES 9 +/* a few names */ +static char *opcodes[] = +{ + "Request", + "Reply", + "Request_Reverse", + "Reply_Reverse", + "DRARP_Request", + "DRARP_Reply", + "DRARP_Error", + "InARP_Request", + "ARP_NAK", +}; + +static void print_help() +{ + int i; + + printf( +"arp options:\n" +"--arp-opcode [!] opcode : ARP opcode (integer or string)\n" +"--arp-htype [!] type : ARP hardware type (integer or string)\n" +"--arp-ptype [!] type : ARP protocol type (hexadecimal or string)\n" +"--arp-ip-src [!] address[/mask]: ARP IP source specification\n" +"--arp-ip-dst [!] address[/mask]: ARP IP target specification\n" +"--arp-mac-src [!] address[/mask]: ARP MAC source specification\n" +"--arp-mac-dst [!] address[/mask]: ARP MAC target specification\n" +"[!] --arp-gratuitous : ARP gratuitous packet\n" +" opcode strings: \n"); + for (i = 0; i < NUMOPCODES; i++) + printf(" %d = %s\n", i + 1, opcodes[i]); + printf( +" hardware type string: 1 = Ethernet\n" +" protocol type string: see "_PATH_ETHERTYPES"\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; + + arpinfo->invflags = 0; + arpinfo->bitmask = 0; +} + + +#define OPT_OPCODE 0x01 +#define OPT_HTYPE 0x02 +#define OPT_PTYPE 0x04 +#define OPT_IP_S 0x08 +#define OPT_IP_D 0x10 +#define OPT_MAC_S 0x20 +#define OPT_MAC_D 0x40 +#define OPT_GRAT 0x80 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data; + long int i; + char *end; + uint32_t *addr; + uint32_t *mask; + unsigned char *maddr; + unsigned char *mmask; + + switch (c) { + case ARP_OPCODE: + ebt_check_option2(flags, OPT_OPCODE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_OPCODE; + i = strtol(optarg, &end, 10); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + for (i = 0; i < NUMOPCODES; i++) + if (!strcasecmp(opcodes[i], optarg)) + break; + if (i == NUMOPCODES) + ebt_print_error2("Problem with specified ARP opcode"); + i++; + } + arpinfo->opcode = htons(i); + arpinfo->bitmask |= EBT_ARP_OPCODE; + break; + + case ARP_HTYPE: + ebt_check_option2(flags, OPT_HTYPE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_HTYPE; + i = strtol(optarg, &end, 10); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + if (!strcasecmp("Ethernet", argv[optind - 1])) + i = 1; + else + ebt_print_error2("Problem with specified ARP hardware type"); + } + arpinfo->htype = htons(i); + arpinfo->bitmask |= EBT_ARP_HTYPE; + break; + + case ARP_PTYPE: + { + uint16_t proto; + + ebt_check_option2(flags, OPT_PTYPE); + if (ebt_check_inverse2(optarg)) + arpinfo->invflags |= EBT_ARP_PTYPE; + + i = strtol(optarg, &end, 16); + if (i < 0 || i >= (0x1 << 16) || *end !='\0') { + struct ethertypeent *ent; + + ent = getethertypebyname(argv[optind - 1]); + if (!ent) + ebt_print_error2("Problem with specified ARP " + "protocol type"); + proto = ent->e_ethertype; + + } else + proto = i; + arpinfo->ptype = htons(proto); + arpinfo->bitmask |= EBT_ARP_PTYPE; + break; + } + + case ARP_IP_S: + case ARP_IP_D: + if (c == ARP_IP_S) { + ebt_check_option2(flags, OPT_IP_S); + addr = &arpinfo->saddr; + mask = &arpinfo->smsk; + arpinfo->bitmask |= EBT_ARP_SRC_IP; + } else { + ebt_check_option2(flags, OPT_IP_D); + addr = &arpinfo->daddr; + mask = &arpinfo->dmsk; + arpinfo->bitmask |= EBT_ARP_DST_IP; + } + if (ebt_check_inverse2(optarg)) { + if (c == ARP_IP_S) + arpinfo->invflags |= EBT_ARP_SRC_IP; + else + arpinfo->invflags |= EBT_ARP_DST_IP; + } + ebt_parse_ip_address(optarg, addr, mask); + break; + + case ARP_MAC_S: + case ARP_MAC_D: + if (c == ARP_MAC_S) { + ebt_check_option2(flags, OPT_MAC_S); + maddr = arpinfo->smaddr; + mmask = arpinfo->smmsk; + arpinfo->bitmask |= EBT_ARP_SRC_MAC; + } else { + ebt_check_option2(flags, OPT_MAC_D); + maddr = arpinfo->dmaddr; + mmask = arpinfo->dmmsk; + arpinfo->bitmask |= EBT_ARP_DST_MAC; + } + if (ebt_check_inverse2(optarg)) { + if (c == ARP_MAC_S) + arpinfo->invflags |= EBT_ARP_SRC_MAC; + else + arpinfo->invflags |= EBT_ARP_DST_MAC; + } + if (ebt_get_mac_and_mask(optarg, maddr, mmask)) + ebt_print_error2("Problem with ARP MAC address argument"); + break; + case ARP_GRAT: + ebt_check_option2(flags, OPT_GRAT); + arpinfo->bitmask |= EBT_ARP_GRAT; + if (ebt_invert) + arpinfo->invflags |= EBT_ARP_GRAT; + break; + + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) || + entry->invflags & EBT_IPROTO) + ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data; + int i; + + if (arpinfo->bitmask & EBT_ARP_OPCODE) { + int opcode = ntohs(arpinfo->opcode); + printf("--arp-op "); + if (arpinfo->invflags & EBT_ARP_OPCODE) + printf("! "); + if (opcode > 0 && opcode <= NUMOPCODES) + printf("%s ", opcodes[opcode - 1]); + else + printf("%d ", opcode); + } + if (arpinfo->bitmask & EBT_ARP_HTYPE) { + printf("--arp-htype "); + if (arpinfo->invflags & EBT_ARP_HTYPE) + printf("! "); + printf("%d ", ntohs(arpinfo->htype)); + } + if (arpinfo->bitmask & EBT_ARP_PTYPE) { + struct ethertypeent *ent; + + printf("--arp-ptype "); + if (arpinfo->invflags & EBT_ARP_PTYPE) + printf("! "); + ent = getethertypebynumber(ntohs(arpinfo->ptype)); + if (!ent) + printf("0x%x ", ntohs(arpinfo->ptype)); + else + printf("%s ", ent->e_name); + } + if (arpinfo->bitmask & EBT_ARP_SRC_IP) { + printf("--arp-ip-src "); + if (arpinfo->invflags & EBT_ARP_SRC_IP) + printf("! "); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i], + (i == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(arpinfo->smsk)); + } + if (arpinfo->bitmask & EBT_ARP_DST_IP) { + printf("--arp-ip-dst "); + if (arpinfo->invflags & EBT_ARP_DST_IP) + printf("! "); + for (i = 0; i < 4; i++) + printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i], + (i == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk)); + } + if (arpinfo->bitmask & EBT_ARP_SRC_MAC) { + printf("--arp-mac-src "); + if (arpinfo->invflags & EBT_ARP_SRC_MAC) + printf("! "); + ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk); + printf(" "); + } + if (arpinfo->bitmask & EBT_ARP_DST_MAC) { + printf("--arp-mac-dst "); + if (arpinfo->invflags & EBT_ARP_DST_MAC) + printf("! "); + ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk); + printf(" "); + } + if (arpinfo->bitmask & EBT_ARP_GRAT) { + if (arpinfo->invflags & EBT_ARP_GRAT) + printf("! "); + printf("--arp-gratuitous "); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data; + struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data; + + if (arpinfo1->bitmask != arpinfo2->bitmask) + return 0; + if (arpinfo1->invflags != arpinfo2->invflags) + return 0; + if (arpinfo1->bitmask & EBT_ARP_OPCODE) { + if (arpinfo1->opcode != arpinfo2->opcode) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_HTYPE) { + if (arpinfo1->htype != arpinfo2->htype) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_PTYPE) { + if (arpinfo1->ptype != arpinfo2->ptype) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_SRC_IP) { + if (arpinfo1->saddr != arpinfo2->saddr) + return 0; + if (arpinfo1->smsk != arpinfo2->smsk) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_DST_IP) { + if (arpinfo1->daddr != arpinfo2->daddr) + return 0; + if (arpinfo1->dmsk != arpinfo2->dmsk) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) { + if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN)) + return 0; + if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN)) + return 0; + } + if (arpinfo1->bitmask & EBT_ARP_DST_MAC) { + if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN)) + return 0; + if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN)) + return 0; + } + return 1; +} + +static struct ebt_u_match arp_match = +{ + .name = "arp", + .size = sizeof(struct ebt_arp_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&arp_match); +}
diff --git a/extensions/ebt_arpreply.c b/extensions/ebt_arpreply.c new file mode 100644 index 0000000..399868b --- /dev/null +++ b/extensions/ebt_arpreply.c
@@ -0,0 +1,139 @@ +/* ebt_arpreply + * + * Authors: + * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> + * Bart De Schuymer <bdschuym@pandora.be> + * + * August, 2003 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <netinet/ether.h> +#include <linux/netfilter_bridge/ebt_arpreply.h> + +static int mac_supplied; + +#define REPLY_MAC '1' +#define REPLY_TARGET '2' +static const struct option opts[] = +{ + { "arpreply-mac" , required_argument, 0, REPLY_MAC }, + { "arpreply-target" , required_argument, 0, REPLY_TARGET }, + { 0 } +}; + +static void print_help() +{ + printf( + "arpreply target options:\n" + " --arpreply-mac address : source MAC of generated reply\n" + " --arpreply-target target : ACCEPT, DROP, RETURN or CONTINUE\n" + " (standard target is DROP)\n"); +} + +static void init(struct ebt_entry_target *target) +{ + struct ebt_arpreply_info *replyinfo = + (struct ebt_arpreply_info *)target->data; + + replyinfo->target = EBT_DROP; + memset(replyinfo->mac, 0, ETH_ALEN); + mac_supplied = 0; +} + +#define OPT_REPLY_MAC 0x01 +#define OPT_REPLY_TARGET 0x02 +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_arpreply_info *replyinfo = + (struct ebt_arpreply_info *)(*target)->data; + struct ether_addr *addr; + + switch (c) { + case REPLY_MAC: + ebt_check_option2(flags, OPT_REPLY_MAC); + if (!(addr = ether_aton(optarg))) + ebt_print_error2("Problem with specified --arpreply-mac mac"); + memcpy(replyinfo->mac, addr, ETH_ALEN); + mac_supplied = 1; + break; + case REPLY_TARGET: + ebt_check_option2(flags, OPT_REPLY_TARGET); + if (FILL_TARGET(optarg, replyinfo->target)) + ebt_print_error2("Illegal --arpreply-target target"); + break; + + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_arpreply_info *replyinfo = + (struct ebt_arpreply_info *)target->data; + + if (entry->ethproto != ETH_P_ARP || entry->invflags & EBT_IPROTO) { + ebt_print_error("For ARP replying the protocol must be specified as ARP"); + } else if (time == 0 && mac_supplied == 0) { + ebt_print_error("No arpreply mac supplied"); + } else if (BASE_CHAIN && replyinfo->target == EBT_RETURN) { + ebt_print_error("--arpreply-target RETURN not allowed on base chain"); + } else { + CLEAR_BASE_CHAIN_BIT; + if (strcmp(name, "nat") || hookmask & ~(1 << NF_BR_PRE_ROUTING)) + ebt_print_error("arpreply only allowed in PREROUTING"); + } +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_arpreply_info *replyinfo = + (struct ebt_arpreply_info *)target->data; + + printf("--arpreply-mac "); + ebt_print_mac(replyinfo->mac); + if (replyinfo->target == EBT_DROP) + return; + printf(" --arpreply-target %s", TARGET_NAME(replyinfo->target)); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + struct ebt_arpreply_info *replyinfo1 = + (struct ebt_arpreply_info *)t1->data; + struct ebt_arpreply_info *replyinfo2 = + (struct ebt_arpreply_info *)t2->data; + + return memcmp(replyinfo1->mac, replyinfo2->mac, ETH_ALEN) == 0 + && replyinfo1->target == replyinfo2->target; +} + +static struct ebt_u_target arpreply_target = +{ + .name = "arpreply", + .size = sizeof(struct ebt_arpreply_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_target(&arpreply_target); +}
diff --git a/extensions/ebt_inat.c b/extensions/ebt_inat.c new file mode 100644 index 0000000..48f65b4 --- /dev/null +++ b/extensions/ebt_inat.c
@@ -0,0 +1,386 @@ +/* ebt_inat + * + * Authors: + * Grzegorz Borowiak <grzes@gnu.univ.gda.pl> + * + * August, 2003 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/ether.h> +#include <getopt.h> +#include <ctype.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_inat.h> + +static int s_sub_supplied, d_sub_supplied; + +#define NAT_S '1' +#define NAT_D '1' +#define NAT_S_SUB '2' +#define NAT_D_SUB '2' +#define NAT_S_TARGET '3' +#define NAT_D_TARGET '3' +static const struct option opts_s[] = +{ + { "isnat-list" , required_argument, 0, NAT_S }, + { "isnat-sub" , required_argument, 0, NAT_S_SUB }, + { "isnat-default-target" , required_argument, 0, NAT_S_TARGET }, + { 0 } +}; + +static const struct option opts_d[] = +{ + { "idnat-list" , required_argument, 0, NAT_D }, + { "idnat-sub" , required_argument, 0, NAT_D_SUB }, + { "idnat-default-target" , required_argument, 0, NAT_D_TARGET }, + { 0 } +}; + +static void print_help_common(const char *cas) +{ + printf( +"isnat options:\n" +" --i%1.1snat-list : indexed list of MAC addresses\n" +" --i%1.1snat-sub : /24 subnet to which the rule apply\n" +" --i%1.1snat-default-target target : ACCEPT, DROP, RETURN or CONTINUE\n" +"Indexed list of addresses is as follows:\n" +"\tlist := chunk\n" +"\tlist := list chunk\n" +"\tchunk := pair ','\n" +"\tpair := index '=' action\n" +"\taction := mac_addr\n" +"\taction := mac_addr '+'\n" +"\taction := '_'\n" +"where\n" +"\tindex -- an integer [0..255]\n" +"\tmac_addr -- a MAC address in format xx:xx:xx:xx:xx:xx\n" +"If '_' at some index is specified, packets with last %s IP address byte\n" +"equal to index are DROPped. If there is a MAC address, they are %1.1snatted\n" +"to this and the target is CONTINUE. If this MAC is followed by '+', the\n" +"target is ACCEPT.\n" +"For example,\n" +"--idnat-list 2=20:21:22:23:24:25,4=_,7=30:31:32:33:34:35,\n" +"is valid.\n" +"The subnet MUST be specified. Only packets with 3 first bytes of their\n" +"%s address are considered. --i%1.1snat-sub parameter must begin with\n" +"3 integers separated by dots. Only they are considered, the rest is ignored.\n" +"No matter if you write '192.168.42.', '192.168.42.0', '192.168.42.12',\n" +"'192.168.42.0/24', '192.168.42.0/23' or '192.168.42.i%1.1snat_sucks!!!',\n" +"The numbers 192, 168 and 42 are taken and it behaves like a /24 IP subnet.\n" +"--i%1.1snat-default-target affects only the packet not matching the subnet.\n", + cas, cas, cas, cas, cas, cas, cas, cas, + cas, cas, cas, cas, cas, cas, cas, cas + ); +} + +static void print_help_s() +{ + print_help_common("src"); +} + +static void print_help_d() +{ + print_help_common("dest"); +} + +static void init_s(struct ebt_entry_target *target) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data; + + s_sub_supplied = 0; + memset(natinfo, 0, sizeof(struct ebt_inat_info)); + natinfo->target = EBT_CONTINUE; + return; +} + +static void init_d(struct ebt_entry_target *target) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data; + + d_sub_supplied = 0; + memset(natinfo, 0, sizeof(struct ebt_inat_info)); + natinfo->target = EBT_CONTINUE; + return; +} + +static void parse_list(const char *arg, struct ebt_inat_info *info) +{ + int i; + char c; + int count = 0; + int now_index = 1; + int index; + char buf[4]; + unsigned char mac[6]; + int ibuf = 0; + int imac = 0; + int target; + memset(buf, 0, 4); + i = 0; + while (1) { + c = arg[i]; + if (now_index) { + if (isdigit(c)) { + buf[ibuf++] = c; + if (ibuf > 3) { + print_error("Index too long at position %d", i); + } + goto next; + } + if (c == '=') { + if (ibuf == 0) { + print_error("Integer index expected before '=' at position %d", i); + } + buf[ibuf] = 0; + ibuf = 0; + index = atoi(buf); + if (index < 0 || 255 < index) { + print_error("Index out of range [0..255], namely %d", index); + } + now_index = 0; + memset(mac, 0, 6); + imac = 0; + target = EBT_CONTINUE; + goto next; + } + if (c == '\0') { + goto next; + } + print_error("Unexpected '%c' where integer or '=' expected", c); + } + else { + if (isxdigit(c)) { + buf[ibuf++] = c; + if (ibuf > 2) { + print_error("MAC address chunk too long at position %d", i); + } + goto next; + } + if (c == ':' || c == ',' || c == '\0') { + buf[ibuf] = 0; + ibuf = 0; + mac[imac++] = strtol(buf, 0, 16); + if (c == ',' || c == '\0') { + info->a[index].enabled = 1; + info->a[index].target = target; + memcpy(info->a[index].mac, mac, 6); + now_index = 1; + count++; + goto next; + } + if (c == ':' && imac >= 6) { + print_error("Too many MAC address chunks at position %d", i); + } + goto next; + } + if (c == '_') { + target = EBT_DROP; + goto next; + } + if (c == '+') { + target = EBT_ACCEPT; + goto next; + } + print_error("Unexpected '%c' where hex digit, '_', '+', ',' or end of string expected", c); + } + next: + if (!c) break; + i++; + } + if (count == 0) { + print_error("List empty"); + } +} + +static uint32_t parse_ip(const char *s) +{ + int a0, a1, a2; + char ip[4]; + sscanf(s, "%d.%d.%d", &a0, &a1, &a2); + ip[0] = a0; + ip[1] = a1; + ip[2] = a2; + ip[3] = 0; + return *(uint32_t*)ip; +} + +#define OPT_ISNAT 0x01 +#define OPT_ISNAT_SUB 0x02 +#define OPT_ISNAT_TARGET 0x04 +static int parse_s(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data; + + switch (c) { + case NAT_S: + check_option(flags, OPT_ISNAT); + parse_list(optarg, natinfo); + break; + case NAT_S_TARGET: + check_option(flags, OPT_ISNAT_TARGET); + if (FILL_TARGET(optarg, natinfo->target)) + print_error("Illegal --isnat-default-target target"); + break; + case NAT_S_SUB: + natinfo->ip_subnet = parse_ip(optarg); + s_sub_supplied = 1; + break; + default: + return 0; + } + return 1; +} + +#define OPT_IDNAT 0x01 +#define OPT_IDNAT_SUB 0x02 +#define OPT_IDNAT_TARGET 0x04 +static int parse_d(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)(*target)->data; + + switch (c) { + case NAT_D: + check_option(flags, OPT_IDNAT); + parse_list(optarg, natinfo); + break; + case NAT_D_TARGET: + check_option(flags, OPT_IDNAT_TARGET); + if (FILL_TARGET(optarg, natinfo->target)) + print_error("Illegal --idnat-default-target target"); + break; + case NAT_D_SUB: + natinfo->ip_subnet = parse_ip(optarg); + d_sub_supplied = 1; + break; + default: + return 0; + } + return 1; +} + +static void final_check_s(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data; + + if (BASE_CHAIN && natinfo->target == EBT_RETURN) + print_error("--isnat-default-target RETURN not allowed on base chain"); + CLEAR_BASE_CHAIN_BIT; + if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat")) + print_error("Wrong chain for isnat"); + if (time == 0 && s_sub_supplied == 0) + print_error("No isnat subnet supplied"); +} + +static void final_check_d(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_inat_info *natinfo = (struct ebt_inat_info *)target->data; + + if (BASE_CHAIN && natinfo->target == EBT_RETURN) + print_error("--idnat-default-target RETURN not allowed on base chain"); + CLEAR_BASE_CHAIN_BIT; + if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) + || strcmp(name, "nat")) && + ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute"))) + print_error("Wrong chain for idnat"); + if (time == 0 && d_sub_supplied == 0) + print_error("No idnat subnet supplied"); +} + +static void print_list(const struct ebt_inat_info *info) +{ + int i; + for (i = 0; i < 256; i++) { + if (info->a[i].enabled) { + printf("%d=", i); + if (info->a[i].target == EBT_DROP) { + printf("_"); + } + else { + if (info->a[i].target == EBT_ACCEPT) { + printf("+"); + } + print_mac(info->a[i].mac); + } + printf(","); + } + } +} + +static void print_s(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_inat_info *info = (struct ebt_inat_info *)target->data; + + unsigned char sub[4]; + *(uint32_t*)sub = info->ip_subnet; + printf("--isnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]); + printf(" --isnat-list "); + print_list(info); + printf(" --isnat-default-target %s", TARGET_NAME(info->target)); +} + +static void print_d(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_inat_info *info = (struct ebt_inat_info *)target->data; + + unsigned char sub[4]; + *(uint32_t*)sub = info->ip_subnet; + printf("--idnat-sub %u.%u.%u.0/24", sub[0], sub[1], sub[2]); + printf(" --idnat-list "); + print_list(info); + printf(" --idnat-default-target %s", TARGET_NAME(info->target)); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + struct ebt_inat_info *natinfo1 = (struct ebt_inat_info *)t1->data; + struct ebt_inat_info *natinfo2 = (struct ebt_inat_info *)t2->data; + + + return !memcmp(natinfo1, natinfo2, sizeof(struct ebt_inat_info)); +} + +static struct ebt_u_target isnat_target = +{ + .name = EBT_ISNAT_TARGET, + .size = sizeof(struct ebt_inat_info), + .help = print_help_s, + .init = init_s, + .parse = parse_s, + .final_check = final_check_s, + .print = print_s, + .compare = compare, + .extra_ops = opts_s, +}; + +static struct ebt_u_target idnat_target = +{ + .name = EBT_IDNAT_TARGET, + .size = sizeof(struct ebt_inat_info), + .help = print_help_d, + .init = init_d, + .parse = parse_d, + .final_check = final_check_d, + .print = print_d, + .compare = compare, + .extra_ops = opts_d, +}; + +static void _INIT(void) +{ + register_target(&isnat_target); + register_target(&idnat_target); +}
diff --git a/extensions/ebt_ip.c b/extensions/ebt_ip.c new file mode 100644 index 0000000..faffade --- /dev/null +++ b/extensions/ebt_ip.c
@@ -0,0 +1,478 @@ +/* ebt_ip + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * Changes: + * added ip-sport and ip-dport; parsing of port arguments is + * based on code from iptables-1.2.7a + * Innominate Security Technologies AG <mhopf@innominate.com> + * September, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <netdb.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_ip.h> + +#define IP_SOURCE '1' +#define IP_DEST '2' +#define IP_myTOS '3' /* include/bits/in.h seems to already define IP_TOS */ +#define IP_PROTO '4' +#define IP_SPORT '5' +#define IP_DPORT '6' +#define IP_ICMP '7' +#define IP_IGMP '8' + +static const struct option opts[] = +{ + { "ip-source" , required_argument, 0, IP_SOURCE }, + { "ip-src" , required_argument, 0, IP_SOURCE }, + { "ip-destination" , required_argument, 0, IP_DEST }, + { "ip-dst" , required_argument, 0, IP_DEST }, + { "ip-tos" , required_argument, 0, IP_myTOS }, + { "ip-protocol" , required_argument, 0, IP_PROTO }, + { "ip-proto" , required_argument, 0, IP_PROTO }, + { "ip-source-port" , required_argument, 0, IP_SPORT }, + { "ip-sport" , required_argument, 0, IP_SPORT }, + { "ip-destination-port" , required_argument, 0, IP_DPORT }, + { "ip-dport" , required_argument, 0, IP_DPORT }, + { "ip-icmp-type" , required_argument, 0, IP_ICMP }, + { "ip-igmp-type" , required_argument, 0, IP_IGMP }, + { 0 } +}; + +static const struct ebt_icmp_names icmp_codes[] = { + { "echo-reply", 0, 0, 0xFF }, + /* Alias */ { "pong", 0, 0, 0xFF }, + + { "destination-unreachable", 3, 0, 0xFF }, + { "network-unreachable", 3, 0, 0 }, + { "host-unreachable", 3, 1, 1 }, + { "protocol-unreachable", 3, 2, 2 }, + { "port-unreachable", 3, 3, 3 }, + { "fragmentation-needed", 3, 4, 4 }, + { "source-route-failed", 3, 5, 5 }, + { "network-unknown", 3, 6, 6 }, + { "host-unknown", 3, 7, 7 }, + { "network-prohibited", 3, 9, 9 }, + { "host-prohibited", 3, 10, 10 }, + { "TOS-network-unreachable", 3, 11, 11 }, + { "TOS-host-unreachable", 3, 12, 12 }, + { "communication-prohibited", 3, 13, 13 }, + { "host-precedence-violation", 3, 14, 14 }, + { "precedence-cutoff", 3, 15, 15 }, + + { "source-quench", 4, 0, 0xFF }, + + { "redirect", 5, 0, 0xFF }, + { "network-redirect", 5, 0, 0 }, + { "host-redirect", 5, 1, 1 }, + { "TOS-network-redirect", 5, 2, 2 }, + { "TOS-host-redirect", 5, 3, 3 }, + + { "echo-request", 8, 0, 0xFF }, + /* Alias */ { "ping", 8, 0, 0xFF }, + + { "router-advertisement", 9, 0, 0xFF }, + + { "router-solicitation", 10, 0, 0xFF }, + + { "time-exceeded", 11, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 11, 0, 0xFF }, + { "ttl-zero-during-transit", 11, 0, 0 }, + { "ttl-zero-during-reassembly", 11, 1, 1 }, + + { "parameter-problem", 12, 0, 0xFF }, + { "ip-header-bad", 12, 0, 0 }, + { "required-option-missing", 12, 1, 1 }, + + { "timestamp-request", 13, 0, 0xFF }, + + { "timestamp-reply", 14, 0, 0xFF }, + + { "address-mask-request", 17, 0, 0xFF }, + + { "address-mask-reply", 18, 0, 0xFF } +}; + +static const struct ebt_icmp_names igmp_types[] = { + { "membership-query", 0x11 }, + { "membership-report-v1", 0x12 }, + { "membership-report-v2", 0x16 }, + { "leave-group", 0x17 }, + { "membership-report-v3", 0x22 }, +}; + +/* put the mask into 4 bytes */ +/* transform a protocol and service name into a port number */ +static uint16_t parse_port(const char *protocol, const char *name) +{ + struct servent *service; + char *end; + int port; + + port = strtol(name, &end, 10); + if (*end != '\0') { + if (protocol && + (service = getservbyname(name, protocol)) != NULL) + return ntohs(service->s_port); + } + else if (port >= 0 || port <= 0xFFFF) { + return port; + } + ebt_print_error("Problem with specified %s port '%s'", + protocol?protocol:"", name); + return 0; +} + +static void +parse_port_range(const char *protocol, const char *portstring, uint16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = parse_port(protocol, buffer); + else { + *cp = '\0'; + cp++; + ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0; + if (ebt_errormsg[0] != '\0') + return; + ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF; + if (ebt_errormsg[0] != '\0') + return; + + if (ports[0] > ports[1]) + ebt_print_error("Invalid portrange (min > max)"); + } + free(buffer); +} + +static void print_port_range(uint16_t *ports) +{ + if (ports[0] == ports[1]) + printf("%d ", ports[0]); + else + printf("%d:%d ", ports[0], ports[1]); +} + +static void print_help() +{ + printf( +"ip options:\n" +"--ip-src [!] address[/mask]: ip source specification\n" +"--ip-dst [!] address[/mask]: ip destination specification\n" +"--ip-tos [!] tos : ip tos specification\n" +"--ip-proto [!] protocol : ip protocol specification\n" +"--ip-sport [!] port[:port] : tcp/udp source port or port range\n" +"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n" +"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n" +"--ip-igmp-type [!] type[:type] : igmp type or type range\n"); + + printf("\nValid ICMP Types:\n"); + ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes)); + printf("\nValid IGMP Types:\n"); + ebt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types)); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + + ipinfo->invflags = 0; + ipinfo->bitmask = 0; +} + +#define OPT_SOURCE 0x01 +#define OPT_DEST 0x02 +#define OPT_TOS 0x04 +#define OPT_PROTO 0x08 +#define OPT_SPORT 0x10 +#define OPT_DPORT 0x20 +#define OPT_ICMP 0x40 +#define OPT_IGMP 0x80 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data; + char *end; + long int i; + + switch (c) { + case IP_SOURCE: + ebt_check_option2(flags, OPT_SOURCE); + ipinfo->bitmask |= EBT_IP_SOURCE; + + case IP_DEST: + if (c == IP_DEST) { + ebt_check_option2(flags, OPT_DEST); + ipinfo->bitmask |= EBT_IP_DEST; + } + if (ebt_check_inverse2(optarg)) { + if (c == IP_SOURCE) + ipinfo->invflags |= EBT_IP_SOURCE; + else + ipinfo->invflags |= EBT_IP_DEST; + } + if (c == IP_SOURCE) + ebt_parse_ip_address(optarg, &ipinfo->saddr, &ipinfo->smsk); + else + ebt_parse_ip_address(optarg, &ipinfo->daddr, &ipinfo->dmsk); + break; + + case IP_SPORT: + case IP_DPORT: + if (c == IP_SPORT) { + ebt_check_option2(flags, OPT_SPORT); + ipinfo->bitmask |= EBT_IP_SPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_SPORT; + } else { + ebt_check_option2(flags, OPT_DPORT); + ipinfo->bitmask |= EBT_IP_DPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_DPORT; + } + if (c == IP_SPORT) + parse_port_range(NULL, optarg, ipinfo->sport); + else + parse_port_range(NULL, optarg, ipinfo->dport); + break; + + case IP_ICMP: + ebt_check_option2(flags, OPT_ICMP); + ipinfo->bitmask |= EBT_IP_ICMP; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_ICMP; + if (ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg, + ipinfo->icmp_type, ipinfo->icmp_code)) + return 0; + break; + + case IP_IGMP: + ebt_check_option2(flags, OPT_IGMP); + ipinfo->bitmask |= EBT_IP_IGMP; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_IGMP; + if (ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg, + ipinfo->igmp_type, NULL)) + return 0; + break; + + case IP_myTOS: + ebt_check_option2(flags, OPT_TOS); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_TOS; + i = strtol(optarg, &end, 16); + if (i < 0 || i > 255 || *end != '\0') + ebt_print_error2("Problem with specified IP tos"); + ipinfo->tos = i; + ipinfo->bitmask |= EBT_IP_TOS; + break; + + case IP_PROTO: + ebt_check_option2(flags, OPT_PROTO); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP_PROTO; + i = strtoul(optarg, &end, 10); + if (*end != '\0') { + struct protoent *pe; + + pe = getprotobyname(optarg); + if (pe == NULL) + ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]); + ipinfo->protocol = pe->p_proto; + } else { + ipinfo->protocol = (unsigned char) i; + } + ipinfo->bitmask |= EBT_IP_PROTO; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + + if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO) { + ebt_print_error("For IP filtering the protocol must be " + "specified as IPv4"); + } else if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + (ipinfo->protocol!=IPPROTO_TCP && + ipinfo->protocol!=IPPROTO_UDP && + ipinfo->protocol!=IPPROTO_SCTP && + ipinfo->protocol!=IPPROTO_DCCP))) { + ebt_print_error("For port filtering the IP protocol must be " + "either 6 (tcp), 17 (udp), 33 (dccp) or " + "132 (sctp)"); + } else if ((ipinfo->bitmask & EBT_IP_ICMP) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + ipinfo->protocol != IPPROTO_ICMP)) { + ebt_print_error("For ICMP filtering the IP protocol must be " + "1 (icmp)"); + } else if ((ipinfo->bitmask & EBT_IP_IGMP) && + (!(ipinfo->bitmask & EBT_IP_PROTO) || + ipinfo->invflags & EBT_IP_PROTO || + ipinfo->protocol != IPPROTO_IGMP)) { + ebt_print_error("For IGMP filtering the IP protocol must be " + "2 (igmp)"); + } +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data; + int j; + + if (ipinfo->bitmask & EBT_IP_SOURCE) { + printf("--ip-src "); + if (ipinfo->invflags & EBT_IP_SOURCE) + printf("! "); + for (j = 0; j < 4; j++) + printf("%d%s",((unsigned char *)&ipinfo->saddr)[j], + (j == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(ipinfo->smsk)); + } + if (ipinfo->bitmask & EBT_IP_DEST) { + printf("--ip-dst "); + if (ipinfo->invflags & EBT_IP_DEST) + printf("! "); + for (j = 0; j < 4; j++) + printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j], + (j == 3) ? "" : "."); + printf("%s ", ebt_mask_to_dotted(ipinfo->dmsk)); + } + if (ipinfo->bitmask & EBT_IP_TOS) { + printf("--ip-tos "); + if (ipinfo->invflags & EBT_IP_TOS) + printf("! "); + printf("0x%02X ", ipinfo->tos); + } + if (ipinfo->bitmask & EBT_IP_PROTO) { + struct protoent *pe; + + printf("--ip-proto "); + if (ipinfo->invflags & EBT_IP_PROTO) + printf("! "); + pe = getprotobynumber(ipinfo->protocol); + if (pe == NULL) { + printf("%d ", ipinfo->protocol); + } else { + printf("%s ", pe->p_name); + } + } + if (ipinfo->bitmask & EBT_IP_SPORT) { + printf("--ip-sport "); + if (ipinfo->invflags & EBT_IP_SPORT) + printf("! "); + print_port_range(ipinfo->sport); + } + if (ipinfo->bitmask & EBT_IP_DPORT) { + printf("--ip-dport "); + if (ipinfo->invflags & EBT_IP_DPORT) + printf("! "); + print_port_range(ipinfo->dport); + } + if (ipinfo->bitmask & EBT_IP_ICMP) { + printf("--ip-icmp-type "); + if (ipinfo->invflags & EBT_IP_ICMP) + printf("! "); + ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes), + ipinfo->icmp_type, ipinfo->icmp_code); + } + if (ipinfo->bitmask & EBT_IP_IGMP) { + printf("--ip-igmp-type "); + if (ipinfo->invflags & EBT_IP_IGMP) + printf("! "); + ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types), + ipinfo->igmp_type, NULL); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data; + struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data; + + if (ipinfo1->bitmask != ipinfo2->bitmask) + return 0; + if (ipinfo1->invflags != ipinfo2->invflags) + return 0; + if (ipinfo1->bitmask & EBT_IP_SOURCE) { + if (ipinfo1->saddr != ipinfo2->saddr) + return 0; + if (ipinfo1->smsk != ipinfo2->smsk) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_DEST) { + if (ipinfo1->daddr != ipinfo2->daddr) + return 0; + if (ipinfo1->dmsk != ipinfo2->dmsk) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_TOS) { + if (ipinfo1->tos != ipinfo2->tos) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_PROTO) { + if (ipinfo1->protocol != ipinfo2->protocol) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_SPORT) { + if (ipinfo1->sport[0] != ipinfo2->sport[0] || + ipinfo1->sport[1] != ipinfo2->sport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_DPORT) { + if (ipinfo1->dport[0] != ipinfo2->dport[0] || + ipinfo1->dport[1] != ipinfo2->dport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_ICMP) { + if (ipinfo1->icmp_type[0] != ipinfo2->icmp_type[0] || + ipinfo1->icmp_type[1] != ipinfo2->icmp_type[1] || + ipinfo1->icmp_code[0] != ipinfo2->icmp_code[0] || + ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP_IGMP) { + if (ipinfo1->igmp_type[0] != ipinfo2->igmp_type[0] || + ipinfo1->igmp_type[1] != ipinfo2->igmp_type[1]) + return 0; + } + return 1; +} + +static struct ebt_u_match ip_match = +{ + .name = "ip", + .size = sizeof(struct ebt_ip_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&ip_match); +}
diff --git a/extensions/ebt_ip6.c b/extensions/ebt_ip6.c new file mode 100644 index 0000000..17a4303 --- /dev/null +++ b/extensions/ebt_ip6.c
@@ -0,0 +1,419 @@ +/* ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng <kuo-lang.tseng@intel.com> + * Manohar Castelino <manohar.castelino@intel.com> + * + * Summary: + * This is just a modification of the IPv4 code written by + * Bart De Schuymer <bdschuym@pandora.be> + * with the changes required to support IPv6 + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <netdb.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_ip6.h> + + + +#define IP_SOURCE '1' +#define IP_DEST '2' +#define IP_TCLASS '3' +#define IP_PROTO '4' +#define IP_SPORT '5' +#define IP_DPORT '6' +#define IP_ICMP6 '7' + +static const struct option opts[] = +{ + { "ip6-source" , required_argument, 0, IP_SOURCE }, + { "ip6-src" , required_argument, 0, IP_SOURCE }, + { "ip6-destination" , required_argument, 0, IP_DEST }, + { "ip6-dst" , required_argument, 0, IP_DEST }, + { "ip6-traffic-class" , required_argument, 0, IP_TCLASS }, + { "ip6-tclass" , required_argument, 0, IP_TCLASS }, + { "ip6-protocol" , required_argument, 0, IP_PROTO }, + { "ip6-proto" , required_argument, 0, IP_PROTO }, + { "ip6-source-port" , required_argument, 0, IP_SPORT }, + { "ip6-sport" , required_argument, 0, IP_SPORT }, + { "ip6-destination-port" , required_argument, 0, IP_DPORT }, + { "ip6-dport" , required_argument, 0, IP_DPORT }, + { "ip6-icmp-type" , required_argument, 0, IP_ICMP6 }, + { 0 } +}; + + +static const struct ebt_icmp_names icmpv6_codes[] = { + { "destination-unreachable", 1, 0, 0xFF }, + { "no-route", 1, 0, 0 }, + { "communication-prohibited", 1, 1, 1 }, + { "address-unreachable", 1, 3, 3 }, + { "port-unreachable", 1, 4, 4 }, + + { "packet-too-big", 2, 0, 0xFF }, + + { "time-exceeded", 3, 0, 0xFF }, + /* Alias */ { "ttl-exceeded", 3, 0, 0xFF }, + { "ttl-zero-during-transit", 3, 0, 0 }, + { "ttl-zero-during-reassembly", 3, 1, 1 }, + + { "parameter-problem", 4, 0, 0xFF }, + { "bad-header", 4, 0, 0 }, + { "unknown-header-type", 4, 1, 1 }, + { "unknown-option", 4, 2, 2 }, + + { "echo-request", 128, 0, 0xFF }, + /* Alias */ { "ping", 128, 0, 0xFF }, + + { "echo-reply", 129, 0, 0xFF }, + /* Alias */ { "pong", 129, 0, 0xFF }, + + { "router-solicitation", 133, 0, 0xFF }, + + { "router-advertisement", 134, 0, 0xFF }, + + { "neighbour-solicitation", 135, 0, 0xFF }, + /* Alias */ { "neighbor-solicitation", 135, 0, 0xFF }, + + { "neighbour-advertisement", 136, 0, 0xFF }, + /* Alias */ { "neighbor-advertisement", 136, 0, 0xFF }, + + { "redirect", 137, 0, 0xFF }, +}; + +/* transform a protocol and service name into a port number */ +static uint16_t parse_port(const char *protocol, const char *name) +{ + struct servent *service; + char *end; + int port; + + port = strtol(name, &end, 10); + if (*end != '\0') { + if (protocol && + (service = getservbyname(name, protocol)) != NULL) + return ntohs(service->s_port); + } + else if (port >= 0 || port <= 0xFFFF) { + return port; + } + ebt_print_error("Problem with specified %s port '%s'", + protocol?protocol:"", name); + return 0; +} + +static void +parse_port_range(const char *protocol, const char *portstring, uint16_t *ports) +{ + char *buffer; + char *cp; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) + ports[0] = ports[1] = parse_port(protocol, buffer); + else { + *cp = '\0'; + cp++; + ports[0] = buffer[0] ? parse_port(protocol, buffer) : 0; + if (ebt_errormsg[0] != '\0') + return; + ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF; + if (ebt_errormsg[0] != '\0') + return; + + if (ports[0] > ports[1]) + ebt_print_error("Invalid portrange (min > max)"); + } + free(buffer); +} + +static void print_port_range(uint16_t *ports) +{ + if (ports[0] == ports[1]) + printf("%d ", ports[0]); + else + printf("%d:%d ", ports[0], ports[1]); +} + +static void print_help() +{ + printf( +"ip6 options:\n" +"--ip6-src [!] address[/mask]: ipv6 source specification\n" +"--ip6-dst [!] address[/mask]: ipv6 destination specification\n" +"--ip6-tclass [!] tclass : ipv6 traffic class specification\n" +"--ip6-proto [!] protocol : ipv6 protocol specification\n" +"--ip6-sport [!] port[:port] : tcp/udp source port or port range\n" +"--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n" +"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n"); + + printf("\nValid ICMPv6 Types:\n"); + ebt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes)); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + ipinfo->invflags = 0; + ipinfo->bitmask = 0; + memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr)); + memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr)); + memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr)); + memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr)); +} + +#define OPT_SOURCE 0x01 +#define OPT_DEST 0x02 +#define OPT_TCLASS 0x04 +#define OPT_PROTO 0x08 +#define OPT_SPORT 0x10 +#define OPT_DPORT 0x20 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data; + char *end; + long int i; + + switch (c) { + case IP_SOURCE: + ebt_check_option2(flags, OPT_SOURCE); + ipinfo->bitmask |= EBT_IP6_SOURCE; + if (ebt_check_inverse2(optarg)) { + ipinfo->invflags |= EBT_IP6_SOURCE; + } + ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk); + break; + + case IP_DEST: + ebt_check_option2(flags, OPT_DEST); + ipinfo->bitmask |= EBT_IP6_DEST; + if (ebt_check_inverse2(optarg)) { + ipinfo->invflags |= EBT_IP6_DEST; + } + ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk); + break; + + case IP_SPORT: + case IP_DPORT: + if (c == IP_SPORT) { + ebt_check_option2(flags, OPT_SPORT); + ipinfo->bitmask |= EBT_IP6_SPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_SPORT; + } else { + ebt_check_option2(flags, OPT_DPORT); + ipinfo->bitmask |= EBT_IP6_DPORT; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_DPORT; + } + if (c == IP_SPORT) + parse_port_range(NULL, optarg, ipinfo->sport); + else + parse_port_range(NULL, optarg, ipinfo->dport); + break; + + case IP_ICMP6: + ebt_check_option2(flags, EBT_IP6_ICMP6); + ipinfo->bitmask |= EBT_IP6_ICMP6; + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_ICMP6; + if (ebt_parse_icmp(icmpv6_codes, ARRAY_SIZE(icmpv6_codes), + optarg, ipinfo->icmpv6_type, + ipinfo->icmpv6_code)) + return 0; + break; + + case IP_TCLASS: + ebt_check_option2(flags, OPT_TCLASS); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_TCLASS; + i = strtol(optarg, &end, 16); + if (i < 0 || i > 255 || *end != '\0') + ebt_print_error2("Problem with specified IPv6 traffic class"); + ipinfo->tclass = i; + ipinfo->bitmask |= EBT_IP6_TCLASS; + break; + + case IP_PROTO: + ebt_check_option2(flags, OPT_PROTO); + if (ebt_check_inverse2(optarg)) + ipinfo->invflags |= EBT_IP6_PROTO; + i = strtoul(optarg, &end, 10); + if (*end != '\0') { + struct protoent *pe; + + pe = getprotobyname(optarg); + if (pe == NULL) + ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]); + ipinfo->protocol = pe->p_proto; + } else { + ipinfo->protocol = (unsigned char) i; + } + ipinfo->bitmask |= EBT_IP6_PROTO; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) { + ebt_print_error("For IPv6 filtering the protocol must be " + "specified as IPv6"); + } else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) && + (!(ipinfo->bitmask & EBT_IP6_PROTO) || + ipinfo->invflags & EBT_IP6_PROTO || + (ipinfo->protocol!=IPPROTO_TCP && + ipinfo->protocol!=IPPROTO_UDP && + ipinfo->protocol!=IPPROTO_SCTP && + ipinfo->protocol!=IPPROTO_DCCP))) + ebt_print_error("For port filtering the IP protocol must be " + "either 6 (tcp), 17 (udp), 33 (dccp) or " + "132 (sctp)"); + if ((ipinfo->bitmask & EBT_IP6_ICMP6) && + (!(ipinfo->bitmask & EBT_IP6_PROTO) || + ipinfo->invflags & EBT_IP6_PROTO || + ipinfo->protocol != IPPROTO_ICMPV6)) + ebt_print_error("For ipv6-icmp filtering the IP protocol must be " + "58 (ipv6-icmp)"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data; + + if (ipinfo->bitmask & EBT_IP6_SOURCE) { + printf("--ip6-src "); + if (ipinfo->invflags & EBT_IP6_SOURCE) + printf("! "); + printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr)); + printf("%s ", ebt_ip6_mask_to_string(&ipinfo->smsk)); + } + if (ipinfo->bitmask & EBT_IP6_DEST) { + printf("--ip6-dst "); + if (ipinfo->invflags & EBT_IP6_DEST) + printf("! "); + printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr)); + printf("%s ", ebt_ip6_mask_to_string(&ipinfo->dmsk)); + } + if (ipinfo->bitmask & EBT_IP6_TCLASS) { + printf("--ip6-tclass "); + if (ipinfo->invflags & EBT_IP6_TCLASS) + printf("! "); + printf("0x%02X ", ipinfo->tclass); + } + if (ipinfo->bitmask & EBT_IP6_PROTO) { + struct protoent *pe; + + printf("--ip6-proto "); + if (ipinfo->invflags & EBT_IP6_PROTO) + printf("! "); + pe = getprotobynumber(ipinfo->protocol); + if (pe == NULL) { + printf("%d ", ipinfo->protocol); + } else { + printf("%s ", pe->p_name); + } + } + if (ipinfo->bitmask & EBT_IP6_SPORT) { + printf("--ip6-sport "); + if (ipinfo->invflags & EBT_IP6_SPORT) + printf("! "); + print_port_range(ipinfo->sport); + } + if (ipinfo->bitmask & EBT_IP6_DPORT) { + printf("--ip6-dport "); + if (ipinfo->invflags & EBT_IP6_DPORT) + printf("! "); + print_port_range(ipinfo->dport); + } + if (ipinfo->bitmask & EBT_IP6_ICMP6) { + printf("--ip6-icmp-type "); + if (ipinfo->invflags & EBT_IP6_ICMP6) + printf("! "); + ebt_print_icmp_type(icmpv6_codes, ARRAY_SIZE(icmpv6_codes), + ipinfo->icmpv6_type, ipinfo->icmpv6_code); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data; + struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data; + + if (ipinfo1->bitmask != ipinfo2->bitmask) + return 0; + if (ipinfo1->invflags != ipinfo2->invflags) + return 0; + if (ipinfo1->bitmask & EBT_IP6_SOURCE) { + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr)) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk)) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_DEST) { + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr)) + return 0; + if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk)) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_TCLASS) { + if (ipinfo1->tclass != ipinfo2->tclass) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_PROTO) { + if (ipinfo1->protocol != ipinfo2->protocol) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_SPORT) { + if (ipinfo1->sport[0] != ipinfo2->sport[0] || + ipinfo1->sport[1] != ipinfo2->sport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_DPORT) { + if (ipinfo1->dport[0] != ipinfo2->dport[0] || + ipinfo1->dport[1] != ipinfo2->dport[1]) + return 0; + } + if (ipinfo1->bitmask & EBT_IP6_ICMP6) { + if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] || + ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] || + ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] || + ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1]) + return 0; + } + return 1; +} + +static struct ebt_u_match ip6_match = +{ + .name = EBT_IP6_MATCH, + .size = sizeof(struct ebt_ip6_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&ip6_match); +}
diff --git a/extensions/ebt_limit.c b/extensions/ebt_limit.c new file mode 100644 index 0000000..1fe9d84 --- /dev/null +++ b/extensions/ebt_limit.c
@@ -0,0 +1,218 @@ +/* ebt_limit + * + * Authors: + * Tom Marshall <tommy@home.tig-grr.com> + * + * Mostly copied from iptables' limit match. + * + * September, 2003 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_limit.h> + +#define EBT_LIMIT_AVG "3/hour" +#define EBT_LIMIT_BURST 5 + +static int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + long number; + char *end; + + errno = 0; + number = strtol(s, &end, 0); + if (*end == '\0' && end != s) { + if (errno != ERANGE && min <= number && number <= max) { + *ret = number; + return 0; + } + } + return -1; +} + +#define FLAG_LIMIT 0x01 +#define FLAG_LIMIT_BURST 0x02 +#define ARG_LIMIT '1' +#define ARG_LIMIT_BURST '2' + +static const struct option opts[] = +{ + { "limit", required_argument, 0, ARG_LIMIT }, + { "limit-burst", required_argument, 0, ARG_LIMIT_BURST }, + { 0 } +}; + +static void print_help(void) +{ + printf( +"limit options:\n" +"--limit avg : max average match rate: default "EBT_LIMIT_AVG"\n" +" [Packets per second unless followed by \n" +" /sec /minute /hour /day postfixes]\n" +"--limit-burst number : number to match in a burst, -1 < number < 10001,\n" +" default %u\n", EBT_LIMIT_BURST); +} + +static int parse_rate(const char *rate, uint32_t *val) +{ + const char *delim; + uint32_t r; + uint32_t mult = 1; /* Seconds by default. */ + + delim = strchr(rate, '/'); + if (delim) { + if (strlen(delim+1) == 0) + return 0; + + if (strncasecmp(delim+1, "second", strlen(delim+1)) == 0) + mult = 1; + else if (strncasecmp(delim+1, "minute", strlen(delim+1)) == 0) + mult = 60; + else if (strncasecmp(delim+1, "hour", strlen(delim+1)) == 0) + mult = 60*60; + else if (strncasecmp(delim+1, "day", strlen(delim+1)) == 0) + mult = 24*60*60; + else + return 0; + } + r = atoi(rate); + if (!r) + return 0; + + /* This would get mapped to infinite (1/day is minimum they + can specify, so we're ok at that end). */ + if (r / mult > EBT_LIMIT_SCALE) + return 0; + + *val = EBT_LIMIT_SCALE * mult / r; + return 1; +} + +/* Initialize the match. */ +static void init(struct ebt_entry_match *m) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)m->data; + + parse_rate(EBT_LIMIT_AVG, &r->avg); + r->burst = EBT_LIMIT_BURST; +} + +/* FIXME: handle overflow: + if (r->avg*r->burst/r->burst != r->avg) + exit_error(PARAMETER_PROBLEM, + "Sorry: burst too large for that avg rate.\n"); +*/ + +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, + unsigned int *flags, + struct ebt_entry_match **match) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)(*match)->data; + unsigned int num; + + switch(c) { + case ARG_LIMIT: + ebt_check_option2(flags, FLAG_LIMIT); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --limit"); + if (!parse_rate(optarg, &r->avg)) + ebt_print_error2("bad rate `%s'", optarg); + break; + + case ARG_LIMIT_BURST: + ebt_check_option2(flags, FLAG_LIMIT_BURST); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --limit-burst"); + if (string_to_number(optarg, 0, 10000, &num) == -1) + ebt_print_error2("bad --limit-burst `%s'", optarg); + r->burst = num; + break; + + default: + return 0; + } + + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +struct rates +{ + const char *name; + uint32_t mult; +}; + +static struct rates g_rates[] = +{ + { "day", EBT_LIMIT_SCALE*24*60*60 }, + { "hour", EBT_LIMIT_SCALE*60*60 }, + { "min", EBT_LIMIT_SCALE*60 }, + { "sec", EBT_LIMIT_SCALE } +}; + +static void print_rate(uint32_t period) +{ + unsigned int i; + + for (i = 1; i < sizeof(g_rates)/sizeof(struct rates); i++) + if (period > g_rates[i].mult || + g_rates[i].mult/period < g_rates[i].mult%period) + break; + + printf("%u/%s ", g_rates[i-1].mult / period, g_rates[i-1].name); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_limit_info *r = (struct ebt_limit_info *)match->data; + + printf("--limit "); + print_rate(r->avg); + printf("--limit-burst %u ", r->burst); +} + +static int compare(const struct ebt_entry_match* m1, + const struct ebt_entry_match *m2) +{ + struct ebt_limit_info* li1 = (struct ebt_limit_info*)m1->data; + struct ebt_limit_info* li2 = (struct ebt_limit_info*)m2->data; + + if (li1->avg != li2->avg) + return 0; + + if (li1->burst != li2->burst) + return 0; + + return 1; +} + +static struct ebt_u_match limit_match = +{ + .name = "limit", + .size = sizeof(struct ebt_limit_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&limit_match); +}
diff --git a/extensions/ebt_log.c b/extensions/ebt_log.c new file mode 100644 index 0000000..b5d3232 --- /dev/null +++ b/extensions/ebt_log.c
@@ -0,0 +1,223 @@ +/* ebt_log + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_log.h> + +/* + * copied from syslog.h + * used for the LOG target + */ +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ + +#define LOG_DEFAULT_LEVEL LOG_INFO + +typedef struct _code { + char *c_name; + int c_val; +} CODE; + +static CODE eight_priority[] = { + { "emerg", LOG_EMERG }, + { "alert", LOG_ALERT }, + { "crit", LOG_CRIT }, + { "error", LOG_ERR }, + { "warning", LOG_WARNING }, + { "notice", LOG_NOTICE }, + { "info", LOG_INFO }, + { "debug", LOG_DEBUG } +}; + +static int name_to_loglevel(char* arg) +{ + int i; + + for (i = 0; i < 8; i++) + if (!strcmp(arg, eight_priority[i].c_name)) + return eight_priority[i].c_val; + /* return bad loglevel */ + return 9; +} + +#define LOG_PREFIX '1' +#define LOG_LEVEL '2' +#define LOG_ARP '3' +#define LOG_IP '4' +#define LOG_LOG '5' +#define LOG_IP6 '6' +static const struct option opts[] = +{ + { "log-prefix", required_argument, 0, LOG_PREFIX }, + { "log-level" , required_argument, 0, LOG_LEVEL }, + { "log-arp" , no_argument , 0, LOG_ARP }, + { "log-ip" , no_argument , 0, LOG_IP }, + { "log" , no_argument , 0, LOG_LOG }, + { "log-ip6" , no_argument , 0, LOG_IP6 }, + { 0 } +}; + +static void print_help() +{ + int i; + + printf( +"log options:\n" +"--log : use this if you're not specifying anything\n" +"--log-level level : level = [1-8] or a string\n" +"--log-prefix prefix : max. %d chars.\n" +"--log-ip : put ip info. in the log for ip packets\n" +"--log-arp : put (r)arp info. in the log for (r)arp packets\n" +"--log-ip6 : put ip6 info. in the log for ip6 packets\n" + , EBT_LOG_PREFIX_SIZE - 1); + printf("levels:\n"); + for (i = 0; i < 8; i++) + printf("%d = %s\n", eight_priority[i].c_val, + eight_priority[i].c_name); +} + +static void init(struct ebt_entry_watcher *watcher) +{ + struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data; + + loginfo->bitmask = 0; + loginfo->prefix[0] = '\0'; + loginfo->loglevel = LOG_NOTICE; +} + +#define OPT_PREFIX 0x01 +#define OPT_LEVEL 0x02 +#define OPT_ARP 0x04 +#define OPT_IP 0x08 +#define OPT_LOG 0x10 +#define OPT_IP6 0x20 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_watcher **watcher) +{ + struct ebt_log_info *loginfo = (struct ebt_log_info *)(*watcher)->data; + long int i; + char *end; + + switch (c) { + case LOG_PREFIX: + ebt_check_option2(flags, OPT_PREFIX); + if (ebt_check_inverse(optarg)) + ebt_print_error2("Unexpected `!' after --log-prefix"); + if (strlen(optarg) > sizeof(loginfo->prefix) - 1) + ebt_print_error2("Prefix too long"); + if (strchr(optarg, '\"')) + ebt_print_error2("Use of \\\" is not allowed in the prefix"); + strcpy((char *)loginfo->prefix, (char *)optarg); + break; + + case LOG_LEVEL: + ebt_check_option2(flags, OPT_LEVEL); + i = strtol(optarg, &end, 16); + if (*end != '\0' || i < 0 || i > 7) + loginfo->loglevel = name_to_loglevel(optarg); + else + loginfo->loglevel = i; + if (loginfo->loglevel == 9) + ebt_print_error2("Problem with the log-level"); + break; + + case LOG_IP: + ebt_check_option2(flags, OPT_IP); + if (ebt_check_inverse(optarg)) + ebt_print_error2("Unexpected `!' after --log-ip"); + loginfo->bitmask |= EBT_LOG_IP; + break; + + case LOG_ARP: + ebt_check_option2(flags, OPT_ARP); + if (ebt_check_inverse(optarg)) + ebt_print_error2("Unexpected `!' after --log-arp"); + loginfo->bitmask |= EBT_LOG_ARP; + break; + + case LOG_LOG: + ebt_check_option2(flags, OPT_LOG); + if (ebt_check_inverse(optarg)) + ebt_print_error2("Unexpected `!' after --log"); + break; + + case LOG_IP6: + ebt_check_option2(flags, OPT_IP6); + if (ebt_check_inverse(optarg)) + ebt_print_error2("Unexpected `!' after --log-ip6"); + loginfo->bitmask |= EBT_LOG_IP6; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher) +{ + struct ebt_log_info *loginfo = (struct ebt_log_info *)watcher->data; + + printf("--log-level %s --log-prefix \"%s\"", + eight_priority[loginfo->loglevel].c_name, + loginfo->prefix); + if (loginfo->bitmask & EBT_LOG_IP) + printf(" --log-ip"); + if (loginfo->bitmask & EBT_LOG_ARP) + printf(" --log-arp"); + if (loginfo->bitmask & EBT_LOG_IP6) + printf(" --log-ip6"); + printf(" "); +} + +static int compare(const struct ebt_entry_watcher *w1, + const struct ebt_entry_watcher *w2) +{ + struct ebt_log_info *loginfo1 = (struct ebt_log_info *)w1->data; + struct ebt_log_info *loginfo2 = (struct ebt_log_info *)w2->data; + + if (loginfo1->loglevel != loginfo2->loglevel) + return 0; + if (loginfo1->bitmask != loginfo2->bitmask) + return 0; + return !strcmp((char *)loginfo1->prefix, (char *)loginfo2->prefix); +} + +static struct ebt_u_watcher log_watcher = +{ + .name = "log", + .size = sizeof(struct ebt_log_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_watcher(&log_watcher); +}
diff --git a/extensions/ebt_mark.c b/extensions/ebt_mark.c new file mode 100644 index 0000000..b4f93b5 --- /dev/null +++ b/extensions/ebt_mark.c
@@ -0,0 +1,178 @@ +/* ebt_mark + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * July, 2002, September 2006 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_mark_t.h> + +static int mark_supplied; + +#define MARK_TARGET '1' +#define MARK_SETMARK '2' +#define MARK_ORMARK '3' +#define MARK_ANDMARK '4' +#define MARK_XORMARK '5' +static const struct option opts[] = +{ + { "mark-target" , required_argument, 0, MARK_TARGET }, + /* an oldtime messup, we should have always used the scheme + * <extension-name>-<option> */ + { "set-mark" , required_argument, 0, MARK_SETMARK }, + { "mark-set" , required_argument, 0, MARK_SETMARK }, + { "mark-or" , required_argument, 0, MARK_ORMARK }, + { "mark-and" , required_argument, 0, MARK_ANDMARK }, + { "mark-xor" , required_argument, 0, MARK_XORMARK }, + { 0 } +}; + +static void print_help() +{ + printf( + "mark target options:\n" + " --mark-set value : Set nfmark value\n" + " --mark-or value : Or nfmark with value (nfmark |= value)\n" + " --mark-and value : And nfmark with value (nfmark &= value)\n" + " --mark-xor value : Xor nfmark with value (nfmark ^= value)\n" + " --mark-target target : ACCEPT, DROP, RETURN or CONTINUE\n"); +} + +static void init(struct ebt_entry_target *target) +{ + struct ebt_mark_t_info *markinfo = + (struct ebt_mark_t_info *)target->data; + + markinfo->target = EBT_ACCEPT; + markinfo->mark = 0; + mark_supplied = 0; +} + +#define OPT_MARK_TARGET 0x01 +#define OPT_MARK_SETMARK 0x02 +#define OPT_MARK_ORMARK 0x04 +#define OPT_MARK_ANDMARK 0x08 +#define OPT_MARK_XORMARK 0x10 +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_mark_t_info *markinfo = + (struct ebt_mark_t_info *)(*target)->data; + char *end; + + switch (c) { + case MARK_TARGET: + { int tmp; + ebt_check_option2(flags, OPT_MARK_TARGET); + if (FILL_TARGET(optarg, tmp)) + ebt_print_error2("Illegal --mark-target target"); + /* the 4 lsb are left to designate the target */ + markinfo->target = (markinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS); + } + return 1; + case MARK_SETMARK: + ebt_check_option2(flags, OPT_MARK_SETMARK); + if (*flags & (OPT_MARK_ORMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK)) + ebt_print_error2("--mark-set cannot be used together with specific --mark option"); + break; + case MARK_ORMARK: + ebt_check_option2(flags, OPT_MARK_ORMARK); + if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_XORMARK)) + ebt_print_error2("--mark-or cannot be used together with specific --mark option"); + markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_OR_VALUE; + break; + case MARK_ANDMARK: + ebt_check_option2(flags, OPT_MARK_ANDMARK); + if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ORMARK|OPT_MARK_XORMARK)) + ebt_print_error2("--mark-and cannot be used together with specific --mark option"); + markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_AND_VALUE; + break; + case MARK_XORMARK: + ebt_check_option2(flags, OPT_MARK_XORMARK); + if (*flags & (OPT_MARK_SETMARK|OPT_MARK_ANDMARK|OPT_MARK_ORMARK)) + ebt_print_error2("--mark-xor cannot be used together with specific --mark option"); + markinfo->target = (markinfo->target & EBT_VERDICT_BITS) | MARK_XOR_VALUE; + break; + default: + return 0; + } + /* mutual code */ + markinfo->mark = strtoul(optarg, &end, 0); + if (*end != '\0' || end == optarg) + ebt_print_error2("Bad MARK value '%s'", optarg); + mark_supplied = 1; + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_mark_t_info *markinfo = + (struct ebt_mark_t_info *)target->data; + + if (time == 0 && mark_supplied == 0) { + ebt_print_error("No mark value supplied"); + } else if (BASE_CHAIN && (markinfo->target|~EBT_VERDICT_BITS) == EBT_RETURN) + ebt_print_error("--mark-target RETURN not allowed on base chain"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_mark_t_info *markinfo = + (struct ebt_mark_t_info *)target->data; + int tmp; + + tmp = markinfo->target & ~EBT_VERDICT_BITS; + if (tmp == MARK_SET_VALUE) + printf("--mark-set"); + else if (tmp == MARK_OR_VALUE) + printf("--mark-or"); + else if (tmp == MARK_XOR_VALUE) + printf("--mark-xor"); + else if (tmp == MARK_AND_VALUE) + printf("--mark-and"); + else + ebt_print_error("oops, unknown mark action, try a later version of ebtables"); + printf(" 0x%lx", markinfo->mark); + tmp = markinfo->target | ~EBT_VERDICT_BITS; + printf(" --mark-target %s", TARGET_NAME(tmp)); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + struct ebt_mark_t_info *markinfo1 = + (struct ebt_mark_t_info *)t1->data; + struct ebt_mark_t_info *markinfo2 = + (struct ebt_mark_t_info *)t2->data; + + return markinfo1->target == markinfo2->target && + markinfo1->mark == markinfo2->mark; +} + +static struct ebt_u_target mark_target = +{ + .name = "mark", + .size = sizeof(struct ebt_mark_t_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_target(&mark_target); +}
diff --git a/extensions/ebt_mark_m.c b/extensions/ebt_mark_m.c new file mode 100644 index 0000000..b6d11a2 --- /dev/null +++ b/extensions/ebt_mark_m.c
@@ -0,0 +1,127 @@ +/* ebt_mark_m + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * July, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_mark_m.h> + +#define MARK '1' + +static const struct option opts[] = +{ + { "mark", required_argument, 0, MARK }, + { 0 } +}; + +static void print_help() +{ + printf( +"mark option:\n" +"--mark [!] [value][/mask]: Match nfmask value (see man page)\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *)match->data; + + markinfo->mark = 0; + markinfo->mask = 0; + markinfo->invert = 0; + markinfo->bitmask = 0; +} + +#define OPT_MARK 0x01 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_mark_m_info *markinfo = (struct ebt_mark_m_info *) + (*match)->data; + char *end; + + switch (c) { + case MARK: + ebt_check_option2(flags, MARK); + if (ebt_check_inverse2(optarg)) + markinfo->invert = 1; + markinfo->mark = strtoul(optarg, &end, 0); + markinfo->bitmask = EBT_MARK_AND; + if (*end == '/') { + if (end == optarg) + markinfo->bitmask = EBT_MARK_OR; + markinfo->mask = strtoul(end+1, &end, 0); + } else + markinfo->mask = 0xffffffff; + if ( *end != '\0' || end == optarg) + ebt_print_error2("Bad mark value '%s'", optarg); + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_mark_m_info *markinfo = + (struct ebt_mark_m_info *)match->data; + + printf("--mark "); + if (markinfo->invert) + printf("! "); + if (markinfo->bitmask == EBT_MARK_OR) + printf("/0x%lx ", markinfo->mask); + else if(markinfo->mask != 0xffffffff) + printf("0x%lx/0x%lx ", markinfo->mark, markinfo->mask); + else + printf("0x%lx ", markinfo->mark); +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_mark_m_info *markinfo1 = (struct ebt_mark_m_info *)m1->data; + struct ebt_mark_m_info *markinfo2 = (struct ebt_mark_m_info *)m2->data; + + if (markinfo1->invert != markinfo2->invert) + return 0; + if (markinfo1->mark != markinfo2->mark) + return 0; + if (markinfo1->mask != markinfo2->mask) + return 0; + if (markinfo1->bitmask != markinfo2->bitmask) + return 0; + return 1; +} + +static struct ebt_u_match mark_match = +{ + .name = "mark_m", + .size = sizeof(struct ebt_mark_m_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&mark_match); +}
diff --git a/extensions/ebt_nat.c b/extensions/ebt_nat.c new file mode 100644 index 0000000..fe7e987 --- /dev/null +++ b/extensions/ebt_nat.c
@@ -0,0 +1,238 @@ +/* ebt_nat + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * June, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <netinet/ether.h> +#include <linux/netfilter_bridge/ebt_nat.h> + +static int to_source_supplied, to_dest_supplied; + +#define NAT_S '1' +#define NAT_D '1' +#define NAT_S_TARGET '2' +#define NAT_D_TARGET '2' +#define NAT_S_ARP '3' +static const struct option opts_s[] = +{ + { "to-source" , required_argument, 0, NAT_S }, + { "to-src" , required_argument, 0, NAT_S }, + { "snat-target" , required_argument, 0, NAT_S_TARGET }, + { "snat-arp" , no_argument, 0, NAT_S_ARP }, + { 0 } +}; + +static const struct option opts_d[] = +{ + { "to-destination", required_argument, 0, NAT_D }, + { "to-dst" , required_argument, 0, NAT_D }, + { "dnat-target" , required_argument, 0, NAT_D_TARGET }, + { 0 } +}; + +static void print_help_s() +{ + printf( + "snat options:\n" + " --to-src address : MAC address to map source to\n" + " --snat-target target : ACCEPT, DROP, RETURN or CONTINUE\n" + " --snat-arp : also change src address in arp msg\n"); +} + +static void print_help_d() +{ + printf( + "dnat options:\n" + " --to-dst address : MAC address to map destination to\n" + " --dnat-target target : ACCEPT, DROP, RETURN or CONTINUE\n"); +} + +static void init_s(struct ebt_entry_target *target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + to_source_supplied = 0; + natinfo->target = EBT_ACCEPT; + return; +} + +static void init_d(struct ebt_entry_target *target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + to_dest_supplied = 0; + natinfo->target = EBT_ACCEPT; + return; +} + +#define OPT_SNAT 0x01 +#define OPT_SNAT_TARGET 0x02 +#define OPT_SNAT_ARP 0x04 +static int parse_s(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data; + struct ether_addr *addr; + + switch (c) { + case NAT_S: + ebt_check_option2(flags, OPT_SNAT); + to_source_supplied = 1; + if (!(addr = ether_aton(optarg))) + ebt_print_error2("Problem with specified --to-source mac"); + memcpy(natinfo->mac, addr, ETH_ALEN); + break; + case NAT_S_TARGET: + { int tmp; + ebt_check_option2(flags, OPT_SNAT_TARGET); + if (FILL_TARGET(optarg, tmp)) + ebt_print_error2("Illegal --snat-target target"); + natinfo->target = (natinfo->target & ~EBT_VERDICT_BITS) | (tmp & EBT_VERDICT_BITS); + } + break; + case NAT_S_ARP: + ebt_check_option2(flags, OPT_SNAT_ARP); + natinfo->target ^= NAT_ARP_BIT; + break; + default: + return 0; + } + return 1; +} + +#define OPT_DNAT 0x01 +#define OPT_DNAT_TARGET 0x02 +static int parse_d(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)(*target)->data; + struct ether_addr *addr; + + switch (c) { + case NAT_D: + ebt_check_option2(flags, OPT_DNAT); + to_dest_supplied = 1; + if (!(addr = ether_aton(optarg))) + ebt_print_error2("Problem with specified --to-destination mac"); + memcpy(natinfo->mac, addr, ETH_ALEN); + break; + case NAT_D_TARGET: + ebt_check_option2(flags, OPT_DNAT_TARGET); + if (FILL_TARGET(optarg, natinfo->target)) + ebt_print_error2("Illegal --dnat-target target"); + break; + default: + return 0; + } + return 1; +} + +static void final_check_s(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + if (BASE_CHAIN && (natinfo->target | ~EBT_VERDICT_BITS) == EBT_RETURN) { + ebt_print_error("--snat-target RETURN not allowed on base chain"); + return; + } + CLEAR_BASE_CHAIN_BIT; + if ((hookmask & ~(1 << NF_BR_POST_ROUTING)) || strcmp(name, "nat")) { + ebt_print_error("Wrong chain for snat"); + } else if (time == 0 && to_source_supplied == 0) + ebt_print_error("No snat address supplied"); +} + +static void final_check_d(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + if (BASE_CHAIN && natinfo->target == EBT_RETURN) { + ebt_print_error("--dnat-target RETURN not allowed on base chain"); + return; + } + CLEAR_BASE_CHAIN_BIT; + if (((hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT))) + || strcmp(name, "nat")) && + ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute"))) { + ebt_print_error("Wrong chain for dnat"); + } else if (time == 0 && to_dest_supplied == 0) + ebt_print_error("No dnat address supplied"); +} + +static void print_s(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + printf("--to-src "); + ebt_print_mac(natinfo->mac); + if (!(natinfo->target&NAT_ARP_BIT)) + printf(" --snat-arp"); + printf(" --snat-target %s", TARGET_NAME((natinfo->target|~EBT_VERDICT_BITS))); +} + +static void print_d(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_nat_info *natinfo = (struct ebt_nat_info *)target->data; + + printf("--to-dst "); + ebt_print_mac(natinfo->mac); + printf(" --dnat-target %s", TARGET_NAME(natinfo->target)); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + struct ebt_nat_info *natinfo1 = (struct ebt_nat_info *)t1->data; + struct ebt_nat_info *natinfo2 = (struct ebt_nat_info *)t2->data; + + return !memcmp(natinfo1->mac, natinfo2->mac, sizeof(natinfo1->mac)) && + natinfo1->target == natinfo2->target; +} + +static struct ebt_u_target snat_target = +{ + .name = "snat", + .size = sizeof(struct ebt_nat_info), + .help = print_help_s, + .init = init_s, + .parse = parse_s, + .final_check = final_check_s, + .print = print_s, + .compare = compare, + .extra_ops = opts_s, +}; + +static struct ebt_u_target dnat_target = +{ + .name = "dnat", + .size = sizeof(struct ebt_nat_info), + .help = print_help_d, + .init = init_d, + .parse = parse_d, + .final_check = final_check_d, + .print = print_d, + .compare = compare, + .extra_ops = opts_d, +}; + +static void _INIT(void) +{ + ebt_register_target(&snat_target); + ebt_register_target(&dnat_target); +}
diff --git a/extensions/ebt_nflog.c b/extensions/ebt_nflog.c new file mode 100644 index 0000000..04c547d --- /dev/null +++ b/extensions/ebt_nflog.c
@@ -0,0 +1,172 @@ +/* ebt_nflog + * + * Authors: + * Peter Warasin <peter@endian.com> + * + * February, 2008 + * + * Based on: + * ebt_ulog.c, (C) 2004, Bart De Schuymer <bdschuym@pandora.be> + * libxt_NFLOG.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_nflog.h> + +enum { + NFLOG_GROUP = 0x1, + NFLOG_PREFIX = 0x2, + NFLOG_RANGE = 0x4, + NFLOG_THRESHOLD = 0x8, + NFLOG_NFLOG = 0x16, +}; + +static const struct option nflog_opts[] = { + {"nflog-group", required_argument, NULL, NFLOG_GROUP}, + {"nflog-prefix", required_argument, NULL, NFLOG_PREFIX}, + {"nflog-range", required_argument, NULL, NFLOG_RANGE}, + {"nflog-threshold", required_argument, NULL, NFLOG_THRESHOLD}, + {"nflog", no_argument, NULL, NFLOG_NFLOG}, + {.name = NULL} +}; + +static void nflog_help() +{ + printf("nflog options:\n" + "--nflog : use the default nflog parameters\n" + "--nflog-prefix prefix : Prefix string for log message\n" + "--nflog-group group : NETLINK group used for logging\n" + "--nflog-range range : Number of byte to copy\n" + "--nflog-threshold : Message threshold of" + "in-kernel queue\n"); +} + +static void init(struct ebt_entry_watcher *watcher) +{ + struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data; + + info->prefix[0] = '\0'; + info->group = EBT_NFLOG_DEFAULT_GROUP; + info->threshold = EBT_NFLOG_DEFAULT_THRESHOLD; +} + +static int nflog_parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_watcher **watcher) +{ + struct ebt_nflog_info *info; + unsigned int i; + char *end; + + info = (struct ebt_nflog_info *)(*watcher)->data; + switch (c) { + case NFLOG_PREFIX: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, NFLOG_PREFIX); + if (strlen(optarg) > EBT_NFLOG_PREFIX_SIZE - 1) + ebt_print_error("Prefix too long for nflog-prefix"); + strcpy(info->prefix, optarg); + break; + + case NFLOG_GROUP: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, NFLOG_GROUP); + i = strtoul(optarg, &end, 10); + if (*end != '\0') + ebt_print_error2("--nflog-group must be a number!"); + info->group = i; + break; + + case NFLOG_RANGE: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, NFLOG_RANGE); + i = strtoul(optarg, &end, 10); + if (*end != '\0') + ebt_print_error2("--nflog-range must be a number!"); + info->len = i; + break; + + case NFLOG_THRESHOLD: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, NFLOG_THRESHOLD); + i = strtoul(optarg, &end, 10); + if (*end != '\0') + ebt_print_error2("--nflog-threshold must be a number!"); + info->threshold = i; + break; + case NFLOG_NFLOG: + if (ebt_check_inverse(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, NFLOG_NFLOG); + break; + + default: + return 0; + } + return 1; + + inverse_invalid: + ebt_print_error("The use of '!' makes no sense for the nflog watcher"); + return 1; +} + +static void nflog_final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher, + const char *name, unsigned int hookmask, + unsigned int time) +{ +} + +static void nflog_print(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher) +{ + struct ebt_nflog_info *info = (struct ebt_nflog_info *)watcher->data; + + if (info->prefix[0] != '\0') + printf("--nflog-prefix \"%s\"", info->prefix); + if (info->group) + printf("--nflog-group %d ", info->group); + if (info->len) + printf("--nflog-range %d", info->len); + if (info->threshold != EBT_NFLOG_DEFAULT_THRESHOLD) + printf(" --nflog-threshold %d ", info->threshold); +} + +static int nflog_compare(const struct ebt_entry_watcher *w1, + const struct ebt_entry_watcher *w2) +{ + struct ebt_nflog_info *info1 = (struct ebt_nflog_info *)w1->data; + struct ebt_nflog_info *info2 = (struct ebt_nflog_info *)w2->data; + + if (info1->group != info2->group || + info1->len != info2->len || + info1->threshold != info2->threshold || + strcmp(info1->prefix, info2->prefix)) + return 0; + return 1; +} + +static struct ebt_u_watcher nflog_watcher = { + .name = "nflog", + .size = sizeof(struct ebt_nflog_info), + .help = nflog_help, + .init = init, + .parse = nflog_parse, + .final_check = nflog_final_check, + .print = nflog_print, + .compare = nflog_compare, + .extra_ops = nflog_opts, +}; + +static void _INIT(void) +{ + ebt_register_watcher(&nflog_watcher); +}
diff --git a/extensions/ebt_pkttype.c b/extensions/ebt_pkttype.c new file mode 100644 index 0000000..bf578fc --- /dev/null +++ b/extensions/ebt_pkttype.c
@@ -0,0 +1,131 @@ +/* ebt_pkttype + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2003 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <netdb.h> +#include "../include/ebtables_u.h" +#include <linux/if_packet.h> +#include <linux/netfilter_bridge/ebt_pkttype.h> + +char *classes[] = +{ + "host", + "broadcast", + "multicast", + "otherhost", + "outgoing", + "loopback", + "fastroute", + "\0" +}; + +static const struct option opts[] = +{ + { "pkttype-type" , required_argument, 0, '1' }, + { 0 } +}; + +static void print_help() +{ + printf( +"pkttype options:\n" +"--pkttype-type [!] type: class the packet belongs to\n" +"Possible values: broadcast, multicast, host, otherhost, or any other byte value (which would be pretty useless).\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data; + + pt->invert = 0; +} + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_pkttype_info *ptinfo = (struct ebt_pkttype_info *)(*match)->data; + char *end; + long int i; + + switch (c) { + case '1': + ebt_check_option2(flags, 1); + if (ebt_check_inverse2(optarg)) + ptinfo->invert = 1; + i = strtol(optarg, &end, 16); + if (*end != '\0') { + int j = 0; + i = -1; + while (classes[j][0]) + if (!strcasecmp(optarg, classes[j++])) { + i = j - 1; + break; + } + } + if (i < 0 || i > 255) + ebt_print_error2("Problem with specified pkttype class"); + ptinfo->pkt_type = (uint8_t)i; + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_pkttype_info *pt = (struct ebt_pkttype_info *)match->data; + int i = 0; + + printf("--pkttype-type %s", pt->invert ? "! " : ""); + while (classes[i++][0]); + if (pt->pkt_type < i - 1) + printf("%s ", classes[pt->pkt_type]); + else + printf("%d ", pt->pkt_type); +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + struct ebt_pkttype_info *pt1 = (struct ebt_pkttype_info *)m1->data; + struct ebt_pkttype_info *pt2 = (struct ebt_pkttype_info *)m2->data; + + if (pt1->invert != pt2->invert || + pt1->pkt_type != pt2->pkt_type) + return 0; + return 1; +} + +static struct ebt_u_match pkttype_match = +{ + .name = "pkttype", + .size = sizeof(struct ebt_pkttype_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&pkttype_match); +}
diff --git a/extensions/ebt_redirect.c b/extensions/ebt_redirect.c new file mode 100644 index 0000000..59fe818 --- /dev/null +++ b/extensions/ebt_redirect.c
@@ -0,0 +1,114 @@ +/* ebt_redirect + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_redirect.h> + +#define REDIRECT_TARGET '1' +static const struct option opts[] = +{ + { "redirect-target", required_argument, 0, REDIRECT_TARGET }, + { 0 } +}; + +static void print_help() +{ + printf( + "redirect option:\n" + " --redirect-target target : ACCEPT, DROP, RETURN or CONTINUE\n"); +} + +static void init(struct ebt_entry_target *target) +{ + struct ebt_redirect_info *redirectinfo = + (struct ebt_redirect_info *)target->data; + + redirectinfo->target = EBT_ACCEPT; + return; +} + +#define OPT_REDIRECT_TARGET 0x01 +static int parse(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target) +{ + struct ebt_redirect_info *redirectinfo = + (struct ebt_redirect_info *)(*target)->data; + + switch (c) { + case REDIRECT_TARGET: + ebt_check_option2(flags, OPT_REDIRECT_TARGET); + if (FILL_TARGET(optarg, redirectinfo->target)) + ebt_print_error2("Illegal --redirect-target target"); + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct ebt_redirect_info *redirectinfo = + (struct ebt_redirect_info *)target->data; + + if (BASE_CHAIN && redirectinfo->target == EBT_RETURN) { + ebt_print_error("--redirect-target RETURN not allowed on base chain"); + return; + } + CLEAR_BASE_CHAIN_BIT; + if ( ((hookmask & ~(1 << NF_BR_PRE_ROUTING)) || strcmp(name, "nat")) && + ((hookmask & ~(1 << NF_BR_BROUTING)) || strcmp(name, "broute")) ) + ebt_print_error("Wrong chain for redirect"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + struct ebt_redirect_info *redirectinfo = + (struct ebt_redirect_info *)target->data; + + if (redirectinfo->target == EBT_ACCEPT) + return; + printf(" --redirect-target %s", TARGET_NAME(redirectinfo->target)); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + struct ebt_redirect_info *redirectinfo1 = + (struct ebt_redirect_info *)t1->data; + struct ebt_redirect_info *redirectinfo2 = + (struct ebt_redirect_info *)t2->data; + + return redirectinfo1->target == redirectinfo2->target; +} + +static struct ebt_u_target redirect_target = +{ + .name = "redirect", + .size = sizeof(struct ebt_redirect_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_target(&redirect_target); +}
diff --git a/extensions/ebt_standard.c b/extensions/ebt_standard.c new file mode 100644 index 0000000..f3c3308 --- /dev/null +++ b/extensions/ebt_standard.c
@@ -0,0 +1,90 @@ +/* ebt_standard + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <getopt.h> +#include "../include/ebtables_u.h" + +static const struct option opts[] = +{ + {0} +}; + +static void print_help() +{ + printf("Standard targets: DROP, ACCEPT, RETURN or CONTINUE;\n" + "The target can also be a user defined chain.\n"); +} + +static void init(struct ebt_entry_target *t) +{ + ((struct ebt_standard_target *)t)->verdict = EBT_CONTINUE; +} + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_target **target) +{ + return 0; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target) +{ + int verdict = ((struct ebt_standard_target *)target)->verdict; + + if (verdict >= 0) { + struct ebt_u_entries *entries; + + entries = entry->replace->chains[verdict + NF_BR_NUMHOOKS]; + printf("%s", entries->name); + return; + } + if (verdict == EBT_CONTINUE) + printf("CONTINUE "); + else if (verdict == EBT_ACCEPT) + printf("ACCEPT "); + else if (verdict == EBT_DROP) + printf("DROP "); + else if (verdict == EBT_RETURN) + printf("RETURN "); + else + ebt_print_bug("Bad standard target"); +} + +static int compare(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2) +{ + return ((struct ebt_standard_target *)t1)->verdict == + ((struct ebt_standard_target *)t2)->verdict; +} + +static struct ebt_u_target standard = +{ + .name = "standard", + .size = sizeof(struct ebt_standard_target) - + sizeof(struct ebt_entry_target), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_target(&standard); +}
diff --git a/extensions/ebt_stp.c b/extensions/ebt_stp.c new file mode 100644 index 0000000..311bc63 --- /dev/null +++ b/extensions/ebt_stp.c
@@ -0,0 +1,343 @@ +/* ebt_stp + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * July, 2003 + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <linux/netfilter_bridge/ebt_stp.h> + +#define STP_TYPE 'a' +#define STP_FLAGS 'b' +#define STP_ROOTPRIO 'c' +#define STP_ROOTADDR 'd' +#define STP_ROOTCOST 'e' +#define STP_SENDERPRIO 'f' +#define STP_SENDERADDR 'g' +#define STP_PORT 'h' +#define STP_MSGAGE 'i' +#define STP_MAXAGE 'j' +#define STP_HELLOTIME 'k' +#define STP_FWDD 'l' +#define STP_NUMOPS 12 + +static const struct option opts[] = +{ + { "stp-type" , required_argument, 0, STP_TYPE}, + { "stp-flags" , required_argument, 0, STP_FLAGS}, + { "stp-root-prio" , required_argument, 0, STP_ROOTPRIO}, + { "stp-root-addr" , required_argument, 0, STP_ROOTADDR}, + { "stp-root-cost" , required_argument, 0, STP_ROOTCOST}, + { "stp-sender-prio" , required_argument, 0, STP_SENDERPRIO}, + { "stp-sender-addr" , required_argument, 0, STP_SENDERADDR}, + { "stp-port" , required_argument, 0, STP_PORT}, + { "stp-msg-age" , required_argument, 0, STP_MSGAGE}, + { "stp-max-age" , required_argument, 0, STP_MAXAGE}, + { "stp-hello-time" , required_argument, 0, STP_HELLOTIME}, + { "stp-forward-delay", required_argument, 0, STP_FWDD}, + { 0 } +}; + +#define BPDU_TYPE_CONFIG 0 +#define BPDU_TYPE_TCN 0x80 +#define BPDU_TYPE_CONFIG_STRING "config" +#define BPDU_TYPE_TCN_STRING "tcn" + +#define FLAG_TC 0x01 +#define FLAG_TC_ACK 0x80 +#define FLAG_TC_STRING "topology-change" +#define FLAG_TC_ACK_STRING "topology-change-ack" + +static void print_help() +{ + printf( +"stp options:\n" +"--stp-type type : BPDU type\n" +"--stp-flags flag : control flag\n" +"--stp-root-prio prio[:prio] : root priority (16-bit) range\n" +"--stp-root-addr address[/mask] : MAC address of root\n" +"--stp-root-cost cost[:cost] : root cost (32-bit) range\n" +"--stp-sender-prio prio[:prio] : sender priority (16-bit) range\n" +"--stp-sender-addr address[/mask] : MAC address of sender\n" +"--stp-port port[:port] : port id (16-bit) range\n" +"--stp-msg-age age[:age] : message age timer (16-bit) range\n" +"--stp-max-age age[:age] : maximum age timer (16-bit) range\n" +"--stp-hello-time time[:time] : hello time timer (16-bit) range\n" +"--stp-forward-delay delay[:delay]: forward delay timer (16-bit) range\n" +" Recognized BPDU type strings:\n" +" \"config\": configuration BPDU (=0)\n" +" \"tcn\" : topology change notification BPDU (=0x80)\n" +" Recognized control flag strings:\n" +" \"topology-change\" : topology change flag (0x01)\n" +" \"topology-change-ack\": topology change acknowledgement flag (0x80)"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data; + + stpinfo->invflags = 0; + stpinfo->bitmask = 0; +} + +static int parse_range(const char *portstring, void *lower, void *upper, + int bits, uint32_t min, uint32_t max) +{ + char *buffer; + char *cp, *end; + uint32_t low_nr, upp_nr; + int ret = 0; + + buffer = strdup(portstring); + if ((cp = strchr(buffer, ':')) == NULL) { + low_nr = strtoul(buffer, &end, 10); + if (*end || low_nr < min || low_nr > max) { + ret = -1; + goto out; + } + if (bits == 2) { + *(uint16_t *)lower = low_nr; + *(uint16_t *)upper = low_nr; + } else { + *(uint32_t *)lower = low_nr; + *(uint32_t *)upper = low_nr; + } + } else { + *cp = '\0'; + cp++; + if (!*buffer) + low_nr = min; + else { + low_nr = strtoul(buffer, &end, 10); + if (*end || low_nr < min) { + ret = -1; + goto out; + } + } + if (!*cp) + upp_nr = max; + else { + upp_nr = strtoul(cp, &end, 10); + if (*end || upp_nr > max) { + ret = -1; + goto out; + } + } + if (upp_nr < low_nr) { + ret = -1; + goto out; + } + if (bits == 2) { + *(uint16_t *)lower = low_nr; + *(uint16_t *)upper = upp_nr; + } else { + *(uint32_t *)lower = low_nr; + *(uint32_t *)upper = upp_nr; + } + } +out: + free(buffer); + return ret; +} + +static void print_range(unsigned int l, unsigned int u) +{ + if (l == u) + printf("%u ", l); + else + printf("%u:%u ", l, u); +} + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)(*match)->data; + unsigned int flag; + long int i; + char *end = NULL; + + if (c < 'a' || c > ('a' + STP_NUMOPS - 1)) + return 0; + flag = 1 << (c - 'a'); + ebt_check_option2(flags, flag); + if (ebt_check_inverse2(optarg)) + stpinfo->invflags |= flag; + stpinfo->bitmask |= flag; + switch (flag) { + case EBT_STP_TYPE: + i = strtol(optarg, &end, 0); + if (i < 0 || i > 255 || *end != '\0') { + if (!strcasecmp(optarg, BPDU_TYPE_CONFIG_STRING)) + stpinfo->type = BPDU_TYPE_CONFIG; + else if (!strcasecmp(optarg, BPDU_TYPE_TCN_STRING)) + stpinfo->type = BPDU_TYPE_TCN; + else + ebt_print_error2("Bad --stp-type argument"); + } else + stpinfo->type = i; + break; + case EBT_STP_FLAGS: + i = strtol(optarg, &end, 0); + if (i < 0 || i > 255 || *end != '\0') { + if (!strcasecmp(optarg, FLAG_TC_STRING)) + stpinfo->config.flags = FLAG_TC; + else if (!strcasecmp(optarg, FLAG_TC_ACK_STRING)) + stpinfo->config.flags = FLAG_TC_ACK; + else + ebt_print_error2("Bad --stp-flags argument"); + } else + stpinfo->config.flags = i; + break; + case EBT_STP_ROOTPRIO: + if (parse_range(argv[optind-1], &(stpinfo->config.root_priol), + &(stpinfo->config.root_priou), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-root-prio range"); + break; + case EBT_STP_ROOTCOST: + if (parse_range(argv[optind-1], &(stpinfo->config.root_costl), + &(stpinfo->config.root_costu), 4, 0, 0xffffffff)) + ebt_print_error("Bad --stp-root-cost range"); + break; + case EBT_STP_SENDERPRIO: + if (parse_range(argv[optind-1], &(stpinfo->config.sender_priol), + &(stpinfo->config.sender_priou), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-sender-prio range"); + break; + case EBT_STP_PORT: + if (parse_range(argv[optind-1], &(stpinfo->config.portl), + &(stpinfo->config.portu), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-port range"); + break; + case EBT_STP_MSGAGE: + if (parse_range(argv[optind-1], &(stpinfo->config.msg_agel), + &(stpinfo->config.msg_ageu), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-msg-age range"); + break; + case EBT_STP_MAXAGE: + if (parse_range(argv[optind-1], &(stpinfo->config.max_agel), + &(stpinfo->config.max_ageu), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-max-age range"); + break; + case EBT_STP_HELLOTIME: + if (parse_range(argv[optind-1], &(stpinfo->config.hello_timel), + &(stpinfo->config.hello_timeu), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-hello-time range"); + break; + case EBT_STP_FWDD: + if (parse_range(argv[optind-1], &(stpinfo->config.forward_delayl), + &(stpinfo->config.forward_delayu), 2, 0, 0xffff)) + ebt_print_error("Bad --stp-forward-delay range"); + break; + case EBT_STP_ROOTADDR: + if (ebt_get_mac_and_mask(argv[optind-1], + (unsigned char *)stpinfo->config.root_addr, + (unsigned char *)stpinfo->config.root_addrmsk)) + ebt_print_error("Bad --stp-root-addr address"); + break; + case EBT_STP_SENDERADDR: + if (ebt_get_mac_and_mask(argv[optind-1], + (unsigned char *)stpinfo->config.sender_addr, + (unsigned char *)stpinfo->config.sender_addrmsk)) + ebt_print_error("Bad --stp-sender-addr address"); + break; + default: + ebt_print_error("stp match: this shouldn't happen"); + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + uint8_t bridge_ula[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; + uint8_t msk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if (memcmp(entry->destmac, bridge_ula, 6) || + memcmp(entry->destmsk, msk, 6)) + ebt_print_error("STP matching is only valid when the " + "destination MAC address is the bridge group " + "address (BGA) 01:80:c2:00:00:00"); +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_stp_info *stpinfo = (struct ebt_stp_info *)match->data; + struct ebt_stp_config_info *c = &(stpinfo->config); + int i; + + for (i = 0; i < STP_NUMOPS; i++) { + if (!(stpinfo->bitmask & (1 << i))) + continue; + printf("--%s %s", opts[i].name, + (stpinfo->invflags & (1 << i)) ? "! " : ""); + if (EBT_STP_TYPE == (1 << i)) { + if (stpinfo->type == BPDU_TYPE_CONFIG) + printf("%s", BPDU_TYPE_CONFIG_STRING); + else if (stpinfo->type == BPDU_TYPE_TCN) + printf("%s", BPDU_TYPE_TCN_STRING); + else + printf("%d", stpinfo->type); + } else if (EBT_STP_FLAGS == (1 << i)) { + if (c->flags == FLAG_TC) + printf("%s", FLAG_TC_STRING); + else if (c->flags == FLAG_TC_ACK) + printf("%s", FLAG_TC_ACK_STRING); + else + printf("%d", c->flags); + } else if (EBT_STP_ROOTPRIO == (1 << i)) + print_range(c->root_priol, c->root_priou); + else if (EBT_STP_ROOTADDR == (1 << i)) + ebt_print_mac_and_mask((unsigned char *)c->root_addr, + (unsigned char*)c->root_addrmsk); + else if (EBT_STP_ROOTCOST == (1 << i)) + print_range(c->root_costl, c->root_costu); + else if (EBT_STP_SENDERPRIO == (1 << i)) + print_range(c->sender_priol, c->sender_priou); + else if (EBT_STP_SENDERADDR == (1 << i)) + ebt_print_mac_and_mask((unsigned char *)c->sender_addr, + (unsigned char *)c->sender_addrmsk); + else if (EBT_STP_PORT == (1 << i)) + print_range(c->portl, c->portu); + else if (EBT_STP_MSGAGE == (1 << i)) + print_range(c->msg_agel, c->msg_ageu); + else if (EBT_STP_MAXAGE == (1 << i)) + print_range(c->max_agel, c->max_ageu); + else if (EBT_STP_HELLOTIME == (1 << i)) + print_range(c->hello_timel, c->hello_timeu); + else if (EBT_STP_FWDD == (1 << i)) + print_range(c->forward_delayl, c->forward_delayu); + printf(" "); + } +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + return (!memcmp(m1->data, m2->data, sizeof(struct ebt_stp_info))); +} + +static struct ebt_u_match stp_match = +{ + .name = "stp", + .size = sizeof(struct ebt_stp_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&stp_match); +}
diff --git a/extensions/ebt_string.c b/extensions/ebt_string.c new file mode 100644 index 0000000..97fbe19 --- /dev/null +++ b/extensions/ebt_string.c
@@ -0,0 +1,318 @@ +/* ebt_string + * + * Author: + * Bernie Harris <bernie.harris@alliedtelesis.co.nz> + * + * February, 2018 + * + * Based on: + * libxt_string.c, Copyright (C) 2000 Emmanuel Roger <winfield@freegates.be> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <getopt.h> +#include <netdb.h> +#include <ctype.h> +#include "../include/ebtables_u.h" +#include <linux/if_packet.h> +#include <linux/netfilter/xt_string.h> + +#define STRING_FROM '1' +#define STRING_TO '2' +#define STRING_ALGO '3' +#define STRING_ICASE '4' +#define STRING '5' +#define STRING_HEX '6' +#define OPT_STRING_FROM (1 << 0) +#define OPT_STRING_TO (1 << 1) +#define OPT_STRING_ALGO (1 << 2) +#define OPT_STRING_ICASE (1 << 3) +#define OPT_STRING (1 << 4) +#define OPT_STRING_HEX (1 << 5) + +static const struct option opts[] = +{ + { "string-from" , required_argument, 0, STRING_FROM }, + { "string-to" , required_argument, 0, STRING_TO }, + { "string-algo" , required_argument, 0, STRING_ALGO }, + { "string-icase" , no_argument, 0, STRING_ICASE }, + { "string" , required_argument, 0, STRING }, + { "string-hex" , required_argument, 0, STRING_HEX }, + { 0 } +}; + +static void print_help() +{ + printf( +"string options:\n" +"--string-from offset : Offset to start searching from (default: 0)\n" +"--string-to offset : Offset to stop searching (default: packet size)\n" +"--string-algo algorithm : Algorithm (bm = Boyer-Moore, kmp = Knuth-Pratt-Morris)\n" +"--string-icase : Ignore case when searching\n" +"--string [!] string : Match a string in a packet\n" +"--string-hex [!] string : Match a hex string in a packet, e.g. |0D 0A|, |0D0A|, netfilter|03|org\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct xt_string_info *info = (struct xt_string_info *)match->data; + + info->to_offset = UINT16_MAX; +} + +static void parse_string(const char *s, struct xt_string_info *info) +{ + /* xt_string does not need \0 at the end of the pattern */ + if (strlen(s) <= XT_STRING_MAX_PATTERN_SIZE) { + strncpy(info->pattern, s, XT_STRING_MAX_PATTERN_SIZE); + info->patlen = strnlen(s, XT_STRING_MAX_PATTERN_SIZE); + return; + } + ebt_print_error3("STRING too long \"%s\"", s); +} + +static void parse_hex_string(const char *s, struct xt_string_info *info) +{ + int i=0, slen, sindex=0, schar; + short hex_f = 0, literal_f = 0; + char hextmp[3]; + + slen = strlen(s); + + if (slen == 0) { + ebt_print_error3("STRING must contain at least one char"); + } + + while (i < slen) { + if (s[i] == '\\' && !hex_f) { + literal_f = 1; + } else if (s[i] == '\\') { + ebt_print_error3("Cannot include literals in hex data"); + } else if (s[i] == '|') { + if (hex_f) + hex_f = 0; + else { + hex_f = 1; + /* get past any initial whitespace just after the '|' */ + while (s[i+1] == ' ') + i++; + } + if (i+1 >= slen) + break; + else + i++; /* advance to the next character */ + } + + if (literal_f) { + if (i+1 >= slen) { + ebt_print_error3("Bad literal placement at end of string"); + } + info->pattern[sindex] = s[i+1]; + i += 2; /* skip over literal char */ + literal_f = 0; + } else if (hex_f) { + if (i+1 >= slen) { + ebt_print_error3("Odd number of hex digits"); + } + if (i+2 >= slen) { + /* must end with a "|" */ + ebt_print_error3("Invalid hex block"); + } + if (! isxdigit(s[i])) /* check for valid hex char */ + ebt_print_error3("Invalid hex char '%c'", s[i]); + if (! isxdigit(s[i+1])) /* check for valid hex char */ + ebt_print_error3("Invalid hex char '%c'", s[i+1]); + hextmp[0] = s[i]; + hextmp[1] = s[i+1]; + hextmp[2] = '\0'; + if (! sscanf(hextmp, "%x", &schar)) + ebt_print_error3("Invalid hex char `%c'", s[i]); + info->pattern[sindex] = (char) schar; + if (s[i+2] == ' ') + i += 3; /* spaces included in the hex block */ + else + i += 2; + } else { /* the char is not part of hex data, so just copy */ + info->pattern[sindex] = s[i]; + i++; + } + if (sindex > XT_STRING_MAX_PATTERN_SIZE) + ebt_print_error3("STRING too long \"%s\"", s); + sindex++; + } + info->patlen = sindex; +} + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct xt_string_info *info = (struct xt_string_info *)(*match)->data; + + switch (c) { + case STRING_FROM: + ebt_check_option2(flags, OPT_STRING_FROM); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --string-from"); + info->from_offset = (__u16)strtoul(optarg, NULL, 10); + break; + case STRING_TO: + ebt_check_option2(flags, OPT_STRING_TO); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --string-to"); + info->to_offset = (__u16)strtoul(optarg, NULL, 10); + break; + case STRING_ALGO: + ebt_check_option2(flags, OPT_STRING_ALGO); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --string-algo"); + if (snprintf(info->algo, sizeof(info->algo), "%s", optarg) >= + sizeof(info->algo)) + ebt_print_error2("\"%s\" is truncated", info->algo); + break; + case STRING_ICASE: + ebt_check_option2(flags, OPT_STRING_ICASE); + if (ebt_check_inverse2(optarg)) + ebt_print_error2("Unexpected `!' after --string-icase"); + info->u.v1.flags |= XT_STRING_FLAG_IGNORECASE; + break; + case STRING: + ebt_check_option2(flags, OPT_STRING); + parse_string(optarg, info); + if (ebt_check_inverse2(optarg)) { + info->u.v1.flags |= XT_STRING_FLAG_INVERT; + } + break; + case STRING_HEX: + ebt_check_option2(flags, OPT_STRING_HEX); + parse_hex_string(optarg, info); + if (ebt_check_inverse2(optarg)) { + info->u.v1.flags |= XT_STRING_FLAG_INVERT; + } + break; + default: + return 0; + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, const char *name, + unsigned int hookmask, unsigned int time) +{ + struct xt_string_info *info = (struct xt_string_info *)match->data; + + if (info->to_offset < info->from_offset) { + ebt_print_error3("'to' offset should not be less than 'from' " + "offset"); + } +} + +/* Test to see if the string contains non-printable chars or quotes */ +static unsigned short int is_hex_string(const char *str, + const unsigned short int len) +{ + unsigned int i; + for (i=0; i < len; i++) { + if (! isprint(str[i])) { + /* string contains at least one non-printable char */ + return 1; + } + } + /* use hex output if the last char is a "\" */ + if (str[len-1] == '\\') + return 1; + return 0; +} + +/* Print string with "|" chars included as one would pass to --string-hex */ +static void print_hex_string(const char *str, const unsigned short int len) +{ + unsigned int i; + /* start hex block */ + printf("\"|"); + for (i=0; i < len; i++) + printf("%02x", (unsigned char)str[i]); + /* close hex block */ + printf("|\" "); +} + +static void print_string(const char *str, const unsigned short int len) +{ + unsigned int i; + printf("\""); + for (i=0; i < len; i++) { + if (str[i] == '\"' || str[i] == '\\') + putchar('\\'); + printf("%c", (unsigned char) str[i]); + } + printf("\" "); /* closing quote */ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + const struct xt_string_info *info = + (const struct xt_string_info *) match->data; + int invert = info->u.v1.flags & XT_STRING_FLAG_INVERT; + + if (is_hex_string(info->pattern, info->patlen)) { + printf("--string-hex %s", invert ? "! " : ""); + print_hex_string(info->pattern, info->patlen); + } else { + printf("--string %s", invert ? "! " : ""); + print_string(info->pattern, info->patlen); + } + printf("--string-algo %s ", info->algo); + if (info->from_offset != 0) + printf("--string-from %u ", info->from_offset); + if (info->to_offset != 0) + printf("--string-to %u ", info->to_offset); + if (info->u.v1.flags & XT_STRING_FLAG_IGNORECASE) + printf("--string-icase "); +} + +static int compare(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2) +{ + const struct xt_string_info *info1 = + (const struct xt_string_info *) m1->data; + const struct xt_string_info *info2 = + (const struct xt_string_info *) m2->data; + + if (info1->from_offset != info2->from_offset) + return 0; + if (info1->to_offset != info2->to_offset) + return 0; + if (info1->u.v1.flags != info2->u.v1.flags) + return 0; + if (info1->patlen != info2->patlen) + return 0; + if (strncmp (info1->algo, info2->algo, XT_STRING_MAX_ALGO_NAME_SIZE) != 0) + return 0; + if (strncmp (info1->pattern, info2->pattern, info1->patlen) != 0) + return 0; + + return 1; +} + +static struct ebt_u_match string_match = +{ + .name = "string", + .revision = 1, + .size = sizeof(struct xt_string_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&string_match); +}
diff --git a/extensions/ebt_ulog.c b/extensions/ebt_ulog.c new file mode 100644 index 0000000..72a6c8b --- /dev/null +++ b/extensions/ebt_ulog.c
@@ -0,0 +1,186 @@ +/* ebt_ulog + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * November, 2004 + */ + +#define __need_time_t +#define __need_suseconds_t +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "../include/ebtables_u.h" +#include <sys/time.h> +#include <linux/netfilter_bridge/ebt_ulog.h> + +#define CP_NO_LIMIT_S "default_cprange" +#define CP_NO_LIMIT_N 0 + +#define ULOG_PREFIX '1' +#define ULOG_NLGROUP '2' +#define ULOG_CPRANGE '3' +#define ULOG_QTHRESHOLD '4' +#define ULOG_ULOG '5' +static const struct option opts[] = +{ + { "ulog-prefix" , required_argument, 0, ULOG_PREFIX }, + { "ulog-nlgroup" , required_argument, 0, ULOG_NLGROUP }, + { "ulog-cprange" , required_argument, 0, ULOG_CPRANGE }, + { "ulog-qthreshold", required_argument, 0, ULOG_QTHRESHOLD }, + { "ulog" , no_argument , 0, ULOG_ULOG }, + { 0 } +}; + +static void print_help() +{ + printf( +"ulog options:\n" +"--ulog : use the default ulog parameters\n" +"--ulog-prefix prefix : max %d characters (default is no prefix)\n" +"--ulog-nlgroup group : 0 < group number < %d (default = %d)\n" +"--ulog-cprange range : max copy range (default is " CP_NO_LIMIT_S ")\n" +"--ulog-qthreshold : 0 < queueing threshold < %d (default = %d)\n", + EBT_ULOG_PREFIX_LEN - 1, EBT_ULOG_MAXNLGROUPS + 1, + EBT_ULOG_DEFAULT_NLGROUP + 1, EBT_ULOG_MAX_QLEN + 1, + EBT_ULOG_DEFAULT_QTHRESHOLD); +} + +static void init(struct ebt_entry_watcher *watcher) +{ + struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data; + + uloginfo->prefix[0] = '\0'; + uloginfo->nlgroup = EBT_ULOG_DEFAULT_NLGROUP; + uloginfo->cprange = CP_NO_LIMIT_N; /* Use default netlink buffer size */ + uloginfo->qthreshold = EBT_ULOG_DEFAULT_QTHRESHOLD; +} + +#define OPT_PREFIX 0x01 +#define OPT_NLGROUP 0x02 +#define OPT_CPRANGE 0x04 +#define OPT_QTHRESHOLD 0x08 +#define OPT_ULOG 0x10 +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_watcher **watcher) +{ + struct ebt_ulog_info *uloginfo; + unsigned int i; + char *end; + + uloginfo = (struct ebt_ulog_info *)(*watcher)->data; + switch (c) { + case ULOG_PREFIX: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, OPT_PREFIX); + if (strlen(optarg) > EBT_ULOG_PREFIX_LEN - 1) + ebt_print_error("Prefix too long for ulog-prefix"); + strcpy(uloginfo->prefix, optarg); + break; + + case ULOG_NLGROUP: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, OPT_NLGROUP); + i = strtoul(optarg, &end, 10); + if (*end != '\0') + ebt_print_error2("Problem with ulog-nlgroup: %s", optarg); + if (i < 1 || i > EBT_ULOG_MAXNLGROUPS) + ebt_print_error2("the ulog-nlgroup number must be between 1 and 32"); + uloginfo->nlgroup = i - 1; + break; + + case ULOG_CPRANGE: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, OPT_CPRANGE); + i = strtoul(optarg, &end, 10); + if (*end != '\0') { + if (strcasecmp(optarg, CP_NO_LIMIT_S)) + ebt_print_error2("Problem with ulog-cprange: %s", optarg); + i = CP_NO_LIMIT_N; + } + uloginfo->cprange = i; + break; + + case ULOG_QTHRESHOLD: + if (ebt_check_inverse2(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, OPT_QTHRESHOLD); + i = strtoul(optarg, &end, 10); + if (*end != '\0') + ebt_print_error2("Problem with ulog-qthreshold: %s", optarg); + if (i > EBT_ULOG_MAX_QLEN) + ebt_print_error2("ulog-qthreshold argument %d exceeds the maximum of %d", i, EBT_ULOG_MAX_QLEN); + uloginfo->qthreshold = i; + break; + case ULOG_ULOG: + if (ebt_check_inverse(optarg)) + goto inverse_invalid; + ebt_check_option2(flags, OPT_ULOG); + break; + + default: + return 0; + } + return 1; + +inverse_invalid: + ebt_print_error("The use of '!' makes no sense for the ulog watcher"); + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher, const char *name, + unsigned int hookmask, unsigned int time) +{ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher) +{ + struct ebt_ulog_info *uloginfo = (struct ebt_ulog_info *)watcher->data; + + printf("--ulog-prefix \"%s\" --ulog-nlgroup %d --ulog-cprange ", + uloginfo->prefix, uloginfo->nlgroup + 1); + if (uloginfo->cprange == CP_NO_LIMIT_N) + printf(CP_NO_LIMIT_S); + else + printf("%d", uloginfo->cprange); + printf(" --ulog-qthreshold %d ", uloginfo->qthreshold); +} + +static int compare(const struct ebt_entry_watcher *w1, + const struct ebt_entry_watcher *w2) +{ + struct ebt_ulog_info *uloginfo1 = (struct ebt_ulog_info *)w1->data; + struct ebt_ulog_info *uloginfo2 = (struct ebt_ulog_info *)w2->data; + + if (uloginfo1->nlgroup != uloginfo2->nlgroup || + uloginfo1->cprange != uloginfo2->cprange || + uloginfo1->qthreshold != uloginfo2->qthreshold || + strcmp(uloginfo1->prefix, uloginfo2->prefix)) + return 0; + return 1; +} + +static struct ebt_u_watcher ulog_watcher = +{ + .name = "ulog", + .size = sizeof(struct ebt_ulog_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_watcher(&ulog_watcher); +}
diff --git a/extensions/ebt_vlan.c b/extensions/ebt_vlan.c new file mode 100644 index 0000000..0818d48 --- /dev/null +++ b/extensions/ebt_vlan.c
@@ -0,0 +1,187 @@ +/* ebt_vlan + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * Nick Fedchik <nick@fedchik.org.ua> + * + * June, 2002 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <ctype.h> +#include "../include/ebtables_u.h" +#include "../include/ethernetdb.h" +#include <linux/netfilter_bridge/ebt_vlan.h> +#include <linux/if_ether.h> + +#define NAME_VLAN_ID "id" +#define NAME_VLAN_PRIO "prio" +#define NAME_VLAN_ENCAP "encap" + +#define VLAN_ID '1' +#define VLAN_PRIO '2' +#define VLAN_ENCAP '3' + +static const struct option opts[] = { + {"vlan-id" , required_argument, NULL, VLAN_ID}, + {"vlan-prio" , required_argument, NULL, VLAN_PRIO}, + {"vlan-encap", required_argument, NULL, VLAN_ENCAP}, + { 0 } +}; + +/* + * option inverse flags definition + */ +#define OPT_VLAN_ID 0x01 +#define OPT_VLAN_PRIO 0x02 +#define OPT_VLAN_ENCAP 0x04 +#define OPT_VLAN_FLAGS (OPT_VLAN_ID | OPT_VLAN_PRIO | OPT_VLAN_ENCAP) + +struct ethertypeent *ethent; + +static void print_help() +{ + printf( +"vlan options:\n" +"--vlan-id [!] id : vlan-tagged frame identifier, 0,1-4096 (integer)\n" +"--vlan-prio [!] prio : Priority-tagged frame's user priority, 0-7 (integer)\n" +"--vlan-encap [!] encap : Encapsulated frame protocol (hexadecimal or name)\n"); +} + +static void init(struct ebt_entry_match *match) +{ + struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data; + vlaninfo->invflags = 0; + vlaninfo->bitmask = 0; +} + + +static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry, + unsigned int *flags, struct ebt_entry_match **match) +{ + struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) (*match)->data; + char *end; + struct ebt_vlan_info local; + + switch (c) { + case VLAN_ID: + ebt_check_option2(flags, OPT_VLAN_ID); + if (ebt_check_inverse2(optarg)) + vlaninfo->invflags |= EBT_VLAN_ID; + local.id = strtoul(optarg, &end, 10); + if (local.id > 4094 || *end != '\0') + ebt_print_error2("Invalid --vlan-id range ('%s')", optarg); + vlaninfo->id = local.id; + vlaninfo->bitmask |= EBT_VLAN_ID; + break; + case VLAN_PRIO: + ebt_check_option2(flags, OPT_VLAN_PRIO); + if (ebt_check_inverse2(optarg)) + vlaninfo->invflags |= EBT_VLAN_PRIO; + local.prio = strtoul(optarg, &end, 10); + if (local.prio >= 8 || *end != '\0') + ebt_print_error2("Invalid --vlan-prio range ('%s')", optarg); + vlaninfo->prio = local.prio; + vlaninfo->bitmask |= EBT_VLAN_PRIO; + break; + case VLAN_ENCAP: + ebt_check_option2(flags, OPT_VLAN_ENCAP); + if (ebt_check_inverse2(optarg)) + vlaninfo->invflags |= EBT_VLAN_ENCAP; + local.encap = strtoul(optarg, &end, 16); + if (*end != '\0') { + ethent = getethertypebyname(optarg); + if (ethent == NULL) + ebt_print_error("Unknown --vlan-encap value ('%s')", optarg); + local.encap = ethent->e_ethertype; + } + if (local.encap < ETH_ZLEN) + ebt_print_error2("Invalid --vlan-encap range ('%s')", optarg); + vlaninfo->encap = htons(local.encap); + vlaninfo->bitmask |= EBT_VLAN_ENCAP; + break; + default: + return 0; + + } + return 1; +} + +static void final_check(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, + const char *name, unsigned int hookmask, unsigned int time) +{ + if (entry->ethproto != ETH_P_8021Q || entry->invflags & EBT_IPROTO) + ebt_print_error("For vlan filtering the protocol must be specified as 802_1Q"); + + /* Check if specified vlan-id=0 (priority-tagged frame condition) + * when vlan-prio was specified. */ + /* I see no reason why a user should be prohibited to match on a perhaps impossible situation <BDS> + if (vlaninfo->bitmask & EBT_VLAN_PRIO && + vlaninfo->id && vlaninfo->bitmask & EBT_VLAN_ID) + ebt_print_error("When setting --vlan-prio the specified --vlan-id must be 0");*/ +} + +static void print(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match) +{ + struct ebt_vlan_info *vlaninfo = (struct ebt_vlan_info *) match->data; + + if (vlaninfo->bitmask & EBT_VLAN_ID) { + printf("--vlan-id %s%d ", (vlaninfo->invflags & EBT_VLAN_ID) ? "! " : "", vlaninfo->id); + } + if (vlaninfo->bitmask & EBT_VLAN_PRIO) { + printf("--vlan-prio %s%d ", (vlaninfo->invflags & EBT_VLAN_PRIO) ? "! " : "", vlaninfo->prio); + } + if (vlaninfo->bitmask & EBT_VLAN_ENCAP) { + printf("--vlan-encap %s", (vlaninfo->invflags & EBT_VLAN_ENCAP) ? "! " : ""); + ethent = getethertypebynumber(ntohs(vlaninfo->encap)); + if (ethent != NULL) { + printf("%s ", ethent->e_name); + } else { + printf("%4.4X ", ntohs(vlaninfo->encap)); + } + } +} + +static int compare(const struct ebt_entry_match *vlan1, + const struct ebt_entry_match *vlan2) +{ + struct ebt_vlan_info *vlaninfo1 = (struct ebt_vlan_info *) vlan1->data; + struct ebt_vlan_info *vlaninfo2 = (struct ebt_vlan_info *) vlan2->data; + + if (vlaninfo1->bitmask != vlaninfo2->bitmask) + return 0; + if (vlaninfo1->invflags != vlaninfo2->invflags) + return 0; + if (vlaninfo1->bitmask & EBT_VLAN_ID && + vlaninfo1->id != vlaninfo2->id) + return 0; + if (vlaninfo1->bitmask & EBT_VLAN_PRIO && + vlaninfo1->prio != vlaninfo2->prio) + return 0; + if (vlaninfo1->bitmask & EBT_VLAN_ENCAP && + vlaninfo1->encap != vlaninfo2->encap) + return 0; + return 1; +} + +static struct ebt_u_match vlan_match = { + .name = "vlan", + .size = sizeof(struct ebt_vlan_info), + .help = print_help, + .init = init, + .parse = parse, + .final_check = final_check, + .print = print, + .compare = compare, + .extra_ops = opts, +}; + +static void _INIT(void) +{ + ebt_register_match(&vlan_match); +}
diff --git a/extensions/ebtable_broute.c b/extensions/ebtable_broute.c new file mode 100644 index 0000000..c106f08 --- /dev/null +++ b/extensions/ebtable_broute.c
@@ -0,0 +1,29 @@ +/* ebtable_broute + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include "../include/ebtables_u.h" + + +static void print_help(const char **hn) +{ + printf("Supported chain for the broute table:\n"); + printf("%s\n",hn[NF_BR_BROUTING]); +} + +static struct +ebt_u_table table = +{ + .name = "broute", + .help = print_help, +}; + +static void _INIT(void) +{ + ebt_register_table(&table); +}
diff --git a/extensions/ebtable_filter.c b/extensions/ebtable_filter.c new file mode 100644 index 0000000..c0bf105 --- /dev/null +++ b/extensions/ebtable_filter.c
@@ -0,0 +1,35 @@ +/* ebtable_filter + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include "../include/ebtables_u.h" + +#define FILTER_VALID_HOOKS ((1 << NF_BR_LOCAL_IN) | (1 << NF_BR_FORWARD) | \ + (1 << NF_BR_LOCAL_OUT)) + +static void print_help(const char **hn) +{ + int i; + + printf("Supported chains for the filter table:\n"); + for (i = 0; i < NF_BR_NUMHOOKS; i++) + if (FILTER_VALID_HOOKS & (1 << i)) + printf("%s ", hn[i]); + printf("\n"); +} + +static struct ebt_u_table table = +{ + .name = "filter", + .help = print_help, +}; + +static void _INIT(void) +{ + ebt_register_table(&table); +}
diff --git a/extensions/ebtable_nat.c b/extensions/ebtable_nat.c new file mode 100644 index 0000000..ee04482 --- /dev/null +++ b/extensions/ebtable_nat.c
@@ -0,0 +1,36 @@ +/* ebtable_nat + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * April, 2002 + */ + +#include <stdio.h> +#include "../include/ebtables_u.h" + +#define NAT_VALID_HOOKS ((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT) | \ + (1 << NF_BR_POST_ROUTING)) + +static void print_help(const char **hn) +{ + int i; + + printf("Supported chains for the nat table:\n"); + for (i = 0; i < NF_BR_NUMHOOKS; i++) + if (NAT_VALID_HOOKS & (1 << i)) + printf("%s ", hn[i]); + printf("\n"); +} + +static struct +ebt_u_table table = +{ + .name = "nat", + .help = print_help, +}; + +static void _INIT(void) +{ + ebt_register_table(&table); +}
diff --git a/getethertype.c b/getethertype.c new file mode 100644 index 0000000..e4e4812 --- /dev/null +++ b/getethertype.c
@@ -0,0 +1,161 @@ +/* +* getethertype.c +* +* This file was part of the NYS Library. +* +** The NYS Library is free software; you can redistribute it and/or +** modify it under the terms of the GNU Library General Public License as +** published by the Free Software Foundation; either version 2 of the +** License, or (at your option) any later version. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/******************************************************************** +* Description: Ethertype name service switch and the ethertypes +* database access functions +* Author: Nick Fedchik <fnm@ukrsat.com> +* Checker: Bart De Schuymer <bdschuym@pandora.be> +* Origin: uClibc-0.9.16/libc/inet/getproto.c +* Created at: Mon Nov 11 12:20:11 EET 2002 +********************************************************************/ + +#include <ctype.h> +#include <features.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/ether.h> +#include <net/ethernet.h> + +#include "ethernetdb.h" + +#define MAXALIASES 35 + +static FILE *etherf = NULL; +static char line[BUFSIZ + 1]; +static struct ethertypeent et_ent; +static char *ethertype_aliases[MAXALIASES]; +static int ethertype_stayopen; + +void setethertypeent(int f) +{ + if (etherf == NULL) + etherf = fopen(_PATH_ETHERTYPES, "r"); + else + rewind(etherf); + ethertype_stayopen |= f; +} + +void endethertypeent(void) +{ + if (etherf) { + fclose(etherf); + etherf = NULL; + } + ethertype_stayopen = 0; +} + +struct ethertypeent *getethertypeent(void) +{ + char *e; + char *endptr; + register char *cp, **q; + + if (etherf == NULL + && (etherf = fopen(_PATH_ETHERTYPES, "r")) == NULL) { + return (NULL); + } + +again: + if ((e = fgets(line, BUFSIZ, etherf)) == NULL) { + return (NULL); + } + if (*e == '#') + goto again; + cp = strpbrk(e, "#\n"); + if (cp == NULL) + goto again; + *cp = '\0'; + et_ent.e_name = e; + cp = strpbrk(e, " \t"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + while (*cp == ' ' || *cp == '\t') + cp++; + e = strpbrk(cp, " \t"); + if (e != NULL) + *e++ = '\0'; +// Check point + et_ent.e_ethertype = strtol(cp, &endptr, 16); + if (*endptr != '\0' + || (et_ent.e_ethertype < ETH_ZLEN + || et_ent.e_ethertype > 0xFFFF)) + goto again; // Skip invalid etherproto type entry + q = et_ent.e_aliases = ethertype_aliases; + if (e != NULL) { + cp = e; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < ðertype_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + return (&et_ent); +} + + +struct ethertypeent *getethertypebyname(const char *name) +{ + register struct ethertypeent *e; + register char **cp; + + setethertypeent(ethertype_stayopen); + while ((e = getethertypeent()) != NULL) { + if (strcasecmp(e->e_name, name) == 0) + break; + for (cp = e->e_aliases; *cp != 0; cp++) + if (strcasecmp(*cp, name) == 0) + goto found; + } +found: + if (!ethertype_stayopen) + endethertypeent(); + return (e); +} + +struct ethertypeent *getethertypebynumber(int type) +{ + register struct ethertypeent *e; + + setethertypeent(ethertype_stayopen); + while ((e = getethertypeent()) != NULL) + if (e->e_ethertype == type) + break; + if (!ethertype_stayopen) + endethertypeent(); + return (e); +}
diff --git a/include/ebtables_u.h b/include/ebtables_u.h new file mode 100644 index 0000000..901b282 --- /dev/null +++ b/include/ebtables_u.h
@@ -0,0 +1,407 @@ +/* + * $Id: ebtables.c,v 1.03 2002/01/19 + * + * Copyright (C) 2001-2002 Bart De Schuymer + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef EBTABLES_U_H +#define EBTABLES_U_H +#include <netinet/in.h> +#include <netinet/ether.h> +#include <linux/netfilter_bridge/ebtables.h> +#include <linux/netfilter/x_tables.h> + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif + +#define EXEC_STYLE_PRG 0 +#define EXEC_STYLE_DAEMON 1 + +#ifndef EBT_MIN_ALIGN +#define EBT_MIN_ALIGN (__alignof__(struct _xt_align)) +#endif +#define EBT_ALIGN(s) (((s) + (EBT_MIN_ALIGN-1)) & ~(EBT_MIN_ALIGN-1)) +#define ERRORMSG_MAXLEN 128 + +#define _INIT __attribute__((constructor)) _init + +struct ebt_u_entries +{ + int policy; + unsigned int nentries; + /* counter offset for this chain */ + unsigned int counter_offset; + /* used for udc */ + unsigned int hook_mask; + char *kernel_start; + char name[EBT_CHAIN_MAXNAMELEN]; + struct ebt_u_entry *entries; +}; + +struct ebt_cntchanges +{ + unsigned short type; + unsigned short change; /* determines incremental/decremental/change */ + struct ebt_cntchanges *prev; + struct ebt_cntchanges *next; +}; + +#define EBT_ORI_MAX_CHAINS 10 +struct ebt_u_replace +{ + char name[EBT_TABLE_MAXNAMELEN]; + unsigned int valid_hooks; + /* nr of rules in the table */ + unsigned int nentries; + unsigned int num_chains; + unsigned int max_chains; + struct ebt_u_entries **chains; + /* nr of counters userspace expects back */ + unsigned int num_counters; + /* where the kernel will put the old counters */ + struct ebt_counter *counters; + /* + * can be used e.g. to know if a standard option + * has been specified twice + */ + unsigned int flags; + /* we stick the specified command (e.g. -A) in here */ + char command; + /* + * here we stick the chain to do our thing on (can be -1 if unspecified) + */ + int selected_chain; + /* used for the atomic option */ + char *filename; + /* tells what happened to the old rules (counter changes) */ + struct ebt_cntchanges *cc; +}; + +struct ebt_u_table +{ + char name[EBT_TABLE_MAXNAMELEN]; + void (*check)(struct ebt_u_replace *repl); + void (*help)(const char **); + struct ebt_u_table *next; +}; + +struct ebt_u_match_list +{ + struct ebt_u_match_list *next; + struct ebt_entry_match *m; +}; + +struct ebt_u_watcher_list +{ + struct ebt_u_watcher_list *next; + struct ebt_entry_watcher *w; +}; + +struct ebt_u_entry +{ + unsigned int bitmask; + unsigned int invflags; + uint16_t ethproto; + char in[IFNAMSIZ]; + char logical_in[IFNAMSIZ]; + char out[IFNAMSIZ]; + char logical_out[IFNAMSIZ]; + unsigned char sourcemac[ETH_ALEN]; + unsigned char sourcemsk[ETH_ALEN]; + unsigned char destmac[ETH_ALEN]; + unsigned char destmsk[ETH_ALEN]; + struct ebt_u_match_list *m_list; + struct ebt_u_watcher_list *w_list; + struct ebt_entry_target *t; + struct ebt_u_entry *prev; + struct ebt_u_entry *next; + struct ebt_counter cnt; + struct ebt_counter cnt_surplus; /* for increasing/decreasing a counter and for option 'C' */ + struct ebt_cntchanges *cc; + /* the standard target needs this to know the name of a udc when + * printing out rules. */ + struct ebt_u_replace *replace; +}; + +struct ebt_u_match +{ + char name[EBT_FUNCTION_MAXNAMELEN]; + uint8_t revision; + /* size of the real match data */ + unsigned int size; + void (*help)(void); + void (*init)(struct ebt_entry_match *m); + int (*parse)(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_match **match); + void (*final_check)(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match, + const char *name, unsigned int hookmask, unsigned int time); + void (*print)(const struct ebt_u_entry *entry, + const struct ebt_entry_match *match); + int (*compare)(const struct ebt_entry_match *m1, + const struct ebt_entry_match *m2); + const struct option *extra_ops; + /* + * can be used e.g. to check for multiple occurance of the same option + */ + unsigned int flags; + unsigned int option_offset; + struct ebt_entry_match *m; + /* + * if used == 1 we no longer have to add it to + * the match chain of the new entry + * be sure to put it back on 0 when finished + */ + unsigned int used; + struct ebt_u_match *next; +}; + +struct ebt_u_watcher +{ + char name[EBT_FUNCTION_MAXNAMELEN]; + unsigned int size; + void (*help)(void); + void (*init)(struct ebt_entry_watcher *w); + int (*parse)(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_watcher **watcher); + void (*final_check)(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watch, const char *name, + unsigned int hookmask, unsigned int time); + void (*print)(const struct ebt_u_entry *entry, + const struct ebt_entry_watcher *watcher); + int (*compare)(const struct ebt_entry_watcher *w1, + const struct ebt_entry_watcher *w2); + const struct option *extra_ops; + unsigned int flags; + unsigned int option_offset; + struct ebt_entry_watcher *w; + unsigned int used; + struct ebt_u_watcher *next; +}; + +struct ebt_u_target +{ + char name[EBT_FUNCTION_MAXNAMELEN]; + unsigned int size; + void (*help)(void); + void (*init)(struct ebt_entry_target *t); + int (*parse)(int c, char **argv, int argc, + const struct ebt_u_entry *entry, unsigned int *flags, + struct ebt_entry_target **target); + void (*final_check)(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target, const char *name, + unsigned int hookmask, unsigned int time); + void (*print)(const struct ebt_u_entry *entry, + const struct ebt_entry_target *target); + int (*compare)(const struct ebt_entry_target *t1, + const struct ebt_entry_target *t2); + const struct option *extra_ops; + unsigned int option_offset; + unsigned int flags; + struct ebt_entry_target *t; + unsigned int used; + struct ebt_u_target *next; +}; + + +struct ebt_icmp_names { + const char *name; + uint8_t type; + uint8_t code_min, code_max; +}; + + + +/* libebtc.c */ + +extern struct ebt_u_table *ebt_tables; +extern struct ebt_u_match *ebt_matches; +extern struct ebt_u_watcher *ebt_watchers; +extern struct ebt_u_target *ebt_targets; + +extern int use_lockfd; + +void ebt_register_table(struct ebt_u_table *); +void ebt_register_match(struct ebt_u_match *); +void ebt_register_watcher(struct ebt_u_watcher *); +void ebt_register_target(struct ebt_u_target *t); +int ebt_get_kernel_table(struct ebt_u_replace *replace, int init); +struct ebt_u_target *ebt_find_target(const char *name); +struct ebt_u_match *ebt_find_match(const char *name); +struct ebt_u_watcher *ebt_find_watcher(const char *name); +struct ebt_u_table *ebt_find_table(const char *name); +int ebtables_insmod(const char *modname); +void ebt_list_extensions(); +void ebt_initialize_entry(struct ebt_u_entry *e); +void ebt_cleanup_replace(struct ebt_u_replace *replace); +void ebt_reinit_extensions(); +void ebt_double_chains(struct ebt_u_replace *replace); +void ebt_free_u_entry(struct ebt_u_entry *e); +struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, + const char* arg); +struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, + const char* arg); +int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg); +/**/ +void ebt_change_policy(struct ebt_u_replace *replace, int policy); +void ebt_flush_chains(struct ebt_u_replace *replace); +int ebt_check_rule_exists(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry); +void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, + int rule_nr); +void ebt_delete_rule(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int begin, int end); +void ebt_zero_counters(struct ebt_u_replace *replace); +void ebt_change_counters(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int begin, int end, + struct ebt_counter *cnt, int mask); +void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy); +void ebt_delete_chain(struct ebt_u_replace *replace); +void ebt_rename_chain(struct ebt_u_replace *replace, const char *name); +/**/ +void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e, + struct ebt_u_entries *entries); +int ebt_check_for_references(struct ebt_u_replace *replace, int print_err); +int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr, + int print_err); +void ebt_check_for_loops(struct ebt_u_replace *replace); +void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m); +void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w); +void ebt_iterate_matches(void (*f)(struct ebt_u_match *)); +void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *)); +void ebt_iterate_targets(void (*f)(struct ebt_u_target *)); +void __ebt_print_bug(char *file, int line, char *format, ...); +void __ebt_print_error(char *format, ...); + +/* communication.c */ + +int ebt_get_table(struct ebt_u_replace *repl, int init); +void ebt_deliver_counters(struct ebt_u_replace *repl); +void ebt_deliver_table(struct ebt_u_replace *repl); + +/* useful_functions.c */ + +extern int ebt_invert; +void ebt_check_option(unsigned int *flags, unsigned int mask); +#define ebt_check_inverse(arg) _ebt_check_inverse(arg, argc, argv) +int _ebt_check_inverse(const char option[], int argc, char **argv); +void ebt_print_mac(const unsigned char *mac); +void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask); +int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask); +void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk); +char *ebt_mask_to_dotted(uint32_t mask); +void ebt_parse_ip6_address(char *address, struct in6_addr *addr, + struct in6_addr *msk); +char *ebt_ip6_to_numeric(const struct in6_addr *addrp); +char *ebt_ip6_mask_to_string(const struct in6_addr *msk); + +int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes, + const char *icmptype, uint8_t type[], uint8_t code[]); +void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes, + size_t n_codes, uint8_t *type, uint8_t *code); +void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes, + size_t n_codes); + +int do_command(int argc, char *argv[], int exec_style, + struct ebt_u_replace *replace_); + +struct ethertypeent *parseethertypebynumber(int type); + +#define ebt_to_chain(repl) \ +({struct ebt_u_entries *_ch = NULL; \ +if (repl->selected_chain != -1) \ + _ch = repl->chains[repl->selected_chain]; \ +_ch;}) +#define ebt_print_bug(format, args...) \ + __ebt_print_bug(__FILE__, __LINE__, format, ##args) +#define ebt_print_error(format,args...) __ebt_print_error(format, ##args); +#define ebt_print_error2(format, args...) do {__ebt_print_error(format, ##args); \ + return -1;} while (0) +#define ebt_print_error3(format, args...) do {__ebt_print_error(format, ##args); \ + return;} while (0) +#define ebt_check_option2(flags,mask) \ +({ebt_check_option(flags,mask); \ + if (ebt_errormsg[0] != '\0') \ + return -1;}) +#define ebt_check_inverse2(option) \ +({int __ret = ebt_check_inverse(option); \ +if (ebt_errormsg[0] != '\0') \ + return -1; \ +if (!optarg) { \ + __ebt_print_error("Option without (mandatory) argument"); \ + return -1; \ +} \ +__ret;}) +#define ebt_print_memory() do {printf("Ebtables: " __FILE__ \ + " %s %d :Out of memory.\n", __FUNCTION__, __LINE__); exit(-1);} while (0) + +/* used for keeping the rule counters right during rule adds or deletes */ +#define CNT_NORM 0 +#define CNT_DEL 1 +#define CNT_ADD 2 +#define CNT_CHANGE 3 + +extern const char *ebt_hooknames[NF_BR_NUMHOOKS]; +extern const char *ebt_standard_targets[NUM_STANDARD_TARGETS]; +extern char ebt_errormsg[ERRORMSG_MAXLEN]; +extern char *ebt_modprobe; +extern int ebt_silent; +extern int ebt_printstyle_mac; + +/* + * Transforms a target string into the right integer, + * returns 0 on success. + */ +#define FILL_TARGET(_str, _pos) ({ \ + int _i, _ret = 0; \ + for (_i = 0; _i < NUM_STANDARD_TARGETS; _i++) \ + if (!strcmp(_str, ebt_standard_targets[_i])) {\ + _pos = -_i - 1; \ + break; \ + } \ + if (_i == NUM_STANDARD_TARGETS) \ + _ret = 1; \ + _ret; \ +}) + +/* Transforms the target value to an index into standard_targets[] */ +#define TARGET_INDEX(_value) (-_value - 1) +/* Returns a target string corresponding to the value */ +#define TARGET_NAME(_value) (ebt_standard_targets[TARGET_INDEX(_value)]) +/* True if the hook mask denotes that the rule is in a base chain */ +#define BASE_CHAIN (hookmask & (1 << NF_BR_NUMHOOKS)) +/* Clear the bit in the hook_mask that tells if the rule is on a base chain */ +#define CLEAR_BASE_CHAIN_BIT (hookmask &= ~(1 << NF_BR_NUMHOOKS)) +#define PRINT_VERSION printf(PROGNAME" v"PROGVERSION" (legacy) ("PROGDATE")\n") +#ifndef PROC_SYS_MODPROBE +#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" +#endif +#define ATOMIC_ENV_VARIABLE "EBTABLES_ATOMIC_FILE" + +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif +#endif /* EBTABLES_U_H */
diff --git a/include/ethernetdb.h b/include/ethernetdb.h new file mode 100644 index 0000000..1683abe --- /dev/null +++ b/include/ethernetdb.h
@@ -0,0 +1,57 @@ +/* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* All data returned by the network data base library are supplied in + host order and returned in network order (suitable for use in + system calls). */ + +#ifndef _ETHERNETDB_H +#define _ETHERNETDB_H 1 + +#include <features.h> +#include <netinet/in.h> +#include <stdint.h> + +/* Absolute file name for network data base files. */ +#ifndef _PATH_ETHERTYPES +#define _PATH_ETHERTYPES "/etc/ethertypes" +#endif /* _PATH_ETHERTYPES */ + +struct ethertypeent { + char *e_name; /* Official ethernet type name. */ + char **e_aliases; /* Alias list. */ + int e_ethertype; /* Ethernet type number. */ +}; + +/* Open ethertype data base files and mark them as staying open even + after a later search if STAY_OPEN is non-zero. */ +extern void setethertypeent(int __stay_open); + +/* Close ethertype data base files and clear `stay open' flag. */ +extern void endethertypeent(void); + +/* Get next entry from ethertype data base file. Open data base if + necessary. */ +extern struct ethertypeent *getethertypeent(void); + +/* Return entry from ethertype data base for network with NAME. */ +extern struct ethertypeent *getethertypebyname(__const char *__name); + +/* Return entry from ethertype data base which number is PROTO. */ +extern struct ethertypeent *getethertypebynumber(int __ethertype); + + +#endif /* ethernetdb.h */
diff --git a/include/linux/netfilter/xt_AUDIT.h b/include/linux/netfilter/xt_AUDIT.h new file mode 100644 index 0000000..44111b2 --- /dev/null +++ b/include/linux/netfilter/xt_AUDIT.h
@@ -0,0 +1,30 @@ +/* + * Header file for iptables xt_AUDIT target + * + * (C) 2010-2011 Thomas Graf <tgraf@redhat.com> + * (C) 2010-2011 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _XT_AUDIT_TARGET_H +#define _XT_AUDIT_TARGET_H + +#include <linux/types.h> + +enum { + XT_AUDIT_TYPE_ACCEPT = 0, + XT_AUDIT_TYPE_DROP, + XT_AUDIT_TYPE_REJECT, + __XT_AUDIT_TYPE_MAX, +}; + +#define XT_AUDIT_TYPE_MAX (__XT_AUDIT_TYPE_MAX - 1) + +struct xt_AUDIT_info { + __u8 type; /* XT_AUDIT_TYPE_* */ +}; + +#endif /* _XT_AUDIT_TARGET_H */
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h new file mode 100644 index 0000000..c4dbfd9 --- /dev/null +++ b/include/linux/netfilter_bridge.h
@@ -0,0 +1,27 @@ +#ifndef __LINUX_BRIDGE_NETFILTER_H +#define __LINUX_BRIDGE_NETFILTER_H + +/* bridge-specific defines for netfilter. + */ + +#include <linux/netfilter.h> +#include <linux/if_ether.h> +#include <linux/if_vlan.h> +#include <linux/if_pppox.h> + +/* Bridge Hooks */ +/* After promisc drops, checksum checks. */ +#define NF_BR_PRE_ROUTING 0 +/* If the packet is destined for this box. */ +#define NF_BR_LOCAL_IN 1 +/* If the packet is destined for another interface. */ +#define NF_BR_FORWARD 2 +/* Packets coming from a local process. */ +#define NF_BR_LOCAL_OUT 3 +/* Packets about to hit the wire. */ +#define NF_BR_POST_ROUTING 4 +/* Not really a hook, but used for the ebtables broute table */ +#define NF_BR_BROUTING 5 +#define NF_BR_NUMHOOKS 6 + +#endif /* __LINUX_BRIDGE_NETFILTER_H */
diff --git a/include/linux/netfilter_bridge/ebt_802_3.h b/include/linux/netfilter_bridge/ebt_802_3.h new file mode 100644 index 0000000..70028c1 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_802_3.h
@@ -0,0 +1,63 @@ +#ifndef __LINUX_BRIDGE_EBT_802_3_H +#define __LINUX_BRIDGE_EBT_802_3_H + +#include <linux/types.h> +#include <linux/if_ether.h> + +#define EBT_802_3_SAP 0x01 +#define EBT_802_3_TYPE 0x02 + +#define EBT_802_3_MATCH "802_3" + +/* + * If frame has DSAP/SSAP value 0xaa you must check the SNAP type + * to discover what kind of packet we're carrying. + */ +#define CHECK_TYPE 0xaa + +/* + * Control field may be one or two bytes. If the first byte has + * the value 0x03 then the entire length is one byte, otherwise it is two. + * One byte controls are used in Unnumbered Information frames. + * Two byte controls are used in Numbered Information frames. + */ +#define IS_UI 0x03 + +#define EBT_802_3_MASK (EBT_802_3_SAP | EBT_802_3_TYPE | EBT_802_3) + +/* ui has one byte ctrl, ni has two */ +struct hdr_ui { + __u8 dsap; + __u8 ssap; + __u8 ctrl; + __u8 orig[3]; + __be16 type; +}; + +struct hdr_ni { + __u8 dsap; + __u8 ssap; + __be16 ctrl; + __u8 orig[3]; + __be16 type; +}; + +struct ebt_802_3_hdr { + __u8 daddr[ETH_ALEN]; + __u8 saddr[ETH_ALEN]; + __be16 len; + union { + struct hdr_ui ui; + struct hdr_ni ni; + } llc; +}; + + +struct ebt_802_3_info { + __u8 sap; + __be16 type; + __u8 bitmask; + __u8 invflags; +}; + +#endif /* __LINUX_BRIDGE_EBT_802_3_H */
diff --git a/include/linux/netfilter_bridge/ebt_among.h b/include/linux/netfilter_bridge/ebt_among.h new file mode 100644 index 0000000..bd4e3ad --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_among.h
@@ -0,0 +1,64 @@ +#ifndef __LINUX_BRIDGE_EBT_AMONG_H +#define __LINUX_BRIDGE_EBT_AMONG_H + +#include <linux/types.h> + +#define EBT_AMONG_DST 0x01 +#define EBT_AMONG_SRC 0x02 + +/* Grzegorz Borowiak <grzes@gnu.univ.gda.pl> 2003 + * + * Write-once-read-many hash table, used for checking if a given + * MAC address belongs to a set or not and possibly for checking + * if it is related with a given IPv4 address. + * + * The hash value of an address is its last byte. + * + * In real-world ethernet addresses, values of the last byte are + * evenly distributed and there is no need to consider other bytes. + * It would only slow the routines down. + * + * For MAC address comparison speedup reasons, we introduce a trick. + * MAC address is mapped onto an array of two 32-bit integers. + * This pair of integers is compared with MAC addresses in the + * hash table, which are stored also in form of pairs of integers + * (in `cmp' array). This is quick as it requires only two elementary + * number comparisons in worst case. Further, we take advantage of + * fact that entropy of 3 last bytes of address is larger than entropy + * of 3 first bytes. So first we compare 4 last bytes of addresses and + * if they are the same we compare 2 first. + * + * Yes, it is a memory overhead, but in 2003 AD, who cares? + */ + +struct ebt_mac_wormhash_tuple { + __u32 cmp[2]; + __be32 ip; +}; + +struct ebt_mac_wormhash { + int table[257]; + int poolsize; + struct ebt_mac_wormhash_tuple pool[0]; +}; + +#define ebt_mac_wormhash_size(x) ((x) ? sizeof(struct ebt_mac_wormhash) \ + + (x)->poolsize * sizeof(struct ebt_mac_wormhash_tuple) : 0) + +struct ebt_among_info { + int wh_dst_ofs; + int wh_src_ofs; + int bitmask; +}; + +#define EBT_AMONG_DST_NEG 0x1 +#define EBT_AMONG_SRC_NEG 0x2 + +#define ebt_among_wh_dst(x) ((x)->wh_dst_ofs ? \ + (struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_dst_ofs) : NULL) +#define ebt_among_wh_src(x) ((x)->wh_src_ofs ? \ + (struct ebt_mac_wormhash*)((char*)(x) + (x)->wh_src_ofs) : NULL) + +#define EBT_AMONG_MATCH "among" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_arp.h b/include/linux/netfilter_bridge/ebt_arp.h new file mode 100644 index 0000000..522f3e4 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_arp.h
@@ -0,0 +1,36 @@ +#ifndef __LINUX_BRIDGE_EBT_ARP_H +#define __LINUX_BRIDGE_EBT_ARP_H + +#include <linux/types.h> + +#define EBT_ARP_OPCODE 0x01 +#define EBT_ARP_HTYPE 0x02 +#define EBT_ARP_PTYPE 0x04 +#define EBT_ARP_SRC_IP 0x08 +#define EBT_ARP_DST_IP 0x10 +#define EBT_ARP_SRC_MAC 0x20 +#define EBT_ARP_DST_MAC 0x40 +#define EBT_ARP_GRAT 0x80 +#define EBT_ARP_MASK (EBT_ARP_OPCODE | EBT_ARP_HTYPE | EBT_ARP_PTYPE | \ + EBT_ARP_SRC_IP | EBT_ARP_DST_IP | EBT_ARP_SRC_MAC | EBT_ARP_DST_MAC | \ + EBT_ARP_GRAT) +#define EBT_ARP_MATCH "arp" + +struct ebt_arp_info +{ + __be16 htype; + __be16 ptype; + __be16 opcode; + __be32 saddr; + __be32 smsk; + __be32 daddr; + __be32 dmsk; + unsigned char smaddr[ETH_ALEN]; + unsigned char smmsk[ETH_ALEN]; + unsigned char dmaddr[ETH_ALEN]; + unsigned char dmmsk[ETH_ALEN]; + __u8 bitmask; + __u8 invflags; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_arpreply.h b/include/linux/netfilter_bridge/ebt_arpreply.h new file mode 100644 index 0000000..7e77896 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_arpreply.h
@@ -0,0 +1,10 @@ +#ifndef __LINUX_BRIDGE_EBT_ARPREPLY_H +#define __LINUX_BRIDGE_EBT_ARPREPLY_H + +struct ebt_arpreply_info { + unsigned char mac[ETH_ALEN]; + int target; +}; +#define EBT_ARPREPLY_TARGET "arpreply" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_ip.h b/include/linux/netfilter_bridge/ebt_ip.h new file mode 100644 index 0000000..46d6261 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_ip.h
@@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * ebt_ip + * + * Authors: + * Bart De Schuymer <bart.de.schuymer@pandora.be> + * + * April, 2002 + * + * Changes: + * added ip-sport and ip-dport + * Innominate Security Technologies AG <mhopf@innominate.com> + * September, 2002 + */ + +#ifndef __LINUX_BRIDGE_EBT_IP_H +#define __LINUX_BRIDGE_EBT_IP_H + +#include <linux/types.h> + +#define EBT_IP_SOURCE 0x01 +#define EBT_IP_DEST 0x02 +#define EBT_IP_TOS 0x04 +#define EBT_IP_PROTO 0x08 +#define EBT_IP_SPORT 0x10 +#define EBT_IP_DPORT 0x20 +#define EBT_IP_ICMP 0x40 +#define EBT_IP_IGMP 0x80 +#define EBT_IP_MASK (EBT_IP_SOURCE | EBT_IP_DEST | EBT_IP_TOS | EBT_IP_PROTO |\ + EBT_IP_SPORT | EBT_IP_DPORT | EBT_IP_ICMP | EBT_IP_IGMP) +#define EBT_IP_MATCH "ip" + +/* the same values are used for the invflags */ +struct ebt_ip_info { + __be32 saddr; + __be32 daddr; + __be32 smsk; + __be32 dmsk; + __u8 tos; + __u8 protocol; + __u8 bitmask; + __u8 invflags; + union { + __u16 sport[2]; + __u8 icmp_type[2]; + __u8 igmp_type[2]; + }; + union { + __u16 dport[2]; + __u8 icmp_code[2]; + }; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_ip6.h b/include/linux/netfilter_bridge/ebt_ip6.h new file mode 100644 index 0000000..42b8896 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_ip6.h
@@ -0,0 +1,50 @@ +/* + * ebt_ip6 + * + * Authors: + * Kuo-Lang Tseng <kuo-lang.tseng@intel.com> + * Manohar Castelino <manohar.r.castelino@intel.com> + * + * Jan 11, 2008 + * + */ + +#ifndef __LINUX_BRIDGE_EBT_IP6_H +#define __LINUX_BRIDGE_EBT_IP6_H + +#include <linux/types.h> + +#define EBT_IP6_SOURCE 0x01 +#define EBT_IP6_DEST 0x02 +#define EBT_IP6_TCLASS 0x04 +#define EBT_IP6_PROTO 0x08 +#define EBT_IP6_SPORT 0x10 +#define EBT_IP6_DPORT 0x20 +#define EBT_IP6_ICMP6 0x40 + +#define EBT_IP6_MASK (EBT_IP6_SOURCE | EBT_IP6_DEST | EBT_IP6_TCLASS |\ + EBT_IP6_PROTO | EBT_IP6_SPORT | EBT_IP6_DPORT | \ + EBT_IP6_ICMP6) +#define EBT_IP6_MATCH "ip6" + +/* the same values are used for the invflags */ +struct ebt_ip6_info { + struct in6_addr saddr; + struct in6_addr daddr; + struct in6_addr smsk; + struct in6_addr dmsk; + __u8 tclass; + __u8 protocol; + __u8 bitmask; + __u8 invflags; + union { + __u16 sport[2]; + __u8 icmpv6_type[2]; + }; + union { + __u16 dport[2]; + __u8 icmpv6_code[2]; + }; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_limit.h b/include/linux/netfilter_bridge/ebt_limit.h new file mode 100644 index 0000000..66d80b3 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_limit.h
@@ -0,0 +1,24 @@ +#ifndef __LINUX_BRIDGE_EBT_LIMIT_H +#define __LINUX_BRIDGE_EBT_LIMIT_H + +#include <linux/types.h> + +#define EBT_LIMIT_MATCH "limit" + +/* timings are in milliseconds. */ +#define EBT_LIMIT_SCALE 10000 + +/* 1/10,000 sec period => max of 10,000/sec. Min rate is then 429490 + seconds, or one every 59 hours. */ + +struct ebt_limit_info { + __u32 avg; /* Average secs between packets * scale */ + __u32 burst; /* Period multiplier for upper limit. */ + + /* Used internally by the kernel */ + unsigned long prev; + __u32 credit; + __u32 credit_cap, cost; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_log.h b/include/linux/netfilter_bridge/ebt_log.h new file mode 100644 index 0000000..7e7f1d1 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_log.h
@@ -0,0 +1,20 @@ +#ifndef __LINUX_BRIDGE_EBT_LOG_H +#define __LINUX_BRIDGE_EBT_LOG_H + +#include <linux/types.h> + +#define EBT_LOG_IP 0x01 /* if the frame is made by ip, log the ip information */ +#define EBT_LOG_ARP 0x02 +#define EBT_LOG_NFLOG 0x04 +#define EBT_LOG_IP6 0x08 +#define EBT_LOG_MASK (EBT_LOG_IP | EBT_LOG_ARP | EBT_LOG_IP6) +#define EBT_LOG_PREFIX_SIZE 30 +#define EBT_LOG_WATCHER "log" + +struct ebt_log_info { + __u8 loglevel; + __u8 prefix[EBT_LOG_PREFIX_SIZE]; + __u32 bitmask; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_m.h b/include/linux/netfilter_bridge/ebt_mark_m.h new file mode 100644 index 0000000..410f9e5 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_mark_m.h
@@ -0,0 +1,16 @@ +#ifndef __LINUX_BRIDGE_EBT_MARK_M_H +#define __LINUX_BRIDGE_EBT_MARK_M_H + +#include <linux/types.h> + +#define EBT_MARK_AND 0x01 +#define EBT_MARK_OR 0x02 +#define EBT_MARK_MASK (EBT_MARK_AND | EBT_MARK_OR) +struct ebt_mark_m_info { + unsigned long mark, mask; + __u8 invert; + __u8 bitmask; +}; +#define EBT_MARK_MATCH "mark_m" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_mark_t.h b/include/linux/netfilter_bridge/ebt_mark_t.h new file mode 100644 index 0000000..7d5a268 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_mark_t.h
@@ -0,0 +1,23 @@ +#ifndef __LINUX_BRIDGE_EBT_MARK_T_H +#define __LINUX_BRIDGE_EBT_MARK_T_H + +/* The target member is reused for adding new actions, the + * value of the real target is -1 to -NUM_STANDARD_TARGETS. + * For backward compatibility, the 4 lsb (2 would be enough, + * but let's play it safe) are kept to designate this target. + * The remaining bits designate the action. By making the set + * action 0xfffffff0, the result will look ok for older + * versions. [September 2006] */ +#define MARK_SET_VALUE (0xfffffff0) +#define MARK_OR_VALUE (0xffffffe0) +#define MARK_AND_VALUE (0xffffffd0) +#define MARK_XOR_VALUE (0xffffffc0) + +struct ebt_mark_t_info { + unsigned long mark; + /* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */ + int target; +}; +#define EBT_MARK_TARGET "mark" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_nat.h b/include/linux/netfilter_bridge/ebt_nat.h new file mode 100644 index 0000000..5e74e3b --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_nat.h
@@ -0,0 +1,13 @@ +#ifndef __LINUX_BRIDGE_EBT_NAT_H +#define __LINUX_BRIDGE_EBT_NAT_H + +#define NAT_ARP_BIT (0x00000010) +struct ebt_nat_info { + unsigned char mac[ETH_ALEN]; + /* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */ + int target; +}; +#define EBT_SNAT_TARGET "snat" +#define EBT_DNAT_TARGET "dnat" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_nflog.h b/include/linux/netfilter_bridge/ebt_nflog.h new file mode 100644 index 0000000..df829fc --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_nflog.h
@@ -0,0 +1,23 @@ +#ifndef __LINUX_BRIDGE_EBT_NFLOG_H +#define __LINUX_BRIDGE_EBT_NFLOG_H + +#include <linux/types.h> + +#define EBT_NFLOG_MASK 0x0 + +#define EBT_NFLOG_PREFIX_SIZE 64 +#define EBT_NFLOG_WATCHER "nflog" + +#define EBT_NFLOG_DEFAULT_GROUP 0x1 +#define EBT_NFLOG_DEFAULT_THRESHOLD 1 + +struct ebt_nflog_info { + __u32 len; + __u16 group; + __u16 threshold; + __u16 flags; + __u16 pad; + char prefix[EBT_NFLOG_PREFIX_SIZE]; +}; + +#endif /* __LINUX_BRIDGE_EBT_NFLOG_H */
diff --git a/include/linux/netfilter_bridge/ebt_pkttype.h b/include/linux/netfilter_bridge/ebt_pkttype.h new file mode 100644 index 0000000..c241bad --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_pkttype.h
@@ -0,0 +1,12 @@ +#ifndef __LINUX_BRIDGE_EBT_PKTTYPE_H +#define __LINUX_BRIDGE_EBT_PKTTYPE_H + +#include <linux/types.h> + +struct ebt_pkttype_info { + __u8 pkt_type; + __u8 invert; +}; +#define EBT_PKTTYPE_MATCH "pkttype" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_redirect.h b/include/linux/netfilter_bridge/ebt_redirect.h new file mode 100644 index 0000000..dd9622c --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_redirect.h
@@ -0,0 +1,10 @@ +#ifndef __LINUX_BRIDGE_EBT_REDIRECT_H +#define __LINUX_BRIDGE_EBT_REDIRECT_H + +struct ebt_redirect_info { + /* EBT_ACCEPT, EBT_DROP, EBT_CONTINUE or EBT_RETURN */ + int target; +}; +#define EBT_REDIRECT_TARGET "redirect" + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_stp.h b/include/linux/netfilter_bridge/ebt_stp.h new file mode 100644 index 0000000..1025b9f --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_stp.h
@@ -0,0 +1,46 @@ +#ifndef __LINUX_BRIDGE_EBT_STP_H +#define __LINUX_BRIDGE_EBT_STP_H + +#include <linux/types.h> + +#define EBT_STP_TYPE 0x0001 + +#define EBT_STP_FLAGS 0x0002 +#define EBT_STP_ROOTPRIO 0x0004 +#define EBT_STP_ROOTADDR 0x0008 +#define EBT_STP_ROOTCOST 0x0010 +#define EBT_STP_SENDERPRIO 0x0020 +#define EBT_STP_SENDERADDR 0x0040 +#define EBT_STP_PORT 0x0080 +#define EBT_STP_MSGAGE 0x0100 +#define EBT_STP_MAXAGE 0x0200 +#define EBT_STP_HELLOTIME 0x0400 +#define EBT_STP_FWDD 0x0800 + +#define EBT_STP_MASK 0x0fff +#define EBT_STP_CONFIG_MASK 0x0ffe + +#define EBT_STP_MATCH "stp" + +struct ebt_stp_config_info { + __u8 flags; + __u16 root_priol, root_priou; + char root_addr[6], root_addrmsk[6]; + __u32 root_costl, root_costu; + __u16 sender_priol, sender_priou; + char sender_addr[6], sender_addrmsk[6]; + __u16 portl, portu; + __u16 msg_agel, msg_ageu; + __u16 max_agel, max_ageu; + __u16 hello_timel, hello_timeu; + __u16 forward_delayl, forward_delayu; +}; + +struct ebt_stp_info { + __u8 type; + struct ebt_stp_config_info config; + __u16 bitmask; + __u16 invflags; +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebt_ulog.h b/include/linux/netfilter_bridge/ebt_ulog.h new file mode 100644 index 0000000..89a6bec --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_ulog.h
@@ -0,0 +1,38 @@ +#ifndef _EBT_ULOG_H +#define _EBT_ULOG_H + +#include <linux/types.h> + +#define EBT_ULOG_DEFAULT_NLGROUP 0 +#define EBT_ULOG_DEFAULT_QTHRESHOLD 1 +#define EBT_ULOG_MAXNLGROUPS 32 /* hardcoded netlink max */ +#define EBT_ULOG_PREFIX_LEN 32 +#define EBT_ULOG_MAX_QLEN 50 +#define EBT_ULOG_WATCHER "ulog" +#define EBT_ULOG_VERSION 1 + +struct ebt_ulog_info { + __u32 nlgroup; + unsigned int cprange; + unsigned int qthreshold; + char prefix[EBT_ULOG_PREFIX_LEN]; +}; + +typedef struct ebt_ulog_packet_msg { + int version; + char indev[IFNAMSIZ]; + char outdev[IFNAMSIZ]; + char physindev[IFNAMSIZ]; + char physoutdev[IFNAMSIZ]; + char prefix[EBT_ULOG_PREFIX_LEN]; + struct timeval stamp; + unsigned long mark; + unsigned int hook; + size_t data_len; + /* The complete packet, including Ethernet header and perhaps + * the VLAN header is appended */ + unsigned char data[0] __attribute__ + ((aligned (__alignof__(struct ebt_ulog_info)))); +} ebt_ulog_packet_msg_t; + +#endif /* _EBT_ULOG_H */
diff --git a/include/linux/netfilter_bridge/ebt_vlan.h b/include/linux/netfilter_bridge/ebt_vlan.h new file mode 100644 index 0000000..967d1d5 --- /dev/null +++ b/include/linux/netfilter_bridge/ebt_vlan.h
@@ -0,0 +1,22 @@ +#ifndef __LINUX_BRIDGE_EBT_VLAN_H +#define __LINUX_BRIDGE_EBT_VLAN_H + +#include <linux/types.h> + +#define EBT_VLAN_ID 0x01 +#define EBT_VLAN_PRIO 0x02 +#define EBT_VLAN_ENCAP 0x04 +#define EBT_VLAN_MASK (EBT_VLAN_ID | EBT_VLAN_PRIO | EBT_VLAN_ENCAP) +#define EBT_VLAN_MATCH "vlan" + +struct ebt_vlan_info { + __u16 id; /* VLAN ID {1-4095} */ + __u8 prio; /* VLAN User Priority {0-7} */ + __be16 encap; /* VLAN Encapsulated frame code {0-65535} */ + __u8 bitmask; /* Args bitmask bit 1=1 - ID arg, + bit 2=1 User-Priority arg, bit 3=1 encap*/ + __u8 invflags; /* Inverse bitmask bit 1=1 - inversed ID arg, + bit 2=1 - inversed Pirority arg */ +}; + +#endif
diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h new file mode 100644 index 0000000..5be75f2 --- /dev/null +++ b/include/linux/netfilter_bridge/ebtables.h
@@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * ebtables + * + * Authors: + * Bart De Schuymer <bdschuym@pandora.be> + * + * ebtables.c,v 2.0, April, 2002 + * + * This code is strongly inspired by the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + */ + +#ifndef __LINUX_BRIDGE_EFF_H +#define __LINUX_BRIDGE_EFF_H +#include <linux/types.h> +#include <linux/if.h> +#include <linux/netfilter_bridge.h> + +#define EBT_TABLE_MAXNAMELEN 32 +#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN +#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN +#define EBT_EXTENSION_MAXNAMELEN 31 + +/* verdicts >0 are "branches" */ +#define EBT_ACCEPT -1 +#define EBT_DROP -2 +#define EBT_CONTINUE -3 +#define EBT_RETURN -4 +#define NUM_STANDARD_TARGETS 4 +/* ebtables target modules store the verdict inside an int. We can + * reclaim a part of this int for backwards compatible extensions. + * The 4 lsb are more than enough to store the verdict. */ +#define EBT_VERDICT_BITS 0x0000000F + +struct xt_match; +struct xt_target; + +struct ebt_counter { + __u64 pcnt; + __u64 bcnt; +}; + +struct ebt_replace { + char name[EBT_TABLE_MAXNAMELEN]; + unsigned int valid_hooks; + /* nr of rules in the table */ + unsigned int nentries; + /* total size of the entries */ + unsigned int entries_size; + /* start of the chains */ + struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; + /* nr of counters userspace expects back */ + unsigned int num_counters; + /* where the kernel will put the old counters */ + struct ebt_counter *counters; + char *entries; +}; + +struct ebt_replace_kernel { + char name[EBT_TABLE_MAXNAMELEN]; + unsigned int valid_hooks; + /* nr of rules in the table */ + unsigned int nentries; + /* total size of the entries */ + unsigned int entries_size; + /* start of the chains */ + struct ebt_entries *hook_entry[NF_BR_NUMHOOKS]; + /* nr of counters userspace expects back */ + unsigned int num_counters; + /* where the kernel will put the old counters */ + struct ebt_counter *counters; + char *entries; +}; + +struct ebt_entries { + /* this field is always set to zero + * See EBT_ENTRY_OR_ENTRIES. + * Must be same size as ebt_entry.bitmask */ + unsigned int distinguisher; + /* the chain name */ + char name[EBT_CHAIN_MAXNAMELEN]; + /* counter offset for this chain */ + unsigned int counter_offset; + /* one standard (accept, drop, return) per hook */ + int policy; + /* nr. of entries */ + unsigned int nentries; + /* entry list */ + char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); +}; + +/* used for the bitmask of struct ebt_entry */ + +/* This is a hack to make a difference between an ebt_entry struct and an + * ebt_entries struct when traversing the entries from start to end. + * Using this simplifies the code a lot, while still being able to use + * ebt_entries. + * Contrary, iptables doesn't use something like ebt_entries and therefore uses + * different techniques for naming the policy and such. So, iptables doesn't + * need a hack like this. + */ +#define EBT_ENTRY_OR_ENTRIES 0x01 +/* these are the normal masks */ +#define EBT_NOPROTO 0x02 +#define EBT_802_3 0x04 +#define EBT_SOURCEMAC 0x08 +#define EBT_DESTMAC 0x10 +#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \ + | EBT_ENTRY_OR_ENTRIES) + +#define EBT_IPROTO 0x01 +#define EBT_IIN 0x02 +#define EBT_IOUT 0x04 +#define EBT_ISOURCE 0x8 +#define EBT_IDEST 0x10 +#define EBT_ILOGICALIN 0x20 +#define EBT_ILOGICALOUT 0x40 +#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \ + | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST) + +struct ebt_entry_match { + union { + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; + struct xt_match *match; + } u; + /* size of data */ + unsigned int match_size; + unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); +}; + +struct ebt_entry_watcher { + union { + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; + struct xt_target *watcher; + } u; + /* size of data */ + unsigned int watcher_size; + unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); +}; + +struct ebt_entry_target { + union { + struct { + char name[EBT_EXTENSION_MAXNAMELEN]; + uint8_t revision; + }; + struct xt_target *target; + } u; + /* size of data */ + unsigned int target_size; + unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); +}; + +#define EBT_STANDARD_TARGET "standard" +struct ebt_standard_target { + struct ebt_entry_target target; + int verdict; +}; + +/* one entry */ +struct ebt_entry { + /* this needs to be the first field */ + unsigned int bitmask; + unsigned int invflags; + __be16 ethproto; + /* the physical in-dev */ + char in[IFNAMSIZ]; + /* the logical in-dev */ + char logical_in[IFNAMSIZ]; + /* the physical out-dev */ + char out[IFNAMSIZ]; + /* the logical out-dev */ + char logical_out[IFNAMSIZ]; + unsigned char sourcemac[ETH_ALEN]; + unsigned char sourcemsk[ETH_ALEN]; + unsigned char destmac[ETH_ALEN]; + unsigned char destmsk[ETH_ALEN]; + /* sizeof ebt_entry + matches */ + unsigned int watchers_offset; + /* sizeof ebt_entry + matches + watchers */ + unsigned int target_offset; + /* sizeof ebt_entry + matches + watchers + target */ + unsigned int next_offset; + unsigned char elems[0] __attribute__ ((aligned (__alignof__(struct ebt_replace)))); +}; + +/* {g,s}etsockopt numbers */ +#define EBT_BASE_CTL 128 + +#define EBT_SO_SET_ENTRIES (EBT_BASE_CTL) +#define EBT_SO_SET_COUNTERS (EBT_SO_SET_ENTRIES+1) +#define EBT_SO_SET_MAX (EBT_SO_SET_COUNTERS+1) + +#define EBT_SO_GET_INFO (EBT_BASE_CTL) +#define EBT_SO_GET_ENTRIES (EBT_SO_GET_INFO+1) +#define EBT_SO_GET_INIT_INFO (EBT_SO_GET_ENTRIES+1) +#define EBT_SO_GET_INIT_ENTRIES (EBT_SO_GET_INIT_INFO+1) +#define EBT_SO_GET_MAX (EBT_SO_GET_INIT_ENTRIES+1) + + +/* blatently stolen from ip_tables.h + * fn returns 0 to continue iteration */ +#define EBT_MATCH_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry_match *__match; \ + \ + for (__i = sizeof(struct ebt_entry); \ + __i < (e)->watchers_offset; \ + __i += __match->match_size + \ + sizeof(struct ebt_entry_match)) { \ + __match = (void *)(e) + __i; \ + \ + __ret = fn(__match , ## args); \ + if (__ret != 0) \ + break; \ + } \ + if (__ret == 0) { \ + if (__i != (e)->watchers_offset) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#define EBT_WATCHER_ITERATE(e, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry_watcher *__watcher; \ + \ + for (__i = e->watchers_offset; \ + __i < (e)->target_offset; \ + __i += __watcher->watcher_size + \ + sizeof(struct ebt_entry_watcher)) { \ + __watcher = (void *)(e) + __i; \ + \ + __ret = fn(__watcher , ## args); \ + if (__ret != 0) \ + break; \ + } \ + if (__ret == 0) { \ + if (__i != (e)->target_offset) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#define EBT_ENTRY_ITERATE(entries, size, fn, args...) \ +({ \ + unsigned int __i; \ + int __ret = 0; \ + struct ebt_entry *__entry; \ + \ + for (__i = 0; __i < (size);) { \ + __entry = (void *)(entries) + __i; \ + __ret = fn(__entry , ## args); \ + if (__ret != 0) \ + break; \ + if (__entry->bitmask != 0) \ + __i += __entry->next_offset; \ + else \ + __i += sizeof(struct ebt_entries); \ + } \ + if (__ret == 0) { \ + if (__i != (size)) \ + __ret = -EINVAL; \ + } \ + __ret; \ +}) + +#endif /* __LINUX_BRIDGE_EFF_H */
diff --git a/include/linux/types.h b/include/linux/types.h new file mode 100644 index 0000000..23ea78f --- /dev/null +++ b/include/linux/types.h
@@ -0,0 +1,51 @@ +#ifndef _LINUX_TYPES_H +#define _LINUX_TYPES_H + +#include <asm/types.h> + +#ifndef __ASSEMBLY__ + +#include <linux/posix_types.h> + + +/* + * Below are truly Linux-specific types that should never collide with + * any application/library that wants linux/types.h. + */ + +#ifdef __CHECKER__ +#define __bitwise__ __attribute__((bitwise)) +#else +#define __bitwise__ +#endif +#ifdef __CHECK_ENDIAN__ +#define __bitwise __bitwise__ +#else +#define __bitwise +#endif + +typedef __u16 __bitwise __le16; +typedef __u16 __bitwise __be16; +typedef __u32 __bitwise __le32; +typedef __u32 __bitwise __be32; +typedef __u64 __bitwise __le64; +typedef __u64 __bitwise __be64; + +typedef __u16 __bitwise __sum16; +typedef __u32 __bitwise __wsum; + +/* + * aligned_u64 should be used in defining kernel<->userspace ABIs to avoid + * common 32/64-bit compat problems. + * 64-bit values align to 4-byte boundaries on x86_32 (and possibly other + * architectures) and to 8-byte boundaries on 64-bit architectures. The new + * aligned_64 type enforces 8-byte alignment so that structs containing + * aligned_64 values have the same alignment on 32-bit and 64-bit architectures. + * No conversions are necessary between 32-bit user-space and a 64-bit kernel. + */ +#define __aligned_u64 __u64 __attribute__((aligned(8))) +#define __aligned_be64 __be64 __attribute__((aligned(8))) +#define __aligned_le64 __le64 __attribute__((aligned(8))) + +#endif /* __ASSEMBLY__ */ +#endif /* _LINUX_TYPES_H */
diff --git a/libebtc.c b/libebtc.c new file mode 100644 index 0000000..112c307 --- /dev/null +++ b/libebtc.c
@@ -0,0 +1,1320 @@ +/* + * libebtc.c, January 2004 + * + * Contains the functions with which to make a table in userspace. + * + * Author: Bart De Schuymer + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" +#include <unistd.h> +#include <fcntl.h> +#include <sys/file.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <errno.h> +#include <libgen.h> + +static void decrease_chain_jumps(struct ebt_u_replace *replace); +static int iterate_entries(struct ebt_u_replace *replace, int type); + +/* The standard names */ +const char *ebt_hooknames[NF_BR_NUMHOOKS] = +{ + [NF_BR_PRE_ROUTING]"PREROUTING", + [NF_BR_LOCAL_IN]"INPUT", + [NF_BR_FORWARD]"FORWARD", + [NF_BR_LOCAL_OUT]"OUTPUT", + [NF_BR_POST_ROUTING]"POSTROUTING", + [NF_BR_BROUTING]"BROUTING" +}; + +/* The four target names */ +const char* ebt_standard_targets[NUM_STANDARD_TARGETS] = +{ + "ACCEPT", + "DROP", + "CONTINUE", + "RETURN", +}; + +/* The lists of supported tables, matches, watchers and targets */ +struct ebt_u_table *ebt_tables; +struct ebt_u_match *ebt_matches; +struct ebt_u_watcher *ebt_watchers; +struct ebt_u_target *ebt_targets; + +/* Find the right structure belonging to a name */ +struct ebt_u_target *ebt_find_target(const char *name) +{ + struct ebt_u_target *t = ebt_targets; + + while (t && strcmp(t->name, name)) + t = t->next; + return t; +} + +struct ebt_u_match *ebt_find_match(const char *name) +{ + struct ebt_u_match *m = ebt_matches; + + while (m && strcmp(m->name, name)) + m = m->next; + return m; +} + +struct ebt_u_watcher *ebt_find_watcher(const char *name) +{ + struct ebt_u_watcher *w = ebt_watchers; + + while (w && strcmp(w->name, name)) + w = w->next; + return w; +} + +struct ebt_u_table *ebt_find_table(const char *name) +{ + struct ebt_u_table *t = ebt_tables; + + while (t && strcmp(t->name, name)) + t = t->next; + return t; +} + +/* Prints all registered extensions */ +void ebt_list_extensions() +{ + struct ebt_u_table *tbl = ebt_tables; + struct ebt_u_target *t = ebt_targets; + struct ebt_u_match *m = ebt_matches; + struct ebt_u_watcher *w = ebt_watchers; + + PRINT_VERSION; + printf("Loaded userspace extensions:\n\nLoaded tables:\n"); + while (tbl) { + printf("%s\n", tbl->name); + tbl = tbl->next; + } + printf("\nLoaded targets:\n"); + while (t) { + printf("%s\n", t->name); + t = t->next; + } + printf("\nLoaded matches:\n"); + while (m) { + printf("%s\n", m->name); + m = m->next; + } + printf("\nLoaded watchers:\n"); + while (w) { + printf("%s\n", w->name); + w = w->next; + } +} + +int use_lockfd; +/* Returns 0 on success, -1 when the file is locked by another process + * or -2 on any other error. */ +static int lock_file() +{ + char pathbuf[] = LOCKFILE; + int fd, try = 0; + +retry: + fd = open(LOCKFILE, O_CREAT|O_WRONLY|O_CLOEXEC, 00600); + if (fd < 0) { + if (try == 1 || mkdir(dirname(pathbuf), 00700)) + return -2; + try = 1; + goto retry; + } + return flock(fd, LOCK_EX); +} + +/* Get the table from the kernel or from a binary file + * init: 1 = ask the kernel for the initial contents of a table, i.e. the + * way it looks when the table is insmod'ed + * 0 = get the current data in the table */ +int ebt_get_kernel_table(struct ebt_u_replace *replace, int init) +{ + int ret; + + if (!ebt_find_table(replace->name)) { + ebt_print_error("Bad table name '%s'", replace->name); + return -1; + } + while (use_lockfd && (ret = lock_file())) { + if (ret == -2) { + /* if we get an error we can't handle, we exit. This + * doesn't break backwards compatibility since using + * this file locking is disabled by default. */ + ebt_print_error2("Unable to create lock file "LOCKFILE); + } + fprintf(stderr, "Trying to obtain lock %s\n", LOCKFILE); + sleep(1); + } + /* Get the kernel's information */ + if (ebt_get_table(replace, init)) { + if (ebt_errormsg[0] != '\0') + return -1; + ebtables_insmod("ebtables"); + if (ebt_get_table(replace, init)) { + ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name); + return -1; + } + } + return 0; +} + +/* Put sane values into a new entry */ +void ebt_initialize_entry(struct ebt_u_entry *e) +{ + e->bitmask = EBT_NOPROTO; + e->invflags = 0; + e->ethproto = 0; + strcpy(e->in, ""); + strcpy(e->out, ""); + strcpy(e->logical_in, ""); + strcpy(e->logical_out, ""); + e->m_list = NULL; + e->w_list = NULL; + e->t = (struct ebt_entry_target *)ebt_find_target(EBT_STANDARD_TARGET); + ebt_find_target(EBT_STANDARD_TARGET)->used = 1; + e->cnt.pcnt = e->cnt.bcnt = e->cnt_surplus.pcnt = e->cnt_surplus.bcnt = 0; + + if (!e->t) + ebt_print_bug("Couldn't load standard target"); + ((struct ebt_standard_target *)((struct ebt_u_target *)e->t)->t)->verdict = EBT_CONTINUE; +} + +/* Free up the memory of the table held in userspace, *replace can be reused */ +void ebt_cleanup_replace(struct ebt_u_replace *replace) +{ + int i; + struct ebt_u_entries *entries; + struct ebt_cntchanges *cc1, *cc2; + struct ebt_u_entry *u_e1, *u_e2; + + replace->name[0] = '\0'; + replace->valid_hooks = 0; + replace->nentries = 0; + replace->num_counters = 0; + replace->flags = 0; + replace->command = 0; + replace->selected_chain = -1; + free(replace->filename); + replace->filename = NULL; + free(replace->counters); + replace->counters = NULL; + + for (i = 0; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + u_e1 = entries->entries->next; + while (u_e1 != entries->entries) { + ebt_free_u_entry(u_e1); + u_e2 = u_e1->next; + free(u_e1); + u_e1 = u_e2; + } + free(entries->entries); + free(entries); + replace->chains[i] = NULL; + } + cc1 = replace->cc->next; + while (cc1 != replace->cc) { + cc2 = cc1->next; + free(cc1); + cc1 = cc2; + } + replace->cc->next = replace->cc->prev = replace->cc; +} + +/* Should be called, e.g., between 2 rule adds */ +void ebt_reinit_extensions() +{ + struct ebt_u_match *m; + struct ebt_u_watcher *w; + struct ebt_u_target *t; + int size; + + /* The init functions should determine by themselves whether they are + * called for the first time or not (when necessary). */ + for (m = ebt_matches; m; m = m->next) { + if (m->used) { + size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); + m->m = (struct ebt_entry_match *)malloc(size); + if (!m->m) + ebt_print_memory(); + strcpy(m->m->u.name, m->name); + m->m->u.revision = m->revision; + m->m->match_size = EBT_ALIGN(m->size); + m->used = 0; + } + m->flags = 0; /* An error can occur before used is set, while flags is changed. */ + m->init(m->m); + } + for (w = ebt_watchers; w; w = w->next) { + if (w->used) { + size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); + w->w = (struct ebt_entry_watcher *)malloc(size); + if (!w->w) + ebt_print_memory(); + strcpy(w->w->u.name, w->name); + w->w->watcher_size = EBT_ALIGN(w->size); + w->used = 0; + } + w->flags = 0; + w->init(w->w); + } + for (t = ebt_targets; t; t = t->next) { + if (t->used) { + size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); + t->t = (struct ebt_entry_target *)malloc(size); + if (!t->t) + ebt_print_memory(); + strcpy(t->t->u.name, t->name); + t->t->target_size = EBT_ALIGN(t->size); + t->used = 0; + } + t->flags = 0; + t->init(t->t); + } +} + +/* This doesn't free e, because the calling function might need e->next */ +void ebt_free_u_entry(struct ebt_u_entry *e) +{ + struct ebt_u_match_list *m_l, *m_l2; + struct ebt_u_watcher_list *w_l, *w_l2; + + m_l = e->m_list; + while (m_l) { + m_l2 = m_l->next; + free(m_l->m); + free(m_l); + m_l = m_l2; + } + w_l = e->w_list; + while (w_l) { + w_l2 = w_l->next; + free(w_l->w); + free(w_l); + w_l = w_l2; + } + free(e->t); +} + +static char *get_modprobe(void) +{ + int procfile; + char *ret; + + procfile = open(PROC_SYS_MODPROBE, O_RDONLY); + if (procfile < 0) + return NULL; + + ret = malloc(1024); + if (ret) { + if (read(procfile, ret, 1024) == -1) + goto fail; + /* The kernel adds a '\n' */ + ret[1023] = '\n'; + *strchr(ret, '\n') = '\0'; + close(procfile); + return ret; + } + fail: + free(ret); + close(procfile); + return NULL; +} + +char *ebt_modprobe; +/* Try to load the kernel module, analogous to ip_tables.c */ +int ebtables_insmod(const char *modname) +{ + char *buf = NULL; + char *argv[3]; + + /* If they don't explicitly set it, read out of /proc */ + if (!ebt_modprobe) { + buf = get_modprobe(); + if (!buf) + return -1; + ebt_modprobe = buf; /* Keep the value for possible later use */ + } + + switch (fork()) { + case 0: + argv[0] = (char *)ebt_modprobe; + argv[1] = (char *)modname; + argv[2] = NULL; + execv(argv[0], argv); + + /* Not usually reached */ + exit(0); + case -1: + return -1; + + default: /* Parent */ + wait(NULL); + } + + return 0; +} + +/* Parse the chain name and return a pointer to the chain base. + * Returns NULL on failure. */ +struct ebt_u_entries *ebt_name_to_chain(const struct ebt_u_replace *replace, const char* arg) +{ + int i; + struct ebt_u_entries *chain; + + for (i = 0; i < replace->num_chains; i++) { + if (!(chain = replace->chains[i])) + continue; + if (!strcmp(arg, chain->name)) + return chain; + } + return NULL; +} + +/* Parse the chain name and return the corresponding chain nr + * returns -1 on failure */ +int ebt_get_chainnr(const struct ebt_u_replace *replace, const char* arg) +{ + int i; + + for (i = 0; i < replace->num_chains; i++) { + if (!replace->chains[i]) + continue; + if (!strcmp(arg, replace->chains[i]->name)) + return i; + } + return -1; +} + + /* +************ +************ +**COMMANDS** +************ +************ + */ + +/* Change the policy of selected_chain. + * Handing a bad policy to this function is a bug. */ +void ebt_change_policy(struct ebt_u_replace *replace, int policy) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (policy < -NUM_STANDARD_TARGETS || policy == EBT_CONTINUE) + ebt_print_bug("Wrong policy: %d", policy); + entries->policy = policy; +} + +void ebt_delete_cc(struct ebt_cntchanges *cc) +{ + if (cc->type == CNT_ADD) { + cc->prev->next = cc->next; + cc->next->prev = cc->prev; + free(cc); + } else + cc->type = CNT_DEL; +} + +void ebt_empty_chain(struct ebt_u_entries *entries) +{ + struct ebt_u_entry *u_e = entries->entries->next, *tmp; + while (u_e != entries->entries) { + ebt_delete_cc(u_e->cc); + ebt_free_u_entry(u_e); + tmp = u_e->next; + free(u_e); + u_e = tmp; + } + entries->entries->next = entries->entries->prev = entries->entries; + entries->nentries = 0; +} + +/* Flush one chain or the complete table + * If selected_chain == -1 then flush the complete table */ +void ebt_flush_chains(struct ebt_u_replace *replace) +{ + int i, numdel; + struct ebt_u_entries *entries = ebt_to_chain(replace); + + /* Flush whole table */ + if (!entries) { + if (replace->nentries == 0) + return; + replace->nentries = 0; + + /* Free everything and zero (n)entries */ + for (i = 0; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + entries->counter_offset = 0; + ebt_empty_chain(entries); + } + return; + } + + if (entries->nentries == 0) + return; + replace->nentries -= entries->nentries; + numdel = entries->nentries; + + /* Update counter_offset */ + for (i = replace->selected_chain+1; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + entries->counter_offset -= numdel; + } + + entries = ebt_to_chain(replace); + ebt_empty_chain(entries); +} + +#define OPT_COUNT 0x1000 /* This value is also defined in ebtables.c */ +/* Returns the rule number on success (starting from 0), -1 on failure + * + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target} */ +int ebt_check_rule_exists(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry) +{ + struct ebt_u_entry *u_e; + struct ebt_u_match_list *m_l, *m_l2; + struct ebt_u_match *m; + struct ebt_u_watcher_list *w_l, *w_l2; + struct ebt_u_watcher *w; + struct ebt_u_target *t = (struct ebt_u_target *)new_entry->t; + struct ebt_u_entries *entries = ebt_to_chain(replace); + int i, j, k; + + u_e = entries->entries->next; + /* Check for an existing rule (if there are duplicate rules, + * take the first occurance) */ + for (i = 0; i < entries->nentries; i++, u_e = u_e->next) { + if (u_e->ethproto != new_entry->ethproto) + continue; + if (strcmp(u_e->in, new_entry->in)) + continue; + if (strcmp(u_e->out, new_entry->out)) + continue; + if (strcmp(u_e->logical_in, new_entry->logical_in)) + continue; + if (strcmp(u_e->logical_out, new_entry->logical_out)) + continue; + if (new_entry->bitmask & EBT_SOURCEMAC && + memcmp(u_e->sourcemac, new_entry->sourcemac, ETH_ALEN)) + continue; + if (new_entry->bitmask & EBT_DESTMAC && + memcmp(u_e->destmac, new_entry->destmac, ETH_ALEN)) + continue; + if (new_entry->bitmask != u_e->bitmask || + new_entry->invflags != u_e->invflags) + continue; + if (replace->flags & OPT_COUNT && (new_entry->cnt.pcnt != + u_e->cnt.pcnt || new_entry->cnt.bcnt != u_e->cnt.bcnt)) + continue; + /* Compare all matches */ + m_l = new_entry->m_list; + j = 0; + while (m_l) { + m = (struct ebt_u_match *)(m_l->m); + m_l2 = u_e->m_list; + while (m_l2 && (strcmp(m_l2->m->u.name, m->m->u.name) || + m_l2->m->u.revision != m->m->u.revision)) { + m_l2 = m_l2->next; + } + if (!m_l2 || !m->compare(m->m, m_l2->m)) + goto letscontinue; + j++; + m_l = m_l->next; + } + /* Now be sure they have the same nr of matches */ + k = 0; + m_l = u_e->m_list; + while (m_l) { + k++; + m_l = m_l->next; + } + if (j != k) + continue; + + /* Compare all watchers */ + w_l = new_entry->w_list; + j = 0; + while (w_l) { + w = (struct ebt_u_watcher *)(w_l->w); + w_l2 = u_e->w_list; + while (w_l2 && strcmp(w_l2->w->u.name, w->w->u.name)) + w_l2 = w_l2->next; + if (!w_l2 || !w->compare(w->w, w_l2->w)) + goto letscontinue; + j++; + w_l = w_l->next; + } + k = 0; + w_l = u_e->w_list; + while (w_l) { + k++; + w_l = w_l->next; + } + if (j != k) + continue; + if (strcmp(t->t->u.name, u_e->t->u.name)) + continue; + if (!t->compare(t->t, u_e->t)) + continue; + return i; +letscontinue:; + } + return -1; +} + +/* Add a rule, rule_nr is the rule to update + * rule_nr specifies where the rule should be inserted + * rule_nr > 0 : insert the rule right before the rule_nr'th rule + * (the first rule is rule 1) + * rule_nr < 0 : insert the rule right before the (n+rule_nr+1)'th rule, + * where n denotes the number of rules in the chain + * rule_nr == 0: add a new rule at the end of the chain + * + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target} and updates these + * pointers so that they point to ebt_{match,watcher,target}, before adding + * the rule to the chain. Don't free() the ebt_{match,watcher,target} and + * don't reuse the new_entry after a successful call to ebt_add_rule() */ +void ebt_add_rule(struct ebt_u_replace *replace, struct ebt_u_entry *new_entry, int rule_nr) +{ + int i; + struct ebt_u_entry *u_e; + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_cntchanges *cc, *new_cc; + + if (rule_nr <= 0) + rule_nr += entries->nentries; + else + rule_nr--; + if (rule_nr > entries->nentries || rule_nr < 0) { + ebt_print_error("The specified rule number is incorrect"); + return; + } + /* Go to the right position in the chain */ + if (rule_nr == entries->nentries) + u_e = entries->entries; + else { + u_e = entries->entries->next; + for (i = 0; i < rule_nr; i++) + u_e = u_e->next; + } + /* We're adding one rule */ + replace->nentries++; + entries->nentries++; + /* Insert the rule */ + new_entry->next = u_e; + new_entry->prev = u_e->prev; + u_e->prev->next = new_entry; + u_e->prev = new_entry; + new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges)); + if (!new_cc) + ebt_print_memory(); + new_cc->type = CNT_ADD; + new_cc->change = 0; + if (new_entry->next == entries->entries) { + for (i = replace->selected_chain+1; i < replace->num_chains; i++) + if (!replace->chains[i] || replace->chains[i]->nentries == 0) + continue; + else + break; + if (i == replace->num_chains) + cc = replace->cc; + else + cc = replace->chains[i]->entries->next->cc; + } else + cc = new_entry->next->cc; + new_cc->next = cc; + new_cc->prev = cc->prev; + cc->prev->next = new_cc; + cc->prev = new_cc; + new_entry->cc = new_cc; + + /* Put the ebt_{match, watcher, target} pointers in place */ + m_l = new_entry->m_list; + while (m_l) { + m_l->m = ((struct ebt_u_match *)m_l->m)->m; + m_l = m_l->next; + } + w_l = new_entry->w_list; + while (w_l) { + w_l->w = ((struct ebt_u_watcher *)w_l->w)->w; + w_l = w_l->next; + } + new_entry->t = ((struct ebt_u_target *)new_entry->t)->t; + /* Update the counter_offset of chains behind this one */ + for (i = replace->selected_chain+1; i < replace->num_chains; i++) { + entries = replace->chains[i]; + if (!(entries = replace->chains[i])) + continue; + entries->counter_offset++; + } +} + +/* If *begin==*end==0 then find the rule corresponding to new_entry, + * else make the rule numbers positive (starting from 0) and check + * for bad rule numbers. */ +static int check_and_change_rule_number(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int *begin, int *end) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (*begin < 0) + *begin += entries->nentries + 1; + if (*end < 0) + *end += entries->nentries + 1; + + if (*begin < 0 || *begin > *end || *end > entries->nentries) { + ebt_print_error("Sorry, wrong rule numbers"); + return -1; + } + + if ((*begin * *end == 0) && (*begin + *end != 0)) + ebt_print_bug("begin and end should be either both zero, " + "either both non-zero"); + if (*begin != 0) { + (*begin)--; + (*end)--; + } else { + *begin = ebt_check_rule_exists(replace, new_entry); + *end = *begin; + if (*begin == -1) { + ebt_print_error("Sorry, rule does not exist"); + return -1; + } + } + return 0; +} + +/* Delete a rule or rules + * begin == end == 0: delete the rule corresponding to new_entry + * + * The first rule has rule nr 1, the last rule has rule nr -1, etc. + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target}. */ +void ebt_delete_rule(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int begin, int end) +{ + int i, nr_deletes; + struct ebt_u_entry *u_e, *u_e2, *u_e3; + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (check_and_change_rule_number(replace, new_entry, &begin, &end)) + return; + /* We're deleting rules */ + nr_deletes = end - begin + 1; + replace->nentries -= nr_deletes; + entries->nentries -= nr_deletes; + /* Go to the right position in the chain */ + u_e = entries->entries->next; + for (i = 0; i < begin; i++) + u_e = u_e->next; + u_e3 = u_e->prev; + /* Remove the rules */ + for (i = 0; i < nr_deletes; i++) { + u_e2 = u_e; + ebt_delete_cc(u_e2->cc); + u_e = u_e->next; + /* Free everything */ + ebt_free_u_entry(u_e2); + free(u_e2); + } + u_e3->next = u_e; + u_e->prev = u_e3; + /* Update the counter_offset of chains behind this one */ + for (i = replace->selected_chain+1; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + entries->counter_offset -= nr_deletes; + } +} + +/* Change the counters of a rule or rules + * begin == end == 0: change counters of the rule corresponding to new_entry + * + * The first rule has rule nr 1, the last rule has rule nr -1, etc. + * This function expects the ebt_{match,watcher,target} members of new_entry + * to contain pointers to ebt_u_{match,watcher,target}. + * The mask denotes the following: + * pcnt: mask % 3 = 0 : change; = 1: increment; = 2: decrement + * bcnt: mask / 3 = 0 : change; = 1: increment = 2: increment + * In daemon mode, mask==0 must hold */ +void ebt_change_counters(struct ebt_u_replace *replace, + struct ebt_u_entry *new_entry, int begin, int end, + struct ebt_counter *cnt, int mask) +{ + int i; + struct ebt_u_entry *u_e; + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (check_and_change_rule_number(replace, new_entry, &begin, &end)) + return; + u_e = entries->entries->next; + for (i = 0; i < begin; i++) + u_e = u_e->next; + for (i = end-begin+1; i > 0; i--) { + if (mask % 3 == 0) { + u_e->cnt.pcnt = (*cnt).pcnt; + u_e->cnt_surplus.pcnt = 0; + } else { +#ifdef EBT_DEBUG + if (u_e->cc->type != CNT_NORM) + ebt_print_bug("cc->type != CNT_NORM"); +#endif + u_e->cnt_surplus.pcnt = (*cnt).pcnt; + } + + if (mask / 3 == 0) { + u_e->cnt.bcnt = (*cnt).bcnt; + u_e->cnt_surplus.bcnt = 0; + } else { +#ifdef EBT_DEBUG + if (u_e->cc->type != CNT_NORM) + ebt_print_bug("cc->type != CNT_NORM"); +#endif + u_e->cnt_surplus.bcnt = (*cnt).bcnt; + } + if (u_e->cc->type != CNT_ADD) + u_e->cc->type = CNT_CHANGE; + u_e->cc->change = mask; + u_e = u_e->next; + } +} + +/* If selected_chain == -1 then zero all counters, + * otherwise, zero the counters of selected_chain */ +void ebt_zero_counters(struct ebt_u_replace *replace) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + struct ebt_u_entry *next; + int i; + + if (!entries) { + for (i = 0; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + next = entries->entries->next; + while (next != entries->entries) { + if (next->cc->type == CNT_NORM) + next->cc->type = CNT_CHANGE; + next->cnt.bcnt = next->cnt.pcnt = 0; + next->cc->change = 0; + next = next->next; + } + } + } else { + if (entries->nentries == 0) + return; + + next = entries->entries->next; + while (next != entries->entries) { + if (next->cc->type == CNT_NORM) + next->cc->type = CNT_CHANGE; + next->cnt.bcnt = next->cnt.pcnt = 0; + next = next->next; + } + } +} + +/* Add a new chain and specify its policy */ +void ebt_new_chain(struct ebt_u_replace *replace, const char *name, int policy) +{ + struct ebt_u_entries *new; + + if (replace->num_chains == replace->max_chains) + ebt_double_chains(replace); + new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries)); + if (!new) + ebt_print_memory(); + replace->chains[replace->num_chains++] = new; + new->nentries = 0; + new->policy = policy; + new->counter_offset = replace->nentries; + new->hook_mask = 0; + strcpy(new->name, name); + new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry)); + if (!new->entries) + ebt_print_memory(); + new->entries->next = new->entries->prev = new->entries; + new->kernel_start = NULL; +} + +/* returns -1 if the chain is referenced, 0 on success */ +static int ebt_delete_a_chain(struct ebt_u_replace *replace, int chain, int print_err) +{ + int tmp = replace->selected_chain; + /* If the chain is referenced, don't delete it, + * also decrement jumps to a chain behind the + * one we're deleting */ + replace->selected_chain = chain; + if (ebt_check_for_references(replace, print_err)) + return -1; + decrease_chain_jumps(replace); + ebt_flush_chains(replace); + replace->selected_chain = tmp; + free(replace->chains[chain]->entries); + free(replace->chains[chain]); + memmove(replace->chains+chain, replace->chains+chain+1, (replace->num_chains-chain-1)*sizeof(void *)); + replace->num_chains--; + return 0; +} + +/* Selected_chain == -1: delete all non-referenced udc + * selected_chain < NF_BR_NUMHOOKS is illegal */ +void ebt_delete_chain(struct ebt_u_replace *replace) +{ + if (replace->selected_chain != -1 && replace->selected_chain < NF_BR_NUMHOOKS) + ebt_print_bug("You can't remove a standard chain"); + if (replace->selected_chain == -1) { + int i = NF_BR_NUMHOOKS; + + while (i < replace->num_chains) + if (ebt_delete_a_chain(replace, i, 0)) + i++; + } else + ebt_delete_a_chain(replace, replace->selected_chain, 1); +} + +/* Rename an existing chain. */ +void ebt_rename_chain(struct ebt_u_replace *replace, const char *name) +{ + struct ebt_u_entries *entries = ebt_to_chain(replace); + + if (!entries) + ebt_print_bug("ebt_rename_chain: entries == NULL"); + strcpy(entries->name, name); +} + + + /* +************************* +************************* +**SPECIALIZED*FUNCTIONS** +************************* +************************* + */ + + +void ebt_double_chains(struct ebt_u_replace *replace) +{ + struct ebt_u_entries **new; + + replace->max_chains *= 2; + new = (struct ebt_u_entries **)malloc(replace->max_chains*sizeof(void *)); + if (!new) + ebt_print_memory(); + memcpy(new, replace->chains, replace->max_chains/2*sizeof(void *)); + free(replace->chains); + replace->chains = new; +} + +/* Executes the final_check() function for all extensions used by the rule + * ebt_check_for_loops should have been executed earlier, to make sure the + * hook_mask is correct. The time argument to final_check() is set to 1, + * meaning it's the second time the final_check() function is executed. */ +void ebt_do_final_checks(struct ebt_u_replace *replace, struct ebt_u_entry *e, + struct ebt_u_entries *entries) +{ + struct ebt_u_match_list *m_l; + struct ebt_u_watcher_list *w_l; + struct ebt_u_target *t; + struct ebt_u_match *m; + struct ebt_u_watcher *w; + + m_l = e->m_list; + w_l = e->w_list; + while (m_l) { + m = ebt_find_match(m_l->m->u.name); + m->final_check(e, m_l->m, replace->name, + entries->hook_mask, 1); + if (ebt_errormsg[0] != '\0') + return; + m_l = m_l->next; + } + while (w_l) { + w = ebt_find_watcher(w_l->w->u.name); + w->final_check(e, w_l->w, replace->name, + entries->hook_mask, 1); + if (ebt_errormsg[0] != '\0') + return; + w_l = w_l->next; + } + t = ebt_find_target(e->t->u.name); + t->final_check(e, e->t, replace->name, + entries->hook_mask, 1); +} + +/* Returns 1 (if it returns) when the chain is referenced, 0 when it isn't. + * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ +int ebt_check_for_references(struct ebt_u_replace *replace, int print_err) +{ + if (print_err) + return iterate_entries(replace, 1); + else + return iterate_entries(replace, 2); +} + +/* chain_nr: nr of the udc (>= NF_BR_NUMHOOKS) + * Returns 1 (if it returns) when the chain is referenced, 0 when it isn't. + * print_err: 0 (resp. 1) = don't (resp. do) print error when referenced */ +int ebt_check_for_references2(struct ebt_u_replace *replace, int chain_nr, + int print_err) +{ + int tmp = replace->selected_chain, ret; + + replace->selected_chain = chain_nr; + if (print_err) + ret = iterate_entries(replace, 1); + else + ret = iterate_entries(replace, 2); + replace->selected_chain = tmp; + return ret; +} + +struct ebt_u_stack +{ + int chain_nr; + int n; + struct ebt_u_entry *e; + struct ebt_u_entries *entries; +}; + +/* Checks for loops + * As a by-product, the hook_mask member of each chain is filled in + * correctly. The check functions of the extensions need this hook_mask + * to know from which standard chains they can be called. */ +void ebt_check_for_loops(struct ebt_u_replace *replace) +{ + int chain_nr , i, j , k, sp = 0, verdict; + struct ebt_u_entries *entries, *entries2; + struct ebt_u_stack *stack = NULL; + struct ebt_u_entry *e; + + /* Initialize hook_mask to 0 */ + for (i = 0; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + if (i < NF_BR_NUMHOOKS) + /* (1 << NF_BR_NUMHOOKS) implies it's a standard chain + * (usefull in the final_check() funtions) */ + entries->hook_mask = (1 << i) | (1 << NF_BR_NUMHOOKS); + else + entries->hook_mask = 0; + } + if (replace->num_chains == NF_BR_NUMHOOKS) + return; + stack = (struct ebt_u_stack *)malloc((replace->num_chains - NF_BR_NUMHOOKS) * sizeof(struct ebt_u_stack)); + if (!stack) + ebt_print_memory(); + + /* Check for loops, starting from every base chain */ + for (i = 0; i < NF_BR_NUMHOOKS; i++) { + if (!(entries = replace->chains[i])) + continue; + chain_nr = i; + + e = entries->entries->next; + for (j = 0; j < entries->nentries; j++) { + if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) + goto letscontinue; + verdict = ((struct ebt_standard_target *)(e->t))->verdict; + if (verdict < 0) + goto letscontinue; + /* Now see if we've been here before */ + for (k = 0; k < sp; k++) + if (stack[k].chain_nr == verdict + NF_BR_NUMHOOKS) { + ebt_print_error("Loop from chain '%s' to chain '%s'", + replace->chains[chain_nr]->name, + replace->chains[stack[k].chain_nr]->name); + goto free_stack; + } + entries2 = replace->chains[verdict + NF_BR_NUMHOOKS]; + /* check if we've dealt with this chain already */ + if (entries2->hook_mask & (1<<i)) + goto letscontinue; + entries2->hook_mask |= entries->hook_mask & ~(1 << NF_BR_NUMHOOKS); + /* Jump to the chain, make sure we know how to get back */ + stack[sp].chain_nr = chain_nr; + stack[sp].n = j; + stack[sp].entries = entries; + stack[sp].e = e; + sp++; + j = -1; + e = entries2->entries->next; + chain_nr = verdict + NF_BR_NUMHOOKS; + entries = entries2; + continue; +letscontinue: + e = e->next; + } + /* We are at the end of a standard chain */ + if (sp == 0) + continue; + /* Go back to the chain one level higher */ + sp--; + j = stack[sp].n; + chain_nr = stack[sp].chain_nr; + e = stack[sp].e; + entries = stack[sp].entries; + goto letscontinue; + } +free_stack: + free(stack); + return; +} + +/* The user will use the match, so put it in new_entry. The ebt_u_match + * pointer is put in the ebt_entry_match pointer. ebt_add_rule will + * fill in the final value for new->m. Unless the rule is added to a chain, + * the pointer will keep pointing to the ebt_u_match (until the new_entry + * is freed). I know, I should use a union for these 2 pointer types... */ +void ebt_add_match(struct ebt_u_entry *new_entry, struct ebt_u_match *m) +{ + struct ebt_u_match_list **m_list, *new; + + for (m_list = &new_entry->m_list; *m_list; m_list = &(*m_list)->next); + new = (struct ebt_u_match_list *) + malloc(sizeof(struct ebt_u_match_list)); + if (!new) + ebt_print_memory(); + *m_list = new; + new->next = NULL; + new->m = (struct ebt_entry_match *)m; +} + +void ebt_add_watcher(struct ebt_u_entry *new_entry, struct ebt_u_watcher *w) +{ + struct ebt_u_watcher_list **w_list; + struct ebt_u_watcher_list *new; + + for (w_list = &new_entry->w_list; *w_list; w_list = &(*w_list)->next); + new = (struct ebt_u_watcher_list *) + malloc(sizeof(struct ebt_u_watcher_list)); + if (!new) + ebt_print_memory(); + *w_list = new; + new->next = NULL; + new->w = (struct ebt_entry_watcher *)w; +} + + + /* +******************* +******************* +**OTHER*FUNCTIONS** +******************* +******************* + */ + + +/* type = 0 => update chain jumps + * type = 1 => check for reference, print error when referenced + * type = 2 => check for reference, don't print error when referenced + * + * Returns 1 when type == 1 and the chain is referenced + * returns 0 otherwise */ +static int iterate_entries(struct ebt_u_replace *replace, int type) +{ + int i, j, chain_nr = replace->selected_chain - NF_BR_NUMHOOKS; + struct ebt_u_entries *entries; + struct ebt_u_entry *e; + + if (chain_nr < 0) + ebt_print_bug("iterate_entries: udc = %d < 0", chain_nr); + for (i = 0; i < replace->num_chains; i++) { + if (!(entries = replace->chains[i])) + continue; + e = entries->entries->next; + for (j = 0; j < entries->nentries; j++) { + int chain_jmp; + + if (strcmp(e->t->u.name, EBT_STANDARD_TARGET)) { + e = e->next; + continue; + } + chain_jmp = ((struct ebt_standard_target *)e->t)-> + verdict; + switch (type) { + case 1: + case 2: + if (chain_jmp == chain_nr) { + if (type == 2) + return 1; + ebt_print_error("Can't delete the chain '%s', it's referenced in chain '%s', rule %d", + replace->chains[chain_nr + NF_BR_NUMHOOKS]->name, entries->name, j); + return 1; + } + break; + case 0: + /* Adjust the chain jumps when necessary */ + if (chain_jmp > chain_nr) + ((struct ebt_standard_target *)e->t)->verdict--; + break; + } /* End switch */ + e = e->next; + } + } + return 0; +} + +static void decrease_chain_jumps(struct ebt_u_replace *replace) +{ + iterate_entries(replace, 0); +} + +/* Used in initialization code of modules */ +void ebt_register_match(struct ebt_u_match *m) +{ + int size = EBT_ALIGN(m->size) + sizeof(struct ebt_entry_match); + struct ebt_u_match **i; + + m->m = (struct ebt_entry_match *)malloc(size); + if (!m->m) + ebt_print_memory(); + strcpy(m->m->u.name, m->name); + m->m->u.revision = m->revision; + m->m->match_size = EBT_ALIGN(m->size); + m->init(m->m); + + for (i = &ebt_matches; *i; i = &((*i)->next)); + m->next = NULL; + *i = m; +} + +void ebt_register_watcher(struct ebt_u_watcher *w) +{ + int size = EBT_ALIGN(w->size) + sizeof(struct ebt_entry_watcher); + struct ebt_u_watcher **i; + + w->w = (struct ebt_entry_watcher *)malloc(size); + if (!w->w) + ebt_print_memory(); + strcpy(w->w->u.name, w->name); + w->w->watcher_size = EBT_ALIGN(w->size); + w->init(w->w); + + for (i = &ebt_watchers; *i; i = &((*i)->next)); + w->next = NULL; + *i = w; +} + +void ebt_register_target(struct ebt_u_target *t) +{ + int size = EBT_ALIGN(t->size) + sizeof(struct ebt_entry_target); + struct ebt_u_target **i; + + t->t = (struct ebt_entry_target *)malloc(size); + if (!t->t) + ebt_print_memory(); + strcpy(t->t->u.name, t->name); + t->t->target_size = EBT_ALIGN(t->size); + t->init(t->t); + + for (i = &ebt_targets; *i; i = &((*i)->next)); + t->next = NULL; + *i = t; +} + +void ebt_register_table(struct ebt_u_table *t) +{ + t->next = ebt_tables; + ebt_tables = t; +} + +void ebt_iterate_matches(void (*f)(struct ebt_u_match *)) +{ + struct ebt_u_match *i; + + for (i = ebt_matches; i; i = i->next) + f(i); +} + +void ebt_iterate_watchers(void (*f)(struct ebt_u_watcher *)) +{ + struct ebt_u_watcher *i; + + for (i = ebt_watchers; i; i = i->next) + f(i); +} + +void ebt_iterate_targets(void (*f)(struct ebt_u_target *)) +{ + struct ebt_u_target *i; + + for (i = ebt_targets; i; i = i->next) + f(i); +} + +/* Don't use this function, use ebt_print_bug() */ +void __ebt_print_bug(char *file, int line, char *format, ...) +{ + va_list l; + + va_start(l, format); + fprintf(stderr, PROGNAME" v"PROGVERSION":%s:%d:--BUG--: \n", file, line); + vfprintf(stderr, format, l); + fprintf(stderr, "\n"); + va_end(l); + exit (-1); +} + +/* The error messages are put in here when ebt_silent == 1 + * ebt_errormsg[0] == '\0' implies there was no error */ +char ebt_errormsg[ERRORMSG_MAXLEN]; +/* When error messages should not be printed on the screen, after which + * the program exit()s, set ebt_silent to 1. */ +int ebt_silent; +/* Don't use this function, use ebt_print_error() */ +void __ebt_print_error(char *format, ...) +{ + va_list l; + + va_start(l, format); + if (ebt_silent && ebt_errormsg[0] == '\0') { + vsnprintf(ebt_errormsg, ERRORMSG_MAXLEN, format, l); + va_end(l); + } else { + vfprintf(stderr, format, l); + fprintf(stderr, ".\n"); + va_end(l); + exit (-1); + } +}
diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..64d9bbc --- /dev/null +++ b/m4/.gitignore
@@ -0,0 +1,2 @@ +/libtool.m4 +/lt*.m4
diff --git a/useful_functions.c b/useful_functions.c new file mode 100644 index 0000000..133ae2f --- /dev/null +++ b/useful_functions.c
@@ -0,0 +1,607 @@ +/* + * useful_functions.c, January 2004 + * + * Random collection of functions that can be used by extensions. + * + * Author: Bart De Schuymer + * + * This code is stongly inspired on the iptables code which is + * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "include/ebtables_u.h" +#include "include/ethernetdb.h" +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stdio.h> +#include <netinet/ether.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + + +const unsigned char mac_type_unicast[ETH_ALEN] = {0,0,0,0,0,0}; +const unsigned char msk_type_unicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char mac_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char msk_type_multicast[ETH_ALEN] = {1,0,0,0,0,0}; +const unsigned char mac_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; +const unsigned char msk_type_broadcast[ETH_ALEN] = {255,255,255,255,255,255}; +const unsigned char mac_type_bridge_group[ETH_ALEN] = {0x01,0x80,0xc2,0,0,0}; +const unsigned char msk_type_bridge_group[ETH_ALEN] = {255,255,255,255,255,255}; + +/* 0: default, print only 2 digits if necessary + * 2: always print 2 digits, a printed mac address + * then always has the same length */ +int ebt_printstyle_mac; + +void ebt_print_mac(const unsigned char *mac) +{ + if (ebt_printstyle_mac == 2) { + int j; + for (j = 0; j < ETH_ALEN; j++) + printf("%02x%s", mac[j], + (j==ETH_ALEN-1) ? "" : ":"); + } else + printf("%s", ether_ntoa((struct ether_addr *) mac)); +} + +void ebt_print_mac_and_mask(const unsigned char *mac, const unsigned char *mask) +{ + char hlpmsk[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + if (!memcmp(mac, mac_type_unicast, 6) && + !memcmp(mask, msk_type_unicast, 6)) + printf("Unicast"); + else if (!memcmp(mac, mac_type_multicast, 6) && + !memcmp(mask, msk_type_multicast, 6)) + printf("Multicast"); + else if (!memcmp(mac, mac_type_broadcast, 6) && + !memcmp(mask, msk_type_broadcast, 6)) + printf("Broadcast"); + else if (!memcmp(mac, mac_type_bridge_group, 6) && + !memcmp(mask, msk_type_bridge_group, 6)) + printf("BGA"); + else { + ebt_print_mac(mac); + if (memcmp(mask, hlpmsk, 6)) { + printf("/"); + ebt_print_mac(mask); + } + } +} + +/* Checks the type for validity and calls getethertypebynumber(). */ +struct ethertypeent *parseethertypebynumber(int type) +{ + if (type < 1536) + ebt_print_error("Ethernet protocols have values >= 0x0600"); + if (type > 0xffff) + ebt_print_error("Ethernet protocols have values <= 0xffff"); + return getethertypebynumber(type); +} + +/* Put the mac address into 6 (ETH_ALEN) bytes returns 0 on success. */ +int ebt_get_mac_and_mask(const char *from, unsigned char *to, + unsigned char *mask) +{ + char *p; + int i; + struct ether_addr *addr; + + if (strcasecmp(from, "Unicast") == 0) { + memcpy(to, mac_type_unicast, ETH_ALEN); + memcpy(mask, msk_type_unicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Multicast") == 0) { + memcpy(to, mac_type_multicast, ETH_ALEN); + memcpy(mask, msk_type_multicast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "Broadcast") == 0) { + memcpy(to, mac_type_broadcast, ETH_ALEN); + memcpy(mask, msk_type_broadcast, ETH_ALEN); + return 0; + } + if (strcasecmp(from, "BGA") == 0) { + memcpy(to, mac_type_bridge_group, ETH_ALEN); + memcpy(mask, msk_type_bridge_group, ETH_ALEN); + return 0; + } + if ( (p = strrchr(from, '/')) != NULL) { + *p = '\0'; + if (!(addr = ether_aton(p + 1))) + return -1; + memcpy(mask, addr, ETH_ALEN); + } else + memset(mask, 0xff, ETH_ALEN); + if (!(addr = ether_aton(from))) + return -1; + memcpy(to, addr, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + to[i] &= mask[i]; + return 0; +} + +/* 0: default + * 1: the inverse '!' of the option has already been specified */ +int ebt_invert = 0; + +/* + * Check if the inverse of the option is specified. This is used + * in the parse functions of the extensions and ebtables.c + */ +int _ebt_check_inverse(const char option[], int argc, char **argv) +{ + if (!option) + return ebt_invert; + if (strcmp(option, "!") == 0) { + if (ebt_invert == 1) + ebt_print_error("Double use of '!' not allowed"); + if (optind >= argc) + optarg = NULL; + else + optarg = argv[optind]; + optind++; + ebt_invert = 1; + return 1; + } + return ebt_invert; +} + +/* Make sure the same option wasn't specified twice. This is used + * in the parse functions of the extensions and ebtables.c */ +void ebt_check_option(unsigned int *flags, unsigned int mask) +{ + if (*flags & mask) + ebt_print_error("Multiple use of same option not allowed"); + *flags |= mask; +} + +/* Put the ip string into 4 bytes. */ +static int undot_ip(char *ip, unsigned char *ip2) +{ + char *p, *q, *end; + long int onebyte; + int i; + char buf[20]; + + strncpy(buf, ip, sizeof(buf) - 1); + + p = buf; + for (i = 0; i < 3; i++) { + if ((q = strchr(p, '.')) == NULL) + return -1; + *q = '\0'; + onebyte = strtol(p, &end, 10); + if (*end != '\0' || onebyte > 255 || onebyte < 0) + return -1; + ip2[i] = (unsigned char)onebyte; + p = q + 1; + } + + onebyte = strtol(p, &end, 10); + if (*end != '\0' || onebyte > 255 || onebyte < 0) + return -1; + ip2[3] = (unsigned char)onebyte; + + return 0; +} + +/* Put the mask into 4 bytes. */ +static int ip_mask(char *mask, unsigned char *mask2) +{ + char *end; + long int bits; + uint32_t mask22; + + if (undot_ip(mask, mask2)) { + /* not the /a.b.c.e format, maybe the /x format */ + bits = strtol(mask, &end, 10); + if (*end != '\0' || bits > 32 || bits < 0) + return -1; + if (bits != 0) { + mask22 = htonl(0xFFFFFFFF << (32 - bits)); + memcpy(mask2, &mask22, 4); + } else { + mask22 = 0xFFFFFFFF; + memcpy(mask2, &mask22, 4); + } + } + return 0; +} + +/* Set the ip mask and ip address. Callers should check ebt_errormsg[0]. + * The string pointed to by address can be altered. */ +void ebt_parse_ip_address(char *address, uint32_t *addr, uint32_t *msk) +{ + char *p; + + /* first the mask */ + if ((p = strrchr(address, '/')) != NULL) { + *p = '\0'; + if (ip_mask(p + 1, (unsigned char *)msk)) { + ebt_print_error("Problem with the IP mask '%s'", p + 1); + return; + } + } else + *msk = 0xFFFFFFFF; + + if (undot_ip(address, (unsigned char *)addr)) { + ebt_print_error("Problem with the IP address '%s'", address); + return; + } + *addr = *addr & *msk; +} + + +/* Transform the ip mask into a string ready for output. */ +char *ebt_mask_to_dotted(uint32_t mask) +{ + int i; + static char buf[20]; + uint32_t maskaddr, bits; + + maskaddr = ntohl(mask); + + /* don't print /32 */ + if (mask == 0xFFFFFFFFL) { + *buf = '\0'; + return buf; + } + + i = 32; + bits = 0xFFFFFFFEL; /* Case 0xFFFFFFFF has just been dealt with */ + while (--i >= 0 && maskaddr != bits) + bits <<= 1; + + if (i > 0) + sprintf(buf, "/%d", i); + else if (!i) + *buf = '\0'; + else + /* Mask was not a decent combination of 1's and 0's */ + sprintf(buf, "/%d.%d.%d.%d", ((unsigned char *)&mask)[0], + ((unsigned char *)&mask)[1], ((unsigned char *)&mask)[2], + ((unsigned char *)&mask)[3]); + + return buf; +} + +/* Most of the following code is derived from iptables */ +static void +in6addrcpy(struct in6_addr *dst, struct in6_addr *src) +{ + memcpy(dst, src, sizeof(struct in6_addr)); +} + +int string_to_number_ll(const char *s, unsigned long long min, + unsigned long long max, unsigned long long *ret) +{ + unsigned long long number; + char *end; + + /* Handle hex, octal, etc. */ + errno = 0; + number = strtoull(s, &end, 0); + if (*end == '\0' && end != s) { + /* we parsed a number, let's see if we want this */ + if (errno != ERANGE && min <= number && (!max || number <= max)) { + *ret = number; + return 0; + } + } + return -1; +} + +int string_to_number_l(const char *s, unsigned long min, unsigned long max, + unsigned long *ret) +{ + int result; + unsigned long long number; + + result = string_to_number_ll(s, min, max, &number); + *ret = (unsigned long)number; + + return result; +} + +int string_to_number(const char *s, unsigned int min, unsigned int max, + unsigned int *ret) +{ + int result; + unsigned long number; + + result = string_to_number_l(s, min, max, &number); + *ret = (unsigned int)number; + + return result; +} + +static struct in6_addr *numeric_to_addr(const char *num) +{ + static struct in6_addr ap; + int err; + + if ((err=inet_pton(AF_INET6, num, &ap)) == 1) + return ≈ + return (struct in6_addr *)NULL; +} + +static struct in6_addr *parse_ip6_mask(char *mask) +{ + static struct in6_addr maskaddr; + struct in6_addr *addrp; + unsigned int bits; + + if (mask == NULL) { + /* no mask at all defaults to 128 bits */ + memset(&maskaddr, 0xff, sizeof maskaddr); + return &maskaddr; + } + if ((addrp = numeric_to_addr(mask)) != NULL) + return addrp; + if (string_to_number(mask, 0, 128, &bits) == -1) + ebt_print_error("Invalid IPv6 Mask '%s' specified", mask); + if (bits != 0) { + char *p = (char *)&maskaddr; + memset(p, 0xff, bits / 8); + memset(p + (bits / 8) + 1, 0, (128 - bits) / 8); + p[bits / 8] = 0xff << (8 - (bits & 7)); + return &maskaddr; + } + + memset(&maskaddr, 0, sizeof maskaddr); + return &maskaddr; +} + +/* Set the ipv6 mask and address. Callers should check ebt_errormsg[0]. + * The string pointed to by address can be altered. */ +void ebt_parse_ip6_address(char *address, struct in6_addr *addr, + struct in6_addr *msk) +{ + struct in6_addr *tmp_addr; + char buf[256]; + char *p; + int i; + int err; + + strncpy(buf, address, sizeof(buf) - 1); + /* first the mask */ + buf[sizeof(buf) - 1] = '\0'; + if ((p = strrchr(buf, '/')) != NULL) { + *p = '\0'; + tmp_addr = parse_ip6_mask(p + 1); + } else + tmp_addr = parse_ip6_mask(NULL); + in6addrcpy(msk, tmp_addr); + + /* if a null mask is given, the name is ignored, like in "any/0" */ + if (!memcmp(msk, &in6addr_any, sizeof(in6addr_any))) + strcpy(buf, "::"); + + if ((err=inet_pton(AF_INET6, buf, addr)) < 1) { + ebt_print_error("Invalid IPv6 Address '%s' specified", buf); + return; + } + + for (i = 0; i < 4; i++) + addr->s6_addr32[i] &= msk->s6_addr32[i]; +} + +/* Transform the ip6 addr into a string ready for output. */ +char *ebt_ip6_to_numeric(const struct in6_addr *addrp) +{ + /* 0000:0000:0000:0000:0000:000.000.000.000 + * 0000:0000:0000:0000:0000:0000:0000:0000 */ + static char buf[50+1]; + return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf)); +} + +int ebt_ip6mask_to_cidr(const struct in6_addr *k) +{ + unsigned int bits = 0; + uint32_t a, b, c, d; + + a = ntohl(k->s6_addr32[0]); + b = ntohl(k->s6_addr32[1]); + c = ntohl(k->s6_addr32[2]); + d = ntohl(k->s6_addr32[3]); + while (a & 0x80000000U) { + ++bits; + a <<= 1; + a |= (b >> 31) & 1; + b <<= 1; + b |= (c >> 31) & 1; + c <<= 1; + c |= (d >> 31) & 1; + d <<= 1; + } + if (a != 0 || b != 0 || c != 0 || d != 0) + return -1; + return bits; +} + +char *ebt_ip6_mask_to_string(const struct in6_addr *msk) +{ + int l = ebt_ip6mask_to_cidr(msk); + static char buf[51+1]; + + if (l == 128) + *buf = '\0'; + else if (l == -1) + sprintf(buf, "/%s", ebt_ip6_to_numeric(msk)); + else + sprintf(buf, "/%d", l); + return buf; +} + +static char* +parse_num(const char *str, long min, long max, long *num) +{ + char *end; + + errno = 0; + *num = strtol(str, &end, 10); + if (errno && (*num == LONG_MIN || *num == LONG_MAX)) { + ebt_print_error("Invalid number %s: %s", str, strerror(errno)); + return NULL; + } + if (min <= max) { + if (*num > max || *num < min) { + ebt_print_error("Value %ld out of range (%ld, %ld)", *num, min, max); + return NULL; + } + } + if (*num == 0 && str == end) + return NULL; + return end; +} + +static char * +parse_range(const char *str, long min, long max, long num[]) +{ + char *next; + + next = parse_num(str, min, max, num); + if (next == NULL) + return NULL; + if (next && *next == ':') + next = parse_num(next+1, min, max, &num[1]); + else + num[1] = num[0]; + return next; +} + +int ebt_parse_icmp(const struct ebt_icmp_names *icmp_codes, size_t n_codes, + const char *icmptype, uint8_t type[], uint8_t code[]) +{ + unsigned int match = n_codes; + unsigned int i; + long number[2]; + + for (i = 0; i < n_codes; i++) { + if (strncasecmp(icmp_codes[i].name, icmptype, strlen(icmptype))) + continue; + if (match != n_codes) + ebt_print_error("Ambiguous ICMP type `%s':" + " `%s' or `%s'?", + icmptype, icmp_codes[match].name, + icmp_codes[i].name); + match = i; + } + + if (match < n_codes) { + type[0] = type[1] = icmp_codes[match].type; + if (code) { + code[0] = icmp_codes[match].code_min; + code[1] = icmp_codes[match].code_max; + } + } else { + char *next = parse_range(icmptype, 0, 255, number); + if (!next) { + ebt_print_error("Unknown ICMP type `%s'", + icmptype); + return -1; + } + type[0] = (uint8_t) number[0]; + type[1] = (uint8_t) number[1]; + switch (*next) { + case 0: + if (code) { + code[0] = 0; + code[1] = 255; + } + return 0; + case '/': + if (code) { + next = parse_range(next+1, 0, 255, number); + code[0] = (uint8_t) number[0]; + code[1] = (uint8_t) number[1]; + if (next == NULL) + return -1; + if (next && *next == 0) + return 0; + } + /* fallthrough */ + default: + ebt_print_error("unknown character %c", *next); + return -1; + } + } + return 0; +} + +static void print_icmp_code(uint8_t *code) +{ + if (!code) + return; + + if (code[0] == code[1]) + printf("/%"PRIu8 " ", code[0]); + else + printf("/%"PRIu8":%"PRIu8 " ", code[0], code[1]); +} + +void ebt_print_icmp_type(const struct ebt_icmp_names *icmp_codes, + size_t n_codes, uint8_t *type, uint8_t *code) +{ + unsigned int i; + + if (type[0] != type[1]) { + printf("%"PRIu8 ":%" PRIu8, type[0], type[1]); + print_icmp_code(code); + return; + } + + for (i = 0; i < n_codes; i++) { + if (icmp_codes[i].type != type[0]) + continue; + + if (!code || (icmp_codes[i].code_min == code[0] && + icmp_codes[i].code_max == code[1])) { + printf("%s ", icmp_codes[i].name); + return; + } + } + printf("%"PRIu8, type[0]); + print_icmp_code(code); +} + +void ebt_print_icmp_types(const struct ebt_icmp_names *icmp_codes, + size_t n_codes) +{ + unsigned int i; + + for (i = 0; i < n_codes; i++) { + if (i && icmp_codes[i].type == icmp_codes[i-1].type) { + if (icmp_codes[i].code_min == icmp_codes[i-1].code_min + && (icmp_codes[i].code_max + == icmp_codes[i-1].code_max)) + printf(" (%s)", icmp_codes[i].name); + else + printf("\n %s", icmp_codes[i].name); + } + else + printf("\n%s", icmp_codes[i].name); + } + printf("\n"); +}