From fac10ea799fe9b6158d74f66d6ad46536d38a545 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 18 Mar 2009 04:55:00 +0100 Subject: Initial commit --- .gitignore | 18 + COPYING | 349 +++++ Makefile.defs.in | 40 + Makefile.in | 5 + Makefile.rules.in | 85 ++ TODO | 27 + autogen.sh | 4 + configure.ac | 99 ++ debian/.gitignore | 4 + debian/changelog | 6 + debian/compat | 1 + debian/control | 13 + debian/copyright | 4 + debian/nftables.install | 1 + debian/rules | 20 + doc/.gitignore | 2 + doc/Makefile.in | 14 + doc/nftables.xml | 966 +++++++++++++ files/Makefile.in | 4 + files/nftables/ipv4-filter | 7 + files/nftables/ipv4-mangle | 9 + files/nftables/ipv4-raw | 6 + files/nftables/ipv4-security | 7 + files/nftables/ipv6-filter | 7 + files/nftables/ipv6-mangle | 9 + files/nftables/ipv6-security | 7 + include/ct.h | 29 + include/datatype.h | 156 ++ include/erec.h | 64 + include/expression.h | 323 +++++ include/exthdr.h | 84 ++ include/gmputil.h | 42 + include/headers.h | 119 ++ include/linux/netfilter.h | 59 + include/linux/netfilter/nf_conntrack_common.h | 152 ++ .../linux/netfilter/nf_conntrack_tuple_common.h | 13 + include/linux/netfilter/nf_tables.h | 337 +++++ include/linux/netfilter_arp.h | 19 + include/linux/netfilter_bridge.h | 27 + include/linux/netfilter_decnet.h | 72 + include/linux/netfilter_ipv4.h | 75 + include/linux/netfilter_ipv6.h | 72 + include/list.h | 625 ++++++++ include/meta.h | 29 + include/netlink.h | 60 + include/nftables.h | 96 ++ include/parser.h | 36 + include/payload.h | 289 ++++ include/rbtree.h | 98 ++ include/rule.h | 174 +++ include/statement.h | 140 ++ include/utils.h | 69 + install-sh | 269 ++++ src/.gitignore | 5 + src/Makefile.in | 30 + src/cli.c | 175 +++ src/ct.c | 149 ++ src/datatype.c | 568 ++++++++ src/erec.c | 159 ++ src/evaluate.c | 1031 +++++++++++++ src/expression.c | 622 ++++++++ src/exthdr.c | 238 +++ src/gmputil.c | 156 ++ src/main.c | 202 +++ src/meta.c | 347 +++++ src/netlink.c | 476 ++++++ src/netlink_delinearize.c | 781 ++++++++++ src/netlink_linearize.c | 720 +++++++++ src/parser-skeleton.c | 1529 ++++++++++++++++++++ src/parser.y | 1386 ++++++++++++++++++ src/payload.c | 908 ++++++++++++ src/rbtree.c | 388 +++++ src/rule.c | 441 ++++++ src/scanner.l | 581 ++++++++ src/segtree.c | 541 +++++++ src/statement.c | 210 +++ src/utils.c | 69 + tests/dictionary | 52 + tests/expr-concat | 19 + tests/expr-ct | 26 + tests/expr-meta | 40 + tests/family-bridge | 13 + tests/family-ipv4 | 10 + tests/family-ipv6 | 10 + tests/feat-adjancent-load-merging | 13 + tests/obj-chain | 22 + tests/obj-table | 11 + tests/payload-ll | 15 + tests/prefix | 5 + tests/set | 14 + tests/stmt-log | 6 + 91 files changed, 17210 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile.defs.in create mode 100644 Makefile.in create mode 100644 Makefile.rules.in create mode 100644 TODO create mode 100755 autogen.sh create mode 100644 configure.ac create mode 100644 debian/.gitignore create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/nftables.install create mode 100755 debian/rules create mode 100644 doc/.gitignore create mode 100644 doc/Makefile.in create mode 100644 doc/nftables.xml create mode 100644 files/Makefile.in create mode 100644 files/nftables/ipv4-filter create mode 100644 files/nftables/ipv4-mangle create mode 100644 files/nftables/ipv4-raw create mode 100644 files/nftables/ipv4-security create mode 100644 files/nftables/ipv6-filter create mode 100644 files/nftables/ipv6-mangle create mode 100644 files/nftables/ipv6-security create mode 100644 include/ct.h create mode 100644 include/datatype.h create mode 100644 include/erec.h create mode 100644 include/expression.h create mode 100644 include/exthdr.h create mode 100644 include/gmputil.h create mode 100644 include/headers.h create mode 100644 include/linux/netfilter.h create mode 100644 include/linux/netfilter/nf_conntrack_common.h create mode 100644 include/linux/netfilter/nf_conntrack_tuple_common.h create mode 100644 include/linux/netfilter/nf_tables.h create mode 100644 include/linux/netfilter_arp.h create mode 100644 include/linux/netfilter_bridge.h create mode 100644 include/linux/netfilter_decnet.h create mode 100644 include/linux/netfilter_ipv4.h create mode 100644 include/linux/netfilter_ipv6.h create mode 100644 include/list.h create mode 100644 include/meta.h create mode 100644 include/netlink.h create mode 100644 include/nftables.h create mode 100644 include/parser.h create mode 100644 include/payload.h create mode 100644 include/rbtree.h create mode 100644 include/rule.h create mode 100644 include/statement.h create mode 100644 include/utils.h create mode 100755 install-sh create mode 100644 src/.gitignore create mode 100644 src/Makefile.in create mode 100644 src/cli.c create mode 100644 src/ct.c create mode 100644 src/datatype.c create mode 100644 src/erec.c create mode 100644 src/evaluate.c create mode 100644 src/expression.c create mode 100644 src/exthdr.c create mode 100644 src/gmputil.c create mode 100644 src/main.c create mode 100644 src/meta.c create mode 100644 src/netlink.c create mode 100644 src/netlink_delinearize.c create mode 100644 src/netlink_linearize.c create mode 100644 src/parser-skeleton.c create mode 100644 src/parser.y create mode 100644 src/payload.c create mode 100644 src/rbtree.c create mode 100644 src/rule.c create mode 100644 src/scanner.l create mode 100644 src/segtree.c create mode 100644 src/statement.c create mode 100644 src/utils.c create mode 100755 tests/dictionary create mode 100755 tests/expr-concat create mode 100755 tests/expr-ct create mode 100755 tests/expr-meta create mode 100755 tests/family-bridge create mode 100755 tests/family-ipv4 create mode 100755 tests/family-ipv6 create mode 100755 tests/feat-adjancent-load-merging create mode 100755 tests/obj-chain create mode 100755 tests/obj-table create mode 100755 tests/payload-ll create mode 100755 tests/prefix create mode 100755 tests/set create mode 100755 tests/stmt-log 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 + +------------------------------------------------------------------------------- + + 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. + + + Copyright (C) 19yy + + 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. + + , 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 ]) +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 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 +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 @@ + + + + + + + Patrick + McHardy + kaber@trash.net + + + 2008 + Patrick McHardy + + + + + nftables + 8 + + + + nftables + + Administration tool for packet filtering and classification + + + + + + nftables + + + + + + directory + + + + + filename + + + + + + cmd + + + + + nftables + + + + + + + + + + + Description + + nftables is used to set up, maintain and inspect packet + filtering and classification rules in the Linux kernel. + + + + + Options + + For a full summary of options, run nftables --help. + + + + + + + + Show help message and all options. + + + + + + + + Show version. + + + + + + + + Numeric output: IP addresses and other information + that might need network traffic to resolve to symbolic names + are shown numerically. + + + + + + + + Add the directory directory to the list of directories to by searched for included files. + + + + + + + + Read input from filename. + + + + + + + + Read input from an interactive readline CLI. + + + + + + + + Input file format + + Input is parsed line-wise. When the last character of a line just before + the newline character is a non-quoted backslash (\), + the newline is treated as a line continuation. + + + A # begins a comment. All following characters on + the same line are ignored. + + + Other files can be included by using + include "filename". + + + + + Tables + + + table + + add + delete + list + flush + + family + table + + + + + Tables are containers for chains. They are identified by their family + and their name. The family must be one of + + + ip + ip6 + arp + bridge + . + + When no family is specified, ip is used by default. + + + + + + + + Add a new table for the given family with the given name. + + + + + + + + Delete the specified table. + + + + + + + + List all chains and rules of the specified table. + + + + + + + + Flush all chains and rules of the specified table. + + + + + + + + Chains + + + chain + add + family + table + chain + hook + priority + + + chain + + add + delete + list + flush + + family + table + chain + + + + + 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. + + + + + + + + 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. + + + + + + + + Delete the specified chain. + + + + + + + + List all rules of the specified chain. + + + + + + + + Flush all rules of the specified chain. + + + + + + + + Rules + + + rule + + add + delete + + family + table + chain + handle handle + statement + + + + 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. + + + + + Primary expressions + + Meta expressions + + A meta expression refers to meta data associated with a packet. + + + Meta expressions + + + + + + + Keyword + Description + Type + + + + + length + Length of the packet in bytes + Numeric (32 bit) + + + protocol + Ethertype protocol value + ethertype + + + priority + TC packet priority + Numeric (32 bit) + + + mark + Packet mark + packetmark + + + iif + Input interface index + ifindex + + + iifname + Input interface name + ifname + + + iiftype + Input interface hardware type + hwtype + + + oif + Output interface index + ifindex + + + oifname + Output interface name + ifname + + + oiftype + Output interface hardware type + hwtype + + + skuid + UID associated with originating socket + uid + + + skgid + GID associated with originating socket + gid + + + rtclassid + Routing realm + realm + + + +
+ + Meta expression specific types + + + + + + Type + Description + + + + + ifindex + + Interface index (32 bit number). Can be specified numerically + or as name of an existing interface. + + + + ifname + + Interface name (16 byte string). Does not have to exist. + + + + uid + + User ID (32 bit number). Can be specified numerically or as + user name. + + + + gid + + Group ID (32 bit number). Can be specified numerically or as + group name. + + + + realm + + Routing Realm (32 bit number). Can be specified numerically + or as symbolic name defined in /etc/iproute2/rt_realms. + + + + +
+
+ + + Payload expressions + + Ethernet header expression + + + + + + Keyword + Description + + + + + daddr + Destination address + + + saddr + Source address + + + type + EtherType + + + +
+ + + VLAN header expression + + + + + + Keyword + Description + + + + + id + VLAN ID (VID) + + + cfi + Canonical Format Indicator + + + pcp + Priority code point + + + type + EtherType + + + +
+ + + ARP header expression + + + + + + Keyword + Description + + + + + htype + ARP hardware type + + + ptype + EtherType + + + hlen + Hardware address len + + + plen + Protocol address len + + + op + Operation + + + +
+ + + IPv4 header expression + + + + + + Keyword + Description + + + + + version + IP header version (4) + + + hdrlength + IP header length including options + + + tos + Type Of Service + + + length + Total packet length + + + id + IP ID + + + frag-off + Fragment offset + + + ttl + Time to live + + + protocol + Upper layer protocol + + + checksum + IP header checksum + + + saddr + Source address + + + daddr + Destination address + + + +
+ + + IPv6 header expression + + + + + + Keyword + Description + + + + + version + IP header version (6) + + + priority + + + + flowlabel + + + + length + + + + nexthdr + Nexthdr protocol + + + hoplimit + + + + saddr + Source address + + + daddr + Destination address + + + +
+ + + SCTP header expression + + + + + + Keyword + Description + + + + + sport + Source port + + + dport + Destination port + + + vtag + Verfication Tag + + + checksum + Checksum + + + +
+ + + DCCP header expression + + + + + + Keyword + Description + + + + + sport + Source port + + + dport + Destination port + + + +
+ + + TCP header expression + + + + + + Keyword + Description + + + + + sport + Source port + + + dport + Destination port + + + sequence + Sequence number + + + ackseq + Acknowledgement number + + + doff + Data offset + + + reserved + Reserved area + + + flags + TCP flags + + + window + Window + + + checksum + Checksum + + + urgptr + Urgent pointer + + + +
+ + + UDP header expression + + + + + + Keyword + Description + + + + + sport + Source port + + + dport + Destination port + + + length + Total packet length + + + checksum + Checksum + + + +
+ + + UDP-Lite header expression + + + + + + Keyword + Description + + + + + sport + Source port + + + dport + Destination port + + + cscov + Checksum coverage + + + checksum + Checksum + + + +
+ + + + AH header expression + + + + + + Keyword + Description + + + + + nexthdr + Next header protocol + + + hdrlength + AH Header length + + + reserved + Reserved area + + + spi + Security Parameter Index + + + sequence + Sequence number + + + +
+ + + ESP header expression + + + + + + Keyword + Description + + + + + spi + Security Parameter Index + + + sequence + Sequence number + + + +
+ + + IPComp header expression + + + + + + Keyword + Description + + + + + nexthdr + Next header protocol + + + flags + Flags + + + cfi + Compression Parameter Index + + + +
+
+
+ + + Exit status + + 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. + + + + + See Also + + + iptables(8) + ip6tables(8) + arptables(8) + ebtables(8) + ip(8) + tc(8) + + + + + + Authors + + nftables was written by Patrick McHardy. + + + + + Copyright + + Copyright © 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. + + +
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 +#include + +/** + * 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 +#include +#include + +#include +#include +#include +#include + +/** + * 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 +#include +#include +#include + +/** + * 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 + +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 + + +/* 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 + +/* 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 +#include +#include +#include + +/* 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 + +/* 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 + +/* 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 + +/* 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 + +#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 +#include +#include +#include +#include +#include +#include + +#include + +/** + * 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 +#include +#include + +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 + +#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 + +/** + * 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 + * + * 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 + +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 +#include +#include + +/** + * 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 +#include + +/** + * 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 +#include + +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 +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 +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 + * + * 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 +#include +#include + +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 Read input from \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 Add to the paths searched for include files.\n" +"\n", + name); +} + +static const struct input_descriptor indesc_cmdline = { + .type = INDESC_BUFFER, + .name = "", +}; + +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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", ðertype_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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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, ¶ms); +} + +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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +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 + * + * 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 + +#include +#include +#include +#include +#include +#include + +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 $$. +m4_define([b4_lhs_value], +[(yyval[]m4_ifval([$1], [.$1]))]) + + +# b4_rhs_value(RULE-LENGTH, NUM, [TYPE]) +# -------------------------------------- +# Expansion of $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 /* 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 /* 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 /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && ]b4_c_modern[ +# include /* 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 /* 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 /* 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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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 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 NUM "number" +%token STRING "string" +%token 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 identifier string +%destructor { xfree($$); } identifier string + +%type line +%destructor { cmd_free($$); } line + +%type base_cmd add_cmd delete_cmd list_cmd flush_cmd +%destructor { cmd_free($$); } base_cmd add_cmd delete_cmd list_cmd flush_cmd + +%type table_spec chain_spec chain_identifier ruleid_spec +%destructor { handle_free(&$$); } table_spec chain_spec chain_identifier ruleid_spec +%type handle_spec family_spec + +%type table_block_alloc table_block +%destructor { table_free($$); } table_block_alloc +%type table_line chain_block_alloc chain_block +%destructor { chain_free($$); } table_line chain_block_alloc +%type rule +%destructor { rule_free($$); } rule + +%type stmt_list +%destructor { stmt_list_free($$); xfree($$); } stmt_list +%type stmt match_stmt verdict_stmt +%destructor { stmt_free($$); } stmt match_stmt verdict_stmt +%type counter_stmt +%destructor { stmt_free($$); } counter_stmt +%type meta_stmt +%destructor { stmt_free($$); } meta_stmt +%type log_stmt log_stmt_alloc +%destructor { stmt_free($$); } log_stmt log_stmt_alloc +%type limit_stmt +%destructor { stmt_free($$); } limit_stmt +%type time_unit +%type reject_stmt +%destructor { stmt_free($$); } reject_stmt +%type nat_stmt nat_stmt_alloc +%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc + +%type symbol_expr verdict_expr integer_expr +%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr +%type primary_expr shift_expr and_expr +%destructor { expr_free($$); } primary_expr shift_expr and_expr +%type exclusive_or_expr inclusive_or_expr +%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr +%type basic_expr +%destructor { expr_free($$); } basic_expr + +%type multiton_expr +%destructor { expr_free($$); } multiton_expr +%type prefix_expr range_expr wildcard_expr +%destructor { expr_free($$); } prefix_expr range_expr wildcard_expr +%type list_expr +%destructor { expr_free($$); } list_expr +%type concat_expr map_lhs_expr +%destructor { expr_free($$); } concat_expr map_lhs_expr + +%type map_expr map_list map_list_expr +%destructor { expr_free($$); } map_expr map_list map_list_expr + +%type verdict_map_expr verdict_map_list verdict_map_list_expr +%destructor { expr_free($$); } verdict_map_expr verdict_map_list verdict_map_list_expr + +%type set_expr +%destructor { expr_free($$); } set_expr + +%type expr +%destructor { expr_free($$); } expr + +%type match_expr +%destructor { expr_free($$); } match_expr +%type relational_expr membership_expr +%destructor { expr_free($$); } relational_expr membership_expr +%type relational_op + +%type payload_expr payload_raw_expr +%destructor { expr_free($$); } payload_expr payload_raw_expr +%type payload_base_spec +%type eth_hdr_expr vlan_hdr_expr +%destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr +%type eth_hdr_field vlan_hdr_field +%type arp_hdr_expr +%destructor { expr_free($$); } arp_hdr_expr +%type arp_hdr_field +%type ip_hdr_expr icmp_hdr_expr +%destructor { expr_free($$); } ip_hdr_expr icmp_hdr_expr +%type ip_hdr_field icmp_hdr_field +%type ip6_hdr_expr +%destructor { expr_free($$); } ip6_hdr_expr +%type ip6_hdr_field +%type auth_hdr_expr esp_hdr_expr comp_hdr_expr +%destructor { expr_free($$); } auth_hdr_expr esp_hdr_expr comp_hdr_expr +%type auth_hdr_field esp_hdr_field comp_hdr_field +%type udp_hdr_expr udplite_hdr_expr tcp_hdr_expr +%destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr tcp_hdr_expr +%type udp_hdr_field udplite_hdr_field tcp_hdr_field +%type dccp_hdr_expr sctp_hdr_expr +%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr +%type dccp_hdr_field sctp_hdr_field + +%type exthdr_expr +%destructor { expr_free($$); } exthdr_expr +%type hbh_hdr_expr frag_hdr_expr dst_hdr_expr +%destructor { expr_free($$); } hbh_hdr_expr frag_hdr_expr dst_hdr_expr +%type hbh_hdr_field frag_hdr_field dst_hdr_field +%type rt_hdr_expr rt0_hdr_expr rt2_hdr_expr +%destructor { expr_free($$); } rt_hdr_expr rt0_hdr_expr rt2_hdr_expr +%type rt_hdr_field rt0_hdr_field rt2_hdr_field +%type mh_hdr_expr +%destructor { expr_free($$); } mh_hdr_expr +%type mh_hdr_field + +%type meta_expr +%destructor { expr_free($$); } meta_expr +%type meta_key + +%type ct_expr +%destructor { expr_free($$); } ct_expr +%type 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 */ { $$ = $
-1; } + | common_block { $$ = $
-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 */ { $$ = $-1; } + | common_block { $$ = $-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 + { + $0->hooknum = $2; + $0->priority = $3; + } + | HOOK HOOKNUM DASH NUM + { + $0->hooknum = $2; + $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 + { + $$ = $0; + } + | log_args log_arg + ; + +log_arg : PREFIX string + { + $0->log.prefix = $2; + } + | GROUP NUM + { + $0->log.group = $2; + } + | SNAPLEN NUM + { + $0->log.snaplen = $2; + } + | QUEUE_THRESHOLD NUM + { + $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 + { + $0->nat.addr = $1; + } + | expr COLON expr + { + $0->nat.addr = $1; + $0->nat.proto = $3; + } + | COLON expr + { + $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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +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 + +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 +#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 + +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 +#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 + +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", ðertype_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 + +#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", ðertype_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 = ðertype_tbl, +}; + +#define ETHHDR_TEMPLATE(__name, __dtype, __member) \ + HDR_TEMPLATE(__name, __dtype, struct ether_header, __member) +#define ETHHDR_TYPE(__name, __member) \ + ETHHDR_TEMPLATE(__name, ðertype_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 + * (C) 2002 David Woodhouse + * + * 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 + +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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + + +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 + +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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#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} + +<> { + 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 + * + * 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 +#include +#include + +#include +#include +#include +#include + +/** + * 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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +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 + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +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 -- cgit v1.2.3