ebtables-tiny: introduce stripped-down ebtables variant
This commit is contained in:
parent
e1d11c9393
commit
73d9ff871b
|
@ -0,0 +1,25 @@
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=ebtables-tiny
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
|
||||||
|
PKG_LICENSE:=GPL-2.0
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/ebtables-tiny
|
||||||
|
SECTION:=net
|
||||||
|
CATEGORY:=Network
|
||||||
|
SUBMENU:=Firewall
|
||||||
|
CONFLICTS:=ebtables
|
||||||
|
TITLE:=Ethernet bridge firewall administration utility (tiny)
|
||||||
|
endef
|
||||||
|
|
||||||
|
TARGET_CFLAGS += -fvisibility=hidden
|
||||||
|
|
||||||
|
define Package/ebtables-tiny/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/sbin
|
||||||
|
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ebtables $(1)/usr/sbin/ebtables-tiny
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,ebtables-tiny))
|
|
@ -0,0 +1,10 @@
|
||||||
|
--- a/include/ebtables_u.h
|
||||||
|
+++ b/include/ebtables_u.h
|
||||||
|
@@ -23,6 +23,7 @@
|
||||||
|
|
||||||
|
#ifndef EBTABLES_U_H
|
||||||
|
#define EBTABLES_U_H
|
||||||
|
+#define _NETINET_IF_ETHER_H
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <linux/netfilter_bridge/ebtables.h>
|
||||||
|
#include <linux/netfilter/x_tables.h>
|
|
@ -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.
|
|
@ -0,0 +1,32 @@
|
||||||
|
LOCKFILE?=/var/lib/ebtables/lock
|
||||||
|
LOCKDIR:=$(shell echo $(LOCKFILE) | sed 's/\(.*\)\/.*/\1/')/
|
||||||
|
|
||||||
|
|
||||||
|
CFLAGS += -Iinclude
|
||||||
|
CFLAGS += -Wall
|
||||||
|
|
||||||
|
EXT_TABLES:=filter nat broute
|
||||||
|
EXT_FUNC:=arp ip6 ip limit mark mark_m standard
|
||||||
|
EXT_OBJS:=$(foreach T,$(EXT_FUNC),extensions/ebt_$(T).o) $(foreach T,$(EXT_TABLES),extensions/ebtable_$(T).o)
|
||||||
|
|
||||||
|
OBJECTS2:=getethertype.o communication.o libebtc.o useful_functions.o ebtables.o
|
||||||
|
|
||||||
|
OBJECTS:=$(OBJECTS2) $(EXT_OBJS)
|
||||||
|
|
||||||
|
PROGSPECS:= \
|
||||||
|
-DLOCKFILE=\"$(LOCKFILE)\" \
|
||||||
|
-DLOCKDIR=\"$(LOCKDIR)\"
|
||||||
|
|
||||||
|
all: ebtables
|
||||||
|
|
||||||
|
%.o: %.c include/ebtables_u.h include/ethernetdb.h
|
||||||
|
$(CC) $(CFLAGS) $(PROGSPECS) -c -o $@ $<
|
||||||
|
|
||||||
|
ebtables: ebtables-standalone.o $(OBJECTS)
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm -f ebtables
|
||||||
|
rm -f *.o
|
||||||
|
rm -f extensions/*.o
|
|
@ -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
|
|
@ -0,0 +1,563 @@
|
||||||
|
/*
|
||||||
|
* communication.c, v2.0 July 2002
|
||||||
|
*
|
||||||
|
* Author: Bart De Schuymer
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the userspace/kernel communication is in this file.
|
||||||
|
* The other code should not have to know anything about the way the
|
||||||
|
* kernel likes the structure of the table data.
|
||||||
|
* The other code works with linked lists. So, the translation is done here.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <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];
|
||||||
|
|
||||||
|
int sockfd = -1;
|
||||||
|
|
||||||
|
static int get_sockfd()
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
if (sockfd == -1) {
|
||||||
|
sockfd = socket(AF_INET, SOCK_RAW, PF_INET);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
ebt_print_error("Problem getting a socket, "
|
||||||
|
"you probably don't have the right "
|
||||||
|
"permissions");
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ebt_replace *translate_user2kernel(struct ebt_u_replace *u_repl)
|
||||||
|
{
|
||||||
|
struct ebt_replace *new;
|
||||||
|
struct ebt_u_entry *e;
|
||||||
|
struct ebt_u_match_list *m_l;
|
||||||
|
struct ebt_u_entries *entries;
|
||||||
|
char *p, *base;
|
||||||
|
int i, j;
|
||||||
|
unsigned int entries_size = 0, *chain_offsets;
|
||||||
|
|
||||||
|
new = (struct ebt_replace *)malloc(sizeof(struct ebt_replace));
|
||||||
|
if (!new)
|
||||||
|
ebt_print_memory();
|
||||||
|
new->valid_hooks = u_repl->valid_hooks;
|
||||||
|
strcpy(new->name, u_repl->name);
|
||||||
|
new->nentries = u_repl->nentries;
|
||||||
|
new->num_counters = u_repl->num_counters;
|
||||||
|
new->counters = u_repl->counters;
|
||||||
|
chain_offsets = (unsigned int *)calloc(u_repl->num_chains, sizeof(unsigned int));
|
||||||
|
if (!chain_offsets)
|
||||||
|
ebt_print_memory();
|
||||||
|
/* Determine size */
|
||||||
|
for (i = 0; i < u_repl->num_chains; i++) {
|
||||||
|
if (!(entries = u_repl->chains[i]))
|
||||||
|
continue;
|
||||||
|
chain_offsets[i] = entries_size;
|
||||||
|
entries_size += sizeof(struct ebt_entries);
|
||||||
|
j = 0;
|
||||||
|
e = entries->entries->next;
|
||||||
|
while (e != entries->entries) {
|
||||||
|
j++;
|
||||||
|
entries_size += sizeof(struct ebt_entry);
|
||||||
|
m_l = e->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
entries_size += m_l->m->match_size +
|
||||||
|
sizeof(struct ebt_entry_match);
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
entries_size += e->t->target_size +
|
||||||
|
sizeof(struct ebt_entry_target);
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
/* A little sanity check */
|
||||||
|
if (j != entries->nentries)
|
||||||
|
ebt_print_bug("Wrong nentries: %d != %d, hook = %s", j,
|
||||||
|
entries->nentries, entries->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
new->entries_size = entries_size;
|
||||||
|
p = (char *)malloc(entries_size);
|
||||||
|
if (!p)
|
||||||
|
ebt_print_memory();
|
||||||
|
|
||||||
|
/* Put everything in one block */
|
||||||
|
new->entries = p;
|
||||||
|
for (i = 0; i < u_repl->num_chains; i++) {
|
||||||
|
struct ebt_entries *hlp;
|
||||||
|
|
||||||
|
hlp = (struct ebt_entries *)p;
|
||||||
|
if (!(entries = u_repl->chains[i]))
|
||||||
|
continue;
|
||||||
|
if (i < NF_BR_NUMHOOKS)
|
||||||
|
new->hook_entry[i] = hlp;
|
||||||
|
hlp->nentries = entries->nentries;
|
||||||
|
hlp->policy = entries->policy;
|
||||||
|
strcpy(hlp->name, entries->name);
|
||||||
|
hlp->counter_offset = entries->counter_offset;
|
||||||
|
hlp->distinguisher = 0; /* Make the kernel see the light */
|
||||||
|
p += sizeof(struct ebt_entries);
|
||||||
|
e = entries->entries->next;
|
||||||
|
while (e != entries->entries) {
|
||||||
|
struct ebt_entry *tmp = (struct ebt_entry *)p;
|
||||||
|
|
||||||
|
tmp->bitmask = e->bitmask | EBT_ENTRY_OR_ENTRIES;
|
||||||
|
tmp->invflags = e->invflags;
|
||||||
|
tmp->ethproto = e->ethproto;
|
||||||
|
strcpy(tmp->in, e->in);
|
||||||
|
strcpy(tmp->out, e->out);
|
||||||
|
strcpy(tmp->logical_in, e->logical_in);
|
||||||
|
strcpy(tmp->logical_out, e->logical_out);
|
||||||
|
memcpy(tmp->sourcemac, e->sourcemac,
|
||||||
|
sizeof(tmp->sourcemac));
|
||||||
|
memcpy(tmp->sourcemsk, e->sourcemsk,
|
||||||
|
sizeof(tmp->sourcemsk));
|
||||||
|
memcpy(tmp->destmac, e->destmac, sizeof(tmp->destmac));
|
||||||
|
memcpy(tmp->destmsk, e->destmsk, sizeof(tmp->destmsk));
|
||||||
|
|
||||||
|
base = p;
|
||||||
|
p += sizeof(struct ebt_entry);
|
||||||
|
m_l = e->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
memcpy(p, m_l->m, m_l->m->match_size +
|
||||||
|
sizeof(struct ebt_entry_match));
|
||||||
|
p += m_l->m->match_size +
|
||||||
|
sizeof(struct ebt_entry_match);
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
tmp->watchers_offset = p - base;
|
||||||
|
tmp->target_offset = p - base;
|
||||||
|
memcpy(p, e->t, e->t->target_size +
|
||||||
|
sizeof(struct ebt_entry_target));
|
||||||
|
if (!strcmp(e->t->u.name, EBT_STANDARD_TARGET)) {
|
||||||
|
struct ebt_standard_target *st =
|
||||||
|
(struct ebt_standard_target *)p;
|
||||||
|
/* Translate the jump to a udc */
|
||||||
|
if (st->verdict >= 0)
|
||||||
|
st->verdict = chain_offsets
|
||||||
|
[st->verdict + NF_BR_NUMHOOKS];
|
||||||
|
}
|
||||||
|
p += e->t->target_size +
|
||||||
|
sizeof(struct ebt_entry_target);
|
||||||
|
tmp->next_offset = p - base;
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if (p - (char *)new->entries != new->entries_size)
|
||||||
|
ebt_print_bug("Entries_size bug");
|
||||||
|
free(chain_offsets);
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ebt_deliver_table(struct ebt_u_replace *u_repl)
|
||||||
|
{
|
||||||
|
socklen_t optlen;
|
||||||
|
struct ebt_replace *repl;
|
||||||
|
|
||||||
|
/* Translate the struct ebt_u_replace to a struct ebt_replace */
|
||||||
|
repl = translate_user2kernel(u_repl);
|
||||||
|
/* Give the data to the kernel */
|
||||||
|
optlen = sizeof(struct ebt_replace) + repl->entries_size;
|
||||||
|
if (get_sockfd())
|
||||||
|
goto free_repl;
|
||||||
|
if (!setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_ENTRIES, repl, optlen))
|
||||||
|
goto free_repl;
|
||||||
|
if (u_repl->command == 8)
|
||||||
|
goto free_repl;
|
||||||
|
|
||||||
|
ebt_print_error("Unable to update the kernel. Two possible causes:\n"
|
||||||
|
"1. Multiple ebtables programs were executing simultaneously. The ebtables\n"
|
||||||
|
" userspace tool doesn't by default support multiple ebtables programs running\n"
|
||||||
|
" concurrently. The ebtables option --concurrent or a tool like flock can be\n"
|
||||||
|
" used to support concurrent scripts that update the ebtables kernel tables.\n"
|
||||||
|
"2. The kernel doesn't support a certain ebtables extension, consider\n"
|
||||||
|
" recompiling your kernel or insmod the extension.\n");
|
||||||
|
free_repl:
|
||||||
|
if (repl) {
|
||||||
|
free(repl->entries);
|
||||||
|
free(repl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets executed after ebt_deliver_table. Delivers the counters to the kernel
|
||||||
|
* and resets the counterchanges to CNT_NORM */
|
||||||
|
void ebt_deliver_counters(struct ebt_u_replace *u_repl)
|
||||||
|
{
|
||||||
|
struct ebt_counter *old, *new, *newcounters;
|
||||||
|
socklen_t optlen;
|
||||||
|
struct ebt_replace repl;
|
||||||
|
struct ebt_cntchanges *cc = u_repl->cc->next, *cc2;
|
||||||
|
struct ebt_u_entries *entries = NULL;
|
||||||
|
struct ebt_u_entry *next = NULL;
|
||||||
|
int i, chainnr = -1;
|
||||||
|
|
||||||
|
if (u_repl->nentries == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
newcounters = (struct ebt_counter *)
|
||||||
|
malloc(u_repl->nentries * sizeof(struct ebt_counter));
|
||||||
|
if (!newcounters)
|
||||||
|
ebt_print_memory();
|
||||||
|
memset(newcounters, 0, u_repl->nentries * sizeof(struct ebt_counter));
|
||||||
|
old = u_repl->counters;
|
||||||
|
new = newcounters;
|
||||||
|
while (cc != u_repl->cc) {
|
||||||
|
if (!next || next == entries->entries) {
|
||||||
|
chainnr++;
|
||||||
|
while (chainnr < u_repl->num_chains && (!(entries = u_repl->chains[chainnr]) ||
|
||||||
|
(next = entries->entries->next) == entries->entries))
|
||||||
|
chainnr++;
|
||||||
|
if (chainnr == u_repl->num_chains)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next == NULL)
|
||||||
|
ebt_print_bug("next == NULL");
|
||||||
|
if (cc->type == CNT_NORM) {
|
||||||
|
/* 'Normal' rule, meaning we didn't do anything to it
|
||||||
|
* So, we just copy */
|
||||||
|
*new = *old;
|
||||||
|
next->cnt = *new;
|
||||||
|
next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
|
||||||
|
old++; /* We've used an old counter */
|
||||||
|
new++; /* We've set a new counter */
|
||||||
|
next = next->next;
|
||||||
|
} else if (cc->type == CNT_DEL) {
|
||||||
|
old++; /* Don't use this old counter */
|
||||||
|
} else {
|
||||||
|
if (cc->type == CNT_CHANGE) {
|
||||||
|
if (cc->change % 3 == 1)
|
||||||
|
new->pcnt = old->pcnt + next->cnt_surplus.pcnt;
|
||||||
|
else if (cc->change % 3 == 2)
|
||||||
|
new->pcnt = old->pcnt - next->cnt_surplus.pcnt;
|
||||||
|
else
|
||||||
|
new->pcnt = next->cnt.pcnt;
|
||||||
|
if (cc->change / 3 == 1)
|
||||||
|
new->bcnt = old->bcnt + next->cnt_surplus.bcnt;
|
||||||
|
else if (cc->change / 3 == 2)
|
||||||
|
new->bcnt = old->bcnt - next->cnt_surplus.bcnt;
|
||||||
|
else
|
||||||
|
new->bcnt = next->cnt.bcnt;
|
||||||
|
} else
|
||||||
|
*new = next->cnt;
|
||||||
|
next->cnt = *new;
|
||||||
|
next->cnt_surplus.pcnt = next->cnt_surplus.bcnt = 0;
|
||||||
|
if (cc->type == CNT_ADD)
|
||||||
|
new++;
|
||||||
|
else {
|
||||||
|
old++;
|
||||||
|
new++;
|
||||||
|
}
|
||||||
|
next = next->next;
|
||||||
|
}
|
||||||
|
cc = cc->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(u_repl->counters);
|
||||||
|
u_repl->counters = newcounters;
|
||||||
|
u_repl->num_counters = u_repl->nentries;
|
||||||
|
/* Reset the counterchanges to CNT_NORM and delete the unused cc */
|
||||||
|
i = 0;
|
||||||
|
cc = u_repl->cc->next;
|
||||||
|
while (cc != u_repl->cc) {
|
||||||
|
if (cc->type == CNT_DEL) {
|
||||||
|
cc->prev->next = cc->next;
|
||||||
|
cc->next->prev = cc->prev;
|
||||||
|
cc2 = cc->next;
|
||||||
|
free(cc);
|
||||||
|
cc = cc2;
|
||||||
|
} else {
|
||||||
|
cc->type = CNT_NORM;
|
||||||
|
cc->change = 0;
|
||||||
|
i++;
|
||||||
|
cc = cc->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i != u_repl->nentries)
|
||||||
|
ebt_print_bug("i != u_repl->nentries");
|
||||||
|
optlen = u_repl->nentries * sizeof(struct ebt_counter) +
|
||||||
|
sizeof(struct ebt_replace);
|
||||||
|
/* Now put the stuff in the kernel's struct ebt_replace */
|
||||||
|
repl.counters = u_repl->counters;
|
||||||
|
repl.num_counters = u_repl->num_counters;
|
||||||
|
memcpy(repl.name, u_repl->name, sizeof(repl.name));
|
||||||
|
|
||||||
|
if (get_sockfd())
|
||||||
|
return;
|
||||||
|
if (setsockopt(sockfd, IPPROTO_IP, EBT_SO_SET_COUNTERS, &repl, optlen))
|
||||||
|
ebt_print_bug("Couldn't update kernel counters");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ebt_translate_match(struct ebt_entry_match *m, struct ebt_u_match_list ***l)
|
||||||
|
{
|
||||||
|
struct ebt_u_match_list *new;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
new = (struct ebt_u_match_list *)
|
||||||
|
malloc(sizeof(struct ebt_u_match_list));
|
||||||
|
if (!new)
|
||||||
|
ebt_print_memory();
|
||||||
|
new->m = (struct ebt_entry_match *)
|
||||||
|
malloc(m->match_size + sizeof(struct ebt_entry_match));
|
||||||
|
if (!new->m)
|
||||||
|
ebt_print_memory();
|
||||||
|
memcpy(new->m, m, m->match_size + sizeof(struct ebt_entry_match));
|
||||||
|
new->next = NULL;
|
||||||
|
**l = new;
|
||||||
|
*l = &new->next;
|
||||||
|
if (ebt_find_match(new->m->u.name) == NULL) {
|
||||||
|
ebt_print_error("Kernel match %s unsupported by userspace tool",
|
||||||
|
new->m->u.name);
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ebt_translate_watcher(struct ebt_entry_watcher *w)
|
||||||
|
{
|
||||||
|
ebt_print_error("Kernel watcher %s unsupported by userspace "
|
||||||
|
"tool", w->u.name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ebt_translate_entry(struct ebt_entry *e, int *hook, int *n, int *cnt,
|
||||||
|
int *totalcnt, struct ebt_u_entry **u_e, struct ebt_u_replace *u_repl,
|
||||||
|
unsigned int valid_hooks, char *base, struct ebt_cntchanges **cc)
|
||||||
|
{
|
||||||
|
/* An entry */
|
||||||
|
if (e->bitmask & EBT_ENTRY_OR_ENTRIES) {
|
||||||
|
struct ebt_u_entry *new;
|
||||||
|
struct ebt_u_match_list **m_l;
|
||||||
|
struct ebt_entry_target *t;
|
||||||
|
|
||||||
|
new = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
|
||||||
|
if (!new)
|
||||||
|
ebt_print_memory();
|
||||||
|
new->bitmask = e->bitmask;
|
||||||
|
/*
|
||||||
|
* Plain userspace code doesn't know about
|
||||||
|
* EBT_ENTRY_OR_ENTRIES
|
||||||
|
*/
|
||||||
|
new->bitmask &= ~EBT_ENTRY_OR_ENTRIES;
|
||||||
|
new->invflags = e->invflags;
|
||||||
|
new->ethproto = e->ethproto;
|
||||||
|
strcpy(new->in, e->in);
|
||||||
|
strcpy(new->out, e->out);
|
||||||
|
strcpy(new->logical_in, e->logical_in);
|
||||||
|
strcpy(new->logical_out, e->logical_out);
|
||||||
|
memcpy(new->sourcemac, e->sourcemac, sizeof(new->sourcemac));
|
||||||
|
memcpy(new->sourcemsk, e->sourcemsk, sizeof(new->sourcemsk));
|
||||||
|
memcpy(new->destmac, e->destmac, sizeof(new->destmac));
|
||||||
|
memcpy(new->destmsk, e->destmsk, sizeof(new->destmsk));
|
||||||
|
if (*totalcnt >= u_repl->nentries)
|
||||||
|
ebt_print_bug("*totalcnt >= u_repl->nentries");
|
||||||
|
new->cnt = u_repl->counters[*totalcnt];
|
||||||
|
new->cnt_surplus.pcnt = new->cnt_surplus.bcnt = 0;
|
||||||
|
new->cc = *cc;
|
||||||
|
*cc = (*cc)->next;
|
||||||
|
new->m_list = NULL;
|
||||||
|
new->next = (*u_e)->next;
|
||||||
|
new->next->prev = new;
|
||||||
|
(*u_e)->next = new;
|
||||||
|
new->prev = *u_e;
|
||||||
|
*u_e = new;
|
||||||
|
m_l = &new->m_list;
|
||||||
|
EBT_MATCH_ITERATE(e, ebt_translate_match, &m_l);
|
||||||
|
EBT_WATCHER_ITERATE(e, ebt_translate_watcher);
|
||||||
|
|
||||||
|
t = (struct ebt_entry_target *)(((char *)e) + e->target_offset);
|
||||||
|
new->t = (struct ebt_entry_target *)
|
||||||
|
malloc(t->target_size + sizeof(struct ebt_entry_target));
|
||||||
|
if (!new->t)
|
||||||
|
ebt_print_memory();
|
||||||
|
if (ebt_find_target(t->u.name) == NULL) {
|
||||||
|
ebt_print_error("Kernel target %s unsupported by "
|
||||||
|
"userspace tool", t->u.name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(new->t, t, t->target_size +
|
||||||
|
sizeof(struct ebt_entry_target));
|
||||||
|
/* Deal with jumps to udc */
|
||||||
|
if (!strcmp(t->u.name, EBT_STANDARD_TARGET)) {
|
||||||
|
char *tmp = base;
|
||||||
|
int verdict = ((struct ebt_standard_target *)t)->verdict;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (verdict >= 0) {
|
||||||
|
tmp += verdict;
|
||||||
|
for (i = NF_BR_NUMHOOKS; i < u_repl->num_chains; i++)
|
||||||
|
if (u_repl->chains[i]->kernel_start == tmp)
|
||||||
|
break;
|
||||||
|
if (i == u_repl->num_chains)
|
||||||
|
ebt_print_bug("Can't find udc for jump");
|
||||||
|
((struct ebt_standard_target *)new->t)->verdict = i-NF_BR_NUMHOOKS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(*cnt)++;
|
||||||
|
(*totalcnt)++;
|
||||||
|
return 0;
|
||||||
|
} else { /* A new chain */
|
||||||
|
int i;
|
||||||
|
struct ebt_entries *entries = (struct ebt_entries *)e;
|
||||||
|
|
||||||
|
if (*n != *cnt)
|
||||||
|
ebt_print_bug("Nr of entries in the chain is wrong");
|
||||||
|
*n = entries->nentries;
|
||||||
|
*cnt = 0;
|
||||||
|
for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
|
||||||
|
if (valid_hooks & (1 << i))
|
||||||
|
break;
|
||||||
|
*hook = i;
|
||||||
|
*u_e = u_repl->chains[*hook]->entries;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize all chain headers */
|
||||||
|
static int
|
||||||
|
ebt_translate_chains(struct ebt_entry *e, int *hook,
|
||||||
|
struct ebt_u_replace *u_repl, unsigned int valid_hooks)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct ebt_entries *entries = (struct ebt_entries *)e;
|
||||||
|
struct ebt_u_entries *new;
|
||||||
|
|
||||||
|
if (!(e->bitmask & EBT_ENTRY_OR_ENTRIES)) {
|
||||||
|
for (i = *hook + 1; i < NF_BR_NUMHOOKS; i++)
|
||||||
|
if (valid_hooks & (1 << i))
|
||||||
|
break;
|
||||||
|
new = (struct ebt_u_entries *)malloc(sizeof(struct ebt_u_entries));
|
||||||
|
if (!new)
|
||||||
|
ebt_print_memory();
|
||||||
|
if (i == u_repl->max_chains)
|
||||||
|
ebt_double_chains(u_repl);
|
||||||
|
u_repl->chains[i] = new;
|
||||||
|
if (i >= NF_BR_NUMHOOKS)
|
||||||
|
new->kernel_start = (char *)e;
|
||||||
|
*hook = i;
|
||||||
|
new->nentries = entries->nentries;
|
||||||
|
new->policy = entries->policy;
|
||||||
|
new->entries = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
|
||||||
|
if (!new->entries)
|
||||||
|
ebt_print_memory();
|
||||||
|
new->entries->next = new->entries->prev = new->entries;
|
||||||
|
new->counter_offset = entries->counter_offset;
|
||||||
|
strcpy(new->name, entries->name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int retrieve_from_kernel(struct ebt_replace *repl, char command)
|
||||||
|
{
|
||||||
|
socklen_t optlen;
|
||||||
|
int optname;
|
||||||
|
char *entries;
|
||||||
|
|
||||||
|
optlen = sizeof(struct ebt_replace);
|
||||||
|
if (get_sockfd())
|
||||||
|
return -1;
|
||||||
|
optname = EBT_SO_GET_INFO;
|
||||||
|
if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ( !(entries = (char *)malloc(repl->entries_size)) )
|
||||||
|
ebt_print_memory();
|
||||||
|
repl->entries = entries;
|
||||||
|
if (repl->nentries) {
|
||||||
|
struct ebt_counter *counters;
|
||||||
|
|
||||||
|
if (!(counters = (struct ebt_counter *)
|
||||||
|
malloc(repl->nentries * sizeof(struct ebt_counter))) )
|
||||||
|
ebt_print_memory();
|
||||||
|
repl->counters = counters;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
repl->counters = NULL;
|
||||||
|
|
||||||
|
/* We want to receive the counters */
|
||||||
|
repl->num_counters = repl->nentries;
|
||||||
|
optlen += repl->entries_size + repl->num_counters *
|
||||||
|
sizeof(struct ebt_counter);
|
||||||
|
optname = EBT_SO_GET_ENTRIES;
|
||||||
|
if (getsockopt(sockfd, IPPROTO_IP, optname, repl, &optlen))
|
||||||
|
ebt_print_bug("Hmm, what is wrong??? bug#1");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ebt_get_table(struct ebt_u_replace *u_repl)
|
||||||
|
{
|
||||||
|
int i, j, k, hook;
|
||||||
|
struct ebt_replace repl;
|
||||||
|
struct ebt_u_entry *u_e = NULL;
|
||||||
|
struct ebt_cntchanges *new_cc = NULL, *cc;
|
||||||
|
|
||||||
|
strcpy(repl.name, u_repl->name);
|
||||||
|
if (retrieve_from_kernel(&repl, u_repl->command))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Translate the struct ebt_replace to a struct ebt_u_replace */
|
||||||
|
u_repl->valid_hooks = repl.valid_hooks;
|
||||||
|
u_repl->nentries = repl.nentries;
|
||||||
|
u_repl->num_counters = repl.num_counters;
|
||||||
|
u_repl->counters = repl.counters;
|
||||||
|
u_repl->cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
|
||||||
|
if (!u_repl->cc)
|
||||||
|
ebt_print_memory();
|
||||||
|
u_repl->cc->next = u_repl->cc->prev = u_repl->cc;
|
||||||
|
cc = u_repl->cc;
|
||||||
|
for (i = 0; i < repl.nentries; i++) {
|
||||||
|
new_cc = (struct ebt_cntchanges *)malloc(sizeof(struct ebt_cntchanges));
|
||||||
|
if (!new_cc)
|
||||||
|
ebt_print_memory();
|
||||||
|
new_cc->type = CNT_NORM;
|
||||||
|
new_cc->change = 0;
|
||||||
|
new_cc->prev = cc;
|
||||||
|
cc->next = new_cc;
|
||||||
|
cc = new_cc;
|
||||||
|
}
|
||||||
|
if (repl.nentries) {
|
||||||
|
new_cc->next = u_repl->cc;
|
||||||
|
u_repl->cc->prev = new_cc;
|
||||||
|
}
|
||||||
|
u_repl->chains = (struct ebt_u_entries **)calloc(EBT_ORI_MAX_CHAINS, sizeof(void *));
|
||||||
|
u_repl->max_chains = EBT_ORI_MAX_CHAINS;
|
||||||
|
hook = -1;
|
||||||
|
/* FIXME: Clean up when an error is encountered */
|
||||||
|
EBT_ENTRY_ITERATE(repl.entries, repl.entries_size, ebt_translate_chains,
|
||||||
|
&hook, u_repl, u_repl->valid_hooks);
|
||||||
|
if (hook >= NF_BR_NUMHOOKS)
|
||||||
|
u_repl->num_chains = hook + 1;
|
||||||
|
else
|
||||||
|
u_repl->num_chains = NF_BR_NUMHOOKS;
|
||||||
|
i = 0; /* Holds the expected nr. of entries for the chain */
|
||||||
|
j = 0; /* Holds the up to now counted entries for the chain */
|
||||||
|
k = 0; /* Holds the total nr. of entries, should equal u_repl->nentries afterwards */
|
||||||
|
cc = u_repl->cc->next;
|
||||||
|
hook = -1;
|
||||||
|
EBT_ENTRY_ITERATE((char *)repl.entries, repl.entries_size,
|
||||||
|
ebt_translate_entry, &hook, &i, &j, &k, &u_e, u_repl,
|
||||||
|
u_repl->valid_hooks, (char *)repl.entries, &cc);
|
||||||
|
if (k != u_repl->nentries)
|
||||||
|
ebt_print_bug("Wrong total nentries");
|
||||||
|
free(repl.entries);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#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_early_init_once();
|
||||||
|
strcpy(replace.name, "filter");
|
||||||
|
do_command(argc, argv, &replace);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,829 @@
|
||||||
|
/*
|
||||||
|
* ebtables.c, v2.0 July 2002
|
||||||
|
*
|
||||||
|
* Author: Bart De Schuymer
|
||||||
|
*
|
||||||
|
* This code was stongly inspired on the iptables code which is
|
||||||
|
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation; either version 2 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <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)
|
||||||
|
|
||||||
|
#define OPT_COMMAND 0x01
|
||||||
|
#define OPT_TABLE 0x02
|
||||||
|
#define OPT_IN 0x04
|
||||||
|
#define OPT_OUT 0x08
|
||||||
|
#define OPT_JUMP 0x10
|
||||||
|
#define OPT_PROTOCOL 0x20
|
||||||
|
#define OPT_SOURCE 0x40
|
||||||
|
#define OPT_DEST 0x80
|
||||||
|
#define OPT_LOGICALIN 0x200
|
||||||
|
#define OPT_LOGICALOUT 0x400
|
||||||
|
|
||||||
|
/* Default command line options. Do not mess around with the already
|
||||||
|
* assigned numbers unless you know what you are doing */
|
||||||
|
static struct option ebt_original_options[] =
|
||||||
|
{
|
||||||
|
{ "append" , required_argument, 0, 'A' },
|
||||||
|
{ "insert" , required_argument, 0, 'I' },
|
||||||
|
{ "delete" , required_argument, 0, 'D' },
|
||||||
|
{ "list" , optional_argument, 0, 'L' },
|
||||||
|
{ "flush" , optional_argument, 0, 'F' },
|
||||||
|
{ "policy" , required_argument, 0, 'P' },
|
||||||
|
{ "in-interface" , required_argument, 0, 'i' },
|
||||||
|
{ "in-if" , required_argument, 0, 'i' },
|
||||||
|
{ "logical-in" , required_argument, 0, 2 },
|
||||||
|
{ "logical-out" , required_argument, 0, 3 },
|
||||||
|
{ "out-interface" , required_argument, 0, 'o' },
|
||||||
|
{ "out-if" , required_argument, 0, 'o' },
|
||||||
|
{ "version" , no_argument , 0, 'V' },
|
||||||
|
{ "help" , no_argument , 0, 'h' },
|
||||||
|
{ "jump" , required_argument, 0, 'j' },
|
||||||
|
{ "proto" , required_argument, 0, 'p' },
|
||||||
|
{ "protocol" , required_argument, 0, 'p' },
|
||||||
|
{ "source" , required_argument, 0, 's' },
|
||||||
|
{ "src" , required_argument, 0, 's' },
|
||||||
|
{ "destination" , required_argument, 0, 'd' },
|
||||||
|
{ "dst" , required_argument, 0, 'd' },
|
||||||
|
{ "table" , required_argument, 0, 't' },
|
||||||
|
{ "new-chain" , required_argument, 0, 'N' },
|
||||||
|
{ "rename-chain" , required_argument, 0, 'E' },
|
||||||
|
{ "delete-chain" , optional_argument, 0, 'X' },
|
||||||
|
{ 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct option *ebt_options = ebt_original_options;
|
||||||
|
|
||||||
|
/* Holds all the data */
|
||||||
|
static struct ebt_u_replace *replace;
|
||||||
|
|
||||||
|
/* The chosen table */
|
||||||
|
static struct ebt_u_table *table;
|
||||||
|
|
||||||
|
/* The pointers in here are special:
|
||||||
|
* The struct ebt_target pointer is actually a struct ebt_u_target pointer.
|
||||||
|
* I do not feel like using a union.
|
||||||
|
* We need a struct ebt_u_target pointer because we know the address of the data
|
||||||
|
* they point to won't change. We want to allow that the struct ebt_u_target.t
|
||||||
|
* member can change.
|
||||||
|
* The same holds for the struct ebt_match and struct ebt_watcher pointers */
|
||||||
|
static struct ebt_u_entry *new_entry;
|
||||||
|
|
||||||
|
|
||||||
|
static int global_option_offset;
|
||||||
|
#define OPTION_OFFSET 256
|
||||||
|
static struct option *merge_options(struct option *oldopts,
|
||||||
|
const struct option *newopts, unsigned int *options_offset)
|
||||||
|
{
|
||||||
|
unsigned int num_old, num_new, i;
|
||||||
|
struct option *merge;
|
||||||
|
|
||||||
|
if (!newopts || !oldopts || !options_offset)
|
||||||
|
ebt_print_bug("merge wrong");
|
||||||
|
for (num_old = 0; oldopts[num_old].name; num_old++);
|
||||||
|
for (num_new = 0; newopts[num_new].name; num_new++);
|
||||||
|
|
||||||
|
global_option_offset += OPTION_OFFSET;
|
||||||
|
*options_offset = global_option_offset;
|
||||||
|
|
||||||
|
merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
|
||||||
|
if (!merge)
|
||||||
|
ebt_print_memory();
|
||||||
|
memcpy(merge, oldopts, num_old * sizeof(struct option));
|
||||||
|
for (i = 0; i < num_new; i++) {
|
||||||
|
merge[num_old + i] = newopts[i];
|
||||||
|
merge[num_old + i].val += *options_offset;
|
||||||
|
}
|
||||||
|
memset(merge + num_old + num_new, 0, sizeof(struct option));
|
||||||
|
/* Only free dynamically allocated stuff */
|
||||||
|
if (oldopts != ebt_original_options)
|
||||||
|
free(oldopts);
|
||||||
|
|
||||||
|
return merge;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void merge_match(struct ebt_u_match *m)
|
||||||
|
{
|
||||||
|
ebt_options = merge_options
|
||||||
|
(ebt_options, m->extra_ops, &(m->option_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void merge_target(struct ebt_u_target *t)
|
||||||
|
{
|
||||||
|
ebt_options = merge_options
|
||||||
|
(ebt_options, t->extra_ops, &(t->option_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Be backwards compatible, so don't use '+' in kernel */
|
||||||
|
#define IF_WILDCARD 1
|
||||||
|
static void print_iface(const char *iface)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
if ((c = strchr(iface, IF_WILDCARD)))
|
||||||
|
*c = '+';
|
||||||
|
printf("%s ", iface);
|
||||||
|
if (c)
|
||||||
|
*c = IF_WILDCARD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for list_rules() */
|
||||||
|
static void list_em(struct ebt_u_entries *entries)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct ebt_u_entry *hlp;
|
||||||
|
struct ebt_u_match_list *m_l;
|
||||||
|
struct ebt_u_match *m;
|
||||||
|
struct ebt_u_target *t;
|
||||||
|
|
||||||
|
hlp = entries->entries->next;
|
||||||
|
printf("\nBridge chain: %s, entries: %d, policy: %s\n",
|
||||||
|
entries->name, entries->nentries,
|
||||||
|
ebt_standard_targets[-entries->policy - 1]);
|
||||||
|
|
||||||
|
for (i = 0; i < entries->nentries; i++) {
|
||||||
|
/* The standard target's print() uses this to find out
|
||||||
|
* the name of a udc */
|
||||||
|
hlp->replace = replace;
|
||||||
|
|
||||||
|
/* Don't print anything about the protocol if no protocol was
|
||||||
|
* specified, obviously this means any protocol will do. */
|
||||||
|
if (!(hlp->bitmask & EBT_NOPROTO)) {
|
||||||
|
printf("-p ");
|
||||||
|
if (hlp->invflags & EBT_IPROTO)
|
||||||
|
printf("! ");
|
||||||
|
if (hlp->bitmask & EBT_802_3)
|
||||||
|
printf("Length ");
|
||||||
|
else {
|
||||||
|
const struct ethertypeent *ent;
|
||||||
|
|
||||||
|
ent = getethertypebynumber(ntohs(hlp->ethproto));
|
||||||
|
if (!ent)
|
||||||
|
printf("0x%x ", ntohs(hlp->ethproto));
|
||||||
|
else
|
||||||
|
printf("%s ", ent->e_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hlp->bitmask & EBT_SOURCEMAC) {
|
||||||
|
printf("-s ");
|
||||||
|
if (hlp->invflags & EBT_ISOURCE)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_mac_and_mask(hlp->sourcemac, hlp->sourcemsk);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
if (hlp->bitmask & EBT_DESTMAC) {
|
||||||
|
printf("-d ");
|
||||||
|
if (hlp->invflags & EBT_IDEST)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_mac_and_mask(hlp->destmac, hlp->destmsk);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
if (hlp->in[0] != '\0') {
|
||||||
|
printf("-i ");
|
||||||
|
if (hlp->invflags & EBT_IIN)
|
||||||
|
printf("! ");
|
||||||
|
print_iface(hlp->in);
|
||||||
|
}
|
||||||
|
if (hlp->logical_in[0] != '\0') {
|
||||||
|
printf("--logical-in ");
|
||||||
|
if (hlp->invflags & EBT_ILOGICALIN)
|
||||||
|
printf("! ");
|
||||||
|
print_iface(hlp->logical_in);
|
||||||
|
}
|
||||||
|
if (hlp->logical_out[0] != '\0') {
|
||||||
|
printf("--logical-out ");
|
||||||
|
if (hlp->invflags & EBT_ILOGICALOUT)
|
||||||
|
printf("! ");
|
||||||
|
print_iface(hlp->logical_out);
|
||||||
|
}
|
||||||
|
if (hlp->out[0] != '\0') {
|
||||||
|
printf("-o ");
|
||||||
|
if (hlp->invflags & EBT_IOUT)
|
||||||
|
printf("! ");
|
||||||
|
print_iface(hlp->out);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_l = hlp->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
m = ebt_find_match(m_l->m->u.name);
|
||||||
|
if (!m)
|
||||||
|
ebt_print_bug("Match not found");
|
||||||
|
m->print(hlp, m_l->m);
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("-j ");
|
||||||
|
if (strcmp(hlp->t->u.name, EBT_STANDARD_TARGET))
|
||||||
|
printf("%s ", hlp->t->u.name);
|
||||||
|
t = ebt_find_target(hlp->t->u.name);
|
||||||
|
if (!t)
|
||||||
|
ebt_print_bug("Target '%s' not found", hlp->t->u.name);
|
||||||
|
t->print(hlp, hlp->t);
|
||||||
|
printf("\n");
|
||||||
|
hlp = hlp->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help()
|
||||||
|
{
|
||||||
|
struct ebt_u_match_list *m_l;
|
||||||
|
|
||||||
|
PRINT_VERSION;
|
||||||
|
printf(
|
||||||
|
"Usage:\n"
|
||||||
|
"ebtables -[ADI] chain rule-specification [options]\n"
|
||||||
|
"ebtables -P chain target\n"
|
||||||
|
"ebtables -[LFZ] [chain]\n"
|
||||||
|
"ebtables -[NX] [chain]\n"
|
||||||
|
"ebtables -E old-chain-name new-chain-name\n\n"
|
||||||
|
"Commands:\n"
|
||||||
|
"--append -A chain : append to chain\n"
|
||||||
|
"--delete -D chain : delete matching rule from chain\n"
|
||||||
|
"--delete -D chain rulenum : delete rule at position rulenum from chain\n"
|
||||||
|
"--insert -I chain rulenum : insert rule at position rulenum in chain\n"
|
||||||
|
"--list -L [chain] : list the rules in a chain or in all chains\n"
|
||||||
|
"--flush -F [chain] : delete all rules in chain or in all chains\n"
|
||||||
|
"--policy -P chain target : change policy on chain to target\n"
|
||||||
|
"--new-chain -N chain : create a user defined chain\n"
|
||||||
|
"--rename-chain -E old new : rename a chain\n"
|
||||||
|
"--delete-chain -X [chain] : delete a user defined chain\n"
|
||||||
|
"Options:\n"
|
||||||
|
"--proto -p [!] proto : protocol hexadecimal, by name or LENGTH\n"
|
||||||
|
"--src -s [!] address[/mask]: source mac address\n"
|
||||||
|
"--dst -d [!] address[/mask]: destination mac address\n"
|
||||||
|
"--in-if -i [!] name[+] : network input interface name\n"
|
||||||
|
"--out-if -o [!] name[+] : network output interface name\n"
|
||||||
|
"--logical-in [!] name[+] : logical bridge input interface name\n"
|
||||||
|
"--logical-out [!] name[+] : logical bridge output interface name\n"
|
||||||
|
"--version -V : print package version\n"
|
||||||
|
"\n");
|
||||||
|
m_l = new_entry->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
((struct ebt_u_match *)m_l->m)->help();
|
||||||
|
printf("\n");
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
((struct ebt_u_target *)new_entry->t)->help();
|
||||||
|
printf("\n");
|
||||||
|
if (table->help)
|
||||||
|
table->help(ebt_hooknames);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute command L */
|
||||||
|
static void list_rules()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
printf("Bridge table: %s\n", table->name);
|
||||||
|
if (replace->selected_chain != -1)
|
||||||
|
list_em(ebt_to_chain(replace));
|
||||||
|
else {
|
||||||
|
for (i = 0; i < replace->num_chains; i++)
|
||||||
|
if (replace->chains[i])
|
||||||
|
list_em(replace->chains[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_rule_range(const char *argv, int *rule_nr, int *rule_nr_end)
|
||||||
|
{
|
||||||
|
char *colon = strchr(argv, ':'), *buffer;
|
||||||
|
|
||||||
|
if (colon) {
|
||||||
|
*colon = '\0';
|
||||||
|
if (*(colon + 1) == '\0')
|
||||||
|
*rule_nr_end = -1; /* Until the last rule */
|
||||||
|
else {
|
||||||
|
*rule_nr_end = strtol(colon + 1, &buffer, 10);
|
||||||
|
if (*buffer != '\0' || *rule_nr_end == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (colon == argv)
|
||||||
|
*rule_nr = 1; /* Beginning with the first rule */
|
||||||
|
else {
|
||||||
|
*rule_nr = strtol(argv, &buffer, 10);
|
||||||
|
if (*buffer != '\0' || *rule_nr == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!colon)
|
||||||
|
*rule_nr_end = *rule_nr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_iface(char *iface, char *option)
|
||||||
|
{
|
||||||
|
char *c;
|
||||||
|
|
||||||
|
if ((c = strchr(iface, '+'))) {
|
||||||
|
if (*(c + 1) != '\0') {
|
||||||
|
ebt_print_error("Spurious characters after '+' wildcard for '%s'", option);
|
||||||
|
return -1;
|
||||||
|
} else
|
||||||
|
*c = IF_WILDCARD;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ebt_early_init_once()
|
||||||
|
{
|
||||||
|
ebt_iterate_matches(merge_match);
|
||||||
|
ebt_iterate_targets(merge_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We use exec_style instead of #ifdef's because ebtables.so is a shared object. */
|
||||||
|
int do_command(int argc, char *argv[], struct ebt_u_replace *replace_)
|
||||||
|
{
|
||||||
|
char *buffer;
|
||||||
|
int c, i;
|
||||||
|
int policy = 0;
|
||||||
|
int rule_nr = 0;
|
||||||
|
int rule_nr_end = 0;
|
||||||
|
struct ebt_u_target *t;
|
||||||
|
struct ebt_u_match *m;
|
||||||
|
struct ebt_u_match_list *m_l;
|
||||||
|
struct ebt_u_entries *entries;
|
||||||
|
|
||||||
|
opterr = 0;
|
||||||
|
|
||||||
|
replace = replace_;
|
||||||
|
|
||||||
|
replace->flags = 0;
|
||||||
|
replace->selected_chain = -1;
|
||||||
|
replace->command = 'h';
|
||||||
|
|
||||||
|
if (!new_entry) {
|
||||||
|
new_entry = (struct ebt_u_entry *)malloc(sizeof(struct ebt_u_entry));
|
||||||
|
if (!new_entry)
|
||||||
|
ebt_print_memory();
|
||||||
|
}
|
||||||
|
/* Put some sane values in our new entry */
|
||||||
|
ebt_initialize_entry(new_entry);
|
||||||
|
new_entry->replace = replace;
|
||||||
|
|
||||||
|
/* The scenario induced by this loop makes that:
|
||||||
|
* '-t' and '-M' (if specified) have to come
|
||||||
|
* before '-A' and the like */
|
||||||
|
|
||||||
|
/* Getopt saves the day */
|
||||||
|
while ((c = getopt_long(argc, argv,
|
||||||
|
"-A:D:I:N:E:X::L::F::P:Vhi:o:j:p:s:d:t:", ebt_options, NULL)) != -1) {
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'A': /* Add a rule */
|
||||||
|
case 'D': /* Delete a rule */
|
||||||
|
case 'P': /* Define policy */
|
||||||
|
case 'I': /* Insert a rule */
|
||||||
|
case 'N': /* Make a user defined chain */
|
||||||
|
case 'E': /* Rename chain */
|
||||||
|
case 'X': /* Delete chain */
|
||||||
|
/* We allow -N chainname -P policy */
|
||||||
|
if (replace->command == 'N' && c == 'P') {
|
||||||
|
replace->command = c;
|
||||||
|
optind--; /* No table specified */
|
||||||
|
goto handle_P;
|
||||||
|
}
|
||||||
|
if (OPT_COMMANDS)
|
||||||
|
ebt_print_error2("Multiple commands are not allowed");
|
||||||
|
|
||||||
|
replace->command = c;
|
||||||
|
replace->flags |= OPT_COMMAND;
|
||||||
|
ebt_get_kernel_table(replace);
|
||||||
|
if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!")))
|
||||||
|
ebt_print_error2("No chain name specified");
|
||||||
|
if (c == 'N') {
|
||||||
|
if (ebt_get_chainnr(replace, optarg) != -1)
|
||||||
|
ebt_print_error2("Chain %s already exists", optarg);
|
||||||
|
else if (ebt_find_target(optarg))
|
||||||
|
ebt_print_error2("Target with name %s exists", optarg);
|
||||||
|
else if (strlen(optarg) >= EBT_CHAIN_MAXNAMELEN)
|
||||||
|
ebt_print_error2("Chain name length can't exceed %d",
|
||||||
|
EBT_CHAIN_MAXNAMELEN - 1);
|
||||||
|
else if (strchr(optarg, ' ') != NULL)
|
||||||
|
ebt_print_error2("Use of ' ' not allowed in chain names");
|
||||||
|
ebt_new_chain(replace, optarg, EBT_ACCEPT);
|
||||||
|
/* This is needed to get -N x -P y working */
|
||||||
|
replace->selected_chain = ebt_get_chainnr(replace, optarg);
|
||||||
|
break;
|
||||||
|
} else if (c == 'X') {
|
||||||
|
if (optind >= argc) {
|
||||||
|
replace->selected_chain = -1;
|
||||||
|
ebt_delete_chain(replace);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind < argc - 1)
|
||||||
|
ebt_print_error2("No extra options allowed with -X");
|
||||||
|
|
||||||
|
if ((replace->selected_chain = ebt_get_chainnr(replace, argv[optind])) == -1)
|
||||||
|
ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
|
||||||
|
ebt_delete_chain(replace);
|
||||||
|
optind++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((replace->selected_chain = ebt_get_chainnr(replace, optarg)) == -1)
|
||||||
|
ebt_print_error2("Chain '%s' doesn't exist", optarg);
|
||||||
|
if (c == 'E') {
|
||||||
|
if (optind >= argc)
|
||||||
|
ebt_print_error2("No new chain name specified");
|
||||||
|
else if (optind < argc - 1)
|
||||||
|
ebt_print_error2("No extra options allowed with -E");
|
||||||
|
else if (strlen(argv[optind]) >= EBT_CHAIN_MAXNAMELEN)
|
||||||
|
ebt_print_error2("Chain name length can't exceed %d characters", EBT_CHAIN_MAXNAMELEN - 1);
|
||||||
|
else if (ebt_get_chainnr(replace, argv[optind]) != -1)
|
||||||
|
ebt_print_error2("Chain '%s' already exists", argv[optind]);
|
||||||
|
else if (ebt_find_target(argv[optind]))
|
||||||
|
ebt_print_error2("Target with name '%s' exists", argv[optind]);
|
||||||
|
else if (strchr(argv[optind], ' ') != NULL)
|
||||||
|
ebt_print_error2("Use of ' ' not allowed in chain names");
|
||||||
|
ebt_rename_chain(replace, argv[optind]);
|
||||||
|
optind++;
|
||||||
|
break;
|
||||||
|
} else if (c == 'D' && optind < argc && (argv[optind][0] != '-' || (argv[optind][1] >= '0' && argv[optind][1] <= '9'))) {
|
||||||
|
if (optind != argc - 1)
|
||||||
|
ebt_print_error2("No extra options allowed with -D start_nr[:end_nr]");
|
||||||
|
if (parse_rule_range(argv[optind], &rule_nr, &rule_nr_end))
|
||||||
|
ebt_print_error2("Problem with the specified rule number(s) '%s'", argv[optind]);
|
||||||
|
optind++;
|
||||||
|
} else if (c == 'I') {
|
||||||
|
if (optind >= argc || (argv[optind][0] == '-' && (argv[optind][1] < '0' || argv[optind][1] > '9')))
|
||||||
|
rule_nr = 1;
|
||||||
|
else {
|
||||||
|
rule_nr = strtol(argv[optind], &buffer, 10);
|
||||||
|
if (*buffer != '\0')
|
||||||
|
ebt_print_error2("Problem with the specified rule number '%s'", argv[optind]);
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
} else if (c == 'P') {
|
||||||
|
handle_P:
|
||||||
|
if (optind >= argc)
|
||||||
|
ebt_print_error2("No policy specified");
|
||||||
|
for (i = 0; i < NUM_STANDARD_TARGETS; i++)
|
||||||
|
if (!strcmp(argv[optind], ebt_standard_targets[i])) {
|
||||||
|
policy = -i -1;
|
||||||
|
if (policy == EBT_CONTINUE)
|
||||||
|
ebt_print_error2("Wrong policy '%s'", argv[optind]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i == NUM_STANDARD_TARGETS)
|
||||||
|
ebt_print_error2("Unknown policy '%s'", argv[optind]);
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'L': /* List */
|
||||||
|
case 'F': /* Flush */
|
||||||
|
if (replace->flags & OPT_COMMAND)
|
||||||
|
ebt_print_error2("Multiple commands are not allowed");
|
||||||
|
replace->command = c;
|
||||||
|
replace->flags |= OPT_COMMAND;
|
||||||
|
|
||||||
|
ebt_get_kernel_table(replace);
|
||||||
|
i = -1;
|
||||||
|
if (optind < argc && argv[optind][0] != '-') {
|
||||||
|
if ((i = ebt_get_chainnr(replace, argv[optind])) == -1)
|
||||||
|
ebt_print_error2("Chain '%s' doesn't exist", argv[optind]);
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
if (i != -1)
|
||||||
|
replace->selected_chain = i;
|
||||||
|
break;
|
||||||
|
case 'V': /* Version */
|
||||||
|
if (OPT_COMMANDS)
|
||||||
|
ebt_print_error2("Multiple commands are not allowed");
|
||||||
|
replace->command = 'V';
|
||||||
|
PRINT_VERSION;
|
||||||
|
exit(0);
|
||||||
|
case 'h': /* Help */
|
||||||
|
if (OPT_COMMANDS)
|
||||||
|
ebt_print_error2("Multiple commands are not allowed");
|
||||||
|
replace->command = 'h';
|
||||||
|
|
||||||
|
/* All other arguments should be extension names */
|
||||||
|
while (optind < argc) {
|
||||||
|
struct ebt_u_match *m;
|
||||||
|
|
||||||
|
if (!strcasecmp("list_extensions", argv[optind])) {
|
||||||
|
ebt_list_extensions();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if ((m = ebt_find_match(argv[optind])))
|
||||||
|
ebt_add_match(new_entry, m);
|
||||||
|
else {
|
||||||
|
if (!(t = ebt_find_target(argv[optind])))
|
||||||
|
ebt_print_error2("Extension '%s' not found", argv[optind]);
|
||||||
|
if (replace->flags & OPT_JUMP)
|
||||||
|
ebt_print_error2("Sorry, you can only see help for one target extension at a time");
|
||||||
|
replace->flags |= OPT_JUMP;
|
||||||
|
new_entry->t = (struct ebt_entry_target *)t;
|
||||||
|
}
|
||||||
|
optind++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't': /* Table */
|
||||||
|
if (OPT_COMMANDS)
|
||||||
|
ebt_print_error2("Please put the -t option first");
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_TABLE);
|
||||||
|
if (strlen(optarg) > EBT_TABLE_MAXNAMELEN - 1)
|
||||||
|
ebt_print_error2("Table name length cannot exceed %d characters", EBT_TABLE_MAXNAMELEN - 1);
|
||||||
|
strcpy(replace->name, optarg);
|
||||||
|
break;
|
||||||
|
case 'i': /* Input interface */
|
||||||
|
case 2 : /* Logical input interface */
|
||||||
|
case 'o': /* Output interface */
|
||||||
|
case 3 : /* Logical output interface */
|
||||||
|
case 'j': /* Target */
|
||||||
|
case 'p': /* Net family protocol */
|
||||||
|
case 's': /* Source mac */
|
||||||
|
case 'd': /* Destination mac */
|
||||||
|
if (!OPT_COMMANDS)
|
||||||
|
ebt_print_error2("No command specified");
|
||||||
|
if (replace->command != 'A' && replace->command != 'D' && replace->command != 'I')
|
||||||
|
ebt_print_error2("Command and option do not match");
|
||||||
|
if (c == 'i') {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_IN);
|
||||||
|
if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
|
||||||
|
ebt_print_error2("Use -i only in INPUT, FORWARD, PREROUTING and BROUTING chains");
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_IIN;
|
||||||
|
|
||||||
|
if (strlen(optarg) >= IFNAMSIZ)
|
||||||
|
big_iface_length:
|
||||||
|
ebt_print_error2("Interface name length cannot exceed %d characters", IFNAMSIZ - 1);
|
||||||
|
strcpy(new_entry->in, optarg);
|
||||||
|
if (parse_iface(new_entry->in, "-i"))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
} else if (c == 2) {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_LOGICALIN);
|
||||||
|
if (replace->selected_chain > 2 && replace->selected_chain < NF_BR_BROUTING)
|
||||||
|
ebt_print_error2("Use --logical-in only in INPUT, FORWARD, PREROUTING and BROUTING chains");
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_ILOGICALIN;
|
||||||
|
|
||||||
|
if (strlen(optarg) >= IFNAMSIZ)
|
||||||
|
goto big_iface_length;
|
||||||
|
strcpy(new_entry->logical_in, optarg);
|
||||||
|
if (parse_iface(new_entry->logical_in, "--logical-in"))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
} else if (c == 'o') {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_OUT);
|
||||||
|
if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
|
||||||
|
ebt_print_error2("Use -o only in OUTPUT, FORWARD and POSTROUTING chains");
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_IOUT;
|
||||||
|
|
||||||
|
if (strlen(optarg) >= IFNAMSIZ)
|
||||||
|
goto big_iface_length;
|
||||||
|
strcpy(new_entry->out, optarg);
|
||||||
|
if (parse_iface(new_entry->out, "-o"))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
} else if (c == 3) {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_LOGICALOUT);
|
||||||
|
if (replace->selected_chain < 2 || replace->selected_chain == NF_BR_BROUTING)
|
||||||
|
ebt_print_error2("Use --logical-out only in OUTPUT, FORWARD and POSTROUTING chains");
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_ILOGICALOUT;
|
||||||
|
|
||||||
|
if (strlen(optarg) >= IFNAMSIZ)
|
||||||
|
goto big_iface_length;
|
||||||
|
strcpy(new_entry->logical_out, optarg);
|
||||||
|
if (parse_iface(new_entry->logical_out, "--logical-out"))
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
} else if (c == 'j') {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_JUMP);
|
||||||
|
for (i = 0; i < NUM_STANDARD_TARGETS; i++)
|
||||||
|
if (!strcmp(optarg, ebt_standard_targets[i])) {
|
||||||
|
t = ebt_find_target(EBT_STANDARD_TARGET);
|
||||||
|
((struct ebt_standard_target *) t->t)->verdict = -i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (-i - 1 == EBT_RETURN && replace->selected_chain < NF_BR_NUMHOOKS) {
|
||||||
|
ebt_print_error2("Return target only for user defined chains");
|
||||||
|
} else if (i != NUM_STANDARD_TARGETS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if ((i = ebt_get_chainnr(replace, optarg)) != -1) {
|
||||||
|
if (i < NF_BR_NUMHOOKS)
|
||||||
|
ebt_print_error2("Don't jump to a standard chain");
|
||||||
|
t = ebt_find_target(EBT_STANDARD_TARGET);
|
||||||
|
((struct ebt_standard_target *) t->t)->verdict = i - NF_BR_NUMHOOKS;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Must be an extension then */
|
||||||
|
struct ebt_u_target *t;
|
||||||
|
|
||||||
|
t = ebt_find_target(optarg);
|
||||||
|
/* -j standard not allowed either */
|
||||||
|
if (!t || t == (struct ebt_u_target *)new_entry->t)
|
||||||
|
ebt_print_error2("Illegal target name '%s'", optarg);
|
||||||
|
new_entry->t = (struct ebt_entry_target *)t;
|
||||||
|
ebt_find_target(EBT_STANDARD_TARGET)->used = 0;
|
||||||
|
t->used = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} else if (c == 's') {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_SOURCE);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_ISOURCE;
|
||||||
|
|
||||||
|
if (ebt_get_mac_and_mask(optarg, new_entry->sourcemac, new_entry->sourcemsk))
|
||||||
|
ebt_print_error2("Problem with specified source mac '%s'", optarg);
|
||||||
|
new_entry->bitmask |= EBT_SOURCEMAC;
|
||||||
|
break;
|
||||||
|
} else if (c == 'd') {
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_DEST);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_IDEST;
|
||||||
|
|
||||||
|
if (ebt_get_mac_and_mask(optarg, new_entry->destmac, new_entry->destmsk))
|
||||||
|
ebt_print_error2("Problem with specified destination mac '%s'", optarg);
|
||||||
|
new_entry->bitmask |= EBT_DESTMAC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ebt_check_option2(&(replace->flags), OPT_PROTOCOL);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
new_entry->invflags |= EBT_IPROTO;
|
||||||
|
|
||||||
|
new_entry->bitmask &= ~((unsigned int)EBT_NOPROTO);
|
||||||
|
i = strtol(optarg, &buffer, 16);
|
||||||
|
if (*buffer == '\0' && (i < 0 || i > 0xFFFF))
|
||||||
|
ebt_print_error2("Problem with the specified protocol");
|
||||||
|
if (*buffer != '\0') {
|
||||||
|
const struct ethertypeent *ent;
|
||||||
|
|
||||||
|
if (!strcasecmp(optarg, "LENGTH")) {
|
||||||
|
new_entry->bitmask |= EBT_802_3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ent = getethertypebyname(optarg);
|
||||||
|
if (!ent)
|
||||||
|
ebt_print_error2("Problem with the specified Ethernet protocol '%s'", optarg);
|
||||||
|
new_entry->ethproto = ent->e_ethertype;
|
||||||
|
} else
|
||||||
|
new_entry->ethproto = i;
|
||||||
|
|
||||||
|
if (new_entry->ethproto < 0x0600)
|
||||||
|
ebt_print_error2("Sorry, protocols have values above or equal to 0x0600");
|
||||||
|
break;
|
||||||
|
case 1 :
|
||||||
|
if (!strcmp(optarg, "!"))
|
||||||
|
ebt_check_inverse2(optarg);
|
||||||
|
else
|
||||||
|
ebt_print_error2("Bad argument : '%s'", optarg);
|
||||||
|
/* ebt_check_inverse() did optind++ */
|
||||||
|
optind--;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
/* Is it a target option? */
|
||||||
|
t = (struct ebt_u_target *)new_entry->t;
|
||||||
|
if ((t->parse(c - t->option_offset, argv, argc, new_entry, &t->flags, &t->t))) {
|
||||||
|
goto check_extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is it a match_option? */
|
||||||
|
for (m = ebt_matches; m; m = m->next)
|
||||||
|
if (m->parse(c - m->option_offset, argv, argc, new_entry, &m->flags, &m->m))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (m != NULL) {
|
||||||
|
if (m->used == 0) {
|
||||||
|
ebt_add_match(new_entry, m);
|
||||||
|
m->used = 1;
|
||||||
|
}
|
||||||
|
goto check_extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '?')
|
||||||
|
ebt_print_error2("Unknown argument: '%s'", argv[optind - 1], (char)optopt, (char)c);
|
||||||
|
else {
|
||||||
|
if (!strcmp(t->name, "standard"))
|
||||||
|
ebt_print_error2("Unknown argument: don't forget the -t option");
|
||||||
|
else
|
||||||
|
ebt_print_error2("Target-specific option does not correspond with specified target");
|
||||||
|
}
|
||||||
|
check_extension:
|
||||||
|
if (replace->command != 'A' && replace->command != 'I' &&
|
||||||
|
replace->command != 'D')
|
||||||
|
ebt_print_error2("Extensions only for -A, -I and -D");
|
||||||
|
}
|
||||||
|
ebt_invert = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(table = ebt_find_table(replace->name)))
|
||||||
|
ebt_print_error2("Bad table name");
|
||||||
|
|
||||||
|
if (replace->command == 'h') {
|
||||||
|
print_help();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the final checks */
|
||||||
|
if (replace->command == 'A' || replace->command == 'I' ||
|
||||||
|
replace->command == 'D') {
|
||||||
|
/* This will put the hook_mask right for the chains */
|
||||||
|
ebt_check_for_loops(replace);
|
||||||
|
entries = ebt_to_chain(replace);
|
||||||
|
m_l = new_entry->m_list;
|
||||||
|
t = (struct ebt_u_target *)new_entry->t;
|
||||||
|
while (m_l) {
|
||||||
|
m = (struct ebt_u_match *)(m_l->m);
|
||||||
|
m->final_check(new_entry, m->m, replace->name,
|
||||||
|
entries->hook_mask, 0);
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
t->final_check(new_entry, t->t, replace->name,
|
||||||
|
entries->hook_mask, 0);
|
||||||
|
}
|
||||||
|
/* So, the extensions can work with the host endian.
|
||||||
|
* The kernel does not have to do this of course */
|
||||||
|
new_entry->ethproto = htons(new_entry->ethproto);
|
||||||
|
|
||||||
|
if (replace->command == 'P') {
|
||||||
|
if (replace->selected_chain < NF_BR_NUMHOOKS && policy == EBT_RETURN)
|
||||||
|
ebt_print_error2("Policy RETURN only allowed for user defined chains");
|
||||||
|
ebt_change_policy(replace, policy);
|
||||||
|
} else if (replace->command == 'L') {
|
||||||
|
list_rules();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
if (replace->command == 'F') {
|
||||||
|
ebt_flush_chains(replace);
|
||||||
|
} else if (replace->command == 'A' || replace->command == 'I') {
|
||||||
|
ebt_add_rule(replace, new_entry, rule_nr);
|
||||||
|
if (rule_nr <= 0)
|
||||||
|
rule_nr--;
|
||||||
|
rule_nr_end = rule_nr;
|
||||||
|
|
||||||
|
/* a jump to a udc requires checking for loops */
|
||||||
|
if (!strcmp(new_entry->t->u.name, EBT_STANDARD_TARGET) &&
|
||||||
|
((struct ebt_standard_target *)(new_entry->t))->verdict >= 0) {
|
||||||
|
/* FIXME: this can be done faster */
|
||||||
|
ebt_check_for_loops(replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do the final_check(), for all entries.
|
||||||
|
* This is needed when adding a rule that has a chain target */
|
||||||
|
i = -1;
|
||||||
|
while (++i != replace->num_chains) {
|
||||||
|
struct ebt_u_entry *e;
|
||||||
|
|
||||||
|
entries = replace->chains[i];
|
||||||
|
if (!entries) {
|
||||||
|
if (i < NF_BR_NUMHOOKS)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
ebt_print_bug("whoops\n");
|
||||||
|
}
|
||||||
|
e = entries->entries->next;
|
||||||
|
while (e != entries->entries) {
|
||||||
|
/* Userspace extensions use host endian */
|
||||||
|
e->ethproto = ntohs(e->ethproto);
|
||||||
|
ebt_do_final_checks(replace, e, entries);
|
||||||
|
e->ethproto = htons(e->ethproto);
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Don't reuse the added rule */
|
||||||
|
new_entry = NULL;
|
||||||
|
} else if (replace->command == 'D') {
|
||||||
|
ebt_delete_rule(replace, new_entry, rule_nr, rule_nr_end);
|
||||||
|
}
|
||||||
|
/* Commands -N, -E, -X fall through */
|
||||||
|
|
||||||
|
if (table->check)
|
||||||
|
table->check(replace);
|
||||||
|
|
||||||
|
ebt_deliver_table(replace);
|
||||||
|
|
||||||
|
if (replace->nentries)
|
||||||
|
ebt_deliver_counters(replace);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,367 @@
|
||||||
|
/* 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
|
||||||
|
|
||||||
|
arpinfo->invflags = 0;
|
||||||
|
arpinfo->bitmask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define OPT_OPCODE 0x01
|
||||||
|
#define OPT_HTYPE 0x02
|
||||||
|
#define OPT_PTYPE 0x04
|
||||||
|
#define OPT_IP_S 0x08
|
||||||
|
#define OPT_IP_D 0x10
|
||||||
|
#define OPT_MAC_S 0x20
|
||||||
|
#define OPT_MAC_D 0x40
|
||||||
|
#define OPT_GRAT 0x80
|
||||||
|
static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
|
||||||
|
unsigned int *flags, struct ebt_entry_match **match)
|
||||||
|
{
|
||||||
|
struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)(*match)->data;
|
||||||
|
long int i;
|
||||||
|
char *end;
|
||||||
|
uint32_t *addr;
|
||||||
|
uint32_t *mask;
|
||||||
|
unsigned char *maddr;
|
||||||
|
unsigned char *mmask;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case ARP_OPCODE:
|
||||||
|
ebt_check_option2(flags, OPT_OPCODE);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
arpinfo->invflags |= EBT_ARP_OPCODE;
|
||||||
|
i = strtol(optarg, &end, 10);
|
||||||
|
if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
|
||||||
|
for (i = 0; i < NUMOPCODES; i++)
|
||||||
|
if (!strcasecmp(opcodes[i], optarg))
|
||||||
|
break;
|
||||||
|
if (i == NUMOPCODES)
|
||||||
|
ebt_print_error2("Problem with specified ARP opcode");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
arpinfo->opcode = htons(i);
|
||||||
|
arpinfo->bitmask |= EBT_ARP_OPCODE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARP_HTYPE:
|
||||||
|
ebt_check_option2(flags, OPT_HTYPE);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
arpinfo->invflags |= EBT_ARP_HTYPE;
|
||||||
|
i = strtol(optarg, &end, 10);
|
||||||
|
if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
|
||||||
|
if (!strcasecmp("Ethernet", argv[optind - 1]))
|
||||||
|
i = 1;
|
||||||
|
else
|
||||||
|
ebt_print_error2("Problem with specified ARP hardware type");
|
||||||
|
}
|
||||||
|
arpinfo->htype = htons(i);
|
||||||
|
arpinfo->bitmask |= EBT_ARP_HTYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARP_PTYPE:
|
||||||
|
{
|
||||||
|
uint16_t proto;
|
||||||
|
|
||||||
|
ebt_check_option2(flags, OPT_PTYPE);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
arpinfo->invflags |= EBT_ARP_PTYPE;
|
||||||
|
|
||||||
|
i = strtol(optarg, &end, 16);
|
||||||
|
if (i < 0 || i >= (0x1 << 16) || *end !='\0') {
|
||||||
|
const struct ethertypeent *ent;
|
||||||
|
|
||||||
|
ent = getethertypebyname(argv[optind - 1]);
|
||||||
|
if (!ent)
|
||||||
|
ebt_print_error2("Problem with specified ARP "
|
||||||
|
"protocol type");
|
||||||
|
proto = ent->e_ethertype;
|
||||||
|
|
||||||
|
} else
|
||||||
|
proto = i;
|
||||||
|
arpinfo->ptype = htons(proto);
|
||||||
|
arpinfo->bitmask |= EBT_ARP_PTYPE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ARP_IP_S:
|
||||||
|
case ARP_IP_D:
|
||||||
|
if (c == ARP_IP_S) {
|
||||||
|
ebt_check_option2(flags, OPT_IP_S);
|
||||||
|
addr = &arpinfo->saddr;
|
||||||
|
mask = &arpinfo->smsk;
|
||||||
|
arpinfo->bitmask |= EBT_ARP_SRC_IP;
|
||||||
|
} else {
|
||||||
|
ebt_check_option2(flags, OPT_IP_D);
|
||||||
|
addr = &arpinfo->daddr;
|
||||||
|
mask = &arpinfo->dmsk;
|
||||||
|
arpinfo->bitmask |= EBT_ARP_DST_IP;
|
||||||
|
}
|
||||||
|
if (ebt_check_inverse2(optarg)) {
|
||||||
|
if (c == ARP_IP_S)
|
||||||
|
arpinfo->invflags |= EBT_ARP_SRC_IP;
|
||||||
|
else
|
||||||
|
arpinfo->invflags |= EBT_ARP_DST_IP;
|
||||||
|
}
|
||||||
|
ebt_parse_ip_address(optarg, addr, mask);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARP_MAC_S:
|
||||||
|
case ARP_MAC_D:
|
||||||
|
if (c == ARP_MAC_S) {
|
||||||
|
ebt_check_option2(flags, OPT_MAC_S);
|
||||||
|
maddr = arpinfo->smaddr;
|
||||||
|
mmask = arpinfo->smmsk;
|
||||||
|
arpinfo->bitmask |= EBT_ARP_SRC_MAC;
|
||||||
|
} else {
|
||||||
|
ebt_check_option2(flags, OPT_MAC_D);
|
||||||
|
maddr = arpinfo->dmaddr;
|
||||||
|
mmask = arpinfo->dmmsk;
|
||||||
|
arpinfo->bitmask |= EBT_ARP_DST_MAC;
|
||||||
|
}
|
||||||
|
if (ebt_check_inverse2(optarg)) {
|
||||||
|
if (c == ARP_MAC_S)
|
||||||
|
arpinfo->invflags |= EBT_ARP_SRC_MAC;
|
||||||
|
else
|
||||||
|
arpinfo->invflags |= EBT_ARP_DST_MAC;
|
||||||
|
}
|
||||||
|
if (ebt_get_mac_and_mask(optarg, maddr, mmask))
|
||||||
|
ebt_print_error2("Problem with ARP MAC address argument");
|
||||||
|
break;
|
||||||
|
case ARP_GRAT:
|
||||||
|
ebt_check_option2(flags, OPT_GRAT);
|
||||||
|
arpinfo->bitmask |= EBT_ARP_GRAT;
|
||||||
|
if (ebt_invert)
|
||||||
|
arpinfo->invflags |= EBT_ARP_GRAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void final_check(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match, const char *name,
|
||||||
|
unsigned int hookmask, unsigned int time)
|
||||||
|
{
|
||||||
|
if ((entry->ethproto != ETH_P_ARP && entry->ethproto != ETH_P_RARP) ||
|
||||||
|
entry->invflags & EBT_IPROTO)
|
||||||
|
ebt_print_error("For (R)ARP filtering the protocol must be specified as ARP or RARP");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_arp_info *arpinfo = (struct ebt_arp_info *)match->data;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_OPCODE) {
|
||||||
|
int opcode = ntohs(arpinfo->opcode);
|
||||||
|
printf("--arp-op ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_OPCODE)
|
||||||
|
printf("! ");
|
||||||
|
if (opcode > 0 && opcode <= NUMOPCODES)
|
||||||
|
printf("%s ", opcodes[opcode - 1]);
|
||||||
|
else
|
||||||
|
printf("%d ", opcode);
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_HTYPE) {
|
||||||
|
printf("--arp-htype ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_HTYPE)
|
||||||
|
printf("! ");
|
||||||
|
printf("%d ", ntohs(arpinfo->htype));
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_PTYPE) {
|
||||||
|
const struct ethertypeent *ent;
|
||||||
|
|
||||||
|
printf("--arp-ptype ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_PTYPE)
|
||||||
|
printf("! ");
|
||||||
|
ent = getethertypebynumber(ntohs(arpinfo->ptype));
|
||||||
|
if (!ent)
|
||||||
|
printf("0x%x ", ntohs(arpinfo->ptype));
|
||||||
|
else
|
||||||
|
printf("%s ", ent->e_name);
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_SRC_IP) {
|
||||||
|
printf("--arp-ip-src ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_SRC_IP)
|
||||||
|
printf("! ");
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
printf("%d%s", ((unsigned char *)&arpinfo->saddr)[i],
|
||||||
|
(i == 3) ? "" : ".");
|
||||||
|
printf("%s ", ebt_mask_to_dotted(arpinfo->smsk));
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_DST_IP) {
|
||||||
|
printf("--arp-ip-dst ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_DST_IP)
|
||||||
|
printf("! ");
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
printf("%d%s", ((unsigned char *)&arpinfo->daddr)[i],
|
||||||
|
(i == 3) ? "" : ".");
|
||||||
|
printf("%s ", ebt_mask_to_dotted(arpinfo->dmsk));
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_SRC_MAC) {
|
||||||
|
printf("--arp-mac-src ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_SRC_MAC)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_mac_and_mask(arpinfo->smaddr, arpinfo->smmsk);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_DST_MAC) {
|
||||||
|
printf("--arp-mac-dst ");
|
||||||
|
if (arpinfo->invflags & EBT_ARP_DST_MAC)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_mac_and_mask(arpinfo->dmaddr, arpinfo->dmmsk);
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
if (arpinfo->bitmask & EBT_ARP_GRAT) {
|
||||||
|
if (arpinfo->invflags & EBT_ARP_GRAT)
|
||||||
|
printf("! ");
|
||||||
|
printf("--arp-gratuitous ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare(const struct ebt_entry_match *m1,
|
||||||
|
const struct ebt_entry_match *m2)
|
||||||
|
{
|
||||||
|
struct ebt_arp_info *arpinfo1 = (struct ebt_arp_info *)m1->data;
|
||||||
|
struct ebt_arp_info *arpinfo2 = (struct ebt_arp_info *)m2->data;
|
||||||
|
|
||||||
|
if (arpinfo1->bitmask != arpinfo2->bitmask)
|
||||||
|
return 0;
|
||||||
|
if (arpinfo1->invflags != arpinfo2->invflags)
|
||||||
|
return 0;
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_OPCODE) {
|
||||||
|
if (arpinfo1->opcode != arpinfo2->opcode)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_HTYPE) {
|
||||||
|
if (arpinfo1->htype != arpinfo2->htype)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_PTYPE) {
|
||||||
|
if (arpinfo1->ptype != arpinfo2->ptype)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_SRC_IP) {
|
||||||
|
if (arpinfo1->saddr != arpinfo2->saddr)
|
||||||
|
return 0;
|
||||||
|
if (arpinfo1->smsk != arpinfo2->smsk)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_DST_IP) {
|
||||||
|
if (arpinfo1->daddr != arpinfo2->daddr)
|
||||||
|
return 0;
|
||||||
|
if (arpinfo1->dmsk != arpinfo2->dmsk)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_SRC_MAC) {
|
||||||
|
if (memcmp(arpinfo1->smaddr, arpinfo2->smaddr, ETH_ALEN))
|
||||||
|
return 0;
|
||||||
|
if (memcmp(arpinfo1->smmsk, arpinfo2->smmsk, ETH_ALEN))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (arpinfo1->bitmask & EBT_ARP_DST_MAC) {
|
||||||
|
if (memcmp(arpinfo1->dmaddr, arpinfo2->dmaddr, ETH_ALEN))
|
||||||
|
return 0;
|
||||||
|
if (memcmp(arpinfo1->dmmsk, arpinfo2->dmmsk, ETH_ALEN))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ebt_u_match arp_match =
|
||||||
|
{
|
||||||
|
.name = "arp",
|
||||||
|
.size = sizeof(struct ebt_arp_info),
|
||||||
|
.help = print_help,
|
||||||
|
.init = init,
|
||||||
|
.parse = parse,
|
||||||
|
.final_check = final_check,
|
||||||
|
.print = print,
|
||||||
|
.compare = compare,
|
||||||
|
.extra_ops = opts,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_match(&arp_match);
|
||||||
|
}
|
|
@ -0,0 +1,474 @@
|
||||||
|
/* 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;
|
||||||
|
ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
|
||||||
|
|
||||||
|
if (ports[0] > ports[1])
|
||||||
|
ebt_print_error("Invalid portrange (min > max)");
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_port_range(uint16_t *ports)
|
||||||
|
{
|
||||||
|
if (ports[0] == ports[1])
|
||||||
|
printf("%d ", ports[0]);
|
||||||
|
else
|
||||||
|
printf("%d:%d ", ports[0], ports[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help()
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"ip options:\n"
|
||||||
|
"--ip-src [!] address[/mask]: ip source specification\n"
|
||||||
|
"--ip-dst [!] address[/mask]: ip destination specification\n"
|
||||||
|
"--ip-tos [!] tos : ip tos specification\n"
|
||||||
|
"--ip-proto [!] protocol : ip protocol specification\n"
|
||||||
|
"--ip-sport [!] port[:port] : tcp/udp source port or port range\n"
|
||||||
|
"--ip-dport [!] port[:port] : tcp/udp destination port or port range\n"
|
||||||
|
"--ip-icmp-type [!] type[[:type]/code[:code]] : icmp type/code or type/code range\n"
|
||||||
|
"--ip-igmp-type [!] type[:type] : igmp type or type range\n");
|
||||||
|
|
||||||
|
printf("\nValid ICMP Types:\n");
|
||||||
|
ebt_print_icmp_types(icmp_codes, ARRAY_SIZE(icmp_codes));
|
||||||
|
printf("\nValid IGMP Types:\n");
|
||||||
|
ebt_print_icmp_types(igmp_types, ARRAY_SIZE(igmp_types));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
|
||||||
|
|
||||||
|
ipinfo->invflags = 0;
|
||||||
|
ipinfo->bitmask = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OPT_SOURCE 0x01
|
||||||
|
#define OPT_DEST 0x02
|
||||||
|
#define OPT_TOS 0x04
|
||||||
|
#define OPT_PROTO 0x08
|
||||||
|
#define OPT_SPORT 0x10
|
||||||
|
#define OPT_DPORT 0x20
|
||||||
|
#define OPT_ICMP 0x40
|
||||||
|
#define OPT_IGMP 0x80
|
||||||
|
static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
|
||||||
|
unsigned int *flags, struct ebt_entry_match **match)
|
||||||
|
{
|
||||||
|
struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)(*match)->data;
|
||||||
|
char *end;
|
||||||
|
long int i;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case IP_SOURCE:
|
||||||
|
ebt_check_option2(flags, OPT_SOURCE);
|
||||||
|
ipinfo->bitmask |= EBT_IP_SOURCE;
|
||||||
|
|
||||||
|
case IP_DEST:
|
||||||
|
if (c == IP_DEST) {
|
||||||
|
ebt_check_option2(flags, OPT_DEST);
|
||||||
|
ipinfo->bitmask |= EBT_IP_DEST;
|
||||||
|
}
|
||||||
|
if (ebt_check_inverse2(optarg)) {
|
||||||
|
if (c == IP_SOURCE)
|
||||||
|
ipinfo->invflags |= EBT_IP_SOURCE;
|
||||||
|
else
|
||||||
|
ipinfo->invflags |= EBT_IP_DEST;
|
||||||
|
}
|
||||||
|
if (c == IP_SOURCE)
|
||||||
|
ebt_parse_ip_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
|
||||||
|
else
|
||||||
|
ebt_parse_ip_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_SPORT:
|
||||||
|
case IP_DPORT:
|
||||||
|
if (c == IP_SPORT) {
|
||||||
|
ebt_check_option2(flags, OPT_SPORT);
|
||||||
|
ipinfo->bitmask |= EBT_IP_SPORT;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_SPORT;
|
||||||
|
} else {
|
||||||
|
ebt_check_option2(flags, OPT_DPORT);
|
||||||
|
ipinfo->bitmask |= EBT_IP_DPORT;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_DPORT;
|
||||||
|
}
|
||||||
|
if (c == IP_SPORT)
|
||||||
|
parse_port_range(NULL, optarg, ipinfo->sport);
|
||||||
|
else
|
||||||
|
parse_port_range(NULL, optarg, ipinfo->dport);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_ICMP:
|
||||||
|
ebt_check_option2(flags, OPT_ICMP);
|
||||||
|
ipinfo->bitmask |= EBT_IP_ICMP;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_ICMP;
|
||||||
|
if (ebt_parse_icmp(icmp_codes, ARRAY_SIZE(icmp_codes), optarg,
|
||||||
|
ipinfo->icmp_type, ipinfo->icmp_code))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_IGMP:
|
||||||
|
ebt_check_option2(flags, OPT_IGMP);
|
||||||
|
ipinfo->bitmask |= EBT_IP_IGMP;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_IGMP;
|
||||||
|
if (ebt_parse_icmp(igmp_types, ARRAY_SIZE(igmp_types), optarg,
|
||||||
|
ipinfo->igmp_type, NULL))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_myTOS:
|
||||||
|
ebt_check_option2(flags, OPT_TOS);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_TOS;
|
||||||
|
i = strtol(optarg, &end, 16);
|
||||||
|
if (i < 0 || i > 255 || *end != '\0')
|
||||||
|
ebt_print_error2("Problem with specified IP tos");
|
||||||
|
ipinfo->tos = i;
|
||||||
|
ipinfo->bitmask |= EBT_IP_TOS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_PROTO:
|
||||||
|
ebt_check_option2(flags, OPT_PROTO);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP_PROTO;
|
||||||
|
i = strtoul(optarg, &end, 10);
|
||||||
|
if (*end != '\0') {
|
||||||
|
struct protoent *pe;
|
||||||
|
|
||||||
|
pe = getprotobyname(optarg);
|
||||||
|
if (pe == NULL)
|
||||||
|
ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
|
||||||
|
ipinfo->protocol = pe->p_proto;
|
||||||
|
} else {
|
||||||
|
ipinfo->protocol = (unsigned char) i;
|
||||||
|
}
|
||||||
|
ipinfo->bitmask |= EBT_IP_PROTO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void final_check(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match, const char *name,
|
||||||
|
unsigned int hookmask, unsigned int time)
|
||||||
|
{
|
||||||
|
struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
|
||||||
|
|
||||||
|
if (entry->ethproto != ETH_P_IP || entry->invflags & EBT_IPROTO) {
|
||||||
|
ebt_print_error("For IP filtering the protocol must be "
|
||||||
|
"specified as IPv4");
|
||||||
|
} else if (ipinfo->bitmask & (EBT_IP_SPORT|EBT_IP_DPORT) &&
|
||||||
|
(!(ipinfo->bitmask & EBT_IP_PROTO) ||
|
||||||
|
ipinfo->invflags & EBT_IP_PROTO ||
|
||||||
|
(ipinfo->protocol!=IPPROTO_TCP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_UDP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_SCTP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_DCCP))) {
|
||||||
|
ebt_print_error("For port filtering the IP protocol must be "
|
||||||
|
"either 6 (tcp), 17 (udp), 33 (dccp) or "
|
||||||
|
"132 (sctp)");
|
||||||
|
} else if ((ipinfo->bitmask & EBT_IP_ICMP) &&
|
||||||
|
(!(ipinfo->bitmask & EBT_IP_PROTO) ||
|
||||||
|
ipinfo->invflags & EBT_IP_PROTO ||
|
||||||
|
ipinfo->protocol != IPPROTO_ICMP)) {
|
||||||
|
ebt_print_error("For ICMP filtering the IP protocol must be "
|
||||||
|
"1 (icmp)");
|
||||||
|
} else if ((ipinfo->bitmask & EBT_IP_IGMP) &&
|
||||||
|
(!(ipinfo->bitmask & EBT_IP_PROTO) ||
|
||||||
|
ipinfo->invflags & EBT_IP_PROTO ||
|
||||||
|
ipinfo->protocol != IPPROTO_IGMP)) {
|
||||||
|
ebt_print_error("For IGMP filtering the IP protocol must be "
|
||||||
|
"2 (igmp)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_ip_info *ipinfo = (struct ebt_ip_info *)match->data;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
if (ipinfo->bitmask & EBT_IP_SOURCE) {
|
||||||
|
printf("--ip-src ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_SOURCE)
|
||||||
|
printf("! ");
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
printf("%d%s",((unsigned char *)&ipinfo->saddr)[j],
|
||||||
|
(j == 3) ? "" : ".");
|
||||||
|
printf("%s ", ebt_mask_to_dotted(ipinfo->smsk));
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_DEST) {
|
||||||
|
printf("--ip-dst ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_DEST)
|
||||||
|
printf("! ");
|
||||||
|
for (j = 0; j < 4; j++)
|
||||||
|
printf("%d%s", ((unsigned char *)&ipinfo->daddr)[j],
|
||||||
|
(j == 3) ? "" : ".");
|
||||||
|
printf("%s ", ebt_mask_to_dotted(ipinfo->dmsk));
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_TOS) {
|
||||||
|
printf("--ip-tos ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_TOS)
|
||||||
|
printf("! ");
|
||||||
|
printf("0x%02X ", ipinfo->tos);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_PROTO) {
|
||||||
|
struct protoent *pe;
|
||||||
|
|
||||||
|
printf("--ip-proto ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_PROTO)
|
||||||
|
printf("! ");
|
||||||
|
pe = getprotobynumber(ipinfo->protocol);
|
||||||
|
if (pe == NULL) {
|
||||||
|
printf("%d ", ipinfo->protocol);
|
||||||
|
} else {
|
||||||
|
printf("%s ", pe->p_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_SPORT) {
|
||||||
|
printf("--ip-sport ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_SPORT)
|
||||||
|
printf("! ");
|
||||||
|
print_port_range(ipinfo->sport);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_DPORT) {
|
||||||
|
printf("--ip-dport ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_DPORT)
|
||||||
|
printf("! ");
|
||||||
|
print_port_range(ipinfo->dport);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_ICMP) {
|
||||||
|
printf("--ip-icmp-type ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_ICMP)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_icmp_type(icmp_codes, ARRAY_SIZE(icmp_codes),
|
||||||
|
ipinfo->icmp_type, ipinfo->icmp_code);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP_IGMP) {
|
||||||
|
printf("--ip-igmp-type ");
|
||||||
|
if (ipinfo->invflags & EBT_IP_IGMP)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_icmp_type(igmp_types, ARRAY_SIZE(igmp_types),
|
||||||
|
ipinfo->igmp_type, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare(const struct ebt_entry_match *m1,
|
||||||
|
const struct ebt_entry_match *m2)
|
||||||
|
{
|
||||||
|
struct ebt_ip_info *ipinfo1 = (struct ebt_ip_info *)m1->data;
|
||||||
|
struct ebt_ip_info *ipinfo2 = (struct ebt_ip_info *)m2->data;
|
||||||
|
|
||||||
|
if (ipinfo1->bitmask != ipinfo2->bitmask)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->invflags != ipinfo2->invflags)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_SOURCE) {
|
||||||
|
if (ipinfo1->saddr != ipinfo2->saddr)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->smsk != ipinfo2->smsk)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_DEST) {
|
||||||
|
if (ipinfo1->daddr != ipinfo2->daddr)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->dmsk != ipinfo2->dmsk)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_TOS) {
|
||||||
|
if (ipinfo1->tos != ipinfo2->tos)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_PROTO) {
|
||||||
|
if (ipinfo1->protocol != ipinfo2->protocol)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_SPORT) {
|
||||||
|
if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
|
||||||
|
ipinfo1->sport[1] != ipinfo2->sport[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_DPORT) {
|
||||||
|
if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
|
||||||
|
ipinfo1->dport[1] != ipinfo2->dport[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_ICMP) {
|
||||||
|
if (ipinfo1->icmp_type[0] != ipinfo2->icmp_type[0] ||
|
||||||
|
ipinfo1->icmp_type[1] != ipinfo2->icmp_type[1] ||
|
||||||
|
ipinfo1->icmp_code[0] != ipinfo2->icmp_code[0] ||
|
||||||
|
ipinfo1->icmp_code[1] != ipinfo2->icmp_code[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP_IGMP) {
|
||||||
|
if (ipinfo1->igmp_type[0] != ipinfo2->igmp_type[0] ||
|
||||||
|
ipinfo1->igmp_type[1] != ipinfo2->igmp_type[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ebt_u_match ip_match =
|
||||||
|
{
|
||||||
|
.name = "ip",
|
||||||
|
.size = sizeof(struct ebt_ip_info),
|
||||||
|
.help = print_help,
|
||||||
|
.init = init,
|
||||||
|
.parse = parse,
|
||||||
|
.final_check = final_check,
|
||||||
|
.print = print,
|
||||||
|
.compare = compare,
|
||||||
|
.extra_ops = opts,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_match(&ip_match);
|
||||||
|
}
|
|
@ -0,0 +1,415 @@
|
||||||
|
/* 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;
|
||||||
|
ports[1] = cp[0] ? parse_port(protocol, cp) : 0xFFFF;
|
||||||
|
|
||||||
|
if (ports[0] > ports[1])
|
||||||
|
ebt_print_error("Invalid portrange (min > max)");
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_port_range(uint16_t *ports)
|
||||||
|
{
|
||||||
|
if (ports[0] == ports[1])
|
||||||
|
printf("%d ", ports[0]);
|
||||||
|
else
|
||||||
|
printf("%d:%d ", ports[0], ports[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_help()
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"ip6 options:\n"
|
||||||
|
"--ip6-src [!] address[/mask]: ipv6 source specification\n"
|
||||||
|
"--ip6-dst [!] address[/mask]: ipv6 destination specification\n"
|
||||||
|
"--ip6-tclass [!] tclass : ipv6 traffic class specification\n"
|
||||||
|
"--ip6-proto [!] protocol : ipv6 protocol specification\n"
|
||||||
|
"--ip6-sport [!] port[:port] : tcp/udp source port or port range\n"
|
||||||
|
"--ip6-dport [!] port[:port] : tcp/udp destination port or port range\n"
|
||||||
|
"--ip6-icmp-type [!] type[[:type]/code[:code]] : ipv6-icmp type/code or type/code range\n");
|
||||||
|
|
||||||
|
printf("\nValid ICMPv6 Types:\n");
|
||||||
|
ebt_print_icmp_types(icmpv6_codes, ARRAY_SIZE(icmpv6_codes));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init(struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
|
||||||
|
|
||||||
|
ipinfo->invflags = 0;
|
||||||
|
ipinfo->bitmask = 0;
|
||||||
|
memset(ipinfo->saddr.s6_addr, 0, sizeof(ipinfo->saddr.s6_addr));
|
||||||
|
memset(ipinfo->smsk.s6_addr, 0, sizeof(ipinfo->smsk.s6_addr));
|
||||||
|
memset(ipinfo->daddr.s6_addr, 0, sizeof(ipinfo->daddr.s6_addr));
|
||||||
|
memset(ipinfo->dmsk.s6_addr, 0, sizeof(ipinfo->dmsk.s6_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OPT_SOURCE 0x01
|
||||||
|
#define OPT_DEST 0x02
|
||||||
|
#define OPT_TCLASS 0x04
|
||||||
|
#define OPT_PROTO 0x08
|
||||||
|
#define OPT_SPORT 0x10
|
||||||
|
#define OPT_DPORT 0x20
|
||||||
|
static int parse(int c, char **argv, int argc, const struct ebt_u_entry *entry,
|
||||||
|
unsigned int *flags, struct ebt_entry_match **match)
|
||||||
|
{
|
||||||
|
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)(*match)->data;
|
||||||
|
char *end;
|
||||||
|
long int i;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case IP_SOURCE:
|
||||||
|
ebt_check_option2(flags, OPT_SOURCE);
|
||||||
|
ipinfo->bitmask |= EBT_IP6_SOURCE;
|
||||||
|
if (ebt_check_inverse2(optarg)) {
|
||||||
|
ipinfo->invflags |= EBT_IP6_SOURCE;
|
||||||
|
}
|
||||||
|
ebt_parse_ip6_address(optarg, &ipinfo->saddr, &ipinfo->smsk);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_DEST:
|
||||||
|
ebt_check_option2(flags, OPT_DEST);
|
||||||
|
ipinfo->bitmask |= EBT_IP6_DEST;
|
||||||
|
if (ebt_check_inverse2(optarg)) {
|
||||||
|
ipinfo->invflags |= EBT_IP6_DEST;
|
||||||
|
}
|
||||||
|
ebt_parse_ip6_address(optarg, &ipinfo->daddr, &ipinfo->dmsk);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_SPORT:
|
||||||
|
case IP_DPORT:
|
||||||
|
if (c == IP_SPORT) {
|
||||||
|
ebt_check_option2(flags, OPT_SPORT);
|
||||||
|
ipinfo->bitmask |= EBT_IP6_SPORT;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP6_SPORT;
|
||||||
|
} else {
|
||||||
|
ebt_check_option2(flags, OPT_DPORT);
|
||||||
|
ipinfo->bitmask |= EBT_IP6_DPORT;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP6_DPORT;
|
||||||
|
}
|
||||||
|
if (c == IP_SPORT)
|
||||||
|
parse_port_range(NULL, optarg, ipinfo->sport);
|
||||||
|
else
|
||||||
|
parse_port_range(NULL, optarg, ipinfo->dport);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_ICMP6:
|
||||||
|
ebt_check_option2(flags, EBT_IP6_ICMP6);
|
||||||
|
ipinfo->bitmask |= EBT_IP6_ICMP6;
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP6_ICMP6;
|
||||||
|
if (ebt_parse_icmp(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
|
||||||
|
optarg, ipinfo->icmpv6_type,
|
||||||
|
ipinfo->icmpv6_code))
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_TCLASS:
|
||||||
|
ebt_check_option2(flags, OPT_TCLASS);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP6_TCLASS;
|
||||||
|
i = strtol(optarg, &end, 16);
|
||||||
|
if (i < 0 || i > 255 || *end != '\0')
|
||||||
|
ebt_print_error2("Problem with specified IPv6 traffic class");
|
||||||
|
ipinfo->tclass = i;
|
||||||
|
ipinfo->bitmask |= EBT_IP6_TCLASS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IP_PROTO:
|
||||||
|
ebt_check_option2(flags, OPT_PROTO);
|
||||||
|
if (ebt_check_inverse2(optarg))
|
||||||
|
ipinfo->invflags |= EBT_IP6_PROTO;
|
||||||
|
i = strtoul(optarg, &end, 10);
|
||||||
|
if (*end != '\0') {
|
||||||
|
struct protoent *pe;
|
||||||
|
|
||||||
|
pe = getprotobyname(optarg);
|
||||||
|
if (pe == NULL)
|
||||||
|
ebt_print_error("Unknown specified IP protocol - %s", argv[optind - 1]);
|
||||||
|
ipinfo->protocol = pe->p_proto;
|
||||||
|
} else {
|
||||||
|
ipinfo->protocol = (unsigned char) i;
|
||||||
|
}
|
||||||
|
ipinfo->bitmask |= EBT_IP6_PROTO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void final_check(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match, const char *name,
|
||||||
|
unsigned int hookmask, unsigned int time)
|
||||||
|
{
|
||||||
|
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
|
||||||
|
|
||||||
|
if (entry->ethproto != ETH_P_IPV6 || entry->invflags & EBT_IPROTO) {
|
||||||
|
ebt_print_error("For IPv6 filtering the protocol must be "
|
||||||
|
"specified as IPv6");
|
||||||
|
} else if (ipinfo->bitmask & (EBT_IP6_SPORT|EBT_IP6_DPORT) &&
|
||||||
|
(!(ipinfo->bitmask & EBT_IP6_PROTO) ||
|
||||||
|
ipinfo->invflags & EBT_IP6_PROTO ||
|
||||||
|
(ipinfo->protocol!=IPPROTO_TCP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_UDP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_SCTP &&
|
||||||
|
ipinfo->protocol!=IPPROTO_DCCP)))
|
||||||
|
ebt_print_error("For port filtering the IP protocol must be "
|
||||||
|
"either 6 (tcp), 17 (udp), 33 (dccp) or "
|
||||||
|
"132 (sctp)");
|
||||||
|
if ((ipinfo->bitmask & EBT_IP6_ICMP6) &&
|
||||||
|
(!(ipinfo->bitmask & EBT_IP6_PROTO) ||
|
||||||
|
ipinfo->invflags & EBT_IP6_PROTO ||
|
||||||
|
ipinfo->protocol != IPPROTO_ICMPV6))
|
||||||
|
ebt_print_error("For ipv6-icmp filtering the IP protocol must be "
|
||||||
|
"58 (ipv6-icmp)");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print(const struct ebt_u_entry *entry,
|
||||||
|
const struct ebt_entry_match *match)
|
||||||
|
{
|
||||||
|
struct ebt_ip6_info *ipinfo = (struct ebt_ip6_info *)match->data;
|
||||||
|
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_SOURCE) {
|
||||||
|
printf("--ip6-src ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_SOURCE)
|
||||||
|
printf("! ");
|
||||||
|
printf("%s", ebt_ip6_to_numeric(&ipinfo->saddr));
|
||||||
|
printf("%s ", ebt_ip6_mask_to_string(&ipinfo->smsk));
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_DEST) {
|
||||||
|
printf("--ip6-dst ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_DEST)
|
||||||
|
printf("! ");
|
||||||
|
printf("%s", ebt_ip6_to_numeric(&ipinfo->daddr));
|
||||||
|
printf("%s ", ebt_ip6_mask_to_string(&ipinfo->dmsk));
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_TCLASS) {
|
||||||
|
printf("--ip6-tclass ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_TCLASS)
|
||||||
|
printf("! ");
|
||||||
|
printf("0x%02X ", ipinfo->tclass);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_PROTO) {
|
||||||
|
struct protoent *pe;
|
||||||
|
|
||||||
|
printf("--ip6-proto ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_PROTO)
|
||||||
|
printf("! ");
|
||||||
|
pe = getprotobynumber(ipinfo->protocol);
|
||||||
|
if (pe == NULL) {
|
||||||
|
printf("%d ", ipinfo->protocol);
|
||||||
|
} else {
|
||||||
|
printf("%s ", pe->p_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_SPORT) {
|
||||||
|
printf("--ip6-sport ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_SPORT)
|
||||||
|
printf("! ");
|
||||||
|
print_port_range(ipinfo->sport);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_DPORT) {
|
||||||
|
printf("--ip6-dport ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_DPORT)
|
||||||
|
printf("! ");
|
||||||
|
print_port_range(ipinfo->dport);
|
||||||
|
}
|
||||||
|
if (ipinfo->bitmask & EBT_IP6_ICMP6) {
|
||||||
|
printf("--ip6-icmp-type ");
|
||||||
|
if (ipinfo->invflags & EBT_IP6_ICMP6)
|
||||||
|
printf("! ");
|
||||||
|
ebt_print_icmp_type(icmpv6_codes, ARRAY_SIZE(icmpv6_codes),
|
||||||
|
ipinfo->icmpv6_type, ipinfo->icmpv6_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare(const struct ebt_entry_match *m1,
|
||||||
|
const struct ebt_entry_match *m2)
|
||||||
|
{
|
||||||
|
struct ebt_ip6_info *ipinfo1 = (struct ebt_ip6_info *)m1->data;
|
||||||
|
struct ebt_ip6_info *ipinfo2 = (struct ebt_ip6_info *)m2->data;
|
||||||
|
|
||||||
|
if (ipinfo1->bitmask != ipinfo2->bitmask)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->invflags != ipinfo2->invflags)
|
||||||
|
return 0;
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_SOURCE) {
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->saddr, &ipinfo2->saddr))
|
||||||
|
return 0;
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->smsk, &ipinfo2->smsk))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_DEST) {
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->daddr, &ipinfo2->daddr))
|
||||||
|
return 0;
|
||||||
|
if (!IN6_ARE_ADDR_EQUAL(&ipinfo1->dmsk, &ipinfo2->dmsk))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_TCLASS) {
|
||||||
|
if (ipinfo1->tclass != ipinfo2->tclass)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_PROTO) {
|
||||||
|
if (ipinfo1->protocol != ipinfo2->protocol)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_SPORT) {
|
||||||
|
if (ipinfo1->sport[0] != ipinfo2->sport[0] ||
|
||||||
|
ipinfo1->sport[1] != ipinfo2->sport[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_DPORT) {
|
||||||
|
if (ipinfo1->dport[0] != ipinfo2->dport[0] ||
|
||||||
|
ipinfo1->dport[1] != ipinfo2->dport[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ipinfo1->bitmask & EBT_IP6_ICMP6) {
|
||||||
|
if (ipinfo1->icmpv6_type[0] != ipinfo2->icmpv6_type[0] ||
|
||||||
|
ipinfo1->icmpv6_type[1] != ipinfo2->icmpv6_type[1] ||
|
||||||
|
ipinfo1->icmpv6_code[0] != ipinfo2->icmpv6_code[0] ||
|
||||||
|
ipinfo1->icmpv6_code[1] != ipinfo2->icmpv6_code[1])
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ebt_u_match ip6_match =
|
||||||
|
{
|
||||||
|
.name = EBT_IP6_MATCH,
|
||||||
|
.size = sizeof(struct ebt_ip6_info),
|
||||||
|
.help = print_help,
|
||||||
|
.init = init,
|
||||||
|
.parse = parse,
|
||||||
|
.final_check = final_check,
|
||||||
|
.print = print,
|
||||||
|
.compare = compare,
|
||||||
|
.extra_ops = opts,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_match(&ip6_match);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_match(&limit_match);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_target(&mark_target);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_match(&mark_match);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_target(&standard);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_table(&table);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_table(&table);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((constructor)) static void extension_init(void)
|
||||||
|
{
|
||||||
|
ebt_register_table(&table);
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* 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 <stddef.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include "include/ethernetdb.h"
|
||||||
|
|
||||||
|
|
||||||
|
static const struct ethertypeent ethertypes[] = {
|
||||||
|
{"IPv4", 0x0800 }, /* Internet IP (IPv4) */
|
||||||
|
{"X25", 0x0805 },
|
||||||
|
{"ARP", 0x0806 },
|
||||||
|
{"FR_ARP", 0x0808 }, /* Frame Relay ARP [RFC1701] */
|
||||||
|
{"BPQ", 0x08FF }, /* G8BPQ AX.25 Ethernet Packet */
|
||||||
|
{"DEC", 0x6000 }, /* DEC Assigned proto */
|
||||||
|
{"DNA_DL", 0x6001 }, /* DEC DNA Dump/Load */
|
||||||
|
{"DNA_RC", 0x6002 }, /* DEC DNA Remote Console */
|
||||||
|
{"DNA_RT", 0x6003 }, /* DEC DNA Routing */
|
||||||
|
{"LAT", 0x6004 }, /* DEC LAT */
|
||||||
|
{"DIAG", 0x6005 }, /* DEC Diagnostics */
|
||||||
|
{"CUST", 0x6006 }, /* DEC Customer use */
|
||||||
|
{"SCA", 0x6007 }, /* DEC Systems Comms Arch */
|
||||||
|
{"TEB", 0x6558 }, /* Trans Ether Bridging [RFC1701] */
|
||||||
|
{"RAW_FR", 0x6559 }, /* Raw Frame Relay [RFC1701] */
|
||||||
|
{"RARP", 0x8035 }, /* Reverse ARP [RFC903] */
|
||||||
|
{"AARP", 0x80F3 }, /* Appletalk AARP */
|
||||||
|
{"ATALK", 0x809B }, /* Appletalk */
|
||||||
|
{"802_1Q", 0x8100 }, /* 802.1Q Virtual LAN tagged frame */
|
||||||
|
{"IPX", 0x8137 }, /* Novell IPX */
|
||||||
|
{"NetBEUI", 0x8191 }, /* NetBEUI */
|
||||||
|
{"IPv6", 0x86DD }, /* IP version 6 */
|
||||||
|
{"PPP", 0x880B }, /* PPP */
|
||||||
|
{"ATMMPOA", 0x884C }, /* MultiProtocol over ATM */
|
||||||
|
{"PPP_DISC", 0x8863 }, /* PPPoE discovery messages */
|
||||||
|
{"PPP_SES", 0x8864 }, /* PPPoE session messages */
|
||||||
|
{"ATMFATE", 0x8884 }, /* Frame-based ATM Transport over Ethernet */
|
||||||
|
{"LOOP", 0x9000 }, /* loop proto */
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const struct ethertypeent *getethertypebyname(const char *name)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; ethertypes[i].e_name; i++)
|
||||||
|
if (strcasecmp(ethertypes[i].e_name, name) == 0)
|
||||||
|
return ðertypes[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct ethertypeent *getethertypebynumber(int type)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; ethertypes[i].e_name; i++)
|
||||||
|
if (ethertypes[i].e_ethertype == type)
|
||||||
|
return ðertypes[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* ebtables
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Bart De Schuymer <bdschuym@pandora.be>
|
||||||
|
*
|
||||||
|
* ebtables.c,v 2.0, April, 2002
|
||||||
|
*
|
||||||
|
* This code is stongly inspired on the iptables code which is
|
||||||
|
* Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Local copy of the kernel file, needed for Sparc64 support */
|
||||||
|
#ifndef __LINUX_BRIDGE_EFF_H
|
||||||
|
#define __LINUX_BRIDGE_EFF_H
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/netfilter_bridge.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
|
||||||
|
#define EBT_TABLE_MAXNAMELEN 32
|
||||||
|
#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
|
||||||
|
#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
|
||||||
|
|
||||||
|
/* 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 ebt_counter
|
||||||
|
{
|
||||||
|
uint64_t pcnt;
|
||||||
|
uint64_t 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_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 alot, 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 {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
struct ebt_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 {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
struct ebt_watcher *watcher;
|
||||||
|
} u;
|
||||||
|
/* size of data */
|
||||||
|
unsigned int watcher_size;
|
||||||
|
unsigned char data[0] __attribute__ ((aligned (__alignof__(struct ebt_replace))));
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ebt_entry_target
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
struct ebt_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;
|
||||||
|
uint16_t 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
|
|
@ -0,0 +1,329 @@
|
||||||
|
/*
|
||||||
|
* $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 <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
|
||||||
|
|
||||||
|
#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))
|
||||||
|
|
||||||
|
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;
|
||||||
|
/* 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_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_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];
|
||||||
|
/* 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_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_target *ebt_targets;
|
||||||
|
|
||||||
|
void ebt_register_table(struct ebt_u_table *);
|
||||||
|
void ebt_register_match(struct ebt_u_match *);
|
||||||
|
void ebt_register_target(struct ebt_u_target *t);
|
||||||
|
int ebt_get_kernel_table(struct ebt_u_replace *replace);
|
||||||
|
struct ebt_u_target *ebt_find_target(const char *name);
|
||||||
|
struct ebt_u_match *ebt_find_match(const char *name);
|
||||||
|
struct ebt_u_table *ebt_find_table(const char *name);
|
||||||
|
void ebt_list_extensions();
|
||||||
|
void ebt_initialize_entry(struct ebt_u_entry *e);
|
||||||
|
void ebt_double_chains(struct ebt_u_replace *replace);
|
||||||
|
void ebt_free_u_entry(struct ebt_u_entry *e);
|
||||||
|
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_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);
|
||||||
|
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_iterate_matches(void (*f)(struct ebt_u_match *));
|
||||||
|
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);
|
||||||
|
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[], struct ebt_u_replace *replace_);
|
||||||
|
|
||||||
|
#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_check_option2(flags,mask) \
|
||||||
|
({ebt_check_option(flags,mask);})
|
||||||
|
#define ebt_check_inverse2(option) \
|
||||||
|
({int __ret = ebt_check_inverse(option); \
|
||||||
|
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];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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("ebtables (tiny)\n")
|
||||||
|
|
||||||
|
#ifndef ARRAY_SIZE
|
||||||
|
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
#endif
|
||||||
|
#endif /* EBTABLES_U_H */
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
struct ethertypeent {
|
||||||
|
const char *e_name; /* Official ethernet type name. */
|
||||||
|
int e_ethertype; /* Ethernet type number. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Return entry from ethertype data base for network with NAME. */
|
||||||
|
const struct ethertypeent *getethertypebyname(__const char *__name);
|
||||||
|
|
||||||
|
/* Return entry from ethertype data base which number is PROTO. */
|
||||||
|
const struct ethertypeent *getethertypebynumber(int __ethertype);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ethernetdb.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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* ebtables
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Bart De Schuymer <bdschuym@pandora.be>
|
||||||
|
*
|
||||||
|
* ebtables.c,v 2.0, April, 2002
|
||||||
|
*
|
||||||
|
* This code is stongly inspired on 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/if.h>
|
||||||
|
#include <linux/netfilter_bridge.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
|
||||||
|
#define EBT_TABLE_MAXNAMELEN 32
|
||||||
|
#define EBT_CHAIN_MAXNAMELEN EBT_TABLE_MAXNAMELEN
|
||||||
|
#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN
|
||||||
|
|
||||||
|
/* 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 {
|
||||||
|
uint64_t pcnt;
|
||||||
|
uint64_t 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 {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
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 {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
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 {
|
||||||
|
char name[EBT_FUNCTION_MAXNAMELEN];
|
||||||
|
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
|
|
@ -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 */
|
|
@ -0,0 +1,913 @@
|
||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
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_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_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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef LOCKFILE
|
||||||
|
#define LOCKDIR "/var/lib/ebtables"
|
||||||
|
#define LOCKFILE LOCKDIR"/lock"
|
||||||
|
#endif
|
||||||
|
/* Returns 0 on success, -1 when the file is locked by another process
|
||||||
|
* or -2 on any other error. */
|
||||||
|
static int lock_file()
|
||||||
|
{
|
||||||
|
int fd, try = 0;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
fd = open(LOCKFILE, O_CREAT, 00600);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (try == 1 || mkdir(LOCKDIR, 00700))
|
||||||
|
return -2;
|
||||||
|
try = 1;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
return flock(fd, LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the table from the kernel or from a binary file */
|
||||||
|
int ebt_get_kernel_table(struct ebt_u_replace *replace)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!ebt_find_table(replace->name)) {
|
||||||
|
ebt_print_error("Bad table name '%s'", replace->name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while ((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))
|
||||||
|
ebt_print_error("The kernel doesn't support the ebtables '%s' table", replace->name);
|
||||||
|
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->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
m_l = e->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
m_l2 = m_l->next;
|
||||||
|
free(m_l->m);
|
||||||
|
free(m_l);
|
||||||
|
m_l = m_l2;
|
||||||
|
}
|
||||||
|
free(e->t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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_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;
|
||||||
|
/* Compare all matches */
|
||||||
|
m_l = new_entry->m_list;
|
||||||
|
j = 0;
|
||||||
|
while (m_l) {
|
||||||
|
m = (struct ebt_u_match *)(m_l->m);
|
||||||
|
m_l2 = u_e->m_list;
|
||||||
|
while (m_l2 && strcmp(m_l2->m->u.name, m->m->u.name))
|
||||||
|
m_l2 = m_l2->next;
|
||||||
|
if (!m_l2 || !m->compare(m->m, m_l2->m))
|
||||||
|
goto letscontinue;
|
||||||
|
j++;
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
/* Now be sure they have the same nr of matches */
|
||||||
|
k = 0;
|
||||||
|
m_l = u_e->m_list;
|
||||||
|
while (m_l) {
|
||||||
|
k++;
|
||||||
|
m_l = m_l->next;
|
||||||
|
}
|
||||||
|
if (j != k)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
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_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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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_target *t;
|
||||||
|
struct ebt_u_match *m;
|
||||||
|
|
||||||
|
m_l = e->m_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);
|
||||||
|
m_l = m_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
*******************
|
||||||
|
*******************
|
||||||
|
**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->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_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_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, "ebtables: %s:%d:--BUG--: \n", file, line);
|
||||||
|
vfprintf(stderr, format, l);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
va_end(l);
|
||||||
|
exit (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't use this function, use ebt_print_error() */
|
||||||
|
void __ebt_print_error(char *format, ...)
|
||||||
|
{
|
||||||
|
va_list l;
|
||||||
|
|
||||||
|
va_start(l, format);
|
||||||
|
vfprintf(stderr, format, l);
|
||||||
|
fprintf(stderr, ".\n");
|
||||||
|
va_end(l);
|
||||||
|
exit (-1);
|
||||||
|
}
|
|
@ -0,0 +1,564 @@
|
||||||
|
/*
|
||||||
|
* 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};
|
||||||
|
|
||||||
|
void ebt_print_mac(const unsigned char *mac)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
for (j = 0; j < ETH_ALEN; j++)
|
||||||
|
printf("%02x%s", mac[j],
|
||||||
|
(j==ETH_ALEN-1) ? "" : ":");
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
* 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.
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
char *ebt_ip6_mask_to_string(const struct in6_addr *msk)
|
||||||
|
{
|
||||||
|
/* /0000:0000:0000:0000:0000:000.000.000.000
|
||||||
|
* /0000:0000:0000:0000:0000:0000:0000:0000 */
|
||||||
|
static char buf[51+1];
|
||||||
|
if (msk->s6_addr32[0] == 0xFFFFFFFFL && msk->s6_addr32[1] == 0xFFFFFFFFL &&
|
||||||
|
msk->s6_addr32[2] == 0xFFFFFFFFL && msk->s6_addr32[3] == 0xFFFFFFFFL)
|
||||||
|
*buf = '\0';
|
||||||
|
else
|
||||||
|
sprintf(buf, "/%s", ebt_ip6_to_numeric(msk));
|
||||||
|
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");
|
||||||
|
}
|
Loading…
Reference in New Issue