]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
Initial commit v0.01-alpha1
authorPatrick McHardy <kaber@trash.net>
Wed, 18 Mar 2009 03:55:00 +0000 (04:55 +0100)
committerPatrick McHardy <kaber@trash.net>
Wed, 18 Mar 2009 03:55:00 +0000 (04:55 +0100)
91 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
Makefile.defs.in [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
Makefile.rules.in [new file with mode: 0644]
TODO [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
debian/.gitignore [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/nftables.install [new file with mode: 0644]
debian/rules [new file with mode: 0755]
doc/.gitignore [new file with mode: 0644]
doc/Makefile.in [new file with mode: 0644]
doc/nftables.xml [new file with mode: 0644]
files/Makefile.in [new file with mode: 0644]
files/nftables/ipv4-filter [new file with mode: 0644]
files/nftables/ipv4-mangle [new file with mode: 0644]
files/nftables/ipv4-raw [new file with mode: 0644]
files/nftables/ipv4-security [new file with mode: 0644]
files/nftables/ipv6-filter [new file with mode: 0644]
files/nftables/ipv6-mangle [new file with mode: 0644]
files/nftables/ipv6-security [new file with mode: 0644]
include/ct.h [new file with mode: 0644]
include/datatype.h [new file with mode: 0644]
include/erec.h [new file with mode: 0644]
include/expression.h [new file with mode: 0644]
include/exthdr.h [new file with mode: 0644]
include/gmputil.h [new file with mode: 0644]
include/headers.h [new file with mode: 0644]
include/linux/netfilter.h [new file with mode: 0644]
include/linux/netfilter/nf_conntrack_common.h [new file with mode: 0644]
include/linux/netfilter/nf_conntrack_tuple_common.h [new file with mode: 0644]
include/linux/netfilter/nf_tables.h [new file with mode: 0644]
include/linux/netfilter_arp.h [new file with mode: 0644]
include/linux/netfilter_bridge.h [new file with mode: 0644]
include/linux/netfilter_decnet.h [new file with mode: 0644]
include/linux/netfilter_ipv4.h [new file with mode: 0644]
include/linux/netfilter_ipv6.h [new file with mode: 0644]
include/list.h [new file with mode: 0644]
include/meta.h [new file with mode: 0644]
include/netlink.h [new file with mode: 0644]
include/nftables.h [new file with mode: 0644]
include/parser.h [new file with mode: 0644]
include/payload.h [new file with mode: 0644]
include/rbtree.h [new file with mode: 0644]
include/rule.h [new file with mode: 0644]
include/statement.h [new file with mode: 0644]
include/utils.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
src/.gitignore [new file with mode: 0644]
src/Makefile.in [new file with mode: 0644]
src/cli.c [new file with mode: 0644]
src/ct.c [new file with mode: 0644]
src/datatype.c [new file with mode: 0644]
src/erec.c [new file with mode: 0644]
src/evaluate.c [new file with mode: 0644]
src/expression.c [new file with mode: 0644]
src/exthdr.c [new file with mode: 0644]
src/gmputil.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]
src/meta.c [new file with mode: 0644]
src/netlink.c [new file with mode: 0644]
src/netlink_delinearize.c [new file with mode: 0644]
src/netlink_linearize.c [new file with mode: 0644]
src/parser-skeleton.c [new file with mode: 0644]
src/parser.y [new file with mode: 0644]
src/payload.c [new file with mode: 0644]
src/rbtree.c [new file with mode: 0644]
src/rule.c [new file with mode: 0644]
src/scanner.l [new file with mode: 0644]
src/segtree.c [new file with mode: 0644]
src/statement.c [new file with mode: 0644]
src/utils.c [new file with mode: 0644]
tests/dictionary [new file with mode: 0755]
tests/expr-concat [new file with mode: 0755]
tests/expr-ct [new file with mode: 0755]
tests/expr-meta [new file with mode: 0755]
tests/family-bridge [new file with mode: 0755]
tests/family-ipv4 [new file with mode: 0755]
tests/family-ipv6 [new file with mode: 0755]
tests/feat-adjancent-load-merging [new file with mode: 0755]
tests/obj-chain [new file with mode: 0755]
tests/obj-table [new file with mode: 0755]
tests/payload-ll [new file with mode: 0755]
tests/prefix [new file with mode: 0755]
tests/set [new file with mode: 0755]
tests/stmt-log [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d26b395
--- /dev/null
@@ -0,0 +1,18 @@
+# Dependency and object files
+.*.d
+*.o
+
+# Generated by autoconf/configure
+Makefile
+Makefile.defs
+Makefile.rules
+config.h
+config.h.in
+config.h.in~
+config.log
+config.status
+configure
+autom4te.cache
+
+# Debian package build temporary files
+build-stamp
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..bf7f06e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,349 @@
+
+nftables is distributed under the terms of the GPL version 2. Note that
+*only* version 2 of the GPL applies, not "any later version".
+
+Patrick McHardy <kaber@trash.net>
+
+-------------------------------------------------------------------------------
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+               51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+
+
+           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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Makefile.defs.in b/Makefile.defs.in
new file mode 100644 (file)
index 0000000..545c4ee
--- /dev/null
@@ -0,0 +1,40 @@
+DEBUG          = @CONFIG_DEBUG@
+CC             = @CC@
+CPP            = @CPP@
+LEX            = @LEX@
+YACC           = @YACC@
+MKDIR_P                = @MKDIR_P@
+INSTALL                = @INSTALL@
+
+PACKAGE_TARNAME        = @PACKAGE_TARNAME@
+
+prefix         = @prefix@
+exec_prefix    = @exec_prefix@
+sysconfdir     = @sysconfdir@
+datarootdir    = @datarootdir@
+mandir         = @mandir@
+docdir         = @docdir@
+pdfdir         = @pdfdir@
+confdir                = @sysconfdir@/nftables
+
+LDFLAGS                += @LDFLAGS@
+LDFLAGS                += @LIBS@
+
+CPPFLAGS       += @CPPFLAGS@
+
+CFLAGS         += @CFLAGS@ @DEFS@
+CFLAGS         += -DDEFAULT_INCLUDE_PATH="\"$(confdir)\""
+CFLAGS         += -include config.h
+CFLAGS         += -Iinclude
+CFLAGS         += -fno-strict-aliasing
+
+CFLAGS         += -Wall -Werror
+CFLAGS         += -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
+CFLAGS         += -Wdeclaration-after-statement -Wsign-compare -Winit-self
+CFLAGS         += -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute
+CFLAGS         += -Wcast-align -Wundef -Wbad-function-cast # -Wshadow
+CFLAGS         += -Waggregate-return -Wunused -Wwrite-strings
+
+ifeq ($(DEBUG),y)
+CFLAGS         += -g -DDEBUG
+endif
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..5d42541
--- /dev/null
@@ -0,0 +1,5 @@
+SUBDIRS                += src
+SUBDIRS                += files
+SUBDIRS                += doc
+
+include Makefile.rules
diff --git a/Makefile.rules.in b/Makefile.rules.in
new file mode 100644 (file)
index 0000000..7ee9a07
--- /dev/null
@@ -0,0 +1,85 @@
+include Makefile.defs
+
+makedeps               += $(SUBDIR)Makefile
+makedeps               += Makefile
+makedeps               += Makefile.defs
+makedeps               += Makefile.rules
+
+configure:             configure.ac
+                       sh autogen.sh
+
+%:                     %.in    configure
+                       sh configure
+
+%.o:                   %.c     $(makedeps)
+                       @echo -e "  CC\t\t$<"
+                       $(CC) $(CFLAGS) -c -o $@ $<
+
+.%.d:                  %.c     $(makedeps)
+                       @echo -e "  DEP\t\t$<"
+                       $(RM) $@
+                       $(CC) -M $(CFLAGS) $< | sed 's,$(*F)\.o[ :]*,$*.o $@ : ,g' > $@
+
+%.c %.h:               %.y     $(makedeps)
+                       @echo -e "  YACC\t\t$<"
+                       $(YACC) $(YACCFLAGS) -d -o $@ $<
+
+%.c %.h:               %.l     $(makedeps)
+                       @echo -e "  LEX\t\t$<"
+                       $(LEX) -t --header-file=$(<:.l=.h) $< > $@
+
+%.8:                   %.xml   $(makedeps)
+                       @echo -e "  MAN\t\t$@"
+                       docbook2x-man $<
+
+%.pdf:                 %.xml   $(makedeps)
+                       @echo -e "  PDF\t\t$@"
+                       db2pdf -o $(SUBDIR) $<
+
+define program_template
+$(1)-obj               := $$(patsubst %,$(SUBDIR)%,$$($(1)-obj))
+$(1)-extra-clean-files := $$(patsubst %,$(SUBDIR)%,$$($(1)-extra-clean-files))
+
+depfiles               := $$(patsubst $(SUBDIR)%.o,$(SUBDIR).%.d,$$($(1)-obj))
+
+$(SUBDIR)$(1):         $$($(1)-extra-targets) $$($(1)-obj)
+                       @echo -e "  LD\t\t$$(SUBDIR)$$@"
+                       $$(CC) $$($(1)-obj) $$(LDFLAGS) -o $$@
+all_targets            += $(SUBDIR)$(1)
+
+.PHONY:                        $(1)-clean
+$(1)-clean:
+                       @echo -e "  CLEAN\t\t$(1)"
+                       $$(RM) $$($(1)-obj) $$(depfiles) $$($(1)-extra-clean-files) $(SUBDIR)$(1)
+clean_targets          += $(1)-clean
+
+.PHONY:                        $(1)-install
+$(1)-install:
+                       @echo -e "  INSTALL\t$1"
+                       $(MKDIR_P) $$(DESTDIR)/$$($(1)-destdir)
+                       $(INSTALL) -m 755 -o root -g root \
+                               $(SUBDIR)$(1) \
+                               $$(DESTDIR)/$$($(1)-destdir)/$(1)
+install_targets                += $(1)-install
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $$(depfiles)
+endif
+endef
+
+ifneq ($(SUBDIR),)
+include $(SUBDIR)/Makefile
+$(foreach prog,$(PROGRAMS),$(eval $(call program_template,$(prog))))
+endif
+
+.DEFAULT_GOAL          := all
+
+.PHONY:                        all clean install
+all:                   $(SUBDIRS) $(all_targets)
+clean:                 $(SUBDIRS) $(clean_targets)
+install:               all $(SUBDIRS) $(install_targets)
+
+.PHONY: $(SUBDIRS)
+$(SUBDIRS):
+                       @echo -e "  SUBDIR\t$@/"
+                       @$(MAKE) -s -f Makefile.rules $(MAKECMDGOALS) SUBDIR="$@/"
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..20ab42d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,27 @@
+nftables frontend
+-----------------
+- Define lexical distinction between keywords, symbolic constants and
+  identifiers
+- Define syntax for changing data (connmark, meta etc.)
+- shorter syntax for specifying rules: entire chains without repeating "rule add ..."
+- payload syntax for matching on IP headers of IPIP/GRE tunnels etc.
+
+- netlink monitor for CLI
+
+Kernel
+------
+- netlink set API
+- kernel set implementation selection
+- TC hookup - use dummy classifier or hook "natively" ?
+- kill mangle table, make rerouting a configurable table/chain property
+- kill nat table? harder because of more special handling
+- multi-family tables
+
+- IPv6 ext header matching
+- IP style options (IP/TCP/DCCP) matching
+- IPsec policy matching
+- hashlimit
+- quota
+- recent(?)
+- TCPMSS target - generic packet editor?
+- include NLM_F_ ... flags in notifications?
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..a62e737
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+autoreconf -fi;
+rm -Rf autom4te*.cache config.h.in~
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..d8a850c
--- /dev/null
@@ -0,0 +1,99 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.61)
+
+AC_COPYRIGHT([Copyright (c) 2008 Patrick McHardy <kaber@trash.net>])
+AC_INIT([nftables], [0.01-alpha1], [netfilter-devel@vger.kernel.org])
+AC_DEFINE([RELEASE_NAME], ["schäublefilter"], [Release name])
+
+AC_CONFIG_SRCDIR([src/rule.c])
+AC_CONFIG_HEADER([config.h])
+
+AC_DEFINE([_GNU_SOURCE], [], [Enable various GNU extensions])
+AC_DEFINE([_STDC_FORMAT_MACROS], [], [printf-style format macros])
+
+AC_ARG_ENABLE([debug],
+             AS_HELP_STRING([--enable-debug], [Enable debugging]),
+             [CONFIG_DEBUG="$(echo $enableval | cut -b1)"],
+             [CONFIG_DEBUG="y"])
+AC_SUBST([CONFIG_DEBUG])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_MKDIR_P
+AC_PROG_INSTALL
+
+AC_CHECK_PROG(CONFIG_MAN, docbook2x-man, y, n)
+if test "$CONFIG_MAN" != "y"
+then
+       AC_MSG_WARN([docbookx2-man not found, no manpages will be built])
+fi
+
+AC_CHECK_PROG(CONFIG_PDF, db2pdf, y, n)
+if test "$CONFIG_PDF" != "y"
+then
+       AC_MSG_WARN([db2pdf not found, no PDF manpages will be built])
+fi
+
+AC_PATH_PROG(LEX, [flex])
+if test -z "$LEX"
+then
+       AC_MSG_ERROR([No suitable version of flex found])
+fi
+
+AC_PATH_PROG(YACC, [bison])
+if test -z "$YACC"
+then
+       AC_MSG_ERROR([No suitable version of bison found])
+fi
+
+# Checks for libraries.
+AC_CHECK_LIB([nl], [nl_socket_alloc], ,
+            AC_MSG_ERROR([No suitable version of libnl found]))
+
+AC_CHECK_LIB([nl-nf], [nfnl_nft_rule_alloc], ,
+            AC_MSG_ERROR([No suitable version of libnl-nf found]))
+
+AC_CHECK_LIB([nl-route], [rtnl_link_alloc], ,
+            AC_MSG_ERROR([No suitable version of libnl-route found]))
+
+AC_CHECK_LIB([gmp], [__gmpz_init], ,
+            AC_MSG_ERROR([No suitable version of libgmp found]))
+
+AC_CHECK_LIB([readline], [readline], ,
+            AC_MSG_ERROR([No suitable version of libreadline found]))
+
+# Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_ASSERT
+AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h libintl.h limits.h malloc.h \
+                 netdb.h netinet/in.h netinet/ip.h netinet/ip6.h \
+                 netinet/tcp.h netinet/udp.h netinet/ip_icmp.h \
+                 stddef.h stdint.h stdlib.h string.h unistd.h], ,
+                AC_MSG_ERROR([Header file not found]))
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_CONST
+AC_C_INLINE
+AC_TYPE_OFF_T
+AC_TYPE_SIZE_T
+AC_TYPE_UID_T
+AC_TYPE_INT8_T
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_REALLOC
+AC_CHECK_FUNCS([memmove memset strchr strdup strerror strtoull])
+
+AC_CONFIG_FILES([Makefile Makefile.defs Makefile.rules])
+AC_CONFIG_FILES([src/Makefile doc/Makefile files/Makefile])
+AC_OUTPUT
diff --git a/debian/.gitignore b/debian/.gitignore
new file mode 100644 (file)
index 0000000..b1e1dfb
--- /dev/null
@@ -0,0 +1,4 @@
+debhelper.log
+nftables.debhelper.log
+nftables/
+tmp/
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..32b17fa
--- /dev/null
@@ -0,0 +1,6 @@
+nftables (0.01-alpha1) unstable; urgency=low
+
+  * initial alpha version
+
+ -- Patrick McHardy <kaber@trash.net>  Wed,  3 Dec 2008 14:00:00 +0100
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..e006278
--- /dev/null
@@ -0,0 +1,13 @@
+Source: nftables
+Section: net
+Priority: optional
+Maintainer: Patrick McHardy <kaber@trash.net>
+Build-Depends: libnl-dev (>= 2.0)
+Standards-Version: 3.8.0
+
+Package: nftables
+Architecture: any
+Priority: optional
+Section: net
+Depends: ${shlibs:Depends}
+Description: Program to control packet filtering rules
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..d6e4857
--- /dev/null
@@ -0,0 +1,4 @@
+The GNU General Public License version 2 applies to the upstream
+sources.  On Debian systems, the complete text of the GNU General
+Public License version 2 can be found in the
+/usr/share/common-licenses/GPL-2 file.
diff --git a/debian/nftables.install b/debian/nftables.install
new file mode 100644 (file)
index 0000000..92e31e9
--- /dev/null
@@ -0,0 +1 @@
+src/nft /sbin
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..11988d5
--- /dev/null
@@ -0,0 +1,20 @@
+#! /usr/bin/make -f
+
+configure_opts := --prefix=/ --datarootdir=/usr
+install_opts   :=
+
+%:
+       dh $@
+
+build: build-stamp
+build-stamp:
+       dh build --before dh_auto_configure
+       autoreconf
+       dh_auto_configure -- $(configure_opts)
+       dh build --after dh_auto_configure
+       touch $@
+
+install: build
+       dh_install $(install_opts)
+
+binary: install
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644 (file)
index 0000000..fc24d49
--- /dev/null
@@ -0,0 +1,2 @@
+nftables.8
+nftables.pdf
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644 (file)
index 0000000..497e17a
--- /dev/null
@@ -0,0 +1,14 @@
+mandocs-@CONFIG_MAN@   += doc/nftables.8
+pdfdocs-@CONFIG_PDF@   += doc/nftables.pdf
+
+all:           $(mandocs-y) $(pdfdocs-y)
+clean:
+               @echo -e "  CLEAN\t\tdoc"
+               $(RM) $(mandocs-y) $(pdfdocs-y)
+
+install:
+               @echo -e "  INSTALL\tdoc"
+               $(MKDIR_P) $(DESTDIR)/${mandir}/man8
+               $(INSTALL) -m 755 -o root -g root $(mandocs-y) $(DESTDIR)/${mandir}/man8/
+               $(MKDIR_P) $(DESTDIR)/${pdfdir}
+               $(INSTALL) -m 755 -o root -g root $(pdfdocs-y) $(DESTDIR)/${pdfdir}/
diff --git a/doc/nftables.xml b/doc/nftables.xml
new file mode 100644 (file)
index 0000000..ec6de38
--- /dev/null
@@ -0,0 +1,966 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "/usr/share/xml/docbook/schema/dtd/4.5/docbookx.dtd">
+
+<refentry>
+       <refentryinfo>
+               <author>
+                       <firstname>Patrick</firstname>
+                       <surname>McHardy</surname>
+                       <email>kaber@trash.net</email>
+               </author>
+               <copyright>
+                       <year>2008</year>
+                       <holder>Patrick McHardy</holder>
+               </copyright>
+       </refentryinfo>
+
+       <refmeta>
+               <refentrytitle>nftables</refentrytitle>
+               <manvolnum>8</manvolnum>
+       </refmeta>
+
+       <refnamediv>
+               <refname>nftables</refname>
+               <refpurpose>
+                       Administration tool for packet filtering and classification
+               </refpurpose>
+       </refnamediv>
+
+       <refsynopsisdiv>
+               <cmdsynopsis>
+                       <command>nftables</command>
+                       <arg choice="opt">
+                               <option>-n/--numeric</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-I/--includepath</option>
+                               <replaceable>directory</replaceable>
+                       </arg>
+                       <group>
+                               <arg choice="opt">
+                                       <option>-f/--file</option>
+                                       <replaceable>filename</replaceable>
+                               </arg>
+                               <arg choice="opt">
+                                       <option>-i/--interactive</option>
+                               </arg>
+                               <arg choice="opt" rep="repeat">
+                                       <replaceable>cmd</replaceable>
+                               </arg>
+                       </group>
+               </cmdsynopsis>
+               <cmdsynopsis>
+                       <command>nftables</command>
+                       <arg choice="opt">
+                               <option>-h/--help</option>
+                       </arg>
+                       <arg choice="opt">
+                               <option>-v/--version</option>
+                       </arg>
+               </cmdsynopsis>
+       </refsynopsisdiv>
+
+       <refsect1>
+               <title>Description</title>
+               <para>
+                       nftables is used to set up, maintain and inspect packet
+                       filtering and classification rules in the Linux kernel.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Options</title>
+               <para>
+                       For a full summary of options, run <command>nftables --help</command>.
+               </para>
+
+               <variablelist>
+                       <varlistentry>
+                               <term><option>-h/--help</option></term>
+                               <listitem>
+                                       <para>
+                                               Show help message and all options.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>-v/--version</option></term>
+                               <listitem>
+                                       <para>
+                                               Show version.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>-n/--numeric</option></term>
+                               <listitem>
+                                       <para>
+                                               Numeric output: IP addresses and other information
+                                               that might need network traffic to resolve to symbolic names
+                                               are shown numerically.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>-I/--includepath <replaceable>directory</replaceable></option></term>
+                               <listitem>
+                                       <para>
+                                               Add the directory <replaceable>directory</replaceable> to the list of directories to by searched for included files.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>-f/--file <replaceable>filename</replaceable></option></term>
+                               <listitem>
+                                       <para>
+                                               Read input from <replaceable>filename</replaceable>.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>-i/--interactive</option></term>
+                               <listitem>
+                                       <para>
+                                               Read input from an interactive readline CLI.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>Input file format</title>
+               <para>
+                       Input is parsed line-wise. When the last character of a line just before
+                       the newline character is a non-quoted backslash (<literal>\</literal>),
+                       the newline is treated as a line continuation.
+               </para>
+               <para>
+                       A <literal>#</literal> begins a comment. All following characters on
+                       the same line are ignored.
+               </para>
+               <para>
+                       Other files can be included by using
+                       <command>include "<replaceable>filename</replaceable>"</command>.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Tables</title>
+               <para>
+                       <cmdsynopsis>
+                               <command>table</command>
+                               <group choice="req">
+                                       <arg>add</arg>
+                                       <arg>delete</arg>
+                                       <arg>list</arg>
+                                       <arg>flush</arg>
+                               </group>
+                               <arg choice="opt"><replaceable>family</replaceable></arg>
+                               <arg choice="req"><replaceable>table</replaceable></arg>
+                       </cmdsynopsis>
+               </para>
+
+               <para>
+                       Tables are containers for chains. They are identified by their family
+                       and their name. The family must be one of
+
+                       <simplelist type="inline">
+                               <member><literal>ip</literal></member>
+                               <member><literal>ip6</literal></member>
+                               <member><literal>arp</literal></member>
+                               <member><literal>bridge</literal></member>
+                       </simplelist>.
+
+                       When no family is specified, <literal>ip</literal> is used by default.
+               </para>
+
+               <variablelist>
+                       <varlistentry>
+                               <term><option>add</option></term>
+                               <listitem>
+                                       <para>
+                                               Add a new table for the given family with the given name.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>delete</option></term>
+                               <listitem>
+                                       <para>
+                                               Delete the specified table.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>list</option></term>
+                               <listitem>
+                                       <para>
+                                               List all chains and rules of the specified table.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>flush</option></term>
+                               <listitem>
+                                       <para>
+                                               Flush all chains and rules of the specified table.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>Chains</title>
+               <para>
+                       <cmdsynopsis>
+                               <command>chain</command>
+                               <arg choice="req">add</arg>
+                               <arg choice="opt"><replaceable>family</replaceable></arg>
+                               <arg choice="req"><replaceable>table</replaceable></arg>
+                               <arg choice="req"><replaceable>chain</replaceable></arg>
+                               <arg choice="req"><replaceable>hook</replaceable></arg>
+                               <arg choice="req"><replaceable>priority</replaceable></arg>
+                       </cmdsynopsis>
+                       <cmdsynopsis>
+                               <command>chain</command>
+                               <group choice="req">
+                                       <arg>add</arg>
+                                       <arg>delete</arg>
+                                       <arg>list</arg>
+                                       <arg>flush</arg>
+                               </group>
+                               <arg choice="opt"><replaceable>family</replaceable></arg>
+                               <arg choice="req"><replaceable>table</replaceable></arg>
+                               <arg choice="req"><replaceable>chain</replaceable></arg>
+                       </cmdsynopsis>
+               </para>
+
+               <para>
+                       Chains are containers for rules. They exist in two kinds,
+                       basechains and regular chains. A basecase is an entry point for
+                       packets from the networking stack, a regular chain may be used
+                       as jump target and is used for better rule organization.
+               </para>
+
+               <variablelist>
+                       <varlistentry>
+                               <term><option>add</option></term>
+                               <listitem>
+                                       <para>
+                                               Add a new chain in the specified table. When a hook and priority
+                                               value are specified, the chain is created as a base chain and hooked
+                                               up to the networking stack.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>delete</option></term>
+                               <listitem>
+                                       <para>
+                                               Delete the specified chain.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>list</option></term>
+                               <listitem>
+                                       <para>
+                                               List all rules of the specified chain.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term><option>flush</option></term>
+                               <listitem>
+                                       <para>
+                                               Flush all rules of the specified chain.
+                                       </para>
+                               </listitem>
+                       </varlistentry>
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>Rules</title>
+               <para>
+                       <cmdsynopsis>
+                               <command>rule</command>
+                               <group choice="req">
+                                       <arg>add</arg>
+                                       <arg>delete</arg>
+                               </group>
+                               <arg choice="opt"><replaceable>family</replaceable></arg>
+                               <arg choice="req"><replaceable>table</replaceable></arg>
+                               <arg choice="req"><replaceable>chain</replaceable></arg>
+                               <arg choice="opt">handle <replaceable>handle</replaceable></arg>
+                               <arg choice="req" rep="repeat"><replaceable>statement</replaceable></arg>
+                       </cmdsynopsis>
+               </para>
+               <para>
+                       Rules are constructed from two kinds of components according to a set
+                       of rules: expressions and statements. The lowest order expression is a
+                       primary expression, representing either a constant or a single datum
+                       from a packets payload, meta data or a stateful module. Primary expressions
+                       can be used as arguments to relational expressions (equality,
+                       set membership, ...) to construct match expressions.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Primary expressions</title>
+               <refsect2>
+                       <title>Meta expressions</title>
+                       <para>
+                               A meta expression refers to meta data associated with a packet.
+                       </para>
+                       <table frame="all">
+                               <title>Meta expressions</title>
+                               <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <colspec colname='c3'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                                       <entry>Type</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>length</entry>
+                                                       <entry>Length of the packet in bytes</entry>
+                                                       <entry>Numeric (32 bit)</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>protocol</entry>
+                                                       <entry>Ethertype protocol value</entry>
+                                                       <entry>ethertype</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>priority</entry>
+                                                       <entry>TC packet priority</entry>
+                                                       <entry>Numeric (32 bit)</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>mark</entry>
+                                                       <entry>Packet mark</entry>
+                                                       <entry>packetmark</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>iif</entry>
+                                                       <entry>Input interface index</entry>
+                                                       <entry>ifindex</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>iifname</entry>
+                                                       <entry>Input interface name</entry>
+                                                       <entry>ifname</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>iiftype</entry>
+                                                       <entry>Input interface hardware type</entry>
+                                                       <entry>hwtype</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>oif</entry>
+                                                       <entry>Output interface index</entry>
+                                                       <entry>ifindex</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>oifname</entry>
+                                                       <entry>Output interface name</entry>
+                                                       <entry>ifname</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>oiftype</entry>
+                                                       <entry>Output interface hardware type</entry>
+                                                       <entry>hwtype</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>skuid</entry>
+                                                       <entry>UID associated with originating socket</entry>
+                                                       <entry>uid</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>skgid</entry>
+                                                       <entry>GID associated with originating socket</entry>
+                                                       <entry>gid</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>rtclassid</entry>
+                                                       <entry>Routing realm</entry>
+                                                       <entry>realm</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+                       <table frame="all">
+                               <title>Meta expression specific types</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Type</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>ifindex</entry>
+                                                       <entry>
+                                                               Interface index (32 bit number). Can be specified numerically
+                                                               or as name of an existing interface.
+                                                       </entry>
+                                               </row>
+                                               <row>
+                                                       <entry>ifname</entry>
+                                                       <entry>
+                                                               Interface name (16 byte string). Does not have to exist.
+                                                       </entry>
+                                               </row>
+                                               <row>
+                                                       <entry>uid</entry>
+                                                       <entry>
+                                                               User ID (32 bit number). Can be specified numerically or as
+                                                               user name.
+                                                       </entry>
+                                               </row>
+                                               <row>
+                                                       <entry>gid</entry>
+                                                       <entry>
+                                                               Group ID (32 bit number). Can be specified numerically or as
+                                                               group name.
+                                                       </entry>
+                                               </row>
+                                               <row>
+                                                       <entry>realm</entry>
+                                                       <entry>
+                                                               Routing Realm (32 bit number). Can be specified numerically
+                                                               or as symbolic name defined in /etc/iproute2/rt_realms.
+                                                       </entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+               </refsect2>
+
+               <refsect2>
+                       <title>Payload expressions</title>
+                       <table frame="all">
+                               <title>Ethernet header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>daddr</entry>
+                                                       <entry>Destination address</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>saddr</entry>
+                                                       <entry>Source address</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>type</entry>
+                                                       <entry>EtherType</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>VLAN header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>id</entry>
+                                                       <entry>VLAN ID (VID)</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>cfi</entry>
+                                                       <entry>Canonical Format Indicator</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>pcp</entry>
+                                                       <entry>Priority code point</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>type</entry>
+                                                       <entry>EtherType</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>ARP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>htype</entry>
+                                                       <entry>ARP hardware type</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>ptype</entry>
+                                                       <entry>EtherType</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>hlen</entry>
+                                                       <entry>Hardware address len</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>plen</entry>
+                                                       <entry>Protocol address len</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>op</entry>
+                                                       <entry>Operation</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>IPv4 header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>version</entry>
+                                                       <entry>IP header version (4)</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>hdrlength</entry>
+                                                       <entry>IP header length including options</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>tos</entry>
+                                                       <entry>Type Of Service</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>length</entry>
+                                                       <entry>Total packet length</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>id</entry>
+                                                       <entry>IP ID</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>frag-off</entry>
+                                                       <entry>Fragment offset</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>ttl</entry>
+                                                       <entry>Time to live</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>protocol</entry>
+                                                       <entry>Upper layer protocol</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>checksum</entry>
+                                                       <entry>IP header checksum</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>saddr</entry>
+                                                       <entry>Source address</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>daddr</entry>
+                                                       <entry>Destination address</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>IPv6 header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>version</entry>
+                                                       <entry>IP header version (6)</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>priority</entry>
+                                                       <entry></entry>
+                                               </row>
+                                               <row>
+                                                       <entry>flowlabel</entry>
+                                                       <entry></entry>
+                                               </row>
+                                               <row>
+                                                       <entry>length</entry>
+                                                       <entry></entry>
+                                               </row>
+                                               <row>
+                                                       <entry>nexthdr</entry>
+                                                       <entry>Nexthdr protocol</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>hoplimit</entry>
+                                                       <entry></entry>
+                                               </row>
+                                               <row>
+                                                       <entry>saddr</entry>
+                                                       <entry>Source address</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>daddr</entry>
+                                                       <entry>Destination address</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>SCTP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>sport</entry>
+                                                       <entry>Source port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>dport</entry>
+                                                       <entry>Destination port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>vtag</entry>
+                                                       <entry>Verfication Tag</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>checksum</entry>
+                                                       <entry>Checksum</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>DCCP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>sport</entry>
+                                                       <entry>Source port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>dport</entry>
+                                                       <entry>Destination port</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>TCP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>sport</entry>
+                                                       <entry>Source port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>dport</entry>
+                                                       <entry>Destination port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>sequence</entry>
+                                                       <entry>Sequence number</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>ackseq</entry>
+                                                       <entry>Acknowledgement number</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>doff</entry>
+                                                       <entry>Data offset</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>reserved</entry>
+                                                       <entry>Reserved area</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>flags</entry>
+                                                       <entry>TCP flags</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>window</entry>
+                                                       <entry>Window</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>checksum</entry>
+                                                       <entry>Checksum</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>urgptr</entry>
+                                                       <entry>Urgent pointer</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>UDP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>sport</entry>
+                                                       <entry>Source port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>dport</entry>
+                                                       <entry>Destination port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>length</entry>
+                                                       <entry>Total packet length</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>checksum</entry>
+                                                       <entry>Checksum</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>UDP-Lite header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>sport</entry>
+                                                       <entry>Source port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>dport</entry>
+                                                       <entry>Destination port</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>cscov</entry>
+                                                       <entry>Checksum coverage</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>checksum</entry>
+                                                       <entry>Checksum</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+
+                       <table frame="all">
+                               <title>AH header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>nexthdr</entry>
+                                                       <entry>Next header protocol</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>hdrlength</entry>
+                                                       <entry>AH Header length</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>reserved</entry>
+                                                       <entry>Reserved area</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>spi</entry>
+                                                       <entry>Security Parameter Index</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>sequence</entry>
+                                                       <entry>Sequence number</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>ESP header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>spi</entry>
+                                                       <entry>Security Parameter Index</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>sequence</entry>
+                                                       <entry>Sequence number</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+
+                       <table frame="all">
+                               <title>IPComp header expression</title>
+                               <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+                                       <colspec colname='c1'/>
+                                       <colspec colname='c2'/>
+                                       <thead>
+                                               <row>
+                                                       <entry>Keyword</entry>
+                                                       <entry>Description</entry>
+                                               </row>
+                                       </thead>
+                                       <tbody>
+                                               <row>
+                                                       <entry>nexthdr</entry>
+                                                       <entry>Next header protocol</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>flags</entry>
+                                                       <entry>Flags</entry>
+                                               </row>
+                                               <row>
+                                                       <entry>cfi</entry>
+                                                       <entry>Compression Parameter Index</entry>
+                                               </row>
+                                       </tbody>
+                               </tgroup>
+                       </table>
+               </refsect2>
+       </refsect1>
+
+       <refsect1>
+               <title>Exit status</title>
+               <para>
+                       On success, nftables exits with a status of 0. Unspecified
+                       errors cause it to exit with a status of 1, memory allocation
+                       errors with a status of 2.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>See Also</title>
+               <para>
+                       <simplelist type="inline">
+                               <member>iptables(8)</member>
+                               <member>ip6tables(8)</member>
+                               <member>arptables(8)</member>
+                               <member>ebtables(8)</member>
+                               <member>ip(8)</member>
+                               <member>tc(8)</member>
+                       </simplelist>
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Authors</title>
+               <para>
+                       nftables was written by Patrick McHardy.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Copyright</title>
+               <para>
+                       Copyright &copy; 2008 Patrick McHardy <email>kaber@trash.net</email>
+               </para>
+               <para>
+                       This program is free software; you can redistribute it and/or modify
+                       it under the terms of the GNU General Public License version 2 as
+                       published by the Free Software Foundation.
+               </para>
+       </refsect1>
+</refentry>
diff --git a/files/Makefile.in b/files/Makefile.in
new file mode 100644 (file)
index 0000000..cd67c37
--- /dev/null
@@ -0,0 +1,4 @@
+install:
+       @echo -e "  INSTALL\tfiles"
+       $(MKDIR_P) $(DESTDIR)/$(confdir)
+       $(INSTALL) -m 755 -o root -g root $(SUBDIR)nftables/* $(DESTDIR)/$(confdir)/
diff --git a/files/nftables/ipv4-filter b/files/nftables/ipv4-filter
new file mode 100644 (file)
index 0000000..3f96214
--- /dev/null
@@ -0,0 +1,7 @@
+#! nft -f
+
+table filter {
+       chain input             { hook NF_INET_LOCAL_IN         0; }
+       chain forward           { hook NF_INET_FORWARD          0; }
+       chain output            { hook NF_INET_LOCAL_OUT        0; }
+}
diff --git a/files/nftables/ipv4-mangle b/files/nftables/ipv4-mangle
new file mode 100644 (file)
index 0000000..339cace
--- /dev/null
@@ -0,0 +1,9 @@
+#! nft -f
+
+table mangle {
+       chain prerouting        { hook NF_INET_PRE_ROUTING      -150; }
+       chain input             { hook NF_INET_LOCAL_IN         -150; }
+       chain forward           { hook NF_INET_FORWARD          -150; }
+       chain output            { hook NF_INET_LOCAL_OUT        -150; }
+       chain postrouting       { hook NF_INET_POST_ROUTING     -150; }
+}
diff --git a/files/nftables/ipv4-raw b/files/nftables/ipv4-raw
new file mode 100644 (file)
index 0000000..6bc2181
--- /dev/null
@@ -0,0 +1,6 @@
+#! nft -f
+
+table raw {
+       chain prerouting        { hook NF_INET_PRE_ROUTING      -300; }
+       chain output            { hook NF_INET_LOCAL_OUT        -300; }
+}
diff --git a/files/nftables/ipv4-security b/files/nftables/ipv4-security
new file mode 100644 (file)
index 0000000..3ac5bd2
--- /dev/null
@@ -0,0 +1,7 @@
+#! nft -f
+
+table security {
+       chain input             { hook NF_INET_LOCAL_IN         50; }
+       chain forward           { hook NF_INET_FORWARD          50; }
+       chain output            { hook NF_INET_LOCAL_OUT        50; }
+}
diff --git a/files/nftables/ipv6-filter b/files/nftables/ipv6-filter
new file mode 100644 (file)
index 0000000..9e41278
--- /dev/null
@@ -0,0 +1,7 @@
+#! nft -f
+
+table ip6 filter {
+       chain input             { hook NF_INET_LOCAL_IN         0; }
+       chain forward           { hook NF_INET_FORWARD          0; }
+       chain output            { hook NF_INET_LOCAL_OUT        0; }
+}
diff --git a/files/nftables/ipv6-mangle b/files/nftables/ipv6-mangle
new file mode 100644 (file)
index 0000000..dc18c7a
--- /dev/null
@@ -0,0 +1,9 @@
+#! nft -f
+
+table ip6 mangle {
+       chain prerouting        { hook NF_INET_PRE_ROUTING      -150; }
+       chain input             { hook NF_INET_LOCAL_IN         -150; }
+       chain forward           { hook NF_INET_FORWARD          -150; }
+       chain output            { hook NF_INET_LOCAL_OUT        -150; }
+       chain postrouting       { hook NF_INET_POST_ROUTING     -150; }
+}
diff --git a/files/nftables/ipv6-security b/files/nftables/ipv6-security
new file mode 100644 (file)
index 0000000..f379bfd
--- /dev/null
@@ -0,0 +1,7 @@
+#! nft -f
+
+table ip6 security {
+       chain input             { hook NF_INET_LOCAL_IN         50; }
+       chain forward           { hook NF_INET_FORWARD          50; }
+       chain output            { hook NF_INET_LOCAL_OUT        50; }
+}
diff --git a/include/ct.h b/include/ct.h
new file mode 100644 (file)
index 0000000..946bc29
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _CT_H
+#define _CT_H
+
+/**
+ * struct ct_template
+ *
+ * @token:     parser token for the expression
+ * @dtype:     data type of the expression
+ * @len:       length of the expression
+ * @byteorder: byteorder
+ */
+struct ct_template {
+       const char              *token;
+       const struct datatype   *dtype;
+       enum byteorder          byteorder;
+       unsigned int            len;
+};
+
+#define CT_TEMPLATE(__token, __dtype, __byteorder, __len) {    \
+       .token          = (__token),                            \
+       .dtype          = (__dtype),                            \
+       .byteorder      = (__byteorder),                        \
+       .len            = (__len),                              \
+}
+
+extern struct expr *ct_expr_alloc(const struct location *loc,
+                                 enum nft_ct_keys key);
+
+#endif /* _CT_H */
diff --git a/include/datatype.h b/include/datatype.h
new file mode 100644 (file)
index 0000000..c6aeeed
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef _DATATYPE_H
+#define _DATATYPE_H
+
+/**
+ * enum datatypes
+ *
+ * @TYPE_INVALID:      uninitialized
+ * @TYPE_VERDICT:      nftables verdict
+ * @TYPE_BITMASK:      bitmask
+ * @TYPE_INTEGER:      integer
+ * @TYPE_STRING:       string
+ * @TYPE_LLADDR:       link layer address (integer subtype)
+ * @TYPE_IPADDR:       IPv4 address (integer subtype)
+ * @TYPE_IP6ADDR:      IPv6 address (integer subtype)
+ * @TYPE_ETHERTYPE:    EtherType (integer subtype)
+ * @TYPE_ARPOP:                ARP operation (integer subtype)
+ * @TYPE_INET_PROTOCOL:        internet protocol (integer subtype)
+ * @TYPE_INET_SERVICE: internet service (integer subtype)
+ * @TYPE_ICMP_TYPE:    ICMP type codes (integer subtype)
+ * @TYPE_TCP_FLAG:     TCP flag (bitmask subtype)
+ * @TYPE_MH_TYPE:      Mobility Header type (integer subtype)
+ * @TYPE_TIME:         relative time
+ * @TYPE_MARK:         packet mark (integer subtype)
+ * @TYPE_IFINDEX:      interface index (integer subtype)
+ * @TYPE_ARPHRD:       interface type (integer subtype)
+ * @TYPE_REALM:                routing realm (integer subtype)
+ * @TYPE_TC_HANDLE:    TC handle (integer subtype)
+ * @TYPE_UID:          user ID (integer subtype)
+ * @TYPE_GID:          group ID (integer subtype)
+ * @TYPE_CT_STATE:     conntrack state (bitmask subtype)
+ * @TYPE_CT_DIR:       conntrack direction
+ * @TYPE_CT_STATUS:    conntrack status (bitmask subtype)
+ */
+enum datatypes {
+       TYPE_INVALID,
+       TYPE_VERDICT,
+       TYPE_BITMASK,
+       TYPE_INTEGER,
+       TYPE_STRING,
+       TYPE_LLADDR,
+       TYPE_IPADDR,
+       TYPE_IP6ADDR,
+       TYPE_ETHERTYPE,
+       TYPE_ARPOP,
+       TYPE_INET_PROTOCOL,
+       TYPE_INET_SERVICE,
+       TYPE_ICMP_TYPE,
+       TYPE_TCP_FLAG,
+       TYPE_MH_TYPE,
+       TYPE_TIME,
+       TYPE_MARK,
+       TYPE_IFINDEX,
+       TYPE_ARPHRD,
+       TYPE_REALM,
+       TYPE_TC_HANDLE,
+       TYPE_UID,
+       TYPE_GID,
+       TYPE_CT_STATE,
+       TYPE_CT_DIR,
+       TYPE_CT_STATUS,
+};
+
+/**
+ * enum byteorder
+ *
+ * @BYTEORDER_INVALID:         uninitialized/unknown
+ * @BYTEORDER_HOST_ENDIAN:     host endian
+ * @BYTEORDER_BIG_ENDIAN:      big endian
+ */
+enum byteorder {
+       BYTEORDER_INVALID,
+       BYTEORDER_HOST_ENDIAN,
+       BYTEORDER_BIG_ENDIAN,
+};
+
+struct expr;
+
+/**
+ * struct datatype
+ *
+ * @type:      numeric identifier
+ * @name:      type name for diagnostics
+ * @basetype:  basetype for subtypes, determines type compatibilty
+ * @basefmt:   format string for basetype
+ * @print:     function to print a constant of this type
+ * @parse:     function to parse a symbol and return an expression
+ * @sym_tbl:   symbol table for this type
+ */
+struct datatype {
+       enum datatypes                  type;
+       const char                      *name;
+       const struct datatype           *basetype;
+       const char                      *basefmt;
+       void                            (*print)(const struct expr *expr);
+       struct error_record             *(*parse)(const struct expr *sym,
+                                                 struct expr **res);
+       const struct symbol_table       *sym_tbl;
+};
+
+extern struct error_record *symbol_parse(const struct expr *sym,
+                                        struct expr **res);
+extern void datatype_print(const struct expr *expr);
+
+/**
+ * struct symbolic_constant - symbol <-> constant mapping
+ *
+ * @identifier:        symbol
+ * @value:     symbolic value
+ */
+struct symbolic_constant {
+       const char                      *identifier;
+       uint64_t                        value;
+};
+
+#define SYMBOL(id, v)  { .identifier = (id), .value = (v) }
+#define SYMBOL_LIST_END        (struct symbolic_constant) { }
+
+/**
+ * struct symbol_table - type construction from symbolic values
+ *
+ * @byteorder: byteorder of symbol values
+ * @size:      size of symbol values
+ * @symbols:   the symbols
+ */
+struct symbol_table {
+       enum byteorder                  byteorder;
+       unsigned int                    size;
+       struct symbolic_constant        symbols[];
+};
+
+extern struct error_record *symbolic_constant_parse(const struct expr *sym,
+                                                   const struct symbol_table *tbl,
+                                                   struct expr **res);
+extern void symbolic_constant_print(const struct symbol_table *tbl,
+                                   const struct expr *expr);
+extern void symbol_table_print(const struct symbol_table *tbl);
+
+extern struct symbol_table *rt_symbol_table_init(const char *filename);
+extern void rt_symbol_table_free(struct symbol_table *tbl);
+
+extern const struct datatype invalid_type;
+extern const struct datatype verdict_type;
+extern const struct datatype bitmask_type;
+extern const struct datatype integer_type;
+extern const struct datatype string_type;
+extern const struct datatype lladdr_type;
+extern const struct datatype ipaddr_type;
+extern const struct datatype ip6addr_type;
+extern const struct datatype ethertype_type;
+extern const struct datatype arphrd_type;
+extern const struct datatype inet_protocol_type;
+extern const struct datatype inet_service_type;
+extern const struct datatype mark_type;
+extern const struct datatype time_type;
+
+#endif /* _DATATYPE_H */
diff --git a/include/erec.h b/include/erec.h
new file mode 100644 (file)
index 0000000..51f3958
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _EREC_H
+#define _EREC_H
+
+#include <nftables.h>
+#include <utils.h>
+
+/**
+ * enum error_record_types
+ *
+ * @EREC_INFORMATIONAL:        informational message
+ * @EREC_WARNING:      warning message
+ * @EREC_ERROR:                error message
+ */
+enum error_record_types {
+       EREC_INFORMATIONAL,
+       EREC_WARNING,
+       EREC_ERROR,
+};
+
+#define EREC_MSGBUFSIZE                1024
+#define EREC_LOCATIONS_MAX     3
+
+/**
+ * struct error_record
+ *
+ * @list:              message queue node
+ * @type:              error record type
+ * @num_locations:     number of locations
+ * @locations:         location(s) of error
+ * @msg:               message
+ */
+struct error_record {
+       struct list_head        list;
+       enum error_record_types type;
+       unsigned int            num_locations;
+       struct location         locations[EREC_LOCATIONS_MAX];
+       char                    *msg;
+};
+
+extern struct error_record *erec_vcreate(enum error_record_types type,
+                                        const struct location *loc,
+                                        const char *fmt, va_list ap)
+                                        __gmp_fmtstring(3, 0);
+extern struct error_record *erec_create(enum error_record_types type,
+                                       const struct location *loc,
+                                       const char *fmt, ...) __gmp_fmtstring(3, 4);
+extern void erec_add_location(struct error_record *erec,
+                             const struct location *loc);
+
+#define error(loc, fmt, args...) \
+       erec_create(EREC_ERROR, (loc), (fmt), ## args)
+#define warning(loc, fmt, args...) \
+       erec_create(EREC_WARNING, (loc), (fmt), ## args)
+
+static inline void erec_queue(struct error_record *erec,
+                             struct list_head *queue)
+{
+       list_add_tail(&erec->list, queue);
+}
+
+extern void erec_print(FILE *f, const struct error_record *erec);
+extern void erec_print_list(FILE *f, struct list_head *list);
+
+#endif /* _EREC_H */
diff --git a/include/expression.h b/include/expression.h
new file mode 100644 (file)
index 0000000..7258546
--- /dev/null
@@ -0,0 +1,323 @@
+#ifndef _EXPRESSION_H
+#define _EXPRESSION_H
+
+#include <stdbool.h>
+#include <gmp.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <nftables.h>
+#include <datatype.h>
+#include <utils.h>
+#include <list.h>
+
+/**
+ * enum expr_types
+ *
+ * @EXPR_INVALID:      uninitialized type, should not happen
+ * @EXPR_VERDICT:      nftables verdict expression
+ * @EXPR_SYMBOL:       unparsed symbol
+ * @EXPR_VALUE:                literal numeric or string expression
+ * @EXPR_PREFIX:       prefixed expression
+ * @EXPR_RANGE:                literal range
+ * @EXPR_PAYLOAD:      payload expression
+ * @EXPR_EXTHDR:       exthdr expression
+ * @EXPR_META:         meta expression
+ * @EXPR_CT:           conntrack expression
+ * @EXPR_CONCAT:       concatenation
+ * @EXPR_LIST:         list of expressions
+ * @EXPR_SET:          literal set
+ * @EXPR_MAPPING:      a single mapping (key => value)
+ * @EXPR_MAP:          map operation (expr map { EXPR_MAPPING, ... })
+ * @EXPR_UNARY:                byteorder conversion, generated during evaluation
+ * @EXPR_BINOP:                binary operations (bitwise, shifts)
+ * @EXPR_RELATIONAL:   equality and relational expressions
+ */
+enum expr_types {
+       EXPR_INVALID,
+       EXPR_VERDICT,
+       EXPR_SYMBOL,
+       EXPR_VALUE,
+       EXPR_PREFIX,
+       EXPR_RANGE,
+       EXPR_PAYLOAD,
+       EXPR_EXTHDR,
+       EXPR_META,
+       EXPR_CT,
+       EXPR_CONCAT,
+       EXPR_LIST,
+       EXPR_SET,
+       EXPR_MAPPING,
+       EXPR_MAP,
+       EXPR_UNARY,
+       EXPR_BINOP,
+       EXPR_RELATIONAL,
+};
+
+enum ops {
+       OP_INVALID,
+       /* Unary operations */
+       OP_HTON,
+       OP_NTOH,
+       /* Binary operations */
+       OP_LSHIFT,
+       OP_RSHIFT,
+       OP_AND,
+       OP_XOR,
+       OP_OR,
+       /* Relational operations */
+       OP_EQ,
+       OP_NEQ,
+       OP_LT,
+       OP_GT,
+       OP_LTE,
+       OP_GTE,
+       /* Range comparison */
+       OP_RANGE,
+       /* Flag comparison */
+       OP_FLAGCMP,
+       /* Set lookup */
+       OP_LOOKUP,
+};
+
+extern const char *expr_op_symbols[];
+
+/**
+ * struct expr_ctx - type context for symbol parsing during evaluation
+ *
+ * @dtype:     expected datatype
+ * @len:       expected len
+ */
+struct expr_ctx {
+       const struct datatype   *dtype;
+       unsigned int            len;
+};
+
+static inline void expr_set_context(struct expr_ctx *ctx,
+                                   const struct datatype *dtype,
+                                   unsigned int len)
+{
+       ctx->dtype = dtype;
+       ctx->len   = len;
+}
+
+/**
+ * struct expr_ops
+ *
+ * @type:      expression type
+ * @name:      expression name for diagnostics
+ * @destroy:   destructor, must release inner expressions
+ * @set_type:  function to promote type and byteorder of inner types
+ * @print:     function to print the expression
+ */
+struct expr_ops {
+       enum expr_types         type;
+       const char              *name;
+       void                    (*destroy)(struct expr *expr);
+       void                    (*set_type)(const struct expr *expr,
+                                           const struct datatype *dtype,
+                                           enum byteorder byteorder);
+       void                    (*print)(const struct expr *expr);
+};
+
+/**
+ * enum expr_flags
+ *
+ * @EXPR_F_PRIMARY:            primary expression
+ * @EXPR_F_CONSTANT:           constant expression
+ * @EXPR_F_SINGLETON:          singleton (implies primary and constant)
+ * @EXPR_F_INTERVAL_END:       set member ends an open interval
+ * @SET_F_INTERVAL:            set includes ranges and/or prefix expressions
+ */
+enum expr_flags {
+       EXPR_F_PRIMARY          = 0x1,
+       EXPR_F_CONSTANT         = 0x2,
+       EXPR_F_SINGLETON        = 0x4,
+       EXPR_F_INTERVAL_END     = 0x8,
+       SET_F_INTERVAL          = 0x10,
+};
+
+#include <payload.h>
+#include <exthdr.h>
+#include <meta.h>
+#include <ct.h>
+
+/**
+ * struct expr
+ *
+ * @list:      list node
+ * @location:  location from parser
+ * @refcnt:    reference count
+ * @flags:     mask of enum expr_flags
+ * @dtype:     data type of expression
+ * @byteorder: byteorder of expression
+ * @len:       length of expression
+ * @ops:       expression ops
+ * @op:                operation for unary, binary and relational expressions
+ * @union:     type specific data
+ */
+struct expr {
+       struct list_head        list;
+       struct location         location;
+
+       unsigned int            refcnt;
+       unsigned int            flags;
+
+       const struct datatype   *dtype;
+       enum byteorder          byteorder;
+       unsigned int            len;
+
+       const struct expr_ops   *ops;
+       enum ops                op;
+       union {
+               struct {
+                       /* EXPR_SYMBOL */
+                       const struct datatype   *sym_type;
+                       const char              *identifier;
+               };
+               struct {
+                       /* EXPR_VERDICT */
+                       int                     verdict;
+                       const char              *chain;
+               };
+               struct {
+                       /* EXPR_VALUE */
+                       mpz_t                   value;
+               };
+               struct {
+                       /* EXPR_PREFIX */
+                       struct expr             *expr;
+                       unsigned int            prefix_len;
+               };
+               struct {
+                       /* EXPR_CONCAT, EXPR_LIST, EXPR_SET */
+                       struct list_head        expressions;
+                       unsigned int            size;
+               };
+               struct {
+                       /* EXPR_UNARY */
+                       struct expr             *arg;
+               };
+               struct {
+                       /* EXPR_RANGE, EXPR_BINOP, EXPR_MAPPING, EXPR_RELATIONAL */
+                       struct expr             *left;
+                       struct expr             *right;
+               };
+               struct {
+                       /* EXPR_MAP */
+                       struct expr             *expr;
+                       struct expr             *mappings;
+               };
+
+               struct {
+                       /* EXPR_PAYLOAD */
+                       const struct payload_desc       *desc;
+                       const struct payload_template   *tmpl;
+                       enum payload_bases              base;
+                       unsigned int                    offset;
+                       unsigned int                    flags;
+               } payload;
+               struct {
+                       /* EXPR_EXTHDR */
+                       const struct exthdr_desc        *desc;
+                       const struct payload_template   *tmpl;
+               } exthdr;
+               struct {
+                       /* EXPR_META */
+                       enum nft_meta_keys      key;
+               } meta;
+               struct {
+                       /* EXPR_CT */
+                       enum nft_ct_keys        key;
+               } ct;
+       };
+};
+
+extern struct expr *expr_alloc(const struct location *loc,
+                              const struct expr_ops *ops,
+                              const struct datatype *dtype,
+                              enum byteorder byteorder, unsigned int len);
+extern struct expr *expr_get(struct expr *expr);
+extern void expr_free(struct expr *expr);
+extern void expr_print(const struct expr *expr);
+extern void expr_describe(const struct expr *expr);
+
+extern const struct datatype *expr_basetype(const struct expr *expr);
+extern void expr_set_type(struct expr *expr, const struct datatype *dtype,
+                         enum byteorder byteorder);
+
+struct eval_ctx;
+extern int expr_binary_error(struct eval_ctx *ctx,
+                            const struct expr *e1, const struct expr *e2,
+                            const char *fmt, ...) __gmp_fmtstring(4, 5);
+
+#define expr_error(ctx, expr, fmt, args...) \
+       expr_binary_error(ctx, expr, NULL, fmt, ## args)
+
+static inline bool expr_is_primary(const struct expr *expr)
+{
+       return expr->flags & EXPR_F_PRIMARY ? true : false;
+}
+
+static inline bool expr_is_constant(const struct expr *expr)
+{
+       return expr->flags & EXPR_F_CONSTANT ? true : false;
+}
+
+static inline bool expr_is_singleton(const struct expr *expr)
+{
+       return expr->flags & EXPR_F_SINGLETON ? true : false;
+}
+
+extern struct expr *unary_expr_alloc(const struct location *loc,
+                                    enum ops op, struct expr *arg);
+
+extern struct expr *binop_expr_alloc(const struct location *loc, enum ops op,
+                                    struct expr *left, struct expr *right);
+
+extern struct expr *relational_expr_alloc(const struct location *loc, enum ops op,
+                                         struct expr *left, struct expr *right);
+
+extern struct expr *verdict_expr_alloc(const struct location *loc,
+                                      int verdict, const char *chain);
+
+extern struct expr *symbol_expr_alloc(const struct location *loc,
+                                     const char *identifier);
+
+static inline void symbol_expr_set_type(struct expr *expr,
+                                       const struct datatype *dtype)
+{
+       if (expr->ops->type == EXPR_SYMBOL)
+               expr->sym_type = dtype;
+}
+
+extern struct expr *constant_expr_alloc(const struct location *loc,
+                                       const struct datatype *dtype,
+                                       enum byteorder byteorder,
+                                       unsigned int len, const void *data);
+extern struct expr *constant_expr_join(const struct expr *e1,
+                                      const struct expr *e2);
+extern struct expr *constant_expr_splice(struct expr *expr, unsigned int len);
+
+extern struct expr *prefix_expr_alloc(const struct location *loc,
+                                     struct expr *expr,
+                                     unsigned int prefix_len);
+
+extern struct expr *range_expr_alloc(const struct location *loc,
+                                    struct expr *low, struct expr *high);
+
+extern void compound_expr_add(struct expr *compound, struct expr *expr);
+extern void compound_expr_remove(struct expr *compound, struct expr *expr);
+
+extern struct expr *concat_expr_alloc(const struct location *loc);
+
+extern struct expr *list_expr_alloc(const struct location *loc);
+
+extern struct expr *set_expr_alloc(const struct location *loc);
+extern void set_to_intervals(struct expr *set);
+
+extern struct expr *mapping_expr_alloc(const struct location *loc,
+                                      struct expr *from, struct expr *to);
+extern struct expr *map_expr_alloc(const struct location *loc,
+                                  struct expr *arg, struct expr *list);
+
+#endif /* _EXPRESSION_H */
diff --git a/include/exthdr.h b/include/exthdr.h
new file mode 100644 (file)
index 0000000..8ef0b39
--- /dev/null
@@ -0,0 +1,84 @@
+#ifndef _EXTHDR_H
+#define _EXTHDR_H
+
+/**
+ * struct exthdr_desc - extension header description
+ *
+ * @name:      extension header name
+ * @type:      extension header protocol value
+ * @templates: header templates
+ */
+struct exthdr_desc {
+       const char                      *name;
+       uint8_t                         type;
+       struct payload_template         templates[10];
+};
+
+extern struct expr *exthdr_expr_alloc(const struct location *loc,
+                                     const struct exthdr_desc *desc,
+                                     uint8_t type);
+
+extern void exthdr_init_raw(struct expr *expr, uint8_t type,
+                           unsigned int offset, unsigned int len);
+
+
+enum hbh_hdr_fields {
+       HBHHDR_INVALID,
+       HBHHDR_NEXTHDR,
+       HBHHDR_HDRLENGTH,
+};
+
+enum rt_hdr_fields {
+       RTHDR_INVALID,
+       RTHDR_NEXTHDR,
+       RTHDR_HDRLENGTH,
+       RTHDR_TYPE,
+       RTHDR_SEG_LEFT,
+};
+
+enum rt0_hdr_fields {
+       RT0HDR_INVALID,
+       RT0HDR_RESERVED,
+       RT0HDR_ADDR_1,
+};
+
+enum rt2_hdr_fields {
+       RT2HDR_INVALID,
+       RT2HDR_RESERVED,
+       RT2HDR_ADDR,
+};
+
+enum frag_hdr_fields {
+       FRAGHDR_INVALID,
+       FRAGHDR_NEXTHDR,
+       FRAGHDR_RESERVED,
+       FRAGHDR_FRAG_OFF,
+       FRAGHDR_RESERVED2,
+       FRAGHDR_MFRAGS,
+       FRAGHDR_ID,
+};
+
+enum dst_hdr_fields {
+       DSTHDR_INVALID,
+       DSTHDR_NEXTHDR,
+       DSTHDR_HDRLENGTH,
+};
+
+enum mh_hdr_fields {
+       MHHDR_INVALID,
+       MHHDR_NEXTHDR,
+       MHHDR_HDRLENGTH,
+       MHHDR_TYPE,
+       MHHDR_RESERVED,
+       MHHDR_CHECKSUM,
+};
+
+extern const struct exthdr_desc exthdr_hbh;
+extern const struct exthdr_desc exthdr_rt;
+extern const struct exthdr_desc exthdr_rt0;
+extern const struct exthdr_desc exthdr_rt2;
+extern const struct exthdr_desc exthdr_frag;
+extern const struct exthdr_desc exthdr_dst;
+extern const struct exthdr_desc exthdr_mh;
+
+#endif /* _EXTHDR_H */
diff --git a/include/gmputil.h b/include/gmputil.h
new file mode 100644 (file)
index 0000000..41a6c27
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _GMPUTIL_H
+#define _GMPUTIL_H
+
+#include <gmp.h>
+
+enum mpz_word_order {
+       MPZ_MSWF                = 1,
+       MPZ_LSWF                = -1,
+};
+
+enum mpz_byte_order {
+       MPZ_BIG_ENDIAN          = 1,
+       MPZ_HOST_ENDIAN         = 0,
+       MPZ_LITTLE_ENDIAN       = -1,
+};
+
+extern void mpz_bitmask(mpz_t rop, unsigned int width);
+extern void mpz_init_bitmask(mpz_t rop, unsigned int width);
+extern void mpz_prefixmask(mpz_t rop, unsigned int width, unsigned int prefix_len);
+
+extern void mpz_lshift_ui(mpz_t rop, unsigned int n);
+extern void mpz_rshift_ui(mpz_t rop, unsigned int n);
+
+extern uint64_t mpz_get_uint64(const mpz_t op);
+extern uint32_t mpz_get_uint32(const mpz_t op);
+extern uint16_t mpz_get_uint16(const mpz_t op);
+extern uint8_t mpz_get_uint8(const mpz_t op);
+
+extern uint64_t mpz_get_be64(const mpz_t op);
+extern uint32_t mpz_get_be32(const mpz_t op);
+extern uint16_t mpz_get_be16(const mpz_t op);
+
+enum byteorder;
+extern void *mpz_export_data(void *data, const mpz_t op,
+                            enum byteorder byteorder,
+                            unsigned int len);
+extern void mpz_import_data(mpz_t rop, const void *data,
+                           enum byteorder byteorder,
+                           unsigned int len);
+extern void mpz_switch_byteorder(mpz_t rop, unsigned int len);
+
+#endif /* _GMPUTIL_H */
diff --git a/include/headers.h b/include/headers.h
new file mode 100644 (file)
index 0000000..be20cec
--- /dev/null
@@ -0,0 +1,119 @@
+#ifndef _HEADERS_H
+#define _HEADERS_H
+
+#ifndef IPPROTO_UDPLITE
+# define IPPROTO_UDPLITE       136
+#endif
+
+enum tcp_hdr_flags {
+       TCP_FLAG_FIN    = 0x01,
+       TCP_FLAG_SYN    = 0x02,
+       TCP_FLAG_RST    = 0x04,
+       TCP_FLAG_PSH    = 0x08,
+       TCP_FLAG_ACK    = 0x10,
+       TCP_FLAG_URG    = 0x20,
+       TCP_FLAG_ECN    = 0x40,
+       TCP_FLAG_CWR    = 0x80,
+};
+
+struct ip_auth_hdr {
+       uint8_t         nexthdr;
+       uint8_t         hdrlen;
+       uint16_t        reserved;
+       uint32_t        spi;
+       uint32_t        seq_no;
+};
+
+struct ip_esp_hdr {
+       uint32_t        spi;
+       uint32_t        seq_no;
+};
+
+struct ip_comp_hdr {
+       uint8_t         nexthdr;
+       uint8_t         flags;
+       uint16_t        cpi;
+};
+
+#ifndef IPPROTO_DCCP
+# define IPPROTO_DCCP 33
+#endif
+
+struct dccp_hdr {
+       uint16_t        dccph_sport,
+                       dccph_dport;
+       uint8_t         dccph_doff;
+       uint8_t         dccph_ccval:4,
+                       dccph_cscov:4;
+       uint16_t        dccph_checksum;
+       uint8_t         dccph_reserved:3,
+                       dccph_type:4,
+                       dccph_x:1;
+       uint8_t         dccph_seq2;
+       uint16_t        dccph_seq;
+};
+
+#ifndef IPPROTO_SCTP
+# define IPPROTO_SCTP 132
+#endif
+
+struct sctphdr {
+       uint16_t        source;
+       uint16_t        dest;
+       uint32_t        vtag;
+       uint32_t        checksum;
+};
+
+struct ipv6hdr {
+       uint8_t         version:4,
+                       priority:4;
+       uint8_t         flow_lbl[3];
+
+       uint16_t        payload_len;
+       uint8_t         nexthdr;
+       uint8_t         hop_limit;
+
+       struct in6_addr saddr;
+       struct in6_addr daddr;
+};
+
+struct vlan_hdr {
+       uint16_t        vlan_id:12,
+                       vlan_cfi:1,
+                       vlan_pcp:3;
+       uint16_t        vlan_type;
+};
+
+#ifndef IPPROTO_MH
+# define IPPROTO_MH 135
+#endif
+
+struct ip6_mh {
+       uint8_t         ip6mh_proto;
+       uint8_t         ip6mh_hdrlen;
+       uint8_t         ip6mh_type;
+       uint8_t         ip6mh_reserved;
+       uint16_t        ip6mh_cksum;
+       /* Followed by type specific messages */
+       uint8_t         data[0];
+};
+
+/* RFC 3775 */
+#define IP6_MH_TYPE_BRR                0       /* Binding Refresh Request      */
+#define IP6_MH_TYPE_HOTI       1       /* HOTI Message                 */
+#define IP6_MH_TYPE_COTI       2       /* COTI Message                 */
+#define IP6_MH_TYPE_HOT                3       /* HOT Message                  */
+#define IP6_MH_TYPE_COT                4       /* COT Message                  */
+#define IP6_MH_TYPE_BU         5       /* Binding Update               */
+#define IP6_MH_TYPE_BACK       6       /* Binding ACK                  */
+#define IP6_MH_TYPE_BERROR     7       /* Binding Error                */
+/* RFC 4068 */
+#define IP6_MH_TYPE_FBU                8       /* Fast Binding Update          */
+#define IP6_MH_TYPE_FBACK      9       /* Fast Binding ACK             */
+#define IP6_MH_TYPE_FNA                10      /* Fast Binding Advertisement   */
+/* RFC 5096 */
+#define IP6_MH_TYPE_EMH                11      /* Experimental Mobility Header */
+/* RFC 5142 */
+#define IP6_MH_TYPE_HASM       12      /* Home Agent Switch Message    */
+
+#endif /* _HEADERS_H */
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
new file mode 100644 (file)
index 0000000..2eb00b6
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __LINUX_NETFILTER_H
+#define __LINUX_NETFILTER_H
+
+#include <linux/types.h>
+
+
+/* Responses from hook functions. */
+#define NF_DROP 0
+#define NF_ACCEPT 1
+#define NF_STOLEN 2
+#define NF_QUEUE 3
+#define NF_REPEAT 4
+#define NF_STOP 5
+#define NF_MAX_VERDICT NF_STOP
+
+/* we overload the higher bits for encoding auxiliary data such as the queue
+ * number. Not nice, but better than additional function arguments. */
+#define NF_VERDICT_MASK 0x0000ffff
+#define NF_VERDICT_BITS 16
+
+#define NF_VERDICT_QMASK 0xffff0000
+#define NF_VERDICT_QBITS 16
+
+#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+
+/* only for userspace compatibility */
+/* Generic cache responses from hook functions.
+   <= 0x2000 is used for protocol-flags. */
+#define NFC_UNKNOWN 0x4000
+#define NFC_ALTERED 0x8000
+
+enum nf_inet_hooks {
+       NF_INET_PRE_ROUTING,
+       NF_INET_LOCAL_IN,
+       NF_INET_FORWARD,
+       NF_INET_LOCAL_OUT,
+       NF_INET_POST_ROUTING,
+       NF_INET_NUMHOOKS
+};
+
+enum {
+       NFPROTO_UNSPEC =  0,
+       NFPROTO_IPV4   =  2,
+       NFPROTO_ARP    =  3,
+       NFPROTO_BRIDGE =  7,
+       NFPROTO_IPV6   = 10,
+       NFPROTO_DECNET = 12,
+       NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+       __u32           all[4];
+       __be32          ip;
+       __be32          ip6[4];
+       struct in_addr  in;
+       struct in6_addr in6;
+};
+
+#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
new file mode 100644 (file)
index 0000000..27a1895
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef _NF_CONNTRACK_COMMON_H
+#define _NF_CONNTRACK_COMMON_H
+/* Connection state tracking for netfilter.  This is separated from,
+   but required by, the NAT layer; it can also be used by an iptables
+   extension. */
+enum ip_conntrack_info
+{
+       /* Part of an established connection (either direction). */
+       IP_CT_ESTABLISHED,
+
+       /* Like NEW, but related to an existing connection, or ICMP error
+          (in either direction). */
+       IP_CT_RELATED,
+
+       /* Started a new connection to track (only
+           IP_CT_DIR_ORIGINAL); may be a retransmission. */
+       IP_CT_NEW,
+
+       /* >= this indicates reply direction */
+       IP_CT_IS_REPLY,
+
+       /* Number of distinct IP_CT types (no NEW in reply dirn). */
+       IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+};
+
+#define NF_CT_STATE_INVALID_BIT                (1 << 0)
+#define NF_CT_STATE_BIT(ctinfo)                (1 << ((ctinfo) % IP_CT_IS_REPLY + 1))
+#define NF_CT_STATE_UNTRACKED_BIT      (1 << (IP_CT_NUMBER + 1))
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+       /* It's an expected connection: bit 0 set.  This bit never changed */
+       IPS_EXPECTED_BIT = 0,
+       IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+       /* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
+       IPS_SEEN_REPLY_BIT = 1,
+       IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+       /* Conntrack should never be early-expired. */
+       IPS_ASSURED_BIT = 2,
+       IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+
+       /* Connection is confirmed: originating packet has left box */
+       IPS_CONFIRMED_BIT = 3,
+       IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
+
+       /* Connection needs src nat in orig dir.  This bit never changed. */
+       IPS_SRC_NAT_BIT = 4,
+       IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT),
+
+       /* Connection needs dst nat in orig dir.  This bit never changed. */
+       IPS_DST_NAT_BIT = 5,
+       IPS_DST_NAT = (1 << IPS_DST_NAT_BIT),
+
+       /* Both together. */
+       IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT),
+
+       /* Connection needs TCP sequence adjusted. */
+       IPS_SEQ_ADJUST_BIT = 6,
+       IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT),
+
+       /* NAT initialization bits. */
+       IPS_SRC_NAT_DONE_BIT = 7,
+       IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT),
+
+       IPS_DST_NAT_DONE_BIT = 8,
+       IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT),
+
+       /* Both together */
+       IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE),
+
+       /* Connection is dying (removed from lists), can not be unset. */
+       IPS_DYING_BIT = 9,
+       IPS_DYING = (1 << IPS_DYING_BIT),
+
+       /* Connection has fixed timeout. */
+       IPS_FIXED_TIMEOUT_BIT = 10,
+       IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+};
+
+/* Connection tracking event bits */
+enum ip_conntrack_events
+{
+       /* New conntrack */
+       IPCT_NEW_BIT = 0,
+       IPCT_NEW = (1 << IPCT_NEW_BIT),
+
+       /* Expected connection */
+       IPCT_RELATED_BIT = 1,
+       IPCT_RELATED = (1 << IPCT_RELATED_BIT),
+
+       /* Destroyed conntrack */
+       IPCT_DESTROY_BIT = 2,
+       IPCT_DESTROY = (1 << IPCT_DESTROY_BIT),
+
+       /* Timer has been refreshed */
+       IPCT_REFRESH_BIT = 3,
+       IPCT_REFRESH = (1 << IPCT_REFRESH_BIT),
+
+       /* Status has changed */
+       IPCT_STATUS_BIT = 4,
+       IPCT_STATUS = (1 << IPCT_STATUS_BIT),
+
+       /* Update of protocol info */
+       IPCT_PROTOINFO_BIT = 5,
+       IPCT_PROTOINFO = (1 << IPCT_PROTOINFO_BIT),
+
+       /* Volatile protocol info */
+       IPCT_PROTOINFO_VOLATILE_BIT = 6,
+       IPCT_PROTOINFO_VOLATILE = (1 << IPCT_PROTOINFO_VOLATILE_BIT),
+
+       /* New helper for conntrack */
+       IPCT_HELPER_BIT = 7,
+       IPCT_HELPER = (1 << IPCT_HELPER_BIT),
+
+       /* Update of helper info */
+       IPCT_HELPINFO_BIT = 8,
+       IPCT_HELPINFO = (1 << IPCT_HELPINFO_BIT),
+
+       /* Volatile helper info */
+       IPCT_HELPINFO_VOLATILE_BIT = 9,
+       IPCT_HELPINFO_VOLATILE = (1 << IPCT_HELPINFO_VOLATILE_BIT),
+
+       /* NAT info */
+       IPCT_NATINFO_BIT = 10,
+       IPCT_NATINFO = (1 << IPCT_NATINFO_BIT),
+
+       /* Counter highest bit has been set, unused */
+       IPCT_COUNTER_FILLING_BIT = 11,
+       IPCT_COUNTER_FILLING = (1 << IPCT_COUNTER_FILLING_BIT),
+
+       /* Mark is set */
+       IPCT_MARK_BIT = 12,
+       IPCT_MARK = (1 << IPCT_MARK_BIT),
+
+       /* NAT sequence adjustment */
+       IPCT_NATSEQADJ_BIT = 13,
+       IPCT_NATSEQADJ = (1 << IPCT_NATSEQADJ_BIT),
+
+       /* Secmark is set */
+       IPCT_SECMARK_BIT = 14,
+       IPCT_SECMARK = (1 << IPCT_SECMARK_BIT),
+};
+
+enum ip_conntrack_expect_events {
+       IPEXP_NEW_BIT = 0,
+       IPEXP_NEW = (1 << IPEXP_NEW_BIT),
+};
+
+
+#endif /* _NF_CONNTRACK_COMMON_H */
diff --git a/include/linux/netfilter/nf_conntrack_tuple_common.h b/include/linux/netfilter/nf_conntrack_tuple_common.h
new file mode 100644 (file)
index 0000000..8e145f0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _NF_CONNTRACK_TUPLE_COMMON_H
+#define _NF_CONNTRACK_TUPLE_COMMON_H
+
+enum ip_conntrack_dir
+{
+       IP_CT_DIR_ORIGINAL,
+       IP_CT_DIR_REPLY,
+       IP_CT_DIR_MAX
+};
+
+#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP_CT_IS_REPLY ? IP_CT_DIR_REPLY : IP_CT_DIR_ORIGINAL)
+
+#endif /* _NF_CONNTRACK_TUPLE_COMMON_H */
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
new file mode 100644 (file)
index 0000000..b4d518e
--- /dev/null
@@ -0,0 +1,337 @@
+#ifndef _LINUX_NF_TABLES_H
+#define _LINUX_NF_TABLES_H
+
+enum nft_registers {
+       NFT_REG_VERDICT,
+       NFT_REG_1,
+       NFT_REG_2,
+       NFT_REG_3,
+       NFT_REG_4,
+       __NFT_REG_MAX
+};
+#define NFT_REG_MAX    (__NFT_REG_MAX - 1)
+
+enum nft_verdicts {
+       NFT_CONTINUE    = -1,
+       NFT_BREAK       = -2,
+       NFT_JUMP        = -3,
+       NFT_GOTO        = -4,
+       NFT_RETURN      = -5,
+};
+
+enum nf_tables_msg_types {
+       NFT_MSG_NEWTABLE,
+       NFT_MSG_GETTABLE,
+       NFT_MSG_DELTABLE,
+       NFT_MSG_NEWCHAIN,
+       NFT_MSG_GETCHAIN,
+       NFT_MSG_DELCHAIN,
+       NFT_MSG_NEWRULE,
+       NFT_MSG_GETRULE,
+       NFT_MSG_DELRULE,
+       NFT_MSG_MAX,
+};
+
+enum nft_list_attributes {
+       NFTA_LIST_UNPEC,
+       NFTA_LIST_ELEM,
+       __NFTA_LIST_MAX
+};
+#define NFTA_LIST_MAX          (__NFTA_LIST_MAX - 1)
+
+enum nft_hook_attributes {
+       NFTA_HOOK_UNSPEC,
+       NFTA_HOOK_HOOKNUM,
+       NFTA_HOOK_PRIORITY,
+       __NFTA_HOOK_MAX
+};
+#define NFTA_HOOK_MAX          (__NFTA_HOOK_MAX - 1)
+
+enum nft_table_attributes {
+       NFTA_TABLE_UNSPEC,
+       NFTA_TABLE_NAME,
+       __NFTA_TABLE_MAX
+};
+#define NFTA_TABLE_MAX         (__NFTA_TABLE_MAX - 1)
+
+enum nft_chain_attributes {
+       NFTA_CHAIN_UNSPEC,
+       NFTA_CHAIN_TABLE,
+       NFTA_CHAIN_NAME,
+       NFTA_CHAIN_HOOK,
+       __NFTA_CHAIN_MAX
+};
+#define NFTA_CHAIN_MAX         (__NFTA_CHAIN_MAX - 1)
+
+enum nft_rule_attributes {
+       NFTA_RULE_UNSPEC,
+       NFTA_RULE_TABLE,
+       NFTA_RULE_CHAIN,
+       NFTA_RULE_HANDLE,
+       NFTA_RULE_EXPRESSIONS,
+       __NFTA_RULE_MAX
+};
+#define NFTA_RULE_MAX          (__NFTA_RULE_MAX - 1)
+
+enum nft_data_attributes {
+       NFTA_DATA_UNSPEC,
+       NFTA_DATA_VALUE,
+       NFTA_DATA_VERDICT,
+       __NFTA_DATA_MAX
+};
+#define NFTA_DATA_MAX          (__NFTA_DATA_MAX - 1)
+
+enum nft_verdict_attributes {
+       NFTA_VERDICT_UNSPEC,
+       NFTA_VERDICT_CODE,
+       NFTA_VERDICT_CHAIN,
+       __NFTA_VERDICT_MAX
+};
+#define NFTA_VERDICT_MAX       (__NFTA_VERDICT_MAX - 1)
+
+enum nft_expr_attributes {
+       NFTA_EXPR_UNSPEC,
+       NFTA_EXPR_NAME,
+       NFTA_EXPR_DATA,
+       __NFTA_EXPR_MAX
+};
+#define NFTA_EXPR_MAX          (__NFTA_EXPR_MAX - 1)
+
+enum nft_immediate_attributes {
+       NFTA_IMMEDIATE_UNSPEC,
+       NFTA_IMMEDIATE_DREG,
+       NFTA_IMMEDIATE_DATA,
+       __NFTA_IMMEDIATE_MAX
+};
+#define NFTA_IMMEDIATE_MAX     (__NFTA_IMMEDIATE_MAX - 1)
+
+enum nft_bitwise_attributes {
+       NFTA_BITWISE_UNSPEC,
+       NFTA_BITWISE_SREG,
+       NFTA_BITWISE_DREG,
+       NFTA_BITWISE_LEN,
+       NFTA_BITWISE_MASK,
+       NFTA_BITWISE_XOR,
+       __NFTA_BITWISE_MAX
+};
+#define NFTA_BITWISE_MAX       (__NFTA_BITWISE_MAX - 1)
+
+enum nft_byteorder_ops {
+       NFT_BYTEORDER_NTOH,
+       NFT_BYTEORDER_HTON,
+};
+
+enum nft_byteorder_attributes {
+       NFTA_BYTEORDER_UNSPEC,
+       NFTA_BYTEORDER_SREG,
+       NFTA_BYTEORDER_DREG,
+       NFTA_BYTEORDER_OP,
+       NFTA_BYTEORDER_LEN,
+       NFTA_BYTEORDER_SIZE,
+       __NFTA_BYTEORDER_MAX
+};
+#define NFTA_BYTEORDER_MAX     (__NFTA_BYTEORDER_MAX - 1)
+
+enum nft_cmp_ops {
+       NFT_CMP_EQ,
+       NFT_CMP_NEQ,
+       NFT_CMP_LT,
+       NFT_CMP_LTE,
+       NFT_CMP_GT,
+       NFT_CMP_GTE,
+};
+
+enum nft_cmp_attributes {
+       NFTA_CMP_UNSPEC,
+       NFTA_CMP_SREG,
+       NFTA_CMP_OP,
+       NFTA_CMP_DATA,
+       __NFTA_CMP_MAX
+};
+#define NFTA_CMP_MAX           (__NFTA_CMP_MAX - 1)
+
+enum nft_set_elem_flags {
+       NFT_SE_INTERVAL_END     = 0x1,
+};
+
+enum nft_set_elem_attributes {
+       NFTA_SE_UNSPEC,
+       NFTA_SE_KEY,
+       NFTA_SE_DATA,
+       NFTA_SE_FLAGS,
+       __NFTA_SE_MAX
+};
+#define NFTA_SE_MAX            (__NFTA_SE_MAX - 1)
+
+enum nft_set_flags {
+       NFT_SET_INTERVAL        = 0x1,
+       NFT_SET_MAP             = 0x2,
+};
+
+enum nft_set_attributes {
+       NFTA_SET_UNSPEC,
+       NFTA_SET_FLAGS,
+       NFTA_SET_SREG,
+       NFTA_SET_DREG,
+       NFTA_SET_KLEN,
+       NFTA_SET_ELEMENTS,
+       __NFTA_SET_MAX
+};
+#define NFTA_SET_MAX           (__NFTA_SET_MAX - 1)
+
+enum nft_hash_flags {
+       NFT_HASH_MAP            = 0x1,
+};
+
+enum nft_hash_elem_attributes {
+       NFTA_HE_UNSPEC,
+       NFTA_HE_KEY,
+       NFTA_HE_DATA,
+       __NFTA_HE_MAX
+};
+#define NFTA_HE_MAX            (__NFTA_HE_MAX - 1)
+
+enum nft_hash_attributes {
+       NFTA_HASH_UNSPEC,
+       NFTA_HASH_FLAGS,
+       NFTA_HASH_SREG,
+       NFTA_HASH_DREG,
+       NFTA_HASH_KLEN,
+       NFTA_HASH_ELEMENTS,
+       __NFTA_HASH_MAX
+};
+#define NFTA_HASH_MAX          (__NFTA_HASH_MAX - 1)
+
+enum nft_payload_bases {
+       NFT_PAYLOAD_LL_HEADER,
+       NFT_PAYLOAD_NETWORK_HEADER,
+       NFT_PAYLOAD_TRANSPORT_HEADER,
+};
+
+enum nft_payload_attributes {
+       NFTA_PAYLOAD_UNSPEC,
+       NFTA_PAYLOAD_DREG,
+       NFTA_PAYLOAD_BASE,
+       NFTA_PAYLOAD_OFFSET,
+       NFTA_PAYLOAD_LEN,
+       __NFTA_PAYLOAD_MAX
+};
+#define NFTA_PAYLOAD_MAX       (__NFTA_PAYLOAD_MAX - 1)
+
+enum nft_exthdr_attributes {
+       NFTA_EXTHDR_UNSPEC,
+       NFTA_EXTHDR_DREG,
+       NFTA_EXTHDR_TYPE,
+       NFTA_EXTHDR_OFFSET,
+       NFTA_EXTHDR_LEN,
+       __NFTA_EXTHDR_MAX
+};
+#define NFTA_EXTHDR_MAX                (__NFTA_EXTHDR_MAX - 1)
+
+enum nft_meta_keys {
+       NFT_META_LEN,
+       NFT_META_PROTOCOL,
+       NFT_META_PRIORITY,
+       NFT_META_MARK,
+       NFT_META_IIF,
+       NFT_META_OIF,
+       NFT_META_IIFNAME,
+       NFT_META_OIFNAME,
+       NFT_META_IIFTYPE,
+       NFT_META_OIFTYPE,
+       NFT_META_SKUID,
+       NFT_META_SKGID,
+       NFT_META_NFTRACE,
+       NFT_META_RTCLASSID,
+       NFT_META_SECMARK,
+};
+
+enum nft_meta_attributes {
+       NFTA_META_UNSPEC,
+       NFTA_META_DREG,
+       NFTA_META_KEY,
+       __NFTA_META_MAX
+};
+#define NFTA_META_MAX          (__NFTA_META_MAX - 1)
+
+enum nft_ct_keys {
+       NFT_CT_STATE,
+       NFT_CT_DIRECTION,
+       NFT_CT_STATUS,
+       NFT_CT_MARK,
+       NFT_CT_SECMARK,
+       NFT_CT_EXPIRATION,
+       NFT_CT_HELPER,
+       NFT_CT_L3PROTO,
+       NFT_CT_SADDR,
+       NFT_CT_DADDR,
+       NFT_CT_PROTOCOL,
+       NFT_CT_PROTO_SRC,
+       NFT_CT_PROTO_DST,
+};
+
+enum nft_ct_attributes {
+       NFTA_CT_UNSPEC,
+       NFTA_CT_DREG,
+       NFTA_CT_KEY,
+       NFTA_CT_DIRECTION,
+       __NFTA_CT_MAX
+};
+#define NFTA_CT_MAX            (__NFTA_CT_MAX - 1)
+
+enum nft_limit_attributes {
+       NFTA_LIMIT_UNSPEC,
+       NFTA_LIMIT_RATE,
+       NFTA_LIMIT_DEPTH,
+       __NFTA_LIMIT_MAX
+};
+#define NFTA_LIMIT_MAX         (__NFTA_LIMIT_MAX - 1)
+
+enum nft_counter_attributes {
+       NFTA_COUNTER_UNSPEC,
+       NFTA_COUNTER_BYTES,
+       NFTA_COUNTER_PACKETS,
+       __NFTA_COUNTER_MAX
+};
+#define NFTA_COUNTER_MAX       (__NFTA_COUNTER_MAX - 1)
+
+enum nft_log_attributes {
+       NFTA_LOG_UNSPEC,
+       NFTA_LOG_GROUP,
+       NFTA_LOG_PREFIX,
+       NFTA_LOG_SNAPLEN,
+       NFTA_LOG_QTHRESHOLD,
+       __NFTA_LOG_MAX
+};
+#define NFTA_LOG_MAX           (__NFTA_LOG_MAX - 1)
+
+enum nft_reject_types {
+       NFT_REJECT_ICMP_UNREACH,
+       NFT_REJECT_TCP_RST,
+};
+
+enum nft_reject_attributes {
+       NFTA_REJECT_UNSPEC,
+       NFTA_REJECT_TYPE,
+       NFTA_REJECT_ICMP_CODE,
+       __NFTA_REJECT_MAX
+};
+#define NFTA_REJECT_MAX                (__NFTA_REJECT_MAX - 1)
+
+enum nft_nat_types {
+       NFT_NAT_SNAT,
+       NFT_NAT_DNAT,
+};
+
+enum nft_nat_attributes {
+       NFTA_NAT_UNSPEC,
+       NFTA_NAT_TYPE,
+       NFTA_NAT_ADDR_MIN,
+       NFTA_NAT_ADDR_MAX,
+       NFTA_NAT_PROTO_MIN,
+       NFTA_NAT_PROTO_MAX,
+       __NFTA_NAT_MAX
+};
+#define NFTA_NAT_MAX           (__NFTA_NAT_MAX - 1)
+
+#endif /* _LINUX_NF_TABLES_H */
diff --git a/include/linux/netfilter_arp.h b/include/linux/netfilter_arp.h
new file mode 100644 (file)
index 0000000..92bc6dd
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LINUX_ARP_NETFILTER_H
+#define __LINUX_ARP_NETFILTER_H
+
+/* ARP-specific defines for netfilter.
+ * (C)2002 Rusty Russell IBM -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* There is no PF_ARP. */
+#define NF_ARP         0
+
+/* ARP Hooks */
+#define NF_ARP_IN      0
+#define NF_ARP_OUT     1
+#define NF_ARP_FORWARD 2
+#define NF_ARP_NUMHOOKS        3
+
+#endif /* __LINUX_ARP_NETFILTER_H */
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h
new file mode 100644 (file)
index 0000000..5094ecc
--- /dev/null
@@ -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
diff --git a/include/linux/netfilter_decnet.h b/include/linux/netfilter_decnet.h
new file mode 100644 (file)
index 0000000..ca70c6c
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __LINUX_DECNET_NETFILTER_H
+#define __LINUX_DECNET_NETFILTER_H
+
+/* DECnet-specific defines for netfilter. 
+ * This file (C) Steve Whitehouse 1999 derived from the
+ * ipv4 netfilter header file which is
+ * (C)1998 Rusty Russell -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* only for userspace compatibility */
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_DN_SRC             0x0001
+/* Dest IP address. */
+#define NFC_DN_DST             0x0002
+/* Input device. */
+#define NFC_DN_IF_IN           0x0004
+/* Output device. */
+#define NFC_DN_IF_OUT          0x0008
+
+/* DECnet Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_DN_PRE_ROUTING      0
+/* If the packet is destined for this box. */
+#define NF_DN_LOCAL_IN         1
+/* If the packet is destined for another interface. */
+#define NF_DN_FORWARD          2
+/* Packets coming from a local process. */
+#define NF_DN_LOCAL_OUT                3
+/* Packets about to hit the wire. */
+#define NF_DN_POST_ROUTING     4
+/* Input Hello Packets */
+#define NF_DN_HELLO            5
+/* Input Routing Packets */
+#define NF_DN_ROUTE            6
+#define NF_DN_NUMHOOKS         7
+
+enum nf_dn_hook_priorities {
+       NF_DN_PRI_FIRST = INT_MIN,
+       NF_DN_PRI_CONNTRACK = -200,
+       NF_DN_PRI_MANGLE = -150,
+       NF_DN_PRI_NAT_DST = -100,
+       NF_DN_PRI_FILTER = 0,
+       NF_DN_PRI_NAT_SRC = 100,
+       NF_DN_PRI_DNRTMSG = 200,
+       NF_DN_PRI_LAST = INT_MAX,
+};
+
+struct nf_dn_rtmsg {
+       int nfdn_ifindex;
+};
+
+#define NFDN_RTMSG(r) ((unsigned char *)(r) + NLMSG_ALIGN(sizeof(struct nf_dn_rtmsg)))
+
+/* backwards compatibility for userspace */
+#define DNRMG_L1_GROUP 0x01
+#define DNRMG_L2_GROUP 0x02
+
+enum {
+       DNRNG_NLGRP_NONE,
+#define DNRNG_NLGRP_NONE       DNRNG_NLGRP_NONE
+       DNRNG_NLGRP_L1,
+#define DNRNG_NLGRP_L1         DNRNG_NLGRP_L1
+       DNRNG_NLGRP_L2,
+#define DNRNG_NLGRP_L2         DNRNG_NLGRP_L2
+       __DNRNG_NLGRP_MAX
+};
+#define DNRNG_NLGRP_MAX        (__DNRNG_NLGRP_MAX - 1)
+
+#endif /*__LINUX_DECNET_NETFILTER_H*/
diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h
new file mode 100644 (file)
index 0000000..4d7ba3e
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef __LINUX_IP_NETFILTER_H
+#define __LINUX_IP_NETFILTER_H
+
+/* IPv4-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ */
+
+#include <linux/netfilter.h>
+
+/* only for userspace compatibility */
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP_SRC             0x0001
+/* Dest IP address. */
+#define NFC_IP_DST             0x0002
+/* Input device. */
+#define NFC_IP_IF_IN           0x0004
+/* Output device. */
+#define NFC_IP_IF_OUT          0x0008
+/* TOS. */
+#define NFC_IP_TOS             0x0010
+/* Protocol. */
+#define NFC_IP_PROTO           0x0020
+/* IP options. */
+#define NFC_IP_OPTIONS         0x0040
+/* Frag & flags. */
+#define NFC_IP_FRAG            0x0080
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP_TCPFLAGS                0x0100
+/* Source port. */
+#define NFC_IP_SRC_PT          0x0200
+/* Dest port. */
+#define NFC_IP_DST_PT          0x0400
+/* Something else about the proto */
+#define NFC_IP_PROTO_UNKNOWN   0x2000
+
+/* IP Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP_PRE_ROUTING      0
+/* If the packet is destined for this box. */
+#define NF_IP_LOCAL_IN         1
+/* If the packet is destined for another interface. */
+#define NF_IP_FORWARD          2
+/* Packets coming from a local process. */
+#define NF_IP_LOCAL_OUT                3
+/* Packets about to hit the wire. */
+#define NF_IP_POST_ROUTING     4
+#define NF_IP_NUMHOOKS         5
+
+enum nf_ip_hook_priorities {
+       NF_IP_PRI_FIRST = INT_MIN,
+       NF_IP_PRI_CONNTRACK_DEFRAG = -400,
+       NF_IP_PRI_RAW = -300,
+       NF_IP_PRI_SELINUX_FIRST = -225,
+       NF_IP_PRI_CONNTRACK = -200,
+       NF_IP_PRI_MANGLE = -150,
+       NF_IP_PRI_NAT_DST = -100,
+       NF_IP_PRI_FILTER = 0,
+       NF_IP_PRI_SECURITY = 50,
+       NF_IP_PRI_NAT_SRC = 100,
+       NF_IP_PRI_SELINUX_LAST = 225,
+       NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
+       NF_IP_PRI_LAST = INT_MAX,
+};
+
+/* Arguments for setsockopt SOL_IP: */
+/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
+/* 2.2 firewalling (+ masq) went from 64 through 76 */
+/* 2.4 firewalling went 64 through 67. */
+#define SO_ORIGINAL_DST 80
+
+
+#endif /*__LINUX_IP_NETFILTER_H*/
diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h
new file mode 100644 (file)
index 0000000..7430b39
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef __LINUX_IP6_NETFILTER_H
+#define __LINUX_IP6_NETFILTER_H
+
+/* IPv6-specific defines for netfilter. 
+ * (C)1998 Rusty Russell -- This code is GPL.
+ * (C)1999 David Jeffery
+ *   this header was blatantly ripped from netfilter_ipv4.h 
+ *   it's amazing what adding a bunch of 6s can do =8^)
+ */
+
+#include <linux/netfilter.h>
+
+/* only for userspace compatibility */
+/* IP Cache bits. */
+/* Src IP address. */
+#define NFC_IP6_SRC              0x0001
+/* Dest IP address. */
+#define NFC_IP6_DST              0x0002
+/* Input device. */
+#define NFC_IP6_IF_IN            0x0004
+/* Output device. */
+#define NFC_IP6_IF_OUT           0x0008
+/* TOS. */
+#define NFC_IP6_TOS              0x0010
+/* Protocol. */
+#define NFC_IP6_PROTO            0x0020
+/* IP options. */
+#define NFC_IP6_OPTIONS          0x0040
+/* Frag & flags. */
+#define NFC_IP6_FRAG             0x0080
+
+
+/* Per-protocol information: only matters if proto match. */
+/* TCP flags. */
+#define NFC_IP6_TCPFLAGS         0x0100
+/* Source port. */
+#define NFC_IP6_SRC_PT           0x0200
+/* Dest port. */
+#define NFC_IP6_DST_PT           0x0400
+/* Something else about the proto */
+#define NFC_IP6_PROTO_UNKNOWN    0x2000
+
+/* IP6 Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_IP6_PRE_ROUTING     0
+/* If the packet is destined for this box. */
+#define NF_IP6_LOCAL_IN                1
+/* If the packet is destined for another interface. */
+#define NF_IP6_FORWARD         2
+/* Packets coming from a local process. */
+#define NF_IP6_LOCAL_OUT               3
+/* Packets about to hit the wire. */
+#define NF_IP6_POST_ROUTING    4
+#define NF_IP6_NUMHOOKS                5
+
+
+enum nf_ip6_hook_priorities {
+       NF_IP6_PRI_FIRST = INT_MIN,
+       NF_IP6_PRI_CONNTRACK_DEFRAG = -400,
+       NF_IP6_PRI_SELINUX_FIRST = -225,
+       NF_IP6_PRI_CONNTRACK = -200,
+       NF_IP6_PRI_MANGLE = -150,
+       NF_IP6_PRI_NAT_DST = -100,
+       NF_IP6_PRI_FILTER = 0,
+       NF_IP6_PRI_SECURITY = 50,
+       NF_IP6_PRI_NAT_SRC = 100,
+       NF_IP6_PRI_SELINUX_LAST = 225,
+       NF_IP6_PRI_LAST = INT_MAX,
+};
+
+
+#endif /*__LINUX_IP6_NETFILTER_H*/
diff --git a/include/list.h b/include/list.h
new file mode 100644 (file)
index 0000000..75d2921
--- /dev/null
@@ -0,0 +1,625 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stddef.h>
+
+#define prefetch(x)    ((void)0)
+
+#define LIST_POISON1   ((void *)0x12345678)
+#define LIST_POISON2   ((void *)0x87654321)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void init_list_head(struct list_head *list)
+{
+       list->next = list;
+       list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = LIST_POISON1;
+       entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+                               struct list_head *new)
+{
+       new->next = old->next;
+       new->next->prev = new;
+       new->prev = old->prev;
+       new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+                                       struct list_head *new)
+{
+       list_replace(old, new);
+       init_list_head(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       init_list_head(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+                               const struct list_head *head)
+{
+       return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+       struct list_head *next = head->next;
+       return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+       return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+               struct list_head *head, struct list_head *entry)
+{
+       struct list_head *new_first = entry->next;
+       list->next = head->next;
+       list->next->prev = list;
+       list->prev = entry;
+       entry->next = list;
+       head->next = new_first;
+       new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ *     and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+               struct list_head *head, struct list_head *entry)
+{
+       if (list_empty(head))
+               return;
+       if (list_is_singular(head) &&
+               (head->next != entry && head != entry))
+               return;
+       if (entry == head)
+               init_list_head(list);
+       else
+               __list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+                                struct list_head *prev,
+                                struct list_head *next)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+
+       first->prev = prev;
+       prev->next = first;
+
+       last->next = next;
+       next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+                               struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+                               struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head, head->next);
+               init_list_head(list);
+       }
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+                                        struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head->prev, head);
+               init_list_head(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            prefetch(pos->member.next), &pos->member != (head);        \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member);      \
+            prefetch(pos->member.prev), &pos->member != (head);        \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:       the type * to use as a start point
+ * @head:      the head of the list
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+       ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
+            prefetch(pos->member.next), &pos->member != (head);        \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)                \
+       for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
+            prefetch(pos->member.prev), &pos->member != (head);        \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member)                    \
+       for (; prefetch(pos->member.next), &pos->member != (head);      \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member),          \
+               n = list_entry(pos->member.next, typeof(*pos), member);         \
+            &pos->member != (head);                                            \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member)                    \
+       for (n = list_entry(pos->member.next, typeof(*pos), member);            \
+            &pos->member != (head);                                            \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)         \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+               n = list_entry(pos->member.prev, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+
+#define init_hlist_head(ptr) ((ptr)->first = NULL)
+
+static inline void init_hlist_node(struct hlist_node *h)
+{
+       h->next = NULL;
+       h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+       return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->next = LIST_POISON1;
+       n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+       if (!hlist_unhashed(n)) {
+               __hlist_del(n);
+               init_hlist_node(n);
+       }
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+       n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       n->pprev = next->pprev;
+       n->next = next;
+       next->pprev = &n->next;
+       *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       next->next = n->next;
+       n->next = next;
+       next->pprev = &n->next;
+
+       if(next->next)
+               next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+            pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+            pos = n)
+
+/**
+ * hlist_for_each_entry        - iterate over list of given type
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first;                                        \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)                \
+       for (pos = (pos)->next;                                          \
+            pos && ({ prefetch(pos->next); 1;}) &&                      \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)                    \
+       for (; pos && ({ prefetch(pos->next); 1;}) &&                    \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+#endif
diff --git a/include/meta.h b/include/meta.h
new file mode 100644 (file)
index 0000000..75b1f1e
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _META_H
+#define _META_H
+
+/**
+ * struct meta_template - template for meta expressions and statements
+ *
+ * @token:     parser token for the expression
+ * @dtype:     data type of the expression
+ * @len:       length of the expression
+ * @byteorder: byteorder
+ */
+struct meta_template {
+       const char              *token;
+       const struct datatype   *dtype;
+       enum byteorder          byteorder;
+       unsigned int            len;
+};
+
+#define META_TEMPLATE(__token, __dtype, __len, __byteorder) {  \
+       .token          = (__token),                            \
+       .dtype          = (__dtype),                            \
+       .len            = (__len),                              \
+       .byteorder      = (__byteorder),                        \
+}
+
+extern struct expr *meta_expr_alloc(const struct location *loc,
+                                   enum nft_meta_keys key);
+
+#endif /* _META_H */
diff --git a/include/netlink.h b/include/netlink.h
new file mode 100644 (file)
index 0000000..4849ff5
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef _NETLINK_H
+#define _NETLINK_H
+
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/nft_table.h>
+#include <netlink/netfilter/nft_chain.h>
+#include <netlink/netfilter/nft_rule.h>
+#include <netlink/netfilter/nft_expr.h>
+#include <netlink/netfilter/nft_data.h>
+#include <netlink/object.h>
+
+#include <rule.h>
+
+/** 
+ * struct netlink_ctx
+ *
+ * @msgs:      message queue
+ * @list:      list of parsed rules/chains/tables
+ */
+struct netlink_ctx {
+       struct list_head        *msgs;
+       struct list_head        list;
+};
+
+extern void netlink_dump_object(struct nl_object *obj);
+
+extern struct nfnl_nft_table *alloc_nft_table(const struct handle *h);
+extern struct nfnl_nft_chain *alloc_nft_chain(const struct handle *h);
+extern struct nfnl_nft_rule *alloc_nft_rule(const struct handle *h);
+extern struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *));
+extern struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len);
+
+extern int netlink_linearize_rule(struct netlink_ctx *ctx,
+                                 struct nfnl_nft_rule *nlr,
+                                 const struct rule *rule);
+extern struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
+                                            const struct nl_object *obj);
+
+extern int netlink_add_rule(struct netlink_ctx *ctx, const struct handle *h,
+                           const struct rule *rule);
+extern int netlink_delete_rule(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_get_rule(struct netlink_ctx *ctx, const struct handle *h);
+
+extern int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h,
+                            const struct chain *chain);
+extern int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_chain(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_flush_chain(struct netlink_ctx *ctx, const struct handle *h);
+
+extern int netlink_add_table(struct netlink_ctx *ctx, const struct handle *h,
+                            const struct table *table);
+extern int netlink_delete_table(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_get_table(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_list_table(struct netlink_ctx *ctx, const struct handle *h);
+extern int netlink_flush_table(struct netlink_ctx *ctx, const struct handle *h);
+
+#endif /* _NETLINK_H */
diff --git a/include/nftables.h b/include/nftables.h
new file mode 100644 (file)
index 0000000..7e4a2fc
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef _NFTABLES_H
+#define _NFTABLES_H
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <utils.h>
+
+enum numeric_level {
+       NUMERIC_NONE,
+       NUMERIC_ADDR,
+       NUMERIC_ALL,
+};
+
+#define INCLUDE_PATHS_MAX      16
+
+extern unsigned int numeric_output;
+extern const char *include_paths[INCLUDE_PATHS_MAX];
+
+struct parser_state;
+extern int cli_init(void *scanner, struct parser_state *state);
+extern void cli_exit(void);
+extern void cli_display(const char *fmt, va_list ap) __fmtstring(1, 0);
+
+enum nftables_exit_codes {
+       NFT_EXIT_SUCCESS        = 0,
+       NFT_EXIT_FAILURE        = 1,
+       NFT_EXIT_NOMEM          = 2,
+};
+
+struct input_descriptor;
+struct location {
+       const struct input_descriptor           *indesc;
+       union {
+               struct {
+                       off_t                   token_offset;
+                       off_t                   line_offset;
+
+                       unsigned int            first_line;
+                       unsigned int            last_line;
+                       unsigned int            first_column;
+                       unsigned int            last_column;
+               };
+               struct {
+                       struct nl_object        *nl_obj;
+               };
+       };
+};
+
+extern const struct location internal_location;
+
+/**
+ * enum input_descriptor_types
+ *
+ * @INDESC_INVALID:    invalid
+ * @INDESC_INTERNAL:   dummy type for internally generated messages
+ * @INDESC_BUFFER:     buffer (command line arguments)
+ * @INDESC_FILE:       file
+ * @INDESC_CLI:                command line interface
+ * @INDESC_NETLINK:    received from netlink
+ */
+enum input_descriptor_types {
+       INDESC_INVALID,
+       INDESC_INTERNAL,
+       INDESC_BUFFER,
+       INDESC_FILE,
+       INDESC_CLI,
+       INDESC_NETLINK,
+};
+
+/**
+ * struct input_descriptor
+ *
+ * @location:          location, used for include statements
+ * @type:              input descriptor type
+ * @name:              name describing the input
+ * @union:             buffer or file descriptor, depending on type
+ * @lineno:            current line number in the input
+ * @column:            current column in the input
+ * @token_offset:      offset of the current token to the beginning
+ * @line_offset:       offset of the current line to the beginning
+ */
+struct input_descriptor {
+       struct location                 location;
+       enum input_descriptor_types     type;
+       const char                      *name;
+       union {
+               const char              *data;
+               int                     fd;
+       };
+       unsigned int                    lineno;
+       unsigned int                    column;
+       off_t                           token_offset;
+       off_t                           line_offset;
+};
+
+#endif /* _NFTABLES_H */
diff --git a/include/parser.h b/include/parser.h
new file mode 100644 (file)
index 0000000..2a4c73b
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _PARSER_H
+#define _PARSER_H
+
+#include <list.h>
+
+#define MAX_INCLUDE_DEPTH              16
+#define TABSIZE                                8
+
+#define YYLTYPE                                struct location
+#define YYLTYPE_IS_TRIVIAL             0
+#define YYENABLE_NLS                   0
+
+struct parser_state {
+       struct input_descriptor         *indesc;
+       struct input_descriptor         indescs[MAX_INCLUDE_DEPTH];
+       unsigned int                    indesc_idx;
+
+       struct list_head                *msgs;
+       struct list_head                cmds;
+};
+
+extern void parser_init(struct parser_state *state, struct list_head *msgs);
+extern int nft_parse(void *, struct parser_state *state);
+
+extern void *scanner_init(struct parser_state *state);
+extern void scanner_destroy(struct parser_state *state);
+
+extern int scanner_read_file(void *scanner, const char *filename,
+                            const struct location *loc);
+extern int scanner_include_file(void *scanner, const char *filename,
+                               const struct location *loc);
+extern void scanner_push_buffer(void *scanner,
+                               const struct input_descriptor *indesc,
+                               const char *buffer);
+
+#endif /* _PARSER_H */
diff --git a/include/payload.h b/include/payload.h
new file mode 100644 (file)
index 0000000..ff9b138
--- /dev/null
@@ -0,0 +1,289 @@
+#ifndef _PAYLOAD_H
+#define _PAYLOAD_H
+
+#include <nftables.h>
+
+/**
+ * enum payload_bases
+ *
+ * @PAYLOAD_BASE_INVALID:      uninitialised, does not happen
+ * @PAYLOAD_BASE_LL_HDR:       link layer header
+ * @PAYLOAD_BASE_NETWORK_HDR:  network layer header
+ * @PAYLOAD_BASE_TRANSPORT_HDR:        transport layer header
+ */
+enum payload_bases {
+       PAYLOAD_BASE_INVALID,
+       PAYLOAD_BASE_LL_HDR,
+       PAYLOAD_BASE_NETWORK_HDR,
+       PAYLOAD_BASE_TRANSPORT_HDR,
+       __PAYLOAD_BASE_MAX
+};
+#define PAYLOAD_BASE_MAX       (__PAYLOAD_BASE_MAX - 1)
+
+/**
+ * enum payload_expr_flags
+ *
+ * @PAYLOAD_PROTOCOL_EXPR:     payload expression contains upper layer protocol
+ */
+enum payload_expr_flags {
+       PAYLOAD_PROTOCOL_EXPR           = 0x1,
+};
+
+/**
+ * struct payload_template - template for a payload header expression
+ *
+ * @token:     parser token describing the header field
+ * @dtype:     data type of the expression
+ * @offset:    offset from base
+ * @len:       length of header field
+ */
+struct payload_template {
+       const char                      *token;
+       const struct datatype           *dtype;
+       uint16_t                        offset;
+       uint16_t                        len;
+};
+
+#define PAYLOAD_TEMPLATE(__token, __dtype,  __offset, __len)           \
+       {                                                               \
+               .token          = (__token),                            \
+               .dtype          = (__dtype),                            \
+               .offset         = (__offset),                           \
+               .len            = (__len),                              \
+       }
+
+#define PAYLOAD_PROTO_MAX              16
+#define PAYLOAD_TEMPLATE_MAX           20
+
+/**
+ * struct payload_desc - payload protocol description
+ *
+ * @name:      protocol name
+ * @base:      header base
+ * @protocol_key: key of template containing upper layer protocol description
+ * @protocols: link to upper layer protocol description indexed by protocol value
+ * @templates: header templates
+ */
+struct payload_desc {
+       const char                      *name;
+       enum payload_bases              base;
+       unsigned int                    protocol_key;
+       struct {
+               unsigned int                    num;
+               const struct payload_desc       *desc;
+       }                               protocols[PAYLOAD_PROTO_MAX];
+       struct payload_template         templates[PAYLOAD_TEMPLATE_MAX];
+};
+
+#define PAYLOAD_PROTO(__num, __desc)   { .num = (__num), .desc = (__desc), }
+
+/**
+ * struct payload_hook_desc - description of constraints imposed by hook family
+ *
+ * @base:      protocol base of packets
+ * @desc:      protocol description of packets
+ */
+struct payload_hook_desc {
+       enum payload_bases              base;
+       const struct payload_desc       *desc;
+};
+
+#define PAYLOAD_HOOK(__base, __desc)   { .base = (__base), .desc = (__desc), }
+
+/**
+ * struct dev_payload_desc - description of device LL protocol
+ *
+ * @desc:      protocol description
+ * @type:      arphrd value
+ */
+struct dev_payload_desc {
+       const struct payload_desc       *desc;
+       uint16_t                        type;
+};
+
+#define DEV_PAYLOAD_DESC(__type, __desc) { .type = (__type), .desc = (__desc), }
+
+/**
+ * struct payload_ctx - payload expression protocol context
+ *
+ * @family:    hook family
+ * @location:  location of expression defining the context
+ * @desc:      payload description for this layer
+ *
+ * The location of the context is the location of the relational expression
+ * defining it, either directly through a protocol match or indirectly
+ * through a dependency.
+ */
+struct payload_ctx {
+       unsigned int                    family;
+       struct {
+               struct location                 location;
+               const struct payload_desc       *desc;
+       } protocol[PAYLOAD_BASE_MAX + 1];
+};
+
+extern struct expr *payload_expr_alloc(const struct location *loc,
+                                      const struct payload_desc *desc,
+                                      unsigned int type);
+extern void payload_init_raw(struct expr *expr, enum payload_bases base,
+                            unsigned int offset, unsigned int len);
+
+extern void payload_ctx_init(struct payload_ctx *ctx, unsigned int family);
+extern void payload_ctx_update_meta(struct payload_ctx *ctx,
+                                   const struct expr *expr);
+extern void payload_ctx_update(struct payload_ctx *ctx,
+                              const struct expr *expr);
+
+struct eval_ctx;
+extern int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
+                                 struct expr **res);
+
+extern bool payload_is_adjacent(const struct expr *e1, const struct expr *e2);
+extern struct expr *payload_expr_join(const struct expr *e1,
+                                     const struct expr *e2);
+
+extern void payload_expr_expand(struct list_head *list, struct expr *expr,
+                               const struct payload_ctx *ctx);
+extern void payload_expr_complete(struct expr *expr,
+                                 const struct payload_ctx *ctx);
+
+enum eth_hdr_fields {
+       ETHHDR_INVALID,
+       ETHHDR_DADDR,
+       ETHHDR_SADDR,
+       ETHHDR_TYPE,
+};
+
+enum vlan_hdr_fields {
+       VLANHDR_INVALID,
+       VLANHDR_VID,
+       VLANHDR_CFI,
+       VLANHDR_PCP,
+       VLANHDR_TYPE,
+};
+
+enum arp_hdr_fields {
+       ARPHDR_INVALID,
+       ARPHDR_HRD,
+       ARPHDR_PRO,
+       ARPHDR_HLN,
+       ARPHDR_PLN,
+       ARPHDR_OP,
+};
+
+enum ip_hdr_fields {
+       IPHDR_INVALID,
+       IPHDR_VERSION,
+       IPHDR_HDRLENGTH,
+       IPHDR_TOS,
+       IPHDR_LENGTH,
+       IPHDR_ID,
+       IPHDR_FRAG_OFF,
+       IPHDR_TTL,
+       IPHDR_PROTOCOL,
+       IPHDR_CHECKSUM,
+       IPHDR_SADDR,
+       IPHDR_DADDR,
+};
+
+enum icmp_hdr_fields {
+       ICMPHDR_INVALID,
+       ICMPHDR_TYPE,
+       ICMPHDR_CODE,
+       ICMPHDR_CHECKSUM,
+       ICMPHDR_ID,
+       ICMPHDR_SEQ,
+       ICMPHDR_GATEWAY,
+       ICMPHDR_MTU,
+};
+
+enum ip6_hdr_fields {
+       IP6HDR_INVALID,
+       IP6HDR_VERSION,
+       IP6HDR_PRIORITY,
+       IP6HDR_FLOWLABEL,
+       IP6HDR_LENGTH,
+       IP6HDR_NEXTHDR,
+       IP6HDR_HOPLIMIT,
+       IP6HDR_SADDR,
+       IP6HDR_DADDR,
+};
+
+enum ah_hdr_fields {
+       AHHDR_INVALID,
+       AHHDR_NEXTHDR,
+       AHHDR_HDRLENGTH,
+       AHHDR_RESERVED,
+       AHHDR_SPI,
+       AHHDR_SEQUENCE,
+};
+
+enum esp_hdr_fields {
+       ESPHDR_INVALID,
+       ESPHDR_SPI,
+       ESPHDR_SEQUENCE,
+};
+
+enum comp_hdr_fields {
+       COMPHDR_INVALID,
+       COMPHDR_NEXTHDR,
+       COMPHDR_FLAGS,
+       COMPHDR_CPI,
+};
+
+enum udp_hdr_fields {
+       UDPHDR_INVALID,
+       UDPHDR_SPORT,
+       UDPHDR_DPORT,
+       UDPHDR_LENGTH,
+       UDPHDR_CSUMCOV = UDPHDR_LENGTH,
+       UDPHDR_CHECKSUM,
+};
+
+enum tcp_hdr_fields {
+       TCPHDR_INVALID,
+       TCPHDR_SPORT,
+       TCPHDR_DPORT,
+       TCPHDR_SEQ,
+       TCPHDR_ACKSEQ,
+       TCPHDR_DOFF,
+       TCPHDR_RESERVED,
+       TCPHDR_FLAGS,
+       TCPHDR_WINDOW,
+       TCPHDR_CHECKSUM,
+       TCPHDR_URGPTR,
+};
+
+enum dccp_hdr_fields {
+       DCCPHDR_INVALID,
+       DCCPHDR_SPORT,
+       DCCPHDR_DPORT,
+};
+
+enum sctp_hdr_fields {
+       SCTPHDR_INVALID,
+       SCTPHDR_SPORT,
+       SCTPHDR_DPORT,
+       SCTPHDR_VTAG,
+       SCTPHDR_CHECKSUM,
+};
+
+extern const struct payload_desc payload_icmp;
+extern const struct payload_desc payload_ah;
+extern const struct payload_desc payload_esp;
+extern const struct payload_desc payload_comp;
+extern const struct payload_desc payload_udp;
+extern const struct payload_desc payload_udplite;
+extern const struct payload_desc payload_tcp;
+extern const struct payload_desc payload_dccp;
+extern const struct payload_desc payload_sctp;
+
+extern const struct payload_desc payload_ip;
+extern const struct payload_desc payload_ip6;
+
+extern const struct payload_desc payload_arp;
+
+extern const struct payload_desc payload_vlan;
+extern const struct payload_desc payload_eth;
+
+#endif /* _PAYLOAD_H */
diff --git a/include/rbtree.h b/include/rbtree.h
new file mode 100644 (file)
index 0000000..26b6b44
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Red Black Trees
+ * (C) 1999  Andrea Arcangeli <andrea@suse.de>
+ *
+ * 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
+ *
+ */
+
+#ifndef        _RBTREE_H
+#define        _RBTREE_H
+
+#include <stddef.h>
+
+struct rb_node
+{
+       unsigned long  rb_parent_color;
+#define        RB_RED          0
+#define        RB_BLACK        1
+       struct rb_node *rb_right;
+       struct rb_node *rb_left;
+};
+
+struct rb_root
+{
+       struct rb_node *rb_node;
+};
+
+#define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3))
+#define rb_color(r)   ((r)->rb_parent_color & 1)
+#define rb_is_red(r)   (!rb_color(r))
+#define rb_is_black(r) rb_color(r)
+#define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0)
+#define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0)
+
+static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
+{
+       rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
+}
+static inline void rb_set_color(struct rb_node *rb, int color)
+{
+       rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
+}
+
+#define RB_ROOT        (struct rb_root) { NULL, }
+#define        rb_entry(ptr, type, member) container_of(ptr, type, member)
+
+#define RB_EMPTY_ROOT(root)    ((root)->rb_node == NULL)
+#define RB_EMPTY_NODE(node)    (rb_parent(node) == node)
+#define RB_CLEAR_NODE(node)    (rb_set_parent(node, node))
+
+extern void rb_insert_color(struct rb_node *, struct rb_root *);
+extern void rb_erase(struct rb_node *, struct rb_root *);
+
+/* Find logical next and previous nodes in a tree */
+extern struct rb_node *rb_next(struct rb_node *);
+extern struct rb_node *rb_prev(struct rb_node *);
+extern struct rb_node *rb_first(struct rb_root *);
+extern struct rb_node *rb_last(struct rb_root *);
+
+/* Fast replacement of a single node without remove/rebalance/add/rebalance */
+extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 
+                           struct rb_root *root);
+
+static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,
+                               struct rb_node ** rb_link)
+{
+       node->rb_parent_color = (unsigned long )parent;
+       node->rb_left = node->rb_right = NULL;
+
+       *rb_link = node;
+}
+
+#define rb_for_each_entry(pos, root, member) \
+       for ((pos) = (root)->rb_node ? \
+                       rb_entry(rb_first(root), typeof(*pos), member) : NULL; \
+            (pos) != NULL; \
+            (pos) = rb_entry(rb_next(&(pos)->member), typeof(*pos), member))
+
+#define rb_for_each_entry_safe(pos, node, next, root, member) \
+       for ((node) = rb_first(root); \
+            (pos)  = (node) ? rb_entry((node), typeof(*pos), member) : NULL, \
+            (next) = (node) ? rb_next(node) : NULL, \
+            (pos) != NULL; \
+            (node) = (next))
+
+#endif /* _RBTREE_H */
diff --git a/include/rule.h b/include/rule.h
new file mode 100644 (file)
index 0000000..e4ad9f5
--- /dev/null
@@ -0,0 +1,174 @@
+#ifndef _RULE_H
+#define _RULE_H
+
+#include <stdint.h>
+#include <nftables.h>
+#include <list.h>
+
+/**
+ * struct handle - handle for tables, chains and rules
+ *
+ * @family:    protocol family
+ * @table:     table name
+ * @chain:     chain name (chains and rules only)
+ * @handle:    rule handle (rules only)
+ */
+struct handle {
+       int                     family;
+       const char              *table;
+       const char              *chain;
+       uint32_t                handle;
+};
+
+extern void handle_merge(struct handle *dst, const struct handle *src);
+extern void handle_free(struct handle *h);
+
+/**
+ * struct table - nftables table
+ *
+ * @list:      list node
+ * @handle:    table handle
+ * @chains:    chains contained in the table
+ */
+struct table {
+       struct list_head        list;
+       struct handle           handle;
+       struct list_head        chains;
+};
+
+extern struct table *table_alloc(void);
+extern void table_free(struct table *table);
+extern void table_add_hash(struct table *table);
+extern struct table *table_lookup(const struct handle *h);
+
+/**
+ * struct chain - nftables chain
+ *
+ * @list:      list node in table list
+ * @handle:    chain handle
+ * @hooknum:   hook number (base chains)
+ * @priority:  hook priority (base chains)
+ * @rules:     rules contained in the chain
+ */
+struct chain {
+       struct list_head        list;
+       struct handle           handle;
+       unsigned int            hooknum;
+       unsigned int            priority;
+       struct list_head        rules;
+};
+
+extern struct chain *chain_alloc(const char *name);
+extern void chain_free(struct chain *chain);
+extern void chain_add_hash(struct chain *chain, struct table *table);
+extern struct chain *chain_lookup(const struct table *table,
+                                 const struct handle *h);
+
+/**
+ * struct rule - nftables rule
+ *
+ * @list:      list node in chain list
+ * @handle:    rule handle
+ * @location:  location the rule was defined at
+ * @stmt:      list of statements
+ * @num_stmts: number of statements in stmts list
+ */
+struct rule {
+       struct list_head        list;
+       struct handle           handle;
+       struct location         location;
+       struct list_head        stmts;
+       unsigned int            num_stmts;
+};
+
+extern struct rule *rule_alloc(const struct location *loc,
+                              const struct handle *h);
+extern void rule_free(struct rule *rule);
+extern void rule_print(const struct rule *rule);
+
+/**
+ * enum cmd_ops - command operations
+ *
+ * @CMD_INVALID:       invalid
+ * @CMD_ADD:           add object
+ * @CMD_DELETE:                delete object
+ * @CMD_LIST:          list container
+ * @CMD_FLUSH:         flush container
+ */
+enum cmd_ops {
+       CMD_INVALID,
+       CMD_ADD,
+       CMD_DELETE,
+       CMD_LIST,
+       CMD_FLUSH,
+};
+
+/**
+ * enum cmd_obj - command objects
+ *
+ * @CMD_OBJ_INVALID:   invalid
+ * @CMD_OBJ_RULE:      rule
+ * @CMD_OBJ_CHAIN:     chain
+ * @CMD_OBJ_TABLE:     table
+ */
+enum cmd_obj {
+       CMD_OBJ_INVALID,
+       CMD_OBJ_RULE,
+       CMD_OBJ_CHAIN,
+       CMD_OBJ_TABLE,
+};
+
+/**
+ * struct cmd - command statement
+ *
+ * @list:      list node
+ * @location:  location of the statement
+ * @op:                operation
+ * @obj:       object type to perform operation on
+ * @handle:    handle for operations working without full objects
+ * @union:     object
+ */
+struct cmd {
+       struct list_head        list;
+       struct location         location;
+       enum cmd_ops            op;
+       enum cmd_obj            obj;
+       struct handle           handle;
+       union {
+               void            *data;
+               struct rule     *rule;
+               struct chain    *chain;
+               struct table    *table;
+       };
+};
+
+extern struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
+                            const struct handle *h, void *data);
+extern void cmd_free(struct cmd *cmd);
+
+#include <payload.h>
+#include <expression.h>
+
+/**
+ * struct eval_ctx - evaluation context
+ *
+ * @msgs:      message queue
+ * @stmt:      current statement
+ * @ectx:      expression context
+ * @pctx:      payload context
+ */
+struct eval_ctx {
+       struct list_head        *msgs;
+       struct stmt             *stmt;
+       struct expr_ctx         ectx;
+       struct payload_ctx      pctx;
+};
+
+extern int evaluate(struct eval_ctx *ctx, struct list_head *commands);
+
+extern struct error_record *rule_postprocess(struct rule *rule);
+
+struct netlink_ctx;
+extern int do_command(struct netlink_ctx *ctx, struct cmd *cmd);
+
+#endif /* RULE_H */
diff --git a/include/statement.h b/include/statement.h
new file mode 100644 (file)
index 0000000..6e5cfbd
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef _STATEMENT_H
+#define _STATEMENT_H
+
+#include <list.h>
+#include <expression.h>
+
+extern struct stmt *expr_stmt_alloc(const struct location *loc,
+                                   struct expr *expr);
+
+extern struct stmt *verdict_stmt_alloc(const struct location *loc,
+                                      struct expr *expr);
+
+struct counter_stmt {
+       uint64_t                packets;
+       uint64_t                bytes;
+};
+
+extern struct stmt *counter_stmt_alloc(const struct location *loc);
+
+#include <meta.h>
+struct meta_stmt {
+       enum nft_meta_keys              key;
+       const struct meta_template      *tmpl;
+       struct expr                     *expr;
+};
+
+extern struct stmt *meta_stmt_alloc(const struct location *loc,
+                                   enum nft_meta_keys key,
+                                   struct expr *expr);
+
+struct log_stmt {
+       const char              *prefix;
+       unsigned int            group;
+       unsigned int            snaplen;
+       unsigned int            qthreshold;
+};
+
+extern struct stmt *log_stmt_alloc(const struct location *loc);
+
+
+struct limit_stmt {
+       uint64_t                rate;
+       uint64_t                unit;
+       uint64_t                depth;
+};
+
+extern struct stmt *limit_stmt_alloc(const struct location *loc);
+
+struct reject_stmt {
+       enum nft_reject_types   type;
+};
+
+extern struct stmt *reject_stmt_alloc(const struct location *loc);
+
+struct nat_stmt {
+       enum nft_nat_types      type;
+       struct expr             *addr;
+       struct expr             *proto;
+};
+
+extern struct stmt *nat_stmt_alloc(const struct location *loc);
+
+/**
+ * enum stmt_types - statement types
+ *
+ * @STMT_INVALID:      uninitialised
+ * @STMT_EXPRESSION:   expression statement (relational)
+ * @STMT_VERDICT:      verdict statement
+ * @STMT_COUNTER:      counters
+ * @STMT_META:         meta statement
+ * @STMT_LIMIT:                limit statement
+ * @STMT_LOG:          log statement
+ * @STMT_REJECT:       REJECT statement
+ * @STMT_NAT:          NAT statement
+ */
+enum stmt_types {
+       STMT_INVALID,
+       STMT_EXPRESSION,
+       STMT_VERDICT,
+       STMT_COUNTER,
+       STMT_META,
+       STMT_LIMIT,
+       STMT_LOG,
+       STMT_REJECT,
+       STMT_NAT,
+};
+
+/**
+ * struct stmt_ops
+ *
+ * @type:      statement type
+ * @name:      name
+ * @destroy:   destructor
+ * @print:     function to print statement
+ */
+struct stmt;
+struct stmt_ops {
+       enum stmt_types         type;
+       const char              *name;
+       void                    (*destroy)(struct stmt *stmt);
+       void                    (*print)(const struct stmt *stmt);
+};
+
+enum stmt_flags {
+       STMT_F_TERMINAL         = 0x1,
+};
+
+/**
+ * struct stmt
+ *
+ * @list:      rule list node
+ * @ops:       statement ops
+ * @location:  location where the statement was defined
+ * @flags:     statement flags
+ * @union:     type specific data
+ */
+struct stmt {
+       struct list_head                list;
+       const struct stmt_ops           *ops;
+       struct location                 location;
+       enum stmt_flags                 flags;
+
+       union {
+               struct expr             *expr;
+               struct counter_stmt     counter;
+               struct meta_stmt        meta;
+               struct log_stmt         log;
+               struct limit_stmt       limit;
+               struct reject_stmt      reject;
+               struct nat_stmt         nat;
+       };
+};
+
+extern struct stmt *stmt_alloc(const struct location *loc,
+                              const struct stmt_ops *ops);
+extern void stmt_free(struct stmt *stmt);
+extern void stmt_list_free(struct list_head *list);
+extern void stmt_print(const struct stmt *stmt);
+
+#endif /* _STATEMENT_H */
diff --git a/include/utils.h b/include/utils.h
new file mode 100644 (file)
index 0000000..beb6386
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <list.h>
+#include <gmp.h>
+
+#define BITS_PER_BYTE  8
+
+#ifdef DEBUG
+#define pr_debug(fmt, arg...) gmp_printf(fmt, ##arg)
+#else
+#define pr_debug(fmt, arg...) ({ if (false) gmp_printf(fmt, ##arg); 0; })
+#endif
+
+#define __fmtstring(x, y)      __attribute__((format(printf, x, y)))
+#if 0
+#define __gmp_fmtstring(x, y)  __fmtstring(x, y)
+#else
+#define __gmp_fmtstring(x, y)
+#endif
+
+#define __init                 __attribute__((constructor))
+#define __exit                 __attribute__((destructor))
+#define __must_check           __attribute__((warn_unused_result))
+#define __noreturn             __attribute__((__noreturn__))
+
+#define BUG()                  assert(0)
+
+#define BUILD_BUG_ON(condition)        ((void)sizeof(char[1 - 2*!!(condition)]))
+#define BUILD_BUG_ON_ZERO(e)   (sizeof(char[1 - 2 * !!(e)]) - 1)
+
+#define __must_be_array(a) \
+       BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
+
+#define container_of(ptr, type, member) ({                     \
+       typeof( ((type *)0)->member ) *__mptr = (ptr);          \
+       (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#define field_sizeof(t, f)     (sizeof(((t *)NULL)->f))
+#define array_size(arr)                (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
+#define div_round_up(n, d)     (((n) + (d) - 1) / (d))
+
+#define min(x, y) ({                           \
+       typeof(x) _min1 = (x);                  \
+       typeof(y) _min2 = (y);                  \
+       (void) (&_min1 == &_min2);              \
+       _min1 < _min2 ? _min1 : _min2; })
+
+#define max(x, y) ({                           \
+       typeof(x) _max1 = (x);                  \
+       typeof(y) _max2 = (y);                  \
+       (void) (&_max1 == &_max2);              \
+       _max1 > _max2 ? _max1 : _max2; })
+
+extern void memory_allocation_error(void) __noreturn;
+
+extern void xfree(const void *ptr);
+extern void *xmalloc(size_t size);
+extern void *xrealloc(void *ptr, size_t size);
+extern void *xzalloc(size_t size);
+extern char *xstrdup(const char *s);
+
+#endif /* _UTILS_H */
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..d4744f0
--- /dev/null
@@ -0,0 +1,269 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+#
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+    case $1 in
+       -c) instcmd="$cpprog"
+           shift
+           continue;;
+
+       -d) dir_arg=true
+           shift
+           continue;;
+
+       -m) chmodcmd="$chmodprog $2"
+           shift
+           shift
+           continue;;
+
+       -o) chowncmd="$chownprog $2"
+           shift
+           shift
+           continue;;
+
+       -g) chgrpcmd="$chgrpprog $2"
+           shift
+           shift
+           continue;;
+
+       -s) stripcmd="$stripprog"
+           shift
+           continue;;
+
+       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+           shift
+           continue;;
+
+       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+           shift
+           continue;;
+
+       *)  if [ x"$src" = x ]
+           then
+               src=$1
+           else
+               # this colon is to work around a 386BSD /bin/sh bug
+               :
+               dst=$1
+           fi
+           shift
+           continue;;
+    esac
+done
+
+if [ x"$src" = x ]
+then
+       echo "install:  no input file specified"
+       exit 1
+else
+       true
+fi
+
+if [ x"$dir_arg" != x ]; then
+       dst=$src
+       src=""
+       
+       if [ -d $dst ]; then
+               instcmd=:
+               chmodcmd=""
+       else
+               instcmd=mkdir
+       fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad 
+# if $src (and thus $dsttmp) contains '*'.
+
+       if [ -f $src -o -d $src ]
+       then
+               true
+       else
+               echo "install:  $src does not exist"
+               exit 1
+       fi
+       
+       if [ x"$dst" = x ]
+       then
+               echo "install:  no destination specified"
+               exit 1
+       else
+               true
+       fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+       if [ -d $dst ]
+       then
+               dst="$dst"/`basename $src`
+       else
+               true
+       fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+#  this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='   
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+       pathcomp="${pathcomp}${1}"
+       shift
+
+       if [ ! -d "${pathcomp}" ] ;
+        then
+               $mkdirprog "${pathcomp}"
+       else
+               true
+       fi
+
+       pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+       $doit $instcmd $dst &&
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+       if [ x"$transformarg" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               dstfile=`basename $dst $transformbasename | 
+                       sed $transformarg`$transformbasename
+       fi
+
+# don't allow the sed command to completely eliminate the filename
+
+       if [ x"$dstfile" = x ] 
+       then
+               dstfile=`basename $dst`
+       else
+               true
+       fi
+
+# Make a temp file name in the proper directory.
+
+       dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+       $doit $instcmd $src $dsttmp &&
+
+       trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing.  If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+       $doit $rmcmd -f $dstdir/$dstfile &&
+       $doit $mvcmd $dsttmp $dstdir/$dstfile 
+
+fi &&
+
+
+exit 0
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..23e6ae0
--- /dev/null
@@ -0,0 +1,5 @@
+parser.c
+parser.h
+scanner.c
+scanner.h
+nft
diff --git a/src/Makefile.in b/src/Makefile.in
new file mode 100644 (file)
index 0000000..3c93307
--- /dev/null
@@ -0,0 +1,30 @@
+PROGRAMS               += nft
+YACCFLAGS               += --skeleton=../../..$(shell pwd)/src/parser-skeleton.c
+
+nft-destdir            := @sbindir@
+
+nft-obj                        += main.o
+nft-obj                        += cli.o
+nft-obj                        += rule.o
+nft-obj                        += statement.o
+nft-obj                        += datatype.o
+nft-obj                        += expression.o
+nft-obj                        += evaluate.o
+nft-obj                        += payload.o
+nft-obj                        += exthdr.o
+nft-obj                        += meta.o
+nft-obj                        += ct.o
+nft-obj                        += netlink.o
+nft-obj                        += netlink_linearize.o
+nft-obj                        += netlink_delinearize.o
+nft-obj                        += segtree.o
+nft-obj                        += rbtree.o
+nft-obj                        += gmputil.o
+nft-obj                        += utils.o
+nft-obj                        += erec.o
+
+nft-obj                        += parser.o
+nft-extra-clean-files  += parser.c parser.h
+
+nft-obj                        += scanner.o
+nft-extra-clean-files  += scanner.c scanner.h
diff --git a/src/cli.c b/src/cli.c
new file mode 100644 (file)
index 0000000..e302dfa
--- /dev/null
+++ b/src/cli.c
@@ -0,0 +1,175 @@
+/*
+ * Asynchronous readline-based interactive interface
+ *
+ * Actually not asynchronous so far, but intended to be.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include <nftables.h>
+#include <parser.h>
+#include <erec.h>
+#include <utils.h>
+
+#define CMDLINE_HISTFILE       ".nft.history"
+
+static const struct input_descriptor indesc_cli = {
+       .type   = INDESC_CLI,
+};
+
+static struct parser_state *state;
+static void *scanner;
+
+static char histfile[PATH_MAX];
+static char *multiline;
+static bool eof;
+
+static char *cli_append_multiline(char *line)
+{
+       bool complete = false;
+       size_t len;
+       char *s;
+
+       if (line == NULL && multiline == NULL) {
+               eof = true;
+               return NULL;
+       }
+
+       len = strlen(line);
+       if (line[len - 1] == '\\') {
+               line[len - 1] = '\0';
+               len--;
+       } else if (multiline == NULL)
+               return line;
+       else
+               complete = 1;
+
+       if (multiline == NULL) {
+               multiline = line;
+               rl_save_prompt();
+               rl_clear_message();
+       } else {
+               len += strlen(multiline);
+               s = xmalloc(len + 1);
+               snprintf(s, len + 1, "%s%s", multiline, line);
+               xfree(multiline);
+               multiline = s;
+       }
+       line = NULL;
+
+       if (complete) {
+               line = multiline;
+               multiline = NULL;
+               rl_restore_prompt();
+       }
+       return line;
+}
+
+static void cli_complete(char *line)
+{
+       const HIST_ENTRY *hist;
+       const char *c;
+
+       line = cli_append_multiline(line);
+       if (line == NULL)
+               return;
+
+       for (c = line; *c != '\0'; c++)
+               if (!isspace(*c))
+                       break;
+       if (*c == '\0')
+               return;
+
+       /* avoid duplicate history entries */
+       hist = history_get(history_length);
+       if (hist == NULL || strcmp(hist->line, line))
+               add_history(line);
+
+       scanner_push_buffer(scanner, &indesc_cli, line);
+       nft_parse(scanner, state);
+
+       erec_print_list(stdout, state->msgs);
+       xfree(line);
+}
+
+static char **cli_completion(const char *text, int start, int end)
+{
+       return NULL;
+}
+
+void __fmtstring(1, 0) cli_display(const char *fmt, va_list ap)
+{
+       int point, end;
+       char *buf;
+
+       point = rl_point;
+       end   = rl_end;
+       rl_point = rl_end = 0;
+
+       rl_save_prompt();
+       rl_clear_message();
+
+       if (vasprintf(&buf, fmt, ap) < 0)
+               fprintf(rl_outstream, "cli_display: out of memory\n");
+       else {
+               fprintf(rl_outstream, "%s\n", buf);
+               xfree(buf);
+       }
+
+       rl_restore_prompt();
+
+       rl_point = point;
+       rl_end   = end;
+       rl_forced_update_display();
+}
+
+int cli_init(void *_scanner, struct parser_state *_state)
+{
+       const char *home;
+
+       rl_readline_name = "nft";
+       rl_instream  = stdin;
+       rl_outstream = stdout;
+
+       rl_callback_handler_install("nft> ", cli_complete);
+       rl_attempted_completion_function = cli_completion;
+
+       home = getenv("HOME");
+       if (home == NULL)
+               home = "";
+       snprintf(histfile, sizeof(histfile), "%s/%s", home, CMDLINE_HISTFILE);
+
+       read_history(histfile);
+       history_set_pos(history_length);
+
+       scanner = _scanner;
+       state   = _state;
+
+       while (!eof)
+               rl_callback_read_char();
+       return 0;
+}
+
+void cli_exit(void)
+{
+       rl_callback_handler_remove();
+       rl_deprep_terminal();
+       write_history(histfile);
+}
diff --git a/src/ct.c b/src/ct.c
new file mode 100644 (file)
index 0000000..0089539
--- /dev/null
+++ b/src/ct.c
@@ -0,0 +1,149 @@
+/*
+ * Conntrack expression related definitions and types.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+
+#include <expression.h>
+#include <datatype.h>
+#include <ct.h>
+#include <utils.h>
+
+static const struct symbol_table ct_state_tbl = {
+       .byteorder      = BYTEORDER_HOST_ENDIAN,
+       .size           = 4 * BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("invalid",       NF_CT_STATE_INVALID_BIT),
+               SYMBOL("new",           NF_CT_STATE_BIT(IP_CT_NEW)),
+               SYMBOL("established",   NF_CT_STATE_BIT(IP_CT_ESTABLISHED)),
+               SYMBOL("related",       NF_CT_STATE_BIT(IP_CT_RELATED)),
+               SYMBOL("untracked",     NF_CT_STATE_UNTRACKED_BIT),
+               SYMBOL_LIST_END
+       }
+};
+
+static const struct datatype ct_state_type = {
+       .type           = TYPE_CT_STATE,
+       .name           = "conntrack state",
+       .basetype       = &bitmask_type,
+       .sym_tbl        = &ct_state_tbl,
+};
+
+static const struct symbol_table ct_dir_tbl = {
+       .byteorder      = BYTEORDER_INVALID,
+       .size           = BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("original",      IP_CT_DIR_ORIGINAL),
+               SYMBOL("reply",         IP_CT_DIR_REPLY),
+               SYMBOL_LIST_END
+       }
+};
+
+static const struct datatype ct_dir_type = {
+       .type           = TYPE_CT_DIR,
+       .name           = "conntrack direction",
+       .basetype       = &bitmask_type,
+       .sym_tbl        = &ct_dir_tbl,
+};
+
+static const struct symbol_table ct_status_tbl = {
+       .byteorder      = BYTEORDER_HOST_ENDIAN,
+       .size           = 4 * BITS_PER_BYTE,
+       /*
+        * There are more, but most of them don't make sense for filtering.
+        */
+       .symbols        = {
+               SYMBOL("expected",      IPS_EXPECTED),
+               SYMBOL("seen-reply",    IPS_SEEN_REPLY),
+               SYMBOL("assured",       IPS_ASSURED),
+               SYMBOL("confirmed",     IPS_CONFIRMED),
+               SYMBOL("snat",          IPS_SRC_NAT),
+               SYMBOL("dnat",          IPS_DST_NAT),
+               SYMBOL("dying",         IPS_DYING),
+               SYMBOL_LIST_END
+       },
+};
+
+static const struct datatype ct_status_type = {
+       .type           = TYPE_CT_STATUS,
+       .name           = "conntrack status",
+       .basetype       = &bitmask_type,
+       .sym_tbl        = &ct_status_tbl,
+};
+
+static const struct ct_template ct_templates[] = {
+       [NFT_CT_STATE]          = CT_TEMPLATE("state",      &ct_state_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             4 * BITS_PER_BYTE),
+       [NFT_CT_DIRECTION]      = CT_TEMPLATE("direction",  &ct_dir_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             BITS_PER_BYTE),
+       [NFT_CT_STATUS]         = CT_TEMPLATE("status",     &ct_status_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             4 * BITS_PER_BYTE),
+       [NFT_CT_MARK]           = CT_TEMPLATE("mark",       &mark_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             4 * BITS_PER_BYTE),
+       [NFT_CT_SECMARK]        = CT_TEMPLATE("secmark",    &integer_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             4 * BITS_PER_BYTE),
+       [NFT_CT_EXPIRATION]     = CT_TEMPLATE("expiration", &time_type,
+                                             BYTEORDER_HOST_ENDIAN,
+                                             4 * BITS_PER_BYTE),
+       [NFT_CT_HELPER]         = CT_TEMPLATE("helper",     &string_type,
+                                             BYTEORDER_INVALID, 0),
+       [NFT_CT_L3PROTO]        = CT_TEMPLATE("l3proto",    &invalid_type,
+                                             BYTEORDER_INVALID,
+                                             BITS_PER_BYTE),
+       [NFT_CT_SADDR]          = CT_TEMPLATE("saddr",      &invalid_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
+       [NFT_CT_DADDR]          = CT_TEMPLATE("daddr",      &invalid_type,
+                                             BYTEORDER_BIG_ENDIAN, 0),
+       [NFT_CT_PROTOCOL]       = CT_TEMPLATE("protocol",   &inet_protocol_type,
+                                             BYTEORDER_BIG_ENDIAN,
+                                             BITS_PER_BYTE),
+       [NFT_CT_PROTO_SRC]      = CT_TEMPLATE("proto-src",  &invalid_type,
+                                             BYTEORDER_BIG_ENDIAN,
+                                             2 * BITS_PER_BYTE),
+       [NFT_CT_PROTO_DST]      = CT_TEMPLATE("proto-dst",  &invalid_type,
+                                             BYTEORDER_BIG_ENDIAN,
+                                             2 * BITS_PER_BYTE),
+};
+
+static void ct_expr_print(const struct expr *expr)
+{
+       printf("ct %s", ct_templates[expr->ct.key].token);
+}
+
+static const struct expr_ops ct_expr_ops = {
+       .type           = EXPR_CT,
+       .name           = "ct",
+       .print          = ct_expr_print,
+};
+
+struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key)
+{
+       const struct ct_template *tmpl = &ct_templates[key];
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &ct_expr_ops, tmpl->dtype,
+                         tmpl->byteorder, tmpl->len);
+       expr->ct.key = key;
+       return expr;
+}
diff --git a/src/datatype.c b/src/datatype.c
new file mode 100644 (file)
index 0000000..8e17c21
--- /dev/null
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+#include <nftables.h>
+#include <datatype.h>
+#include <expression.h>
+#include <gmputil.h>
+#include <erec.h>
+
+void datatype_print(const struct expr *expr)
+{
+       const struct datatype *dtype = expr->dtype;
+
+       if (dtype->print != NULL)
+               return dtype->print(expr);
+       if (dtype->sym_tbl != NULL)
+               return symbolic_constant_print(dtype->sym_tbl, expr);
+       BUG();
+}
+
+struct error_record *symbol_parse(const struct expr *sym,
+                                 struct expr **res)
+{
+       const struct datatype *dtype = sym->sym_type;
+
+       assert(sym->ops->type == EXPR_SYMBOL);
+
+       if (dtype == NULL)
+               return error(&sym->location, "No symbol type information");
+       if (dtype->parse != NULL)
+               return dtype->parse(sym, res);
+       if (dtype->sym_tbl != NULL)
+               return symbolic_constant_parse(sym, dtype->sym_tbl, res);
+
+       return error(&sym->location,
+                    "Can't parse symbolic %s expressions",
+                    sym->sym_type->name);
+}
+
+struct error_record *symbolic_constant_parse(const struct expr *sym,
+                                            const struct symbol_table *tbl,
+                                            struct expr **res)
+{
+       const struct symbolic_constant *s;
+
+       for (s = tbl->symbols; s->identifier != NULL; s++) {
+               if (!strcmp(sym->identifier, s->identifier))
+                       break;
+       }
+
+       if (s->identifier == NULL)
+               return error(&sym->location, "Could not parse %s",
+                            sym->sym_type->name);
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  tbl->byteorder, tbl->size, &s->value);
+       return NULL;
+}
+
+void symbolic_constant_print(const struct symbol_table *tbl,
+                            const struct expr *expr)
+{
+       const struct symbolic_constant *s;
+
+       for (s = tbl->symbols; s->identifier != NULL; s++) {
+               if (!mpz_cmp_ui(expr->value, s->value))
+                       break;
+       }
+
+       if (s->identifier == NULL)
+               return expr_basetype(expr)->print(expr);
+
+       printf("%s", s->identifier);
+}
+
+void symbol_table_print(const struct symbol_table *tbl)
+{
+       const struct symbolic_constant *s;
+       unsigned int size = 2 * tbl->size / BITS_PER_BYTE;
+
+       for (s = tbl->symbols; s->identifier != NULL; s++)
+               printf("\t%-30s\t0x%.*" PRIx64 "\n",
+                      s->identifier, size, s->value);
+}
+
+static void invalid_type_print(const struct expr *expr)
+{
+       gmp_printf("0x%Zx [invalid type]", expr->value);
+}
+
+const struct datatype invalid_type = {
+       .type           = TYPE_INVALID,
+       .name           = "invalid",
+       .print          = invalid_type_print,
+};
+
+static void verdict_type_print(const struct expr *expr)
+{
+       switch (expr->verdict) {
+       case NF_ACCEPT:
+               printf("accept");
+               break;
+       case NF_DROP:
+               printf("drop");
+               break;
+       case NF_QUEUE:
+               printf("queue");
+               break;
+       case NFT_CONTINUE:
+               printf("continue");
+               break;
+       case NFT_BREAK:
+               printf("break");
+               break;
+       case NFT_JUMP:
+               printf("jump %s", expr->chain);
+               break;
+       case NFT_GOTO:
+               printf("goto %s", expr->chain);
+               break;
+       case NFT_RETURN:
+               printf("return");
+               break;
+       default:
+               BUG();
+       }
+}
+
+const struct datatype verdict_type = {
+       .type           = TYPE_VERDICT,
+       .name           = "verdict",
+       .print          = verdict_type_print,
+};
+
+const struct datatype bitmask_type = {
+       .type           = TYPE_BITMASK,
+       .name           = "bitmask",
+       .basetype       = &integer_type,
+};
+
+static void integer_type_print(const struct expr *expr)
+{
+       const char *fmt = "%Zu";
+
+       if (expr->dtype->basefmt != NULL)
+               fmt = expr->dtype->basefmt;
+       gmp_printf(fmt, expr->value);
+}
+
+static struct error_record *integer_type_parse(const struct expr *sym,
+                                              struct expr **res)
+{
+       mpz_t v;
+
+       mpz_init(v);
+       if (gmp_sscanf(sym->identifier, "%Zu", v) != 1) {
+               mpz_clear(v);
+               if (sym->sym_type != &integer_type)
+                       return NULL;
+               return error(&sym->location, "Could not parse %s",
+                            sym->sym_type->name);
+       }
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  BYTEORDER_HOST_ENDIAN, 1, NULL);
+       mpz_set((*res)->value, v);
+       mpz_clear(v);
+       return NULL;
+}
+
+const struct datatype integer_type = {
+       .type           = TYPE_INTEGER,
+       .name           = "integer",
+       .print          = integer_type_print,
+       .parse          = integer_type_parse,
+};
+
+static void string_type_print(const struct expr *expr)
+{
+       unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+       char data[len];
+
+       mpz_export_data(data, expr->value, BYTEORDER_BIG_ENDIAN, len);
+       printf("\"%s\"", data);
+}
+
+static struct error_record *string_type_parse(const struct expr *sym,
+                                             struct expr **res)
+{
+       *res = constant_expr_alloc(&sym->location, &string_type,
+                                  BYTEORDER_INVALID,
+                                  (strlen(sym->identifier) + 1) * BITS_PER_BYTE,
+                                  sym->identifier);
+       return NULL;
+}
+
+const struct datatype string_type = {
+       .type           = TYPE_STRING,
+       .name           = "string",
+       .print          = string_type_print,
+       .parse          = string_type_parse,
+};
+
+static void lladdr_type_print(const struct expr *expr)
+{
+       unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+       const char *delim = "";
+       uint8_t data[len];
+       unsigned int i;
+
+       mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len);
+       for (i = 0; i < len; i++) {
+               printf("%s%.2x", delim, data[i]);
+               delim = ":";
+       }
+}
+
+static struct error_record *lladdr_type_parse(const struct expr *sym,
+                                             struct expr **res)
+{
+       char buf[strlen(sym->identifier) + 1], *p;
+       const char *s = sym->identifier;
+       unsigned int len, n;
+
+       for (len = 0;;) {
+               n = strtoul(s, &p, 16);
+               if (s == p || n > 0xff)
+                       return erec_create(EREC_ERROR, &sym->location,
+                                          "Invalid LL address");
+               buf[len++] = n;
+               if (*p == '\0')
+                       break;
+               s = ++p;
+       }
+
+       *res = constant_expr_alloc(&sym->location, &lladdr_type,
+                                  BYTEORDER_HOST_ENDIAN, len * BITS_PER_BYTE,
+                                  buf);
+       return NULL;
+}
+
+const struct datatype lladdr_type = {
+       .type           = TYPE_LLADDR,
+       .name           = "LL address",
+       .basetype       = &integer_type,
+       .print          = lladdr_type_print,
+       .parse          = lladdr_type_parse,
+};
+
+static void ipaddr_type_print(const struct expr *expr)
+{
+       struct sockaddr_in sin = { .sin_family = AF_INET, };
+       char buf[NI_MAXHOST];
+
+       sin.sin_addr.s_addr = mpz_get_be32(expr->value);
+       getnameinfo((struct sockaddr *)&sin, sizeof(sin), buf, sizeof(buf),
+                   NULL, 0, numeric_output ? NI_NUMERICHOST : 0);
+       printf("%s", buf);
+}
+
+static struct error_record *ipaddr_type_parse(const struct expr *sym,
+                                             struct expr **res)
+{
+       struct addrinfo *ai, hints = { .ai_family = AF_INET,
+                                      .ai_socktype = SOCK_DGRAM};
+       struct in_addr *addr;
+       int err;
+
+       err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+       if (err != 0)
+               return error(&sym->location, "Could not resolve hostname: %s",
+                            gai_strerror(err));
+
+       if (ai->ai_next != NULL) {
+               freeaddrinfo(ai);
+               return error(&sym->location,
+                            "Hostname resolves to multiple addresses");
+       }
+
+       addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
+       *res = constant_expr_alloc(&sym->location, &ipaddr_type,
+                                  BYTEORDER_BIG_ENDIAN,
+                                  sizeof(*addr) * BITS_PER_BYTE, addr);
+       freeaddrinfo(ai);
+       return NULL;
+}
+
+const struct datatype ipaddr_type = {
+       .type           = TYPE_IPADDR,
+       .name           = "IPv4 address",
+       .basetype       = &integer_type,
+       .print          = ipaddr_type_print,
+       .parse          = ipaddr_type_parse,
+};
+
+static void ip6addr_type_print(const struct expr *expr)
+{
+       struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
+       char buf[NI_MAXHOST];
+
+       mpz_export_data(&sin6.sin6_addr, expr->value, BYTEORDER_BIG_ENDIAN,
+                       sizeof(sin6.sin6_addr));
+
+       getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), buf, sizeof(buf),
+                   NULL, 0, numeric_output ? NI_NUMERICHOST : 0);
+       printf("%s", buf);
+}
+
+static struct error_record *ip6addr_type_parse(const struct expr *sym,
+                                              struct expr **res)
+{
+       struct addrinfo *ai, hints = { .ai_family = AF_INET6,
+                                      .ai_socktype = SOCK_DGRAM};
+       struct in6_addr *addr;
+       int err;
+
+       err = getaddrinfo(sym->identifier, NULL, &hints, &ai);
+       if (err != 0)
+               return error(&sym->location, "Could not resolve hostname: %s",
+                            gai_strerror(err));
+
+       if (ai->ai_next != NULL) {
+               freeaddrinfo(ai);
+               return error(&sym->location,
+                            "Hostname resolves to multiple addresses");
+       }
+
+       addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+       *res = constant_expr_alloc(&sym->location, &ip6addr_type,
+                                  BYTEORDER_BIG_ENDIAN,
+                                  sizeof(*addr) * BITS_PER_BYTE, addr);
+       freeaddrinfo(ai);
+       return NULL;
+}
+
+const struct datatype ip6addr_type = {
+       .type           = TYPE_IP6ADDR,
+       .name           = "IPv6 address",
+       .basetype       = &integer_type,
+       .print          = ip6addr_type_print,
+       .parse          = ip6addr_type_parse,
+};
+
+static void inet_protocol_type_print(const struct expr *expr)
+{
+       struct protoent *p;
+
+       if (numeric_output < NUMERIC_ALL) {
+               p = getprotobynumber(mpz_get_uint8(expr->value));
+               if (p != NULL) {
+                       printf("%s", p->p_name);
+                       return;
+               }
+       }
+       integer_type_print(expr);
+}
+
+static struct error_record *inet_protocol_type_parse(const struct expr *sym,
+                                                    struct expr **res)
+{
+       struct protoent *p;
+
+       p = getprotobyname(sym->identifier);
+       if (p == NULL)
+               return error(&sym->location, "Could not resolve protocol name");
+
+       *res = constant_expr_alloc(&sym->location, &inet_protocol_type,
+                                  BYTEORDER_HOST_ENDIAN, BITS_PER_BYTE,
+                                  &p->p_proto);
+       return NULL;
+}
+
+const struct datatype inet_protocol_type = {
+       .type           = TYPE_INET_PROTOCOL,
+       .name           = "Internet protocol",
+       .basetype       = &integer_type,
+       .print          = inet_protocol_type_print,
+       .parse          = inet_protocol_type_parse,
+};
+
+static void inet_service_type_print(const struct expr *expr)
+{
+       struct sockaddr_in sin = { .sin_family = AF_INET };
+       char buf[NI_MAXSERV];
+
+       sin.sin_port = mpz_get_be16(expr->value);
+       getnameinfo((struct sockaddr *)&sin, sizeof(sin), NULL, 0,
+                   buf, sizeof(buf),
+                   numeric_output < NUMERIC_ALL ? 0 : NI_NUMERICSERV);
+       printf("%s", buf);
+}
+
+static struct error_record *inet_service_type_parse(const struct expr *sym,
+                                                   struct expr **res)
+{
+       struct addrinfo *ai;
+       uint16_t port;
+       int err;
+
+       err = getaddrinfo(NULL, sym->identifier, NULL, &ai);
+       if (err != 0)
+               return error(&sym->location, "Could not resolve service: %s",
+                            gai_strerror(err));
+
+       port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
+       *res = constant_expr_alloc(&sym->location, &inet_service_type,
+                                  BYTEORDER_BIG_ENDIAN,
+                                  sizeof(port) * BITS_PER_BYTE, &port);
+       freeaddrinfo(ai);
+       return NULL;
+}
+
+const struct datatype inet_service_type = {
+       .type           = TYPE_INET_SERVICE,
+       .name           = "internet network service",
+       .basetype       = &integer_type,
+       .print          = inet_service_type_print,
+       .parse          = inet_service_type_parse,
+};
+
+#define RT_SYM_TAB_INITIAL_SIZE                16
+
+struct symbol_table *rt_symbol_table_init(const char *filename)
+{
+       struct symbolic_constant s;
+       struct symbol_table *tbl;
+       unsigned int size, nelems, val;
+       char buf[512], namebuf[512], *p;
+       FILE *f;
+
+       size = RT_SYM_TAB_INITIAL_SIZE;
+       tbl = xmalloc(sizeof(*tbl) + size * sizeof(s));
+       nelems = 0;
+
+       tbl->size      = 4 * BITS_PER_BYTE;
+       tbl->byteorder = BYTEORDER_HOST_ENDIAN;
+
+       f = fopen(filename, "r");
+       if (f == NULL)
+               goto out;
+
+       while (fgets(buf, sizeof(buf), f)) {
+               p = buf;
+               while (*p == ' ' || *p == '\t')
+                       p++;
+               if (*p == '#' || *p == '\n' || *p == '\0')
+                       continue;
+               if (sscanf(p, "0x%x %511s\n", &val, namebuf) != 2 &&
+                   sscanf(p, "0x%x %511s #", &val, namebuf) != 2 &&
+                   sscanf(p, "%u %511s\n", &val, namebuf) != 2 &&
+                   sscanf(p, "%u %511s #", &val, namebuf) != 2) {
+                       fprintf(stderr, "iproute database '%s' corrupted\n",
+                               filename);
+                       goto out;
+               }
+
+               /* One element is reserved for list terminator */
+               if (nelems == size - 2) {
+                       size *= 2;
+                       tbl = xrealloc(tbl, sizeof(*tbl) + size * sizeof(s));
+               }
+
+               tbl->symbols[nelems].identifier = xstrdup(namebuf);
+               tbl->symbols[nelems].value = val;
+               nelems++;
+       }
+
+       fclose(f);
+out:
+       tbl->symbols[nelems] = SYMBOL_LIST_END;
+       return tbl;
+}
+
+void rt_symbol_table_free(struct symbol_table *tbl)
+{
+       const struct symbolic_constant *s;
+
+       for (s = tbl->symbols; s->identifier != NULL; s++)
+               xfree(s->identifier);
+       xfree(tbl);
+}
+
+static struct symbol_table *mark_tbl;
+static void __init mark_table_init(void)
+{
+       mark_tbl = rt_symbol_table_init("/etc/iproute2/rt_marks");
+}
+
+static void __exit mark_table_exit(void)
+{
+       rt_symbol_table_free(mark_tbl);
+}
+
+static void mark_type_print(const struct expr *expr)
+{
+       return symbolic_constant_print(mark_tbl, expr);
+}
+
+static struct error_record *mark_type_parse(const struct expr *sym,
+                                           struct expr **res)
+{
+       return symbolic_constant_parse(sym, mark_tbl, res);
+}
+
+const struct datatype mark_type = {
+       .type           = TYPE_MARK,
+       .name           = "packet mark",
+       .basetype       = &integer_type,
+       .basefmt        = "0x%.8Zx",
+       .print          = mark_type_print,
+       .parse          = mark_type_parse,
+};
+
+static void time_type_print(const struct expr *expr)
+{
+       uint64_t days, hours, minutes, seconds;
+       const char *delim = "";
+
+       seconds = mpz_get_uint64(expr->value);
+
+       days = seconds / 86400;
+       seconds %= 86400;
+
+       hours = seconds / 3600;
+       seconds %= 3600;
+
+       minutes = seconds / 60;
+       seconds %= 60;
+
+       if (days > 0) {
+               printf("%s%" PRIu64 " d", delim, days);
+               delim = " ";
+       }
+       if (hours > 0) {
+               printf("%s%" PRIu64 " h", delim, hours);
+               delim = " ";
+       }
+       if (minutes > 0) {
+               printf("%s%" PRIu64 " min", delim, minutes);
+               delim = " ";
+       }
+       if (seconds > 0) {
+               printf("%s%" PRIu64 " s", delim, seconds);
+               delim = " ";
+       }
+}
+
+const struct datatype time_type = {
+       .type           = TYPE_TIME,
+       .name           = "relative time",
+       .basetype       = &integer_type,
+       .print          = time_type_print,
+};
diff --git a/src/erec.c b/src/erec.c
new file mode 100644 (file)
index 0000000..501bf4b
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <netlink.h>
+#include <gmputil.h>
+#include <erec.h>
+
+static const struct input_descriptor internal_indesc = {
+       .type   = INDESC_INTERNAL,
+       .name   = "internal",
+};
+
+const struct location internal_location = {
+       .indesc = &internal_indesc,
+};
+
+static const char *error_record_names[] = {
+       [EREC_INFORMATIONAL]    = NULL,
+       [EREC_WARNING]          = "Warning",
+       [EREC_ERROR]            = "Error"
+};
+
+void erec_add_location(struct error_record *erec, const struct location *loc)
+{
+       assert(erec->num_locations < EREC_LOCATIONS_MAX);
+       erec->locations[erec->num_locations++] = *loc;
+}
+
+static void erec_destroy(struct error_record *erec)
+{
+       xfree(erec->msg);
+       xfree(erec);
+}
+
+struct error_record *erec_vcreate(enum error_record_types type,
+                                 const struct location *loc,
+                                 const char *fmt, va_list ap)
+{
+       struct error_record *erec;
+
+       erec = xmalloc(sizeof(*erec));
+       erec->type              = type;
+       erec->num_locations     = 0;
+       erec_add_location(erec, loc);
+
+       gmp_vasprintf(&erec->msg, fmt, ap);
+       return erec;
+}
+
+struct error_record *erec_create(enum error_record_types type,
+                                const struct location *loc,
+                                const char *fmt, ...)
+{
+       struct error_record *erec;
+       va_list ap;
+
+       va_start(ap, fmt);
+       erec = erec_vcreate(type, loc, fmt, ap);
+       va_end(ap);
+       return erec;
+}
+
+void erec_print(FILE *f, const struct error_record *erec)
+{
+       const struct location *loc = erec->locations, *iloc;
+       const struct input_descriptor *indesc = loc->indesc, *tmp;
+       const char *line = NULL; /* silence gcc */
+       char buf[1024];
+       unsigned int i, end;
+       int l;
+
+       switch (indesc->type) {
+       case INDESC_BUFFER:
+       case INDESC_CLI:
+               line = indesc->data;
+               break;
+       case INDESC_FILE:
+               memset(buf, 0, sizeof(buf));
+               lseek(indesc->fd, loc->line_offset, SEEK_SET);
+               read(indesc->fd, buf, sizeof(buf) - 1);
+               *strchrnul(buf, '\n') = '\0';
+               line = buf;
+               break;
+       case INDESC_INTERNAL:
+       case INDESC_NETLINK:
+               break;
+       default:
+               BUG();
+       }
+
+       if (indesc->type == INDESC_NETLINK) {
+               fprintf(f, "%s: ", indesc->name);
+               if (error_record_names[erec->type])
+                       fprintf(f, "%s: ", error_record_names[erec->type]);
+               fprintf(f, "%s\n", erec->msg);
+               for (l = 0; l < (int)erec->num_locations; l++) {
+                       loc = &erec->locations[l];
+                       netlink_dump_object(loc->nl_obj);
+               }
+               fprintf(f, "\n");
+       } else {
+               if (indesc->location.indesc != NULL) {
+                       const char *prefix = "In file included from";
+                       iloc = &indesc->location;
+                       for (tmp = iloc->indesc; tmp != NULL; tmp = iloc->indesc) {
+                               fprintf(f, "%s %s:%u:%u-%u:\n", prefix,
+                                       tmp->name,
+                                       iloc->first_line, iloc->first_column,
+                                       iloc->last_column);
+                               prefix = "                 from";
+                               iloc = &tmp->location;
+                       }
+               }
+               if (indesc->name != NULL)
+                       fprintf(f, "%s:%u:%u-%u: ", indesc->name,
+                               loc->first_line, loc->first_column,
+                               loc->last_column);
+               if (error_record_names[erec->type])
+                       fprintf(f, "%s: ", error_record_names[erec->type]);
+               fprintf(f, "%s\n", erec->msg);
+
+               if (indesc->type != INDESC_INTERNAL)
+                       fprintf(f, "%s\n", line);
+
+               memset(buf, ' ', sizeof(buf));
+               end = 0;
+               for (l = erec->num_locations - 1; l >= 0; l--) {
+                       loc = &erec->locations[l];
+                       for (i = loc->first_column - 1; i < loc->last_column; i++)
+                               buf[i] = l ? '~' : '^';
+                       end = max(end, loc->last_column);
+               }
+               buf[end] = '\0';
+               fprintf(f, "%s", buf);
+       }
+       fprintf(f, "\n");
+}
+
+void erec_print_list(FILE *f, struct list_head *list)
+{
+       struct error_record *erec, *next;
+
+       list_for_each_entry_safe(erec, next, list, list) {
+               list_del(&erec->list);
+               erec_print(f, erec);
+               erec_destroy(erec);
+       }
+}
diff --git a/src/evaluate.c b/src/evaluate.c
new file mode 100644 (file)
index 0000000..0deff9a
--- /dev/null
@@ -0,0 +1,1031 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <expression.h>
+#include <statement.h>
+#include <rule.h>
+#include <erec.h>
+#include <gmputil.h>
+#include <utils.h>
+
+#define TRACE  0
+
+static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr);
+static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt);
+
+static const char *byteorder_names[] = {
+       [BYTEORDER_INVALID]             = "invalid",
+       [BYTEORDER_HOST_ENDIAN]         = "host endian",
+       [BYTEORDER_BIG_ENDIAN]          = "big endian",
+};
+
+
+static int __fmtstring(4, 5) stmt_binary_error(struct eval_ctx *ctx,
+                                              const struct stmt *s1,
+                                              const struct stmt *s2,
+                                              const char *fmt, ...)
+{
+       struct error_record *erec;
+       va_list ap;
+
+       va_start(ap, fmt);
+       erec = erec_vcreate(EREC_ERROR, &s1->location, fmt, ap);
+       if (s2 != NULL)
+               erec_add_location(erec, &s2->location);
+       va_end(ap);
+       erec_queue(erec, ctx->msgs);
+       return -1;
+}
+
+static enum ops byteorder_conversion_op(struct expr *expr,
+                                       enum byteorder byteorder)
+{
+       switch (expr->byteorder) {
+       case BYTEORDER_HOST_ENDIAN:
+               if (byteorder == BYTEORDER_BIG_ENDIAN)
+                       return OP_HTON;
+               break;
+       case BYTEORDER_BIG_ENDIAN:
+               if (byteorder == BYTEORDER_HOST_ENDIAN)
+                       return OP_NTOH;
+               break;
+       default:
+               break;
+       }
+       BUG();
+}
+
+static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr,
+                               enum byteorder byteorder)
+{
+       enum ops op;
+
+       assert(!expr_is_constant(*expr) || expr_is_singleton(*expr));
+
+       if ((*expr)->byteorder == byteorder)
+               return 0;
+       if (expr_basetype(*expr)->type != TYPE_INTEGER)
+               return expr_error(ctx, *expr,
+                                 "Byteorder mismatch: expected %s, got %s",
+                                 byteorder_names[byteorder],
+                                 byteorder_names[(*expr)->byteorder]);
+
+       if (expr_is_constant(*expr))
+               (*expr)->byteorder = byteorder;
+       else {
+               op = byteorder_conversion_op(*expr, byteorder);
+               *expr = unary_expr_alloc(&(*expr)->location, op, *expr);
+               if (expr_evaluate(ctx, expr) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+/*
+ * Symbol expression: parse symbol and evaluate resulting expression.
+ */
+static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct error_record *erec;
+       struct expr *new;
+
+       (*expr)->sym_type = ctx->ectx.dtype;
+       erec = symbol_parse(*expr, &new);
+       if (erec != NULL) {
+               erec_queue(erec, ctx->msgs);
+               return -1;
+       }
+
+       expr_free(*expr);
+       *expr = new;
+
+       return expr_evaluate(ctx, expr);
+}
+
+static int expr_evaluate_value(struct eval_ctx *ctx, struct expr **expr)
+{
+       mpz_t mask;
+
+       switch (expr_basetype(*expr)->type) {
+       case TYPE_INTEGER:
+               mpz_init_bitmask(mask, ctx->ectx.len);
+               if (mpz_cmp((*expr)->value, mask) > 0) {
+                       expr_error(ctx, *expr,
+                                  "Value %Zu exceeds valid range 0-%Zu",
+                                  (*expr)->value, mask);
+                       mpz_clear(mask);
+                       return -1;
+               }
+               (*expr)->len = ctx->ectx.len;
+               mpz_clear(mask);
+               break;
+       case TYPE_STRING:
+               if (ctx->ectx.len > 0) {
+                       if ((*expr)->len > ctx->ectx.len)
+                               return expr_error(ctx, *expr,
+                                                 "String exceeds maximum length of %u",
+                                                 ctx->ectx.len / BITS_PER_BYTE);
+                       (*expr)->len = ctx->ectx.len;
+               }
+               break;
+       default:
+               BUG();
+       }
+       return 0;
+}
+
+/*
+ * Primary expressions determine the datatype context.
+ */
+static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr)
+{
+       ctx->ectx.dtype = (*expr)->dtype;
+       ctx->ectx.len   = (*expr)->len;
+       (*expr)->flags |= EXPR_F_PRIMARY;
+       return 0;
+}
+
+/*
+ * Payload expression: check whether dependencies are fulfilled, otherwise
+ * generate the necessary relational expression and prepend it to the current
+ * statement.
+ */
+static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *payload = *expr;
+       enum payload_bases base = payload->payload.base;
+       struct stmt *nstmt;
+       struct expr *nexpr;
+
+       if (ctx->pctx.protocol[base].desc == NULL) {
+               if (payload_gen_dependency(ctx, payload, &nexpr) < 0)
+                       return -1;
+               nstmt = expr_stmt_alloc(&nexpr->location, nexpr);
+               if (stmt_evaluate(ctx, nstmt) < 0)
+                       return -1;
+               list_add_tail(&nstmt->list, &ctx->stmt->list);
+       } else if (ctx->pctx.protocol[base].desc != payload->payload.desc)
+               return expr_error(ctx, payload,
+                                 "conflicting protocols specified: %s vs. %s",
+                                 ctx->pctx.protocol[base].desc->name,
+                                 payload->payload.desc->name);
+
+       return expr_evaluate_primary(ctx, expr);
+}
+
+/*
+ * Prefix expression: the argument must be a constant value of integer base
+ * type; the prefix length must be less than or equal to the type width.
+ */
+static int expr_evaluate_prefix(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *prefix = *expr, *base, *and, *mask;
+
+       if (expr_evaluate(ctx, &prefix->expr) < 0)
+               return -1;
+       base = prefix->expr;
+
+       if (!expr_is_constant(base))
+               return expr_error(ctx, prefix,
+                                 "Prefix expression is undefined for "
+                                 "non-constant expressions");
+
+       if (expr_basetype(base)->type != TYPE_INTEGER)
+               return expr_error(ctx, prefix,
+                                 "Prefix expression is undefined for "
+                                 "%s types", base->dtype->name);
+
+       if (prefix->prefix_len > base->len)
+               return expr_error(ctx, prefix,
+                                 "Prefix length %u is invalid for type "
+                                 "of %u bits width",
+                                 prefix->prefix_len, base->len);
+
+       /* Clear the uncovered bits of the base value */
+       mask = constant_expr_alloc(&prefix->location, &integer_type,
+                                  BYTEORDER_HOST_ENDIAN, base->len, NULL);
+       mpz_prefixmask(mask->value, base->len, prefix->prefix_len);
+       and  = binop_expr_alloc(&prefix->location, OP_AND, base, mask);
+
+       prefix->expr = and;
+       if (expr_evaluate(ctx, &prefix->expr) < 0)
+               return -1;
+       base = prefix->expr;
+       assert(expr_is_constant(base));
+
+       prefix->dtype     = base->dtype;
+       prefix->byteorder = base->byteorder;
+       prefix->len       = base->len;
+       prefix->flags    |= EXPR_F_CONSTANT;
+       return 0;
+}
+
+/*
+ * Range expression: both sides must be constants of integer base type.
+ */
+static int expr_evaluate_range_expr(struct eval_ctx *ctx,
+                                   const struct expr *range,
+                                   struct expr **expr)
+{
+       if (expr_evaluate(ctx, expr) < 0)
+               return -1;
+
+       if (expr_basetype(*expr)->type != TYPE_INTEGER)
+               return expr_binary_error(ctx, *expr, range,
+                                        "Range expression is undefined for "
+                                        "%s types", (*expr)->dtype->name);
+       if (!expr_is_constant(*expr))
+               return expr_binary_error(ctx, *expr, range,
+                                        "Range is not constant");
+       return 0;
+}
+
+static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *range = *expr, *left, *right;
+
+       if (expr_evaluate_range_expr(ctx, range, &range->left) < 0)
+               return -1;
+       left = range->left;
+
+       if (expr_evaluate_range_expr(ctx, range, &range->right) < 0)
+               return -1;
+       right = range->right;
+
+       if (mpz_cmp(left->value, right->value) >= 0)
+               return expr_error(ctx, range, "Range has zero or negative size");
+
+       range->dtype = left->dtype;
+       range->flags |= EXPR_F_CONSTANT;
+       return 0;
+}
+
+/*
+ * Unary expressions: unary expressions are only generated internally for
+ * byteorder conversion of non-constant numerical expressions.
+ */
+static int expr_evaluate_unary(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *unary = *expr, *arg;
+       enum byteorder byteorder;
+
+       if (expr_evaluate(ctx, &unary->arg) < 0)
+               return -1;
+       arg = unary->arg;
+
+       assert(!expr_is_constant(arg));
+       assert(expr_basetype(arg)->type == TYPE_INTEGER);
+       assert(arg->ops->type != EXPR_UNARY);
+
+       switch (unary->op) {
+       case OP_HTON:
+               assert(arg->byteorder == BYTEORDER_HOST_ENDIAN);
+               byteorder = BYTEORDER_BIG_ENDIAN;
+               break;
+       case OP_NTOH:
+               assert(arg->byteorder == BYTEORDER_BIG_ENDIAN);
+               byteorder = BYTEORDER_HOST_ENDIAN;
+               break;
+       default:
+               BUG();
+       }
+
+       unary->dtype     = arg->dtype;
+       unary->byteorder = byteorder;
+       unary->len       = arg->len;
+       return 0;
+}
+
+/*
+ * Binops
+ */
+static int constant_binop_simplify(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *op = *expr, *left = (*expr)->left, *right = (*expr)->right;
+       struct expr *new;
+       mpz_t val, mask;
+
+       assert(left->ops->type == EXPR_VALUE);
+       assert(right->ops->type == EXPR_VALUE);
+       assert(left->byteorder == right->byteorder);
+
+       mpz_init2(val, op->len);
+       mpz_init_bitmask(mask, op->len);
+
+       switch (op->op) {
+       case OP_AND:
+               mpz_and(val, left->value, right->value);
+               mpz_and(val, val, mask);
+               break;
+       case OP_XOR:
+               mpz_xor(val, left->value, right->value);
+               mpz_and(val, val, mask);
+               break;
+       case OP_OR:
+               mpz_ior(val, left->value, right->value);
+               mpz_and(val, val, mask);
+               break;
+       case OP_LSHIFT:
+               assert(left->byteorder == BYTEORDER_HOST_ENDIAN);
+               mpz_lshift_ui(val, mpz_get_uint32(right->value));
+               mpz_and(val, val, mask);
+               break;
+       case OP_RSHIFT:
+               assert(left->byteorder == BYTEORDER_HOST_ENDIAN);
+               mpz_set(val, left->value);
+               mpz_and(val, val, mask);
+               mpz_rshift_ui(val, mpz_get_uint32(right->value));
+               break;
+       default:
+               BUG();
+       }
+
+       new = constant_expr_alloc(&op->location, op->dtype, op->byteorder,
+                                 op->len, NULL);
+       mpz_set(new->value, val);
+
+       expr_free(*expr);
+       *expr = new;
+
+       mpz_clear(mask);
+       mpz_clear(val);
+
+       return expr_evaluate(ctx, expr);
+}
+
+static int expr_evaluate_shift(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *op = *expr, *left = op->left, *right = op->right;
+
+       if (mpz_get_uint32(right->value) >= left->len)
+               return expr_binary_error(ctx, right, left,
+                                        "%s shift of %u bits is undefined "
+                                        "for type of %u bits width",
+                                        op->op == OP_LSHIFT ? "Left" : "Right",
+                                        mpz_get_uint32(right->value),
+                                        left->len);
+
+       /* Both sides need to be in host byte order */
+       if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0)
+               return -1;
+       left = op->left;
+       if (byteorder_conversion(ctx, &op->right, BYTEORDER_HOST_ENDIAN) < 0)
+               return -1;
+
+       op->dtype     = &integer_type;
+       op->byteorder = BYTEORDER_HOST_ENDIAN;
+       op->len       = left->len;
+
+       if (expr_is_constant(left))
+               return constant_binop_simplify(ctx, expr);
+       return 0;
+}
+
+static int expr_evaluate_bitwise(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *op = *expr, *left = op->left, *right = op->right;
+
+       if (byteorder_conversion(ctx, &op->right, left->byteorder) < 0)
+               return -1;
+       right = op->right;
+
+       op->dtype     = left->dtype;
+       op->byteorder = left->byteorder;
+       op->len       = left->len;
+
+       if (expr_is_constant(left))
+               return constant_binop_simplify(ctx, expr);
+       return 0;
+}
+
+/*
+ * Binop expression: both sides must be of integer base type. The left
+ * hand side may be either constant or non-constant; in case its constant
+ * it must be a singleton. The ride hand side must always be a constant
+ * singleton.
+ */
+static int expr_evaluate_binop(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *op = *expr, *left, *right;
+       const char *sym = expr_op_symbols[op->op];
+
+       if (expr_evaluate(ctx, &op->left) < 0)
+               return -1;
+       left = op->left;
+
+       if (op->op == OP_LSHIFT || op->op == OP_RSHIFT)
+               expr_set_context(&ctx->ectx, &integer_type, ctx->ectx.len);
+       if (expr_evaluate(ctx, &op->right) < 0)
+               return -1;
+       right = op->right;
+
+       if (expr_basetype(left)->type != TYPE_INTEGER)
+               return expr_binary_error(ctx, left, op,
+                                        "Binary operation (%s) is undefined "
+                                        "for %s types",
+                                        sym, left->dtype->name);
+
+       if (expr_is_constant(left) && !expr_is_singleton(left))
+               return expr_binary_error(ctx, left, op,
+                                        "Binary operation (%s) is undefined "
+                                        "for %s expressions",
+                                        sym, left->ops->name);
+
+       if (!expr_is_constant(right))
+               return expr_binary_error(ctx, right, op,
+                                        "Right hand side of binary operation "
+                                        "(%s) must be constant", sym);
+
+       if (!expr_is_singleton(right))
+               return expr_binary_error(ctx, left, op,
+                                        "Binary operation (%s) is undefined "
+                                        "for %s expressions",
+                                        sym, right->ops->name);
+
+       /* The grammar guarantees this */
+       assert(expr_basetype(left) == expr_basetype(right));
+
+       switch (op->op) {
+       case OP_LSHIFT:
+       case OP_RSHIFT:
+               return expr_evaluate_shift(ctx, expr);
+       case OP_AND:
+       case OP_XOR:
+       case OP_OR:
+               return expr_evaluate_bitwise(ctx, expr);
+       default:
+               BUG();
+       }
+}
+
+static int list_member_evaluate(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *next = list_entry((*expr)->list.next, struct expr, list);
+       int err;
+
+       assert(*expr != next);
+       list_del(&(*expr)->list);
+       err = expr_evaluate(ctx, expr);
+       list_add_tail(&(*expr)->list, &next->list);
+       return err;
+}
+
+static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr)
+{
+       unsigned int flags = EXPR_F_CONSTANT;
+       struct expr *i, *next;
+
+       list_for_each_entry_safe(i, next, &(*expr)->expressions, list) {
+               if (list_member_evaluate(ctx, &i) < 0)
+                       return -1;
+               flags &= i->flags;
+       }
+
+       (*expr)->flags |= flags;
+       return 0;
+}
+
+static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *list = *expr, *new, *i, *next;
+       mpz_t val;
+
+       mpz_init_set_ui(val, 0);
+       list_for_each_entry_safe(i, next, &list->expressions, list) {
+               if (list_member_evaluate(ctx, &i) < 0)
+                       return -1;
+               if (i->ops->type != EXPR_VALUE)
+                       return expr_error(ctx, i,
+                                         "List member must be a constant "
+                                         "value");
+               if (i->dtype->basetype->type != TYPE_BITMASK)
+                       return expr_error(ctx, i,
+                                         "Basetype of type %s is not bitmask",
+                                         i->dtype->name);
+               mpz_ior(val, val, i->value);
+       }
+
+       new = constant_expr_alloc(&list->location, ctx->ectx.dtype,
+                                 BYTEORDER_HOST_ENDIAN, ctx->ectx.len, NULL);
+       mpz_set(new->value, val);
+       mpz_clear(val);
+
+       expr_free(*expr);
+       *expr = new;
+       return 0;
+}
+
+static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *set = *expr, *i, *next;
+
+       list_for_each_entry_safe(i, next, &set->expressions, list) {
+               if (list_member_evaluate(ctx, &i) < 0)
+                       return -1;
+               if (!expr_is_constant(i))
+                       return expr_error(ctx, i, "Set member is not constant");
+               if (!expr_is_singleton(i))
+                       set->flags |= SET_F_INTERVAL;
+       }
+
+       set->dtype = ctx->ectx.dtype;
+       set->len   = ctx->ectx.len;
+       set->flags |= EXPR_F_CONSTANT;
+       return 0;
+}
+
+static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr_ctx ectx = ctx->ectx;
+       struct expr *map = *expr, *i;
+
+       if (expr_evaluate(ctx, &map->expr) < 0)
+               return -1;
+       if (expr_is_constant(map->expr))
+               return expr_error(ctx, map->expr,
+                                 "Map expression can not be constant");
+
+       /* FIXME: segtree needs to know the dimension of the *key*.
+        * The len should actually be the value of the mapping. */
+       map->mappings->dtype = ctx->ectx.dtype;
+       map->mappings->len   = ctx->ectx.len;
+
+       list_for_each_entry(i, &map->mappings->expressions, list) {
+               expr_set_context(&ctx->ectx, map->expr->dtype, map->expr->len);
+               if (expr_evaluate(ctx, &i->left) < 0)
+                       return -1;
+               if (!expr_is_constant(i->left))
+                       return expr_error(ctx, i->left,
+                                         "Key must be a constant");
+               if (!expr_is_singleton(i->left))
+                       map->mappings->flags |= SET_F_INTERVAL;
+
+               expr_set_context(&ctx->ectx, ectx.dtype, ectx.len);
+               if (expr_evaluate(ctx, &i->right) < 0)
+                       return -1;
+               if (!expr_is_constant(i->right))
+                       return expr_error(ctx, i->right,
+                                         "Mapping must be a constant");
+               if (!expr_is_singleton(i->right))
+                       return expr_error(ctx, i->right,
+                                         "Mapping must be a singleton");
+       }
+
+       map->dtype = ctx->ectx.dtype;
+       map->flags |= EXPR_F_CONSTANT;
+
+       /* Data for range lookups needs to be in big endian order */
+       if (map->mappings->flags & SET_F_INTERVAL &&
+           byteorder_conversion(ctx, &map->expr, BYTEORDER_BIG_ENDIAN) < 0)
+               return -1;
+       return 0;
+}
+
+/*
+ * Transfer the invertible binops to the constant side of an equality
+ * expression. A left shift is only invertible if the low n bits are
+ * zero.
+ */
+static int binop_can_transfer(struct eval_ctx *ctx,
+                             struct expr *left, struct expr *right)
+{
+       switch (left->op) {
+       case OP_LSHIFT:
+               if (mpz_scan1(right->value, 0) < mpz_get_uint32(left->right->value))
+                       return expr_binary_error(ctx, right, left,
+                                                "Comparison is always false");
+               return 1;
+       case OP_XOR:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static int binop_transfer_one(struct eval_ctx *ctx,
+                             const struct expr *left, struct expr **right)
+{
+       expr_get(*right);
+
+       switch (left->op) {
+       case OP_LSHIFT:
+               (*right) = binop_expr_alloc(&(*right)->location, OP_RSHIFT,
+                                           *right, expr_get(left->right));
+               break;
+       case OP_XOR:
+               (*right) = binop_expr_alloc(&(*right)->location, OP_XOR,
+                                           *right, expr_get(left->right));
+               break;
+       default:
+               BUG();
+       }
+
+       return expr_evaluate(ctx, right);
+}
+
+static int binop_transfer(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *left = (*expr)->left, *i, *next;
+       int err;
+
+       if (left->ops->type != EXPR_BINOP)
+               return 0;
+
+       switch ((*expr)->right->ops->type) {
+       case EXPR_VALUE:
+               err = binop_can_transfer(ctx, left, (*expr)->right);
+               if (err <= 0)
+                       return err;
+               if (binop_transfer_one(ctx, left, &(*expr)->right) < 0)
+                       return -1;
+               break;
+       case EXPR_SET:
+               list_for_each_entry(i, &(*expr)->right->expressions, list) {
+                       err = binop_can_transfer(ctx, left, i);
+                       if (err <= 0)
+                               return err;
+               }
+               list_for_each_entry_safe(i, next, &(*expr)->right->expressions,
+                                        list) {
+                       list_del(&i->list);
+                       if (binop_transfer_one(ctx, left, &i) < 0)
+                               return -1;
+                       list_add_tail(&i->list, &next->list);
+               }
+               break;
+       default:
+               return 0;
+       }
+
+       left = expr_get((*expr)->left->left);
+       left->dtype = (*expr)->left->dtype;
+       expr_free((*expr)->left);
+       (*expr)->left = left;
+       return 0;
+}
+
+static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr)
+{
+       struct expr *rel = *expr, *left, *right;
+
+       if (expr_evaluate(ctx, &rel->left) < 0)
+               return -1;
+       left = rel->left;
+
+       if (expr_evaluate(ctx, &rel->right) < 0)
+               return -1;
+       right = rel->right;
+
+       if (!expr_is_constant(right))
+               return expr_binary_error(ctx, right, rel,
+                                        "Right hand side of relational "
+                                        "expression (%s) must be constant",
+                                        expr_op_symbols[rel->op]);
+       if (expr_is_constant(left))
+               return expr_binary_error(ctx, left, right,
+                                        "Relational expression (%s) has "
+                                        "constant value",
+                                        expr_op_symbols[rel->op]);
+
+       switch (rel->op) {
+       case OP_LOOKUP:
+               /* Data for range lookups needs to be in big endian order */
+               if (right->flags & SET_F_INTERVAL &&
+                   byteorder_conversion(ctx, &rel->left,
+                                        BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               left = rel->left;
+               break;
+       case OP_EQ:
+               /*
+                * Update payload context for payload and meta iiftype equality
+                * expressions.
+                */
+               switch (left->ops->type) {
+               case EXPR_PAYLOAD:
+                       payload_ctx_update(&ctx->pctx, rel);
+                       break;
+               case EXPR_META:
+                       payload_ctx_update_meta(&ctx->pctx, rel);
+                       break;
+               default:
+                       break;
+               }
+       case OP_NEQ:
+       case OP_FLAGCMP:
+               switch (right->ops->type) {
+               case EXPR_RANGE:
+                       goto range;
+               case EXPR_PREFIX:
+                       if (byteorder_conversion(ctx, &right->expr, left->byteorder) < 0)
+                               return -1;
+                       break;
+               case EXPR_VALUE:
+                       if (byteorder_conversion(ctx, &rel->right, left->byteorder) < 0)
+                               return -1;
+                       break;
+               default:
+                       BUG();
+               }
+               break;
+       case OP_LT:
+       case OP_GT:
+       case OP_LTE:
+       case OP_GTE:
+               switch (left->ops->type) {
+               case EXPR_CONCAT:
+                       return expr_binary_error(ctx, left, rel,
+                                       "Relational expression (%s) is undefined "
+                                       "for %s expressions",
+                                       expr_op_symbols[rel->op],
+                                       left->ops->name);
+               default:
+                       break;
+               }
+
+               if (!expr_is_singleton(right))
+                       return expr_binary_error(ctx, right, rel,
+                                       "Relational expression (%s) is undefined "
+                                       "for %s expressions",
+                                       expr_op_symbols[rel->op],
+                                       right->ops->name);
+
+               if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               if (byteorder_conversion(ctx, &rel->right, BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               break;
+       case OP_RANGE:
+range:
+               switch (left->ops->type) {
+               case EXPR_CONCAT:
+                       return expr_binary_error(ctx, left, rel,
+                                       "Relational expression (%s) is undefined"
+                                       "for %s expressions",
+                                       expr_op_symbols[rel->op],
+                                       left->ops->name);
+               default:
+                       break;
+               }
+
+               if (byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               if (byteorder_conversion(ctx, &right->left, BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               if (byteorder_conversion(ctx, &right->right, BYTEORDER_BIG_ENDIAN) < 0)
+                       return -1;
+               break;
+       default:
+               BUG();
+       }
+
+       if (binop_transfer(ctx, expr) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr)
+{
+#if TRACE
+       struct error_record *erec;
+       erec = erec_create(EREC_INFORMATIONAL, &(*expr)->location, "Evaluate");
+       erec_print(stdout, erec); expr_print(*expr); printf("\n\n");
+#endif
+
+       switch ((*expr)->ops->type) {
+       case EXPR_SYMBOL:
+               return expr_evaluate_symbol(ctx, expr);
+       case EXPR_VALUE:
+               return expr_evaluate_value(ctx, expr);
+       case EXPR_VERDICT:
+       case EXPR_EXTHDR:
+       case EXPR_META:
+       case EXPR_CT:
+               return expr_evaluate_primary(ctx, expr);
+       case EXPR_PAYLOAD:
+               return expr_evaluate_payload(ctx, expr);
+       case EXPR_PREFIX:
+               return expr_evaluate_prefix(ctx, expr);
+       case EXPR_RANGE:
+               return expr_evaluate_range(ctx, expr);
+       case EXPR_UNARY:
+               return expr_evaluate_unary(ctx, expr);
+       case EXPR_BINOP:
+               return expr_evaluate_binop(ctx, expr);
+       case EXPR_CONCAT:
+               return expr_evaluate_concat(ctx, expr);
+       case EXPR_LIST:
+               return expr_evaluate_list(ctx, expr);
+       case EXPR_SET:
+               return expr_evaluate_set(ctx, expr);
+       case EXPR_MAP:
+               return expr_evaluate_map(ctx, expr);
+       case EXPR_RELATIONAL:
+               return expr_evaluate_relational(ctx, expr);
+       default:
+               BUG();
+       }
+}
+
+static int stmt_evaluate_expr(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       memset(&ctx->ectx, 0, sizeof(ctx->ectx));
+       return expr_evaluate(ctx, &stmt->expr);
+}
+
+static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       if (expr_evaluate(ctx, &stmt->expr) < 0)
+               return -1;
+
+       switch (stmt->expr->ops->type) {
+       case EXPR_VERDICT:
+               if (stmt->expr->verdict != NFT_CONTINUE)
+                       stmt->flags |= STMT_F_TERMINAL;
+               break;
+       case EXPR_MAP:
+               break;
+       default:
+               BUG();
+       }
+       return 0;
+}
+
+static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       expr_set_context(&ctx->ectx, stmt->meta.tmpl->dtype,
+                        stmt->meta.tmpl->len);
+       if (expr_evaluate(ctx, &stmt->meta.expr) < 0)
+               return -1;
+       return 0;
+}
+
+static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       stmt->flags |= STMT_F_TERMINAL;
+       return 0;
+}
+
+static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt)
+{
+       int err;
+
+       if (stmt->nat.addr != NULL) {
+               expr_set_context(&ctx->ectx, &ipaddr_type,
+                                4 * BITS_PER_BYTE);
+               err = expr_evaluate(ctx, &stmt->nat.addr);
+               if (err < 0)
+                       return err;
+       }
+
+       if (stmt->nat.proto != NULL) {
+               expr_set_context(&ctx->ectx, &inet_service_type,
+                                2 * BITS_PER_BYTE);
+               err = expr_evaluate(ctx, &stmt->nat.proto);
+               if (err < 0)
+                       return err;
+       }
+
+       stmt->flags |= STMT_F_TERMINAL;
+       return 0;
+}
+
+static int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt)
+{
+#if TRACE
+       struct error_record *erec;
+       erec = erec_create(EREC_INFORMATIONAL, &stmt->location, "Evaluate");
+       erec_print(stdout, erec); stmt_print(stmt); printf("\n\n");
+#endif
+
+       switch (stmt->ops->type) {
+       case STMT_COUNTER:
+       case STMT_LIMIT:
+       case STMT_LOG:
+               return 0;
+       case STMT_EXPRESSION:
+               return stmt_evaluate_expr(ctx, stmt);
+       case STMT_VERDICT:
+               return stmt_evaluate_verdict(ctx, stmt);
+       case STMT_META:
+               return stmt_evaluate_meta(ctx, stmt);
+       case STMT_REJECT:
+               return stmt_evaluate_reject(ctx, stmt);
+       case STMT_NAT:
+               return stmt_evaluate_nat(ctx, stmt);
+       default:
+               BUG();
+       }
+}
+
+static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule)
+{
+       struct stmt *stmt, *tstmt = NULL;
+
+       payload_ctx_init(&ctx->pctx, rule->handle.family);
+       memset(&ctx->ectx, 0, sizeof(ctx->ectx));
+
+       list_for_each_entry(stmt, &rule->stmts, list) {
+               if (tstmt != NULL)
+                       return stmt_binary_error(ctx, stmt, tstmt,
+                                                "Statement after terminal "
+                                                "statement has no effect");
+
+               ctx->stmt = stmt;
+               if (stmt_evaluate(ctx, stmt) < 0)
+                       return -1;
+               if (stmt->flags & STMT_F_TERMINAL)
+                       tstmt = stmt;
+       }
+       return 0;
+}
+
+static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain)
+{
+       struct rule *rule;
+
+       list_for_each_entry(rule, &chain->rules, list) {
+               handle_merge(&rule->handle, &chain->handle);
+               if (rule_evaluate(ctx, rule) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+static int table_evaluate(struct eval_ctx *ctx, struct table *table)
+{
+       struct chain *chain;
+
+       list_for_each_entry(chain, &table->chains, list) {
+               handle_merge(&chain->handle, &table->handle);
+               if (chain_evaluate(ctx, chain) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd)
+{
+       switch (cmd->obj) {
+       case CMD_OBJ_RULE:
+               handle_merge(&cmd->rule->handle, &cmd->handle);
+               return rule_evaluate(ctx, cmd->rule);
+       case CMD_OBJ_CHAIN:
+               if (cmd->data == NULL)
+                       return 0;
+               return chain_evaluate(ctx, cmd->chain);
+       case CMD_OBJ_TABLE:
+               if (cmd->data == NULL)
+                       return 0;
+               return table_evaluate(ctx, cmd->table);
+       default:
+               BUG();
+       }
+}
+
+static int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd)
+{
+#if TRACE
+       struct error_record *erec;
+       erec = erec_create(EREC_INFORMATIONAL, &cmd->location, "Evaluate");
+       erec_print(stdout, erec); printf("\n\n");
+#endif
+
+       switch (cmd->op) {
+       case CMD_ADD:
+               return cmd_evaluate_add(ctx, cmd);
+       case CMD_DELETE:
+       case CMD_LIST:
+       case CMD_FLUSH:
+               return 0;
+       default:
+               BUG();
+       };
+}
+
+int evaluate(struct eval_ctx *ctx, struct list_head *commands)
+{
+       struct cmd *cmd;
+
+       list_for_each_entry(cmd, commands, list) {
+               if (cmd_evaluate(ctx, cmd) < 0)
+                       return -1;
+       }
+       return 0;
+}
diff --git a/src/expression.c b/src/expression.c
new file mode 100644 (file)
index 0000000..66a8793
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <expression.h>
+#include <datatype.h>
+#include <rule.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <list.h>
+#include <erec.h>
+
+struct expr *expr_alloc(const struct location *loc, const struct expr_ops *ops,
+                       const struct datatype *dtype, enum byteorder byteorder,
+                       unsigned int len)
+{
+       struct expr *expr;
+
+       expr = xzalloc(sizeof(*expr));
+       expr->location  = *loc;
+       expr->ops       = ops;
+       expr->dtype     = dtype;
+       expr->byteorder = byteorder;
+       expr->len       = len;
+       expr->refcnt    = 1;
+       init_list_head(&expr->list);
+       return expr;
+}
+
+struct expr *expr_get(struct expr *expr)
+{
+       expr->refcnt++;
+       return expr;
+}
+
+void expr_free(struct expr *expr)
+{
+       if (expr == NULL)
+               return;
+       if (--expr->refcnt > 0)
+               return;
+       if (expr->ops->destroy)
+               expr->ops->destroy(expr);
+       xfree(expr);
+}
+
+void expr_print(const struct expr *expr)
+{
+       expr->ops->print(expr);
+}
+
+void expr_describe(const struct expr *expr)
+{
+       const struct datatype *dtype = expr->dtype;
+       const char *delim = "";
+
+       printf("%s expression, datatype %s", expr->ops->name, dtype->name);
+       if (dtype->basetype != NULL) {
+               printf(" (basetype ");
+               for (dtype = dtype->basetype; dtype != NULL;
+                    dtype = dtype->basetype) {
+                       printf("%s%s", delim, dtype->name);
+                       delim = ", ";
+               }
+               printf(")");
+       }
+
+       if (expr_basetype(expr)->type == TYPE_STRING) {
+               if (expr->len)
+                       printf(", %u characters", expr->len / BITS_PER_BYTE);
+               else
+                       printf(", dynamic length");
+       } else
+               printf(", %u bits", expr->len);
+
+       printf("\n");
+
+       if (expr->dtype->sym_tbl != NULL) {
+               printf("\npre-defined symbolic constants:\n");
+               symbol_table_print(expr->dtype->sym_tbl);
+       }
+}
+
+void expr_set_type(struct expr *expr, const struct datatype *dtype,
+                  enum byteorder byteorder)
+{
+       if (expr->ops->set_type)
+               expr->ops->set_type(expr, dtype, byteorder);
+       else {
+               expr->dtype     = dtype;
+               expr->byteorder = byteorder;
+       }
+}
+
+const struct datatype *expr_basetype(const struct expr *expr)
+{
+       const struct datatype *type = expr->dtype;
+
+       while (type->basetype != NULL)
+               type = type->basetype;
+       return type;
+}
+
+int __fmtstring(4, 5) expr_binary_error(struct eval_ctx *ctx,
+                                       const struct expr *e1, const struct expr *e2,
+                                       const char *fmt, ...)
+{
+       struct error_record *erec;
+       va_list ap;
+
+       va_start(ap, fmt);
+       erec = erec_vcreate(EREC_ERROR, &e1->location, fmt, ap);
+       if (e2 != NULL)
+               erec_add_location(erec, &e2->location);
+       va_end(ap);
+       erec_queue(erec, ctx->msgs);
+       return -1;
+}
+
+static void verdict_expr_print(const struct expr *expr)
+{
+       datatype_print(expr);
+}
+
+static void verdict_expr_destroy(struct expr *expr)
+{
+        xfree(expr->chain);
+}
+
+static const struct expr_ops verdict_expr_ops = {
+       .type           = EXPR_VERDICT,
+       .name           = "verdict",
+       .print          = verdict_expr_print,
+       .destroy        = verdict_expr_destroy,
+};
+
+struct expr *verdict_expr_alloc(const struct location *loc,
+                               int verdict, const char *chain)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &verdict_expr_ops, &verdict_type,
+                         BYTEORDER_INVALID, 0);
+       expr->verdict = verdict;
+       if (chain != NULL)
+               expr->chain = chain;
+       expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+       return expr;
+}
+
+static void symbol_expr_print(const struct expr *expr)
+{
+       printf("%s", expr->identifier);
+}
+
+static void symbol_expr_destroy(struct expr *expr)
+{
+       xfree(expr->identifier);
+}
+
+static const struct expr_ops symbol_expr_ops = {
+       .type           = EXPR_SYMBOL,
+       .name           = "symbol",
+       .print          = symbol_expr_print,
+       .destroy        = symbol_expr_destroy,
+};
+
+struct expr *symbol_expr_alloc(const struct location *loc,
+                              const char *identifier)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &symbol_expr_ops, &invalid_type,
+                         BYTEORDER_INVALID, 0);
+       expr->identifier = xstrdup(identifier);
+       return expr;
+}
+
+static void constant_expr_print(const struct expr *expr)
+{
+       datatype_print(expr);
+}
+
+static void constant_expr_destroy(struct expr *expr)
+{
+       mpz_clear(expr->value);
+}
+
+static const struct expr_ops constant_expr_ops = {
+       .type           = EXPR_VALUE,
+       .name           = "value",
+       .print          = constant_expr_print,
+       .destroy        = constant_expr_destroy,
+};
+
+struct expr *constant_expr_alloc(const struct location *loc,
+                                const struct datatype *dtype,
+                                enum byteorder byteorder,
+                                unsigned int len, const void *data)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &constant_expr_ops, dtype, byteorder, len);
+       expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON;
+
+       mpz_init2(expr->value, len);
+       if (data != NULL)
+               mpz_import_data(expr->value, data, byteorder,
+                               div_round_up(len, BITS_PER_BYTE));
+
+       return expr;
+}
+
+struct expr *constant_expr_join(const struct expr *e1, const struct expr *e2)
+{
+       unsigned int len = (e1->len + e2->len) / BITS_PER_BYTE, tmp;
+       unsigned char data[len];
+
+       assert(e1->ops->type == EXPR_VALUE);
+       assert(e2->ops->type == EXPR_VALUE);
+
+       tmp = e1->len / BITS_PER_BYTE;
+       mpz_export_data(data, e1->value, e1->byteorder, tmp);
+       mpz_export_data(data + tmp, e2->value, e2->byteorder,
+                       e2->len / BITS_PER_BYTE);
+
+       return constant_expr_alloc(&e1->location, &invalid_type,
+                                  BYTEORDER_INVALID, len * BITS_PER_BYTE,
+                                  data);
+}
+
+struct expr *constant_expr_splice(struct expr *expr, unsigned int len)
+{
+       struct expr *slice;
+       mpz_t mask;
+
+       assert(expr->ops->type == EXPR_VALUE);
+       assert(len <= expr->len);
+
+       slice = constant_expr_alloc(&expr->location, &invalid_type,
+                                   BYTEORDER_INVALID, len, NULL);
+       mpz_init2(mask, len);
+       mpz_bitmask(mask, len);
+       mpz_set(slice->value, expr->value);
+       mpz_and(slice->value, slice->value, mask);
+       mpz_clear(mask);
+
+       mpz_rshift_ui(expr->value, len);
+       expr->len -= len;
+       return slice;
+}
+
+static void prefix_expr_print(const struct expr *expr)
+{
+       expr_print(expr->expr);
+       printf("/%u", expr->prefix_len);
+}
+
+static void prefix_expr_set_type(const struct expr *expr,
+                                const struct datatype *type,
+                                enum byteorder byteorder)
+{
+       expr_set_type(expr->expr, type, byteorder);
+}
+
+static const struct expr_ops prefix_expr_ops = {
+       .type           = EXPR_PREFIX,
+       .name           = "prefix",
+       .print          = prefix_expr_print,
+       .set_type       = prefix_expr_set_type,
+};
+
+struct expr *prefix_expr_alloc(const struct location *loc,
+                              struct expr *expr, unsigned int prefix_len)
+{
+       struct expr *prefix;
+
+       prefix = expr_alloc(loc, &prefix_expr_ops, &invalid_type,
+                           BYTEORDER_INVALID, 0);
+       prefix->expr       = expr;
+       prefix->prefix_len = prefix_len;
+       return prefix;
+}
+
+const char *expr_op_symbols[] = {
+       [OP_INVALID]    = "invalid",
+       [OP_HTON]       = "hton",
+       [OP_NTOH]       = "ntoh",
+       [OP_AND]        = "&",
+       [OP_OR]         = "|",
+       [OP_XOR]        = "^",
+       [OP_LSHIFT]     = "<<",
+       [OP_RSHIFT]     = ">>",
+       [OP_EQ]         = NULL,
+       [OP_NEQ]        = "!=",
+       [OP_LT]         = "<",
+       [OP_GT]         = ">",
+       [OP_LTE]        = "<=",
+       [OP_GTE]        = ">=",
+       [OP_RANGE]      = "within range",
+       [OP_LOOKUP]     = NULL,
+};
+
+static void unary_expr_print(const struct expr *expr)
+{
+       if (expr_op_symbols[expr->op] != NULL)
+               printf("%s(", expr_op_symbols[expr->op]);
+       expr_print(expr->arg);
+       printf(")");
+}
+
+static void unary_expr_destroy(struct expr *expr)
+{
+       expr_free(expr->arg);
+}
+
+static const struct expr_ops unary_expr_ops = {
+       .type           = EXPR_UNARY,
+       .name           = "unary",
+       .print          = unary_expr_print,
+       .destroy        = unary_expr_destroy,
+};
+
+struct expr *unary_expr_alloc(const struct location *loc,
+                             enum ops op, struct expr *arg)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &unary_expr_ops, &invalid_type,
+                         BYTEORDER_INVALID, 0);
+       expr->op  = op;
+       expr->arg = arg;
+       return expr;
+}
+
+static void binop_expr_print(const struct expr *expr)
+{
+       expr_print(expr->left);
+       if (expr_op_symbols[expr->op] != NULL)
+               printf(" %s ", expr_op_symbols[expr->op]);
+       else
+               printf(" ");
+       expr_print(expr->right);
+}
+
+static void binop_expr_destroy(struct expr *expr)
+{
+       expr_free(expr->left);
+       expr_free(expr->right);
+}
+
+static const struct expr_ops binop_expr_ops = {
+       .type           = EXPR_BINOP,
+       .name           = "binop",
+       .print          = binop_expr_print,
+       .destroy        = binop_expr_destroy,
+};
+
+struct expr *binop_expr_alloc(const struct location *loc, enum ops op,
+                             struct expr *left, struct expr *right)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &binop_expr_ops, &invalid_type,
+                         BYTEORDER_INVALID, 0);
+       expr->left  = left;
+       expr->op    = op;
+       expr->right = right;
+       return expr;
+}
+
+static const struct expr_ops relational_expr_ops = {
+       .type           = EXPR_RELATIONAL,
+       .name           = "relational",
+       .print          = binop_expr_print,
+       .destroy        = binop_expr_destroy,
+};
+
+struct expr *relational_expr_alloc(const struct location *loc, enum ops op,
+                                  struct expr *left, struct expr *right)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &relational_expr_ops, &verdict_type,
+                         BYTEORDER_INVALID, 0);
+       expr->left  = left;
+       expr->op    = op;
+       expr->right = right;
+       return expr;
+}
+
+static void range_expr_print(const struct expr *expr)
+{
+       expr_print(expr->left);
+       printf("-");
+       expr_print(expr->right);
+}
+
+static void range_expr_destroy(struct expr *expr)
+{
+       expr_free(expr->left);
+       expr_free(expr->right);
+}
+
+static void range_expr_set_type(const struct expr *expr,
+                               const struct datatype *type,
+                               enum byteorder byteorder)
+{
+       expr_set_type(expr->left, type, byteorder);
+       expr_set_type(expr->right, type, byteorder);
+}
+
+static const struct expr_ops range_expr_ops = {
+       .type           = EXPR_RANGE,
+       .name           = "range",
+       .print          = range_expr_print,
+       .destroy        = range_expr_destroy,
+       .set_type       = range_expr_set_type,
+};
+
+struct expr *range_expr_alloc(const struct location *loc,
+                             struct expr *left, struct expr *right)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &range_expr_ops, &invalid_type,
+                         BYTEORDER_INVALID, 0);
+       expr->left  = left;
+       expr->right = right;
+       return expr;
+}
+
+static struct expr *compound_expr_alloc(const struct location *loc,
+                                       const struct expr_ops *ops)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, ops, &invalid_type, BYTEORDER_INVALID, 0);
+       init_list_head(&expr->expressions);
+       return expr;
+}
+
+static void compound_expr_destroy(struct expr *expr)
+{
+       struct expr *i, *next;
+
+       list_for_each_entry_safe(i, next, &expr->expressions, list)
+               expr_free(i);
+}
+
+static void compound_expr_print(const struct expr *expr, const char *delim)
+{
+       const struct expr *i;
+       const char *d = "";
+
+       list_for_each_entry(i, &expr->expressions, list) {
+               printf("%s", d);
+               expr_print(i);
+               d = delim;
+       }
+}
+
+void compound_expr_add(struct expr *compound, struct expr *expr)
+{
+       list_add_tail(&expr->list, &compound->expressions);
+       compound->size++;
+}
+
+void compound_expr_remove(struct expr *compound, struct expr *expr)
+{
+       compound->size--;
+       list_del(&expr->list);
+}
+
+static void concat_expr_print(const struct expr *expr)
+{
+       compound_expr_print(expr, " . ");
+}
+
+static const struct expr_ops concat_expr_ops = {
+       .type           = EXPR_CONCAT,
+       .name           = "concat",
+       .print          = concat_expr_print,
+       .destroy        = compound_expr_destroy,
+};
+
+struct expr *concat_expr_alloc(const struct location *loc)
+{
+       return compound_expr_alloc(loc, &concat_expr_ops);
+}
+
+static void list_expr_print(const struct expr *expr)
+{
+       compound_expr_print(expr, ",");
+}
+
+static const struct expr_ops list_expr_ops = {
+       .type           = EXPR_LIST,
+       .name           = "list",
+       .print          = list_expr_print,
+       .destroy        = compound_expr_destroy,
+};
+
+struct expr *list_expr_alloc(const struct location *loc)
+{
+       return compound_expr_alloc(loc, &list_expr_ops);
+}
+
+static void set_expr_print(const struct expr *expr)
+{
+       printf("{ ");
+       compound_expr_print(expr, ", ");
+       printf("}");
+}
+
+static void set_expr_set_type(const struct expr *expr,
+                             const struct datatype *dtype,
+                             enum byteorder byteorder)
+{
+       struct expr *i;
+
+       list_for_each_entry(i, &expr->expressions, list)
+               expr_set_type(i, dtype, byteorder);
+}
+
+static const struct expr_ops set_expr_ops = {
+       .type           = EXPR_SET,
+       .name           = "set",
+       .print          = set_expr_print,
+       .set_type       = set_expr_set_type,
+       .destroy        = compound_expr_destroy,
+};
+
+struct expr *set_expr_alloc(const struct location *loc)
+{
+       return compound_expr_alloc(loc, &set_expr_ops);
+}
+
+static void mapping_expr_print(const struct expr *expr)
+{
+       expr_print(expr->left);
+       printf(" => ");
+       expr_print(expr->right);
+}
+
+static void mapping_expr_set_type(const struct expr *expr,
+                                 const struct datatype *dtype,
+                                 enum byteorder byteorder)
+{
+       expr_set_type(expr->left, dtype, byteorder);
+}
+
+static void mapping_expr_destroy(struct expr *expr)
+{
+       expr_free(expr->left);
+       expr_free(expr->right);
+}
+
+static const struct expr_ops mapping_expr_ops = {
+       .type           = EXPR_MAPPING,
+       .name           = "mapping",
+       .print          = mapping_expr_print,
+       .set_type       = mapping_expr_set_type,
+       .destroy        = mapping_expr_destroy,
+};
+
+struct expr *mapping_expr_alloc(const struct location *loc,
+                               struct expr *from, struct expr *to)
+{
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &mapping_expr_ops, from->dtype,
+                         from->byteorder, 0);
+       expr->left  = from;
+       expr->right = to;
+       return expr;
+}
+
+static void map_expr_print(const struct expr *expr)
+{
+       expr_print(expr->expr);
+       printf(" map ");
+       expr_print(expr->mappings);
+}
+
+static void map_expr_destroy(struct expr *expr)
+{
+       expr_free(expr->expr);
+       expr_free(expr->mappings);
+}
+
+static const struct expr_ops map_expr_ops = {
+       .type           = EXPR_MAP,
+       .name           = "map",
+       .print          = map_expr_print,
+       .destroy        = map_expr_destroy,
+};
+
+struct expr *map_expr_alloc(const struct location *loc, struct expr *arg,
+                           struct expr *list)
+{
+       struct expr *expr;
+
+       assert(list->ops->type == EXPR_SET);
+       expr = expr_alloc(loc, &map_expr_ops, list->dtype,
+                         list->byteorder, list->len);
+       expr->expr     = arg;
+       expr->mappings = list;
+       return expr;
+}
diff --git a/src/exthdr.c b/src/exthdr.c
new file mode 100644 (file)
index 0000000..2defc7c
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Exthdr expression protocol and type definitions and related functions.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+
+#include <utils.h>
+#include <headers.h>
+#include <expression.h>
+
+static void exthdr_expr_print(const struct expr *expr)
+{
+       printf("%s %s", expr->exthdr.desc->name, expr->exthdr.tmpl->token);
+}
+
+static const struct expr_ops exthdr_expr_ops = {
+       .type           = EXPR_EXTHDR,
+       .name           = "exthdr",
+       .print          = exthdr_expr_print,
+};
+
+static const struct payload_template exthdr_unknown_template =
+       PAYLOAD_TEMPLATE("unknown", &invalid_type, 0, 0);
+
+struct expr *exthdr_expr_alloc(const struct location *loc,
+                              const struct exthdr_desc *desc,
+                              uint8_t type)
+{
+       const struct payload_template *tmpl;
+       struct expr *expr;
+
+       if (desc != NULL)
+               tmpl = &desc->templates[type];
+       else
+               tmpl = &exthdr_unknown_template;
+
+       expr = expr_alloc(loc, &exthdr_expr_ops, tmpl->dtype,
+                         BYTEORDER_BIG_ENDIAN, tmpl->len);
+       expr->exthdr.desc = desc;
+       expr->exthdr.tmpl = tmpl;
+       return expr;
+}
+
+static const struct exthdr_desc *exthdr_protocols[IPPROTO_MAX] = {
+       [IPPROTO_HOPOPTS]       = &exthdr_hbh,
+       [IPPROTO_ROUTING]       = &exthdr_rt,
+       [IPPROTO_FRAGMENT]      = &exthdr_frag,
+       [IPPROTO_DSTOPTS]       = &exthdr_dst,
+       [IPPROTO_MH]            = &exthdr_mh,
+};
+
+void exthdr_init_raw(struct expr *expr, uint8_t type,
+                    unsigned int offset, unsigned int len)
+{
+       const struct payload_template *tmpl;
+       unsigned int i;
+
+       assert(expr->ops->type == EXPR_EXTHDR);
+
+       expr->len = len;
+       expr->exthdr.desc = exthdr_protocols[type];
+       assert(expr->exthdr.desc != NULL);
+
+       for (i = 0; i < array_size(expr->exthdr.desc->templates); i++) {
+               tmpl = &expr->exthdr.desc->templates[i];
+               if (tmpl->offset != offset ||
+                   tmpl->len    != len)
+                       continue;
+               expr->dtype       = tmpl->dtype;
+               expr->exthdr.tmpl = tmpl;
+               return;
+       }
+}
+
+#define HDR_TEMPLATE(__name, __dtype, __type, __member)                        \
+       PAYLOAD_TEMPLATE(__name, __dtype,                               \
+                        offsetof(__type, __member) * 8,                \
+                        field_sizeof(__type, __member) * 8)
+
+/*
+ * Hop-by-hop options
+ */
+
+#define HBH_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_hbh, __member)
+
+const struct exthdr_desc exthdr_hbh = {
+       .name           = "hbh",
+       .type           = IPPROTO_HOPOPTS,
+       .templates      = {
+               [HBHHDR_NEXTHDR]        = HBH_FIELD("nexthdr", ip6h_nxt, &inet_protocol_type),
+               [HBHHDR_HDRLENGTH]      = HBH_FIELD("hdrlength", ip6h_len, &integer_type),
+       },
+};
+
+/*
+ * Routing header
+ */
+
+const struct exthdr_desc exthdr_rt2 = {
+       .templates      = {
+               [RT2HDR_RESERVED]       = {},
+               [RT2HDR_ADDR]           = {},
+       },
+};
+
+#define RT0_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_rthdr0, __member)
+
+const struct exthdr_desc exthdr_rt0 = {
+       .templates      = {
+               [RT0HDR_RESERVED]       = RT0_FIELD("reserved", ip6r0_reserved, &integer_type),
+               [RT0HDR_ADDR_1]         = RT0_FIELD("addr[1]", ip6r0_addr[0], &ip6addr_type),
+               [RT0HDR_ADDR_1 + 1]     = RT0_FIELD("addr[2]", ip6r0_addr[0], &ip6addr_type),
+               // ...
+       },
+};
+
+#define RT_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_rthdr, __member)
+
+const struct exthdr_desc exthdr_rt = {
+       .name           = "rt",
+       .type           = IPPROTO_ROUTING,
+#if 0
+       .protocol_key   = RTHDR_TYPE,
+       .protocols      = {
+               [0]     = &exthdr_rt0,
+               [2]     = &exthdr_rt2,
+       },
+#endif
+       .templates      = {
+               [RTHDR_NEXTHDR]         = RT_FIELD("nexthdr", ip6r_nxt, &inet_protocol_type),
+               [RTHDR_HDRLENGTH]       = RT_FIELD("hdrlength", ip6r_len, &integer_type),
+               [RTHDR_TYPE]            = RT_FIELD("type", ip6r_type, &integer_type),
+               [RTHDR_SEG_LEFT]        = RT_FIELD("seg-left", ip6r_segleft, &integer_type),
+       },
+};
+
+/*
+ * Fragment header
+ */
+
+#define FRAG_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_frag, __member)
+
+const struct exthdr_desc exthdr_frag = {
+       .name           = "frag",
+       .type           = IPPROTO_FRAGMENT,
+       .templates      = {
+               [FRAGHDR_NEXTHDR]       = FRAG_FIELD("nexthdr", ip6f_nxt, &inet_protocol_type),
+               [FRAGHDR_RESERVED]      = FRAG_FIELD("reserved", ip6f_reserved, &integer_type),
+               [FRAGHDR_FRAG_OFF]      = PAYLOAD_TEMPLATE("frag-off", &integer_type,
+                                                          16, 13),
+               [FRAGHDR_RESERVED2]     = PAYLOAD_TEMPLATE("reserved2", &integer_type,
+                                                          29, 2),
+               [FRAGHDR_MFRAGS]        = PAYLOAD_TEMPLATE("more-fragments", &integer_type,
+                                                          31, 1),
+               [FRAGHDR_ID]            = FRAG_FIELD("id", ip6f_ident, &integer_type),
+       },
+};
+
+/*
+ * DST options
+ */
+
+#define DST_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_dest, __member)
+
+const struct exthdr_desc exthdr_dst = {
+       .name           = "dst",
+       .type           = IPPROTO_DSTOPTS,
+       .templates      = {
+               [DSTHDR_NEXTHDR]        = DST_FIELD("nexthdr", ip6d_nxt, &inet_protocol_type),
+               [DSTHDR_HDRLENGTH]      = DST_FIELD("hdrlength", ip6d_len, &integer_type),
+       },
+};
+
+/*
+ * Mobility header
+ */
+
+#define MH_FIELD(__name, __member, __dtype) \
+       HDR_TEMPLATE(__name, __dtype, struct ip6_mh, __member)
+
+static const struct symbol_table mh_type_tbl = {
+       .byteorder      = BYTEORDER_BIG_ENDIAN,
+       .size           = BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("binding-refresh-request",       IP6_MH_TYPE_BRR),
+               SYMBOL("home-test-init",                IP6_MH_TYPE_HOTI),
+               SYMBOL("careof-test-init",              IP6_MH_TYPE_COTI),
+               SYMBOL("home-test",                     IP6_MH_TYPE_HOT),
+               SYMBOL("careof-test",                   IP6_MH_TYPE_COT),
+               SYMBOL("binding-update",                IP6_MH_TYPE_BU),
+               SYMBOL("binding-acknowledgement",       IP6_MH_TYPE_BACK),
+               SYMBOL("binding-error",                 IP6_MH_TYPE_BERROR),
+               SYMBOL("fast-binding-update",           IP6_MH_TYPE_FBU),
+               SYMBOL("fast-binding-acknowledgement",  IP6_MH_TYPE_FBACK),
+               SYMBOL("fast-binding-advertisement",    IP6_MH_TYPE_FNA),
+               SYMBOL("experimental-mobility-header",  IP6_MH_TYPE_EMH),
+               SYMBOL("home-agent-switch-message",     IP6_MH_TYPE_HASM),
+               SYMBOL_LIST_END
+       },
+};
+
+static const struct datatype mh_type_type = {
+       .type           = TYPE_MH_TYPE,
+       .name           = "Mobility Header Type",
+       .basetype       = &integer_type,
+       .sym_tbl        = &mh_type_tbl,
+};
+
+const struct exthdr_desc exthdr_mh = {
+       .name           = "mh",
+       .type           = IPPROTO_MH,
+       .templates      = {
+               [MHHDR_NEXTHDR]         = MH_FIELD("nexthdr", ip6mh_proto, &inet_protocol_type),
+               [MHHDR_HDRLENGTH]       = MH_FIELD("hdrlength", ip6mh_hdrlen, &integer_type),
+               [MHHDR_TYPE]            = MH_FIELD("type", ip6mh_type, &mh_type_type),
+               [MHHDR_RESERVED]        = MH_FIELD("reserved", ip6mh_reserved, &integer_type),
+               [MHHDR_CHECKSUM]        = MH_FIELD("checksum", ip6mh_cksum, &integer_type),
+       },
+};
diff --git a/src/gmputil.c b/src/gmputil.c
new file mode 100644 (file)
index 0000000..f34c077
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <gmp.h>
+
+#include <nftables.h>
+#include <datatype.h>
+#include <gmputil.h>
+#include <utils.h>
+
+void mpz_bitmask(mpz_t rop, unsigned int width)
+{
+       mpz_set_ui(rop, 0);
+       mpz_setbit(rop, width);
+       mpz_sub_ui(rop, rop, 1);
+}
+
+void mpz_init_bitmask(mpz_t rop, unsigned int width)
+{
+       mpz_init2(rop, width);
+       mpz_bitmask(rop, width);
+}
+
+void mpz_prefixmask(mpz_t rop, unsigned int width, unsigned int prefix_len)
+{
+       mpz_bitmask(rop, prefix_len);
+       mpz_lshift_ui(rop, width - prefix_len);
+}
+
+void mpz_lshift_ui(mpz_t rop, unsigned int n)
+{
+       mpz_mul_2exp(rop, rop, n);
+}
+
+void mpz_rshift_ui(mpz_t rop, unsigned int n)
+{
+       mpz_tdiv_q_2exp(rop, rop, n);
+}
+
+#define mpz_get_type(type, endian, op)                                 \
+({                                                                     \
+       type ret = 0;                                                   \
+       size_t cnt;                                                     \
+       mpz_export(&ret, &cnt, MPZ_LSWF, sizeof(ret), endian, 0, op);   \
+       assert(cnt <= 1);                                               \
+       ret;                                                            \
+ })
+
+uint64_t mpz_get_uint64(const mpz_t op)
+{
+       return mpz_get_type(uint64_t, MPZ_HOST_ENDIAN, op);
+}
+
+uint32_t mpz_get_uint32(const mpz_t op)
+{
+       return mpz_get_type(uint32_t, MPZ_HOST_ENDIAN, op);
+}
+
+uint16_t mpz_get_uint16(const mpz_t op)
+{
+       return mpz_get_type(uint16_t, MPZ_HOST_ENDIAN, op);
+}
+
+uint8_t mpz_get_uint8(const mpz_t op)
+{
+       return mpz_get_type(uint8_t, MPZ_HOST_ENDIAN, op);
+}
+
+uint64_t mpz_get_be64(const mpz_t op)
+{
+       return mpz_get_type(uint64_t, MPZ_BIG_ENDIAN, op);
+}
+
+uint32_t mpz_get_be32(const mpz_t op)
+{
+       return mpz_get_type(uint32_t, MPZ_BIG_ENDIAN, op);
+}
+
+uint16_t mpz_get_be16(const mpz_t op)
+{
+       return mpz_get_type(uint16_t, MPZ_BIG_ENDIAN, op);
+}
+
+void *mpz_export_data(void *data, const mpz_t op,
+                     enum byteorder byteorder,
+                     unsigned int len)
+{
+       enum mpz_byte_order endian;
+
+       switch (byteorder) {
+       case BYTEORDER_BIG_ENDIAN:
+       default:
+               endian = MPZ_BIG_ENDIAN;
+               break;
+       case BYTEORDER_HOST_ENDIAN:
+               endian = MPZ_HOST_ENDIAN;
+               break;
+       }
+
+       memset(data, 0, len);
+       mpz_export(data, NULL, MPZ_MSWF, len, endian, 0, op);
+       return data;
+}
+
+void mpz_import_data(mpz_t rop, const void *data,
+                    enum byteorder byteorder,
+                    unsigned int len)
+{
+       enum mpz_word_order order;
+       enum mpz_byte_order endian;
+
+       switch (byteorder) {
+       case BYTEORDER_BIG_ENDIAN:
+       default:
+               order  = MPZ_MSWF;
+               endian = MPZ_BIG_ENDIAN;
+               break;
+       case BYTEORDER_HOST_ENDIAN:
+               order  = MPZ_LSWF;
+               endian = MPZ_HOST_ENDIAN;
+               break;
+       }
+
+       mpz_import(rop, len, order, 1, endian, 0, data);
+}
+
+void mpz_switch_byteorder(mpz_t rop, unsigned int len)
+{
+       char data[len];
+
+       mpz_export_data(data, rop, BYTEORDER_BIG_ENDIAN, len);
+       mpz_import_data(rop, data, BYTEORDER_HOST_ENDIAN, len);
+}
+
+static void *gmp_xrealloc(void *ptr, size_t old_size, size_t new_size)
+{
+       return xrealloc(ptr, new_size);
+}
+
+static void __init gmp_init(void)
+{
+       mp_set_memory_functions(xmalloc, gmp_xrealloc, NULL);
+}
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..f7686ae
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <nftables.h>
+#include <utils.h>
+#include <parser.h>
+#include <rule.h>
+#include <netlink.h>
+#include <erec.h>
+
+unsigned int numeric_output;
+
+const char *include_paths[INCLUDE_PATHS_MAX] = { DEFAULT_INCLUDE_PATH };
+static unsigned int num_include_paths = 1;
+
+enum opt_vals {
+       OPT_HELP                = 'h',
+       OPT_VERSION             = 'v',
+       OPT_FILE                = 'f',
+       OPT_INTERACTIVE         = 'i',
+       OPT_INCLUDEPATH         = 'I',
+       OPT_NUMERIC             = 'n',
+       OPT_INVALID             = '?',
+};
+
+#define OPTSTRING      "hvf:iI:vn"
+
+static const struct option options[] = {
+       {
+               .name           = "help",
+               .val            = OPT_HELP,
+       },
+       {
+               .name           = "version",
+               .val            = OPT_VERSION,
+       },
+       {
+               .name           = "file",
+               .val            = OPT_FILE,
+               .has_arg        = 1,
+       },
+       {
+               .name           = "interactive",
+               .val            = OPT_INTERACTIVE,
+       },
+       {
+               .name           = "numeric",
+               .val            = OPT_NUMERIC,
+       },
+       {
+               .name           = "includepath",
+               .val            = OPT_INCLUDEPATH,
+               .has_arg        = 1,
+       },
+       {
+               .name           = NULL
+       }
+};
+
+static void show_help(const char *name)
+{
+       printf(
+"Usage: %s [ options ] [ cmds... ]\n"
+"\n"
+"Options:\n"
+"  -h/--help                   Show this help\n"
+"  -v/--version                        Show version information\n"
+"\n"
+"  -f/--file <filename>                Read input from <filename>\n"
+"  -i/--interactive            Read input from interactive CLI\n"
+"\n"
+"  -n/--numeric                        When specified once, show network addresses numerically.\n"
+"                              When specified twice, also show Internet protocols,\n"
+"                              Internet services, user IDs and group IDs numerically.\n"
+"  -i/--includepath <directory>        Add <directory> to the paths searched for include files.\n"
+"\n",
+       name);
+}
+
+static const struct input_descriptor indesc_cmdline = {
+       .type   = INDESC_BUFFER,
+       .name   = "<cmdline>",
+};
+
+int main(int argc, char * const *argv)
+{
+       struct parser_state state;
+       struct eval_ctx ctx;
+       void *scanner;
+       LIST_HEAD(msgs);
+       char *buf = NULL, *filename = NULL;
+       unsigned int len;
+       bool interactive = false;
+       int i, val;
+       int ret;
+
+       while (1) {
+               val = getopt_long(argc, argv, OPTSTRING, options, NULL);
+               if (val == -1)
+                       break;
+
+               switch (val) {
+               case OPT_HELP:
+                       show_help(argv[0]);
+                       exit(NFT_EXIT_SUCCESS);
+               case OPT_VERSION:
+                       printf("%s v%s (%s)\n",
+                              PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME);
+                       exit(NFT_EXIT_SUCCESS);
+               case OPT_FILE:
+                       filename = optarg;
+                       break;
+               case OPT_INTERACTIVE:
+                       interactive = true;
+                       break;
+               case OPT_INCLUDEPATH:
+                       if (num_include_paths >= INCLUDE_PATHS_MAX) {
+                               fprintf(stderr, "Too many include paths "
+                                               "specified, max. %u\n",
+                                       INCLUDE_PATHS_MAX - 1);
+                               exit(NFT_EXIT_FAILURE);
+                       }
+                       include_paths[num_include_paths++] = optarg;
+                       break;
+               case OPT_NUMERIC:
+                       numeric_output++;
+                       break;
+               case OPT_INVALID:
+                       exit(NFT_EXIT_FAILURE);
+               }
+       }
+
+       parser_init(&state, &msgs);
+       scanner = scanner_init(&state);
+
+       if (optind != argc) {
+               for (len = 0, i = optind; i < argc; i++)
+                       len += strlen(argv[i]) + strlen(" ");
+
+               buf = xzalloc(len + 1);
+               for (i = optind; i < argc; i++) {
+                       strcat(buf, argv[i]);
+                       if (i + 1 < argc)
+                               strcat(buf, " ");
+               }
+
+               scanner_push_buffer(scanner, &indesc_cmdline, buf);
+       } else if (filename != NULL) {
+               if (scanner_read_file(scanner, filename, &internal_location) < 0)
+                       goto out;
+       } else if (interactive) {
+               cli_init(scanner, &state);
+       } else {
+               fprintf(stderr, "%s: no command specified\n", argv[0]);
+               exit(NFT_EXIT_FAILURE);
+       }
+
+       ret = nft_parse(scanner, &state);
+       if (ret < 0)
+               goto out;
+
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.msgs = &msgs;
+       if (evaluate(&ctx, &state.cmds) < 0)
+               goto out;
+
+       {
+               struct netlink_ctx ctx;
+               struct cmd *cmd;
+
+               list_for_each_entry(cmd, &state.cmds, list) {
+                       memset(&ctx, 0, sizeof(ctx));
+                       ctx.msgs = &msgs;
+                       init_list_head(&ctx.list);
+                       if (do_command(&ctx, cmd) < 0)
+                               goto out;
+               }
+       }
+out:
+       scanner_destroy(scanner);
+       erec_print_list(stdout, &msgs);
+
+       xfree(buf);
+       return 0;
+}
diff --git a/src/meta.c b/src/meta.c
new file mode 100644 (file)
index 0000000..cb9e495
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Meta expression/statement related definition and types.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <net/if_arp.h>
+#include <pwd.h>
+#include <grp.h>
+#include <netlink/route/link.h>
+#include <netlink/route/tc.h>
+
+#include <nftables.h>
+#include <expression.h>
+#include <statement.h>
+#include <datatype.h>
+#include <meta.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <erec.h>
+
+static struct symbol_table *realm_tbl;
+static void __init realm_table_init(void)
+{
+       realm_tbl = rt_symbol_table_init("/etc/iproute2/rt_realms");
+}
+
+static void __exit realm_table_exit(void)
+{
+       rt_symbol_table_free(realm_tbl);
+}
+
+static void realm_type_print(const struct expr *expr)
+{
+       return symbolic_constant_print(realm_tbl, expr);
+}
+
+static struct error_record *realm_type_parse(const struct expr *sym,
+                                            struct expr **res)
+{
+       return symbolic_constant_parse(sym, realm_tbl, res);
+}
+
+static const struct datatype realm_type = {
+       .type           = TYPE_REALM,
+       .name           = "routing realm",
+       .basetype       = &integer_type,
+       .print          = realm_type_print,
+       .parse          = realm_type_parse,
+};
+
+static void tchandle_type_print(const struct expr *expr)
+{
+       char buf[sizeof("ffff:ffff")];
+
+       printf("%s", rtnl_tc_handle2str(mpz_get_uint32(expr->value),
+                                       buf, sizeof(buf)));
+}
+
+static struct error_record *tchandle_type_parse(const struct expr *sym,
+                                               struct expr **res)
+{
+       uint32_t handle;
+
+       if (rtnl_tc_str2handle(sym->identifier, &handle) < 0)
+               return error(&sym->location, "Could not parse %s",
+                            sym->sym_type->name);
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  BYTEORDER_HOST_ENDIAN,
+                                  sizeof(handle) * BITS_PER_BYTE, &handle);
+       return NULL;
+}
+
+static const struct datatype tchandle_type = {
+       .type           = TYPE_TC_HANDLE,
+       .name           = "TC handle",
+       .basetype       = &integer_type,
+       .print          = tchandle_type_print,
+       .parse          = tchandle_type_parse,
+};
+
+static struct nl_cache *link_cache;
+
+static int link_cache_init(void)
+{
+       struct nl_sock *rt_sock;
+       int err;
+
+       rt_sock = nl_socket_alloc();
+       if (rt_sock == NULL)
+               memory_allocation_error();
+
+       err = nl_connect(rt_sock, NETLINK_ROUTE);
+       if (err < 0)
+               goto err;
+       err = rtnl_link_alloc_cache(rt_sock, &link_cache);
+       if (err < 0)
+               goto err;
+       nl_cache_mngt_provide(link_cache);
+       nl_socket_free(rt_sock);
+       return 0;
+
+err:
+       nl_socket_free(rt_sock);
+       return err;
+}
+
+static void ifindex_type_print(const struct expr *expr)
+{
+       char name[IFNAMSIZ];
+       int ifindex;
+
+       if (link_cache == NULL)
+               link_cache_init();
+
+       ifindex = mpz_get_uint32(expr->value);
+       if (link_cache != NULL &&
+           rtnl_link_i2name(link_cache, ifindex, name, sizeof(name)))
+               printf("%s", name);
+       else
+               printf("%d", ifindex);
+}
+
+static struct error_record *ifindex_type_parse(const struct expr *sym,
+                                              struct expr **res)
+{
+       int ifindex, err;
+
+       if (link_cache == NULL &&
+           (err = link_cache_init()) < 0)
+               return error(&sym->location,
+                            "Could not initialize link cache: %s",
+                            nl_geterror(err));
+
+       ifindex = rtnl_link_name2i(link_cache, sym->identifier);
+       if (ifindex == 0)
+               return error(&sym->location, "Interface does not exist");
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  BYTEORDER_HOST_ENDIAN,
+                                  sizeof(ifindex) * BITS_PER_BYTE, &ifindex);
+       return NULL;
+}
+
+static void __exit ifindex_table_free(void)
+{
+       nl_cache_free(link_cache);
+}
+
+static const struct datatype ifindex_type = {
+       .type           = TYPE_IFINDEX,
+       .name           = "interface index",
+       .basetype       = &integer_type,
+       .print          = ifindex_type_print,
+       .parse          = ifindex_type_parse,
+};
+
+static const struct symbol_table arphrd_tbl = {
+       .byteorder      = BYTEORDER_HOST_ENDIAN,
+       .size           = 2 * BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("ether",         ARPHRD_ETHER),
+               SYMBOL("ppp",           ARPHRD_PPP),
+               /* dummy types */
+               SYMBOL("ipip",          ARPHRD_TUNNEL),
+               SYMBOL("ipip6",         ARPHRD_TUNNEL6),
+               SYMBOL("loopback",      ARPHRD_LOOPBACK),
+               SYMBOL("sit",           ARPHRD_SIT),
+               SYMBOL("ipgre",         ARPHRD_IPGRE),
+               SYMBOL_LIST_END,
+       },
+};
+
+const struct datatype arphrd_type = {
+       .type           = TYPE_ARPHRD,
+       .name           = "hardware type",
+       .basetype       = &integer_type,
+       .sym_tbl        = &arphrd_tbl,
+};
+
+static void uid_type_print(const struct expr *expr)
+{
+       struct passwd *pw;
+
+       if (numeric_output < NUMERIC_ALL) {
+               pw = getpwuid(mpz_get_uint32(expr->value));
+               if (pw != NULL) {
+                       printf("%s", pw->pw_name);
+                       return;
+               }
+       }
+       expr_basetype(expr)->print(expr);
+}
+
+static struct error_record *uid_type_parse(const struct expr *sym,
+                                          struct expr **res)
+{
+       struct passwd *pw;
+
+       pw = getpwnam(sym->identifier);
+       if (pw == NULL)
+               return error(&sym->location, "User does not exist");
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  BYTEORDER_HOST_ENDIAN,
+                                  sizeof(pw->pw_uid) * BITS_PER_BYTE,
+                                  &pw->pw_uid);
+       return NULL;
+}
+
+static const struct datatype uid_type = {
+       .type           = TYPE_UID,
+       .name           = "user ID",
+       .basetype       = &integer_type,
+       .print          = uid_type_print,
+       .parse          = uid_type_parse,
+};
+
+static void gid_type_print(const struct expr *expr)
+{
+       struct group *gr;
+
+       if (numeric_output < NUMERIC_ALL) {
+               gr = getgrgid(mpz_get_uint32(expr->value));
+               if (gr != NULL) {
+                       printf("%s", gr->gr_name);
+                       return;
+               }
+       }
+       expr_basetype(expr)->print(expr);
+}
+
+static struct error_record *gid_type_parse(const struct expr *sym,
+                                          struct expr **res)
+{
+       struct group *gr;
+
+       gr = getgrnam(sym->identifier);
+       if (gr == NULL)
+               return error(&sym->location, "Group does not exist");
+
+       *res = constant_expr_alloc(&sym->location, sym->sym_type,
+                                  BYTEORDER_HOST_ENDIAN,
+                                  sizeof(gr->gr_gid) * BITS_PER_BYTE,
+                                  &gr->gr_gid);
+       return NULL;
+}
+
+static const struct datatype gid_type = {
+       .type           = TYPE_GID,
+       .name           = "group ID",
+       .basetype       = &integer_type,
+       .print          = gid_type_print,
+       .parse          = gid_type_parse,
+};
+
+static const struct meta_template meta_templates[] = {
+       [NFT_META_LEN]          = META_TEMPLATE("len",       &integer_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_PROTOCOL]     = META_TEMPLATE("protocol",  &ethertype_type,
+                                               2 * 8, BYTEORDER_BIG_ENDIAN),
+       [NFT_META_PRIORITY]     = META_TEMPLATE("priority",  &tchandle_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_MARK]         = META_TEMPLATE("mark",      &mark_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_IIF]          = META_TEMPLATE("iif",       &ifindex_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_IIFNAME]      = META_TEMPLATE("iifname",   &string_type,
+                                               IFNAMSIZ * BITS_PER_BYTE,
+                                               BYTEORDER_INVALID),
+       [NFT_META_IIFTYPE]      = META_TEMPLATE("iiftype",   &arphrd_type,
+                                               2 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_OIF]          = META_TEMPLATE("oif",       &ifindex_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_OIFNAME]      = META_TEMPLATE("oifname",   &string_type,
+                                               IFNAMSIZ * BITS_PER_BYTE,
+                                               BYTEORDER_INVALID),
+       [NFT_META_OIFTYPE]      = META_TEMPLATE("oiftype",   &arphrd_type,
+                                               2 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_SKUID]        = META_TEMPLATE("skuid",     &uid_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_SKGID]        = META_TEMPLATE("skgid",     &gid_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_NFTRACE]      = META_TEMPLATE("nftrace",   &integer_type,
+                                               1    , BYTEORDER_HOST_ENDIAN),
+       [NFT_META_RTCLASSID]    = META_TEMPLATE("rtclassid", &realm_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+       [NFT_META_SECMARK]      = META_TEMPLATE("secmark",   &integer_type,
+                                               4 * 8, BYTEORDER_HOST_ENDIAN),
+};
+
+static void meta_expr_print(const struct expr *expr)
+{
+       printf("meta %s", meta_templates[expr->meta.key].token);
+}
+
+static const struct expr_ops meta_expr_ops = {
+       .type           = EXPR_META,
+       .name           = "meta",
+       .print          = meta_expr_print,
+};
+
+struct expr *meta_expr_alloc(const struct location *loc, enum nft_meta_keys key)
+{
+       const struct meta_template *tmpl = &meta_templates[key];
+       struct expr *expr;
+
+       expr = expr_alloc(loc, &meta_expr_ops, tmpl->dtype,
+                         tmpl->byteorder, tmpl->len);
+       expr->meta.key = key;
+       return expr;
+}
+
+static void meta_stmt_print(const struct stmt *stmt)
+{
+       printf("meta %s set ", meta_templates[stmt->meta.key].token);
+       expr_print(stmt->meta.expr);
+}
+
+static const struct stmt_ops meta_stmt_ops = {
+       .type           = STMT_META,
+       .name           = "meta",
+       .print          = meta_stmt_print,
+};
+
+struct stmt *meta_stmt_alloc(const struct location *loc, enum nft_meta_keys key,
+                            struct expr *expr)
+{
+       struct stmt *stmt;
+
+       stmt = stmt_alloc(loc, &meta_stmt_ops);
+       stmt->meta.key  = key;
+       stmt->meta.tmpl = &meta_templates[key];
+       stmt->meta.expr = expr;
+       return stmt;
+}
diff --git a/src/netlink.c b/src/netlink.c
new file mode 100644 (file)
index 0000000..4700cd7
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/netfilter.h>
+#include <netlink/netfilter/nft_table.h>
+#include <netlink/netfilter/nft_chain.h>
+#include <netlink/netfilter/nft_rule.h>
+#include <netlink/netfilter/nft_expr.h>
+#include <netlink/netfilter/nft_data.h>
+#include <linux/netfilter/nf_tables.h>
+
+#include <nftables.h>
+#include <netlink.h>
+#include <expression.h>
+#include <utils.h>
+#include <erec.h>
+
+#define TRACE  0
+
+static struct nl_sock *nf_sock;
+
+static void __init netlink_open_sock(void)
+{
+       // FIXME: should be done dynamically by nft_set and based on set members
+       nlmsg_set_default_size(65536);
+       nf_sock = nl_socket_alloc();
+       nfnl_connect(nf_sock);
+}
+
+static void __exit netlink_close_sock(void)
+{
+       nl_socket_free(nf_sock);
+}
+
+void netlink_dump_object(struct nl_object *obj)
+{
+       struct nl_dump_params params = {
+               .dp_fd          = stdout,
+               .dp_type        = NL_DUMP_DETAILS,
+       };
+       nl_object_dump(obj, &params);
+}
+
+static int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc,
+                           const char *fmt, ...)
+{
+       struct error_record *erec;
+       va_list ap;
+
+       if (loc == NULL)
+               loc = &internal_location;
+
+       va_start(ap, fmt);
+       erec = erec_vcreate(EREC_ERROR, loc, fmt, ap);
+       va_end(ap);
+       erec_queue(erec, ctx->msgs);
+       return -1;
+}
+
+struct nfnl_nft_table *alloc_nft_table(const struct handle *h)
+{
+       struct nfnl_nft_table *nlt;
+
+       nlt = nfnl_nft_table_alloc();
+       if (nlt == NULL)
+               memory_allocation_error();
+       nfnl_nft_table_set_family(nlt, h->family);
+       nfnl_nft_table_set_name(nlt, h->table, strlen(h->table) + 1);
+       return nlt;
+}
+
+struct nfnl_nft_chain *alloc_nft_chain(const struct handle *h)
+{
+       struct nfnl_nft_chain *nlc;
+
+       nlc = nfnl_nft_chain_alloc();
+       if (nlc == NULL)
+               memory_allocation_error();
+       nfnl_nft_chain_set_family(nlc, h->family);
+       nfnl_nft_chain_set_table(nlc, h->table, strlen(h->table) + 1);
+       if (h->chain != NULL)
+               nfnl_nft_chain_set_name(nlc, h->chain, strlen(h->chain) + 1);
+       return nlc;
+}
+
+struct nfnl_nft_rule *alloc_nft_rule(const struct handle *h)
+{
+       struct nfnl_nft_rule *nlr;
+
+       nlr = nfnl_nft_rule_alloc();
+       if (nlr == NULL)
+               memory_allocation_error();
+       nfnl_nft_rule_set_family(nlr, h->family);
+       nfnl_nft_rule_set_table(nlr, h->table, strlen(h->table) + 1);
+       if (h->chain != NULL)
+               nfnl_nft_rule_set_chain(nlr, h->chain, strlen(h->chain) + 1);
+       if (h->handle)
+               nfnl_nft_rule_set_handle(nlr, h->handle);
+       return nlr;
+}
+
+struct nfnl_nft_expr *alloc_nft_expr(int (*init)(struct nfnl_nft_expr *))
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = nfnl_nft_expr_alloc();
+       if (nle == NULL || init(nle) != 0)
+               memory_allocation_error();
+       return nle;
+}
+
+struct nfnl_nft_data *alloc_nft_data(const void *data, unsigned int len)
+{
+       struct nfnl_nft_data *nfd;
+
+       assert(len > 0);
+       nfd = nfnl_nft_data_alloc(data, len);
+       if (nfd == NULL)
+               memory_allocation_error();
+       return nfd;
+}
+
+int netlink_add_rule(struct netlink_ctx *ctx, const struct handle *h,
+                    const struct rule *rule)
+{
+       struct nfnl_nft_rule *nlr;
+       int err;
+
+       nlr = alloc_nft_rule(&rule->handle);
+       err = netlink_linearize_rule(ctx, nlr, rule);
+       if (err == 0) {
+               err = nfnl_nft_rule_add(nf_sock, nlr, NLM_F_EXCL | NLM_F_APPEND);
+               if (err < 0)
+                       netlink_io_error(ctx, &rule->location,
+                                        "Could not add rule: %s", nl_geterror(err));
+       }
+       nfnl_nft_rule_put(nlr);
+       return err;
+}
+
+int netlink_delete_rule(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_rule *nlr;
+       int err;
+
+       nlr = alloc_nft_rule(h);
+       err = nfnl_nft_rule_delete(nf_sock, nlr, 0);
+       nfnl_nft_rule_put(nlr);
+
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not delete rule: %s",
+                                nl_geterror(err));
+       return err;
+}
+
+static void list_rule_cb(struct nl_object *obj, void *arg)
+{
+       const struct nfnl_nft_rule *nlr = (struct nfnl_nft_rule *)obj;
+       struct netlink_ctx *ctx = arg;
+       struct rule *rule;
+#if TRACE
+       printf("\n"); netlink_dump_object(obj); printf("\n");
+#endif
+       if (!nfnl_nft_rule_test_family(nlr) ||
+           !nfnl_nft_rule_test_table(nlr) ||
+           !nfnl_nft_rule_test_chain(nlr) ||
+           !nfnl_nft_rule_test_handle(nlr)) {
+               netlink_io_error(ctx, NULL, "Incomplete rule received");
+               return;
+       }
+
+       rule = netlink_delinearize_rule(ctx, obj);
+       list_add_tail(&rule->list, &ctx->list);
+}
+
+static int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nl_cache *rule_cache;
+       struct nfnl_nft_rule *nlr;
+       int err;
+
+       err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache);
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive rules from kernel: %s",
+                                       nl_geterror(err));
+
+       nlr = alloc_nft_rule(h);
+       nl_cache_foreach_filter(rule_cache, (struct nl_object *)nlr,
+                               list_rule_cb, ctx);
+       nfnl_nft_rule_put(nlr);
+       nl_cache_free(rule_cache);
+       return 0;
+}
+
+static int netlink_get_rule_cb(struct nl_msg *msg, void *arg)
+{
+       return nl_msg_parse(msg, list_rule_cb, arg);
+}
+
+int netlink_get_rule(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_rule *nlr;
+       int err;
+
+       nlr = alloc_nft_rule(h);
+       nfnl_nft_rule_query(nf_sock, nlr, 0);
+       nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM,
+                           netlink_get_rule_cb, ctx);
+       err = nl_recvmsgs_default(nf_sock);
+       nfnl_nft_rule_put(nlr);
+
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive rule from kernel: %s",
+                                       nl_geterror(err));
+       return err;
+}
+
+static void flush_rule_cb(struct nl_object *obj, void *arg)
+{
+       struct netlink_ctx *ctx = arg;
+       int err;
+
+       netlink_dump_object(obj);
+       err = nfnl_nft_rule_delete(nf_sock, (struct nfnl_nft_rule *)obj, 0);
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not delete rule: %s",
+                                nl_geterror(err));
+}
+
+static int netlink_flush_rules(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nl_cache *rule_cache;
+       struct nfnl_nft_rule *nlr;
+       int err;
+
+       err = nfnl_nft_rule_alloc_cache(nf_sock, &rule_cache);
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive rules from kernel: %s",
+                                       nl_geterror(err));
+
+       nlr = alloc_nft_rule(h);
+       nl_cache_foreach_filter(rule_cache, (struct nl_object *)nlr,
+                               flush_rule_cb, ctx);
+       nfnl_nft_rule_put(nlr);
+       nl_cache_free(rule_cache);
+       return 0;
+}
+
+int netlink_add_chain(struct netlink_ctx *ctx, const struct handle *h,
+                     const struct chain *chain)
+{
+       struct nfnl_nft_chain *nlc;
+       int err;
+
+       nlc = alloc_nft_chain(h);
+       if (chain != NULL && (chain->hooknum || chain->priority)) {
+               nfnl_nft_chain_set_hooknum(nlc, chain->hooknum);
+               nfnl_nft_chain_set_priority(nlc, chain->priority);
+       }
+       err = nfnl_nft_chain_add(nf_sock, nlc, NLM_F_EXCL);
+       nfnl_nft_chain_put(nlc);
+
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not add chain: %s",
+                                nl_geterror(err));
+       return err;
+}
+
+int netlink_delete_chain(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_chain *nlc;
+       int err;
+
+       nlc = alloc_nft_chain(h);
+       err = nfnl_nft_chain_delete(nf_sock, nlc, 0);
+       nfnl_nft_chain_put(nlc);
+
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not delete chain: %s",
+                                nl_geterror(err));
+       return err;
+}
+
+static void list_chain_cb(struct nl_object *obj, void *arg)
+{
+       struct nfnl_nft_chain *nlc = (struct nfnl_nft_chain *)obj;
+       struct netlink_ctx *ctx = arg;
+       struct chain *chain;
+#if TRACE
+       netlink_dump_object(obj);;
+#endif
+       if (!nfnl_nft_chain_test_family(nlc) ||
+           !nfnl_nft_chain_test_table(nlc) ||
+           !nfnl_nft_chain_test_name(nlc)) {
+               netlink_io_error(ctx, NULL, "Incomplete chain received");
+               return;
+       }
+
+       chain = chain_alloc(nfnl_nft_chain_get_name(nlc));
+       chain->handle.family = nfnl_nft_chain_get_family(nlc);
+       chain->handle.table  = xstrdup(nfnl_nft_chain_get_table(nlc));
+       chain->hooknum       = nfnl_nft_chain_get_hooknum(nlc);
+       chain->priority      = nfnl_nft_chain_get_priority(nlc);
+       list_add_tail(&chain->list, &ctx->list);
+}
+
+int netlink_list_chains(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nl_cache *chain_cache;
+       struct nfnl_nft_chain *nlc;
+       int err;
+
+       err = nfnl_nft_chain_alloc_cache(nf_sock, &chain_cache);
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive chains from kernel: %s",
+                                       nl_geterror(err));
+
+       nlc = alloc_nft_chain(h);
+       nl_cache_foreach_filter(chain_cache, (struct nl_object *)nlc,
+                               list_chain_cb, ctx);
+       nfnl_nft_chain_put(nlc);
+       nl_cache_free(chain_cache);
+       return 0;
+}
+
+static int netlink_get_chain_cb(struct nl_msg *msg, void *arg)
+{
+       return nl_msg_parse(msg, list_chain_cb, arg);
+}
+
+int netlink_get_chain(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_chain *nlc;
+       int err;
+
+       nlc = alloc_nft_chain(h);
+       nfnl_nft_chain_query(nf_sock, nlc, 0);
+       nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM,
+                           netlink_get_chain_cb, ctx);
+       err = nl_recvmsgs_default(nf_sock);
+       nfnl_nft_chain_put(nlc);
+
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive chain from kernel: %s",
+                                       nl_geterror(err));
+       return err;
+}
+
+int netlink_list_chain(struct netlink_ctx *ctx, const struct handle *h)
+{
+       return netlink_list_rules(ctx, h);
+}
+
+int netlink_flush_chain(struct netlink_ctx *ctx, const struct handle *h)
+{
+       return netlink_flush_rules(ctx, h);
+}
+
+int netlink_add_table(struct netlink_ctx *ctx, const struct handle *h,
+                     const struct table *table)
+{
+       struct nfnl_nft_table *nlt;
+       int err;
+
+       nlt = alloc_nft_table(h);
+       err = nfnl_nft_table_add(nf_sock, nlt, NLM_F_EXCL);
+       nfnl_nft_table_put(nlt);
+
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not add table: %s",
+                                nl_geterror(err));
+       return err;
+}
+
+int netlink_delete_table(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_table *nlt;
+       int err;
+
+       nlt = alloc_nft_table(h);
+       err = nfnl_nft_table_delete(nf_sock, nlt, 0);
+       nfnl_nft_table_put(nlt);
+
+       if (err < 0)
+               netlink_io_error(ctx, NULL, "Could not delete table: %s",
+                                nl_geterror(err));
+       return err;
+}
+
+static void list_table_cb(struct nl_object *obj, void *arg)
+{
+       struct nfnl_nft_table *nlt = (struct nfnl_nft_table *)obj;
+       struct netlink_ctx *ctx = arg;
+       struct table *table;
+#if TRACE
+       netlink_dump_object(obj);
+#endif
+       if (!nfnl_nft_table_test_family(nlt) ||
+           !nfnl_nft_table_test_name(nlt)) {
+               netlink_io_error(ctx, NULL, "Incomplete table received");
+               return;
+       }
+
+       table = table_alloc();
+       table->handle.family = nfnl_nft_table_get_family(nlt);
+       table->handle.table  = xstrdup(nfnl_nft_table_get_name(nlt));
+       list_add_tail(&table->list, &ctx->list);
+}
+
+int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nl_cache *table_cache;
+       struct nfnl_nft_table *nlt;
+       int err;
+
+       err = nfnl_nft_table_alloc_cache(nf_sock, &table_cache);
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive tables from kernel: %s",
+                                       nl_geterror(err));
+
+       nlt = alloc_nft_table(h);
+       nl_cache_foreach_filter(table_cache, (struct nl_object *)nlt,
+                               list_table_cb, ctx);
+       nfnl_nft_table_put(nlt);
+       nl_cache_free(table_cache);
+       return 0;
+}
+
+static int netlink_get_table_cb(struct nl_msg *msg, void *arg)
+{
+       return nl_msg_parse(msg, list_table_cb, arg);
+}
+
+int netlink_get_table(struct netlink_ctx *ctx, const struct handle *h)
+{
+       struct nfnl_nft_table *nlt;
+       int err;
+
+       nlt = alloc_nft_table(h);
+       nfnl_nft_table_query(nf_sock, nlt, 0);
+       nl_socket_modify_cb(nf_sock, NL_CB_VALID, NL_CB_CUSTOM,
+                           netlink_get_table_cb, ctx);
+       err = nl_recvmsgs_default(nf_sock);
+       nfnl_nft_table_put(nlt);
+
+       if (err < 0)
+               return netlink_io_error(ctx, NULL,
+                                       "Could not receive table from kernel: %s",
+                                       nl_geterror(err));
+       return err;
+}
+
+
+int netlink_list_table(struct netlink_ctx *ctx, const struct handle *h)
+{
+       return netlink_list_rules(ctx, h);
+}
+
+int netlink_flush_table(struct netlink_ctx *ctx, const struct handle *h)
+{
+       return netlink_flush_rules(ctx, h);
+}
diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c
new file mode 100644 (file)
index 0000000..be2271c
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/netfilter/nf_tables.h>
+#include <netlink.h>
+#include <rule.h>
+#include <statement.h>
+#include <expression.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <erec.h>
+
+struct netlink_parse_ctx {
+       struct list_head        *msgs;
+       struct rule             *rule;
+       struct expr             *registers[NFT_REG_MAX + 1];
+};
+
+static void __fmtstring(3, 4) netlink_error(struct netlink_parse_ctx *ctx,
+                                           const struct location *loc,
+                                           const char *fmt, ...)
+{
+       struct error_record *erec;
+       va_list ap;
+
+       va_start(ap, fmt);
+       erec = erec_vcreate(EREC_ERROR, loc, fmt, ap);
+       va_end(ap);
+       erec_queue(erec, ctx->msgs);
+}
+
+static void netlink_set_register(struct netlink_parse_ctx *ctx,
+                                enum nft_registers reg,
+                                struct expr *expr)
+{
+       if (reg > NFT_REG_MAX) {
+               netlink_error(ctx, &expr->location,
+                             "Invalid destination register %u", reg);
+               expr_free(expr);
+               return;
+       }
+
+       ctx->registers[reg] = expr;
+}
+
+static struct expr *netlink_get_register(struct netlink_parse_ctx *ctx,
+                                        const struct location *loc,
+                                        enum nft_registers reg)
+{
+       struct expr *expr;
+
+       if (reg == NFT_REG_VERDICT || reg > NFT_REG_MAX) {
+               netlink_error(ctx, loc, "Invalid source register %u", reg);
+               return NULL;
+       }
+
+       expr = ctx->registers[reg];
+       ctx->registers[reg] = NULL;
+       return expr;
+}
+
+static struct expr *netlink_alloc_value(const struct location *loc,
+                                       const struct nfnl_nft_data *nld)
+{
+       return constant_expr_alloc(loc, &invalid_type, BYTEORDER_INVALID,
+                                  nfnl_nft_data_get_size(nld) * BITS_PER_BYTE,
+                                  nfnl_nft_data_get(nld));
+}
+
+static struct expr *netlink_alloc_verdict(const struct location *loc,
+                                         const struct nfnl_nft_data *nld)
+{
+       unsigned int code;
+       char *chain;
+
+       code = nfnl_nft_verdict_get_verdict(nld);
+       switch (code) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               chain = xstrdup(nfnl_nft_verdict_get_chain(nld));
+               break;
+       default:
+               chain = NULL;
+               break;
+       }
+
+       return verdict_expr_alloc(loc, code, chain);
+}
+
+static struct expr *netlink_alloc_data(const struct location *loc,
+                                      const struct nfnl_nft_data *nld,
+                                      enum nft_registers dreg)
+{
+       switch (dreg) {
+       case NFT_REG_VERDICT:
+               return netlink_alloc_verdict(loc, nld);
+       default:
+               return netlink_alloc_value(loc, nld);
+       }
+}
+
+static void netlink_parse_immediate(struct netlink_parse_ctx *ctx,
+                                   const struct location *loc,
+                                   const struct nfnl_nft_expr *nle)
+{
+       const struct nfnl_nft_data *data = nfnl_nft_immediate_get_data(nle);
+       enum nft_registers dreg = nfnl_nft_immediate_get_dreg(nle);
+       struct stmt *stmt;
+       struct expr *expr;
+
+       expr = netlink_alloc_data(loc, data, dreg);
+       if (dreg == NFT_REG_VERDICT) {
+               stmt = verdict_stmt_alloc(loc, expr);
+               list_add_tail(&stmt->list, &ctx->rule->stmts);
+       } else
+               netlink_set_register(ctx, dreg, expr);
+}
+
+static enum ops netlink_parse_cmp_op(const struct nfnl_nft_expr *nle)
+{
+       switch (nfnl_nft_cmp_get_op(nle)) {
+       case NFT_CMP_EQ:
+               return OP_EQ;
+       case NFT_CMP_NEQ:
+               return OP_NEQ;
+       case NFT_CMP_LT:
+               return OP_LT;
+       case NFT_CMP_LTE:
+               return OP_LTE;
+       case NFT_CMP_GT:
+               return OP_GT;
+       case NFT_CMP_GTE:
+               return OP_GTE;
+       default:
+               return OP_INVALID;
+       }
+}
+
+static void netlink_parse_cmp(struct netlink_parse_ctx *ctx,
+                             const struct location *loc,
+                             const struct nfnl_nft_expr *nle)
+{
+       const struct nfnl_nft_data *data = nfnl_nft_cmp_get_data(nle);
+       struct expr *expr, *left, *right;
+       struct stmt *stmt;
+       enum ops op;
+
+       left = netlink_get_register(ctx, loc, nfnl_nft_cmp_get_sreg(nle));
+       if (left == NULL)
+               return netlink_error(ctx, loc,
+                                    "Relational expression has no left "
+                                    "hand side");
+
+       op = netlink_parse_cmp_op(nle);
+       right = netlink_alloc_value(loc, data);
+
+       // FIXME
+       if (left->len && left->dtype && left->dtype->type != TYPE_STRING &&
+           left->len != right->len)
+               return netlink_error(ctx, loc,
+                                    "Relational expression size mismatch");
+
+       expr = relational_expr_alloc(loc, op, left, right);
+       stmt = expr_stmt_alloc(loc, expr);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+struct netlink_data_ctx {
+       const struct location   *location;
+       struct expr             *expr;
+       enum nft_registers      dreg;
+};
+
+static void netlink_data_ctx_init(struct netlink_data_ctx *dctx,
+                                 const struct location *loc,
+                                 struct expr *expr, enum nft_registers dreg)
+{
+       dctx->location  = loc;
+       dctx->expr      = expr;
+       dctx->dreg      = dreg;
+}
+
+static void netlink_set_parse_data(struct nfnl_nft_data *data,
+                                  enum nft_set_elem_flags flags,
+                                  void *arg)
+{
+       struct netlink_data_ctx *dctx = arg;
+       struct expr *expr;
+
+       assert(dctx->dreg != NFT_REG_VERDICT);
+       expr = netlink_alloc_value(dctx->location, data);
+       if (flags & NFT_SE_INTERVAL_END)
+               expr->flags |= EXPR_F_INTERVAL_END;
+       compound_expr_add(dctx->expr, expr);
+}
+
+static void netlink_set_parse_mapping(struct nfnl_nft_data *data,
+                                     struct nfnl_nft_data *mapping,
+                                     enum nft_set_elem_flags flags,
+                                     void *arg)
+{
+       struct netlink_data_ctx *dctx = arg;
+       struct expr *expr, *left, *right;
+
+       left  = netlink_alloc_value(dctx->location, data);
+       if (mapping != NULL) {
+               right = netlink_alloc_data(dctx->location, mapping, dctx->dreg);
+               expr  = mapping_expr_alloc(dctx->location, left, right);
+       } else
+               expr  = left;
+
+       if (flags & NFT_SE_INTERVAL_END)
+               expr->flags |= EXPR_F_INTERVAL_END;
+       compound_expr_add(dctx->expr, expr);
+}
+
+extern void interval_map_decompose(struct expr *set);
+
+static void netlink_parse_set(struct netlink_parse_ctx *ctx,
+                             const struct location *loc,
+                             const struct nfnl_nft_expr *nle)
+{
+       struct stmt *stmt;
+       struct expr *expr, *left, *right;
+       struct netlink_data_ctx dctx;
+       enum nft_registers dreg;
+       enum nft_set_flags flags;
+
+       left = netlink_get_register(ctx, loc, nfnl_nft_set_get_sreg(nle));
+       if (left == NULL)
+               return netlink_error(ctx, loc,
+                                    "Set expression has no left hand side");
+
+       right = set_expr_alloc(loc);
+
+       flags = nfnl_nft_set_get_flags(nle);
+       if (flags & NFT_SET_MAP) {
+               dreg = nfnl_nft_set_get_dreg(nle);
+               netlink_data_ctx_init(&dctx, loc, right, dreg);
+               nfnl_nft_set_foreach_mapping(nle, netlink_set_parse_mapping,
+                                            &dctx);
+
+               expr = map_expr_alloc(loc, left, right);
+               if (dreg != NFT_REG_VERDICT)
+                       return netlink_set_register(ctx, dreg, expr);
+       } else {
+               netlink_data_ctx_init(&dctx, loc, right, EXPR_VALUE);
+               nfnl_nft_set_foreach_elem(nle, netlink_set_parse_data, &dctx);
+               if (flags & NFT_SET_INTERVAL) {
+                       interval_map_decompose(right);
+                       right->flags |= NFT_SET_INTERVAL;
+               }
+               expr = relational_expr_alloc(loc, OP_LOOKUP, left, right);
+       }
+
+       stmt = expr_stmt_alloc(loc, expr);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx,
+                                 const struct location *loc,
+                                 const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr, *left, *mask, *xor, *or;
+       mpz_t m, x, o;
+
+       left = netlink_get_register(ctx, loc, nfnl_nft_bitwise_get_sreg(nle));
+       if (left == NULL)
+               return netlink_error(ctx, loc,
+                                    "Bitwise expression has no left "
+                                    "hand side");
+
+       expr = left;
+
+       mask = netlink_alloc_value(loc, nfnl_nft_bitwise_get_mask(nle));
+       mpz_init_set(m, mask->value);
+
+       xor  = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle));
+       mpz_init_set(x, xor->value);
+
+       mpz_init_set_ui(o, 0);
+       if (mpz_scan0(m, 0) != mask->len || mpz_cmp_ui(x, 0)) {
+               /* o = (m & x) ^ x */
+               mpz_and(o, m, x);
+               mpz_xor(o, o, x);
+               /* x &= m */
+               mpz_and(x, x, m);
+               /* m |= o */
+               mpz_ior(m, m, o);
+       }
+
+       if (mpz_scan0(m, 0) != left->len) {
+               mpz_set(mask->value, m);
+               expr = binop_expr_alloc(loc, OP_AND, expr, mask);
+               expr->len = left->len;
+       } else
+               expr_free(mask);
+
+       if (mpz_cmp_ui(x, 0)) {
+               mpz_set(xor->value, x);
+               expr = binop_expr_alloc(loc, OP_XOR, expr, xor);
+               expr->len = left->len;
+       } else
+               expr_free(xor);
+
+       if (mpz_cmp_ui(o, 0)) {
+               or = netlink_alloc_value(loc, nfnl_nft_bitwise_get_xor(nle));
+               mpz_set(or->value, o);
+               expr = binop_expr_alloc(loc, OP_OR, expr, or);
+               expr->len = left->len;
+       }
+
+       mpz_clear(m);
+       mpz_clear(x);
+       mpz_clear(o);
+
+       netlink_set_register(ctx, nfnl_nft_bitwise_get_dreg(nle), expr);
+}
+
+static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx,
+                                   const struct location *loc,
+                                   const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr, *arg;
+       enum ops op;
+
+       arg = netlink_get_register(ctx, loc, nfnl_nft_byteorder_get_sreg(nle));
+       if (arg == NULL)
+               return netlink_error(ctx, loc,
+                                    "Byteorder expression has no left "
+                                    "hand side");
+
+       switch (nfnl_nft_byteorder_get_op(nle)) {
+       case NFT_BYTEORDER_NTOH:
+               op = OP_NTOH;
+               break;
+       case NFT_BYTEORDER_HTON:
+               op = OP_HTON;
+               break;
+       default:
+               BUG();
+       }
+
+       expr = unary_expr_alloc(loc, op, arg);
+       expr->len = arg->len;
+       netlink_set_register(ctx, nfnl_nft_byteorder_get_dreg(nle), expr);
+}
+
+static void netlink_parse_payload(struct netlink_parse_ctx *ctx,
+                                 const struct location *loc,
+                                 const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr;
+
+       expr = payload_expr_alloc(loc, NULL, 0);
+       payload_init_raw(expr, nfnl_nft_payload_get_base(nle) + 1,
+                        nfnl_nft_payload_get_offset(nle) * BITS_PER_BYTE,
+                        nfnl_nft_payload_get_len(nle) * BITS_PER_BYTE);
+
+       netlink_set_register(ctx, nfnl_nft_payload_get_dreg(nle), expr);
+}
+
+static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx,
+                                const struct location *loc,
+                                const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr;
+
+       expr = exthdr_expr_alloc(loc, NULL, 0);
+       exthdr_init_raw(expr, nfnl_nft_exthdr_get_type(nle),
+                       nfnl_nft_exthdr_get_offset(nle) * BITS_PER_BYTE,
+                       nfnl_nft_exthdr_get_len(nle) * BITS_PER_BYTE);
+
+       netlink_set_register(ctx, nfnl_nft_exthdr_get_dreg(nle), expr);
+}
+
+static void netlink_parse_meta(struct netlink_parse_ctx *ctx,
+                              const struct location *loc,
+                              const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr;
+
+       expr = meta_expr_alloc(loc, nfnl_nft_meta_get_key(nle));
+       netlink_set_register(ctx, nfnl_nft_meta_get_dreg(nle), expr);
+}
+
+static void netlink_parse_ct(struct netlink_parse_ctx *ctx,
+                            const struct location *loc,
+                            const struct nfnl_nft_expr *nle)
+{
+       struct expr *expr;
+
+       expr = ct_expr_alloc(loc, nfnl_nft_ct_get_key(nle));
+       netlink_set_register(ctx, nfnl_nft_ct_get_dreg(nle), expr);
+}
+
+static void netlink_parse_counter(struct netlink_parse_ctx *ctx,
+                                 const struct location *loc,
+                                 const struct nfnl_nft_expr *nle)
+{
+       struct stmt *stmt;
+
+       stmt = counter_stmt_alloc(loc);
+       stmt->counter.packets = nfnl_nft_counter_get_packets(nle);
+       stmt->counter.bytes   = nfnl_nft_counter_get_bytes(nle);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static void netlink_parse_log(struct netlink_parse_ctx *ctx,
+                             const struct location *loc,
+                             const struct nfnl_nft_expr *nle)
+{
+       struct stmt *stmt;
+       const char *prefix;
+
+       stmt = log_stmt_alloc(loc);
+       prefix = nfnl_nft_log_get_prefix(nle);
+       if (prefix != NULL)
+               stmt->log.prefix = xstrdup(prefix);
+       stmt->log.group      = nfnl_nft_log_get_group(nle);
+       stmt->log.snaplen    = nfnl_nft_log_get_snaplen(nle);
+       stmt->log.qthreshold = nfnl_nft_log_get_qthreshold(nle);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static void netlink_parse_limit(struct netlink_parse_ctx *ctx,
+                               const struct location *loc,
+                               const struct nfnl_nft_expr *nle)
+{
+       struct stmt *stmt;
+
+       stmt = limit_stmt_alloc(loc);
+       stmt->limit.rate  = nfnl_nft_limit_get_rate(nle);
+       stmt->limit.depth = nfnl_nft_limit_get_depth(nle);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static void netlink_parse_reject(struct netlink_parse_ctx *ctx,
+                                const struct location *loc,
+                                const struct nfnl_nft_expr *expr)
+{
+       struct stmt *stmt;
+
+       stmt = reject_stmt_alloc(loc);
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static void netlink_parse_nat(struct netlink_parse_ctx *ctx,
+                             const struct location *loc,
+                             const struct nfnl_nft_expr *nle)
+{
+       struct stmt *stmt;
+       struct expr *addr, *proto;
+       enum nft_registers reg1, reg2;
+
+       stmt = nat_stmt_alloc(loc);
+       stmt->nat.type = nfnl_nft_nat_get_type(nle);
+
+       reg1 = nfnl_nft_nat_get_sreg_addr_min(nle);
+       if (reg1) {
+               addr = netlink_get_register(ctx, loc, reg1);
+               if (addr == NULL)
+                       return netlink_error(ctx, loc,
+                                            "NAT statement has no address "
+                                            "expression");
+
+               expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN);
+               stmt->nat.addr = addr;
+       }
+
+       reg2 = nfnl_nft_nat_get_sreg_addr_max(nle);
+       if (reg2 && reg2 != reg1) {
+               addr = netlink_get_register(ctx, loc, reg2);
+               if (addr == NULL)
+                       return netlink_error(ctx, loc,
+                                            "NAT statement has no address "
+                                            "expression");
+
+               expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN);
+               if (stmt->nat.addr != NULL)
+                       addr = range_expr_alloc(loc, stmt->nat.addr, addr);
+               stmt->nat.addr = addr;
+       }
+
+       reg1 = nfnl_nft_nat_get_sreg_proto_min(nle);
+       if (reg1) {
+               proto = netlink_get_register(ctx, loc, reg1);
+               if (proto == NULL)
+                       return netlink_error(ctx, loc,
+                                            "NAT statement has no proto "
+                                            "expression");
+
+               expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN);
+               stmt->nat.proto = proto;
+       }
+
+       reg2 = nfnl_nft_nat_get_sreg_proto_max(nle);
+       if (reg2 && reg2 != reg1) {
+               proto = netlink_get_register(ctx, loc, reg1);
+               if (proto == NULL)
+                       return netlink_error(ctx, loc,
+                                            "NAT statement has no proto "
+                                            "expression");
+
+               expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN);
+               stmt->nat.proto = proto;
+               if (stmt->nat.proto != NULL)
+                       proto = range_expr_alloc(loc, stmt->nat.proto, proto);
+               stmt->nat.proto = proto;
+       }
+
+       list_add_tail(&stmt->list, &ctx->rule->stmts);
+}
+
+static const struct {
+       const char      *name;
+       void            (*parse)(struct netlink_parse_ctx *ctx,
+                                const struct location *loc,
+                                const struct nfnl_nft_expr *nle);
+} netlink_parsers[] = {
+       { .name = "immediate",  .parse = netlink_parse_immediate },
+       { .name = "cmp",        .parse = netlink_parse_cmp },
+       { .name = "set",        .parse = netlink_parse_set },
+       { .name = "bitwise",    .parse = netlink_parse_bitwise },
+       { .name = "byteorder",  .parse = netlink_parse_byteorder },
+       { .name = "payload",    .parse = netlink_parse_payload },
+       { .name = "exthdr",     .parse = netlink_parse_exthdr },
+       { .name = "meta",       .parse = netlink_parse_meta },
+       { .name = "ct",         .parse = netlink_parse_ct },
+       { .name = "counter",    .parse = netlink_parse_counter },
+       { .name = "log",        .parse = netlink_parse_log },
+       { .name = "limit",      .parse = netlink_parse_limit },
+       { .name = "reject",     .parse = netlink_parse_reject },
+       { .name = "nat",        .parse = netlink_parse_nat },
+};
+
+static const struct input_descriptor indesc_netlink = {
+       .name = "netlink",
+       .type  = INDESC_NETLINK,
+};
+
+static void netlink_parse_expr(struct nl_object *obj, void *arg)
+{
+       const struct nfnl_nft_expr *nle = (struct nfnl_nft_expr *)obj;
+       const char *type = nfnl_nft_expr_get_type(nle);
+       struct netlink_parse_ctx *ctx = arg;
+       struct location loc;
+       unsigned int i;
+
+       memset(&loc, 0, sizeof(loc));
+       loc.indesc = &indesc_netlink;
+       loc.nl_obj = obj;
+
+       for (i = 0; i < array_size(netlink_parsers); i++) {
+               if (strcmp(type, netlink_parsers[i].name))
+                       continue;
+               return netlink_parsers[i].parse(ctx, &loc, nle);
+       }
+
+       netlink_error(ctx, &loc, "unknown expression type '%s'", type);
+}
+
+struct rule_pp_ctx {
+       struct payload_ctx      pctx;
+};
+
+static void payload_match_postprocess(struct payload_ctx *ctx,
+                                     struct stmt *stmt, struct expr *expr)
+{
+       struct expr *left = expr->left, *right = expr->right, *tmp;
+       struct list_head list = LIST_HEAD_INIT(list);
+       struct stmt *nstmt;
+       struct expr *nexpr;
+
+       switch (expr->op) {
+       case OP_EQ:
+       case OP_NEQ:
+               payload_expr_expand(&list, left, ctx);
+               list_for_each_entry(left, &list, list) {
+                       tmp = constant_expr_splice(right, left->len);
+                       expr_set_type(tmp, left->dtype, left->byteorder);
+                       nexpr = relational_expr_alloc(&expr->location, expr->op,
+                                                     left, tmp);
+                       payload_ctx_update(ctx, nexpr);
+
+                       nstmt = expr_stmt_alloc(&stmt->location, nexpr);
+                       list_add_tail(&nstmt->list, &stmt->list);
+               }
+               list_del(&stmt->list);
+               stmt_free(stmt);
+               break;
+       default:
+               payload_expr_complete(left, ctx);
+               expr_set_type(expr->right, expr->left->dtype,
+                             expr->left->byteorder);
+               break;
+       }
+}
+
+static void meta_match_postprocess(struct payload_ctx *ctx,
+                                  const struct expr *expr)
+{
+       switch (expr->op) {
+       case OP_EQ:
+               payload_ctx_update_meta(ctx, expr);
+               break;
+       default:
+               break;
+       }
+}
+
+static void expr_postprocess(struct rule_pp_ctx *ctx,
+                            struct stmt *stmt, struct expr **exprp)
+{
+       struct expr *expr = *exprp, *i;
+
+       //pr_debug("%s len %u\n", expr->ops->name, expr->len);
+
+       switch (expr->ops->type) {
+       case EXPR_MAP:
+               expr_postprocess(ctx, stmt, &expr->expr);
+               list_for_each_entry(i, &expr->mappings->expressions, list) {
+                       if (i->flags & EXPR_F_INTERVAL_END)
+                               continue;
+                       expr_set_type(i->left, expr->expr->dtype,
+                                     expr->expr->byteorder);
+               }
+               expr_postprocess(ctx, stmt, &expr->mappings);
+               break;
+       case EXPR_MAPPING:
+               expr_postprocess(ctx, stmt, &expr->left);
+               expr_postprocess(ctx, stmt, &expr->right);
+               break;
+       case EXPR_SET:
+               list_for_each_entry(i, &expr->expressions, list)
+                       expr_postprocess(ctx, stmt, &i);
+               break;
+       case EXPR_UNARY:
+               expr_postprocess(ctx, stmt, &expr->arg);
+               expr_set_type(expr->arg, expr->arg->dtype, !expr->arg->byteorder);
+
+               *exprp = expr_get(expr->arg);
+               expr_free(expr);
+               break;
+       case EXPR_BINOP:
+               expr_postprocess(ctx, stmt, &expr->left);
+               expr_postprocess(ctx, stmt, &expr->right);
+               expr_set_type(expr->right, expr->left->dtype,
+                             expr->left->byteorder);
+               expr_set_type(expr, expr->left->dtype,
+                             expr->left->byteorder);
+               break;
+       case EXPR_RELATIONAL:
+               switch (expr->left->ops->type) {
+               case EXPR_PAYLOAD:
+                       payload_match_postprocess(&ctx->pctx, stmt, expr);
+                       return;
+               case EXPR_META:
+                       meta_match_postprocess(&ctx->pctx, expr);
+                       break;
+               default:
+                       expr_postprocess(ctx, stmt, &expr->left);
+                       break;
+               }
+
+               expr_set_type(expr->right, expr->left->dtype, expr->left->byteorder);
+               expr_postprocess(ctx, stmt, &expr->right);
+
+               if (expr->left->ops->type == EXPR_BINOP &&
+                   expr->left->op == OP_AND &&
+                   expr->op == OP_NEQ &&
+                   expr->right->dtype->basetype->type == TYPE_BITMASK) {
+                       unsigned int n;
+
+                       expr_free(expr->right);
+                       expr->right = list_expr_alloc(&expr->left->left->location);
+                       n = 0;
+                       while ((n = mpz_scan1(expr->left->right->value, n + 1))) {
+                               if (n > expr->left->right->len)
+                                       break;
+                               i = constant_expr_alloc(&expr->left->right->location,
+                                                       expr->left->left->dtype,
+                                                       expr->left->right->byteorder,
+                                                       expr->left->right->len, NULL);
+                               mpz_set_ui(i->value, 1);
+                               mpz_lshift_ui(i->value, n);
+                               compound_expr_add(expr->right, i);
+                       }
+                       expr->left = expr->left->left;
+                       expr->op = OP_FLAGCMP;
+               }
+               break;
+       case EXPR_PAYLOAD:
+               payload_expr_complete(expr, &ctx->pctx);
+               break;
+       case EXPR_VALUE:
+               // FIXME
+               if (expr->byteorder == BYTEORDER_HOST_ENDIAN)
+                       mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE);
+
+               // Quite a hack :)
+               if (expr->dtype->type == TYPE_STRING) {
+                       unsigned int len = expr->len;
+                       mpz_t tmp;
+                       mpz_init(tmp);
+                       while (len >= BITS_PER_BYTE) {
+                               mpz_bitmask(tmp, BITS_PER_BYTE);
+                               mpz_lshift_ui(tmp, len - BITS_PER_BYTE);
+                               mpz_and(tmp, tmp, expr->value);
+                               if (mpz_cmp_ui(tmp, 0))
+                                       break;
+                               len -= BITS_PER_BYTE;
+                       }
+                       mpz_clear(tmp);
+                       expr->len = len;
+               }
+               break;
+       case EXPR_EXTHDR:
+       case EXPR_META:
+       case EXPR_CT:
+       case EXPR_VERDICT:
+               break;
+       default:
+               printf("%s\n", expr->ops->name);
+               BUG();
+       }
+}
+
+static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule)
+{
+       struct rule_pp_ctx rctx;
+       struct stmt *stmt, *next;
+
+       payload_ctx_init(&rctx.pctx, rule->handle.family);
+
+       list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+               switch (stmt->ops->type) {
+               case STMT_EXPRESSION:
+                       expr_postprocess(&rctx, stmt, &stmt->expr);
+                       break;
+               case STMT_NAT:
+                       if (stmt->nat.addr != NULL)
+                               expr_postprocess(&rctx, stmt, &stmt->nat.addr);
+                       if (stmt->nat.proto != NULL)
+                               expr_postprocess(&rctx, stmt, &stmt->nat.proto);
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx,
+                                     const struct nl_object *obj)
+{
+       const struct nfnl_nft_rule *nlr = (const struct nfnl_nft_rule *)obj;
+       struct netlink_parse_ctx _ctx, *pctx = &_ctx;
+       struct handle h;
+
+       memset(&_ctx, 0, sizeof(_ctx));
+       _ctx.msgs = ctx->msgs;
+
+       memset(&h, 0, sizeof(h));
+       h.family = nfnl_nft_rule_get_family(nlr);
+       h.table  = xstrdup(nfnl_nft_rule_get_table(nlr));
+       h.chain  = xstrdup(nfnl_nft_rule_get_chain(nlr));
+       h.handle = nfnl_nft_rule_get_handle(nlr);
+
+       pctx->rule = rule_alloc(&internal_location, &h);
+       nfnl_nft_rule_foreach_expr(nlr, netlink_parse_expr, pctx);
+
+       rule_parse_postprocess(pctx, pctx->rule);
+       return pctx->rule;
+}
diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c
new file mode 100644 (file)
index 0000000..fcb111c
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <linux/netfilter/nf_tables.h>
+
+#include <rule.h>
+#include <statement.h>
+#include <expression.h>
+#include <netlink.h>
+#include <gmputil.h>
+#include <utils.h>
+
+struct netlink_linearize_ctx {
+       struct nfnl_nft_rule    *nlr;
+       unsigned int            reg_low;
+};
+
+static enum nft_registers get_register(struct netlink_linearize_ctx *ctx)
+{
+       if (ctx->reg_low > NFT_REG_MAX)
+               BUG();
+       return ctx->reg_low++;
+}
+
+static void release_register(struct netlink_linearize_ctx *ctx)
+{
+       ctx->reg_low--;
+}
+
+static struct nfnl_nft_data *netlink_gen_mpz_data(const mpz_t value,
+                                                 enum byteorder byteorder,
+                                                 unsigned int len)
+{
+       unsigned char data[len];
+
+       mpz_export_data(data, value, byteorder, len);
+       return alloc_nft_data(data, len);
+}
+
+static struct nfnl_nft_data *netlink_gen_constant_data(const struct expr *expr)
+{
+       assert(expr->ops->type == EXPR_VALUE);
+       return netlink_gen_mpz_data(expr->value, expr->byteorder,
+                                   div_round_up(expr->len, BITS_PER_BYTE));
+}
+
+static struct nfnl_nft_data *netlink_gen_concat_data(const struct expr *expr)
+{
+       struct nfnl_nft_data *data;
+       const struct expr *i;
+       void *buf;
+       unsigned int len, offset;
+
+       len = 0;
+       list_for_each_entry(i, &expr->expressions, list)
+               len += i->len;
+
+       buf = xmalloc(len / BITS_PER_BYTE);
+
+       offset = 0;
+       list_for_each_entry(i, &expr->expressions, list) {
+               assert(i->ops->type == EXPR_VALUE);
+               mpz_export_data(buf + offset, i->value, i->byteorder,
+                               i->len / BITS_PER_BYTE);
+               offset += i->len / BITS_PER_BYTE;
+       }
+
+       data = alloc_nft_data(buf, len / BITS_PER_BYTE);
+       xfree(buf);
+       return data;
+}
+
+static struct nfnl_nft_data *netlink_gen_verdict(const struct expr *expr)
+{
+       struct nfnl_nft_data *verdict;
+
+       verdict = nfnl_nft_verdict_alloc();
+       nfnl_nft_verdict_set_verdict(verdict, expr->verdict);
+
+       switch (expr->verdict) {
+       case NFT_JUMP:
+       case NFT_GOTO:
+               nfnl_nft_verdict_set_chain(verdict, expr->chain);
+       }
+
+       return verdict;
+}
+
+static struct nfnl_nft_data *netlink_gen_data(const struct expr *expr)
+{
+       switch (expr->ops->type) {
+       case EXPR_VALUE:
+               return netlink_gen_constant_data(expr);
+       case EXPR_CONCAT:
+               return netlink_gen_concat_data(expr);
+       case EXPR_VERDICT:
+               return netlink_gen_verdict(expr);
+       default:
+               BUG();
+       }
+}
+
+static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
+                            const struct expr *expr,
+                            enum nft_registers dreg);
+
+static void netlink_gen_concat(struct netlink_linearize_ctx *ctx,
+                              const struct expr *expr,
+                              enum nft_registers dreg)
+{
+       const struct expr *i;
+
+       list_for_each_entry(i, &expr->expressions, list)
+               netlink_gen_expr(ctx, i, dreg);
+}
+
+static void netlink_gen_payload(struct netlink_linearize_ctx *ctx,
+                               const struct expr *expr,
+                               enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_payload_init);
+       nfnl_nft_payload_set_dreg(nle, dreg);
+       nfnl_nft_payload_set_base(nle, expr->payload.base - 1);
+       nfnl_nft_payload_set_offset(nle, expr->payload.offset / BITS_PER_BYTE);
+       nfnl_nft_payload_set_len(nle, expr->len / BITS_PER_BYTE);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx,
+                              const struct expr *expr,
+                              enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_exthdr_init);
+       nfnl_nft_exthdr_set_dreg(nle, dreg);
+       nfnl_nft_exthdr_set_type(nle, expr->exthdr.desc->type);
+       nfnl_nft_exthdr_set_offset(nle, expr->exthdr.tmpl->offset / BITS_PER_BYTE);
+       nfnl_nft_exthdr_set_len(nle, expr->len / BITS_PER_BYTE);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_meta(struct netlink_linearize_ctx *ctx,
+                            const struct expr *expr,
+                            enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_meta_init);
+       nfnl_nft_meta_set_dreg(nle, dreg);
+       nfnl_nft_meta_set_key(nle, expr->meta.key);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_ct(struct netlink_linearize_ctx *ctx,
+                          const struct expr *expr,
+                          enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_ct_init);
+       nfnl_nft_ct_set_dreg(nle, dreg);
+       nfnl_nft_ct_set_key(nle, expr->ct.key);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_map(struct netlink_linearize_ctx *ctx,
+                           const struct expr *expr,
+                           enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+       struct nfnl_nft_data *data;
+       struct nfnl_nft_data *mapping;
+       const struct expr *i;
+       enum nft_set_elem_flags flags;
+       enum nft_registers sreg;
+       unsigned int klen, dlen;
+
+       assert(expr->mappings->ops->type == EXPR_SET);
+
+       klen = expr->expr->len / BITS_PER_BYTE;
+       dlen = expr->mappings->len / BITS_PER_BYTE;
+       if (dreg == NFT_REG_VERDICT)
+               sreg = get_register(ctx);
+       else
+               sreg = dreg;
+
+       netlink_gen_expr(ctx, expr->expr, sreg);
+
+       nle = alloc_nft_expr(nfnl_nft_set_init);
+       nfnl_nft_set_set_flags(nle, NFT_SET_MAP);
+       nfnl_nft_set_set_sreg(nle, sreg);
+       nfnl_nft_set_set_klen(nle, klen);
+       nfnl_nft_set_set_dreg(nle, dreg);
+       nfnl_nft_set_set_dlen(nle, dlen);
+
+       if (expr->mappings->flags & SET_F_INTERVAL) {
+               set_to_intervals(expr->mappings);
+               nfnl_nft_set_set_flags(nle, NFT_SET_INTERVAL);
+       }
+
+       list_for_each_entry(i, &expr->mappings->expressions, list) {
+               flags = 0;
+
+               switch (i->ops->type) {
+               case EXPR_MAPPING:
+                       data    = netlink_gen_data(i->left);
+                       mapping = netlink_gen_data(i->right);
+                       break;
+               case EXPR_VALUE:
+                       assert(i->flags & EXPR_F_INTERVAL_END);
+                       data    = netlink_gen_data(i);
+                       mapping = NULL;
+                       flags   = NFT_SE_INTERVAL_END;
+                       break;
+               default:
+                       BUG();
+               }
+
+               nfnl_nft_set_add_mapping(nle, data, mapping, flags);
+       }
+
+       if (dreg == NFT_REG_VERDICT)
+               release_register(ctx);
+
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx,
+                              const struct expr *expr,
+                              enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+       const struct expr *i;
+       enum nft_set_elem_flags flags;
+       enum nft_registers sreg;
+
+       assert(expr->right->ops->type == EXPR_SET);
+       assert(dreg == NFT_REG_VERDICT);
+
+       sreg = get_register(ctx);
+       netlink_gen_expr(ctx, expr->left, sreg);
+
+       nle = alloc_nft_expr(nfnl_nft_set_init);
+       nfnl_nft_set_set_sreg(nle, sreg);
+       nfnl_nft_set_set_klen(nle, expr->left->len / BITS_PER_BYTE);
+
+       if (expr->right->flags & SET_F_INTERVAL) {
+               set_to_intervals(expr->right);
+               nfnl_nft_set_set_flags(nle, NFT_SET_INTERVAL);
+       }
+
+       list_for_each_entry(i, &expr->right->expressions, list) {
+               assert(i->ops->type == EXPR_VALUE);
+
+               flags = 0;
+               if (i->flags & EXPR_F_INTERVAL_END)
+                       flags = NFT_SE_INTERVAL_END;
+
+               nfnl_nft_set_add_elem(nle, netlink_gen_data(i), flags);
+       }
+
+       release_register(ctx);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op)
+{
+       switch (op) {
+       case OP_EQ:
+               return NFT_CMP_EQ;
+       case OP_NEQ:
+               return NFT_CMP_NEQ;
+       case OP_LT:
+               return NFT_CMP_LT;
+       case OP_GT:
+               return NFT_CMP_GT;
+       case OP_LTE:
+               return NFT_CMP_LTE;
+       case OP_GTE:
+               return NFT_CMP_GTE;
+       default:
+               BUG();
+       }
+}
+
+static void netlink_gen_cmp(struct netlink_linearize_ctx *ctx,
+                           const struct expr *expr,
+                           enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+       enum nft_registers sreg;
+
+       assert(dreg == NFT_REG_VERDICT);
+
+       sreg = get_register(ctx);
+       netlink_gen_expr(ctx, expr->left, sreg);
+
+       nle = alloc_nft_expr(nfnl_nft_cmp_init);
+       nfnl_nft_cmp_set_sreg(nle, sreg);
+       nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(expr->op));
+       nfnl_nft_cmp_set_data(nle, netlink_gen_data(expr->right));
+       release_register(ctx);
+
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_range(struct netlink_linearize_ctx *ctx,
+                             const struct expr *expr,
+                             enum nft_registers dreg)
+{
+       struct expr *range = expr->right;
+       struct nfnl_nft_expr *nle;
+       enum nft_registers sreg;
+
+       assert(dreg == NFT_REG_VERDICT);
+
+       sreg = get_register(ctx);
+       netlink_gen_expr(ctx, expr->left, sreg);
+
+       nle = alloc_nft_expr(nfnl_nft_cmp_init);
+       nfnl_nft_cmp_set_sreg(nle, sreg);
+       nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(OP_GTE));
+       nfnl_nft_cmp_set_data(nle, netlink_gen_data(range->left));
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+
+       nle = alloc_nft_expr(nfnl_nft_cmp_init);
+       nfnl_nft_cmp_set_sreg(nle, sreg);
+       nfnl_nft_cmp_set_op(nle, netlink_gen_cmp_op(OP_LTE));
+       nfnl_nft_cmp_set_data(nle, netlink_gen_data(range->right));
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+
+       release_register(ctx);
+}
+
+static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx,
+                               const struct expr *expr,
+                               enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+       struct nfnl_nft_data *nld;
+       enum nft_registers sreg;
+       unsigned int len;
+       mpz_t zero;
+
+       assert(dreg == NFT_REG_VERDICT);
+
+       sreg = get_register(ctx);
+       netlink_gen_expr(ctx, expr->left, sreg);
+       len = div_round_up(expr->left->len, BITS_PER_BYTE);
+
+       mpz_init_set_ui(zero, 0);
+
+       nle = alloc_nft_expr(nfnl_nft_bitwise_init);
+       nld = netlink_gen_mpz_data(zero, expr->right->byteorder, len);
+       nfnl_nft_bitwise_set_sreg(nle, sreg);
+       nfnl_nft_bitwise_set_dreg(nle, sreg);
+       nfnl_nft_bitwise_set_len(nle, len);
+       nfnl_nft_bitwise_set_mask(nle, netlink_gen_data(expr->right));
+       nfnl_nft_bitwise_set_xor(nle, nld);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+
+       nle = alloc_nft_expr(nfnl_nft_cmp_init);
+       nld = netlink_gen_mpz_data(zero, expr->right->byteorder, len);
+       nfnl_nft_cmp_set_sreg(nle, sreg);
+       nfnl_nft_cmp_set_op(nle, NFT_CMP_NEQ);
+       nfnl_nft_cmp_set_data(nle, nld);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+
+       mpz_clear(zero);
+       release_register(ctx);
+}
+
+static void netlink_gen_relational(struct netlink_linearize_ctx *ctx,
+                                  const struct expr *expr,
+                                  enum nft_registers dreg)
+{
+       switch (expr->op) {
+       case OP_EQ:
+       case OP_NEQ:
+       case OP_LT:
+       case OP_GT:
+       case OP_LTE:
+       case OP_GTE:
+               return netlink_gen_cmp(ctx, expr, dreg);
+       case OP_RANGE:
+               return netlink_gen_range(ctx, expr, dreg);
+       case OP_FLAGCMP:
+               return netlink_gen_flagcmp(ctx, expr, dreg);
+       case OP_LOOKUP:
+               return netlink_gen_lookup(ctx, expr, dreg);
+       default:
+               BUG();
+       }
+}
+
+static void combine_binop(mpz_t mask, mpz_t xor, const mpz_t m, const mpz_t x)
+{
+       /* xor = x ^ (xor & m) */
+       mpz_and(xor, xor, m);
+       mpz_xor(xor, x, xor);
+       /* mask &= m */
+       mpz_and(mask, mask, m);
+}
+
+static void netlink_gen_binop(struct netlink_linearize_ctx *ctx,
+                             const struct expr *expr,
+                             enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+       struct nfnl_nft_data *nld;
+       struct expr *left, *i;
+       struct expr *binops[16];
+       mpz_t mask, xor, val, tmp;
+       unsigned int len;
+       int n = 0;
+
+       mpz_init(mask);
+       mpz_init(xor);
+       mpz_init(val);
+       mpz_init(tmp);
+
+       binops[n++] = left = (void *)expr;
+       while (left->ops->type == EXPR_BINOP && left->left != NULL)
+               binops[n++] = left = left->left;
+       n--;
+
+       netlink_gen_expr(ctx, binops[n--], dreg);
+
+       mpz_bitmask(mask, expr->len);
+       mpz_set_ui(xor, 0);
+       for (; n >= 0; n--) {
+               i = binops[n];
+               mpz_set(val, i->right->value);
+
+               switch (i->op) {
+               case OP_AND:
+                       mpz_set_ui(tmp, 0);
+                       combine_binop(mask, xor, val, tmp);
+                       break;
+               case OP_OR:
+                       mpz_com(tmp, val);
+                       combine_binop(mask, xor, tmp, val);
+                       break;
+               case OP_XOR:
+                       mpz_bitmask(tmp, expr->len);
+                       combine_binop(mask, xor, tmp, val);
+                       break;
+               default:
+                       BUG();
+               }
+       }
+
+       len = div_round_up(expr->len, BITS_PER_BYTE);
+
+       nle = alloc_nft_expr(nfnl_nft_bitwise_init);
+       nfnl_nft_bitwise_set_sreg(nle, dreg);
+       nfnl_nft_bitwise_set_dreg(nle, dreg);
+       nfnl_nft_bitwise_set_len(nle, len);
+
+       nld = netlink_gen_mpz_data(mask, expr->byteorder, len);
+       nfnl_nft_bitwise_set_mask(nle, nld);
+
+       nld = netlink_gen_mpz_data(xor, expr->byteorder, len);
+       nfnl_nft_bitwise_set_xor(nle, nld);
+
+       mpz_clear(tmp);
+       mpz_clear(val);
+       mpz_clear(xor);
+       mpz_clear(mask);
+
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static enum nft_byteorder_ops netlink_gen_unary_op(enum ops op)
+{
+       switch (op) {
+       case OP_HTON:
+               return NFT_BYTEORDER_HTON;
+       case OP_NTOH:
+               return NFT_BYTEORDER_HTON;
+       default:
+               BUG();
+       }
+}
+
+static void netlink_gen_unary(struct netlink_linearize_ctx *ctx,
+                             const struct expr *expr,
+                             enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       netlink_gen_expr(ctx, expr->arg, dreg);
+
+       nle = alloc_nft_expr(nfnl_nft_byteorder_init);
+       nfnl_nft_byteorder_set_sreg(nle, dreg);
+       nfnl_nft_byteorder_set_dreg(nle, dreg);
+       printf("%u\n", expr->len);
+       nfnl_nft_byteorder_set_len(nle, expr->len / BITS_PER_BYTE);
+       printf("%u\n", expr->arg->len);
+       nfnl_nft_byteorder_set_size(nle, expr->arg->len % 32 ? 2 : 4);
+       nfnl_nft_byteorder_set_op(nle, netlink_gen_unary_op(expr->op));
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx,
+                                 const struct expr *expr,
+                                 enum nft_registers dreg)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_immediate_init);
+       nfnl_nft_immediate_set_dreg(nle, dreg);
+       nfnl_nft_immediate_set_data(nle, netlink_gen_data(expr));
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_expr(struct netlink_linearize_ctx *ctx,
+                            const struct expr *expr,
+                            enum nft_registers dreg)
+{
+       switch (expr->ops->type) {
+       case EXPR_VERDICT:
+       case EXPR_VALUE:
+               return netlink_gen_immediate(ctx, expr, dreg);
+       case EXPR_UNARY:
+               return netlink_gen_unary(ctx, expr, dreg);
+       case EXPR_BINOP:
+               return netlink_gen_binop(ctx, expr, dreg);
+       case EXPR_RELATIONAL:
+               return netlink_gen_relational(ctx, expr, dreg);
+       case EXPR_CONCAT:
+               return netlink_gen_concat(ctx, expr, dreg);
+       case EXPR_MAP:
+               return netlink_gen_map(ctx, expr, dreg);
+       case EXPR_PAYLOAD:
+               return netlink_gen_payload(ctx, expr, dreg);
+       case EXPR_EXTHDR:
+               return netlink_gen_exthdr(ctx, expr, dreg);
+       case EXPR_META:
+               return netlink_gen_meta(ctx, expr, dreg);
+       case EXPR_CT:
+               return netlink_gen_ct(ctx, expr, dreg);
+       default:
+               BUG();
+       }
+}
+
+static void netlink_gen_verdict_stmt(struct netlink_linearize_ctx *ctx,
+                                    const struct stmt *stmt)
+{
+       return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
+}
+
+static void netlink_gen_counter_stmt(struct netlink_linearize_ctx *ctx,
+                                    const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_counter_init);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx,
+                                 const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+       enum nft_registers sreg;
+
+       sreg = get_register(ctx);
+       netlink_gen_expr(ctx, stmt->meta.expr, sreg);
+       release_register(ctx);
+
+       nle = alloc_nft_expr(nfnl_nft_meta_init);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx,
+                                const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_log_init);
+       if (stmt->log.prefix != NULL)
+               nfnl_nft_log_set_prefix(nle, stmt->log.prefix);
+       if (stmt->log.group)
+               nfnl_nft_log_set_group(nle, stmt->log.group);
+       if (stmt->log.snaplen)
+               nfnl_nft_log_set_snaplen(nle, stmt->log.snaplen);
+       if (stmt->log.qthreshold)
+               nfnl_nft_log_set_qthreshold(nle, stmt->log.qthreshold);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_limit_stmt(struct netlink_linearize_ctx *ctx,
+                                  const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(nfnl_nft_limit_init);
+       nfnl_nft_limit_set_rate(nle, stmt->limit.rate);
+       nfnl_nft_limit_set_depth(nle, stmt->limit.depth);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx,
+                                   const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+
+       nle = alloc_nft_expr(NULL);
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx,
+                                const struct stmt *stmt)
+{
+       struct nfnl_nft_expr *nle;
+       enum nft_registers amin_reg, amax_reg;
+       enum nft_registers pmin_reg, pmax_reg;
+
+       nle = alloc_nft_expr(nfnl_nft_nat_init);
+       nfnl_nft_nat_set_type(nle, stmt->nat.type);
+
+       if (stmt->nat.addr) {
+               switch (stmt->nat.addr->ops->type) {
+               default:
+                       amin_reg = amax_reg = get_register(ctx);
+                       netlink_gen_expr(ctx, stmt->nat.addr, amin_reg);
+                       nfnl_nft_nat_set_sreg_addr_min(nle, amin_reg);
+                       release_register(ctx);
+                       break;
+               case EXPR_RANGE:
+                       amin_reg = get_register(ctx);
+                       amax_reg = get_register(ctx);
+                       netlink_gen_expr(ctx, stmt->nat.addr->left, amin_reg);
+                       netlink_gen_expr(ctx, stmt->nat.addr->right, amax_reg);
+                       nfnl_nft_nat_set_sreg_addr_min(nle, amin_reg);
+                       nfnl_nft_nat_set_sreg_addr_max(nle, amax_reg);
+                       release_register(ctx);
+                       release_register(ctx);
+                       break;
+               }
+
+       }
+
+       if (stmt->nat.proto) {
+               switch (stmt->nat.proto->ops->type) {
+               default:
+                       pmin_reg = pmax_reg = get_register(ctx);
+                       netlink_gen_expr(ctx, stmt->nat.proto, pmin_reg);
+                       nfnl_nft_nat_set_sreg_proto_min(nle, pmin_reg);
+                       release_register(ctx);
+                       break;
+               case EXPR_RANGE:
+                       pmin_reg = get_register(ctx);
+                       pmax_reg = get_register(ctx);
+                       netlink_gen_expr(ctx, stmt->nat.proto->left, pmin_reg);
+                       netlink_gen_expr(ctx, stmt->nat.proto->right, pmax_reg);
+                       nfnl_nft_nat_set_sreg_proto_min(nle, pmin_reg);
+                       nfnl_nft_nat_set_sreg_proto_max(nle, pmax_reg);
+                       release_register(ctx);
+                       release_register(ctx);
+                       break;
+               }
+       }
+
+       nfnl_nft_rule_add_expr(ctx->nlr, nle);
+}
+
+static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx,
+                            const struct stmt *stmt)
+{
+       switch (stmt->ops->type) {
+       case STMT_EXPRESSION:
+               return netlink_gen_expr(ctx, stmt->expr, NFT_REG_VERDICT);
+       case STMT_VERDICT:
+               return netlink_gen_verdict_stmt(ctx, stmt);
+       case STMT_COUNTER:
+               return netlink_gen_counter_stmt(ctx, stmt);
+       case STMT_META:
+               return netlink_gen_meta_stmt(ctx, stmt);
+       case STMT_LOG:
+               return netlink_gen_log_stmt(ctx, stmt);
+       case STMT_LIMIT:
+               return netlink_gen_limit_stmt(ctx, stmt);
+       case STMT_REJECT:
+               return netlink_gen_reject_stmt(ctx, stmt);
+       case STMT_NAT:
+               return netlink_gen_nat_stmt(ctx, stmt);
+       default:
+               BUG();
+       }
+}
+
+int netlink_linearize_rule(struct netlink_ctx *ctx, struct nfnl_nft_rule *nlr,
+                          const struct rule *rule)
+{
+       struct netlink_linearize_ctx lctx;
+       const struct stmt *stmt;
+
+       memset(&lctx, 0, sizeof(lctx));
+       lctx.reg_low = NFT_REG_1;
+       lctx.nlr = nlr;
+
+       list_for_each_entry(stmt, &rule->stmts, list)
+               netlink_gen_stmt(&lctx, stmt);
+
+       netlink_dump_object((struct nl_object *)nlr);
+       return 0;
+}
diff --git a/src/parser-skeleton.c b/src/parser-skeleton.c
new file mode 100644 (file)
index 0000000..978f7c4
--- /dev/null
@@ -0,0 +1,1529 @@
+m4_divert(-1)                                                -*- C -*-
+
+# Yacc compatible skeleton for Bison
+
+# Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+# Free Software Foundation, Inc.
+
+# 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., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301  USA
+
+m4_include(b4_pkgdatadir/[c.m4])
+
+## ---------------- ##
+## Default values.  ##
+## ---------------- ##
+
+# Stack parameters.
+m4_define_default([b4_stack_depth_max], [10000])
+m4_define_default([b4_stack_depth_init],  [200])
+
+
+## ------------------------ ##
+## Pure/impure interfaces.  ##
+## ------------------------ ##
+
+
+# b4_yacc_pure_if(IF-TRUE, IF-FALSE)
+# ----------------------------------
+# Expand IF-TRUE, if %pure-parser and %parse-param, IF-FALSE otherwise.
+m4_define([b4_yacc_pure_if],
+[b4_pure_if([m4_ifset([b4_parse_param],
+                     [$1], [$2])],
+           [$2])])
+
+
+# b4_yyerror_args
+# ---------------
+# Arguments passed to yyerror: user args plus yylloc.
+m4_define([b4_yyerror_args],
+[b4_yacc_pure_if([b4_locations_if([&yylloc, ])])dnl
+m4_ifset([b4_parse_param], [b4_c_args(b4_parse_param), ])])
+
+
+# b4_lex_param
+# ------------
+# Accumulate in b4_lex_param all the yylex arguments.
+# b4_lex_param arrives quoted twice, but we want to keep only one level.
+m4_define([b4_lex_param],
+m4_dquote(b4_pure_if([[[[YYSTYPE *]], [[&yylval]]][]dnl
+b4_locations_if([, [[YYLTYPE *], [&yylloc]]])m4_ifdef([b4_lex_param], [, ])])dnl
+m4_ifdef([b4_lex_param], b4_lex_param)))
+
+
+
+## ------------ ##
+## Data Types.  ##
+## ------------ ##
+
+# b4_int_type(MIN, MAX)
+# ---------------------
+# Return the smallest int type able to handle numbers ranging from
+# MIN to MAX (included).  Overwrite the version from c.m4, which
+# uses only C89 types, so that the user can override the shorter
+# types, and so that pre-C89 compilers are handled correctly.
+m4_define([b4_int_type],
+[m4_if(b4_ints_in($@,      [0],   [255]), [1], [yytype_uint8],
+       b4_ints_in($@,   [-128],   [127]), [1], [yytype_int8],
+
+       b4_ints_in($@,      [0], [65535]), [1], [yytype_uint16],
+       b4_ints_in($@, [-32768], [32767]), [1], [yytype_int16],
+
+       m4_eval([0 <= $1]),                [1], [unsigned int],
+
+                                              [int])])
+
+
+## ----------------- ##
+## Semantic Values.  ##
+## ----------------- ##
+
+
+# b4_lhs_value([TYPE])
+# --------------------
+# Expansion of $<TYPE>$.
+m4_define([b4_lhs_value],
+[(yyval[]m4_ifval([$1], [.$1]))])
+
+
+# b4_rhs_value(RULE-LENGTH, NUM, [TYPE])
+# --------------------------------------
+# Expansion of $<TYPE>NUM, where the current rule has RULE-LENGTH
+# symbols on RHS.
+m4_define([b4_rhs_value],
+[(yyvsp@{($2) - ($1)@}m4_ifval([$3], [.$3]))])
+
+
+
+## ----------- ##
+## Locations.  ##
+## ----------- ##
+
+# b4_lhs_location()
+# -----------------
+# Expansion of @$.
+m4_define([b4_lhs_location],
+[(yyloc)])
+
+
+# b4_rhs_location(RULE-LENGTH, NUM)
+# ---------------------------------
+# Expansion of @NUM, where the current rule has RULE-LENGTH symbols
+# on RHS.
+m4_define([b4_rhs_location],
+[(yylsp@{($2) - ($1)@})])
+
+
+
+## --------------------------------------------------------- ##
+## Defining symbol actions, e.g., printers and destructors.  ##
+## --------------------------------------------------------- ##
+
+# We do want M4 expansion after # for CPP macros.
+m4_changecom()
+m4_divert(0)dnl
+@output @output_parser_name@
+b4_copyright([Skeleton implementation for Bison's Yacc-like parsers in C],dnl '
+  [1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006])[
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+]b4_identification
+m4_if(b4_prefix, [yy], [],
+[/* Substitute the variable and function names.  */
+#define yyparse b4_prefix[]parse
+#define yylex   b4_prefix[]lex
+#define yyerror b4_prefix[]error
+#define yylval  b4_prefix[]lval
+#define yychar  b4_prefix[]char
+#define yydebug b4_prefix[]debug
+#define yynerrs b4_prefix[]nerrs
+b4_locations_if([#define yylloc b4_prefix[]lloc])])[
+
+]b4_token_enums_defines(b4_tokens)[
+
+/* Copy the first part of user declarations.  */
+]b4_pre_prologue[
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG ]b4_debug_flag[
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE ]b4_error_verbose_flag[
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE ]b4_token_table[
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+]m4_ifdef([b4_stype],
+[typedef union[]m4_bregexp(b4_stype, [^{], [ YYSTYPE])
+b4_stype
+/* Line __line__ of yacc.c.  */
+b4_syncline([@oline@], [@ofile@])
+       YYSTYPE;],
+[typedef int YYSTYPE;])[
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+]b4_locations_if([#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+])[
+
+/* Copy the second part of user declarations.  */
+]b4_post_prologue
+
+/* Line __line__ of yacc.c.  */
+b4_syncline([@oline@], [@ofile@])[
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif ]b4_c_modern[
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && ]b4_c_modern[
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+]b4_c_function_def([YYID], [static int], [[int i], [i]])[
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && ]b4_c_modern[
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+            && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && ]b4_c_modern[
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && ]b4_c_modern[
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+        || (]b4_locations_if([[defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL \
+            && ]])[defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+  ]b4_locations_if([  YYLTYPE yyls;
+])dnl
+[};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+]b4_locations_if(
+[# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \
+      + 2 * YYSTACK_GAP_MAXIMUM)],
+[# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)])[
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         YYSIZE_T yyi;                         \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  ]b4_final_state_number[
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   ]b4_last[
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  ]b4_tokens_number[
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  ]b4_nterms_number[
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  ]b4_rules_number[
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  ]b4_states_number[
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  ]b4_undef_token_number[
+#define YYMAXUTOK   ]b4_user_token_number_max[
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const ]b4_int_type_for([b4_translate])[ yytranslate[] =
+{
+  ]b4_translate[
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const ]b4_int_type_for([b4_prhs])[ yyprhs[] =
+{
+  ]b4_prhs[
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const ]b4_int_type_for([b4_rhs])[ yyrhs[] =
+{
+  ]b4_rhs[
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const ]b4_int_type_for([b4_rline])[ yyrline[] =
+{
+  ]b4_rline[
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  ]b4_tname[
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const ]b4_int_type_for([b4_toknum])[ yytoknum[] =
+{
+  ]b4_toknum[
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const ]b4_int_type_for([b4_r1])[ yyr1[] =
+{
+  ]b4_r1[
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const ]b4_int_type_for([b4_r2])[ yyr2[] =
+{
+  ]b4_r2[
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const ]b4_int_type_for([b4_defact])[ yydefact[] =
+{
+  ]b4_defact[
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const ]b4_int_type_for([b4_defgoto])[ yydefgoto[] =
+{
+  ]b4_defgoto[
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF ]b4_pact_ninf[
+static const ]b4_int_type_for([b4_pact])[ yypact[] =
+{
+  ]b4_pact[
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const ]b4_int_type_for([b4_pgoto])[ yypgoto[] =
+{
+  ]b4_pgoto[
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF ]b4_table_ninf[
+static const ]b4_int_type_for([b4_table])[ yytable[] =
+{
+  ]b4_table[
+};
+
+static const ]b4_int_type_for([b4_check])[ yycheck[] =
+{
+  ]b4_check[
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const ]b4_int_type_for([b4_stos])[ yystos[] =
+{
+  ]b4_stos[
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK (1);                                          \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror (]b4_yyerror_args[YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (YYID (0))
+
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)                               \
+    do                                                                 \
+      if (YYID (N))                                                    \
+       {                                                               \
+         (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;        \
+         (Current).first_column = YYRHSLOC (Rhs, 1).first_column;      \
+         (Current).last_line    = YYRHSLOC (Rhs, N).last_line;         \
+         (Current).last_column  = YYRHSLOC (Rhs, N).last_column;       \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         (Current).first_line   = (Current).last_line   =              \
+           YYRHSLOC (Rhs, 0).last_line;                                \
+         (Current).first_column = (Current).last_column =              \
+           YYRHSLOC (Rhs, 0).last_column;                              \
+       }                                                               \
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)                 \
+     fprintf (File, "%d.%d-%d.%d",                     \
+             (Loc).first_line, (Loc).first_column,     \
+             (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (]b4_pure_if([&yylval[]b4_locations_if([, &yylloc]), ])[YYLEX_PARAM)
+#else
+# define YYLEX ]b4_c_function_call([yylex], [int], b4_lex_param)[
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                   \
+do {                                                                     \
+  if (yydebug)                                                           \
+    {                                                                    \
+      YYFPRINTF (stderr, "%s ", Title);                                          \
+      yy_symbol_print (stderr,                                           \
+                 Type, Value]b4_locations_if([, Location])[]b4_user_args[); \
+      YYFPRINTF (stderr, "\n");                                                  \
+    }                                                                    \
+} while (YYID (0))
+
+]b4_yy_symbol_print_generate([b4_c_function_def])[
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+]b4_c_function_def([yy_stack_print], [static void],
+                  [[yytype_int16 *bottom], [bottom]],
+                  [[yytype_int16 *top],    [top]])[
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+]b4_c_function_def([yy_reduce_print], [static void],
+                  [[YYSTYPE *yyvsp], [yyvsp]],
+    b4_locations_if([[[YYLTYPE *yylsp], [yylsp]],
+                  ])[[int yyrule], [yyrule]]m4_ifset([b4_parse_param], [,
+                  ])b4_parse_param)[
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+            yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+                      &]b4_rhs_value(yynrhs, yyi + 1)[
+                      ]b4_locations_if([, &]b4_rhs_location(yynrhs, yyi + 1))[]dnl
+                      b4_user_args[);
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (yyvsp, ]b4_locations_if([yylsp, ])[Rule]b4_user_args[); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH ]b4_stack_depth_init[
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH ]b4_stack_depth_max[
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+]b4_c_function_def([yystrlen], [static YYSIZE_T],
+   [[const char *yystr], [yystr]])[
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+]b4_c_function_def([yystpcpy], [static char *],
+   [[char *yydest], [yydest]], [[const char *yysrc], [yysrc]])[
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 100 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+        constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+                   + sizeof yyexpecting - 1
+                   + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+                      * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+        YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+       if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+         {
+           if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+             {
+               yycount = 1;
+               yysize = yysize0;
+               yyformat[sizeof yyunexpected - 1] = '\0';
+               break;
+             }
+           yyarg[yycount++] = yytname[yyx];
+           yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+           yysize_overflow |= (yysize1 < yysize);
+           yysize = yysize1;
+           yyfmt = yystpcpy (yyfmt, yyprefix);
+           yyprefix = yyor;
+         }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+       return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+       {
+         /* Avoid sprintf, as that infringes on the user's name space.
+            Don't have undefined behavior even if the translation
+            produced a string with the wrong number of "%s"s.  */
+         char *yyp = yyresult;
+         int yyi = 0;
+         while ((*yyp = *yyf) != '\0')
+           {
+             if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+               {
+                 yyp += yytnamerr (yyp, yyarg[yyi++]);
+                 yyf += 2;
+               }
+             else
+               {
+                 yyp++;
+                 yyf++;
+               }
+           }
+       }
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+\f
+
+]b4_yydestruct_generate([b4_c_function_def])[
+\f
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+]b4_c_function_decl([yyparse], [int],
+   [[void *YYPARSE_PARAM], [YYPARSE_PARAM]])[
+#else /* ! YYPARSE_PARAM */
+]b4_c_function_decl([yyparse], [int], b4_parse_param)[
+#endif /* ! YYPARSE_PARAM */
+
+
+]m4_divert_push([KILL])# ======================== M4 code.
+# b4_declare_parser_variables
+# ---------------------------
+# Declare the variables that are global, or local to YYPARSE if
+# pure-parser.
+m4_define([b4_declare_parser_variables],
+[/* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;b4_locations_if([
+/* Location data for the look-ahead symbol.  */
+YYLTYPE yylloc;])
+])
+m4_divert_pop([KILL])dnl# ====================== End of M4 code.
+
+b4_pure_if([],
+          [b4_declare_parser_variables])
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+b4_c_function_def([yyparse], [int], [[void *YYPARSE_PARAM], [YYPARSE_PARAM]])
+#else /* ! YYPARSE_PARAM */
+b4_c_function_def([yyparse], [int], b4_parse_param)
+#endif
+{[
+  ]b4_pure_if([b4_declare_parser_variables])[
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+]b4_locations_if(
+[[  /* The location stack.  */
+  YYLTYPE yylsa[YYINITDEPTH];
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+  /* The locations where the error started and ended.  */
+  YYLTYPE yyerror_range[2];]])[
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N)]b4_locations_if([, yylsp -= (N)])[)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+]b4_locations_if([  YYLTYPE yyloc;])[
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+]b4_locations_if([[  yylsp = yyls;
+#if YYLTYPE_IS_TRIVIAL
+  /* Initialize the default location before parsing starts.  */
+  yylloc.first_line   = yylloc.last_line   = 1;
+  yylloc.first_column = yylloc.last_column = 0;
+#endif
+]])
+m4_ifdef([b4_initial_action], [
+m4_pushdef([b4_at_dollar],     [m4_define([b4_at_dollar_used])yylloc])dnl
+m4_pushdef([b4_dollar_dollar], [m4_define([b4_dollar_dollar_used])yylval])dnl
+  /* User initialization code.  */
+b4_initial_action
+m4_popdef([b4_dollar_dollar])dnl
+m4_popdef([b4_at_dollar])dnl
+/* Line __line__ of yacc.c.  */
+b4_syncline([@oline@], [@ofile@])
+])dnl
+m4_ifdef([b4_dollar_dollar_used],[[  yyvsp[0] = yylval;
+]])dnl
+m4_ifdef([b4_at_dollar_used], [[  yylsp[0] = yylloc;
+]])dnl
+[  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack.  Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       yytype_int16 *yyss1 = yyss;
+]b4_locations_if([     YYLTYPE *yyls1 = yyls;])[
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+]b4_locations_if([                 &yyls1, yysize * sizeof (*yylsp),])[
+                   &yystacksize);
+]b4_locations_if([     yyls = yyls1;])[
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       yytype_int16 *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+]b4_locations_if([     YYSTACK_RELOCATE (yyls);])[
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+]b4_locations_if([      yylsp = yyls + yysize - 1;])[
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+]b4_locations_if([  *++yylsp = yylloc;])[
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+]b4_locations_if(
+[[  /* Default location.  */
+  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);]])[
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+      ]b4_actions
+/* Line __line__ of yacc.c.  */
+b4_syncline([@oline@], [@ofile@])[
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+]b4_locations_if([  *++yylsp = yyloc;])[
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (]b4_yyerror_args[YY_("syntax error"));
+#else
+      {
+       YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+       if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+         {
+           YYSIZE_T yyalloc = 2 * yysize;
+           if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+             yyalloc = YYSTACK_ALLOC_MAXIMUM;
+           if (yymsg != yymsgbuf)
+             YYSTACK_FREE (yymsg);
+           yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+           if (yymsg)
+             yymsg_alloc = yyalloc;
+           else
+             {
+               yymsg = yymsgbuf;
+               yymsg_alloc = sizeof yymsgbuf;
+             }
+         }
+
+       if (0 < yysize && yysize <= yymsg_alloc)
+         {
+           (void) yysyntax_error (yymsg, yystate, yychar);
+           yyerror (]b4_yyerror_args[yymsg);
+         }
+       else
+         {
+           yyerror (]b4_yyerror_args[YY_("syntax error"));
+           if (yysize != 0)
+             goto yyexhaustedlab;
+         }
+      }
+#endif
+    }
+
+]b4_locations_if([[  yyerror_range[0] = yylloc;]])[
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+       {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+       }
+      else
+       {
+         yydestruct ("Error: discarding",
+                     yytoken, &yylval]b4_locations_if([, &yylloc])[]b4_user_args[);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+]b4_locations_if([[  yyerror_range[0] = yylsp[1-yylen];
+]])[  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+]b4_locations_if([[      yyerror_range[0] = *yylsp;]])[
+      yydestruct ("Error: popping",
+                 yystos[yystate], yyvsp]b4_locations_if([, yylsp])[]b4_user_args[);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+]b4_locations_if([[
+  yyerror_range[1] = yylloc;
+  /* Using YYLLOC is tempting, but would change the location of
+     the look-ahead.  YYLOC is available though.  */
+  YYLLOC_DEFAULT (yyloc, (yyerror_range - 1), 2);
+  *++yylsp = yyloc;]])[
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (]b4_yyerror_args[YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+                yytoken, &yylval]b4_locations_if([, &yylloc])[]b4_user_args[);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp]b4_locations_if([, yylsp])[]b4_user_args[);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+]}
+
+
+b4_epilogue
+b4_defines_if(
+[@output @output_header_name@
+b4_copyright([Skeleton interface for Bison's Yacc-like parsers in C],dnl '
+  [1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006])
+
+b4_token_enums_defines(b4_tokens)
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+m4_ifdef([b4_stype],
+[typedef union[]m4_bregexp(b4_stype, [^{], [ YYSTYPE])
+b4_stype
+/* Line __line__ of yacc.c.  */
+b4_syncline([@oline@], [@ofile@])
+       YYSTYPE;],
+[typedef int YYSTYPE;])
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+b4_pure_if([],
+[extern YYSTYPE b4_prefix[]lval;])
+
+b4_locations_if(
+[#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+} YYLTYPE;
+# define yyltype YYLTYPE /* obsolescent; will be withdrawn */
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+b4_pure_if([],
+          [extern YYLTYPE b4_prefix[]lloc;])
+])dnl b4_locations_if
+])dnl b4_defines_if
diff --git a/src/parser.y b/src/parser.y
new file mode 100644 (file)
index 0000000..c3ade91
--- /dev/null
@@ -0,0 +1,1386 @@
+/*
+ * Copyright (c) 2007-2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+%{
+
+#include <stddef.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <netinet/ip.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+
+#include <rule.h>
+#include <statement.h>
+#include <expression.h>
+#include <utils.h>
+#include <parser.h>
+#include <erec.h>
+
+#include "parser.h"
+#include "scanner.h"
+
+void parser_init(struct parser_state *state, struct list_head *msgs)
+{
+       memset(state, 0, sizeof(*state));
+       init_list_head(&state->cmds);
+       state->msgs = msgs;
+}
+
+static void yyerror(struct location *loc, void *scanner,
+                   struct parser_state *state, const char *s)
+{
+       erec_queue(error(loc, "%s", s), state->msgs);
+}
+
+static void location_init(void *scanner, struct parser_state *state,
+                         struct location *loc)
+{
+       memset(loc, 0, sizeof(*loc));
+       loc->indesc = state->indesc;
+}
+
+static void location_update(struct location *loc, struct location *rhs, int n)
+{
+       if (n) {
+               loc->indesc       = rhs[n].indesc;
+               loc->token_offset = rhs[1].token_offset;
+               loc->line_offset  = rhs[1].line_offset;
+               loc->first_line   = rhs[1].first_line;
+               loc->first_column = rhs[1].first_column;
+               loc->last_line    = rhs[n].last_line;
+               loc->last_column  = rhs[n].last_column;
+       } else {
+               loc->indesc       = rhs[0].indesc;
+               loc->token_offset = rhs[0].token_offset;
+               loc->line_offset  = rhs[0].line_offset;
+               loc->first_line   = loc->last_line   = rhs[0].last_line;
+               loc->first_column = loc->last_column = rhs[0].last_column;
+       }
+}
+
+#define YYLLOC_DEFAULT(Current, Rhs, N)        location_update(&Current, Rhs, N)
+
+%}
+
+/* Declaration section */
+
+%name-prefix="nft_"
+%debug
+%pure-parser
+%parse-param           { void *scanner }
+%parse-param           { struct parser_state *state }
+%lex-param             { scanner }
+%error-verbose
+%locations
+
+%initial-action {
+       location_init(scanner, state, &yylloc);
+#if 0
+       nft_set_debug(1, scanner);
+       yydebug = 1;
+#endif
+}
+
+%union {
+       uint64_t                val;
+       const char *            string;
+
+       struct list_head        *list;
+       struct cmd              *cmd;
+       struct handle           handle;
+       struct table            *table;
+       struct chain            *chain;
+       struct rule             *rule;
+       struct stmt             *stmt;
+       struct expr             *expr;
+}
+
+%token TOKEN_EOF 0             "end of file"
+%token JUNK                    "junk"
+
+%token NEWLINE                 "newline"
+%token COLON                   "colon"
+%token SEMICOLON               "semicolon"
+%token COMMA                   "comma"
+%token DOT                     "."
+
+%token EQ                      "=="
+%token NEQ                     "!="
+%token LT                      "<"
+%token GT                      ">"
+%token GTE                     ">="
+%token LTE                     "<="
+%token LSHIFT                  "<<"
+%token RSHIFT                  ">>"
+%token AMPERSAND               "&"
+%token CARET                   "^"
+%token NOT                     "!"
+%token SLASH                   "/"
+%token ASTERISK                        "*"
+%token DASH                    "-"
+%token AT                      "@"
+%token ARROW                   "=>"
+%token MAP                     "map"
+%token VMAP                    "vmap"
+%token SET                     "set"
+
+%token INCLUDE                 "include"
+
+%token HOOK                    "hook"
+%token <val> HOOKNUM           "hooknum"
+%token TABLE                   "table"
+%token CHAIN                   "chain"
+%token RULE                    "rule"
+%token HANDLE                  "handle"
+
+%token ADD                     "add"
+%token DELETE                  "delete"
+%token LIST                    "list"
+%token FLUSH                   "flush"
+%token DESCRIBE                        "describe"
+
+%token ACCEPT                  "accept"
+%token DROP                    "drop"
+%token CONTINUE                        "continue"
+%token JUMP                    "jump"
+%token GOTO                    "goto"
+%token RETURN                  "return"
+%token QUEUE                   "queue"
+
+%token <val> NUM               "number"
+%token <string> STRING         "string"
+%token <string> QUOTED_STRING
+%destructor { xfree($$); }     STRING QUOTED_STRING
+
+%token LL_HDR                  "ll"
+%token NETWORK_HDR             "nh"
+%token TRANSPORT_HDR           "th"
+
+%token BRIDGE                  "bridge"
+
+%token ETH                     "eth"
+%token SADDR                   "saddr"
+%token DADDR                   "daddr"
+%token TYPE                    "type"
+
+%token VLAN                    "vlan"
+%token ID                      "id"
+%token CFI                     "cfi"
+%token PCP                     "pcp"
+
+%token ARP                     "arp"
+%token HTYPE                   "htype"
+%token PTYPE                   "ptype"
+%token HLEN                    "hlen"
+%token PLEN                    "plen"
+%token OPERATION               "operation"
+
+%token IP                      "ip"
+%token VERSION                 "version"
+%token HDRLENGTH               "hdrlength"
+%token TOS                     "tos"
+%token LENGTH                  "length"
+%token FRAG_OFF                        "frag-off"
+%token TTL                     "ttl"
+%token PROTOCOL                        "protocol"
+%token CHECKSUM                        "checksum"
+
+%token ICMP                    "icmp"
+%token CODE                    "code"
+%token SEQUENCE                        "seq"
+%token GATEWAY                 "gateway"
+%token MTU                     "mtu"
+
+%token IP6                     "ip6"
+%token PRIORITY                        "priority"
+%token FLOWLABEL               "flowlabel"
+%token NEXTHDR                 "nexthdr"
+%token HOPLIMIT                        "hoplimit"
+
+%token AH                      "ah"
+%token RESERVED                        "reserved"
+%token SPI                     "spi"
+
+%token ESP                     "esp"
+
+%token COMP                    "comp"
+%token FLAGS                   "flags"
+%token CPI                     "cpi"
+
+%token UDP                     "udp"
+%token SPORT                   "sport"
+%token DPORT                   "dport"
+%token UDPLITE                 "udplite"
+%token CSUMCOV                 "csumcov"
+
+%token TCP                     "tcp"
+%token ACKSEQ                  "ackseq"
+%token DOFF                    "doff"
+%token WINDOW                  "window"
+%token URGPTR                  "urgptr"
+
+%token DCCP                    "dccp"
+
+%token SCTP                    "sctp"
+%token VTAG                    "vtag"
+
+%token RT                      "rt"
+%token RT0                     "rt0"
+%token RT2                     "rt2"
+%token SEG_LEFT                        "seg-left"
+%token ADDR                    "addr"
+
+%token HBH                     "hbh"
+
+%token FRAG                    "frag"
+%token RESERVED2               "reserved2"
+%token MORE_FRAGMENTS          "more-fragments"
+
+%token DST                     "dst"
+
+%token MH                      "mh"
+
+%token META                    "meta"
+%token MARK                    "mark"
+%token IIF                     "iif"
+%token IIFNAME                 "iifname"
+%token IIFTYPE                 "iiftype"
+%token OIF                     "oif"
+%token OIFNAME                 "oifname"
+%token OIFTYPE                 "oiftype"
+%token SKUID                   "skuid"
+%token SKGID                   "skgid"
+%token NFTRACE                 "nftrace"
+%token RTCLASSID               "rtclassid"
+%token SECMARK                 "secmark"
+
+%token CT                      "ct"
+%token DIRECTION               "direction"
+%token STATE                   "state"
+%token STATUS                  "status"
+%token EXPIRATION              "expiration"
+%token HELPER                  "helper"
+%token PROTO_SRC               "proto-src"
+%token PROTO_DST               "proto-dst"
+
+%token COUNTER                 "counter"
+
+%token LOG                     "log"
+%token PREFIX                  "prefix"
+%token GROUP                   "group"
+%token SNAPLEN                 "snaplen"
+%token QUEUE_THRESHOLD         "queue-threshold"
+
+%token LIMIT                   "limit"
+%token RATE                    "rate"
+
+%token NANOSECOND              "nanosecond"
+%token MICROSECOND             "microsecond"
+%token MILLISECOND             "millisecond"
+%token SECOND                  "second"
+%token MINUTE                  "minute"
+%token HOUR                    "hour"
+%token DAY                     "day"
+%token WEEK                    "week"
+
+%token _REJECT                 "reject"
+
+%token SNAT                    "snat"
+%token DNAT                    "dnat"
+
+%type <string>                 identifier string
+%destructor { xfree($$); }     identifier string
+
+%type <cmd>                    line
+%destructor { cmd_free($$); }  line
+
+%type <cmd>                    base_cmd add_cmd delete_cmd list_cmd flush_cmd
+%destructor { cmd_free($$); }  base_cmd add_cmd delete_cmd list_cmd flush_cmd
+
+%type <handle>                 table_spec chain_spec chain_identifier ruleid_spec
+%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec
+%type <val>                    handle_spec family_spec
+
+%type <table>                  table_block_alloc table_block
+%destructor { table_free($$); }        table_block_alloc
+%type <chain>                  table_line chain_block_alloc chain_block
+%destructor { chain_free($$); }        table_line chain_block_alloc
+%type <rule>                   rule
+%destructor { rule_free($$); } rule
+
+%type <list>                   stmt_list
+%destructor { stmt_list_free($$); xfree($$); } stmt_list
+%type <stmt>                   stmt match_stmt verdict_stmt
+%destructor { stmt_free($$); } stmt match_stmt verdict_stmt
+%type <stmt>                   counter_stmt
+%destructor { stmt_free($$); } counter_stmt
+%type <stmt>                   meta_stmt
+%destructor { stmt_free($$); } meta_stmt
+%type <stmt>                   log_stmt log_stmt_alloc
+%destructor { stmt_free($$); } log_stmt log_stmt_alloc
+%type <stmt>                   limit_stmt
+%destructor { stmt_free($$); } limit_stmt
+%type <val>                    time_unit
+%type <stmt>                   reject_stmt
+%destructor { stmt_free($$); } reject_stmt
+%type <stmt>                   nat_stmt nat_stmt_alloc
+%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc
+
+%type <expr>                   symbol_expr verdict_expr integer_expr
+%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr
+%type <expr>                   primary_expr shift_expr and_expr
+%destructor { expr_free($$); } primary_expr shift_expr and_expr
+%type <expr>                   exclusive_or_expr inclusive_or_expr
+%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr
+%type <expr>                   basic_expr
+%destructor { expr_free($$); } basic_expr
+
+%type <expr>                   multiton_expr
+%destructor { expr_free($$); } multiton_expr
+%type <expr>                   prefix_expr range_expr wildcard_expr
+%destructor { expr_free($$); } prefix_expr range_expr wildcard_expr
+%type <expr>                   list_expr
+%destructor { expr_free($$); } list_expr
+%type <expr>                   concat_expr map_lhs_expr
+%destructor { expr_free($$); } concat_expr map_lhs_expr
+
+%type <expr>                   map_expr map_list map_list_expr
+%destructor { expr_free($$); } map_expr map_list map_list_expr
+
+%type <expr>                   verdict_map_expr verdict_map_list verdict_map_list_expr
+%destructor { expr_free($$); } verdict_map_expr verdict_map_list verdict_map_list_expr
+
+%type <expr>                   set_expr
+%destructor { expr_free($$); } set_expr
+
+%type <expr>                   expr
+%destructor { expr_free($$); } expr
+
+%type <expr>                   match_expr
+%destructor { expr_free($$); } match_expr
+%type <expr>                   relational_expr membership_expr
+%destructor { expr_free($$); } relational_expr membership_expr
+%type <val>                    relational_op
+
+%type <expr>                   payload_expr payload_raw_expr
+%destructor { expr_free($$); } payload_expr payload_raw_expr
+%type <val>                    payload_base_spec
+%type <expr>                   eth_hdr_expr    vlan_hdr_expr
+%destructor { expr_free($$); } eth_hdr_expr    vlan_hdr_expr
+%type <val>                    eth_hdr_field   vlan_hdr_field
+%type <expr>                   arp_hdr_expr
+%destructor { expr_free($$); } arp_hdr_expr
+%type <val>                    arp_hdr_field
+%type <expr>                   ip_hdr_expr     icmp_hdr_expr
+%destructor { expr_free($$); } ip_hdr_expr     icmp_hdr_expr
+%type <val>                    ip_hdr_field    icmp_hdr_field
+%type <expr>                   ip6_hdr_expr
+%destructor { expr_free($$); } ip6_hdr_expr
+%type <val>                    ip6_hdr_field
+%type <expr>                   auth_hdr_expr   esp_hdr_expr            comp_hdr_expr
+%destructor { expr_free($$); } auth_hdr_expr   esp_hdr_expr            comp_hdr_expr
+%type <val>                    auth_hdr_field  esp_hdr_field           comp_hdr_field
+%type <expr>                   udp_hdr_expr    udplite_hdr_expr        tcp_hdr_expr
+%destructor { expr_free($$); } udp_hdr_expr    udplite_hdr_expr        tcp_hdr_expr
+%type <val>                    udp_hdr_field   udplite_hdr_field       tcp_hdr_field
+%type <expr>                   dccp_hdr_expr   sctp_hdr_expr
+%destructor { expr_free($$); } dccp_hdr_expr   sctp_hdr_expr
+%type <val>                    dccp_hdr_field  sctp_hdr_field
+
+%type <expr>                   exthdr_expr
+%destructor { expr_free($$); } exthdr_expr
+%type <expr>                   hbh_hdr_expr    frag_hdr_expr           dst_hdr_expr
+%destructor { expr_free($$); } hbh_hdr_expr    frag_hdr_expr           dst_hdr_expr
+%type <val>                    hbh_hdr_field   frag_hdr_field          dst_hdr_field
+%type <expr>                   rt_hdr_expr     rt0_hdr_expr            rt2_hdr_expr
+%destructor { expr_free($$); } rt_hdr_expr     rt0_hdr_expr            rt2_hdr_expr
+%type <val>                    rt_hdr_field    rt0_hdr_field           rt2_hdr_field
+%type <expr>                   mh_hdr_expr
+%destructor { expr_free($$); } mh_hdr_expr
+%type <val>                    mh_hdr_field
+
+%type <expr>                   meta_expr
+%destructor { expr_free($$); } meta_expr
+%type <val>                    meta_key
+
+%type <expr>                   ct_expr
+%destructor { expr_free($$); } ct_expr
+%type <val>                    ct_key
+
+%%
+
+input                  :       /* empty */
+                       |       input           line
+                       {
+                               if ($2 != NULL) {
+                                       $2->location = @2;
+                                       list_add_tail(&$2->list, &state->cmds);
+                               }
+                       }
+                       ;
+
+stmt_seperator         :       NEWLINE
+                       |       SEMICOLON
+                       ;
+
+common_block           :       INCLUDE         QUOTED_STRING   stmt_seperator
+                       {
+                               if (scanner_include_file(scanner, $2, &@$) < 0) {
+                                       xfree($2);
+                                       YYERROR;
+                               }
+                               xfree($2);
+                       }
+                       ;
+
+line                   :       common_block                    { $$ = NULL; }
+                       |       stmt_seperator                  { $$ = NULL; }
+                       |       base_cmd        stmt_seperator  { $$ = $1; }
+                       |       base_cmd        TOKEN_EOF       { $$ = $1; }
+                       |       base_cmd        error           { $$ = $1; }
+                       ;
+
+base_cmd               :       /* empty */     add_cmd         { $$ = $1; }
+                       |       ADD             add_cmd         { $$ = $2; }
+                       |       DELETE          delete_cmd      { $$ = $2; }
+                       |       LIST            list_cmd        { $$ = $2; }
+                       |       FLUSH           flush_cmd       { $$ = $2; }
+                       |       DESCRIBE        primary_expr
+                       {
+                               expr_describe($2);
+                               expr_free($2);
+                               $$ = NULL;
+                       }
+                       ;
+
+add_cmd                        :       TABLE           table_spec
+                       {
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &$2, NULL);
+                       }
+                       |       TABLE           table_spec      table_block_alloc
+                                               '{'     table_block     '}'
+                       {
+                               handle_merge(&$3->handle, &$2);
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &$2, $5);
+                       }
+                       |       CHAIN           chain_spec
+                       {
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &$2, NULL);
+                       }
+                       |       CHAIN           chain_spec      chain_block_alloc
+                                               '{'     chain_block     '}'
+                       {
+                               handle_merge(&$3->handle, &$2);
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &$2, $5);
+                       }
+                       |       RULE            ruleid_spec     rule
+                       {
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$2, $3);
+                       }
+                       |       /* empty */     ruleid_spec     rule
+                       {
+                               $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$1, $2);
+                       }
+                       ;
+
+delete_cmd             :       TABLE           table_spec
+                       {
+                               $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &$2, NULL);
+                       }
+                       |       CHAIN           chain_spec
+                       {
+                               $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, NULL);
+                       }
+                       |       RULE            ruleid_spec
+                       {
+                               $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, NULL);
+                       }
+                       ;
+
+list_cmd               :       TABLE           table_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, NULL);
+                       }
+                       |       CHAIN           chain_spec
+                       {
+                               $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAIN, &$2, NULL);
+                       }
+                       ;
+
+flush_cmd              :       TABLE           table_spec
+                       {
+                               $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_TABLE, &$2, NULL);
+                       }
+                       |       CHAIN           chain_spec
+                       {
+                               $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_CHAIN, &$2, NULL);
+                       }
+                       ;
+
+table_block_alloc      :       /* empty */     { $$ = table_alloc(); }
+                       ;
+
+table_block            :       /* empty */     { $$ = $<table>-1; }
+                       |       common_block    { $$ = $<table>-1; }
+                       |       table_block     stmt_seperator
+                       |       table_block     table_line      stmt_seperator
+                       {
+                               list_add_tail(&$2->list, &$1->chains);
+                               $$ = $1;
+                       }
+                       ;
+
+table_line             :       CHAIN           chain_identifier        chain_block_alloc
+                                               '{'     chain_block     '}'
+                       {
+                               handle_merge(&$3->handle, &$2);
+                               $$ = $3;
+                       }
+                       ;
+
+chain_block_alloc      :       /* empty */     { $$ = chain_alloc(NULL); }
+                       ;
+
+chain_block            :       /* empty */     { $$ = $<chain>-1; }
+                       |       common_block    { $$ = $<chain>-1; }
+                       |       chain_block     stmt_seperator
+                       |       chain_block     hook_spec       stmt_seperator
+                       |       chain_block     rule            stmt_seperator
+                       {
+                               list_add_tail(&$2->list, &$1->rules);
+                               $$ = $1;
+                       }
+                       ;
+
+hook_spec              :       HOOK            HOOKNUM         NUM
+                       {
+                               $<chain>0->hooknum      = $2;
+                               $<chain>0->priority     = $3;
+                       }
+                       |       HOOK            HOOKNUM         DASH    NUM
+                       {
+                               $<chain>0->hooknum      = $2;
+                               $<chain>0->priority     = -$4;
+                       }
+                       ;
+
+identifier             :       STRING
+                       ;
+
+string                 :       STRING
+                       |       QUOTED_STRING
+                       ;
+
+family_spec            :       /* empty */     { $$ = NFPROTO_IPV4; }
+                       |       IP              { $$ = NFPROTO_IPV4; }
+                       |       IP6             { $$ = NFPROTO_IPV6; }
+                       |       ARP             { $$ = NFPROTO_ARP; }
+                       |       BRIDGE          { $$ = NFPROTO_BRIDGE; }
+                       ;
+
+table_spec             :       family_spec     identifier
+                       {
+                               memset(&$$, 0, sizeof($$));
+                               $$.family       = $1;
+                               $$.table        = $2;
+                       }
+                       ;
+
+chain_spec             :       table_spec      identifier
+                       {
+                               $$              = $1;
+                               $$.chain        = $2;
+                       }
+                       ;
+
+chain_identifier       :       identifier
+                       {
+                               memset(&$$, 0, sizeof($$));
+                               $$.chain        = $1;
+                       }
+                       ;
+
+handle_spec            :       /* empty */
+                       {
+                               $$ = 0;
+                       }
+                       |       HANDLE          NUM
+                       {
+                               $$ = $2;
+                       }
+                       ;
+
+ruleid_spec            :       chain_spec      handle_spec
+                       {
+                               $$              = $1;
+                               $$.handle       = $2;
+                       }
+                       ;
+
+rule                   :       stmt_list
+                       {
+                               struct stmt *i;
+
+                               $$ = rule_alloc(&@$, NULL);
+                               list_for_each_entry(i, $1, list)
+                                       $$->num_stmts++;
+                               list_splice_tail($1, &$$->stmts);
+                               xfree($1);
+                       }
+                       ;
+
+stmt_list              :       stmt
+                       {
+                               $$ = xmalloc(sizeof(*$$));
+                               init_list_head($$);
+                               list_add_tail(&$1->list, $$);
+                       }
+                       |       stmt_list               stmt
+                       {
+                               $$ = $1;
+                               list_add_tail(&$2->list, $1);
+                       }
+                       ;
+
+stmt                   :       verdict_stmt
+                       |       match_stmt
+                       |       counter_stmt
+                       |       meta_stmt
+                       |       log_stmt
+                       |       limit_stmt
+                       |       reject_stmt
+                       |       nat_stmt
+                       ;
+
+verdict_stmt           :       verdict_expr
+                       {
+                               $$ = verdict_stmt_alloc(&@1, $1);
+                       }
+                       |       verdict_map_expr
+                       {
+                               $$ = verdict_stmt_alloc(&@1, $1);
+                       }
+                       ;
+
+counter_stmt           :       COUNTER
+                       {
+                               $$ = counter_stmt_alloc(&@1);
+                       }
+                       ;
+
+log_stmt               :       log_stmt_alloc
+                       |       log_stmt_alloc          log_args
+                       ;
+
+log_stmt_alloc         :       LOG
+                       {
+                               $$ = log_stmt_alloc(&@1);
+                       }
+                       ;
+
+log_args               :       log_arg
+                       {
+                               $<stmt>$        = $<stmt>0;
+                       }
+                       |       log_args        log_arg
+                       ;
+
+log_arg                        :       PREFIX                  string
+                       {
+                               $<stmt>0->log.prefix     = $2;
+                       }
+                       |       GROUP                   NUM
+                       {
+                               $<stmt>0->log.group      = $2;
+                       }
+                       |       SNAPLEN                 NUM
+                       {
+                               $<stmt>0->log.snaplen    = $2;
+                       }
+                       |       QUEUE_THRESHOLD         NUM
+                       {
+                               $<stmt>0->log.qthreshold = $2;
+                       }
+                       ;
+
+limit_stmt             :       LIMIT   RATE    NUM     SLASH   time_unit
+                       {
+                               $$ = limit_stmt_alloc(&@$);
+                               $$->limit.rate  = $3;
+                               $$->limit.unit  = $5;
+                       }
+                       ;
+
+time_unit              :       NANOSECOND      { $$ = 1ULL; }
+                       |       MICROSECOND     { $$ = 1ULL * 1000; }
+                       |       MILLISECOND     { $$ = 1ULL * 1000 * 1000; }
+                       |       SECOND          { $$ = 1ULL * 1000 * 1000 * 1000; }
+                       |       MINUTE          { $$ = 1ULL * 1000 * 1000 * 1000 * 60; }
+                       |       HOUR            { $$ = 1ULL * 1000 * 1000 * 1000 * 60 * 60; }
+                       |       DAY             { $$ = 1ULL * 1000 * 1000 * 1000 * 60 * 60 * 24; }
+                       |       WEEK            { $$ = 1ULL * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7; }
+                       ;
+
+reject_stmt            :       _REJECT
+                       {
+                               $$ = reject_stmt_alloc(&@$);
+                       }
+                       ;
+
+nat_stmt               :       nat_stmt_alloc  nat_stmt_args
+                       ;
+
+nat_stmt_alloc         :       SNAT
+                       {
+                               $$ = nat_stmt_alloc(&@$);
+                               $$->nat.type = NFT_NAT_SNAT;
+                       }
+                       |       DNAT
+                       {
+                               $$ = nat_stmt_alloc(&@$);
+                               $$->nat.type = NFT_NAT_DNAT;
+                       }
+                       ;
+
+nat_stmt_args          :       expr
+                       {
+                               $<stmt>0->nat.addr = $1;
+                       }
+                       |       expr    COLON   expr
+                       {
+                               $<stmt>0->nat.addr = $1;
+                               $<stmt>0->nat.proto = $3;
+                       }
+                       |       COLON   expr
+                       {
+                               $<stmt>0->nat.proto = $2;
+                       }
+                       ;
+
+match_stmt             :       match_expr
+                       {
+                               $$ = expr_stmt_alloc(&@$, $1);
+                       }
+                       ;
+
+symbol_expr            :       string
+                       {
+                               $$ = symbol_expr_alloc(&@1, $1);
+                               xfree($1);
+                       }
+                       ;
+
+integer_expr           :       NUM
+                       {
+                               char str[64];
+
+                               snprintf(str, sizeof(str), "%" PRIu64, $1);
+                               $$ = symbol_expr_alloc(&@1, str);
+                       }
+                       ;
+
+primary_expr           :       symbol_expr                     { $$ = $1; }
+                       |       integer_expr                    { $$ = $1; }
+                       |       payload_expr                    { $$ = $1; }
+                       |       exthdr_expr                     { $$ = $1; }
+                       |       meta_expr                       { $$ = $1; }
+                       |       ct_expr                         { $$ = $1; }
+                       |       '('     basic_expr      ')'     { $$ = $2; }
+                       ;
+
+shift_expr             :       primary_expr
+                       |       shift_expr              LSHIFT          primary_expr
+                       {
+                               $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3);
+                       }
+                       |       shift_expr              RSHIFT          primary_expr
+                       {
+                               $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3);
+                       }
+                       ;
+
+and_expr               :       shift_expr
+                       |       and_expr                AMPERSAND       shift_expr
+                       {
+                               $$ = binop_expr_alloc(&@$, OP_AND, $1, $3);
+                       }
+                       ;
+
+exclusive_or_expr      :       and_expr
+                       |       exclusive_or_expr       CARET           and_expr
+                       {
+                               $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3);
+                       }
+                       ;
+
+inclusive_or_expr      :       exclusive_or_expr
+                       |       inclusive_or_expr       '|'             exclusive_or_expr
+                       {
+                               $$ = binop_expr_alloc(&@$, OP_OR, $1, $3);
+                       }
+                       ;
+
+basic_expr             :       inclusive_or_expr
+                       ;
+
+concat_expr            :       basic_expr
+                       |       concat_expr             DOT             basic_expr
+                       {
+                               if ($$->ops->type != EXPR_CONCAT) {
+                                       $$ = concat_expr_alloc(&@$);
+                                       compound_expr_add($$, $1);
+                               } else {
+                                       $$ = $1;
+                                       $$->location = @$;
+                               }
+                               compound_expr_add($$, $3);
+                       }
+                       ;
+
+list_expr              :       basic_expr              COMMA           basic_expr
+                       {
+                               $$ = list_expr_alloc(&@$);
+                               compound_expr_add($$, $1);
+                               compound_expr_add($$, $3);
+                       }
+                       |       list_expr               COMMA           basic_expr
+                       {
+                               $1->location = @$;
+                               compound_expr_add($1, $3);
+                               $$ = $1;
+                       }
+                       ;
+
+prefix_expr            :       basic_expr              SLASH   NUM
+                       {
+                               $$ = prefix_expr_alloc(&@$, $1, $3);
+                       }
+                       ;
+
+range_expr             :       basic_expr              DASH    basic_expr
+                       {
+                               $$ = range_expr_alloc(&@$, $1, $3);
+                       }
+                       ;
+
+wildcard_expr          :       ASTERISK
+                       {
+                               struct expr *expr;
+
+                               expr = constant_expr_alloc(&@1, &integer_type,
+                                                          BYTEORDER_HOST_ENDIAN,
+                                                          0, NULL);
+                               $$ = prefix_expr_alloc(&@$, expr, 0);
+                       }
+                       ;
+
+multiton_expr          :       prefix_expr
+                       |       range_expr
+                       |       wildcard_expr
+                       ;
+
+map_lhs_expr           :       multiton_expr
+                       |       concat_expr
+                       ;
+
+map_expr               :       concat_expr     MAP     '{'     map_list '}'
+                       {
+                               $$ = map_expr_alloc(&@$, $1, $4);
+                       }
+                       ;
+
+map_list               :       map_list_expr
+                       {
+                               $$ = set_expr_alloc(&@$);
+                               compound_expr_add($$, $1);
+                       }
+                       |       map_list        COMMA   map_list_expr
+                       {
+                               compound_expr_add($1, $3);
+                               $1->location = @$;
+                               $$ = $1;
+                       }
+                       |       map_list        COMMA
+                       ;
+
+map_list_expr          :       map_lhs_expr    ARROW   concat_expr
+                       {
+                               $$ = mapping_expr_alloc(&@$, $1, $3);
+                       }
+                       ;
+
+verdict_map_expr       :       concat_expr     VMAP    '{'     verdict_map_list '}'
+                       {
+                               $$ = map_expr_alloc(&@$, $1, $4);
+                       }
+                       ;
+
+verdict_map_list       :       verdict_map_list_expr
+                       {
+                               $$ = set_expr_alloc(&@$);
+                               compound_expr_add($$, $1);
+                       }
+                       |       verdict_map_list        COMMA   verdict_map_list_expr
+                       {
+                               compound_expr_add($1, $3);
+                               $1->location = @$;
+                               $$ = $1;
+                       }
+                       |       verdict_map_list        COMMA
+                       ;
+
+verdict_map_list_expr  :       map_lhs_expr    ARROW   verdict_expr
+                       {
+                               $$ = mapping_expr_alloc(&@$, $1, $3);
+                       }
+                       ;
+
+expr                   :       concat_expr
+                       |       map_expr
+                       |       multiton_expr
+                       ;
+
+match_expr             :       relational_expr
+                       |       membership_expr
+                       ;
+
+relational_expr                :       expr    /* implicit */  expr
+                       {
+                               enum ops op;
+
+                               /* RHS determines operation */
+                               op = ($2->ops->type == EXPR_RANGE) ? OP_RANGE : OP_EQ;
+                               $$ = relational_expr_alloc(&@$, op, $1, $2);
+                       }
+                       |       expr    /* implicit */  list_expr
+                       {
+                               $$ = relational_expr_alloc(&@$, OP_FLAGCMP, $1, $2);
+                       }
+                       |       expr    relational_op   expr
+                       {
+                               $$ = relational_expr_alloc(&@2, $2, $1, $3);
+                       }
+                       ;
+
+relational_op          :       EQ              { $$ = OP_EQ; }
+                       |       NEQ             { $$ = OP_NEQ; }
+                       |       LT              { $$ = OP_LT; }
+                       |       GT              { $$ = OP_GT; }
+                       |       GTE             { $$ = OP_GTE; }
+                       |       LTE             { $$ = OP_LTE; }
+                       ;
+
+membership_expr                :       expr    '{'     set_expr        '}'
+                       {
+                               $3->location = @$;
+                               $$ = relational_expr_alloc(&@$, OP_LOOKUP, $1, $3);
+                       }
+                       ;
+
+set_expr               :       expr
+                       {
+                               $$ = set_expr_alloc(&@1);
+                               compound_expr_add($$, $1);
+                       }
+                       |       set_expr        COMMA   expr
+                       {
+                               compound_expr_add($1, $3);
+                               $$ = $1;
+                       }
+                       |       set_expr        COMMA
+                       ;
+
+verdict_expr           :       ACCEPT
+                       {
+                               $$ = verdict_expr_alloc(&@1, NF_ACCEPT, NULL);
+                       }
+                       |       DROP
+                       {
+                               $$ = verdict_expr_alloc(&@1, NF_DROP, NULL);
+                       }
+                       |       QUEUE
+                       {
+                               $$ = verdict_expr_alloc(&@1, NF_QUEUE, NULL);
+                       }
+                       |       CONTINUE
+                       {
+                               $$ = verdict_expr_alloc(&@1, NFT_CONTINUE, NULL);
+                       }
+                       |       JUMP                    identifier
+                       {
+                               $$ = verdict_expr_alloc(&@1, NFT_JUMP, $2);
+                       }
+                       |       GOTO                    identifier
+                       {
+                               $$ = verdict_expr_alloc(&@1, NFT_GOTO, $2);
+                       }
+                       |       RETURN
+                       {
+                               $$ = verdict_expr_alloc(&@1, NFT_RETURN, NULL);
+                       }
+                       ;
+
+meta_expr              :       META    meta_key
+                       {
+                               $$ = meta_expr_alloc(&@$, $2);
+                       }
+                       ;
+
+meta_key               :       LENGTH          { $$ = NFT_META_LEN; }
+                       |       PROTOCOL        { $$ = NFT_META_PROTOCOL; }
+                       |       PRIORITY        { $$ = NFT_META_PRIORITY; }
+                       |       MARK            { $$ = NFT_META_MARK; }
+                       |       IIF             { $$ = NFT_META_IIF; }
+                       |       IIFNAME         { $$ = NFT_META_IIFNAME; }
+                       |       IIFTYPE         { $$ = NFT_META_IIFTYPE; }
+                       |       OIF             { $$ = NFT_META_OIF; }
+                       |       OIFNAME         { $$ = NFT_META_OIFNAME; }
+                       |       OIFTYPE         { $$ = NFT_META_OIFTYPE; }
+                       |       SKUID           { $$ = NFT_META_SKUID; }
+                       |       SKGID           { $$ = NFT_META_SKGID; }
+                       |       NFTRACE         { $$ = NFT_META_NFTRACE; }
+                       |       RTCLASSID       { $$ = NFT_META_RTCLASSID; }
+                       |       SECMARK         { $$ = NFT_META_SECMARK; }
+                       ;
+
+meta_stmt              :       META    meta_key        SET     expr
+                       {
+                               $$ = meta_stmt_alloc(&@$, $2, $4);
+                       }
+                       ;
+
+ct_expr                        :       CT      ct_key
+                       {
+                               $$ = ct_expr_alloc(&@$, $2);
+                       }
+                       ;
+
+ct_key                 :       STATE           { $$ = NFT_CT_STATE; }
+                       |       DIRECTION       { $$ = NFT_CT_DIRECTION; }
+                       |       STATUS          { $$ = NFT_CT_STATUS; }
+                       |       MARK            { $$ = NFT_CT_MARK; }
+                       |       SECMARK         { $$ = NFT_CT_SECMARK; }
+                       |       EXPIRATION      { $$ = NFT_CT_EXPIRATION; }
+                       |       HELPER          { $$ = NFT_CT_HELPER; }
+                       |       PROTOCOL        { $$ = NFT_CT_PROTOCOL; }
+                       |       SADDR           { $$ = NFT_CT_SADDR; }
+                       |       DADDR           { $$ = NFT_CT_DADDR; }
+                       |       PROTO_SRC       { $$ = NFT_CT_PROTO_SRC; }
+                       |       PROTO_DST       { $$ = NFT_CT_PROTO_DST; }
+                       ;
+
+payload_expr           :       payload_raw_expr
+                       |       eth_hdr_expr
+                       |       vlan_hdr_expr
+                       |       arp_hdr_expr
+                       |       ip_hdr_expr
+                       |       icmp_hdr_expr
+                       |       ip6_hdr_expr
+                       |       auth_hdr_expr
+                       |       esp_hdr_expr
+                       |       comp_hdr_expr
+                       |       udp_hdr_expr
+                       |       udplite_hdr_expr
+                       |       tcp_hdr_expr
+                       |       dccp_hdr_expr
+                       |       sctp_hdr_expr
+                       ;
+
+payload_raw_expr       :       AT      payload_base_spec       COMMA   NUM     COMMA   NUM
+                       {
+                               $$ = payload_expr_alloc(&@$, NULL, 0);
+                               $$->payload.base        = $2;
+                               $$->payload.offset      = $4;
+                               $$->len                 = $6;
+                               $$->dtype               = &integer_type;
+                       }
+                       ;
+
+payload_base_spec      :       LL_HDR          { $$ = PAYLOAD_BASE_LL_HDR; }
+                       |       NETWORK_HDR     { $$ = PAYLOAD_BASE_NETWORK_HDR; }
+                       |       TRANSPORT_HDR   { $$ = PAYLOAD_BASE_TRANSPORT_HDR; }
+                       ;
+
+eth_hdr_expr           :       ETH     eth_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_eth, $2);
+                       }
+                       ;
+
+eth_hdr_field          :       SADDR           { $$ = ETHHDR_SADDR; }
+                       |       DADDR           { $$ = ETHHDR_DADDR; }
+                       |       TYPE            { $$ = ETHHDR_TYPE; }
+                       ;
+
+vlan_hdr_expr          :       VLAN    vlan_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_vlan, $2);
+                       }
+                       ;
+
+vlan_hdr_field         :       ID              { $$ = VLANHDR_VID; }
+                       |       CFI             { $$ = VLANHDR_CFI; }
+                       |       PCP             { $$ = VLANHDR_PCP; }
+                       |       TYPE            { $$ = VLANHDR_TYPE; }
+                       ;
+
+arp_hdr_expr           :       ARP     arp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_arp, $2);
+                       }
+                       ;
+
+arp_hdr_field          :       HTYPE           { $$ = ARPHDR_HRD; }
+                       |       PTYPE           { $$ = ARPHDR_PRO; }
+                       |       HLEN            { $$ = ARPHDR_HLN; }
+                       |       PLEN            { $$ = ARPHDR_PLN; }
+                       |       OPERATION       { $$ = ARPHDR_OP; }
+                       ;
+
+ip_hdr_expr            :       IP      ip_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_ip, $2);
+                       }
+                       ;
+
+ip_hdr_field           :       VERSION         { $$ = IPHDR_VERSION; }
+                       |       HDRLENGTH       { $$ = IPHDR_HDRLENGTH; }
+                       |       TOS             { $$ = IPHDR_TOS; }
+                       |       LENGTH          { $$ = IPHDR_LENGTH; }
+                       |       ID              { $$ = IPHDR_ID; }
+                       |       FRAG_OFF        { $$ = IPHDR_FRAG_OFF; }
+                       |       TTL             { $$ = IPHDR_TTL; }
+                       |       PROTOCOL        { $$ = IPHDR_PROTOCOL; }
+                       |       CHECKSUM        { $$ = IPHDR_CHECKSUM; }
+                       |       SADDR           { $$ = IPHDR_SADDR; }
+                       |       DADDR           { $$ = IPHDR_DADDR; }
+                       ;
+
+icmp_hdr_expr          :       ICMP    icmp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_icmp, $2);
+                       }
+                       ;
+
+icmp_hdr_field         :       TYPE            { $$ = ICMPHDR_TYPE; }
+                       |       CODE            { $$ = ICMPHDR_CODE; }
+                       |       CHECKSUM        { $$ = ICMPHDR_CHECKSUM; }
+                       |       ID              { $$ = ICMPHDR_ID; }
+                       |       SEQUENCE        { $$ = ICMPHDR_SEQ; }
+                       |       GATEWAY         { $$ = ICMPHDR_GATEWAY; }
+                       |       MTU             { $$ = ICMPHDR_MTU; }
+                       ;
+
+ip6_hdr_expr           :       IP6     ip6_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_ip6, $2);
+                       }
+                       ;
+
+ip6_hdr_field          :       VERSION         { $$ = IP6HDR_VERSION; }
+                       |       PRIORITY        { $$ = IP6HDR_PRIORITY; }
+                       |       FLOWLABEL       { $$ = IP6HDR_FLOWLABEL; }
+                       |       LENGTH          { $$ = IP6HDR_LENGTH; }
+                       |       NEXTHDR         { $$ = IP6HDR_NEXTHDR; }
+                       |       HOPLIMIT        { $$ = IP6HDR_HOPLIMIT; }
+                       |       SADDR           { $$ = IP6HDR_SADDR; }
+                       |       DADDR           { $$ = IP6HDR_DADDR; }
+                       ;
+
+auth_hdr_expr          :       AH      auth_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_ah, $2);
+                       }
+                       ;
+
+auth_hdr_field         :       NEXTHDR         { $$ = AHHDR_NEXTHDR; }
+                       |       HDRLENGTH       { $$ = AHHDR_HDRLENGTH; }
+                       |       RESERVED        { $$ = AHHDR_RESERVED; }
+                       |       SPI             { $$ = AHHDR_SPI; }
+                       |       SEQUENCE        { $$ = AHHDR_SEQUENCE; }
+                       ;
+
+esp_hdr_expr           :       ESP     esp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_esp, $2);
+                       }
+                       ;
+
+esp_hdr_field          :       SPI             { $$ = ESPHDR_SPI; }
+                       |       SEQUENCE        { $$ = ESPHDR_SEQUENCE; }
+                       ;
+
+comp_hdr_expr          :       COMP    comp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_comp, $2);
+                       }
+                       ;
+
+comp_hdr_field         :       NEXTHDR         { $$ = COMPHDR_NEXTHDR; }
+                       |       FLAGS           { $$ = COMPHDR_FLAGS; }
+                       |       CPI             { $$ = COMPHDR_CPI; }
+                       ;
+
+udp_hdr_expr           :       UDP     udp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_udp, $2);
+                       }
+                       ;
+
+udp_hdr_field          :       SPORT           { $$ = UDPHDR_SPORT; }
+                       |       DPORT           { $$ = UDPHDR_DPORT; }
+                       |       LENGTH          { $$ = UDPHDR_LENGTH; }
+                       |       CHECKSUM        { $$ = UDPHDR_CHECKSUM; }
+                       ;
+
+udplite_hdr_expr       :       UDPLITE udplite_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_udplite, $2);
+                       }
+                       ;
+
+udplite_hdr_field      :       SPORT           { $$ = UDPHDR_SPORT; }
+                       |       DPORT           { $$ = UDPHDR_DPORT; }
+                       |       CSUMCOV         { $$ = UDPHDR_LENGTH; }
+                       |       CHECKSUM        { $$ = UDPHDR_CHECKSUM; }
+                       ;
+
+tcp_hdr_expr           :       TCP     tcp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_tcp, $2);
+                       }
+                       ;
+
+tcp_hdr_field          :       SPORT           { $$ = TCPHDR_SPORT; }
+                       |       DPORT           { $$ = TCPHDR_DPORT; }
+                       |       SEQUENCE        { $$ = TCPHDR_SEQ; }
+                       |       ACKSEQ          { $$ = TCPHDR_ACKSEQ; }
+                       |       DOFF            { $$ = TCPHDR_DOFF; }
+                       |       RESERVED        { $$ = TCPHDR_RESERVED; }
+                       |       FLAGS           { $$ = TCPHDR_FLAGS; }
+                       |       WINDOW          { $$ = TCPHDR_WINDOW; }
+                       |       CHECKSUM        { $$ = TCPHDR_CHECKSUM; }
+                       |       URGPTR          { $$ = TCPHDR_URGPTR; }
+                       ;
+
+dccp_hdr_expr          :       DCCP    dccp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_dccp, $2);
+                       }
+                       ;
+
+dccp_hdr_field         :       SPORT           { $$ = DCCPHDR_SPORT; }
+                       |       DPORT           { $$ = DCCPHDR_DPORT; }
+                       ;
+
+sctp_hdr_expr          :       SCTP    sctp_hdr_field
+                       {
+                               $$ = payload_expr_alloc(&@$, &payload_sctp, $2);
+                       }
+                       ;
+
+sctp_hdr_field         :       SPORT           { $$ = SCTPHDR_SPORT; }
+                       |       DPORT           { $$ = SCTPHDR_DPORT; }
+                       |       VTAG            { $$ = SCTPHDR_VTAG; }
+                       |       CHECKSUM        { $$ = SCTPHDR_CHECKSUM; }
+                       ;
+
+exthdr_expr            :       hbh_hdr_expr
+                       |       rt_hdr_expr
+                       |       rt0_hdr_expr
+                       |       rt2_hdr_expr
+                       |       frag_hdr_expr
+                       |       dst_hdr_expr
+                       |       mh_hdr_expr
+                       ;
+
+hbh_hdr_expr           :       HBH     hbh_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2);
+                       }
+                       ;
+
+hbh_hdr_field          :       NEXTHDR         { $$ = HBHHDR_NEXTHDR; }
+                       |       HDRLENGTH       { $$ = HBHHDR_HDRLENGTH; }
+                       ;
+
+rt_hdr_expr            :       RT      rt_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_rt, $2);
+                       }
+                       ;
+
+rt_hdr_field           :       NEXTHDR         { $$ = RTHDR_NEXTHDR; }
+                       |       HDRLENGTH       { $$ = RTHDR_HDRLENGTH; }
+                       |       TYPE            { $$ = RTHDR_TYPE; }
+                       |       SEG_LEFT        { $$ = RTHDR_SEG_LEFT; }
+                       ;
+
+rt0_hdr_expr           :       RT0     rt0_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2);
+                       }
+                       ;
+
+rt0_hdr_field          :       ADDR    '['     NUM     ']'
+                       {
+                               $$ = RT0HDR_ADDR_1 + $3 - 1;
+                       }
+                       ;
+
+rt2_hdr_expr           :       RT2     rt2_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2);
+                       }
+                       ;
+
+rt2_hdr_field          :       ADDR            { $$ = RT2HDR_ADDR; }
+                       ;
+
+frag_hdr_expr          :       FRAG    frag_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_frag, $2);
+                       }
+                       ;
+
+frag_hdr_field         :       NEXTHDR         { $$ = FRAGHDR_NEXTHDR; }
+                       |       RESERVED        { $$ = FRAGHDR_RESERVED; }
+                       |       FRAG_OFF        { $$ = FRAGHDR_FRAG_OFF; }
+                       |       RESERVED2       { $$ = FRAGHDR_RESERVED2; }
+                       |       MORE_FRAGMENTS  { $$ = FRAGHDR_MFRAGS; }
+                       |       ID              { $$ = FRAGHDR_ID; }
+                       ;
+
+dst_hdr_expr           :       DST     dst_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2);
+                       }
+                       ;
+
+dst_hdr_field          :       NEXTHDR         { $$ = DSTHDR_NEXTHDR; }
+                       |       HDRLENGTH       { $$ = DSTHDR_HDRLENGTH; }
+                       ;
+
+mh_hdr_expr            :       MH      mh_hdr_field
+                       {
+                               $$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2);
+                       }
+                       ;
+
+mh_hdr_field           :       NEXTHDR         { $$ = MHHDR_NEXTHDR; }
+                       |       HDRLENGTH       { $$ = MHHDR_HDRLENGTH; }
+                       |       TYPE            { $$ = MHHDR_TYPE; }
+                       |       RESERVED        { $$ = MHHDR_RESERVED; }
+                       |       CHECKSUM        { $$ = MHHDR_CHECKSUM; }
+                       ;
+
+%%
diff --git a/src/payload.c b/src/payload.c
new file mode 100644 (file)
index 0000000..b7fbcb3
--- /dev/null
@@ -0,0 +1,908 @@
+/*
+ * Payload expression protocol and type definitions and related functions.
+ *
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+
+#include <rule.h>
+#include <expression.h>
+#include <payload.h>
+#include <headers.h>
+#include <gmputil.h>
+#include <utils.h>
+
+static const char *payload_base_names[] = {
+       [PAYLOAD_BASE_INVALID]          = "invalid",
+       [PAYLOAD_BASE_LL_HDR]           = "link layer",
+       [PAYLOAD_BASE_NETWORK_HDR]      = "network layer",
+       [PAYLOAD_BASE_TRANSPORT_HDR]    = "transport layer",
+};
+
+static const char *payload_base_tokens[] = {
+       [PAYLOAD_BASE_INVALID]          = "invalid",
+       [PAYLOAD_BASE_LL_HDR]           = "ll",
+       [PAYLOAD_BASE_NETWORK_HDR]      = "nh",
+       [PAYLOAD_BASE_TRANSPORT_HDR]    = "th",
+};
+
+static const struct payload_template payload_unknown_template =
+       PAYLOAD_TEMPLATE("unknown", &invalid_type, 0, 0);
+
+static const struct payload_desc payload_unknown_desc = {
+       .name           = "unknown",
+       .base           = PAYLOAD_BASE_INVALID,
+};
+
+static void payload_expr_print(const struct expr *expr)
+{
+       const struct payload_desc *desc;
+       const struct payload_template *tmpl;
+
+       desc = expr->payload.desc;
+       tmpl = expr->payload.tmpl;
+       if (desc != NULL && tmpl != NULL)
+               printf("%s %s", desc->name, tmpl->token);
+       else
+               printf("payload @%s,%u,%u",
+                      payload_base_tokens[expr->payload.base],
+                      expr->payload.offset, expr->len);
+}
+
+static const struct expr_ops payload_expr_ops = {
+       .type           = EXPR_PAYLOAD,
+       .name           = "payload",
+       .print          = payload_expr_print,
+};
+
+struct expr *payload_expr_alloc(const struct location *loc,
+                               const struct payload_desc *desc,
+                               unsigned int type)
+{
+       const struct payload_template *tmpl;
+       enum payload_bases base;
+       struct expr *expr;
+       unsigned int flags = 0;
+
+       if (desc != NULL) {
+               tmpl = &desc->templates[type];
+               base = desc->base;
+               if (type == desc->protocol_key)
+                       flags = PAYLOAD_PROTOCOL_EXPR;
+       } else {
+               tmpl = &payload_unknown_template;
+               base = PAYLOAD_BASE_INVALID;
+       }
+
+       expr = expr_alloc(loc, &payload_expr_ops, tmpl->dtype,
+                         BYTEORDER_BIG_ENDIAN, tmpl->len);
+       expr->payload.desc   = desc;
+       expr->payload.tmpl   = tmpl;
+       expr->payload.base   = base;
+       expr->payload.offset = tmpl->offset;
+       expr->payload.flags  = flags;
+       return expr;
+}
+
+void payload_init_raw(struct expr *expr, enum payload_bases base,
+                     unsigned int offset, unsigned int len)
+{
+       expr->payload.base      = base;
+       expr->payload.offset    = offset;
+       expr->len               = len;
+}
+
+/**
+ * payload_select_proto - find protocol description by protocol value linking
+ *                       it to lower layer protocol
+ *
+ * @base:      lower layer protocol description
+ * @num:       protocol value
+ */
+static const struct payload_desc *
+payload_select_proto(const struct payload_desc *base, unsigned int num)
+{
+       unsigned int i;
+
+       for (i = 0; i < array_size(base->protocols); i++) {
+               if (base->protocols[i].num == num)
+                       return base->protocols[i].desc;
+       }
+       return NULL;
+}
+
+/**
+ * payload_proto_val - return protocol number linking two protocols together
+ *
+ * @base:      lower layer protocol description
+ * @desc:      upper layer protocol description
+ */
+static unsigned int payload_proto_val(const struct payload_desc *base,
+                                     const struct payload_desc *desc)
+{
+       unsigned int i;
+
+       for (i = 0; i < array_size(base->protocols); i++) {
+               if (base->protocols[i].desc == desc)
+                       return base->protocols[i].num;
+       }
+       return 0;
+}
+
+static const struct dev_payload_desc dev_payload_desc[] = {
+       DEV_PAYLOAD_DESC(ARPHRD_ETHER, &payload_eth),
+};
+
+/**
+ * payload_dev_type - return arphrd type linking a device and a protocol together
+ *
+ * @desc:      the protocol description
+ * @res:       pointer to result
+ */
+static int payload_dev_type(const struct payload_desc *desc, uint16_t *res)
+{
+       unsigned int i;
+
+       for (i = 0; i < array_size(dev_payload_desc); i++) {
+               if (dev_payload_desc[i].desc == desc) {
+                       *res = dev_payload_desc[i].type;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/**
+ * payload_dev_desc - return protocol description for an arphrd type
+ *
+ * @type:      the arphrd type
+ */
+static const struct payload_desc *payload_dev_desc(uint16_t type)
+{
+       unsigned int i;
+
+       for (i = 0; i < array_size(dev_payload_desc); i++) {
+               if (dev_payload_desc[i].type == type)
+                       return dev_payload_desc[i].desc;
+       }
+       return NULL;
+}
+
+static const struct payload_hook_desc payload_hooks[] = {
+       [NFPROTO_BRIDGE]        = PAYLOAD_HOOK(PAYLOAD_BASE_LL_HDR, &payload_eth),
+       [NFPROTO_IPV4]          = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_ip),
+       [NFPROTO_IPV6]          = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_ip6),
+       [NFPROTO_ARP]           = PAYLOAD_HOOK(PAYLOAD_BASE_NETWORK_HDR, &payload_arp),
+};
+
+/**
+ * payload_ctx_init - initialize payload context for a given hook family
+ *
+ * @ctx:       payload context
+ * @family:    hook family
+ */
+void payload_ctx_init(struct payload_ctx *ctx, unsigned int family)
+{
+       const struct payload_hook_desc *h = &payload_hooks[family];
+
+       memset(ctx, 0, sizeof(*ctx));
+       ctx->family = family;
+       ctx->protocol[h->base].desc = h->desc;
+}
+
+/**
+ * payload_ctx_update_meta - update payload context with meta expression
+ *
+ * @ctx:       payload context
+ * @expr:      relational meta expression
+ *
+ * Update LL payload context based on IIFTYPE meta match in non-LL hooks.
+ */
+void payload_ctx_update_meta(struct payload_ctx *ctx, const struct expr *expr)
+{
+       const struct payload_hook_desc *h = &payload_hooks[ctx->family];
+       const struct expr *left = expr->left, *right = expr->right;
+       const struct payload_desc *desc;
+
+       if (left->meta.key != NFT_META_IIFTYPE)
+               return;
+
+       assert(expr->op == OP_EQ);
+       if (h->base < PAYLOAD_BASE_NETWORK_HDR)
+               return;
+
+       desc = payload_dev_desc(mpz_get_uint16(right->value));
+       if (desc == NULL)
+               desc = &payload_unknown_desc;
+
+       ctx->protocol[PAYLOAD_BASE_LL_HDR].location = expr->location;
+       ctx->protocol[PAYLOAD_BASE_LL_HDR].desc = desc;
+}
+
+/**
+ * payload_ctx_update - update payload context
+ *
+ * @ctx:       payload context
+ * @expr:      relational payload expression
+ *
+ * Update payload context for relational payload expressions.
+ */
+void payload_ctx_update(struct payload_ctx *ctx, const struct expr *expr)
+{
+       const struct expr *left = expr->left, *right = expr->right;
+       const struct payload_desc *base, *desc;
+
+       if (!(left->payload.flags & PAYLOAD_PROTOCOL_EXPR))
+               return;
+
+       assert(expr->op == OP_EQ);
+       base = ctx->protocol[left->payload.base].desc;
+       desc = payload_select_proto(base, mpz_get_uint32(right->value));
+
+       ctx->protocol[left->payload.base + 1].location = expr->location;
+       ctx->protocol[left->payload.base + 1].desc = desc;
+}
+
+/**
+ * payload_gen_dependency - generate match expression on payload dependency
+ *
+ * @ctx:       evaluation context
+ * @expr:      payload expression
+ * @res:       dependency expression
+ *
+ * Generate matches on protocol dependencies. There are two different kinds
+ * of dependencies:
+ *
+ * - A payload expression for a base above the hook base requires a match
+ *   on the protocol value in the lower layer header.
+ *
+ * - A payload expression for a base below the hook base is invalid in the
+ *   output path since the lower layer header does not exist when the packet
+ *   is classified. In the input path a payload expressions for a base exactly
+ *   one below the hook base is valid. In this case a match on the device type
+ *   is required to verify that we're dealing with the expected protocol.
+ *
+ *   Note: since it is unknown to userspace which hooks a chain is called from,
+ *   it is not explicitly verified. The NFT_META_IIFTYPE match will only match
+ *   in the input path though.
+ */
+int payload_gen_dependency(struct eval_ctx *ctx, const struct expr *expr,
+                          struct expr **res)
+{
+       const struct payload_hook_desc *h = &payload_hooks[ctx->pctx.family];
+       const struct payload_desc *desc;
+       const struct payload_template *tmpl;
+       struct expr *dep, *left, *right;
+       unsigned int protocol;
+       uint16_t type;
+
+       if (expr->payload.base < h->base) {
+               if (expr->payload.base < h->base - 1)
+                       return expr_error(ctx, expr,
+                                         "payload base is invalid for this "
+                                         "family");
+
+               if (payload_dev_type(expr->payload.desc, &type) < 0)
+                       return expr_error(ctx, expr,
+                                         "protocol specification is invalid "
+                                         "for this family");
+
+               left = meta_expr_alloc(&expr->location, NFT_META_IIFTYPE);
+               right = constant_expr_alloc(&expr->location, &arphrd_type,
+                                           BYTEORDER_HOST_ENDIAN,
+                                           2 * BITS_PER_BYTE, &type);
+
+               dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+               *res = dep;
+               return 0;
+       }
+
+       desc = ctx->pctx.protocol[expr->payload.base - 1].desc;
+       if (desc == NULL)
+               return expr_error(ctx, expr,
+                                 "ambiguous payload specification: "
+                                 "no %s protocol specified",
+                                 payload_base_names[expr->payload.base - 1]);
+
+       tmpl = &desc->templates[desc->protocol_key];
+       left = payload_expr_alloc(&expr->location, desc, desc->protocol_key);
+       protocol = payload_proto_val(desc, expr->payload.desc);
+       right = constant_expr_alloc(&expr->location, tmpl->dtype,
+                                   BYTEORDER_HOST_ENDIAN,
+                                   tmpl->len, &protocol);
+
+       dep = relational_expr_alloc(&expr->location, OP_EQ, left, right);
+       payload_ctx_update(&ctx->pctx, dep);
+       *res = dep;
+       return 0;
+}
+
+/**
+ * payload_expr_complete - fill in type information of a raw payload expr
+ *
+ * @expr:      the payload expression
+ * @ctx:       payload context
+ *
+ * Complete the type of a raw payload expression based on the context. If
+ * insufficient information is available the expression remains unchanged.
+ */
+void payload_expr_complete(struct expr *expr, const struct payload_ctx *ctx)
+{
+       const struct payload_desc *desc;
+       const struct payload_template *tmpl;
+       unsigned int i;
+
+       assert(expr->ops->type == EXPR_PAYLOAD);
+
+       desc = ctx->protocol[expr->payload.base].desc;
+       if (desc == NULL)
+               return;
+       assert(desc->base == expr->payload.base);
+
+       for (i = 0; i < array_size(desc->templates); i++) {
+               tmpl = &desc->templates[i];
+               if (tmpl->offset != expr->payload.offset ||
+                   tmpl->len    != expr->len)
+                       continue;
+               expr->dtype        = tmpl->dtype;
+               expr->payload.desc = desc;
+               expr->payload.tmpl = tmpl;
+               return;
+       }
+}
+
+/**
+ * payload_expr_expand - expand raw merged adjacent payload expressions into its
+ *                      original components
+ *
+ * @list:      list to append expanded payload expressions to
+ * @expr:      the payload expression to expand
+ * @ctx:       payload context
+ *
+ * Expand a merged adjacent payload expression into its original components
+ * by splitting elements off the beginning matching a payload template.
+ *
+ * Note: this requires all payload templates to be specified in ascending
+ *      offset order.
+ */
+void payload_expr_expand(struct list_head *list, struct expr *expr,
+                        const struct payload_ctx *ctx)
+{
+       const struct payload_desc *desc;
+       const struct payload_template *tmpl;
+       struct expr *new;
+       unsigned int i;
+
+       assert(expr->ops->type == EXPR_PAYLOAD);
+
+       desc = ctx->protocol[expr->payload.base].desc;
+       if (desc == NULL)
+               goto raw;
+       assert(desc->base == expr->payload.base);
+
+       for (i = 1; i < array_size(desc->templates); i++) {
+               tmpl = &desc->templates[i];
+               if (tmpl->offset != expr->payload.offset)
+                       continue;
+
+               if (tmpl->len <= expr->len) {
+                       new = payload_expr_alloc(&expr->location, desc, i);
+                       list_add_tail(&new->list, list);
+                       expr->len            -= tmpl->len;
+                       expr->payload.offset += tmpl->len;
+                       if (expr->len == 0)
+                               return;
+               } else
+                       break;
+       }
+raw:
+       new = payload_expr_alloc(&expr->location, NULL, 0);
+       payload_init_raw(new, expr->payload.base, expr->payload.offset,
+                        expr->len);
+       list_add_tail(&new->list, list);
+}
+
+/**
+ * payload_is_adjacent - return whether two payload expressions refer to
+ *                      adjacent header locations
+ *
+ * @e1:                first payload expression
+ * @e2:                second payload expression
+ */
+bool payload_is_adjacent(const struct expr *e1, const struct expr *e2)
+{
+       if (e1->payload.base             == e2->payload.base &&
+           e1->payload.offset + e1->len == e2->payload.offset)
+               return true;
+       return false;
+}
+
+/**
+ * payload_expr_join - join two adjacent payload expressions
+ *
+ * @e1:                first payload expression
+ * @e2:                second payload expression
+ */
+struct expr *payload_expr_join(const struct expr *e1, const struct expr *e2)
+{
+       struct expr *expr;
+
+       assert(payload_is_adjacent(e1, e2));
+
+       expr = payload_expr_alloc(&internal_location, NULL, 0);
+       expr->payload.base   = e1->payload.base;
+       expr->payload.offset = e1->payload.offset;
+       expr->len            = e1->len + e2->len;
+       return expr;
+}
+
+#define HDR_TEMPLATE(__name, __dtype, __type, __member)                        \
+       PAYLOAD_TEMPLATE(__name, __dtype,                               \
+                        offsetof(__type, __member) * 8,                \
+                        field_sizeof(__type, __member) * 8)
+
+#define HDR_FIELD(__name, __struct, __member)                          \
+       HDR_TEMPLATE(__name, &integer_type, __struct, __member)
+#define HDR_BITFIELD(__name, __dtype,  __offset, __len)                        \
+       PAYLOAD_TEMPLATE(__name, __dtype, __offset, __len)
+#define HDR_TYPE(__name, __dtype, __struct, __member)                  \
+       HDR_TEMPLATE(__name, __dtype, __struct, __member)
+
+#define INET_PROTOCOL(__name, __struct, __member)                      \
+       HDR_TYPE(__name, &inet_protocol_type, __struct, __member)
+#define INET_SERVICE(__name, __struct, __member)                       \
+       HDR_TYPE(__name, &inet_service_type, __struct, __member)
+
+/*
+ * AH
+ */
+
+#define AHHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct ip_auth_hdr, __member)
+
+const struct payload_desc payload_ah = {
+       .name           = "ah",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .protocol_key   = AHHDR_NEXTHDR,
+       .protocols      = {
+               PAYLOAD_PROTO(IPPROTO_ESP,      &payload_esp),
+               PAYLOAD_PROTO(IPPROTO_AH,       &payload_ah),
+               PAYLOAD_PROTO(IPPROTO_COMP,     &payload_comp),
+               PAYLOAD_PROTO(IPPROTO_UDP,      &payload_udp),
+               PAYLOAD_PROTO(IPPROTO_UDPLITE,  &payload_udplite),
+               PAYLOAD_PROTO(IPPROTO_TCP,      &payload_tcp),
+               PAYLOAD_PROTO(IPPROTO_DCCP,     &payload_dccp),
+               PAYLOAD_PROTO(IPPROTO_SCTP,     &payload_sctp),
+       },
+       .templates      = {
+               [AHHDR_NEXTHDR]         = INET_PROTOCOL("nexthdr", struct ip_auth_hdr, nexthdr),
+               [AHHDR_HDRLENGTH]       = AHHDR_FIELD("hdrlength", hdrlen),
+               [AHHDR_RESERVED]        = AHHDR_FIELD("reserved", reserved),
+               [AHHDR_SPI]             = AHHDR_FIELD("spi", spi),
+               [AHHDR_SEQUENCE]        = AHHDR_FIELD("sequence", seq_no),
+       },
+};
+
+/*
+ * ESP
+ */
+
+#define ESPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct ip_esp_hdr, __member)
+
+const struct payload_desc payload_esp = {
+       .name           = "esp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [ESPHDR_SPI]            = ESPHDR_FIELD("spi", spi),
+               [ESPHDR_SEQUENCE]       = ESPHDR_FIELD("sequence", seq_no),
+       },
+};
+
+/*
+ * IPCOMP
+ */
+
+#define COMPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct ip_comp_hdr, __member)
+
+const struct payload_desc payload_comp = {
+       .name           = "comp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .protocol_key   = COMPHDR_NEXTHDR,
+       .protocols      = {
+               PAYLOAD_PROTO(IPPROTO_ESP,      &payload_esp),
+               PAYLOAD_PROTO(IPPROTO_AH,       &payload_ah),
+               PAYLOAD_PROTO(IPPROTO_COMP,     &payload_comp),
+               PAYLOAD_PROTO(IPPROTO_UDP,      &payload_udp),
+               PAYLOAD_PROTO(IPPROTO_UDPLITE,  &payload_udplite),
+               PAYLOAD_PROTO(IPPROTO_TCP,      &payload_tcp),
+               PAYLOAD_PROTO(IPPROTO_DCCP,     &payload_dccp),
+               PAYLOAD_PROTO(IPPROTO_SCTP,     &payload_sctp),
+       },
+       .templates      = {
+               [COMPHDR_NEXTHDR]       = INET_PROTOCOL("nexthdr", struct ip_comp_hdr, nexthdr),
+               [COMPHDR_FLAGS]         = COMPHDR_FIELD("flags", flags),
+               [COMPHDR_CPI]           = COMPHDR_FIELD("cpi", cpi),
+       },
+};
+
+/*
+ * ICMP
+ */
+
+#include <netinet/ip_icmp.h>
+
+static const struct symbol_table icmp_type_tbl = {
+       .byteorder      = BYTEORDER_BIG_ENDIAN,
+       .size           = BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("echo-reply",                    ICMP_ECHOREPLY),
+               SYMBOL("destination-unreachable",       ICMP_DEST_UNREACH),
+               SYMBOL("source-quench",                 ICMP_SOURCE_QUENCH),
+               SYMBOL("redirect",                      ICMP_REDIRECT),
+               SYMBOL("echo-request",                  ICMP_ECHO),
+               SYMBOL("time-exceeded",                 ICMP_TIME_EXCEEDED),
+               SYMBOL("parameter-problem",             ICMP_PARAMETERPROB),
+               SYMBOL("timestamp-request",             ICMP_TIMESTAMP),
+               SYMBOL("timestamp-reply",               ICMP_TIMESTAMPREPLY),
+               SYMBOL("info-request",                  ICMP_INFO_REQUEST),
+               SYMBOL("info-reply",                    ICMP_INFO_REPLY),
+               SYMBOL("address-mask-request",          ICMP_ADDRESS),
+               SYMBOL("address-mask-reply",            ICMP_ADDRESSREPLY),
+               SYMBOL_LIST_END
+       },
+};
+
+static const struct datatype icmp_type_type = {
+       .type           = TYPE_ICMP_TYPE,
+       .name           = "ICMP type",
+       .basetype       = &integer_type,
+       .sym_tbl        = &icmp_type_tbl,
+};
+
+#define ICMPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct icmphdr, __member)
+#define ICMPHDR_TYPE(__name, __type, __member) \
+       HDR_TYPE(__name, __type, struct icmphdr, __member)
+
+const struct payload_desc payload_icmp = {
+       .name           = "icmp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [ICMPHDR_TYPE]          = ICMPHDR_TYPE("type", &icmp_type_type, type),
+               [ICMPHDR_CODE]          = ICMPHDR_FIELD("code", code),
+               [ICMPHDR_CHECKSUM]      = ICMPHDR_FIELD("checksum", checksum),
+               [ICMPHDR_ID]            = ICMPHDR_FIELD("id", un.echo.id),
+               [ICMPHDR_SEQ]           = ICMPHDR_FIELD("sequence", un.echo.sequence),
+               [ICMPHDR_GATEWAY]       = ICMPHDR_FIELD("gateway", un.gateway),
+               [ICMPHDR_MTU]           = ICMPHDR_FIELD("mtu", un.frag.mtu),
+       },
+};
+
+/*
+ * UDP/UDP-Lite
+ */
+
+#include <netinet/udp.h>
+#define UDPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct udphdr, __member)
+
+const struct payload_desc payload_udp = {
+       .name           = "udp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [UDPHDR_SPORT]          = INET_SERVICE("sport", struct udphdr, source),
+               [UDPHDR_DPORT]          = INET_SERVICE("dport", struct udphdr, dest),
+               [UDPHDR_LENGTH]         = UDPHDR_FIELD("length", len),
+               [UDPHDR_CHECKSUM]       = UDPHDR_FIELD("checksum", check),
+       },
+};
+
+const struct payload_desc payload_udplite = {
+       .name           = "udplite",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [UDPHDR_SPORT]          = INET_SERVICE("sport", struct udphdr, source),
+               [UDPHDR_DPORT]          = INET_SERVICE("dport", struct udphdr, dest),
+               [UDPHDR_CSUMCOV]        = UDPHDR_FIELD("csumcov", len),
+               [UDPHDR_CHECKSUM]       = UDPHDR_FIELD("checksum", check),
+       },
+};
+
+/*
+ * TCP
+ */
+
+#include <netinet/tcp.h>
+
+static const struct symbol_table tcp_flag_tbl = {
+       .byteorder      = BYTEORDER_BIG_ENDIAN,
+       .size           = BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("fin",   TCP_FLAG_FIN),
+               SYMBOL("syn",   TCP_FLAG_SYN),
+               SYMBOL("rst",   TCP_FLAG_RST),
+               SYMBOL("psh",   TCP_FLAG_PSH),
+               SYMBOL("ack",   TCP_FLAG_ACK),
+               SYMBOL("urh",   TCP_FLAG_URG),
+               SYMBOL("ecn",   TCP_FLAG_ECN),
+               SYMBOL("cwr",   TCP_FLAG_CWR),
+               SYMBOL_LIST_END
+       },
+};
+
+static const struct datatype tcp_flag_type = {
+       .type           = TYPE_TCP_FLAG,
+       .name           = "TCP flag",
+       .basetype       = &bitmask_type,
+       .sym_tbl        = &tcp_flag_tbl,
+};
+
+#define TCPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct tcphdr, __member)
+
+const struct payload_desc payload_tcp = {
+       .name           = "tcp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [TCPHDR_SPORT]          = INET_SERVICE("sport", struct tcphdr, source),
+               [TCPHDR_DPORT]          = INET_SERVICE("dport", struct tcphdr, dest),
+               [TCPHDR_SEQ]            = TCPHDR_FIELD("sequence", seq),
+               [TCPHDR_ACKSEQ]         = TCPHDR_FIELD("ackseq", ack_seq),
+               [TCPHDR_DOFF]           = {},
+               [TCPHDR_RESERVED]       = {},
+               [TCPHDR_FLAGS]          = HDR_BITFIELD("flags", &tcp_flag_type,
+                                                      13 * BITS_PER_BYTE,
+                                                      BITS_PER_BYTE),
+               [TCPHDR_WINDOW]         = TCPHDR_FIELD("window", window),
+               [TCPHDR_CHECKSUM]       = TCPHDR_FIELD("checksum", check),
+               [TCPHDR_URGPTR]         = TCPHDR_FIELD("urgptr", urg_ptr),
+       },
+};
+
+/*
+ * DCCP
+ */
+
+#define DCCPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct dccp_hdr, __member)
+
+const struct payload_desc payload_dccp = {
+       .name           = "dccp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [DCCPHDR_SPORT]         = INET_SERVICE("sport", struct dccp_hdr, dccph_sport),
+               [DCCPHDR_DPORT]         = INET_SERVICE("dport", struct dccp_hdr, dccph_dport),
+       },
+};
+
+/*
+ * SCTP
+ */
+
+#define SCTPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct sctphdr, __member)
+
+const struct payload_desc payload_sctp = {
+       .name           = "sctp",
+       .base           = PAYLOAD_BASE_TRANSPORT_HDR,
+       .templates      = {
+               [SCTPHDR_SPORT]         = INET_SERVICE("sport", struct sctphdr, source),
+               [SCTPHDR_DPORT]         = INET_SERVICE("dport", struct sctphdr, dest),
+               [SCTPHDR_VTAG]          = SCTPHDR_FIELD("vtag", vtag),
+               [SCTPHDR_CHECKSUM]      = SCTPHDR_FIELD("checksum", checksum),
+       },
+};
+
+/*
+ * IPv4
+ */
+
+#include <netinet/ip.h>
+#define IPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct iphdr, __member)
+#define IPHDR_ADDR(__name, __member) \
+       HDR_TYPE(__name, &ipaddr_type, struct iphdr, __member)
+
+const struct payload_desc payload_ip = {
+       .name           = "ip",
+       .base           = PAYLOAD_BASE_NETWORK_HDR,
+       .protocol_key   = IPHDR_PROTOCOL,
+       .protocols      = {
+               PAYLOAD_PROTO(IPPROTO_ICMP,     &payload_icmp),
+               PAYLOAD_PROTO(IPPROTO_ESP,      &payload_esp),
+               PAYLOAD_PROTO(IPPROTO_AH,       &payload_ah),
+               PAYLOAD_PROTO(IPPROTO_COMP,     &payload_comp),
+               PAYLOAD_PROTO(IPPROTO_UDP,      &payload_udp),
+               PAYLOAD_PROTO(IPPROTO_UDPLITE,  &payload_udplite),
+               PAYLOAD_PROTO(IPPROTO_TCP,      &payload_tcp),
+               PAYLOAD_PROTO(IPPROTO_DCCP,     &payload_dccp),
+               PAYLOAD_PROTO(IPPROTO_SCTP,     &payload_sctp),
+       },
+       .templates      = {
+               [IPHDR_VERSION]         = HDR_BITFIELD("version", &integer_type, 0, 4),
+               [IPHDR_HDRLENGTH]       = HDR_BITFIELD("hdrlength", &integer_type, 4, 4),
+               [IPHDR_TOS]             = IPHDR_FIELD("tos",            tos),
+               [IPHDR_LENGTH]          = IPHDR_FIELD("length",         tot_len),
+               [IPHDR_ID]              = IPHDR_FIELD("id",             id),
+               [IPHDR_FRAG_OFF]        = IPHDR_FIELD("frag-off",       frag_off),
+               [IPHDR_TTL]             = IPHDR_FIELD("ttl",            ttl),
+               [IPHDR_PROTOCOL]        = INET_PROTOCOL("protocol", struct iphdr, protocol),
+               [IPHDR_CHECKSUM]        = IPHDR_FIELD("checksum",       check),
+               [IPHDR_SADDR]           = IPHDR_ADDR("saddr",           saddr),
+               [IPHDR_DADDR]           = IPHDR_ADDR("daddr",           daddr),
+       },
+};
+
+/*
+ * IPv6
+ */
+
+#define IP6HDR_FIELD(__name,  __member) \
+       HDR_FIELD(__name, struct ipv6hdr, __member)
+#define IP6HDR_ADDR(__name, __member) \
+       HDR_TYPE(__name, &ip6addr_type, struct ipv6hdr, __member)
+#define IP6HDR_PROTOCOL(__name, __member) \
+       HDR_TYPE(__name, &inet_service_type, struct ipv6hdr, __member)
+
+const struct payload_desc payload_ip6 = {
+       .name           = "ip6",
+       .base           = PAYLOAD_BASE_NETWORK_HDR,
+       .protocol_key   = IP6HDR_NEXTHDR,
+       .protocols      = {
+               PAYLOAD_PROTO(IPPROTO_ESP,      &payload_esp),
+               PAYLOAD_PROTO(IPPROTO_AH,       &payload_ah),
+               PAYLOAD_PROTO(IPPROTO_COMP,     &payload_comp),
+               PAYLOAD_PROTO(IPPROTO_UDP,      &payload_udp),
+               PAYLOAD_PROTO(IPPROTO_UDPLITE,  &payload_udplite),
+               PAYLOAD_PROTO(IPPROTO_TCP,      &payload_tcp),
+               PAYLOAD_PROTO(IPPROTO_DCCP,     &payload_dccp),
+               PAYLOAD_PROTO(IPPROTO_SCTP,     &payload_sctp),
+       },
+       .templates      = {
+               [IP6HDR_VERSION]        = HDR_BITFIELD("version", &integer_type, 0, 4),
+               [IP6HDR_PRIORITY]       = HDR_BITFIELD("priority", &integer_type, 4, 4),
+               [IP6HDR_FLOWLABEL]      = IP6HDR_FIELD("flowlabel",     flow_lbl),
+               [IP6HDR_LENGTH]         = IP6HDR_FIELD("length",        payload_len),
+               [IP6HDR_NEXTHDR]        = INET_PROTOCOL("nexthdr", struct ipv6hdr, nexthdr),
+               [IP6HDR_HOPLIMIT]       = IP6HDR_FIELD("hoplimit",      hop_limit),
+               [IP6HDR_SADDR]          = IP6HDR_ADDR("saddr",          saddr),
+               [IP6HDR_DADDR]          = IP6HDR_ADDR("daddr",          daddr),
+       },
+};
+
+/*
+ * ARP
+ */
+
+#include <net/if_arp.h>
+
+static const struct symbol_table arpop_tbl = {
+       .byteorder      = BYTEORDER_HOST_ENDIAN,
+       .size           = 2 * BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("request",       ARPOP_REQUEST),
+               SYMBOL("reply",         ARPOP_REPLY),
+               SYMBOL("rrequest",      ARPOP_RREQUEST),
+               SYMBOL("rreply",        ARPOP_REPLY),
+               SYMBOL("inrequest",     ARPOP_InREQUEST),
+               SYMBOL("inreply",       ARPOP_InREPLY),
+               SYMBOL("nak",           ARPOP_NAK),
+       },
+};
+
+static const struct datatype arpop_type = {
+       .type           = TYPE_ARPOP,
+       .name           = "ARP operation",
+       .basetype       = &integer_type,
+       .sym_tbl        = &arpop_tbl,
+};
+
+#define ARPHDR_TYPE(__name, __type, __member) \
+       HDR_TYPE(__name, __type, struct arphdr, __member)
+#define ARPHDR_FIELD(__name, __member) \
+       HDR_FIELD(__name, struct arphdr, __member)
+
+const struct payload_desc payload_arp = {
+       .name           = "arp",
+       .base           = PAYLOAD_BASE_NETWORK_HDR,
+       .templates      = {
+               [ARPHDR_HRD]            = ARPHDR_FIELD("htype", ar_hrd),
+               [ARPHDR_PRO]            = ARPHDR_TYPE("ptype", &ethertype_type, ar_pro),
+               [ARPHDR_HLN]            = ARPHDR_FIELD("hlen", ar_hln),
+               [ARPHDR_PLN]            = ARPHDR_FIELD("plen", ar_pln),
+               [ARPHDR_OP]             = ARPHDR_TYPE("operation", &arpop_type, ar_op),
+       },
+};
+
+/*
+ * VLAN
+ */
+
+#include <net/ethernet.h>
+
+#define VLANHDR_BITFIELD(__name, __offset, __len) \
+       HDR_BITFIELD(__name, &integer_type, __offset, __len)
+#define VLANHDR_TYPE(__name, __type, __member) \
+       HDR_TYPE(__name, __type, struct vlan_hdr, __member)
+
+const struct payload_desc payload_vlan = {
+       .name           = "vlan",
+       .base           = PAYLOAD_BASE_LL_HDR,
+       .protocol_key   = VLANHDR_TYPE,
+       .protocols      = {
+               PAYLOAD_PROTO(ETH_P_IP,         &payload_ip),
+               PAYLOAD_PROTO(ETH_P_ARP,        &payload_arp),
+               PAYLOAD_PROTO(ETH_P_IPV6,       &payload_ip6),
+               PAYLOAD_PROTO(ETH_P_8021Q,      &payload_vlan),
+
+       },
+       .templates      = {
+               [VLANHDR_VID]           = VLANHDR_BITFIELD("id", 0, 12),
+               [VLANHDR_CFI]           = VLANHDR_BITFIELD("cfi", 12, 1),
+               [VLANHDR_PCP]           = VLANHDR_BITFIELD("pcp", 13, 3),
+               [VLANHDR_TYPE]          = VLANHDR_TYPE("type", &ethertype_type, vlan_type),
+       },
+};
+
+/*
+ * Ethernet
+ */
+
+static const struct symbol_table ethertype_tbl = {
+       .byteorder      = BYTEORDER_HOST_ENDIAN,
+       .size           = 2 * BITS_PER_BYTE,
+       .symbols        = {
+               SYMBOL("ip",            ETH_P_IP),
+               SYMBOL("arp",           ETH_P_ARP),
+               SYMBOL("ipv6",          ETH_P_IPV6),
+               SYMBOL("vlan",          ETH_P_8021Q),
+               SYMBOL_LIST_END
+       },
+};
+
+const struct datatype ethertype_type = {
+       .type           = TYPE_ETHERTYPE,
+       .name           = "Ethernet protocol",
+       .basetype       = &integer_type,
+       .sym_tbl        = &ethertype_tbl,
+};
+
+#define ETHHDR_TEMPLATE(__name, __dtype, __member) \
+       HDR_TEMPLATE(__name, __dtype, struct ether_header, __member)
+#define ETHHDR_TYPE(__name, __member) \
+       ETHHDR_TEMPLATE(__name, &ethertype_type, __member)
+#define ETHHDR_ADDR(__name, __member) \
+       ETHHDR_TEMPLATE(__name, &lladdr_type, __member)
+
+const struct payload_desc payload_eth = {
+       .name           = "eth",
+       .base           = PAYLOAD_BASE_LL_HDR,
+       .protocol_key   = ETHHDR_TYPE,
+       .protocols      = {
+               PAYLOAD_PROTO(ETH_P_IP,         &payload_ip),
+               PAYLOAD_PROTO(ETH_P_ARP,        &payload_arp),
+               PAYLOAD_PROTO(ETH_P_IPV6,       &payload_ip6),
+               PAYLOAD_PROTO(ETH_P_8021Q,      &payload_vlan),
+       },
+       .templates      = {
+               [ETHHDR_DADDR]          = ETHHDR_ADDR("daddr", ether_dhost),
+               [ETHHDR_SADDR]          = ETHHDR_ADDR("saddr", ether_shost),
+               [ETHHDR_TYPE]           = ETHHDR_TYPE("type", ether_type),
+       },
+};
diff --git a/src/rbtree.c b/src/rbtree.c
new file mode 100644 (file)
index 0000000..325c012
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Red Black Trees
+ * (C) 1999  Andrea Arcangeli <andrea@suse.de>
+ * (C) 2002  David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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
+ *
+ */
+
+#include <rbtree.h>
+
+static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
+{
+       struct rb_node *right = node->rb_right;
+       struct rb_node *parent = rb_parent(node);
+
+       if ((node->rb_right = right->rb_left))
+               rb_set_parent(right->rb_left, node);
+       right->rb_left = node;
+
+       rb_set_parent(right, parent);
+
+       if (parent)
+       {
+               if (node == parent->rb_left)
+                       parent->rb_left = right;
+               else
+                       parent->rb_right = right;
+       }
+       else
+               root->rb_node = right;
+       rb_set_parent(node, right);
+}
+
+static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
+{
+       struct rb_node *left = node->rb_left;
+       struct rb_node *parent = rb_parent(node);
+
+       if ((node->rb_left = left->rb_right))
+               rb_set_parent(left->rb_right, node);
+       left->rb_right = node;
+
+       rb_set_parent(left, parent);
+
+       if (parent)
+       {
+               if (node == parent->rb_right)
+                       parent->rb_right = left;
+               else
+                       parent->rb_left = left;
+       }
+       else
+               root->rb_node = left;
+       rb_set_parent(node, left);
+}
+
+void rb_insert_color(struct rb_node *node, struct rb_root *root)
+{
+       struct rb_node *parent, *gparent;
+
+       while ((parent = rb_parent(node)) && rb_is_red(parent))
+       {
+               gparent = rb_parent(parent);
+
+               if (parent == gparent->rb_left)
+               {
+                       {
+                               register struct rb_node *uncle = gparent->rb_right;
+                               if (uncle && rb_is_red(uncle))
+                               {
+                                       rb_set_black(uncle);
+                                       rb_set_black(parent);
+                                       rb_set_red(gparent);
+                                       node = gparent;
+                                       continue;
+                               }
+                       }
+
+                       if (parent->rb_right == node)
+                       {
+                               register struct rb_node *tmp;
+                               __rb_rotate_left(parent, root);
+                               tmp = parent;
+                               parent = node;
+                               node = tmp;
+                       }
+
+                       rb_set_black(parent);
+                       rb_set_red(gparent);
+                       __rb_rotate_right(gparent, root);
+               } else {
+                       {
+                               register struct rb_node *uncle = gparent->rb_left;
+                               if (uncle && rb_is_red(uncle))
+                               {
+                                       rb_set_black(uncle);
+                                       rb_set_black(parent);
+                                       rb_set_red(gparent);
+                                       node = gparent;
+                                       continue;
+                               }
+                       }
+
+                       if (parent->rb_left == node)
+                       {
+                               register struct rb_node *tmp;
+                               __rb_rotate_right(parent, root);
+                               tmp = parent;
+                               parent = node;
+                               node = tmp;
+                       }
+
+                       rb_set_black(parent);
+                       rb_set_red(gparent);
+                       __rb_rotate_left(gparent, root);
+               }
+       }
+
+       rb_set_black(root->rb_node);
+}
+
+static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,
+                            struct rb_root *root)
+{
+       struct rb_node *other;
+
+       while ((!node || rb_is_black(node)) && node != root->rb_node)
+       {
+               if (parent->rb_left == node)
+               {
+                       other = parent->rb_right;
+                       if (rb_is_red(other))
+                       {
+                               rb_set_black(other);
+                               rb_set_red(parent);
+                               __rb_rotate_left(parent, root);
+                               other = parent->rb_right;
+                       }
+                       if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+                           (!other->rb_right || rb_is_black(other->rb_right)))
+                       {
+                               rb_set_red(other);
+                               node = parent;
+                               parent = rb_parent(node);
+                       }
+                       else
+                       {
+                               if (!other->rb_right || rb_is_black(other->rb_right))
+                               {
+                                       struct rb_node *o_left;
+                                       if ((o_left = other->rb_left))
+                                               rb_set_black(o_left);
+                                       rb_set_red(other);
+                                       __rb_rotate_right(other, root);
+                                       other = parent->rb_right;
+                               }
+                               rb_set_color(other, rb_color(parent));
+                               rb_set_black(parent);
+                               if (other->rb_right)
+                                       rb_set_black(other->rb_right);
+                               __rb_rotate_left(parent, root);
+                               node = root->rb_node;
+                               break;
+                       }
+               }
+               else
+               {
+                       other = parent->rb_left;
+                       if (rb_is_red(other))
+                       {
+                               rb_set_black(other);
+                               rb_set_red(parent);
+                               __rb_rotate_right(parent, root);
+                               other = parent->rb_left;
+                       }
+                       if ((!other->rb_left || rb_is_black(other->rb_left)) &&
+                           (!other->rb_right || rb_is_black(other->rb_right)))
+                       {
+                               rb_set_red(other);
+                               node = parent;
+                               parent = rb_parent(node);
+                       }
+                       else
+                       {
+                               if (!other->rb_left || rb_is_black(other->rb_left))
+                               {
+                                       register struct rb_node *o_right;
+                                       if ((o_right = other->rb_right))
+                                               rb_set_black(o_right);
+                                       rb_set_red(other);
+                                       __rb_rotate_left(other, root);
+                                       other = parent->rb_left;
+                               }
+                               rb_set_color(other, rb_color(parent));
+                               rb_set_black(parent);
+                               if (other->rb_left)
+                                       rb_set_black(other->rb_left);
+                               __rb_rotate_right(parent, root);
+                               node = root->rb_node;
+                               break;
+                       }
+               }
+       }
+       if (node)
+               rb_set_black(node);
+}
+
+void rb_erase(struct rb_node *node, struct rb_root *root)
+{
+       struct rb_node *child, *parent;
+       int color;
+
+       if (!node->rb_left)
+               child = node->rb_right;
+       else if (!node->rb_right)
+               child = node->rb_left;
+       else
+       {
+               struct rb_node *old = node, *left;
+
+               node = node->rb_right;
+               while ((left = node->rb_left) != NULL)
+                       node = left;
+               child = node->rb_right;
+               parent = rb_parent(node);
+               color = rb_color(node);
+
+               if (child)
+                       rb_set_parent(child, parent);
+               if (parent == old) {
+                       parent->rb_right = child;
+                       parent = node;
+               } else
+                       parent->rb_left = child;
+
+               node->rb_parent_color = old->rb_parent_color;
+               node->rb_right = old->rb_right;
+               node->rb_left = old->rb_left;
+
+               if (rb_parent(old))
+               {
+                       if (rb_parent(old)->rb_left == old)
+                               rb_parent(old)->rb_left = node;
+                       else
+                               rb_parent(old)->rb_right = node;
+               } else
+                       root->rb_node = node;
+
+               rb_set_parent(old->rb_left, node);
+               if (old->rb_right)
+                       rb_set_parent(old->rb_right, node);
+               goto color;
+       }
+
+       parent = rb_parent(node);
+       color = rb_color(node);
+
+       if (child)
+               rb_set_parent(child, parent);
+       if (parent)
+       {
+               if (parent->rb_left == node)
+                       parent->rb_left = child;
+               else
+                       parent->rb_right = child;
+       }
+       else
+               root->rb_node = child;
+
+ color:
+       if (color == RB_BLACK)
+               __rb_erase_color(child, parent, root);
+}
+
+/*
+ * This function returns the first node (in sort order) of the tree.
+ */
+struct rb_node *rb_first(struct rb_root *root)
+{
+       struct rb_node  *n;
+
+       n = root->rb_node;
+       if (!n)
+               return NULL;
+       while (n->rb_left)
+               n = n->rb_left;
+       return n;
+}
+
+struct rb_node *rb_last(struct rb_root *root)
+{
+       struct rb_node  *n;
+
+       n = root->rb_node;
+       if (!n)
+               return NULL;
+       while (n->rb_right)
+               n = n->rb_right;
+       return n;
+}
+
+struct rb_node *rb_next(struct rb_node *node)
+{
+       struct rb_node *parent;
+
+       if (rb_parent(node) == node)
+               return NULL;
+
+       /* If we have a right-hand child, go down and then left as far
+          as we can. */
+       if (node->rb_right) {
+               node = node->rb_right;
+               while (node->rb_left)
+                       node=node->rb_left;
+               return node;
+       }
+
+       /* No right-hand children.  Everything down and left is
+          smaller than us, so any 'next' node must be in the general
+          direction of our parent. Go up the tree; any time the
+          ancestor is a right-hand child of its parent, keep going
+          up. First time it's a left-hand child of its parent, said
+          parent is our 'next' node. */
+       while ((parent = rb_parent(node)) && node == parent->rb_right)
+               node = parent;
+
+       return parent;
+}
+
+struct rb_node *rb_prev(struct rb_node *node)
+{
+       struct rb_node *parent;
+
+       if (rb_parent(node) == node)
+               return NULL;
+
+       /* If we have a left-hand child, go down and then right as far
+          as we can. */
+       if (node->rb_left) {
+               node = node->rb_left;
+               while (node->rb_right)
+                       node=node->rb_right;
+               return node;
+       }
+
+       /* No left-hand children. Go up till we find an ancestor which
+          is a right-hand child of its parent */
+       while ((parent = rb_parent(node)) && node == parent->rb_left)
+               node = parent;
+
+       return parent;
+}
+
+void rb_replace_node(struct rb_node *victim, struct rb_node *new,
+                    struct rb_root *root)
+{
+       struct rb_node *parent = rb_parent(victim);
+
+       /* Set the surrounding nodes to point to the replacement */
+       if (parent) {
+               if (victim == parent->rb_left)
+                       parent->rb_left = new;
+               else
+                       parent->rb_right = new;
+       } else {
+               root->rb_node = new;
+       }
+       if (victim->rb_left)
+               rb_set_parent(victim->rb_left, new);
+       if (victim->rb_right)
+               rb_set_parent(victim->rb_right, new);
+
+       /* Copy the pointers/colour from the victim to the replacement */
+       *new = *victim;
+}
diff --git a/src/rule.c b/src/rule.c
new file mode 100644 (file)
index 0000000..e86c78a
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <statement.h>
+#include <rule.h>
+#include <utils.h>
+
+
+void handle_free(struct handle *h)
+{
+       xfree(h->table);
+       xfree(h->chain);
+}
+
+void handle_merge(struct handle *dst, const struct handle *src)
+{
+       if (dst->family == 0)
+               dst->family = src->family;
+       if (dst->table == NULL && src->table != NULL)
+               dst->table = xstrdup(src->table);
+       if (dst->chain == NULL && src->chain != NULL)
+               dst->chain = xstrdup(src->chain);
+       if (dst->handle == 0)
+               dst->handle = src->handle;
+}
+
+struct rule *rule_alloc(const struct location *loc, const struct handle *h)
+{
+       struct rule *rule;
+
+       rule = xzalloc(sizeof(*rule));
+       rule->location = *loc;
+       init_list_head(&rule->list);
+       init_list_head(&rule->stmts);
+       if (h != NULL)
+               rule->handle = *h;
+       return rule;
+}
+
+void rule_free(struct rule *rule)
+{
+       stmt_list_free(&rule->stmts);
+       handle_free(&rule->handle);
+       xfree(rule);
+}
+
+void rule_print(const struct rule *rule)
+{
+       const struct stmt *stmt;
+
+       list_for_each_entry(stmt, &rule->stmts, list) {
+               printf(" ");
+               stmt->ops->print(stmt);
+       }
+       printf("\n");
+}
+
+struct chain *chain_alloc(const char *name)
+{
+       struct chain *chain;
+
+       chain = xzalloc(sizeof(*chain));
+       init_list_head(&chain->rules);
+       if (name != NULL)
+               chain->handle.chain = xstrdup(name);
+       return chain;
+}
+
+void chain_free(struct chain *chain)
+{
+       struct rule *rule, *next;
+
+       list_for_each_entry_safe(rule, next, &chain->rules, list)
+               rule_free(rule);
+       handle_free(&chain->handle);
+       xfree(chain);
+}
+
+void chain_add_hash(struct chain *chain, struct table *table)
+{
+       list_add_tail(&chain->list, &table->chains);
+}
+
+struct chain *chain_lookup(const struct table *table, const struct handle *h)
+{
+       struct chain *chain;
+
+       list_for_each_entry(chain, &table->chains, list) {
+               if (!strcmp(chain->handle.chain, h->chain))
+                       return chain;
+       }
+       return NULL;
+}
+
+static void chain_print(const struct chain *chain)
+{
+       struct rule *rule;
+
+       printf("\tchain %s {\n", chain->handle.chain);
+       list_for_each_entry(rule, &chain->rules, list) {
+               printf("\t\t");
+               rule_print(rule);
+       }
+       printf("\t}\n");
+}
+
+struct table *table_alloc(void)
+{
+       struct table *table;
+
+       table = xzalloc(sizeof(*table));
+       init_list_head(&table->chains);
+       return table;
+}
+
+void table_free(struct table *table)
+{
+       struct chain *chain, *next;
+
+       list_for_each_entry_safe(chain, next, &table->chains, list)
+               chain_free(chain);
+       handle_free(&table->handle);
+       xfree(table);
+}
+
+static LIST_HEAD(table_list);
+
+void table_add_hash(struct table *table)
+{
+       list_add_tail(&table->list, &table_list);
+}
+
+struct table *table_lookup(const struct handle *h)
+{
+       struct table *table;
+
+       list_for_each_entry(table, &table_list, list) {
+               if (table->handle.family == h->family &&
+                   !strcmp(table->handle.table, h->table))
+                       return table;
+       }
+       return NULL;
+}
+
+static void table_print(const struct table *table)
+{
+       struct chain *chain;
+       const char *delim = "";
+
+       printf("table %s {\n", table->handle.table);
+       list_for_each_entry(chain, &table->chains, list) {
+               printf("%s", delim);
+               chain_print(chain);
+               delim = "\n";
+       }
+       printf("}\n");
+}
+
+struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj,
+                     const struct handle *h, void *data)
+{
+       struct cmd *cmd;
+
+       cmd = xzalloc(sizeof(*cmd));
+       cmd->op     = op;
+       cmd->obj    = obj;
+       cmd->handle = *h;
+       cmd->data   = data;
+       return cmd;
+}
+
+void cmd_free(struct cmd *cmd)
+{
+       handle_free(&cmd->handle);
+       if (cmd->data != NULL) {
+               switch (cmd->obj) {
+               case CMD_OBJ_RULE:
+                       rule_free(cmd->rule);
+                       break;
+               case CMD_OBJ_CHAIN:
+                       chain_free(cmd->chain);
+                       break;
+               case CMD_OBJ_TABLE:
+                       table_free(cmd->table);
+                       break;
+               default:
+                       BUG();
+               }
+       }
+       xfree(cmd);
+}
+
+#include <netlink.h>
+
+static int do_add_chain(struct netlink_ctx *ctx, const struct handle *h,
+                       struct chain *chain)
+{
+       struct rule *rule;
+
+       if (netlink_add_chain(ctx, h, chain) < 0)
+               return -1;
+       if (chain != NULL) {
+               list_for_each_entry(rule, &chain->rules, list) {
+                       if (netlink_add_rule(ctx, &rule->handle, rule) < 0)
+                               return -1;
+               }
+       }
+       return 0;
+}
+
+static int do_add_table(struct netlink_ctx *ctx, const struct handle *h,
+                       struct table *table)
+{
+       struct chain *chain;
+
+       if (netlink_add_table(ctx, h, table) < 0)
+               return -1;
+       if (table != NULL) {
+               list_for_each_entry(chain, &table->chains, list) {
+                       if (do_add_chain(ctx, &chain->handle, chain) < 0)
+                               return -1;
+               }
+       }
+       return 0;
+}
+
+static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+       switch (cmd->obj) {
+       case CMD_OBJ_TABLE:
+               return do_add_table(ctx, &cmd->handle, cmd->table);
+       case CMD_OBJ_CHAIN:
+               return do_add_chain(ctx, &cmd->handle, cmd->chain);
+       case CMD_OBJ_RULE:
+               return netlink_add_rule(ctx, &cmd->handle, cmd->rule);
+       default:
+               BUG();
+       }
+       return 0;
+}
+
+static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+       switch (cmd->obj) {
+       case CMD_OBJ_TABLE:
+               return netlink_delete_table(ctx, &cmd->handle);
+       case CMD_OBJ_CHAIN:
+               return netlink_delete_chain(ctx, &cmd->handle);
+       case CMD_OBJ_RULE:
+               return netlink_delete_rule(ctx, &cmd->handle);
+       default:
+               BUG();
+       }
+}
+
+static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+       struct table *table;
+       struct chain *chain;
+       struct rule *rule, *next;
+
+       switch (cmd->obj) {
+       case CMD_OBJ_TABLE:
+               if (netlink_list_table(ctx, &cmd->handle) < 0)
+                       return -1;
+               break;
+       case CMD_OBJ_CHAIN:
+               if (netlink_list_chain(ctx, &cmd->handle) < 0)
+                       return -1;
+               break;
+       default:
+               BUG();
+       }
+
+       table = NULL;
+       list_for_each_entry_safe(rule, next, &ctx->list, list) {
+               table = table_lookup(&rule->handle);
+               if (table == NULL) {
+                       table = table_alloc();
+                       handle_merge(&table->handle, &rule->handle);
+                       table_add_hash(table);
+               }
+
+               chain = chain_lookup(table, &rule->handle);
+               if (chain == NULL) {
+                       chain = chain_alloc(rule->handle.chain);
+                       chain_add_hash(chain, table);
+               }
+
+               list_move_tail(&rule->list, &chain->rules);
+       }
+
+       if (table != NULL)
+               table_print(table);
+       else
+               printf("table %s does not exist\n", cmd->handle.table);
+       return 0;
+}
+
+static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+       switch (cmd->obj) {
+       case CMD_OBJ_TABLE:
+               return netlink_flush_table(ctx, &cmd->handle);
+       case CMD_OBJ_CHAIN:
+               return netlink_flush_chain(ctx, &cmd->handle);
+       default:
+               BUG();
+       }
+       return 0;
+}
+
+int do_command(struct netlink_ctx *ctx, struct cmd *cmd)
+{
+       switch (cmd->op) {
+       case CMD_ADD:
+               return do_command_add(ctx, cmd);
+       case CMD_DELETE:
+               return do_command_delete(ctx, cmd);
+       case CMD_LIST:
+               return do_command_list(ctx, cmd);
+       case CMD_FLUSH:
+               return do_command_flush(ctx, cmd);
+       default:
+               BUG();
+       }
+}
+
+static int payload_match_stmt_cmp(const void *p1, const void *p2)
+{
+       const struct stmt *s1 = *(struct stmt * const *)p1;
+       const struct stmt *s2 = *(struct stmt * const *)p2;
+       const struct expr *e1 = s1->expr, *e2 = s2->expr;
+       int d;
+
+       d = e1->left->payload.base - e2->left->payload.base;
+       if (d != 0)
+               return d;
+       return e1->left->payload.offset - e2->left->payload.offset;
+}
+
+static void payload_do_merge(struct stmt *sa[], unsigned int n)
+{
+       struct expr *last, *this, *expr;
+       struct stmt *stmt;
+       unsigned int i;
+
+       qsort(sa, n, sizeof(sa[0]), payload_match_stmt_cmp);
+
+       last = sa[0]->expr;
+       for (i = 1; i < n; i++) {
+               stmt = sa[i];
+               this = stmt->expr;
+
+               if (!payload_is_adjacent(last->left, this->left) ||
+                   last->op != this->op) {
+                       last = this;
+                       continue;
+               }
+
+               expr = payload_expr_join(last->left, this->left);
+               expr_free(last->left);
+               last->left = expr;
+
+               expr = constant_expr_join(last->right, this->right);
+               expr_free(last->right);
+               last->right = expr;
+
+               list_del(&stmt->list);
+               stmt_free(stmt);
+       }
+}
+
+/**
+ * payload_try_merge - try to merge consecutive payload match statements
+ *
+ * @rule:      nftables rule
+ *
+ * Locate sequences of payload match statements referring to adjacent
+ * header locations and merge those using only equality relations.
+ *
+ * As a side-effect, payload match statements are ordered in ascending
+ * order according to the location of the payload.
+ */
+static void payload_try_merge(const struct rule *rule)
+{
+       struct stmt *sa[rule->num_stmts];
+       struct stmt *stmt, *next;
+       unsigned int idx = 0;
+
+       list_for_each_entry_safe(stmt, next, &rule->stmts, list) {
+               /* Must not merge across other statements */
+               if (stmt->ops->type != STMT_EXPRESSION)
+                       goto do_merge;
+
+               if (stmt->expr->ops->type != EXPR_RELATIONAL)
+                       continue;
+               if (stmt->expr->left->ops->type != EXPR_PAYLOAD)
+                       continue;
+               if (stmt->expr->right->ops->type != EXPR_VALUE)
+                       continue;
+               switch (stmt->expr->op) {
+               case OP_EQ:
+               case OP_NEQ:
+                       break;
+               default:
+                       continue;
+               }
+
+               sa[idx++] = stmt;
+               continue;
+do_merge:
+               if (idx < 2)
+                       continue;
+               payload_do_merge(sa, idx);
+               idx = 0;
+       }
+
+       if (idx > 1)
+               payload_do_merge(sa, idx);
+}
+
+struct error_record *rule_postprocess(struct rule *rule)
+{
+       payload_try_merge(rule);
+       rule_print(rule);
+       return NULL;
+}
diff --git a/src/scanner.l b/src/scanner.l
new file mode 100644 (file)
index 0000000..dc6341d
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 2007-2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+%{
+
+#include <limits.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/types.h>
+#include <linux/netfilter.h>
+
+#include <nftables.h>
+#include <erec.h>
+#include <rule.h>
+#include <parser.h>
+#include "parser.h"
+
+#define YY_NO_INPUT
+
+/*
+ * Work around flex behaviour when reaching the end of buffer: normally, flex
+ * regexes are greedy, when reaching the end of buffer however it tries to
+ * match whatever is left in the buffer and only backs up in case it doesn't
+ * match *any* pattern. Since we accept unquoted strings, this means any partial
+ * token will be recognized as string.
+ *
+ * Make sure to only pass input to flex linewise to avoid this.
+ */
+#define YY_INPUT(buf,result,max_size)                                          \
+{                                                                              \
+       long n = 0;                                                             \
+       errno = 0;                                                              \
+       while ((result = fread(buf, 1, max_size, yyin)) == 0 &&                 \
+              ferror(yyin)) {                                                  \
+               if (errno != EINTR) {                                           \
+                       YY_FATAL_ERROR( "input in flex scanner failed" );       \
+                       break;                                                  \
+               }                                                               \
+               errno = 0;                                                      \
+               clearerr(yyin);                                                 \
+       }                                                                       \
+       if (result > 1) {                                                       \
+               while (result > 1 && buf[result - 1] != '\n')                   \
+                       result--, n++;                                          \
+               result--, n++;                                                  \
+               fseek(yyin, -n, SEEK_CUR);                                      \
+       }                                                                       \
+}
+
+static void scanner_pop_buffer(yyscan_t scanner);
+
+
+static void init_pos(struct parser_state *state)
+{
+       state->indesc->lineno           = 1;
+       state->indesc->column           = 1;
+       state->indesc->token_offset     = 0;
+       state->indesc->line_offset      = 0;
+}
+
+static void update_pos(struct parser_state *state, struct location *loc,
+                      int len)
+{
+       loc->indesc                     = state->indesc;
+       loc->first_line                 = state->indesc->lineno;
+       loc->first_column               = state->indesc->column;
+       loc->last_column                = state->indesc->column + len - 1;
+       state->indesc->column           += len;
+}
+
+static void update_offset(struct parser_state *state, struct location *loc,
+                         unsigned int len)
+{
+       state->indesc->token_offset     += len;
+       loc->token_offset               = state->indesc->token_offset;
+       loc->line_offset                = state->indesc->line_offset;
+}
+
+static void reset_pos(struct parser_state *state, struct location *loc)
+{
+       state->indesc->line_offset      = state->indesc->token_offset;
+       state->indesc->lineno           += 1;
+       state->indesc->column           = 1;
+       loc->line_offset                = state->indesc->line_offset;
+}
+
+#define YY_USER_ACTION {                                       \
+       update_pos(yyget_extra(yyscanner), yylloc, yyleng);     \
+       update_offset(yyget_extra(yyscanner), yylloc, yyleng);  \
+}
+
+/* avoid warnings with -Wmissing-prototypes */
+extern int     yyget_column(yyscan_t);
+extern void    yyset_column(int, yyscan_t);
+
+%}
+
+space          [ ]
+tab            \t
+newline                \n
+digit          [0-9]
+hexdigit       [0-9a-fA-F]
+decstring      {digit}+
+hexstring      0[xX]{hexdigit}+
+range          ({decstring}?:{decstring}?)
+letter         [a-zA-Z]
+string         ({letter})({letter}|{digit}|[/\-_\.])*
+quotedstring   \"[^"]*\"
+comment                #.*$
+slash          \/
+
+hex4           ([[:xdigit:]]{1,4})
+v680           (({hex4}:){7}{hex4})
+v670           ((:)(:{hex4}{7}))
+v671           ((({hex4}:){1})(:{hex4}{6}))
+v672           ((({hex4}:){2})(:{hex4}{5}))
+v673           ((({hex4}:){3})(:{hex4}{4}))
+v674           ((({hex4}:){4})(:{hex4}{3}))
+v675           ((({hex4}:){5})(:{hex4}{2}))
+v676           ((({hex4}:){6})(:{hex4}{1}))
+v677           ((({hex4}:){7})(:))
+v67            ({v670}|{v671}|{v672}|{v673}|{v674}|{v675}|{v676}|{v677})
+v660           ((:)(:{hex4}{6}))
+v661           ((({hex4}:){1})(:{hex4}{5}))
+v662           ((({hex4}:){2})(:{hex4}{4}))
+v663           ((({hex4}:){3})(:{hex4}{3}))
+v664           ((({hex4}:){4})(:{hex4}{2}))
+v665           ((({hex4}:){5})(:{hex4}{1}))
+v666           ((({hex4}:){6})(:))
+v66            ({v660}|{v661}|{v662}|{v663}|{v664}|{v665}|{v666})
+v650           ((:)(:{hex4}{5}))
+v651           ((({hex4}:){1})(:{hex4}{4}))
+v652           ((({hex4}:){2})(:{hex4}{3}))
+v653           ((({hex4}:){3})(:{hex4}{2}))
+v654           ((({hex4}:){4})(:{hex4}{1}))
+v655           ((({hex4}:){5})(:))
+v65            ({v650}|{v651}|{v652}|{v653}|{v654}|{v655})
+v640           ((:)(:{hex4}{4}))
+v641           ((({hex4}:){1})(:{hex4}{3}))
+v642           ((({hex4}:){2})(:{hex4}{2}))
+v643           ((({hex4}:){3})(:{hex4}{1}))
+v644           ((({hex4}:){4})(:))
+v64            ({v640}|{v641}|{v642}|{v643}|{v644})
+v630           ((:)(:{hex4}{3}))
+v631           ((({hex4}:){1})(:{hex4}{2}))
+v632           ((({hex4}:){2})(:{hex4}{1}))
+v633           ((({hex4}:){3})(:))
+v63            ({v630}|{v631}|{v632}|{v633})
+v620           ((:)(:{hex4}{2}))
+v621           ((({hex4}:){1})(:{hex4}{1}))
+v622           ((({hex4}:){2})(:))
+v62            ({v620}|{v621}|{v622})
+v610           ((:)(:{hex4}{1}))
+v611           ((({hex4}:){1})(:))
+v61            ({v610}|{v611})
+v60            (::)
+
+macaddr                (([[:xdigit:]]{1,2}:){5}[[:xdigit:]]{1,2})
+ip4addr                (([[:digit:]]{1,3}"."){3}([[:digit:]]{1,3}))
+ip6addr                ({v680}|{v67}|{v66}|{v65}|{v64}|{v63}|{v62}|{v61}|{v60})
+
+addrstring     ({macaddr}|{ip4addr}|{ip6addr})
+
+%option prefix="nft_"
+%option reentrant
+%option noyywrap
+%option nounput
+%option bison-bridge
+%option bison-locations
+%option debug
+%option yylineno
+%option nodefault
+%option warn
+
+%%
+
+"=="                   { return EQ; }
+"!="                   { return NEQ; }
+"<="                   { return LTE; }
+"<"                    { return LT; }
+">="                   { return GTE; }
+">"                    { return GT; }
+","                    { return COMMA; }
+"."                    { return DOT; }
+":"                    { return COLON; }
+";"                    { return SEMICOLON; }
+"{"                    { return '{'; }
+"}"                    { return '}'; }
+"["                    { return '['; }
+"]"                    { return ']'; }
+"("                    { return '('; }
+")"                    { return ')'; }
+"<<"                   { return LSHIFT; }
+">>"                   { return RSHIFT; }
+"^"                    { return CARET; }
+"&"                    { return AMPERSAND; }
+"|"                    { return '|'; }
+"!"                    { return NOT; }
+"/"                    { return SLASH; }
+"-"                    { return DASH; }
+"*"                    { return ASTERISK; }
+"@"                    { return AT; }
+"=>"                   { return ARROW; }
+"map"                  { return MAP; }
+"vmap"                 { return VMAP; }
+"set"                  { return SET; }
+
+"NF_INET_PRE_ROUTING"  { yylval->val = NF_INET_PRE_ROUTING;    return HOOKNUM; }
+"NF_INET_LOCAL_IN"     { yylval->val = NF_INET_LOCAL_IN;       return HOOKNUM; }
+"NF_INET_FORWARD"      { yylval->val = NF_INET_FORWARD;        return HOOKNUM; }
+"NF_INET_LOCAL_OUT"    { yylval->val = NF_INET_LOCAL_OUT;      return HOOKNUM; }
+"NF_INET_POST_ROUTING" { yylval->val = NF_INET_POST_ROUTING;   return HOOKNUM; }
+
+"include"              { return INCLUDE; }
+
+"describe"             { return DESCRIBE; }
+
+"hook"                 { return HOOK; }
+"table"                        { return TABLE; }
+"chain"                        { return CHAIN; }
+"rule"                 { return RULE; }
+"handle"               { return HANDLE; }
+
+"accept"               { return ACCEPT; }
+"drop"                 { return DROP; }
+"continue"             { return CONTINUE; }
+"jump"                 { return JUMP; }
+"goto"                 { return GOTO; }
+"return"               { return RETURN; }
+"queue"                        { return QUEUE; }
+
+"add"                  { return ADD; }
+"delete"               { return DELETE; }
+"list"                 { return LIST; }
+"flush"                        { return FLUSH; }
+
+"counter"              { return COUNTER; }
+
+"log"                  { return LOG; }
+"prefix"               { return PREFIX; }
+"group"                        { return GROUP; }
+"snaplen"              { return SNAPLEN; }
+"queue-threshold"      { return QUEUE_THRESHOLD; }
+
+"limit"                        { return LIMIT; }
+"rate"                 { return RATE; }
+
+"nanosecond"           { return NANOSECOND; }
+"microsecond"          { return MICROSECOND; }
+"millisecond"          { return MILLISECOND; }
+"second"               { return SECOND; }
+"minute"               { return MINUTE; }
+"hour"                 { return HOUR; }
+"day"                  { return DAY; }
+"week"                 { return WEEK; }
+
+"reject"               { return _REJECT; }
+
+"snat"                 { return SNAT; }
+"dnat"                 { return DNAT; }
+
+"ll"                   { return LL_HDR; }
+"nh"                   { return NETWORK_HDR; }
+"th"                   { return TRANSPORT_HDR; }
+
+"bridge"               { return BRIDGE; }
+
+"eth"                  { return ETH; }
+"saddr"                        { return SADDR; }
+"daddr"                        { return DADDR; }
+"type"                 { return TYPE; }
+
+"vlan"                 { return VLAN; }
+"id"                   { return ID; }
+"cfi"                  { return CFI; }
+"pcp"                  { return PCP; }
+
+"arp"                  { return ARP; }
+"htype"                        { return HTYPE; }
+"ptype"                        { return PTYPE; }
+"hlen"                 { return HLEN; }
+"plen"                 { return PLEN; }
+"operation"            { return OPERATION; }
+
+"ip"                   { return IP; }
+"version"              { return VERSION; }
+"hdrlength"            { return HDRLENGTH; }
+"tos"                  { return TOS; }
+"length"               { return LENGTH; }
+"frag-off"             { return FRAG_OFF; }
+"ttl"                  { return TTL; }
+"protocol"             { return PROTOCOL; }
+"checksum"             { return CHECKSUM; }
+
+"icmp"                 { return ICMP; }
+"code"                 { return CODE; }
+"sequence"             { return SEQUENCE; }
+"gateway"              { return GATEWAY; }
+"mtu"                  { return MTU; }
+
+"ip6"                  { return IP6; }
+"priority"             { return PRIORITY; }
+"flowlabel"            { return FLOWLABEL; }
+"nexthdr"              { return NEXTHDR; }
+"hoplimit"             { return HOPLIMIT; }
+
+"ah"                   { return AH; }
+"reserved"             { return RESERVED; }
+"spi"                  { return SPI; }
+
+"esp"                  { return ESP; }
+
+"comp"                 { return COMP; }
+"flags"                        { return FLAGS; }
+"cpi"                  { return CPI; }
+
+"udp"                  { return UDP; }
+"sport"                        { return SPORT; }
+"dport"                        { return DPORT; }
+
+"tcp"                  { return TCP; }
+"ackseq"               { return ACKSEQ; }
+"doff"                 { return DOFF; }
+"window"               { return WINDOW; }
+"urgptr"               { return URGPTR; }
+
+"dccp"                 { return DCCP; }
+
+"sctp"                 { return SCTP; }
+"vtag"                 { return VTAG; }
+
+"rt"                   { return RT; }
+"rt0"                  { return RT0; }
+"rt2"                  { return RT2; }
+"seg-left"             { return SEG_LEFT; }
+"addr"                 { return ADDR; }
+
+"hbh"                  { return HBH; }
+
+"frag"                 { return FRAG; }
+"reserved2"            { return RESERVED2; }
+"more-fragments"       { return MORE_FRAGMENTS; }
+
+"dst"                  { return DST; }
+
+"mh"                   { return MH; }
+
+"meta"                 { return META; }
+"mark"                 { return MARK; }
+"iif"                  { return IIF; }
+"iifname"              { return IIFNAME; }
+"iiftype"              { return IIFTYPE; }
+"oif"                  { return OIF; }
+"oifname"              { return OIFNAME; }
+"oiftype"              { return OIFTYPE; }
+"skuid"                        { return SKUID; }
+"skgid"                        { return SKGID; }
+"nftrace"              { return NFTRACE; }
+"rtclassid"            { return RTCLASSID; }
+"secmark"              { return SECMARK; }
+
+"ct"                   { return CT; }
+"direction"            { return DIRECTION; }
+"state"                        { return STATE; }
+"status"               { return STATUS; }
+"expiration"           { return EXPIRATION; }
+"helper"               { return HELPER; }
+"proto-src"            { return PROTO_SRC; }
+"proto-dst"            { return PROTO_DST; }
+
+{addrstring}           {
+                               yylval->string = xstrdup(yytext);
+                               return STRING;
+                       }
+
+{decstring}            {
+                               errno = 0;
+                               yylval->val = strtoull(yytext, NULL, 0);
+                               if (errno != 0)
+                                       BUG();
+                               return NUM;
+                       }
+
+{hexstring}            {
+                               errno = 0;
+                               yylval->val = strtoull(yytext, NULL, 0);
+                               if (errno != 0)
+                                       BUG();
+                               return NUM;
+                       }
+
+{quotedstring}         {
+                               yytext[yyleng - 1] = '\0';
+                               yylval->string = xstrdup(yytext + 1);
+                               return QUOTED_STRING;
+                       }
+
+{string}               {
+                               yylval->string = xstrdup(yytext);
+                               return STRING;
+                       }
+
+\\{newline}            {
+                               reset_pos(yyget_extra(yyscanner), yylloc);
+                       }
+
+{newline}              {
+                               reset_pos(yyget_extra(yyscanner), yylloc);
+                               return NEWLINE;
+                       }
+
+{tab}                  {
+                               /*
+                                * Compensate difference between visible length
+                                * and real length.
+                                */
+                               struct parser_state *state = yyget_extra(yyscanner);
+                               unsigned int diff;
+
+                               diff = TABSIZE - strlen("\t");
+                               diff -= (state->indesc->column -
+                                        strlen("\t") - 1) % TABSIZE;
+
+                               update_pos(state, yylloc, diff);
+                       }
+
+{space}+
+{comment}
+
+<<EOF>>                {
+                               update_pos(yyget_extra(yyscanner), yylloc, 1);
+                               scanner_pop_buffer(yyscanner);
+                               if (YY_CURRENT_BUFFER == NULL)
+                                       return TOKEN_EOF;
+                       }
+
+.                      { return JUNK; }
+
+%%
+
+static void scanner_pop_buffer(yyscan_t scanner)
+{
+       struct parser_state *state = yyget_extra(scanner);
+
+       yypop_buffer_state(scanner);
+       state->indesc = &state->indescs[--state->indesc_idx - 1];
+}
+
+static struct error_record *scanner_push_file(void *scanner, const char *filename,
+                                             FILE *f, const struct location *loc)
+{
+       struct parser_state *state = yyget_extra(scanner);
+       YY_BUFFER_STATE b;
+
+       if (state->indesc_idx == MAX_INCLUDE_DEPTH) {
+               fclose(f);
+               return error(loc, "Include nested too deeply, max %u levels",
+                            MAX_INCLUDE_DEPTH);
+       }
+
+       b = yy_create_buffer(f, YY_BUF_SIZE, scanner);
+       yypush_buffer_state(b, scanner);
+
+       state->indesc = &state->indescs[state->indesc_idx++];
+       if (loc != NULL)
+               state->indesc->location = *loc;
+       state->indesc->type     = INDESC_FILE;
+       state->indesc->name     = xstrdup(filename);
+       state->indesc->fd       = fileno(f);
+       init_pos(state);
+       return NULL;
+}
+
+int scanner_read_file(void *scanner, const char *filename,
+                     const struct location *loc)
+{
+       struct parser_state *state = yyget_extra(scanner);
+       struct error_record *erec;
+       FILE *f;
+
+       f = fopen(filename, "r");
+       if (f == NULL) {
+               erec = error(loc, "Could not open file \"%s\": %s\n",
+                            filename, strerror(errno));
+               goto err;
+       }
+
+       erec = scanner_push_file(scanner, filename, f, loc);
+       if (erec != NULL)
+               goto err;
+       return 0;
+
+err:
+       erec_queue(erec, state->msgs);
+       return -1;
+}
+
+int scanner_include_file(void *scanner, const char *filename,
+                        const struct location *loc)
+{
+       struct parser_state *state = yyget_extra(scanner);
+       struct error_record *erec;
+       char buf[PATH_MAX];
+       const char *name = buf;
+       unsigned int i;
+       FILE *f;
+
+       f = NULL;
+       for (i = 0; i < INCLUDE_PATHS_MAX; i++) {
+               if (include_paths[i] == NULL)
+                       break;
+               snprintf(buf, sizeof(buf), "%s/%s", include_paths[i], filename);
+               f = fopen(buf, "r");
+               if (f != NULL)
+                       break;
+       }
+       if (f == NULL) {
+               f = fopen(filename, "r");
+               if (f == NULL) {
+                       erec = error(loc, "Could not open file \"%s\": %s\n",
+                                    filename, strerror(errno));
+                       goto err;
+               }
+               name = filename;
+       }
+
+       erec = scanner_push_file(scanner, name, f, loc);
+       if (erec != NULL)
+               goto err;
+       return 0;
+
+err:
+       erec_queue(erec, state->msgs);
+       return -1;
+}
+
+void scanner_push_buffer(void *scanner, const struct input_descriptor *indesc,
+                        const char *buffer)
+{
+       struct parser_state *state = yyget_extra(scanner);
+       YY_BUFFER_STATE b;
+
+       state->indesc = &state->indescs[state->indesc_idx++];
+       memcpy(state->indesc, indesc, sizeof(*state->indesc));
+       state->indesc->data = buffer;
+
+       b = yy_scan_string(buffer, scanner);
+       assert(b != NULL);
+       init_pos(state);
+}
+
+void *scanner_init(struct parser_state *state)
+{
+       yyscan_t scanner;
+
+       state->indesc = state->indescs;
+
+       yylex_init(&scanner);
+       yyset_extra(state, scanner),
+       yyset_out(NULL, scanner);
+
+       return scanner;
+}
+
+void scanner_destroy(struct parser_state *scanner)
+{
+       struct parser_state *state = yyget_extra(scanner);
+
+       /* Can't free indesc name - locations might still be in use */
+       while (state->indesc_idx--)
+               yypop_buffer_state(scanner);
+
+       yylex_destroy(scanner);
+}
diff --git a/src/segtree.c b/src/segtree.c
new file mode 100644 (file)
index 0000000..9e59bb6
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stdlib.h>
+#include <inttypes.h>
+#include <arpa/inet.h>
+
+#include <expression.h>
+#include <gmputil.h>
+#include <utils.h>
+#include <rbtree.h>
+
+/**
+ * struct seg_tree - segment tree
+ *
+ * @root:      the rbtree's root
+ * @type:      the datatype of the dimension
+ * @dwidth:    width of the dimension
+ * @byteorder: byteorder of elements
+ */
+struct seg_tree {
+       struct rb_root                  root;
+       const struct datatype           *type;
+       unsigned int                    dwidth;
+       enum byteorder                  byteorder;
+};
+
+enum elementary_interval_flags {
+       EI_F_INTERVAL_END       = 0x1,
+};
+
+/**
+ * struct elementary_interval - elementary interval [left, right]
+ *
+ * @rb_node:   seg_tree rb node
+ * @list:      list node for linearized tree
+ * @left:      left endpoint
+ * @right:     right endpoint
+ * @size:      interval size (right - left)
+ * @flags:     flags
+ * @expr:      associated expression
+ */
+struct elementary_interval {
+       union {
+               struct rb_node          rb_node;
+               struct list_head        list;
+       };
+
+       mpz_t                           left;
+       mpz_t                           right;
+       mpz_t                           size;
+
+       enum elementary_interval_flags  flags;
+       struct expr                     *expr;
+};
+
+static void seg_tree_init(struct seg_tree *tree, const struct expr *set)
+{
+       struct expr *first;
+
+       first = list_entry(set->expressions.next, struct expr, list);
+       tree->root      = RB_ROOT;
+       tree->dwidth    = set->len;
+       tree->type      = set->dtype;
+       tree->byteorder = first->byteorder;
+}
+
+static struct elementary_interval *ei_alloc(const mpz_t left, const mpz_t right,
+                                           struct expr *expr,
+                                           enum elementary_interval_flags flags)
+{
+       struct elementary_interval *ei;
+
+       ei = xzalloc(sizeof(*ei));
+       mpz_init_set(ei->left, left);
+       mpz_init_set(ei->right, right);
+       mpz_init(ei->size);
+       mpz_sub(ei->size, right, left);
+       if (expr != NULL)
+               ei->expr = expr_get(expr);
+       ei->flags = flags;
+       return ei;
+}
+
+static void ei_destroy(struct elementary_interval *ei)
+{
+       mpz_clear(ei->left);
+       mpz_clear(ei->right);
+       mpz_clear(ei->size);
+       if (ei->expr != NULL)
+               expr_free(ei->expr);
+       xfree(ei);
+}
+
+/**
+ * ei_lookup - find elementary interval containing point p
+ *
+ * @tree:      segment tree
+ * @p:         the point
+ */
+static struct elementary_interval *ei_lookup(struct seg_tree *tree, const mpz_t p)
+{
+       struct rb_node *n = tree->root.rb_node;
+       struct elementary_interval *ei;
+
+       while (n != NULL) {
+               ei = rb_entry(n, struct elementary_interval, rb_node);
+
+               if (mpz_cmp(p, ei->left) >= 0 &&
+                   mpz_cmp(p, ei->right) <= 0)
+                       return ei;
+               else if (mpz_cmp(p, ei->left) <= 0)
+                       n = n->rb_left;
+               else if (mpz_cmp(p, ei->right) > 0)
+                       n = n->rb_right;
+       }
+       return NULL;
+}
+
+static void ei_remove(struct seg_tree *tree, struct elementary_interval *ei)
+{
+       rb_erase(&ei->rb_node, &tree->root);
+}
+
+static void __ei_insert(struct seg_tree *tree, struct elementary_interval *new)
+{
+       struct rb_node **p = &tree->root.rb_node;
+       struct rb_node *parent = NULL;
+       struct elementary_interval *ei;
+
+       while (*p != NULL) {
+               parent = *p;
+               ei = rb_entry(parent, struct elementary_interval, rb_node);
+
+               if (mpz_cmp(new->left, ei->left) >= 0 &&
+                   mpz_cmp(new->left, ei->right) <= 0)
+                       break;
+               else if (mpz_cmp(new->left, ei->left) <= 0)
+                       p = &(*p)->rb_left;
+               else if (mpz_cmp(new->left, ei->left) > 0)
+                       p = &(*p)->rb_right;
+       }
+
+       rb_link_node(&new->rb_node, parent, p);
+       rb_insert_color(&new->rb_node, &tree->root);
+}
+
+/**
+ * ei_insert - insert an elementary interval into the tree
+ *
+ * @tree:      the seg_tree
+ * @new:       the elementary interval
+ *
+ * New entries take precedence over existing ones. Insertions are assumed to
+ * be ordered by descending interval size, meaning the new interval never
+ * extends over more than two existing intervals.
+ */
+static void ei_insert(struct seg_tree *tree, struct elementary_interval *new)
+{
+       struct elementary_interval *lei, *rei;
+       mpz_t p;
+
+       mpz_init2(p, tree->dwidth);
+
+       /*
+        * Lookup the intervals containing the left and right endpoints.
+        */
+       lei = ei_lookup(tree, new->left);
+       rei = ei_lookup(tree, new->right);
+
+       pr_debug("insert: [%Zx %Zx]\n", new->left, new->right);
+
+       if (lei != NULL && rei != NULL && lei == rei) {
+               /*
+                * The new interval is entirely contained in the same interval,
+                * split it into two parts:
+                *
+                * [lei_left, new_left) and (new_right, rei_right]
+                */
+               pr_debug("split [%Zx %Zx]\n", lei->left, lei->right);
+
+               ei_remove(tree, lei);
+
+               mpz_sub_ui(p, new->left, 1);
+               if (mpz_cmp(lei->left, p) <= 0)
+                       __ei_insert(tree, ei_alloc(lei->left, p, lei->expr, 0));
+
+               mpz_add_ui(p, new->right, 1);
+               if (mpz_cmp(p, rei->right) < 0)
+                       __ei_insert(tree, ei_alloc(p, rei->right, lei->expr, 0));
+               ei_destroy(lei);
+       } else {
+               if (lei != NULL) {
+                       /*
+                        * Left endpoint is within lei, adjust it so we have:
+                        *
+                        * [lei_left, new_left)[new_left, new_right]
+                        */
+                       pr_debug("adjust left [%Zx %Zx]\n", lei->left, lei->right);
+
+                       mpz_sub_ui(lei->right, new->left, 1);
+                       mpz_sub(lei->size, lei->right, lei->left);
+                       if (mpz_sgn(lei->size) < 0) {
+                               ei_remove(tree, lei);
+                               ei_destroy(lei);
+                       }
+               }
+               if (rei != NULL) {
+                       /*
+                        * Right endpoint is within rei, adjust it so we have:
+                        *
+                        * [new_left, new_right](new_right, rei_right]
+                        */
+                       pr_debug("adjust right [%Zx %Zx]\n", rei->left, rei->right);
+
+                       mpz_add_ui(rei->left, new->right, 1);
+                       mpz_sub(rei->size, rei->right, rei->left);
+                       if (mpz_sgn(rei->size) < 0) {
+                               ei_remove(tree, rei);
+                               ei_destroy(rei);
+                       }
+               }
+       }
+
+       __ei_insert(tree, new);
+
+       mpz_clear(p);
+}
+
+static void range_low(mpz_t rop, struct expr *expr)
+{
+       switch (expr->ops->type) {
+       case EXPR_VALUE:
+               return mpz_set(rop, expr->value);
+       case EXPR_PREFIX:
+               return range_low(rop, expr->expr);
+       case EXPR_RANGE:
+               return range_low(rop, expr->left);
+       case EXPR_MAPPING:
+               return range_low(rop, expr->left);
+       default:
+               BUG();
+       }
+}
+
+static void range_high(mpz_t rop, const struct expr *expr)
+{
+       mpz_t tmp;
+
+       switch (expr->ops->type) {
+       case EXPR_VALUE:
+               return mpz_set(rop, expr->value);
+       case EXPR_PREFIX:
+               range_low(rop, expr->expr);
+               mpz_init_bitmask(tmp, expr->len - expr->prefix_len);
+               mpz_add(rop, rop, tmp);
+               mpz_clear(tmp);
+               return;
+       case EXPR_RANGE:
+               return range_high(rop, expr->right);
+       case EXPR_MAPPING:
+               return range_high(rop, expr->left);
+       default:
+               BUG();
+       }
+}
+
+/*
+ * Sort intervals according to their priority, which is defined inversely to
+ * their size.
+ *
+ * The beginning of the interval is used as secondary sorting criterion. This
+ * makes sure that overlapping ranges with equal priority are next to each
+ * other, allowing to easily detect unsolvable conflicts during insertion.
+ *
+ * Note: unsolvable conflicts can only occur when using ranges or two identical
+ * prefix specifications.
+ */
+static int interval_cmp(const void *p1, const void *p2)
+{
+       const struct elementary_interval *e1 = *(void * const *)p1;
+       const struct elementary_interval *e2 = *(void * const *)p2;
+       mpz_t d;
+       int ret;
+
+       mpz_init(d);
+
+       mpz_sub(d, e2->size, e1->size);
+       if (mpz_cmp_ui(d, 0))
+               ret = mpz_sgn(d);
+       else
+               ret = mpz_cmp(e1->left, e2->left);
+
+       mpz_clear(d);
+       return ret;
+}
+
+static bool interval_conflict(const struct elementary_interval *e1,
+                             const struct elementary_interval *e2)
+{
+       if (mpz_cmp(e1->left, e2->left) <= 0 &&
+           mpz_cmp(e1->right, e2->left) >= 0 &&
+           mpz_cmp(e1->size, e2->size) == 0)
+               return true;
+       else
+               return false;
+}
+
+static void set_to_segtree(struct expr *set, struct seg_tree *tree)
+{
+       struct elementary_interval *intervals[set->size];
+       struct elementary_interval *ei;
+       struct expr *i, *next;
+       unsigned int n;
+       mpz_t low, high;
+
+       mpz_init2(low, tree->dwidth);
+       mpz_init2(high, tree->dwidth);
+
+       /*
+        * Convert elements to intervals and sort by priority.
+        */
+       n = 0;
+       list_for_each_entry_safe(i, next, &set->expressions, list) {
+               range_low(low, i);
+               range_high(high, i);
+               ei = ei_alloc(low, high, i, 0);
+               intervals[n++] = ei;
+
+               list_del(&i->list);
+               expr_free(i);
+       }
+       qsort(intervals, n, sizeof(intervals[0]), interval_cmp);
+
+       /*
+        * Insert elements into tree
+        */
+       for (n = 0; n < set->size; n++) {
+               if (n < set->size - 1 &&
+                   interval_conflict(intervals[n], intervals[n+1]))
+                       printf("conflict\n");
+               ei_insert(tree, intervals[n]);
+       }
+
+       mpz_clear(high);
+       mpz_clear(low);
+}
+
+static void segtree_linearize(struct list_head *list, struct seg_tree *tree)
+{
+       struct rb_node *node, *next;
+       struct elementary_interval *ei, *nei, *prev = NULL;
+       mpz_t p, q;
+
+       mpz_init2(p, tree->dwidth);
+       mpz_init2(q, tree->dwidth);
+
+       /*
+        * Convert the tree of open intervals to half-closed map expressions.
+        */
+       rb_for_each_entry_safe(ei, node, next, &tree->root, rb_node) {
+               pr_debug("iter: [%Zx %Zx]\n", ei->left, ei->right);
+
+               if (prev == NULL) {
+                       /*
+                        * If the first segment doesn't begin at zero, insert a
+                        * non-matching segment to cover [0, first_left).
+                        */
+                       if (mpz_cmp_ui(ei->left, 0)) {
+                               mpz_set_ui(p, 0);
+                               mpz_sub_ui(q, ei->left, 1);
+                               nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
+                               list_add_tail(&nei->list, list);
+                       }
+               } else {
+                       /*
+                        * If the previous segment doesn't end directly left to
+                        * this one, insert a non-matching segment to cover
+                        * (prev_right, ei_left).
+                        */
+                       mpz_add_ui(p, prev->right, 1);
+                       if (mpz_cmp(p, ei->left) < 0) {
+                               mpz_sub_ui(q, ei->left, 1);
+                               nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
+                               list_add_tail(&nei->list, list);
+                       } else if (ei->expr->ops->type != EXPR_MAPPING) {
+                               mpz_set(prev->right, ei->right);
+                               ei_remove(tree, ei);
+                               ei_destroy(ei);
+                               continue;
+                       }
+               }
+
+               ei_remove(tree, ei);
+               list_add_tail(&ei->list, list);
+
+               prev = ei;
+       }
+
+       /*
+        * If the last segment doesn't end at the right side of the dimension,
+        * insert a non-matching segment to cover (last_right, end].
+        */
+       if (mpz_scan0(prev->right, 0) != tree->dwidth) {
+               mpz_add_ui(p, prev->right, 1);
+               mpz_bitmask(q, tree->dwidth);
+               nei = ei_alloc(p, q, NULL, EI_F_INTERVAL_END);
+               list_add_tail(&nei->list, list);
+       }
+
+       mpz_clear(p);
+       mpz_clear(q);
+}
+
+static void set_insert_interval(struct expr *set, struct seg_tree *tree,
+                               const struct elementary_interval *ei)
+{
+       struct expr *expr;
+
+       expr = constant_expr_alloc(&internal_location, tree->type,
+                                  tree->byteorder, tree->dwidth, NULL);
+       mpz_set(expr->value, ei->left);
+
+       if (ei->expr != NULL && ei->expr->ops->type == EXPR_MAPPING)
+               expr = mapping_expr_alloc(&ei->expr->location, expr,
+                                         expr_get(ei->expr->right));
+
+       if (ei->flags & EI_F_INTERVAL_END)
+               expr->flags |= EXPR_F_INTERVAL_END;
+
+       compound_expr_add(set, expr);
+}
+
+void set_to_intervals(struct expr *set)
+{
+       struct elementary_interval *ei, *next;
+       struct seg_tree tree;
+       LIST_HEAD(list);
+
+       seg_tree_init(&tree, set);
+       set_to_segtree(set, &tree);
+       segtree_linearize(&list, &tree);
+
+       list_for_each_entry_safe(ei, next, &list, list) {
+               pr_debug("list: [%.*Zx %.*Zx]\n",
+                        2 * tree.dwidth / BITS_PER_BYTE, ei->left,
+                        2 * tree.dwidth / BITS_PER_BYTE, ei->right);
+               set_insert_interval(set, &tree, ei);
+               ei_destroy(ei);
+       }
+
+       expr_print(set); printf("\n");
+}
+
+static bool range_is_prefix(const mpz_t range)
+{
+       mpz_t tmp;
+
+       mpz_init_set(tmp, range);
+       mpz_add_ui(tmp, tmp, 1);
+       mpz_and(tmp, range, tmp);
+       return !mpz_cmp_ui(tmp, 0);
+}
+
+// FIXME: does not support maps
+extern void interval_map_decompose(struct expr *set);
+
+void interval_map_decompose(struct expr *set)
+{
+       struct expr *ranges[set->size];
+       struct expr *i, *tmp, *prefix, *low = NULL;
+       unsigned int n, size, prefix_len;
+       mpz_t range, p;
+
+       mpz_init(range);
+       mpz_init(p);
+
+       size = set->size;
+       n = 0;
+       list_for_each_entry_safe_reverse(i, tmp, &set->expressions, list) {
+               compound_expr_remove(set, i);
+               ranges[n++] = i;
+       }
+
+       for (n = 0; n < size; n++) {
+               i = ranges[n];
+
+               if (low == NULL) {
+                       if (i->flags & EXPR_F_INTERVAL_END) {
+                               /*
+                                * End of interval mark
+                                */
+                               expr_free(i);
+                               continue;
+                       } else {
+                               /*
+                                * Start a new interval
+                                */
+                               low = i;
+                               continue;
+                       }
+               } else
+                       expr_get(low);
+
+               mpz_sub(range, i->value, low->value);
+               mpz_sub_ui(range, range, 1);
+
+               mpz_and(p, low->value, range);
+
+               if (!mpz_cmp_ui(range, 0))
+                       compound_expr_add(set, low);
+               else if (!range_is_prefix(range) || mpz_cmp_ui(p, 0)) {
+                       mpz_add(range, range, low->value);
+                       tmp = constant_expr_alloc(&low->location, low->dtype,
+                                                 low->byteorder, low->len,
+                                                 NULL);
+                       mpz_set(tmp->value, range);
+
+                       tmp = range_expr_alloc(&low->location, low,tmp);
+                       compound_expr_add(set, tmp);
+               } else {
+                       prefix_len = i->len - mpz_scan0(range, 0);
+                       prefix = prefix_expr_alloc(&low->location, low,
+                                                  prefix_len);
+                       compound_expr_add(set, prefix);
+               }
+
+               if (i->flags & EXPR_F_INTERVAL_END) {
+                       expr_free(low);
+                       low = NULL;
+               }
+               expr_free(i);
+       }
+}
diff --git a/src/statement.c b/src/statement.c
new file mode 100644 (file)
index 0000000..1a3ea3c
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <statement.h>
+#include <utils.h>
+#include <list.h>
+
+struct stmt *stmt_alloc(const struct location *loc,
+                       const struct stmt_ops *ops)
+{
+       struct stmt *stmt;
+
+       stmt = xzalloc(sizeof(*stmt));
+       init_list_head(&stmt->list);
+       stmt->location = *loc;
+       stmt->ops      = ops;
+       return stmt;
+}
+
+void stmt_free(struct stmt *stmt)
+{
+       if (stmt->ops->destroy)
+               stmt->ops->destroy(stmt);
+       xfree(stmt);
+}
+
+void stmt_list_free(struct list_head *list)
+{
+       struct stmt *i, *next;
+
+       list_for_each_entry_safe(i, next, list, list) {
+               list_del(&i->list);
+               stmt_free(i);
+       }
+}
+
+void stmt_print(const struct stmt *stmt)
+{
+       stmt->ops->print(stmt);
+}
+
+static void expr_stmt_print(const struct stmt *stmt)
+{
+       expr_print(stmt->expr);
+}
+
+static void expr_stmt_destroy(struct stmt *stmt)
+{
+       expr_free(stmt->expr);
+}
+
+static const struct stmt_ops expr_stmt_ops = {
+       .type           = STMT_EXPRESSION,
+       .name           = "expression",
+       .print          = expr_stmt_print,
+       .destroy        = expr_stmt_destroy,
+};
+
+struct stmt *expr_stmt_alloc(const struct location *loc, struct expr *expr)
+{
+       struct stmt *stmt;
+
+       stmt = stmt_alloc(loc, &expr_stmt_ops);
+       stmt->expr = expr;
+       return stmt;
+}
+
+static const struct stmt_ops verdict_stmt_ops = {
+       .type           = STMT_VERDICT,
+       .name           = "verdict",
+       .print          = expr_stmt_print,
+       .destroy        = expr_stmt_destroy,
+};
+
+struct stmt *verdict_stmt_alloc(const struct location *loc, struct expr *expr)
+{
+       struct stmt *stmt;
+
+       stmt = stmt_alloc(loc, &verdict_stmt_ops);
+       stmt->expr = expr;
+       return stmt;
+}
+
+static void counter_stmt_print(const struct stmt *stmt)
+{
+       printf("counter packets %" PRIu64 " bytes %" PRIu64,
+              stmt->counter.packets, stmt->counter.bytes);
+}
+
+static const struct stmt_ops counter_stmt_ops = {
+       .type           = STMT_COUNTER,
+       .name           = "counter",
+       .print          = counter_stmt_print,
+};
+
+struct stmt *counter_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &counter_stmt_ops);
+}
+
+static void log_stmt_print(const struct stmt *stmt)
+{
+       printf("log");
+       if (stmt->log.prefix != NULL)
+               printf(" prefix \"%s\"", stmt->log.prefix);
+       if (stmt->log.group)
+               printf(" group %u", stmt->log.group);
+       if (stmt->log.snaplen)
+               printf(" snaplen %u", stmt->log.snaplen);
+       if (stmt->log.qthreshold)
+               printf(" threshold %u", stmt->log.qthreshold);
+}
+
+static void log_stmt_destroy(struct stmt *stmt)
+{
+       xfree(stmt->log.prefix);
+}
+
+static const struct stmt_ops log_stmt_ops = {
+       .type           = STMT_LOG,
+       .name           = "log",
+       .print          = log_stmt_print,
+       .destroy        = log_stmt_destroy,
+};
+
+struct stmt *log_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &log_stmt_ops);
+}
+
+static void limit_stmt_print(const struct stmt *stmt)
+{
+       printf("limit rate %" PRIu64 " depth %" PRIu64,
+              stmt->limit.rate, stmt->limit.depth);
+}
+
+static const struct stmt_ops limit_stmt_ops = {
+       .type           = STMT_LIMIT,
+       .name           = "limit",
+       .print          = limit_stmt_print,
+};
+
+struct stmt *limit_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &limit_stmt_ops);
+}
+
+static void reject_stmt_print(const struct stmt *stmt)
+{
+       printf("reject");
+}
+
+static const struct stmt_ops reject_stmt_ops = {
+       .type           = STMT_REJECT,
+       .name           = "reject",
+       .print          = reject_stmt_print,
+};
+
+struct stmt *reject_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &reject_stmt_ops);
+}
+
+static void nat_stmt_print(const struct stmt *stmt)
+{
+       static const char *nat_types[] = {
+               [NFT_NAT_SNAT]  = "snat",
+               [NFT_NAT_DNAT]  = "dnat",
+       };
+
+       printf("%s ", nat_types[stmt->nat.type]);
+       if (stmt->nat.addr)
+               expr_print(stmt->nat.addr);
+       if (stmt->nat.proto) {
+               printf(":");
+               expr_print(stmt->nat.proto);
+       }
+}
+
+static void nat_stmt_destroy(struct stmt *stmt)
+{
+       expr_free(stmt->nat.addr);
+       expr_free(stmt->nat.proto);
+}
+
+static const struct stmt_ops nat_stmt_ops = {
+       .type           = STMT_NAT,
+       .name           = "nat",
+       .print          = nat_stmt_print,
+       .destroy        = nat_stmt_destroy,
+};
+
+struct stmt *nat_stmt_alloc(const struct location *loc)
+{
+       return stmt_alloc(loc, &nat_stmt_ops);
+}
diff --git a/src/utils.c b/src/utils.c
new file mode 100644 (file)
index 0000000..dcb1c8c
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Development of this code funded by Astaro AG (http://www.astaro.com/)
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <nftables.h>
+#include <utils.h>
+
+void __noreturn memory_allocation_error(void)
+{
+       fprintf(stderr, "Memory allocation failure\n");
+       exit(NFT_EXIT_NOMEM);
+}
+
+void xfree(const void *ptr)
+{
+       free((void *)ptr);
+}
+
+void *xmalloc(size_t size)
+{
+       void *ptr;
+
+       ptr = malloc(size);
+       if (ptr == NULL)
+               memory_allocation_error();
+       return ptr;
+}
+
+void *xrealloc(void *ptr, size_t size)
+{
+       assert(ptr != NULL);
+       ptr = realloc(ptr, size);
+       if (ptr == NULL && size != 0)
+               memory_allocation_error();
+       return ptr;
+}
+
+void *xzalloc(size_t size)
+{
+       void *ptr;
+
+       ptr = xmalloc(size);
+       memset(ptr, 0, size);
+       return ptr;
+}
+
+char *xstrdup(const char *s)
+{
+       char *res;
+
+       assert(s != NULL);
+       res = strdup(s);
+       if (res == NULL)
+               memory_allocation_error();
+       return res;
+}
diff --git a/tests/dictionary b/tests/dictionary
new file mode 100755 (executable)
index 0000000..20d5357
--- /dev/null
@@ -0,0 +1,52 @@
+#! nft -f
+#
+table add ip filter
+chain add ip filter OUTPUT NF_INET_LOCAL_OUT 0
+
+chain add ip filter chain1
+rule add ip filter chain1 handle 1 counter
+
+chain add ip filter chain2
+rule add ip filter chain2 handle 1 counter
+
+# must succeed: expr { expr, ... }
+rule add ip filter OUTPUT ip protocol 6 tcp dport { \
+       22, \
+       23, \
+}
+
+# must fail: expr { type1, type2, ... }
+rule add ip filter OUTPUT ip protocol 6 tcp dport { \
+       22, \
+       192.168.0.1, \
+}
+
+# must succeed: expr { expr => verdict, ... }
+rule add ip filter OUTPUT ip protocol 6 tcp dport { \
+       22 => jump chain1, \
+       23 => jump chain2, \
+}
+
+# must fail: expr { expr => verdict, expr => expr, ... }
+rule add ip filter OUTPUT ip protocol 6 tcp dport { \
+       22 => jump chain1, \
+       23 => 0x100, \
+}
+
+# must fail: expr { expr => expr, ...}
+rule add ip filter OUTPUT ip protocol 6 tcp dport { \
+       22 => 0x100, \
+       23 => 0x200, \
+}
+
+# must succeed: expr MAP { expr => expr, ... } expr
+rule add ip filter OUTPUT ip protocol 6 map tcp dport { \
+       22 => 1, \
+       23 => 2, \
+} 2
+
+# must fail: expr MAP { expr => type1, expr => type2, .. } expr
+rule add ip filter OUTPUT ip protocol 6 map tcp dport { \
+       22 => 1, \
+       23 => 192.168.0.1, \
+} 2
diff --git a/tests/expr-concat b/tests/expr-concat
new file mode 100755 (executable)
index 0000000..d9c553b
--- /dev/null
@@ -0,0 +1,19 @@
+#! nft -f
+
+# Concat element mismatch
+rule add ip filter output ip daddr . tcp sport . tcp dport { \
+       192.168.0.1 . 22,       \
+       192.168.0.1 . 80,       \
+}
+
+# Concat type mismatch
+rule add ip filter output ip daddr . tcp dport { \
+       192.168.0.1 . 192.168.0.2,      \
+       192.168.0.1 . 192.168.0.3,      \
+}
+
+# Concat expression
+rule add ip filter output ip daddr . tcp dport { \
+       192.168.0.1 . 22,       \
+       192.168.0.1 . 80,       \
+}
diff --git a/tests/expr-ct b/tests/expr-ct
new file mode 100755 (executable)
index 0000000..8391c3e
--- /dev/null
@@ -0,0 +1,26 @@
+#! nft -f
+
+table add ip filter
+chain add ip filter output NF_INET_LOCAL_OUT 0
+
+# ct: state
+rule add ip filter output ct state 0 counter
+
+# ct: direction original/reply
+rule add ip filter output ct direction 0 counter
+rule add ip filter output ct direction 1 counter
+
+# ct: status
+rule add ip filter output ct status 0 counter
+
+# ct: mark
+rule add ip filter output ct mark 0 counter
+
+# ct: secmark
+rule add ip filter output ct secmark 0 counter
+
+# ct: expiration
+rule add ip filter output ct expiration 30 counter
+
+# ct: helper ftp
+rule add ip filter output ct helper "ftp" counter
diff --git a/tests/expr-meta b/tests/expr-meta
new file mode 100755 (executable)
index 0000000..122d289
--- /dev/null
@@ -0,0 +1,40 @@
+#! nft -f
+
+table add ip filter
+chain add ip filter output NF_INET_LOCAL_OUT 0
+
+# meta: skb len
+rule add ip filter output meta length 1000 counter
+
+# meta: skb protocol
+rule add ip filter output meta protocol 0x0800 counter
+
+# meta: skb mark
+rule add ip filter output meta mark 0 counter
+
+# meta: skb iif
+rule add ip filter output meta iif 1 counter
+
+# meta: skb iifname
+rule add ip filter output meta iifname "eth0" counter
+
+# meta: skb oif
+rule add ip filter output meta oif 1 counter
+
+# meta: skb oifname
+rule add ip filter output meta oifname "eth0" counter
+
+# meta: skb sk uid
+rule add ip filter output meta skuid 1000 counter
+
+# meta: skb sk gid
+rule add ip filter output meta skgid 1000 counter
+
+# meta: nftrace - broken, probably should be removed to avoid abuse
+#rule add ip filter output meta nftrace 0 counter
+
+# meta: rtclassid
+rule add ip filter output meta rtclassid 1 counter
+
+# meta: secmark
+rule add ip filter output meta secmark 0 counter
diff --git a/tests/family-bridge b/tests/family-bridge
new file mode 100755 (executable)
index 0000000..633211f
--- /dev/null
@@ -0,0 +1,13 @@
+#! nft -f
+
+table add bridge filter
+chain add bridge filter output NF_INET_LOCAL_OUT 0
+
+# LL protocol
+rule add bridge filter output eth type 0x0800 counter
+
+# IP address
+rule add bridge filter output eth type 0x0800 ip daddr 20.0.0.2 counter
+
+# IPv6 address
+rule add bridge filter output eth type 0x86DD ip6 daddr 2001:6f8:974:3::2 counter
diff --git a/tests/family-ipv4 b/tests/family-ipv4
new file mode 100755 (executable)
index 0000000..7c28bb4
--- /dev/null
@@ -0,0 +1,10 @@
+#! nft -f
+
+table add ip filter
+chain add ip filter output NF_INET_LOCAL_OUT 0
+
+# IP address
+rule add ip filter output ip daddr 192.168.0.1 counter
+
+# TCP ports
+rule add ip filter output ip protocol 6 tcp dport 22 counter
diff --git a/tests/family-ipv6 b/tests/family-ipv6
new file mode 100755 (executable)
index 0000000..7cf54d6
--- /dev/null
@@ -0,0 +1,10 @@
+#! nft -f
+
+table add ip6 filter
+chain add ip6 filter output NF_INET_LOCAL_OUT 0
+
+# IP address
+rule add ip6 filter output ip6 daddr 2001:6f8:974::1 counter
+
+# TCP ports
+rule add ip6 filter output ip6 nexthdr 6 tcp dport 22 counter
diff --git a/tests/feat-adjancent-load-merging b/tests/feat-adjancent-load-merging
new file mode 100755 (executable)
index 0000000..9c41f50
--- /dev/null
@@ -0,0 +1,13 @@
+#! nft -f
+
+# adjacent payload expressions: 4 bytes in order
+rule add filter output ip protocol 6 tcp sport 1024 tcp dport 22 counter
+
+# adjacent payload expressions: 8 bytes in order
+rule add filter output ip saddr 192.168.0.1 ip daddr 192.168.0.100 counter
+
+# adjacent payload expressions: 8 bytes in order
+rule add filter output tcp sequence 0 tcp sport 1024 tcp dport 22
+
+# adjacent payload expressions: 8 bytes in reverse order
+rule add filter output tcp sport 1024 tcp dport 22 tcp sequence 0
diff --git a/tests/obj-chain b/tests/obj-chain
new file mode 100755 (executable)
index 0000000..8422f3a
--- /dev/null
@@ -0,0 +1,22 @@
+#! nft -f
+
+table add filter
+
+# chains: add and delete chain
+chain add filter testchain
+chain delete filter testchain
+
+# chains: add and delete base chain
+chain add filter input NF_INET_LOCAL_OUT 0
+chain delete filter input NF_INET_LOCAL_OUT 0
+
+# chains: can not delete chain while referenced
+chain add filter testchain
+chain add filter testchain2
+
+rule add filter testchain handle 1 jump testchain2
+chain delete filter testchain2
+rule delete filter testchain handle 1
+
+chain delete filter testchain2
+chain delete filter testchain
diff --git a/tests/obj-table b/tests/obj-table
new file mode 100755 (executable)
index 0000000..3c3e222
--- /dev/null
@@ -0,0 +1,11 @@
+#! nft -f
+
+# table: add and delete table
+table add filter
+table delete filter
+
+# table: deleting table with chain must fail
+# FIXME: not implemented
+# table add filter
+# chain add filter output
+# table delete filter
diff --git a/tests/payload-ll b/tests/payload-ll
new file mode 100755 (executable)
index 0000000..8d2480a
--- /dev/null
@@ -0,0 +1,15 @@
+#! nft -f
+
+table add ip filter
+chain add ip filter input NF_INET_LOCAL_IN 0
+
+# mac source
+rule add ip filter input @ll,48,48 00:15:e9:f0:10:f8 counter
+
+# mac dest
+rule add ip filter input @ll,0,48 00:1b:21:02:6f:ad counter
+
+# mac source and mac dest
+rule add ip filter input @ll,0,48 00:1b:21:02:6f:ad \
+                        @ll,48,48 00:15:e9:f0:10:f8 \
+                        counter
diff --git a/tests/prefix b/tests/prefix
new file mode 100755 (executable)
index 0000000..139f13c
--- /dev/null
@@ -0,0 +1,5 @@
+rule add filter OUTPUT meta mark 123/0x000000ff
+rule add filter OUTPUT ip daddr 192.168.0.0/24
+rule add filter OUTPUT ip daddr 192.168.0.0/255.255.255.0
+rule add filter OUTPUT ip saddr . ip daddr 192.168.0.0/24 . 192.168.0.0/24
+rule add filter OUTPUT ip daddr { 192.168.0.0/24, 192.168.1.0/24}
diff --git a/tests/set b/tests/set
new file mode 100755 (executable)
index 0000000..17fb218
--- /dev/null
+++ b/tests/set
@@ -0,0 +1,14 @@
+#! nft -f
+
+table add filter
+chain add filter output NF_INET_LOCAL_OUT 0
+
+# set: IP addresses
+rule add filter output ip daddr { \
+       192.168.0.1, \
+       192.168.0.2, \
+       192.168.0.3, \
+}
+
+# set: tcp ports
+rule add filter output ip protocol 6 tcp dport { 22, 23 } counter
diff --git a/tests/stmt-log b/tests/stmt-log
new file mode 100755 (executable)
index 0000000..c2d0f86
--- /dev/null
@@ -0,0 +1,6 @@
+#! nft -f
+
+table add ip filter
+chain add ip filter output NF_INET_LOCAL_OUT 0
+
+rule add ip filter output log saddr "prefix" group 0 counter