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