summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2009-03-18 04:55:00 +0100
committerPatrick McHardy <kaber@trash.net>2009-03-18 04:55:00 +0100
commitfac10ea799fe9b6158d74f66d6ad46536d38a545 (patch)
tree8c093bcbb2144aab54c70103e6ed438456ae0d48
Initial commitv0.01-alpha1
-rw-r--r--.gitignore18
-rw-r--r--COPYING349
-rw-r--r--Makefile.defs.in40
-rw-r--r--Makefile.in5
-rw-r--r--Makefile.rules.in85
-rw-r--r--TODO27
-rwxr-xr-xautogen.sh4
-rw-r--r--configure.ac99
-rw-r--r--debian/.gitignore4
-rw-r--r--debian/changelog6
-rw-r--r--debian/compat1
-rw-r--r--debian/control13
-rw-r--r--debian/copyright4
-rw-r--r--debian/nftables.install1
-rwxr-xr-xdebian/rules20
-rw-r--r--doc/.gitignore2
-rw-r--r--doc/Makefile.in14
-rw-r--r--doc/nftables.xml966
-rw-r--r--files/Makefile.in4
-rw-r--r--files/nftables/ipv4-filter7
-rw-r--r--files/nftables/ipv4-mangle9
-rw-r--r--files/nftables/ipv4-raw6
-rw-r--r--files/nftables/ipv4-security7
-rw-r--r--files/nftables/ipv6-filter7
-rw-r--r--files/nftables/ipv6-mangle9
-rw-r--r--files/nftables/ipv6-security7
-rw-r--r--include/ct.h29
-rw-r--r--include/datatype.h156
-rw-r--r--include/erec.h64
-rw-r--r--include/expression.h323
-rw-r--r--include/exthdr.h84
-rw-r--r--include/gmputil.h42
-rw-r--r--include/headers.h119
-rw-r--r--include/linux/netfilter.h59
-rw-r--r--include/linux/netfilter/nf_conntrack_common.h152
-rw-r--r--include/linux/netfilter/nf_conntrack_tuple_common.h13
-rw-r--r--include/linux/netfilter/nf_tables.h337
-rw-r--r--include/linux/netfilter_arp.h19
-rw-r--r--include/linux/netfilter_bridge.h27
-rw-r--r--include/linux/netfilter_decnet.h72
-rw-r--r--include/linux/netfilter_ipv4.h75
-rw-r--r--include/linux/netfilter_ipv6.h72
-rw-r--r--include/list.h625
-rw-r--r--include/meta.h29
-rw-r--r--include/netlink.h60
-rw-r--r--include/nftables.h96
-rw-r--r--include/parser.h36
-rw-r--r--include/payload.h289
-rw-r--r--include/rbtree.h98
-rw-r--r--include/rule.h174
-rw-r--r--include/statement.h140
-rw-r--r--include/utils.h69
-rwxr-xr-xinstall-sh269
-rw-r--r--src/.gitignore5
-rw-r--r--src/Makefile.in30
-rw-r--r--src/cli.c175
-rw-r--r--src/ct.c149
-rw-r--r--src/datatype.c568
-rw-r--r--src/erec.c159
-rw-r--r--src/evaluate.c1031
-rw-r--r--src/expression.c622
-rw-r--r--src/exthdr.c238
-rw-r--r--src/gmputil.c156
-rw-r--r--src/main.c202
-rw-r--r--src/meta.c347
-rw-r--r--src/netlink.c476
-rw-r--r--src/netlink_delinearize.c781
-rw-r--r--src/netlink_linearize.c720
-rw-r--r--src/parser-skeleton.c1529
-rw-r--r--src/parser.y1386
-rw-r--r--src/payload.c908
-rw-r--r--src/rbtree.c388
-rw-r--r--src/rule.c441
-rw-r--r--src/scanner.l581
-rw-r--r--src/segtree.c541
-rw-r--r--src/statement.c210
-rw-r--r--src/utils.c69
-rwxr-xr-xtests/dictionary52
-rwxr-xr-xtests/expr-concat19
-rwxr-xr-xtests/expr-ct26
-rwxr-xr-xtests/expr-meta40
-rwxr-xr-xtests/family-bridge13
-rwxr-xr-xtests/family-ipv410
-rwxr-xr-xtests/family-ipv610
-rwxr-xr-xtests/feat-adjancent-load-merging13
-rwxr-xr-xtests/obj-chain22
-rwxr-xr-xtests/obj-table11
-rwxr-xr-xtests/payload-ll15
-rwxr-xr-xtests/prefix5
-rwxr-xr-xtests/set14
-rwxr-xr-xtests/stmt-log6
91 files changed, 17210 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..d26b3958
--- /dev/null
+++ b/.gitignore
@@ -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
index 00000000..bf7f06eb
--- /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.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+
+ 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
index 00000000..545c4eea
--- /dev/null
+++ b/Makefile.defs.in
@@ -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
index 00000000..5d425419
--- /dev/null
+++ b/Makefile.in
@@ -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
index 00000000..7ee9a078
--- /dev/null
+++ b/Makefile.rules.in
@@ -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
index 00000000..20ab42d5
--- /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
index 00000000..a62e7373
--- /dev/null
+++ b/autogen.sh
@@ -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
index 00000000..d8a850cf
--- /dev/null
+++ b/configure.ac
@@ -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
index 00000000..b1e1dfb4
--- /dev/null
+++ b/debian/.gitignore
@@ -0,0 +1,4 @@
+debhelper.log
+nftables.debhelper.log
+nftables/
+tmp/
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 00000000..32b17fa1
--- /dev/null
+++ b/debian/changelog
@@ -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
index 00000000..7f8f011e
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+7
diff --git a/debian/control b/debian/control
new file mode 100644
index 00000000..e0062787
--- /dev/null
+++ b/debian/control
@@ -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
index 00000000..d6e4857a
--- /dev/null
+++ b/debian/copyright
@@ -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
index 00000000..92e31e96
--- /dev/null
+++ b/debian/nftables.install
@@ -0,0 +1 @@
+src/nft /sbin
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 00000000..11988d5b
--- /dev/null
+++ b/debian/rules
@@ -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
index 00000000..fc24d499
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,2 @@
+nftables.8
+nftables.pdf
diff --git a/doc/Makefile.in b/doc/Makefile.in
new file mode 100644
index 00000000..497e17ab
--- /dev/null
+++ b/doc/Makefile.in
@@ -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
index 00000000..ec6de38a
--- /dev/null
+++ b/doc/nftables.xml
@@ -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
index 00000000..cd67c37d
--- /dev/null
+++ b/files/Makefile.in
@@ -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
index 00000000..3f962143
--- /dev/null
+++ b/files/nftables/ipv4-filter
@@ -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
index 00000000..339cacea
--- /dev/null
+++ b/files/nftables/ipv4-mangle
@@ -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
index 00000000..6bc21815
--- /dev/null
+++ b/files/nftables/ipv4-raw
@@ -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
index 00000000..3ac5bd2b
--- /dev/null
+++ b/files/nftables/ipv4-security
@@ -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
index 00000000..9e412784
--- /dev/null
+++ b/files/nftables/ipv6-filter
@@ -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
index 00000000..dc18c7a8
--- /dev/null
+++ b/files/nftables/ipv6-mangle
@@ -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
index 00000000..f379bfd3
--- /dev/null
+++ b/files/nftables/ipv6-security
@@ -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
index 00000000..946bc297
--- /dev/null
+++ b/include/ct.h
@@ -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
index 00000000..c6aeeeda
--- /dev/null
+++ b/include/datatype.h
@@ -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
index 00000000..51f39588
--- /dev/null
+++ b/include/erec.h
@@ -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
index 00000000..72585469
--- /dev/null
+++ b/include/expression.h
@@ -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
index 00000000..8ef0b395
--- /dev/null
+++ b/include/exthdr.h
@@ -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
index 00000000..41a6c270
--- /dev/null
+++ b/include/gmputil.h
@@ -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
index 00000000..be20cec0
--- /dev/null
+++ b/include/headers.h
@@ -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
index 00000000..2eb00b6c
--- /dev/null
+++ b/include/linux/netfilter.h
@@ -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
index 00000000..27a18952
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -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
index 00000000..8e145f0d
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_tuple_common.h
@@ -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
index 00000000..b4d518e3
--- /dev/null
+++ b/include/linux/netfilter/nf_tables.h
@@ -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
index 00000000..92bc6ddc
--- /dev/null
+++ b/include/linux/netfilter_arp.h
@@ -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
index 00000000..5094ecca
--- /dev/null
+++ b/include/linux/netfilter_bridge.h
@@ -0,0 +1,27 @@
+#ifndef __LINUX_BRIDGE_NETFILTER_H
+#define __LINUX_BRIDGE_NETFILTER_H
+
+/* bridge-specific defines for netfilter.
+ */
+
+#include <linux/netfilter.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/if_pppox.h>
+
+/* Bridge Hooks */
+/* After promisc drops, checksum checks. */
+#define NF_BR_PRE_ROUTING 0
+/* If the packet is destined for this box. */
+#define NF_BR_LOCAL_IN 1
+/* If the packet is destined for another interface. */
+#define NF_BR_FORWARD 2
+/* Packets coming from a local process. */
+#define NF_BR_LOCAL_OUT 3
+/* Packets about to hit the wire. */
+#define NF_BR_POST_ROUTING 4
+/* Not really a hook, but used for the ebtables broute table */
+#define NF_BR_BROUTING 5
+#define NF_BR_NUMHOOKS 6
+
+#endif
diff --git a/include/linux/netfilter_decnet.h b/include/linux/netfilter_decnet.h
new file mode 100644
index 00000000..ca70c6cd
--- /dev/null
+++ b/include/linux/netfilter_decnet.h
@@ -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
index 00000000..4d7ba3e4
--- /dev/null
+++ b/include/linux/netfilter_ipv4.h
@@ -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
index 00000000..7430b392
--- /dev/null
+++ b/include/linux/netfilter_ipv6.h
@@ -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
index 00000000..75d29212
--- /dev/null
+++ b/include/list.h
@@ -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
index 00000000..75b1f1eb
--- /dev/null
+++ b/include/meta.h
@@ -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
index 00000000..4849ff51
--- /dev/null
+++ b/include/netlink.h
@@ -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
index 00000000..7e4a2fc8
--- /dev/null
+++ b/include/nftables.h
@@ -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
index 00000000..2a4c73b4
--- /dev/null
+++ b/include/parser.h
@@ -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
index 00000000..ff9b1381
--- /dev/null
+++ b/include/payload.h
@@ -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
index 00000000..26b6b44a
--- /dev/null
+++ b/include/rbtree.h
@@ -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
index 00000000..e4ad9f58
--- /dev/null
+++ b/include/rule.h
@@ -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
index 00000000..6e5cfbdb
--- /dev/null
+++ b/include/statement.h
@@ -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
index 00000000..beb63868
--- /dev/null
+++ b/include/utils.h
@@ -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
index 00000000..d4744f0c
--- /dev/null
+++ b/install-sh
@@ -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
index 00000000..23e6ae03
--- /dev/null
+++ b/src/.gitignore
@@ -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
index 00000000..3c933070
--- /dev/null
+++ b/src/Makefile.in
@@ -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
index 00000000..e302dfa8
--- /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
index 00000000..00895394
--- /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
index 00000000..8e17c218
--- /dev/null
+++ b/src/datatype.c
@@ -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
index 00000000..501bf4b6
--- /dev/null
+++ b/src/erec.c
@@ -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
index 00000000..0deff9ad
--- /dev/null
+++ b/src/evaluate.c
@@ -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
index 00000000..66a8793f
--- /dev/null
+++ b/src/expression.c
@@ -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
index 00000000..2defc7cb
--- /dev/null
+++ b/src/exthdr.c
@@ -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
index 00000000..f34c077e
--- /dev/null
+++ b/src/gmputil.c
@@ -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
index 00000000..f7686ae3
--- /dev/null
+++ b/src/main.c
@@ -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
index 00000000..cb9e495c
--- /dev/null
+++ b/src/meta.c
@@ -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
index 00000000..4700cd7e
--- /dev/null
+++ b/src/netlink.c
@@ -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
index 00000000..be2271ce
--- /dev/null
+++ b/src/netlink_delinearize.c
@@ -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
index 00000000..fcb111c6
--- /dev/null
+++ b/src/netlink_linearize.c
@@ -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
index 00000000..978f7c48
--- /dev/null
+++ b/src/parser-skeleton.c
@@ -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
+
+
+
+#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 */
+
+
+]b4_yydestruct_generate([b4_c_function_def])[
+
+
+/* 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
index 00000000..c3ade912
--- /dev/null
+++ b/src/parser.y
@@ -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
index 00000000..b7fbcb36
--- /dev/null
+++ b/src/payload.c
@@ -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
index 00000000..325c0123
--- /dev/null
+++ b/src/rbtree.c
@@ -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
index 00000000..e86c78aa
--- /dev/null
+++ b/src/rule.c
@@ -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
index 00000000..dc6341da
--- /dev/null
+++ b/src/scanner.l
@@ -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
index 00000000..9e59bb6e
--- /dev/null
+++ b/src/segtree.c
@@ -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
index 00000000..1a3ea3c1
--- /dev/null
+++ b/src/statement.c
@@ -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
index 00000000..dcb1c8c6
--- /dev/null
+++ b/src/utils.c
@@ -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
index 00000000..20d53570
--- /dev/null
+++ b/tests/dictionary
@@ -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
index 00000000..d9c553b6
--- /dev/null
+++ b/tests/expr-concat
@@ -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
index 00000000..8391c3ec
--- /dev/null
+++ b/tests/expr-ct
@@ -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
index 00000000..122d2895
--- /dev/null
+++ b/tests/expr-meta
@@ -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
index 00000000..633211fb
--- /dev/null
+++ b/tests/family-bridge
@@ -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
index 00000000..7c28bb43
--- /dev/null
+++ b/tests/family-ipv4
@@ -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
index 00000000..7cf54d6f
--- /dev/null
+++ b/tests/family-ipv6
@@ -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
index 00000000..9c41f509
--- /dev/null
+++ b/tests/feat-adjancent-load-merging
@@ -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
index 00000000..8422f3a3
--- /dev/null
+++ b/tests/obj-chain
@@ -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
index 00000000..3c3e222a
--- /dev/null
+++ b/tests/obj-table
@@ -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
index 00000000..8d2480ac
--- /dev/null
+++ b/tests/payload-ll
@@ -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
index 00000000..139f13cc
--- /dev/null
+++ b/tests/prefix
@@ -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
index 00000000..17fb2183
--- /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
index 00000000..c2d0f868
--- /dev/null
+++ b/tests/stmt-log
@@ -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