diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 123 | ||||
-rw-r--r-- | src/Makefile.in | 1097 | ||||
-rw-r--r-- | src/cache.c | 1295 | ||||
-rw-r--r-- | src/cli.c | 275 | ||||
-rw-r--r-- | src/cmd.c | 495 | ||||
-rw-r--r-- | src/ct.c | 593 | ||||
-rw-r--r-- | src/datatype.c | 1553 | ||||
-rw-r--r-- | src/dccpopt.c | 277 | ||||
-rw-r--r-- | src/erec.c | 238 | ||||
-rw-r--r-- | src/evaluate.c | 5857 | ||||
-rw-r--r-- | src/expression.c | 1563 | ||||
-rw-r--r-- | src/exthdr.c | 610 | ||||
-rw-r--r-- | src/fib.c | 202 | ||||
-rw-r--r-- | src/gmputil.c | 198 | ||||
-rw-r--r-- | src/hash.c | 165 | ||||
-rw-r--r-- | src/iface.c | 173 | ||||
-rw-r--r-- | src/intervals.c | 750 | ||||
-rw-r--r-- | src/ipopt.c | 145 | ||||
-rw-r--r-- | src/json.c | 2090 | ||||
-rw-r--r-- | src/libnftables.c | 810 | ||||
-rw-r--r-- | src/libnftables.map | 40 | ||||
-rw-r--r-- | src/main.c | 554 | ||||
-rw-r--r-- | src/mergesort.c | 122 | ||||
-rw-r--r-- | src/meta.c | 1043 | ||||
-rw-r--r-- | src/mini-gmp.c | 4412 | ||||
-rw-r--r-- | src/misspell.c | 114 | ||||
-rw-r--r-- | src/mnl.c | 2669 | ||||
-rw-r--r-- | src/monitor.c | 1014 | ||||
-rw-r--r-- | src/netlink.c | 2240 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 3512 | ||||
-rw-r--r-- | src/netlink_linearize.c | 1789 | ||||
-rw-r--r-- | src/nfnl_osf.c | 396 | ||||
-rw-r--r-- | src/nftutils.c | 100 | ||||
-rw-r--r-- | src/nftutils.h | 20 | ||||
-rw-r--r-- | src/numgen.c | 140 | ||||
-rw-r--r-- | src/optimize.c | 1406 | ||||
-rw-r--r-- | src/osf.c | 84 | ||||
-rw-r--r-- | src/owner.c | 182 | ||||
-rw-r--r-- | src/parser_bison.c | 17491 | ||||
-rw-r--r-- | src/parser_bison.h | 845 | ||||
-rw-r--r-- | src/parser_bison.y | 6290 | ||||
-rw-r--r-- | src/parser_json.c | 4415 | ||||
-rw-r--r-- | src/payload.c | 1494 | ||||
-rw-r--r-- | src/print.c | 39 | ||||
-rw-r--r-- | src/proto.c | 1320 | ||||
-rw-r--r-- | src/rt.c | 211 | ||||
-rw-r--r-- | src/rule.c | 2799 | ||||
-rw-r--r-- | src/scanner.c | 8546 | ||||
-rw-r--r-- | src/scanner.l | 1326 | ||||
-rw-r--r-- | src/sctp_chunk.c | 262 | ||||
-rw-r--r-- | src/segtree.c | 637 | ||||
-rw-r--r-- | src/socket.c | 136 | ||||
-rw-r--r-- | src/statement.c | 1085 | ||||
-rw-r--r-- | src/tcpopt.c | 266 | ||||
-rw-r--r-- | src/utils.c | 107 | ||||
-rw-r--r-- | src/xfrm.c | 184 | ||||
-rw-r--r-- | src/xt.c | 377 |
57 files changed, 86176 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..63a4ef4 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,123 @@ +include $(top_srcdir)/Make_global.am + +sbin_PROGRAMS = nft + +AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CPPFLAGS += -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" \ + ${LIBMNL_CFLAGS} ${LIBNFTNL_CFLAGS} +if BUILD_DEBUG +AM_CPPFLAGS += -g -DDEBUG +endif +if BUILD_XTABLES +AM_CPPFLAGS += ${XTABLES_CFLAGS} +endif +if BUILD_MINIGMP +AM_CPPFLAGS += -DHAVE_MINIGMP +endif +if BUILD_JSON +AM_CPPFLAGS += -DHAVE_JSON +endif +if BUILD_XTABLES +AM_CPPFLAGS += -DHAVE_XTABLES +endif + +AM_CFLAGS = -Wall \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wdeclaration-after-statement -Wsign-compare -Winit-self \ + -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute \ + -Wcast-align -Wundef -Wbad-function-cast \ + -Waggregate-return -Wunused -Wwrite-strings ${GCC_FVISIBILITY_HIDDEN} + + +AM_YFLAGS = -d -Wno-yacc + +BUILT_SOURCES = parser_bison.h + +lib_LTLIBRARIES = libnftables.la + +libnftables_la_SOURCES = \ + rule.c \ + statement.c \ + cache.c \ + cmd.c \ + datatype.c \ + expression.c \ + evaluate.c \ + proto.c \ + payload.c \ + exthdr.c \ + fib.c \ + hash.c \ + intervals.c \ + ipopt.c \ + meta.c \ + rt.c \ + numgen.c \ + ct.c \ + xfrm.c \ + netlink.c \ + netlink_linearize.c \ + netlink_delinearize.c \ + misspell.c \ + monitor.c \ + owner.c \ + segtree.c \ + gmputil.c \ + utils.c \ + nftutils.c \ + nftutils.h \ + erec.c \ + mnl.c \ + iface.c \ + mergesort.c \ + optimize.c \ + osf.c \ + nfnl_osf.c \ + tcpopt.c \ + socket.c \ + print.c \ + sctp_chunk.c \ + dccpopt.c \ + libnftables.c \ + libnftables.map + +# yacc and lex generate dirty code +noinst_LTLIBRARIES = libparser.la +libparser_la_SOURCES = parser_bison.y scanner.l +libparser_la_CFLAGS = ${AM_CFLAGS} \ + -Wno-missing-prototypes \ + -Wno-missing-declarations \ + -Wno-implicit-function-declaration \ + -Wno-unused-but-set-variable \ + -Wno-nested-externs \ + -Wno-undef \ + -Wno-redundant-decls + +libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la +libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \ + -Wl,--version-script=$(srcdir)/libnftables.map + +if BUILD_MINIGMP +noinst_LTLIBRARIES += libminigmp.la +libminigmp_la_SOURCES = mini-gmp.c +libminigmp_la_CFLAGS = ${AM_CFLAGS} -Wno-sign-compare +libnftables_la_LIBADD += libminigmp.la +endif + +libnftables_la_SOURCES += xt.c +if BUILD_XTABLES +libnftables_la_LIBADD += ${XTABLES_LIBS} +endif + +nft_SOURCES = main.c + +if BUILD_CLI +nft_SOURCES += cli.c +endif + +if BUILD_JSON +libnftables_la_SOURCES += json.c parser_json.c +libnftables_la_LIBADD += ${JANSSON_LIBS} +endif + +nft_LDADD = libnftables.la diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..580945e --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1097 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +sbin_PROGRAMS = nft$(EXEEXT) +@BUILD_DEBUG_TRUE@am__append_1 = -g -DDEBUG +@BUILD_XTABLES_TRUE@am__append_2 = ${XTABLES_CFLAGS} +@BUILD_MINIGMP_TRUE@am__append_3 = -DHAVE_MINIGMP +@BUILD_JSON_TRUE@am__append_4 = -DHAVE_JSON +@BUILD_XTABLES_TRUE@am__append_5 = -DHAVE_XTABLES +@BUILD_MINIGMP_TRUE@am__append_6 = libminigmp.la +@BUILD_MINIGMP_TRUE@am__append_7 = libminigmp.la +@BUILD_XTABLES_TRUE@am__append_8 = ${XTABLES_LIBS} +@BUILD_CLI_TRUE@am__append_9 = cli.c +@BUILD_JSON_TRUE@am__append_10 = json.c parser_json.c +@BUILD_JSON_TRUE@am__append_11 = ${JANSSON_LIBS} +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gcc4_visibility.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)" +PROGRAMS = $(sbin_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) +libminigmp_la_LIBADD = +am__libminigmp_la_SOURCES_DIST = mini-gmp.c +@BUILD_MINIGMP_TRUE@am_libminigmp_la_OBJECTS = \ +@BUILD_MINIGMP_TRUE@ libminigmp_la-mini-gmp.lo +libminigmp_la_OBJECTS = $(am_libminigmp_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libminigmp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libminigmp_la_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_MINIGMP_TRUE@am_libminigmp_la_rpath = +am__DEPENDENCIES_1 = +@BUILD_XTABLES_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +libnftables_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) libparser.la $(am__append_7) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) +am__libnftables_la_SOURCES_DIST = rule.c statement.c cache.c cmd.c \ + datatype.c expression.c evaluate.c proto.c payload.c exthdr.c \ + fib.c hash.c intervals.c ipopt.c meta.c rt.c numgen.c ct.c \ + xfrm.c netlink.c netlink_linearize.c netlink_delinearize.c \ + misspell.c monitor.c owner.c segtree.c gmputil.c utils.c \ + nftutils.c nftutils.h erec.c mnl.c iface.c mergesort.c \ + optimize.c osf.c nfnl_osf.c tcpopt.c socket.c print.c \ + sctp_chunk.c dccpopt.c libnftables.c libnftables.map xt.c \ + json.c parser_json.c +@BUILD_JSON_TRUE@am__objects_1 = json.lo parser_json.lo +am_libnftables_la_OBJECTS = rule.lo statement.lo cache.lo cmd.lo \ + datatype.lo expression.lo evaluate.lo proto.lo payload.lo \ + exthdr.lo fib.lo hash.lo intervals.lo ipopt.lo meta.lo rt.lo \ + numgen.lo ct.lo xfrm.lo netlink.lo netlink_linearize.lo \ + netlink_delinearize.lo misspell.lo monitor.lo owner.lo \ + segtree.lo gmputil.lo utils.lo nftutils.lo erec.lo mnl.lo \ + iface.lo mergesort.lo optimize.lo osf.lo nfnl_osf.lo tcpopt.lo \ + socket.lo print.lo sctp_chunk.lo dccpopt.lo libnftables.lo \ + xt.lo $(am__objects_1) +libnftables_la_OBJECTS = $(am_libnftables_la_OBJECTS) +libnftables_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libnftables_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +libparser_la_LIBADD = +am_libparser_la_OBJECTS = libparser_la-parser_bison.lo \ + libparser_la-scanner.lo +libparser_la_OBJECTS = $(am_libparser_la_OBJECTS) +libparser_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libparser_la_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__nft_SOURCES_DIST = main.c cli.c +@BUILD_CLI_TRUE@am__objects_2 = cli.$(OBJEXT) +am_nft_OBJECTS = main.$(OBJEXT) $(am__objects_2) +nft_OBJECTS = $(am_nft_OBJECTS) +nft_DEPENDENCIES = libnftables.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/cache.Plo ./$(DEPDIR)/cli.Po \ + ./$(DEPDIR)/cmd.Plo ./$(DEPDIR)/ct.Plo \ + ./$(DEPDIR)/datatype.Plo ./$(DEPDIR)/dccpopt.Plo \ + ./$(DEPDIR)/erec.Plo ./$(DEPDIR)/evaluate.Plo \ + ./$(DEPDIR)/expression.Plo ./$(DEPDIR)/exthdr.Plo \ + ./$(DEPDIR)/fib.Plo ./$(DEPDIR)/gmputil.Plo \ + ./$(DEPDIR)/hash.Plo ./$(DEPDIR)/iface.Plo \ + ./$(DEPDIR)/intervals.Plo ./$(DEPDIR)/ipopt.Plo \ + ./$(DEPDIR)/json.Plo ./$(DEPDIR)/libminigmp_la-mini-gmp.Plo \ + ./$(DEPDIR)/libnftables.Plo \ + ./$(DEPDIR)/libparser_la-parser_bison.Plo \ + ./$(DEPDIR)/libparser_la-scanner.Plo ./$(DEPDIR)/main.Po \ + ./$(DEPDIR)/mergesort.Plo ./$(DEPDIR)/meta.Plo \ + ./$(DEPDIR)/misspell.Plo ./$(DEPDIR)/mnl.Plo \ + ./$(DEPDIR)/monitor.Plo ./$(DEPDIR)/netlink.Plo \ + ./$(DEPDIR)/netlink_delinearize.Plo \ + ./$(DEPDIR)/netlink_linearize.Plo ./$(DEPDIR)/nfnl_osf.Plo \ + ./$(DEPDIR)/nftutils.Plo ./$(DEPDIR)/numgen.Plo \ + ./$(DEPDIR)/optimize.Plo ./$(DEPDIR)/osf.Plo \ + ./$(DEPDIR)/owner.Plo ./$(DEPDIR)/parser_json.Plo \ + ./$(DEPDIR)/payload.Plo ./$(DEPDIR)/print.Plo \ + ./$(DEPDIR)/proto.Plo ./$(DEPDIR)/rt.Plo ./$(DEPDIR)/rule.Plo \ + ./$(DEPDIR)/sctp_chunk.Plo ./$(DEPDIR)/segtree.Plo \ + ./$(DEPDIR)/socket.Plo ./$(DEPDIR)/statement.Plo \ + ./$(DEPDIR)/tcpopt.Plo ./$(DEPDIR)/utils.Plo \ + ./$(DEPDIR)/xfrm.Plo ./$(DEPDIR)/xt.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/build-aux/ylwrap +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = +SOURCES = $(libminigmp_la_SOURCES) $(libnftables_la_SOURCES) \ + $(libparser_la_SOURCES) $(nft_SOURCES) +DIST_SOURCES = $(am__libminigmp_la_SOURCES_DIST) \ + $(am__libnftables_la_SOURCES_DIST) $(libparser_la_SOURCES) \ + $(am__nft_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/Make_global.am \ + $(top_srcdir)/build-aux/depcomp $(top_srcdir)/build-aux/ylwrap \ + parser_bison.c parser_bison.h scanner.c +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A2X = @A2X@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GCC_FVISIBILITY_HIDDEN = @GCC_FVISIBILITY_HIDDEN@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBMNL_CFLAGS = @LIBMNL_CFLAGS@ +LIBMNL_LIBS = @LIBMNL_LIBS@ +LIBNFTNL_CFLAGS = @LIBNFTNL_CFLAGS@ +LIBNFTNL_LIBS = @LIBNFTNL_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +XTABLES_CFLAGS = @XTABLES_CFLAGS@ +XTABLES_LIBS = @XTABLES_LIBS@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# This is _NOT_ the library release version, it's an API version. +# Extracted from Chapter 6 "Library interface versions" of the libtool docs. +# +# <snippet> +# Here are a set of rules to help you update your library version information: +# +# 1. Start with version information of `0:0:0' for each libtool library. +# 2. Update the version information only immediately before a public release +# of your software. More frequent updates are unnecessary, and only guarantee +# that the current interface number gets larger faster. +# 3. If the library source code has changed at all since the last update, +# then increment revision (`c:r:a' becomes `c:r+1:a'). +# 4. If any interfaces have been added, removed, or changed since the last +# update, increment current, and set revision to 0. +# 5. If any interfaces have been added since the last public release, then +# increment age. +# 6. If any interfaces have been removed since the last public release, then +# set age to 0. +# </snippet> +# +libnftables_LIBVERSION = 2:0:1 +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -DDEFAULT_INCLUDE_PATH="\"${sysconfdir}\"" ${LIBMNL_CFLAGS} \ + ${LIBNFTNL_CFLAGS} $(am__append_1) $(am__append_2) \ + $(am__append_3) $(am__append_4) $(am__append_5) +AM_CFLAGS = -Wall \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wdeclaration-after-statement -Wsign-compare -Winit-self \ + -Wformat-nonliteral -Wformat-security -Wmissing-format-attribute \ + -Wcast-align -Wundef -Wbad-function-cast \ + -Waggregate-return -Wunused -Wwrite-strings ${GCC_FVISIBILITY_HIDDEN} + +AM_YFLAGS = -d -Wno-yacc +BUILT_SOURCES = parser_bison.h +lib_LTLIBRARIES = libnftables.la +libnftables_la_SOURCES = rule.c statement.c cache.c cmd.c datatype.c \ + expression.c evaluate.c proto.c payload.c exthdr.c fib.c \ + hash.c intervals.c ipopt.c meta.c rt.c numgen.c ct.c xfrm.c \ + netlink.c netlink_linearize.c netlink_delinearize.c misspell.c \ + monitor.c owner.c segtree.c gmputil.c utils.c nftutils.c \ + nftutils.h erec.c mnl.c iface.c mergesort.c optimize.c osf.c \ + nfnl_osf.c tcpopt.c socket.c print.c sctp_chunk.c dccpopt.c \ + libnftables.c libnftables.map xt.c $(am__append_10) + +# yacc and lex generate dirty code +noinst_LTLIBRARIES = libparser.la $(am__append_6) +libparser_la_SOURCES = parser_bison.y scanner.l +libparser_la_CFLAGS = ${AM_CFLAGS} \ + -Wno-missing-prototypes \ + -Wno-missing-declarations \ + -Wno-implicit-function-declaration \ + -Wno-unused-but-set-variable \ + -Wno-nested-externs \ + -Wno-undef \ + -Wno-redundant-decls + +libnftables_la_LIBADD = ${LIBMNL_LIBS} ${LIBNFTNL_LIBS} libparser.la \ + $(am__append_7) $(am__append_8) $(am__append_11) +libnftables_la_LDFLAGS = -version-info ${libnftables_LIBVERSION} \ + -Wl,--version-script=$(srcdir)/libnftables.map + +@BUILD_MINIGMP_TRUE@libminigmp_la_SOURCES = mini-gmp.c +@BUILD_MINIGMP_TRUE@libminigmp_la_CFLAGS = ${AM_CFLAGS} -Wno-sign-compare +nft_SOURCES = main.c $(am__append_9) +nft_LDADD = libnftables.la +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .c .l .lo .o .obj .y +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(top_srcdir)/Make_global.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; +$(top_srcdir)/Make_global.am $(am__empty): + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libminigmp.la: $(libminigmp_la_OBJECTS) $(libminigmp_la_DEPENDENCIES) $(EXTRA_libminigmp_la_DEPENDENCIES) + $(AM_V_CCLD)$(libminigmp_la_LINK) $(am_libminigmp_la_rpath) $(libminigmp_la_OBJECTS) $(libminigmp_la_LIBADD) $(LIBS) + +libnftables.la: $(libnftables_la_OBJECTS) $(libnftables_la_DEPENDENCIES) $(EXTRA_libnftables_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnftables_la_LINK) -rpath $(libdir) $(libnftables_la_OBJECTS) $(libnftables_la_LIBADD) $(LIBS) +parser_bison.h: parser_bison.c + @if test ! -f $@; then rm -f parser_bison.c; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) parser_bison.c; else :; fi + +libparser.la: $(libparser_la_OBJECTS) $(libparser_la_DEPENDENCIES) $(EXTRA_libparser_la_DEPENDENCIES) + $(AM_V_CCLD)$(libparser_la_LINK) $(libparser_la_OBJECTS) $(libparser_la_LIBADD) $(LIBS) + +nft$(EXEEXT): $(nft_OBJECTS) $(nft_DEPENDENCIES) $(EXTRA_nft_DEPENDENCIES) + @rm -f nft$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nft_OBJECTS) $(nft_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cache.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cli.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmd.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ct.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/datatype.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dccpopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/erec.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/evaluate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/expression.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exthdr.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fib.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmputil.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hash.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iface.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/intervals.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libminigmp_la-mini-gmp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnftables.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libparser_la-parser_bison.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libparser_la-scanner.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mergesort.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/meta.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misspell.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mnl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/monitor.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlink.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlink_delinearize.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlink_linearize.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfnl_osf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nftutils.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/numgen.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optimize.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/osf.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/owner.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser_json.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/payload.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proto.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rule.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sctp_chunk.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/segtree.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socket.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/statement.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpopt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xfrm.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xt.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libminigmp_la-mini-gmp.lo: mini-gmp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libminigmp_la_CFLAGS) $(CFLAGS) -MT libminigmp_la-mini-gmp.lo -MD -MP -MF $(DEPDIR)/libminigmp_la-mini-gmp.Tpo -c -o libminigmp_la-mini-gmp.lo `test -f 'mini-gmp.c' || echo '$(srcdir)/'`mini-gmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libminigmp_la-mini-gmp.Tpo $(DEPDIR)/libminigmp_la-mini-gmp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mini-gmp.c' object='libminigmp_la-mini-gmp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libminigmp_la_CFLAGS) $(CFLAGS) -c -o libminigmp_la-mini-gmp.lo `test -f 'mini-gmp.c' || echo '$(srcdir)/'`mini-gmp.c + +libparser_la-parser_bison.lo: parser_bison.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libparser_la_CFLAGS) $(CFLAGS) -MT libparser_la-parser_bison.lo -MD -MP -MF $(DEPDIR)/libparser_la-parser_bison.Tpo -c -o libparser_la-parser_bison.lo `test -f 'parser_bison.c' || echo '$(srcdir)/'`parser_bison.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libparser_la-parser_bison.Tpo $(DEPDIR)/libparser_la-parser_bison.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parser_bison.c' object='libparser_la-parser_bison.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libparser_la_CFLAGS) $(CFLAGS) -c -o libparser_la-parser_bison.lo `test -f 'parser_bison.c' || echo '$(srcdir)/'`parser_bison.c + +libparser_la-scanner.lo: scanner.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libparser_la_CFLAGS) $(CFLAGS) -MT libparser_la-scanner.lo -MD -MP -MF $(DEPDIR)/libparser_la-scanner.Tpo -c -o libparser_la-scanner.lo `test -f 'scanner.c' || echo '$(srcdir)/'`scanner.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libparser_la-scanner.Tpo $(DEPDIR)/libparser_la-scanner.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scanner.c' object='libparser_la-scanner.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libparser_la_CFLAGS) $(CFLAGS) -c -o libparser_la-scanner.lo `test -f 'scanner.c' || echo '$(srcdir)/'`scanner.c + +.l.c: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) +installdirs: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -rm -f parser_bison.c + -rm -f parser_bison.h + -rm -f scanner.c + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cache.Plo + -rm -f ./$(DEPDIR)/cli.Po + -rm -f ./$(DEPDIR)/cmd.Plo + -rm -f ./$(DEPDIR)/ct.Plo + -rm -f ./$(DEPDIR)/datatype.Plo + -rm -f ./$(DEPDIR)/dccpopt.Plo + -rm -f ./$(DEPDIR)/erec.Plo + -rm -f ./$(DEPDIR)/evaluate.Plo + -rm -f ./$(DEPDIR)/expression.Plo + -rm -f ./$(DEPDIR)/exthdr.Plo + -rm -f ./$(DEPDIR)/fib.Plo + -rm -f ./$(DEPDIR)/gmputil.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/iface.Plo + -rm -f ./$(DEPDIR)/intervals.Plo + -rm -f ./$(DEPDIR)/ipopt.Plo + -rm -f ./$(DEPDIR)/json.Plo + -rm -f ./$(DEPDIR)/libminigmp_la-mini-gmp.Plo + -rm -f ./$(DEPDIR)/libnftables.Plo + -rm -f ./$(DEPDIR)/libparser_la-parser_bison.Plo + -rm -f ./$(DEPDIR)/libparser_la-scanner.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/mergesort.Plo + -rm -f ./$(DEPDIR)/meta.Plo + -rm -f ./$(DEPDIR)/misspell.Plo + -rm -f ./$(DEPDIR)/mnl.Plo + -rm -f ./$(DEPDIR)/monitor.Plo + -rm -f ./$(DEPDIR)/netlink.Plo + -rm -f ./$(DEPDIR)/netlink_delinearize.Plo + -rm -f ./$(DEPDIR)/netlink_linearize.Plo + -rm -f ./$(DEPDIR)/nfnl_osf.Plo + -rm -f ./$(DEPDIR)/nftutils.Plo + -rm -f ./$(DEPDIR)/numgen.Plo + -rm -f ./$(DEPDIR)/optimize.Plo + -rm -f ./$(DEPDIR)/osf.Plo + -rm -f ./$(DEPDIR)/owner.Plo + -rm -f ./$(DEPDIR)/parser_json.Plo + -rm -f ./$(DEPDIR)/payload.Plo + -rm -f ./$(DEPDIR)/print.Plo + -rm -f ./$(DEPDIR)/proto.Plo + -rm -f ./$(DEPDIR)/rt.Plo + -rm -f ./$(DEPDIR)/rule.Plo + -rm -f ./$(DEPDIR)/sctp_chunk.Plo + -rm -f ./$(DEPDIR)/segtree.Plo + -rm -f ./$(DEPDIR)/socket.Plo + -rm -f ./$(DEPDIR)/statement.Plo + -rm -f ./$(DEPDIR)/tcpopt.Plo + -rm -f ./$(DEPDIR)/utils.Plo + -rm -f ./$(DEPDIR)/xfrm.Plo + -rm -f ./$(DEPDIR)/xt.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES install-sbinPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/cache.Plo + -rm -f ./$(DEPDIR)/cli.Po + -rm -f ./$(DEPDIR)/cmd.Plo + -rm -f ./$(DEPDIR)/ct.Plo + -rm -f ./$(DEPDIR)/datatype.Plo + -rm -f ./$(DEPDIR)/dccpopt.Plo + -rm -f ./$(DEPDIR)/erec.Plo + -rm -f ./$(DEPDIR)/evaluate.Plo + -rm -f ./$(DEPDIR)/expression.Plo + -rm -f ./$(DEPDIR)/exthdr.Plo + -rm -f ./$(DEPDIR)/fib.Plo + -rm -f ./$(DEPDIR)/gmputil.Plo + -rm -f ./$(DEPDIR)/hash.Plo + -rm -f ./$(DEPDIR)/iface.Plo + -rm -f ./$(DEPDIR)/intervals.Plo + -rm -f ./$(DEPDIR)/ipopt.Plo + -rm -f ./$(DEPDIR)/json.Plo + -rm -f ./$(DEPDIR)/libminigmp_la-mini-gmp.Plo + -rm -f ./$(DEPDIR)/libnftables.Plo + -rm -f ./$(DEPDIR)/libparser_la-parser_bison.Plo + -rm -f ./$(DEPDIR)/libparser_la-scanner.Plo + -rm -f ./$(DEPDIR)/main.Po + -rm -f ./$(DEPDIR)/mergesort.Plo + -rm -f ./$(DEPDIR)/meta.Plo + -rm -f ./$(DEPDIR)/misspell.Plo + -rm -f ./$(DEPDIR)/mnl.Plo + -rm -f ./$(DEPDIR)/monitor.Plo + -rm -f ./$(DEPDIR)/netlink.Plo + -rm -f ./$(DEPDIR)/netlink_delinearize.Plo + -rm -f ./$(DEPDIR)/netlink_linearize.Plo + -rm -f ./$(DEPDIR)/nfnl_osf.Plo + -rm -f ./$(DEPDIR)/nftutils.Plo + -rm -f ./$(DEPDIR)/numgen.Plo + -rm -f ./$(DEPDIR)/optimize.Plo + -rm -f ./$(DEPDIR)/osf.Plo + -rm -f ./$(DEPDIR)/owner.Plo + -rm -f ./$(DEPDIR)/parser_json.Plo + -rm -f ./$(DEPDIR)/payload.Plo + -rm -f ./$(DEPDIR)/print.Plo + -rm -f ./$(DEPDIR)/proto.Plo + -rm -f ./$(DEPDIR)/rt.Plo + -rm -f ./$(DEPDIR)/rule.Plo + -rm -f ./$(DEPDIR)/sctp_chunk.Plo + -rm -f ./$(DEPDIR)/segtree.Plo + -rm -f ./$(DEPDIR)/socket.Plo + -rm -f ./$(DEPDIR)/statement.Plo + -rm -f ./$(DEPDIR)/tcpopt.Plo + -rm -f ./$(DEPDIR)/utils.Plo + -rm -f ./$(DEPDIR)/xfrm.Plo + -rm -f ./$(DEPDIR)/xt.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS + +.MAKE: all check install install-am install-exec install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libLTLIBRARIES clean-libtool \ + clean-noinstLTLIBRARIES clean-sbinPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-libLTLIBRARIES uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..4e89fe1 --- /dev/null +++ b/src/cache.c @@ -0,0 +1,1295 @@ +/* + * Copyright (c) 2019 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <expression.h> +#include <statement.h> +#include <rule.h> +#include <erec.h> +#include <utils.h> +#include <cache.h> +#include <netlink.h> +#include <mnl.h> +#include <libnftnl/chain.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> + +static unsigned int evaluate_cache_add(struct cmd *cmd, unsigned int flags) +{ + struct set *set; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (!cmd->table) + break; + + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_OBJECT | + NFT_CACHE_FLOWTABLE; + list_for_each_entry(set, &cmd->table->sets, list) { + if (set->automerge) + flags |= NFT_CACHE_SETELEM_MAYBE; + } + break; + case CMD_OBJ_CHAIN: + case CMD_OBJ_SET: + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_SYNPROXY: + case CMD_OBJ_FLOWTABLE: + flags |= NFT_CACHE_TABLE; + break; + case CMD_OBJ_ELEMENTS: + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_OBJECT | + NFT_CACHE_SETELEM_MAYBE; + break; + case CMD_OBJ_RULE: + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_OBJECT | + NFT_CACHE_FLOWTABLE; + + if (cmd->handle.index.id || + cmd->handle.position.id) + flags |= NFT_CACHE_RULE | NFT_CACHE_UPDATE; + break; + default: + break; + } + + return flags; +} + +static unsigned int evaluate_cache_del(struct cmd *cmd, unsigned int flags) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + flags |= NFT_CACHE_SETELEM_MAYBE; + break; + default: + break; + } + + return flags; +} + +static unsigned int evaluate_cache_get(struct cmd *cmd, unsigned int flags) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + flags |= NFT_CACHE_TABLE | + NFT_CACHE_SET | + NFT_CACHE_SETELEM; + break; + default: + break; + } + + return flags; +} + +struct nft_cache_filter *nft_cache_filter_init(void) +{ + struct nft_cache_filter *filter; + int i; + + filter = xzalloc(sizeof(struct nft_cache_filter)); + memset(&filter->list, 0, sizeof(filter->list)); + for (i = 0; i < NFT_CACHE_HSIZE; i++) + init_list_head(&filter->obj[i].head); + + return filter; +} + +void nft_cache_filter_fini(struct nft_cache_filter *filter) +{ + int i; + + for (i = 0; i < NFT_CACHE_HSIZE; i++) { + struct nft_filter_obj *obj, *next; + + list_for_each_entry_safe(obj, next, &filter->obj[i].head, list) + xfree(obj); + } + xfree(filter); +} + +static void cache_filter_add(struct nft_cache_filter *filter, + const struct cmd *cmd) +{ + struct nft_filter_obj *obj; + uint32_t hash; + + obj = xmalloc(sizeof(struct nft_filter_obj)); + obj->family = cmd->handle.family; + obj->table = cmd->handle.table.name; + obj->set = cmd->handle.set.name; + + hash = djb_hash(cmd->handle.set.name) % NFT_CACHE_HSIZE; + list_add_tail(&obj->list, &filter->obj[hash].head); +} + +static bool cache_filter_find(const struct nft_cache_filter *filter, + const struct handle *handle) +{ + struct nft_filter_obj *obj; + uint32_t hash; + + hash = djb_hash(handle->set.name) % NFT_CACHE_HSIZE; + + list_for_each_entry(obj, &filter->obj[hash].head, list) { + if (obj->family == handle->family && + !strcmp(obj->table, handle->table.name) && + !strcmp(obj->set, handle->set.name)) + return true; + } + + return false; +} + +static unsigned int evaluate_cache_flush(struct cmd *cmd, unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: + flags |= NFT_CACHE_SET; + cache_filter_add(filter, cmd); + break; + case CMD_OBJ_RULESET: + flags |= NFT_CACHE_FLUSHED; + break; + default: + break; + } + + return flags; +} + +static unsigned int evaluate_cache_rename(struct cmd *cmd, unsigned int flags) +{ + switch (cmd->obj) { + case CMD_OBJ_CHAIN: + flags |= NFT_CACHE_CHAIN; + break; + default: + break; + } + + return flags; +} + +static unsigned int evaluate_cache_list(struct nft_ctx *nft, struct cmd *cmd, + unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (filter && cmd->handle.table.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + } + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_CHAIN: + if (filter && cmd->handle.chain.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.chain = cmd->handle.chain.name; + /* implicit terse listing to fetch content of anonymous + * sets only when chain name is specified. + */ + flags |= NFT_CACHE_TERSE; + } + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + if (filter && cmd->handle.table.name && cmd->handle.set.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.set = cmd->handle.set.name; + } + if (filter->list.table && filter->list.set) + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET | NFT_CACHE_SETELEM; + else if (nft_output_terse(&nft->output)) + flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE; + else + flags |= NFT_CACHE_FULL; + break; + case CMD_OBJ_CHAINS: + flags |= NFT_CACHE_TABLE | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_SETS: + case CMD_OBJ_MAPS: + flags |= NFT_CACHE_TABLE | NFT_CACHE_SET; + if (!nft_output_terse(&nft->output)) + flags |= NFT_CACHE_SETELEM; + break; + case CMD_OBJ_FLOWTABLE: + if (filter && + cmd->handle.table.name && + cmd->handle.flowtable.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + filter->list.ft = cmd->handle.flowtable.name; + } + /* fall through */ + case CMD_OBJ_FLOWTABLES: + flags |= NFT_CACHE_TABLE | NFT_CACHE_FLOWTABLE; + break; + case CMD_OBJ_RULESET: + if (nft_output_terse(&nft->output)) + flags |= NFT_CACHE_FULL | NFT_CACHE_TERSE; + else + flags |= NFT_CACHE_FULL; + break; + default: + flags |= NFT_CACHE_FULL; + break; + } + flags |= NFT_CACHE_REFRESH; + + return flags; +} + +static unsigned int evaluate_cache_reset(struct cmd *cmd, unsigned int flags, + struct nft_cache_filter *filter) +{ + switch (cmd->obj) { + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: + if (filter) { + if (cmd->handle.table.name) { + filter->list.family = cmd->handle.family; + filter->list.table = cmd->handle.table.name; + } + if (cmd->handle.chain.name) + filter->list.chain = cmd->handle.chain.name; + } + flags |= NFT_CACHE_SET | NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT | NFT_CACHE_CHAIN; + break; + case CMD_OBJ_ELEMENTS: + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + flags |= NFT_CACHE_SET; + break; + default: + flags |= NFT_CACHE_TABLE; + break; + } + + return flags; +} + +static int nft_handle_validate(const struct cmd *cmd, struct list_head *msgs) +{ + const struct handle *h = &cmd->handle; + const struct location *loc; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_RULE: + case CMD_OBJ_RULES: + case CMD_OBJ_CHAIN: + case CMD_OBJ_CHAINS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->chain.name && + strlen(h->chain.name) > NFT_NAME_MAXLEN) { + loc = &h->chain.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_ELEMENTS: + case CMD_OBJ_SET: + case CMD_OBJ_SETS: + case CMD_OBJ_MAP: + case CMD_OBJ_MAPS: + case CMD_OBJ_METER: + case CMD_OBJ_METERS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->set.name && + strlen(h->set.name) > NFT_NAME_MAXLEN) { + loc = &h->set.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_FLOWTABLE: + case CMD_OBJ_FLOWTABLES: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->flowtable.name && + strlen(h->flowtable.name) > NFT_NAME_MAXLEN) { + loc = &h->flowtable.location; + goto err_name_too_long; + } + break; + case CMD_OBJ_INVALID: + case CMD_OBJ_EXPR: + case CMD_OBJ_RULESET: + case CMD_OBJ_MARKUP: + case CMD_OBJ_MONITOR: + case CMD_OBJ_SETELEMS: + case CMD_OBJ_HOOKS: + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_COUNTERS: + case CMD_OBJ_QUOTA: + case CMD_OBJ_QUOTAS: + case CMD_OBJ_LIMIT: + case CMD_OBJ_LIMITS: + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + case CMD_OBJ_SYNPROXY: + case CMD_OBJ_SYNPROXYS: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_HELPERS: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_TIMEOUTS: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_CT_EXPECTATIONS: + if (h->table.name && + strlen(h->table.name) > NFT_NAME_MAXLEN) { + loc = &h->table.location; + goto err_name_too_long; + } + if (h->obj.name && + strlen(h->obj.name) > NFT_NAME_MAXLEN) { + loc = &h->obj.location; + goto err_name_too_long; + } + break; + } + + return 0; + +err_name_too_long: + erec_queue(error(loc, "name too long, %d characters maximum allowed", + NFT_NAME_MAXLEN), + msgs); + return -1; +} + +int nft_cache_evaluate(struct nft_ctx *nft, struct list_head *cmds, + struct list_head *msgs, struct nft_cache_filter *filter, + unsigned int *pflags) +{ + unsigned int flags = NFT_CACHE_EMPTY; + struct cmd *cmd; + + list_for_each_entry(cmd, cmds, list) { + if (nft_handle_validate(cmd, msgs) < 0) + return -1; + + if (filter->list.table && cmd->op != CMD_LIST) + memset(&filter->list, 0, sizeof(filter->list)); + + switch (cmd->op) { + case CMD_ADD: + case CMD_INSERT: + case CMD_CREATE: + flags = evaluate_cache_add(cmd, flags); + if (nft_output_echo(&nft->output)) + flags |= NFT_CACHE_FULL; + break; + case CMD_REPLACE: + flags = NFT_CACHE_FULL; + break; + case CMD_DELETE: + case CMD_DESTROY: + flags |= NFT_CACHE_TABLE | + NFT_CACHE_CHAIN | + NFT_CACHE_SET | + NFT_CACHE_FLOWTABLE | + NFT_CACHE_OBJECT; + + flags = evaluate_cache_del(cmd, flags); + break; + case CMD_GET: + flags = evaluate_cache_get(cmd, flags); + break; + case CMD_RESET: + flags |= evaluate_cache_reset(cmd, flags, filter); + break; + case CMD_LIST: + flags |= evaluate_cache_list(nft, cmd, flags, filter); + break; + case CMD_MONITOR: + flags |= NFT_CACHE_FULL; + break; + case CMD_FLUSH: + flags = evaluate_cache_flush(cmd, flags, filter); + break; + case CMD_RENAME: + flags = evaluate_cache_rename(cmd, flags); + break; + case CMD_DESCRIBE: + case CMD_IMPORT: + case CMD_EXPORT: + break; + default: + break; + } + } + *pflags = flags; + + return 0; +} + +void table_cache_add(struct table *table, struct nft_cache *cache) +{ + uint32_t hash; + + hash = djb_hash(table->handle.table.name) % NFT_CACHE_HSIZE; + cache_add(&table->cache, &cache->table_cache, hash); +} + +void table_cache_del(struct table *table) +{ + cache_del(&table->cache); +} + +struct table *table_cache_find(const struct cache *cache, + const char *name, uint32_t family) +{ + struct table *table; + uint32_t hash; + + if (!name) + return NULL; + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(table, &cache->ht[hash], cache.hlist) { + if (table->handle.family == family && + !strcmp(table->handle.table.name, name)) + return table; + } + + return NULL; +} + +struct chain_cache_dump_ctx { + struct netlink_ctx *nlctx; + struct table *table; +}; + +static int chain_cache_cb(struct nftnl_chain *nlc, void *arg) +{ + struct chain_cache_dump_ctx *ctx = arg; + const char *chain_name, *table_name; + uint32_t hash, family; + struct chain *chain; + + table_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE); + family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); + + if (family != ctx->table->handle.family || + strcmp(table_name, ctx->table->handle.table.name)) + return 0; + + chain_name = nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME); + hash = djb_hash(chain_name) % NFT_CACHE_HSIZE; + chain = netlink_delinearize_chain(ctx->nlctx, nlc); + + if (chain->flags & CHAIN_F_BINDING) { + list_add_tail(&chain->cache.list, &ctx->table->chain_bindings); + } else { + cache_add(&chain->cache, &ctx->table->chain_cache, hash); + } + + nftnl_chain_list_del(nlc); + nftnl_chain_free(nlc); + + return 0; +} + +static int chain_cache_init(struct netlink_ctx *ctx, struct table *table, + struct nftnl_chain_list *chain_list) +{ + struct chain_cache_dump_ctx dump_ctx = { + .nlctx = ctx, + .table = table, + }; + nftnl_chain_list_foreach(chain_list, chain_cache_cb, &dump_ctx); + + return 0; +} + +static struct nftnl_chain_list * +chain_cache_dump(struct netlink_ctx *ctx, + const struct nft_cache_filter *filter, int *err) +{ + struct nftnl_chain_list *chain_list; + const char *table = NULL; + const char *chain = NULL; + int family = NFPROTO_UNSPEC; + + if (filter && filter->list.table && filter->list.chain) { + family = filter->list.family; + table = filter->list.table; + chain = filter->list.chain; + } + + chain_list = mnl_nft_chain_dump(ctx, family, table, chain); + if (chain_list == NULL) { + if (errno == EINTR) { + *err = -1; + return NULL; + } + *err = 0; + return NULL; + } + + return chain_list; +} + +void nft_chain_cache_update(struct netlink_ctx *ctx, struct table *table, + const char *chain) +{ + struct nftnl_chain_list *chain_list; + + chain_list = mnl_nft_chain_dump(ctx, table->handle.family, + table->handle.table.name, chain); + if (!chain_list) + return; + + chain_cache_init(ctx, table, chain_list); + + nftnl_chain_list_free(chain_list); +} + +void chain_cache_add(struct chain *chain, struct table *table) +{ + uint32_t hash; + + hash = djb_hash(chain->handle.chain.name) % NFT_CACHE_HSIZE; + cache_add(&chain->cache, &table->chain_cache, hash); +} + +void chain_cache_del(struct chain *chain) +{ + cache_del(&chain->cache); +} + +struct chain *chain_cache_find(const struct table *table, const char *name) +{ + struct chain *chain; + uint32_t hash; + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(chain, &table->chain_cache.ht[hash], cache.hlist) { + if (!strcmp(chain->handle.chain.name, name)) + return chain; + } + + return NULL; +} + +static int list_rule_cb(struct nftnl_rule *nlr, void *data) +{ + struct netlink_ctx *ctx = data; + const struct handle *h = ctx->data; + const char *table, *chain; + struct rule *rule; + uint32_t family; + + family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); + chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); + + if ((h->family != NFPROTO_UNSPEC && h->family != family) || + (h->table.name && strcmp(table, h->table.name) != 0) || + (h->chain.name && strcmp(chain, h->chain.name) != 0)) + return 0; + + netlink_dump_rule(nlr, ctx); + rule = netlink_delinearize_rule(ctx, nlr); + assert(rule); + list_add_tail(&rule->list, &ctx->list); + + return 0; +} + +int rule_cache_dump(struct netlink_ctx *ctx, const struct handle *h, + const struct nft_cache_filter *filter, + bool dump, bool reset) +{ + struct nftnl_rule_list *rule_cache; + const char *table = NULL; + const char *chain = NULL; + uint64_t rule_handle = 0; + + if (filter) { + table = filter->list.table; + chain = filter->list.chain; + rule_handle = filter->list.rule_handle; + } + + rule_cache = mnl_nft_rule_dump(ctx, h->family, + table, chain, rule_handle, dump, reset); + if (rule_cache == NULL) { + if (errno == EINTR) + return -1; + + return 0; + } + + ctx->data = h; + nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx); + nftnl_rule_list_free(rule_cache); + return 0; +} + +struct set_cache_dump_ctx { + struct netlink_ctx *nlctx; + struct table *table; +}; + +static int set_cache_cb(struct nftnl_set *nls, void *arg) +{ + struct set_cache_dump_ctx *ctx = arg; + const char *set_table; + const char *set_name; + uint32_t set_family; + struct set *set; + uint32_t hash; + + set_table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + set_family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + + if (set_family != ctx->table->handle.family || + strcmp(set_table, ctx->table->handle.table.name)) + return 0; + + set = netlink_delinearize_set(ctx->nlctx, nls); + if (!set) + return -1; + + set_name = nftnl_set_get_str(nls, NFTNL_SET_NAME); + hash = djb_hash(set_name) % NFT_CACHE_HSIZE; + cache_add(&set->cache, &ctx->table->set_cache, hash); + + nftnl_set_list_del(nls); + nftnl_set_free(nls); + return 0; +} + +static int set_cache_init(struct netlink_ctx *ctx, struct table *table, + struct nftnl_set_list *set_list) +{ + struct set_cache_dump_ctx dump_ctx = { + .nlctx = ctx, + .table = table, + }; + + nftnl_set_list_foreach(set_list, set_cache_cb, &dump_ctx); + + return 0; +} + +static struct nftnl_set_list * +set_cache_dump(struct netlink_ctx *ctx, + const struct nft_cache_filter *filter, int *err) +{ + struct nftnl_set_list *set_list; + int family = NFPROTO_UNSPEC; + const char *table = NULL; + const char *set = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + set = filter->list.set; + } + + set_list = mnl_nft_set_dump(ctx, family, table, set); + if (!set_list) { + if (errno == EINTR) { + *err = -1; + return NULL; + } + *err = 0; + return NULL; + } + + return set_list; +} + +void set_cache_add(struct set *set, struct table *table) +{ + uint32_t hash; + + hash = djb_hash(set->handle.set.name) % NFT_CACHE_HSIZE; + cache_add(&set->cache, &table->set_cache, hash); +} + +void set_cache_del(struct set *set) +{ + cache_del(&set->cache); +} + +struct set *set_cache_find(const struct table *table, const char *name) +{ + struct set *set; + uint32_t hash; + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(set, &table->set_cache.ht[hash], cache.hlist) { + if (!strcmp(set->handle.set.name, name)) + return set; + } + + return NULL; +} + +struct obj_cache_dump_ctx { + struct netlink_ctx *nlctx; + struct table *table; +}; + +static int obj_cache_cb(struct nftnl_obj *nlo, void *arg) +{ + struct obj_cache_dump_ctx *ctx = arg; + const char *obj_name; + struct obj *obj; + uint32_t hash; + + obj = netlink_delinearize_obj(ctx->nlctx, nlo); + if (!obj) + return -1; + + obj_name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME); + hash = djb_hash(obj_name) % NFT_CACHE_HSIZE; + cache_add(&obj->cache, &ctx->table->obj_cache, hash); + + return 0; +} + +static int obj_cache_init(struct netlink_ctx *ctx, struct table *table, + struct nftnl_obj_list *obj_list) +{ + struct obj_cache_dump_ctx dump_ctx = { + .nlctx = ctx, + .table = table, + }; + nftnl_obj_list_foreach(obj_list, obj_cache_cb, &dump_ctx); + + return 0; +} + +static struct nftnl_obj_list *obj_cache_dump(struct netlink_ctx *ctx, + const struct table *table) +{ + struct nftnl_obj_list *obj_list; + + obj_list = mnl_nft_obj_dump(ctx, table->handle.family, + table->handle.table.name, NULL, + 0, true, false); + if (!obj_list) { + if (errno == EINTR) + return NULL; + + /* old kernels do not support this, provide an empty list. */ + obj_list = nftnl_obj_list_alloc(); + if (!obj_list) + memory_allocation_error(); + + return obj_list; + } + + return obj_list; +} + +void obj_cache_add(struct obj *obj, struct table *table) +{ + uint32_t hash; + + hash = djb_hash(obj->handle.obj.name) % NFT_CACHE_HSIZE; + cache_add(&obj->cache, &table->obj_cache, hash); +} + +void obj_cache_del(struct obj *obj) +{ + cache_del(&obj->cache); +} + +struct obj *obj_cache_find(const struct table *table, const char *name, + uint32_t obj_type) +{ + struct obj *obj; + uint32_t hash; + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(obj, &table->obj_cache.ht[hash], cache.hlist) { + if (!strcmp(obj->handle.obj.name, name) && + obj->type == obj_type) + return obj; + } + + return NULL; +} + +struct ft_cache_dump_ctx { + struct netlink_ctx *nlctx; + struct table *table; +}; + +static int ft_cache_cb(struct nftnl_flowtable *nlf, void *arg) +{ + struct ft_cache_dump_ctx *ctx = arg; + struct flowtable *ft; + const char *ft_table; + const char *ft_name; + uint32_t ft_family; + uint32_t hash; + + ft_family = nftnl_flowtable_get_u32(nlf, NFTNL_FLOWTABLE_FAMILY); + ft_table = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_TABLE); + + if (ft_family != ctx->table->handle.family || + strcmp(ft_table, ctx->table->handle.table.name)) + return 0; + + ft = netlink_delinearize_flowtable(ctx->nlctx, nlf); + if (!ft) + return -1; + + ft_name = nftnl_flowtable_get_str(nlf, NFTNL_FLOWTABLE_NAME); + hash = djb_hash(ft_name) % NFT_CACHE_HSIZE; + cache_add(&ft->cache, &ctx->table->ft_cache, hash); + + nftnl_flowtable_list_del(nlf); + nftnl_flowtable_free(nlf); + return 0; +} + +static int ft_cache_init(struct netlink_ctx *ctx, struct table *table, + struct nftnl_flowtable_list *ft_list) +{ + struct ft_cache_dump_ctx dump_ctx = { + .nlctx = ctx, + .table = table, + }; + nftnl_flowtable_list_foreach(ft_list, ft_cache_cb, &dump_ctx); + + return 0; +} + +static struct nftnl_flowtable_list * +ft_cache_dump(struct netlink_ctx *ctx, const struct nft_cache_filter *filter) +{ + struct nftnl_flowtable_list *ft_list; + int family = NFPROTO_UNSPEC; + const char *table = NULL; + const char *ft = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + ft = filter->list.ft; + } + + ft_list = mnl_nft_flowtable_dump(ctx, family, table, ft); + if (!ft_list) { + if (errno == EINTR) + return NULL; + + /* old kernels do not support this, provide an empty list. */ + ft_list = nftnl_flowtable_list_alloc(); + if (!ft_list) + memory_allocation_error(); + + return ft_list; + } + + return ft_list; +} + +void ft_cache_add(struct flowtable *ft, struct table *table) +{ + uint32_t hash; + + hash = djb_hash(ft->handle.flowtable.name) % NFT_CACHE_HSIZE; + cache_add(&ft->cache, &table->ft_cache, hash); +} + +void ft_cache_del(struct flowtable *ft) +{ + cache_del(&ft->cache); +} + +struct flowtable *ft_cache_find(const struct table *table, const char *name) +{ + struct flowtable *ft; + uint32_t hash; + + hash = djb_hash(name) % NFT_CACHE_HSIZE; + list_for_each_entry(ft, &table->ft_cache.ht[hash], cache.hlist) { + if (!strcmp(ft->handle.flowtable.name, name)) + return ft; + } + + return NULL; +} + +static int cache_init_tables(struct netlink_ctx *ctx, struct handle *h, + struct nft_cache *cache, + const struct nft_cache_filter *filter) +{ + struct table *table, *next; + int ret; + + ret = netlink_list_tables(ctx, h, filter); + if (ret < 0) + return -1; + + list_for_each_entry_safe(table, next, &ctx->list, list) { + list_del(&table->list); + table_cache_add(table, cache); + } + + return 0; +} + +static int rule_init_cache(struct netlink_ctx *ctx, struct table *table, + const struct nft_cache_filter *filter) +{ + struct rule *rule, *nrule; + struct chain *chain; + int ret; + + ret = rule_cache_dump(ctx, &table->handle, filter, true, false); + + list_for_each_entry_safe(rule, nrule, &ctx->list, list) { + chain = chain_cache_find(table, rule->handle.chain.name); + if (!chain) + chain = chain_binding_lookup(table, + rule->handle.chain.name); + if (!chain) + goto err_ctx_list; + + list_move_tail(&rule->list, &chain->rules); + } + + return ret; + +err_ctx_list: + list_for_each_entry_safe(rule, nrule, &ctx->list, list) { + list_del(&rule->list); + rule_free(rule); + } + errno = EINTR; + + return -1; +} + +static int implicit_chain_cache(struct netlink_ctx *ctx, struct table *table, + const char *chain_name) +{ + struct nft_cache_filter filter; + struct chain *chain; + int ret = 0; + + list_for_each_entry(chain, &table->chain_bindings, cache.list) { + filter.list = (typeof(filter.list)) { + .table = table->handle.table.name, + .chain = chain->handle.chain.name, + }; + ret = rule_init_cache(ctx, table, &filter); + } + + return ret; +} + +static int cache_init_objects(struct netlink_ctx *ctx, unsigned int flags, + const struct nft_cache_filter *filter) +{ + struct nftnl_flowtable_list *ft_list = NULL; + struct nftnl_chain_list *chain_list = NULL; + struct nftnl_set_list *set_list = NULL; + struct nftnl_obj_list *obj_list; + struct table *table; + struct set *set; + int ret = 0; + + if (flags & NFT_CACHE_CHAIN_BIT) { + chain_list = chain_cache_dump(ctx, filter, &ret); + if (!chain_list) + return -1; + } + if (flags & NFT_CACHE_SET_BIT) { + set_list = set_cache_dump(ctx, filter, &ret); + if (!set_list) { + ret = -1; + goto cache_fails; + } + } + if (flags & NFT_CACHE_FLOWTABLE_BIT) { + ft_list = ft_cache_dump(ctx, filter); + if (!ft_list) { + ret = -1; + goto cache_fails; + } + } + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (flags & NFT_CACHE_SET_BIT) { + ret = set_cache_init(ctx, table, set_list); + if (ret < 0) + goto cache_fails; + } + if (flags & NFT_CACHE_SETELEM_BIT) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cache_filter_find(filter, &set->handle)) + continue; + if (!set_is_anonymous(set->flags) && + flags & NFT_CACHE_TERSE) + continue; + + ret = netlink_list_setelems(ctx, &set->handle, + set, false); + if (ret < 0) + goto cache_fails; + } + } else if (flags & NFT_CACHE_SETELEM_MAYBE) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cache_filter_find(filter, &set->handle)) + continue; + + if (!set_is_non_concat_range(set)) + continue; + + ret = netlink_list_setelems(ctx, &set->handle, + set, false); + if (ret < 0) + goto cache_fails; + } + } + if (flags & NFT_CACHE_CHAIN_BIT) { + ret = chain_cache_init(ctx, table, chain_list); + if (ret < 0) + goto cache_fails; + } + if (flags & NFT_CACHE_FLOWTABLE_BIT) { + ret = ft_cache_init(ctx, table, ft_list); + if (ret < 0) + goto cache_fails; + } + if (flags & NFT_CACHE_OBJECT_BIT) { + obj_list = obj_cache_dump(ctx, table); + if (!obj_list) { + ret = -1; + goto cache_fails; + } + ret = obj_cache_init(ctx, table, obj_list); + + nftnl_obj_list_free(obj_list); + + if (ret < 0) + goto cache_fails; + } + + if (flags & NFT_CACHE_RULE_BIT) { + ret = rule_init_cache(ctx, table, filter); + if (ret < 0) + goto cache_fails; + + if (filter && filter->list.table && filter->list.chain) { + ret = implicit_chain_cache(ctx, table, filter->list.chain); + if (ret < 0) + goto cache_fails; + } + } + } + +cache_fails: + if (set_list) + nftnl_set_list_free(set_list); + if (ft_list) + nftnl_flowtable_list_free(ft_list); + + if (flags & NFT_CACHE_CHAIN_BIT) + nftnl_chain_list_free(chain_list); + + return ret; +} + +static int nft_cache_init(struct netlink_ctx *ctx, unsigned int flags, + const struct nft_cache_filter *filter) +{ + struct handle handle = { + .family = NFPROTO_UNSPEC, + }; + int ret; + + if (flags == NFT_CACHE_EMPTY) + return 0; + + /* assume NFT_CACHE_TABLE is always set. */ + ret = cache_init_tables(ctx, &handle, &ctx->nft->cache, filter); + if (ret < 0) + return ret; + ret = cache_init_objects(ctx, flags, filter); + if (ret < 0) + return ret; + + return 0; +} + +static bool nft_cache_is_complete(struct nft_cache *cache, unsigned int flags) +{ + return (cache->flags & flags) == flags; +} + +static bool nft_cache_needs_refresh(struct nft_cache *cache) +{ + return cache->flags & NFT_CACHE_REFRESH; +} + +static bool nft_cache_is_updated(struct nft_cache *cache, uint16_t genid) +{ + return genid && genid == cache->genid; +} + +bool nft_cache_needs_update(struct nft_cache *cache) +{ + return cache->flags & NFT_CACHE_UPDATE; +} + +int nft_cache_update(struct nft_ctx *nft, unsigned int flags, + struct list_head *msgs, + const struct nft_cache_filter *filter) +{ + struct netlink_ctx ctx = { + .list = LIST_HEAD_INIT(ctx.list), + .nft = nft, + .msgs = msgs, + }; + struct nft_cache *cache = &nft->cache; + uint32_t genid, genid_stop, oldflags; + int ret; +replay: + ctx.seqnum = cache->seqnum++; + genid = mnl_genid_get(&ctx); + if (!nft_cache_needs_refresh(cache) && + nft_cache_is_complete(cache, flags) && + nft_cache_is_updated(cache, genid)) + return 0; + + if (cache->genid) + nft_cache_release(cache); + + if (flags & NFT_CACHE_FLUSHED) { + oldflags = flags; + flags = NFT_CACHE_EMPTY; + if (oldflags & NFT_CACHE_UPDATE) + flags |= NFT_CACHE_UPDATE; + goto skip; + } + + ret = nft_cache_init(&ctx, flags, filter); + if (ret < 0) { + if (errno == EINTR) { + nft_cache_release(cache); + goto replay; + } + + erec_queue(error(&netlink_location, "cache initialization failed: %s", + strerror(errno)), + msgs); + nft_cache_release(cache); + + return -1; + } + + genid_stop = mnl_genid_get(&ctx); + if (genid != genid_stop) { + nft_cache_release(cache); + goto replay; + } +skip: + cache->genid = genid; + cache->flags = flags; + return 0; +} + +static void nft_cache_flush(struct cache *table_cache) +{ + struct table *table, *next; + + list_for_each_entry_safe(table, next, &table_cache->list, cache.list) { + table_cache_del(table); + table_free(table); + } +} + +void nft_cache_release(struct nft_cache *cache) +{ + nft_cache_flush(&cache->table_cache); + cache->genid = 0; + cache->flags = NFT_CACHE_EMPTY; +} + +void cache_init(struct cache *cache) +{ + int i; + + cache->ht = xmalloc(sizeof(struct list_head) * NFT_CACHE_HSIZE); + for (i = 0; i < NFT_CACHE_HSIZE; i++) + init_list_head(&cache->ht[i]); + + init_list_head(&cache->list); +} + +void cache_free(struct cache *cache) +{ + xfree(cache->ht); +} + +void cache_add(struct cache_item *item, struct cache *cache, uint32_t hash) +{ + list_add_tail(&item->hlist, &cache->ht[hash]); + list_add_tail(&item->list, &cache->list); +} + +void cache_del(struct cache_item *item) +{ + list_del(&item->hlist); + list_del(&item->list); +} diff --git a/src/cli.c b/src/cli.c new file mode 100644 index 0000000..448c25c --- /dev/null +++ b/src/cli.c @@ -0,0 +1,275 @@ +/* + * Asynchronous readline-based interactive interface + * + * Actually not asynchronous so far, but intended to be. + * + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stdio.h> +#include <stdarg.h> +#include <unistd.h> +#include <errno.h> +#include <ctype.h> +#include <limits.h> +#ifdef HAVE_LIBREADLINE +#include <readline/readline.h> +#include <readline/history.h> +#elif defined(HAVE_LIBEDIT) +#include <editline/readline.h> +#else +#include <linenoise.h> +#endif + +#include <cli.h> +#include <list.h> + +#define CMDLINE_HISTFILE ".nft.history" +#define CMDLINE_PROMPT "nft> " +#define CMDLINE_QUIT "quit" + +static bool cli_quit; +static int cli_rc; + +static void __cli_exit(int rc) +{ + cli_quit = true; + cli_rc = rc; +} + +static char histfile[PATH_MAX]; + +static void +init_histfile(void) +{ + const char *home; + + home = getenv("HOME"); + if (home == NULL) + home = ""; + snprintf(histfile, sizeof(histfile), "%s/%s", home, CMDLINE_HISTFILE); +} + +#if defined(HAVE_LIBREADLINE) +static void nft_rl_prompt_save(void) +{ + rl_save_prompt(); + rl_clear_message(); + rl_set_prompt(".... "); +} +#define nft_rl_prompt_restore rl_restore_prompt +#elif defined(HAVE_LIBEDIT) +static void nft_rl_prompt_save(void) +{ + rl_set_prompt(".... "); +} +static void nft_rl_prompt_restore(void) +{ + rl_set_prompt("nft> "); +} +#endif + +#if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) + +static struct nft_ctx *cli_nft; +static char *multiline; + +static char *cli_append_multiline(char *line) +{ + size_t len = strlen(line); + bool complete = false; + char *s; + + if (len == 0) + return NULL; + + if (line[len - 1] == '\\') { + line[len - 1] = '\0'; + len--; + } else if (multiline == NULL) + return line; + else + complete = 1; + + if (multiline == NULL) { + multiline = line; + nft_rl_prompt_save(); + } else { + len += strlen(multiline); + s = malloc(len + 1); + if (!s) { + fprintf(stderr, "%s:%u: Memory allocation failure\n", + __FILE__, __LINE__); + cli_exit(EXIT_FAILURE); + return NULL; + } + snprintf(s, len + 1, "%s%s", multiline, line); + free(multiline); + multiline = s; + } + line = NULL; + + if (complete) { + line = multiline; + multiline = NULL; + nft_rl_prompt_restore(); + } + return line; +} + +static void cli_complete(char *line) +{ + const HIST_ENTRY *hist; + const char *c; + LIST_HEAD(msgs); + + if (line == NULL) { + printf("\n"); + return cli_exit(0); + } + + line = cli_append_multiline(line); + if (line == NULL) + return; + + for (c = line; *c != '\0'; c++) + if (!isspace(*c)) + break; + if (*c == '\0') + return; + + if (!strcmp(line, CMDLINE_QUIT)) + return cli_exit(0); + + /* avoid duplicate history entries */ + hist = history_get(history_length); + if (hist == NULL || strcmp(hist->line, line)) + add_history(line); + + nft_run_cmd_from_buffer(cli_nft, line); + free(line); +} +#endif + +#if defined(HAVE_LIBREADLINE) + +static char **cli_completion(const char *text, int start, int end) +{ + return NULL; +} + +int cli_init(struct nft_ctx *nft) +{ + cli_nft = nft; + rl_readline_name = (char *)"nft"; + rl_instream = stdin; + rl_outstream = stdout; + + rl_callback_handler_install(CMDLINE_PROMPT, cli_complete); + rl_attempted_completion_function = cli_completion; + + init_histfile(); + + read_history(histfile); + history_set_pos(history_length); + + while (!cli_quit) + rl_callback_read_char(); + + return cli_rc; +} + +void cli_exit(int rc) +{ + rl_callback_handler_remove(); + rl_deprep_terminal(); + write_history(histfile); + + __cli_exit(rc); +} + +#elif defined(HAVE_LIBEDIT) + +int cli_init(struct nft_ctx *nft) +{ + char *line; + + cli_nft = nft; + rl_readline_name = (char *)"nft"; + rl_instream = stdin; + rl_outstream = stdout; + + init_histfile(); + + read_history(histfile); + history_set_pos(history_length); + + rl_set_prompt(CMDLINE_PROMPT); + while (!cli_quit) { + line = readline(rl_prompt); + if (!line) { + cli_exit(0); + break; + } + line = cli_append_multiline(line); + if (!line) + continue; + + cli_complete(line); + } + + return cli_rc; +} + +void cli_exit(int rc) +{ + rl_deprep_terminal(); + write_history(histfile); + + __cli_exit(rc); +} + +#else /* HAVE_LINENOISE */ + +int cli_init(struct nft_ctx *nft) +{ + char *line; + + init_histfile(); + linenoiseHistoryLoad(histfile); + linenoiseSetMultiLine(1); + + while (!cli_quit) { + line = linenoise(CMDLINE_PROMPT); + if (!line) { + cli_exit(0); + break; + } + if (strcmp(line, CMDLINE_QUIT) == 0) { + cli_exit(0); + } else if (line[0] != '\0') { + linenoiseHistoryAdd(line); + nft_run_cmd_from_buffer(nft, line); + } + linenoiseFree(line); + } + + return cli_rc; +} + +void cli_exit(int rc) +{ + linenoiseHistorySave(histfile); + + __cli_exit(rc); +} + +#endif /* HAVE_LINENOISE */ diff --git a/src/cmd.c b/src/cmd.c new file mode 100644 index 0000000..68c476c --- /dev/null +++ b/src/cmd.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2020 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <erec.h> +#include <mnl.h> +#include <cmd.h> +#include <parser.h> +#include <utils.h> +#include <iface.h> +#include <errno.h> +#include <cache.h> + +void cmd_add_loc(struct cmd *cmd, uint16_t offset, const struct location *loc) +{ + if (cmd->num_attrs >= cmd->attr_array_len) { + cmd->attr_array_len *= 2; + cmd->attr = xrealloc(cmd->attr, sizeof(struct nlerr_loc) * cmd->attr_array_len); + } + + cmd->attr[cmd->num_attrs].offset = offset; + cmd->attr[cmd->num_attrs].location = loc; + cmd->num_attrs++; +} + +static int nft_cmd_enoent_table(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc) +{ + struct table *table; + + if (!cmd->handle.table.name) + return 0; + + table = table_lookup_fuzzy(&cmd->handle, &ctx->nft->cache); + if (!table) + return 0; + + netlink_io_error(ctx, loc, "%s; did you mean table ‘%s’ in family %s?", + strerror(ENOENT), table->handle.table.name, + family2str(table->handle.family)); + return 1; +} + +static int table_fuzzy_check(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct table *table) +{ + if (table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, cmd->handle.family)) + return 0; + + if (strcmp(cmd->handle.table.name, table->handle.table.name) || + cmd->handle.family != table->handle.family) { + netlink_io_error(ctx, &cmd->handle.table.location, + "%s; did you mean table ‘%s’ in family %s?", + strerror(ENOENT), table->handle.table.name, + family2str(table->handle.family)); + return 1; + } + + return 0; +} + +static int nft_cmd_enoent_chain(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc) +{ + const struct table *table = NULL; + struct chain *chain; + + if (!cmd->handle.chain.name) + return 0; + + chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table); + /* check table first. */ + if (!table) + return 0; + + if (table_fuzzy_check(ctx, cmd, table)) + return 1; + + if (!chain) + return 0; + + netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), chain->handle.chain.name, + family2str(table->handle.family), + table->handle.table.name); + return 1; +} + +static int nft_cmd_enoent_rule(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc) +{ + unsigned int flags = NFT_CACHE_TABLE | + NFT_CACHE_CHAIN; + const struct table *table = NULL; + struct chain *chain; + + if (nft_cache_update(ctx->nft, flags, ctx->msgs, NULL) < 0) + return 0; + + chain = chain_lookup_fuzzy(&cmd->handle, &ctx->nft->cache, &table); + /* check table first. */ + if (!table) + return 0; + + if (table_fuzzy_check(ctx, cmd, table)) + return 1; + + if (!chain) + return 0; + + if (strcmp(cmd->handle.chain.name, chain->handle.chain.name)) { + netlink_io_error(ctx, loc, "%s; did you mean chain ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), + chain->handle.chain.name, + family2str(table->handle.family), + table->handle.table.name); + return 1; + } + + return 0; +} + +static int nft_cmd_enoent_set(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc) +{ + const struct table *table = NULL; + struct set *set; + + if (!cmd->handle.set.name) + return 0; + + set = set_lookup_fuzzy(cmd->handle.set.name, &ctx->nft->cache, &table); + /* check table first. */ + if (!table) + return 0; + + if (table_fuzzy_check(ctx, cmd, table)) + return 1; + + if (!set) + return 0; + + netlink_io_error(ctx, loc, "%s; did you mean %s ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), + set_is_map(set->flags) ? "map" : "set", + set->handle.set.name, + family2str(set->handle.family), + table->handle.table.name); + return 1; +} + +static int nft_cmd_enoent_obj(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc) +{ + const struct table *table = NULL; + struct obj *obj; + + if (!cmd->handle.obj.name) + return 0; + + obj = obj_lookup_fuzzy(cmd->handle.obj.name, &ctx->nft->cache, &table); + /* check table first. */ + if (!table) + return 0; + + if (table_fuzzy_check(ctx, cmd, table)) + return 1; + + if (!obj) + return 0; + + netlink_io_error(ctx, loc, "%s; did you mean obj ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), obj->handle.obj.name, + family2str(obj->handle.family), + table->handle.table.name); + return 1; +} + +static int nft_cmd_enoent_flowtable(struct netlink_ctx *ctx, + const struct cmd *cmd, + const struct location *loc) +{ + const struct table *table = NULL; + struct flowtable *ft; + + if (!cmd->handle.flowtable.name) + return 0; + + ft = flowtable_lookup_fuzzy(cmd->handle.flowtable.name, + &ctx->nft->cache, &table); + /* check table first. */ + if (!table) + return 0; + + if (table_fuzzy_check(ctx, cmd, table)) + return 1; + + if (!ft) + return 0; + + netlink_io_error(ctx, loc, "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), ft->handle.flowtable.name, + family2str(ft->handle.family), + table->handle.table.name); + return 1; +} + +static void nft_cmd_enoent(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct location *loc, int err) +{ + int ret = 0; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + ret = nft_cmd_enoent_table(ctx, cmd, loc); + break; + case CMD_OBJ_CHAIN: + ret = nft_cmd_enoent_chain(ctx, cmd, loc); + break; + case CMD_OBJ_SET: + ret = nft_cmd_enoent_set(ctx, cmd, loc); + break; + case CMD_OBJ_RULE: + ret = nft_cmd_enoent_rule(ctx, cmd, loc); + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_SYNPROXY: + ret = nft_cmd_enoent_obj(ctx, cmd, loc); + break; + case CMD_OBJ_FLOWTABLE: + ret = nft_cmd_enoent_flowtable(ctx, cmd, loc); + break; + default: + break; + } + + if (ret) + return; + + netlink_io_error(ctx, loc, "Could not process rule: %s", strerror(err)); +} + +static int nft_cmd_chain_error(struct netlink_ctx *ctx, struct cmd *cmd, + struct mnl_err *err) +{ + struct chain *chain = cmd->chain; + int priority; + + switch (err->err) { + case EOPNOTSUPP: + if (!(chain->flags & CHAIN_F_BASECHAIN)) + break; + + mpz_export_data(&priority, chain->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + if (priority <= -200 && !strcmp(chain->type.str, "nat")) + return netlink_io_error(ctx, &chain->priority.loc, + "Chains of type \"nat\" must have a priority value above -200"); + + return netlink_io_error(ctx, &chain->loc, + "Chain of type \"%s\" is not supported, perhaps kernel support is missing?", + chain->type.str); + default: + break; + } + + return 0; +} + +void nft_cmd_error(struct netlink_ctx *ctx, struct cmd *cmd, + struct mnl_err *err) +{ + const struct location *loc = NULL; + uint32_t i; + + for (i = 0; i < cmd->num_attrs; i++) { + if (!cmd->attr[i].offset) + break; + if (cmd->attr[i].offset == err->offset) + loc = cmd->attr[i].location; + } + + if (loc) { + if (err->err == ENOENT) { + nft_cmd_enoent(ctx, cmd, loc, err->err); + return; + } + } else { + loc = &cmd->location; + } + + switch (cmd->obj) { + case CMD_OBJ_CHAIN: + if (nft_cmd_chain_error(ctx, cmd, err) < 0) + return; + break; + default: + break; + } + + netlink_io_error(ctx, loc, "Could not process rule: %s", + strerror(err->err)); +} + +static void nft_cmd_expand_chain(struct chain *chain, struct list_head *new_cmds) +{ + struct rule *rule, *next; + struct handle h; + struct cmd *new; + + list_for_each_entry_safe(rule, next, &chain->rules, list) { + list_del(&rule->list); + handle_merge(&rule->handle, &chain->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &chain->handle); + if (chain->flags & CHAIN_F_BINDING) { + rule->handle.chain_id = chain->handle.chain_id; + rule->handle.chain.location = chain->location; + } + new = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h, + &rule->location, rule); + list_add_tail(&new->list, new_cmds); + } +} + +void nft_cmd_expand(struct cmd *cmd) +{ + struct list_head new_cmds; + struct flowtable *ft; + struct table *table; + struct chain *chain; + struct set *set; + struct obj *obj; + struct cmd *new; + struct handle h; + + init_list_head(&new_cmds); + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + table = cmd->table; + if (!table) + return; + + list_for_each_entry(chain, &table->chains, list) { + handle_merge(&chain->handle, &table->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &chain->handle); + h.chain_id = chain->handle.chain_id; + new = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, + &chain->location, chain_get(chain)); + list_add_tail(&new->list, &new_cmds); + } + list_for_each_entry(obj, &table->objs, list) { + handle_merge(&obj->handle, &table->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &obj->handle); + new = cmd_alloc(CMD_ADD, obj_type_to_cmd(obj->type), &h, + &obj->location, obj_get(obj)); + list_add_tail(&new->list, &new_cmds); + } + list_for_each_entry(set, &table->sets, list) { + handle_merge(&set->handle, &table->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &set->handle); + new = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, + &set->location, set_get(set)); + list_add_tail(&new->list, &new_cmds); + } + list_for_each_entry(ft, &table->flowtables, list) { + handle_merge(&ft->handle, &table->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &ft->handle); + new = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &h, + &ft->location, flowtable_get(ft)); + list_add_tail(&new->list, &new_cmds); + } + list_for_each_entry(chain, &table->chains, list) + nft_cmd_expand_chain(chain, &new_cmds); + + list_splice(&new_cmds, &cmd->list); + break; + case CMD_OBJ_CHAIN: + chain = cmd->chain; + if (!chain || list_empty(&chain->rules)) + break; + + nft_cmd_expand_chain(chain, &new_cmds); + list_splice(&new_cmds, &cmd->list); + break; + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + set = cmd->set; + if (!set->init) + break; + + memset(&h, 0, sizeof(h)); + handle_merge(&h, &set->handle); + new = cmd_alloc(CMD_ADD, CMD_OBJ_SETELEMS, &h, + &set->location, set_get(set)); + list_add(&new->list, &cmd->list); + break; + default: + break; + } +} + +bool nft_cmd_collapse(struct list_head *cmds) +{ + struct cmd *cmd, *next, *elems = NULL; + struct expr *expr, *enext; + bool collapse = false; + + list_for_each_entry_safe(cmd, next, cmds, list) { + if (cmd->op != CMD_ADD && + cmd->op != CMD_CREATE) { + elems = NULL; + continue; + } + + if (cmd->obj != CMD_OBJ_ELEMENTS) { + elems = NULL; + continue; + } + + if (!elems) { + elems = cmd; + continue; + } + + if (cmd->op != elems->op) { + elems = cmd; + continue; + } + + if (elems->handle.family != cmd->handle.family || + strcmp(elems->handle.table.name, cmd->handle.table.name) || + strcmp(elems->handle.set.name, cmd->handle.set.name)) { + elems = cmd; + continue; + } + + collapse = true; + list_for_each_entry_safe(expr, enext, &cmd->expr->expressions, list) { + expr->cmd = cmd; + list_move_tail(&expr->list, &elems->expr->expressions); + } + elems->expr->size += cmd->expr->size; + list_move_tail(&cmd->list, &elems->collapse_list); + } + + return collapse; +} + +void nft_cmd_uncollapse(struct list_head *cmds) +{ + struct cmd *cmd, *cmd_next, *collapse_cmd, *collapse_cmd_next; + struct expr *expr, *next; + + list_for_each_entry_safe(cmd, cmd_next, cmds, list) { + if (list_empty(&cmd->collapse_list)) + continue; + + assert(cmd->obj == CMD_OBJ_ELEMENTS); + + list_for_each_entry_safe(expr, next, &cmd->expr->expressions, list) { + if (!expr->cmd) + continue; + + list_move_tail(&expr->list, &expr->cmd->expr->expressions); + cmd->expr->size--; + expr->cmd = NULL; + } + + list_for_each_entry_safe(collapse_cmd, collapse_cmd_next, &cmd->collapse_list, list) { + if (cmd->elem.set) + collapse_cmd->elem.set = set_get(cmd->elem.set); + + list_add(&collapse_cmd->list, &cmd->list); + } + } +} diff --git a/src/ct.c b/src/ct.c new file mode 100644 index 0000000..1dda799 --- /dev/null +++ b/src/ct.c @@ -0,0 +1,593 @@ +/* + * Conntrack expression related definitions and types. + * + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> + +#include <netinet/ip.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_conntrack_common.h> +#include <linux/netfilter/nf_conntrack_tuple_common.h> + +#include <errno.h> +#include <erec.h> +#include <expression.h> +#include <datatype.h> +#include <ct.h> +#include <gmputil.h> +#include <utils.h> +#include <statement.h> + +#define CONNLABEL_CONF DEFAULT_INCLUDE_PATH "/connlabel.conf" + +static const struct symbol_table ct_state_tbl = { + .base = BASE_HEXADECIMAL, + .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 + } +}; + +const struct datatype ct_state_type = { + .type = TYPE_CT_STATE, + .name = "ct_state", + .desc = "conntrack state", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &bitmask_type, + .sym_tbl = &ct_state_tbl, +}; + +static const struct symbol_table ct_dir_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("original", IP_CT_DIR_ORIGINAL), + SYMBOL("reply", IP_CT_DIR_REPLY), + SYMBOL_LIST_END + } +}; + +const char *ct_dir2str(int dir) +{ + const struct symbolic_constant *s; + + for (s = ct_dir_tbl.symbols; s->identifier != NULL; s++) { + if (dir == (int)s->value) + return s->identifier; + } + + return NULL; +} + +const struct datatype ct_dir_type = { + .type = TYPE_CT_DIR, + .name = "ct_dir", + .desc = "conntrack direction", + .byteorder = BYTEORDER_INVALID, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &ct_dir_tbl, +}; + +static const struct symbol_table ct_status_tbl = { + /* + * There are more, but most of them don't make sense for filtering. + */ + .base = BASE_HEXADECIMAL, + .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 + }, +}; + +const struct datatype ct_status_type = { + .type = TYPE_CT_STATUS, + .name = "ct_status", + .desc = "conntrack status", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &bitmask_type, + .sym_tbl = &ct_status_tbl, +}; + +static const struct symbol_table ct_events_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("new", 1 << IPCT_NEW), + SYMBOL("related", 1 << IPCT_RELATED), + SYMBOL("destroy", 1 << IPCT_DESTROY), + SYMBOL("reply", 1 << IPCT_REPLY), + SYMBOL("assured", 1 << IPCT_ASSURED), + SYMBOL("protoinfo", 1 << IPCT_PROTOINFO), + SYMBOL("helper", 1 << IPCT_HELPER), + SYMBOL("mark", 1 << IPCT_MARK), + SYMBOL("seqadj", 1 << IPCT_SEQADJ), + SYMBOL("secmark", 1 << IPCT_SECMARK), + SYMBOL("label", 1 << IPCT_LABEL), + SYMBOL_LIST_END + }, +}; + +const struct datatype ct_event_type = { + .type = TYPE_CT_EVENTBIT, + .name = "ct_event", + .desc = "conntrack event bits", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &bitmask_type, + .sym_tbl = &ct_events_tbl, +}; + +#define CT_LABEL_BIT_SIZE 128 + +const char *ct_label2str(const struct symbol_table *ct_label_tbl, + unsigned long value) +{ + const struct symbolic_constant *s; + + for (s = ct_label_tbl->symbols; s->identifier; s++) { + if (value == s->value) + return s->identifier; + } + + return NULL; +} + +static void ct_label_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + unsigned long bit = mpz_scan1(expr->value, 0); + const char *labelstr = ct_label2str(octx->tbl.ct_label, bit); + + if (labelstr) { + nft_print(octx, "\"%s\"", labelstr); + return; + } + /* can happen when connlabel.conf is altered after rules were added */ + nft_print(octx, "%lu", bit); +} + +static struct error_record *ct_label_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + const struct symbolic_constant *s; + const struct datatype *dtype; + uint8_t data[CT_LABEL_BIT_SIZE / BITS_PER_BYTE]; + uint64_t bit; + mpz_t value; + + for (s = ctx->tbl->ct_label->symbols; s->identifier != NULL; s++) { + if (!strcmp(sym->identifier, s->identifier)) + break; + } + + dtype = sym->dtype; + if (s->identifier == NULL) { + char *ptr; + + errno = 0; + bit = strtoull(sym->identifier, &ptr, 0); + if (*ptr) + return error(&sym->location, "%s: could not parse %s \"%s\"", + CONNLABEL_CONF, dtype->desc, sym->identifier); + if (errno) + return error(&sym->location, "%s: could not parse %s \"%s\": %s", + CONNLABEL_CONF, dtype->desc, sym->identifier, strerror(errno)); + + } else { + bit = s->value; + } + + if (bit >= CT_LABEL_BIT_SIZE) + return error(&sym->location, "%s: bit %" PRIu64 " out of range (%u max)", + sym->identifier, bit, CT_LABEL_BIT_SIZE); + + mpz_init2(value, dtype->size); + mpz_setbit(value, bit); + mpz_export_data(data, value, BYTEORDER_HOST_ENDIAN, sizeof(data)); + + *res = constant_expr_alloc(&sym->location, dtype, + dtype->byteorder, CT_LABEL_BIT_SIZE, data); + mpz_clear(value); + return NULL; +} + +const struct datatype ct_label_type = { + .type = TYPE_CT_LABEL, + .name = "ct_label", + .desc = "conntrack label", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = CT_LABEL_BIT_SIZE, + .basetype = &bitmask_type, + .print = ct_label_type_print, + .json = ct_label_type_json, + .parse = ct_label_type_parse, +}; + +void ct_label_table_init(struct nft_ctx *ctx) +{ + ctx->output.tbl.ct_label = rt_symbol_table_init(CONNLABEL_CONF); +} + +void ct_label_table_exit(struct nft_ctx *ctx) +{ + rt_symbol_table_free(ctx->output.tbl.ct_label); +} + +#ifndef NF_CT_HELPER_NAME_LEN +#define NF_CT_HELPER_NAME_LEN 16 +#endif + +const struct ct_template ct_templates[__NFT_CT_MAX] = { + [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_EXPIRATION] = CT_TEMPLATE("expiration", &time_type, + BYTEORDER_HOST_ENDIAN, + 4 * BITS_PER_BYTE), + [NFT_CT_HELPER] = CT_TEMPLATE("helper", &string_type, + BYTEORDER_HOST_ENDIAN, + NF_CT_HELPER_NAME_LEN * BITS_PER_BYTE), + [NFT_CT_L3PROTOCOL] = CT_TEMPLATE("l3proto", &nfproto_type, + BYTEORDER_HOST_ENDIAN, + BITS_PER_BYTE), + [NFT_CT_SRC] = CT_TEMPLATE("saddr", &invalid_type, + BYTEORDER_BIG_ENDIAN, 0), + [NFT_CT_DST] = 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", &inet_service_type, + BYTEORDER_BIG_ENDIAN, + 2 * BITS_PER_BYTE), + [NFT_CT_PROTO_DST] = CT_TEMPLATE("proto-dst", &inet_service_type, + BYTEORDER_BIG_ENDIAN, + 2 * BITS_PER_BYTE), + [NFT_CT_LABELS] = CT_TEMPLATE("label", &ct_label_type, + BYTEORDER_HOST_ENDIAN, + CT_LABEL_BIT_SIZE), + [NFT_CT_BYTES] = CT_TEMPLATE("bytes", &integer_type, + BYTEORDER_HOST_ENDIAN, 64), + [NFT_CT_PKTS] = CT_TEMPLATE("packets", &integer_type, + BYTEORDER_HOST_ENDIAN, 64), + [NFT_CT_AVGPKT] = CT_TEMPLATE("avgpkt", &integer_type, + BYTEORDER_HOST_ENDIAN, 64), + [NFT_CT_ZONE] = CT_TEMPLATE("zone", &integer_type, + BYTEORDER_HOST_ENDIAN, 16), + [NFT_CT_EVENTMASK] = CT_TEMPLATE("event", &ct_event_type, + BYTEORDER_HOST_ENDIAN, 32), + [NFT_CT_SRC_IP] = CT_TEMPLATE("ip saddr", &ipaddr_type, + BYTEORDER_BIG_ENDIAN, 32), + [NFT_CT_DST_IP] = CT_TEMPLATE("ip daddr", &ipaddr_type, + BYTEORDER_BIG_ENDIAN, 32), + [NFT_CT_SRC_IP6] = CT_TEMPLATE("ip6 saddr", &ip6addr_type, + BYTEORDER_BIG_ENDIAN, 128), + [NFT_CT_DST_IP6] = CT_TEMPLATE("ip6 daddr", &ip6addr_type, + BYTEORDER_BIG_ENDIAN, 128), + [NFT_CT_SECMARK] = CT_TEMPLATE("secmark", &integer_type, + BYTEORDER_HOST_ENDIAN, 32), + [NFT_CT_ID] = CT_TEMPLATE("id", &integer_type, + BYTEORDER_BIG_ENDIAN, 32), +}; + +static void ct_print(enum nft_ct_keys key, int8_t dir, uint8_t nfproto, + struct output_ctx *octx) +{ + const char *dirstr = ct_dir2str(dir); + const struct proto_desc *desc; + + nft_print(octx, "ct "); + if (dir < 0) + goto done; + + if (dirstr) + nft_print(octx, "%s ", dirstr); + + switch (key) { + case NFT_CT_SRC: + case NFT_CT_DST: + desc = proto_find_upper(&proto_inet, nfproto); + if (desc) + nft_print(octx, "%s ", desc->name); + break; + default: + break; + } + + done: + nft_print(octx, "%s", ct_templates[key].token); +} + +static void ct_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + ct_print(expr->ct.key, expr->ct.direction, expr->ct.nfproto, octx); +} + +static bool ct_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + if (e1->ct.key != e2->ct.key) + return false; + + return e1->ct.direction == e2->ct.direction; +} + +static void ct_expr_clone(struct expr *new, const struct expr *expr) +{ + new->ct = expr->ct; +} + +static void ct_expr_pctx_update(struct proto_ctx *ctx, + const struct location *loc, + const struct expr *left, + const struct expr *right) +{ + const struct proto_desc *base = NULL, *desc; + uint32_t nhproto; + + nhproto = mpz_get_uint32(right->value); + + base = ctx->protocol[left->ct.base].desc; + if (!base) + return; + desc = proto_find_upper(base, nhproto); + if (!desc) + return; + + proto_ctx_update(ctx, left->ct.base + 1, loc, desc); +} + +#define NFTNL_UDATA_CT_KEY 0 +#define NFTNL_UDATA_CT_DIR 1 +#define NFTNL_UDATA_CT_MAX 2 + +static int ct_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_CT_KEY, expr->ct.key); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_CT_DIR, expr->ct.direction); + + return 0; +} + +static int ct_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_CT_KEY: + case NFTNL_UDATA_CT_DIR: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *ct_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_CT_MAX + 1] = {}; + uint32_t key, dir; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + ct_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_CT_KEY] || + !ud[NFTNL_UDATA_CT_DIR]) + return NULL; + + key = nftnl_udata_get_u32(ud[NFTNL_UDATA_CT_KEY]); + dir = nftnl_udata_get_u32(ud[NFTNL_UDATA_CT_DIR]); + + return ct_expr_alloc(&internal_location, key, dir); +} + +const struct expr_ops ct_expr_ops = { + .type = EXPR_CT, + .name = "ct", + .print = ct_expr_print, + .json = ct_expr_json, + .cmp = ct_expr_cmp, + .clone = ct_expr_clone, + .pctx_update = ct_expr_pctx_update, + .parse_udata = ct_expr_parse_udata, + .build_udata = ct_expr_build_udata, +}; + +struct expr *ct_expr_alloc(const struct location *loc, enum nft_ct_keys key, + int8_t direction) +{ + const struct ct_template *tmpl = &ct_templates[key]; + struct expr *expr; + + expr = expr_alloc(loc, EXPR_CT, tmpl->dtype, + tmpl->byteorder, tmpl->len); + expr->ct.key = key; + expr->ct.direction = direction; + + switch (key) { + case NFT_CT_SRC: + case NFT_CT_DST: + expr->ct.base = PROTO_BASE_NETWORK_HDR; + break; + case NFT_CT_PROTO_SRC: + case NFT_CT_PROTO_DST: + expr->ct.base = PROTO_BASE_TRANSPORT_HDR; + break; + case NFT_CT_PROTOCOL: + expr->flags = EXPR_F_PROTOCOL; + expr->ct.base = PROTO_BASE_NETWORK_HDR; + break; + case NFT_CT_L3PROTOCOL: + expr->flags = EXPR_F_PROTOCOL; + expr->ct.base = PROTO_BASE_LL_HDR; + break; + default: + break; + } + + return expr; +} + +void ct_expr_update_type(struct proto_ctx *ctx, struct expr *expr) +{ + const struct proto_desc *desc; + + desc = ctx->protocol[expr->ct.base].desc; + + switch (expr->ct.key) { + case NFT_CT_SRC: + case NFT_CT_DST: + if (desc == &proto_ip) { + datatype_set(expr, &ipaddr_type); + expr->ct.nfproto = NFPROTO_IPV4; + } else if (desc == &proto_ip6) { + datatype_set(expr, &ip6addr_type); + expr->ct.nfproto = NFPROTO_IPV6; + } + + expr->len = expr->dtype->size; + break; + case NFT_CT_PROTO_SRC: + case NFT_CT_PROTO_DST: + if (desc == NULL) + break; + datatype_set(expr, &inet_service_type); + break; + case NFT_CT_SRC_IP: + case NFT_CT_DST_IP: + expr->dtype = &ipaddr_type; + expr->len = expr->dtype->size; + break; + case NFT_CT_SRC_IP6: + case NFT_CT_DST_IP6: + expr->dtype = &ip6addr_type; + expr->len = expr->dtype->size; + break; + default: + break; + } +} + +static void ct_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + ct_print(stmt->ct.key, stmt->ct.direction, 0, octx); + nft_print(octx, " set "); + expr_print(stmt->ct.expr, octx); +} + +static void ct_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->ct.expr); +} + +static const struct stmt_ops ct_stmt_ops = { + .type = STMT_CT, + .name = "ct", + .print = ct_stmt_print, + .json = ct_stmt_json, + .destroy = ct_stmt_destroy, +}; + +struct stmt *ct_stmt_alloc(const struct location *loc, enum nft_ct_keys key, + int8_t direction, struct expr *expr) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &ct_stmt_ops); + stmt->ct.key = key; + stmt->ct.tmpl = &ct_templates[key]; + stmt->ct.expr = expr; + stmt->ct.direction = direction; + + return stmt; +} + +static void notrack_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "notrack"); +} + +static const struct stmt_ops notrack_stmt_ops = { + .type = STMT_NOTRACK, + .name = "notrack", + .print = notrack_stmt_print, + .json = notrack_stmt_json, +}; + +struct stmt *notrack_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, ¬rack_stmt_ops); +} + +static void flow_offload_stmt_print(const struct stmt *stmt, + struct output_ctx *octx) +{ + nft_print(octx, "flow add @%s", stmt->flow.table_name); +} + +static void flow_offload_stmt_destroy(struct stmt *stmt) +{ + xfree(stmt->flow.table_name); +} + +static const struct stmt_ops flow_offload_stmt_ops = { + .type = STMT_FLOW_OFFLOAD, + .name = "flow_offload", + .print = flow_offload_stmt_print, + .destroy = flow_offload_stmt_destroy, + .json = flow_offload_stmt_json, +}; + +struct stmt *flow_offload_stmt_alloc(const struct location *loc, + const char *table_name) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &flow_offload_stmt_ops); + stmt->flow.table_name = table_name; + + return stmt; +} diff --git a/src/datatype.c b/src/datatype.c new file mode 100644 index 0000000..64e4647 --- /dev/null +++ b/src/datatype.c @@ -0,0 +1,1553 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <inttypes.h> +#include <ctype.h> /* isdigit */ +#include <errno.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/netfilter.h> +#include <linux/icmpv6.h> +#include <dirent.h> +#include <sys/stat.h> + +#include <nftables.h> +#include <datatype.h> +#include <expression.h> +#include <gmputil.h> +#include <erec.h> +#include <netlink.h> +#include <json.h> +#include <misspell.h> +#include "nftutils.h" + +#include <netinet/ip_icmp.h> + +static const struct datatype *datatypes[TYPE_MAX + 1] = { + [TYPE_INVALID] = &invalid_type, + [TYPE_VERDICT] = &verdict_type, + [TYPE_NFPROTO] = &nfproto_type, + [TYPE_BITMASK] = &bitmask_type, + [TYPE_INTEGER] = &integer_type, + [TYPE_STRING] = &string_type, + [TYPE_LLADDR] = &lladdr_type, + [TYPE_IPADDR] = &ipaddr_type, + [TYPE_IP6ADDR] = &ip6addr_type, + [TYPE_ETHERADDR] = ðeraddr_type, + [TYPE_ETHERTYPE] = ðertype_type, + [TYPE_ARPOP] = &arpop_type, + [TYPE_INET_PROTOCOL] = &inet_protocol_type, + [TYPE_INET_SERVICE] = &inet_service_type, + [TYPE_ICMP_TYPE] = &icmp_type_type, + [TYPE_TCP_FLAG] = &tcp_flag_type, + [TYPE_DCCP_PKTTYPE] = &dccp_pkttype_type, + [TYPE_MH_TYPE] = &mh_type_type, + [TYPE_TIME] = &time_type, + [TYPE_MARK] = &mark_type, + [TYPE_IFINDEX] = &ifindex_type, + [TYPE_ARPHRD] = &arphrd_type, + [TYPE_REALM] = &realm_type, + [TYPE_CLASSID] = &tchandle_type, + [TYPE_UID] = &uid_type, + [TYPE_GID] = &gid_type, + [TYPE_CT_STATE] = &ct_state_type, + [TYPE_CT_DIR] = &ct_dir_type, + [TYPE_CT_STATUS] = &ct_status_type, + [TYPE_ICMP6_TYPE] = &icmp6_type_type, + [TYPE_CT_LABEL] = &ct_label_type, + [TYPE_PKTTYPE] = &pkttype_type, + [TYPE_ICMP_CODE] = &icmp_code_type, + [TYPE_ICMPV6_CODE] = &icmpv6_code_type, + [TYPE_ICMPX_CODE] = &icmpx_code_type, + [TYPE_DEVGROUP] = &devgroup_type, + [TYPE_DSCP] = &dscp_type, + [TYPE_ECN] = &ecn_type, + [TYPE_FIB_ADDR] = &fib_addr_type, + [TYPE_BOOLEAN] = &boolean_type, + [TYPE_CT_EVENTBIT] = &ct_event_type, + [TYPE_IFNAME] = &ifname_type, + [TYPE_IGMP_TYPE] = &igmp_type_type, + [TYPE_TIME_DATE] = &date_type, + [TYPE_TIME_HOUR] = &hour_type, + [TYPE_TIME_DAY] = &day_type, + [TYPE_CGROUPV2] = &cgroupv2_type, +}; + +const struct datatype *datatype_lookup(enum datatypes type) +{ + BUILD_BUG_ON(TYPE_MAX & ~TYPE_MASK); + + if (type > TYPE_MAX) + return NULL; + return datatypes[type]; +} + +const struct datatype *datatype_lookup_byname(const char *name) +{ + const struct datatype *dtype; + enum datatypes type; + + for (type = TYPE_INVALID; type <= TYPE_MAX; type++) { + dtype = datatypes[type]; + if (dtype == NULL) + continue; + if (!strcmp(dtype->name, name)) + return dtype; + } + return NULL; +} + +void datatype_print(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype; + + do { + if (dtype->print != NULL) + return dtype->print(expr, octx); + if (dtype->sym_tbl != NULL) + return symbolic_constant_print(dtype->sym_tbl, expr, + false, octx); + } while ((dtype = dtype->basetype)); + + BUG("datatype %s has no print method or symbol table\n", + expr->dtype->name); +} + +struct error_record *symbol_parse(struct parse_ctx *ctx, const struct expr *sym, + struct expr **res) +{ + const struct datatype *dtype = sym->dtype; + struct error_record *erec; + + assert(sym->etype == EXPR_SYMBOL); + + if (dtype == NULL) + return error(&sym->location, "No symbol type information"); + do { + if (dtype->parse != NULL) + return dtype->parse(ctx, sym, res); + if (dtype->sym_tbl != NULL) + return symbolic_constant_parse(ctx, sym, dtype->sym_tbl, + res); + } while ((dtype = dtype->basetype)); + + dtype = sym->dtype; + if (dtype->err) { + erec = dtype->err(sym); + if (erec) + return erec; + } + + return error(&sym->location, "Could not parse symbolic %s expression", + sym->dtype->desc); +} + +static struct error_record *__symbol_parse_fuzzy(const struct expr *sym, + const struct symbol_table *tbl) +{ + const struct symbolic_constant *s; + struct string_misspell_state st; + + string_misspell_init(&st); + + for (s = tbl->symbols; s->identifier != NULL; s++) { + string_misspell_update(sym->identifier, s->identifier, + (void *)s->identifier, &st); + } + + if (st.obj) { + return error(&sym->location, + "Could not parse %s expression; did you you mean `%s`?", + sym->dtype->desc, st.obj); + } + + return NULL; +} + +static struct error_record *symbol_parse_fuzzy(const struct expr *sym, + const struct symbol_table *tbl) +{ + struct error_record *erec; + + if (!tbl) + return NULL; + + erec = __symbol_parse_fuzzy(sym, tbl); + if (erec) + return erec; + + return NULL; +} + +struct error_record *symbolic_constant_parse(struct parse_ctx *ctx, + const struct expr *sym, + const struct symbol_table *tbl, + struct expr **res) +{ + const struct symbolic_constant *s; + const struct datatype *dtype; + struct error_record *erec; + + for (s = tbl->symbols; s->identifier != NULL; s++) { + if (!strcmp(sym->identifier, s->identifier)) + break; + } + + if (s->identifier != NULL) + goto out; + + dtype = sym->dtype; + *res = NULL; + do { + if (dtype->basetype->parse) { + erec = dtype->basetype->parse(ctx, sym, res); + if (erec != NULL) { + struct error_record *fuzzy_erec; + + fuzzy_erec = symbol_parse_fuzzy(sym, tbl); + if (!fuzzy_erec) + return erec; + + erec_destroy(erec); + return fuzzy_erec; + } + if (*res) + return NULL; + goto out; + } + } while ((dtype = dtype->basetype)); + + return error(&sym->location, "Could not parse %s", sym->dtype->desc); +out: + *res = constant_expr_alloc(&sym->location, sym->dtype, + sym->dtype->byteorder, sym->dtype->size, + constant_data_ptr(s->value, + sym->dtype->size)); + return NULL; +} + +void symbolic_constant_print(const struct symbol_table *tbl, + const struct expr *expr, bool quotes, + struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + const struct symbolic_constant *s; + uint64_t val = 0; + + /* Export the data in the correct byteorder for comparison */ + assert(expr->len / BITS_PER_BYTE <= sizeof(val)); + mpz_export_data(constant_data_ptr(val, expr->len), expr->value, + expr->byteorder, len); + + for (s = tbl->symbols; s->identifier != NULL; s++) { + if (val == s->value) + break; + } + + if (s->identifier == NULL || nft_output_numeric_symbol(octx)) + return expr_basetype(expr)->print(expr, octx); + + nft_print(octx, quotes ? "\"%s\"" : "%s", s->identifier); +} + +static void switch_byteorder(void *data, unsigned int len) +{ + mpz_t op; + + mpz_init(op); + mpz_import_data(op, data, BYTEORDER_BIG_ENDIAN, len); + mpz_export_data(data, op, BYTEORDER_HOST_ENDIAN, len); + mpz_clear(op); +} + +void symbol_table_print(const struct symbol_table *tbl, + const struct datatype *dtype, + enum byteorder byteorder, + struct output_ctx *octx) +{ + unsigned int len = div_round_up(dtype->size, BITS_PER_BYTE); + const struct symbolic_constant *s; + uint64_t value; + + for (s = tbl->symbols; s->identifier != NULL; s++) { + value = s->value; + + if (byteorder == BYTEORDER_BIG_ENDIAN) + switch_byteorder(&value, len); + + if (tbl->base == BASE_DECIMAL) + nft_print(octx, "\t%-30s\t%20" PRIu64 "\n", + s->identifier, value); + else + nft_print(octx, "\t%-30s\t0x%.*" PRIx64 "\n", + s->identifier, 2 * len, value); + } +} + +static void invalid_type_print(const struct expr *expr, struct output_ctx *octx) +{ + nft_gmp_print(octx, "0x%Zx [invalid type]", expr->value); +} + +const struct datatype invalid_type = { + .type = TYPE_INVALID, + .name = "invalid", + .desc = "invalid", + .print = invalid_type_print, +}; + +void expr_chain_export(const struct expr *e, char *chain_name) +{ + unsigned int len; + + len = e->len / BITS_PER_BYTE; + if (len >= NFT_CHAIN_MAXNAMELEN) + BUG("verdict expression length %u is too large (%u bits max)", + e->len, NFT_CHAIN_MAXNAMELEN * BITS_PER_BYTE); + + mpz_export_data(chain_name, e->value, BYTEORDER_HOST_ENDIAN, len); +} + +static void verdict_jump_chain_print(const char *what, const struct expr *e, + struct output_ctx *octx) +{ + char chain[NFT_CHAIN_MAXNAMELEN]; + + memset(chain, 0, sizeof(chain)); + expr_chain_export(e, chain); + nft_print(octx, "%s %s", what, chain); +} + +static void verdict_type_print(const struct expr *expr, struct output_ctx *octx) +{ + switch (expr->verdict) { + case NFT_CONTINUE: + nft_print(octx, "continue"); + break; + case NFT_BREAK: + nft_print(octx, "break"); + break; + case NFT_JUMP: + if (expr->chain->etype == EXPR_VALUE) { + verdict_jump_chain_print("jump", expr->chain, octx); + } else { + nft_print(octx, "jump "); + expr_print(expr->chain, octx); + } + break; + case NFT_GOTO: + if (expr->chain->etype == EXPR_VALUE) { + verdict_jump_chain_print("goto", expr->chain, octx); + } else { + nft_print(octx, "goto "); + expr_print(expr->chain, octx); + } + break; + case NFT_RETURN: + nft_print(octx, "return"); + break; + default: + switch (expr->verdict & NF_VERDICT_MASK) { + case NF_ACCEPT: + nft_print(octx, "accept"); + break; + case NF_DROP: + nft_print(octx, "drop"); + break; + case NF_QUEUE: + nft_print(octx, "queue"); + break; + case NF_STOLEN: + nft_print(octx, "stolen"); + break; + default: + nft_print(octx, "unknown verdict value %u", expr->verdict); + break; + } + } +} + +static struct error_record *verdict_type_error(const struct expr *sym) +{ + /* Skip jump and goto from fuzzy match to provide better error + * reporting, fall back to `jump chain' if no clue. + */ + static const char *verdict_array[] = { + "continue", "break", "return", "accept", "drop", "queue", + "stolen", NULL, + }; + struct string_misspell_state st; + int i; + + string_misspell_init(&st); + + for (i = 0; verdict_array[i] != NULL; i++) { + string_misspell_update(sym->identifier, verdict_array[i], + (void *)verdict_array[i], &st); + } + + if (st.obj) { + return error(&sym->location, "Could not parse %s; did you mean `%s'?", + sym->dtype->desc, st.obj); + } + + /* assume user would like to jump to chain as a hint. */ + return error(&sym->location, "Could not parse %s; did you mean `jump %s'?", + sym->dtype->desc, sym->identifier); +} + +const struct datatype verdict_type = { + .type = TYPE_VERDICT, + .name = "verdict", + .desc = "netfilter verdict", + .print = verdict_type_print, + .err = verdict_type_error, +}; + +static const struct symbol_table nfproto_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("ipv4", NFPROTO_IPV4), + SYMBOL("ipv6", NFPROTO_IPV6), + SYMBOL_LIST_END + }, +}; + +const struct datatype nfproto_type = { + .type = TYPE_NFPROTO, + .name = "nf_proto", + .desc = "netfilter protocol", + .size = 1 * BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &nfproto_tbl, +}; + +const struct datatype bitmask_type = { + .type = TYPE_BITMASK, + .name = "bitmask", + .desc = "bitmask", + .basefmt = "0x%Zx", + .basetype = &integer_type, +}; + +static void integer_type_print(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype; + const char *fmt = "%Zu"; + + do { + if (dtype->basefmt != NULL) { + fmt = dtype->basefmt; + break; + } + } while ((dtype = dtype->basetype)); + + nft_gmp_print(octx, fmt, expr->value); +} + +static struct error_record *integer_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + mpz_t v; + + mpz_init(v); + if (mpz_set_str(v, sym->identifier, 0)) { + mpz_clear(v); + return error(&sym->location, "Could not parse %s", + sym->dtype->desc); + } + + *res = constant_expr_alloc(&sym->location, sym->dtype, + 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", + .desc = "integer", + .print = integer_type_print, + .json = integer_type_json, + .parse = integer_type_parse, +}; + +static void xinteger_type_print(const struct expr *expr, struct output_ctx *octx) +{ + nft_gmp_print(octx, "0x%Zx", expr->value); +} + +/* Alias of integer_type to print raw payload expressions in hexadecimal. */ +const struct datatype xinteger_type = { + .type = TYPE_INTEGER, + .name = "integer", + .desc = "integer", + .basetype = &integer_type, + .print = xinteger_type_print, + .json = integer_type_json, + .parse = integer_type_parse, +}; + +static void string_type_print(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + char data[len+1]; + + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + data[len] = '\0'; + nft_print(octx, "\"%s\"", data); +} + +static struct error_record *string_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + *res = constant_expr_alloc(&sym->location, &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen(sym->identifier) + 1) * BITS_PER_BYTE, + sym->identifier); + return NULL; +} + +const struct datatype string_type = { + .type = TYPE_STRING, + .name = "string", + .desc = "string", + .byteorder = BYTEORDER_HOST_ENDIAN, + .print = string_type_print, + .json = string_type_json, + .parse = string_type_parse, +}; + +static void lladdr_type_print(const struct expr *expr, struct output_ctx *octx) +{ + 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_BIG_ENDIAN, len); + + for (i = 0; i < len; i++) { + nft_print(octx, "%s%.2x", delim, data[i]); + delim = ":"; + } +} + +static struct error_record *lladdr_type_parse(struct parse_ctx *ctx, + 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, sym->dtype, + BYTEORDER_BIG_ENDIAN, len * BITS_PER_BYTE, + buf); + return NULL; +} + +const struct datatype lladdr_type = { + .type = TYPE_LLADDR, + .name = "ll_addr", + .desc = "link layer address", + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .print = lladdr_type_print, + .parse = lladdr_type_parse, +}; + +static void ipaddr_type_print(const struct expr *expr, struct output_ctx *octx) +{ + struct sockaddr_in sin = { .sin_family = AF_INET, }; + char buf[NI_MAXHOST]; + int err; + + sin.sin_addr.s_addr = mpz_get_be32(expr->value); + err = getnameinfo((struct sockaddr *)&sin, sizeof(sin), buf, + sizeof(buf), NULL, 0, + nft_output_reversedns(octx) ? 0 : NI_NUMERICHOST); + if (err != 0) { + getnameinfo((struct sockaddr *)&sin, sizeof(sin), buf, + sizeof(buf), NULL, 0, NI_NUMERICHOST); + } + nft_print(octx, "%s", buf); +} + +static struct error_record *ipaddr_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct in_addr addr; + + if (nft_input_no_dns(ctx->input)) { + if (inet_pton(AF_INET, sym->identifier, &addr) != 1) + return error(&sym->location, "Invalid IPv4 address"); + } else { + struct addrinfo *ai, hints = { .ai_family = AF_INET, + .ai_socktype = SOCK_DGRAM}; + 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"); + } + assert(ai->ai_addr->sa_family == AF_INET); + addr = ((struct sockaddr_in *) (void *) ai->ai_addr)->sin_addr; + freeaddrinfo(ai); + } + + *res = constant_expr_alloc(&sym->location, &ipaddr_type, + BYTEORDER_BIG_ENDIAN, + sizeof(addr) * BITS_PER_BYTE, &addr); + return NULL; +} + +const struct datatype ipaddr_type = { + .type = TYPE_IPADDR, + .name = "ipv4_addr", + .desc = "IPv4 address", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = ipaddr_type_print, + .parse = ipaddr_type_parse, + .flags = DTYPE_F_PREFIX, +}; + +static void ip6addr_type_print(const struct expr *expr, struct output_ctx *octx) +{ + struct sockaddr_in6 sin6 = { .sin6_family = AF_INET6 }; + char buf[NI_MAXHOST]; + int err; + + mpz_export_data(&sin6.sin6_addr, expr->value, BYTEORDER_BIG_ENDIAN, + sizeof(sin6.sin6_addr)); + + err = getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), buf, + sizeof(buf), NULL, 0, + nft_output_reversedns(octx) ? 0 : NI_NUMERICHOST); + if (err != 0) { + getnameinfo((struct sockaddr *)&sin6, sizeof(sin6), buf, + sizeof(buf), NULL, 0, NI_NUMERICHOST); + } + nft_print(octx, "%s", buf); +} + +static struct error_record *ip6addr_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct in6_addr addr; + + if (nft_input_no_dns(ctx->input)) { + if (inet_pton(AF_INET6, sym->identifier, &addr) != 1) + return error(&sym->location, "Invalid IPv6 address"); + } else { + struct addrinfo *ai, hints = { .ai_family = AF_INET6, + .ai_socktype = SOCK_DGRAM}; + 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"); + } + + assert(ai->ai_addr->sa_family == AF_INET6); + addr = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_addr; + freeaddrinfo(ai); + } + + *res = constant_expr_alloc(&sym->location, &ip6addr_type, + BYTEORDER_BIG_ENDIAN, + sizeof(addr) * BITS_PER_BYTE, &addr); + return NULL; +} + +const struct datatype ip6addr_type = { + .type = TYPE_IP6ADDR, + .name = "ipv6_addr", + .desc = "IPv6 address", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 16 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = ip6addr_type_print, + .parse = ip6addr_type_parse, + .flags = DTYPE_F_PREFIX, +}; + +static void inet_protocol_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + if (!nft_output_numeric_proto(octx)) { + char name[NFT_PROTONAME_MAXSIZE]; + + if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) { + nft_print(octx, "%s", name); + return; + } + } + integer_type_print(expr, octx); +} + +static void inet_protocol_type_describe(struct output_ctx *octx) +{ + uint8_t protonum; + + for (protonum = 0; protonum < UINT8_MAX; protonum++) { + char name[NFT_PROTONAME_MAXSIZE]; + + if (!nft_getprotobynumber(protonum, name, sizeof(name))) + continue; + + nft_print(octx, "\t%-30s\t%u\n", name, protonum); + } +} + +static struct error_record *inet_protocol_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + uint8_t proto; + uintmax_t i; + char *end; + + errno = 0; + i = strtoumax(sym->identifier, &end, 0); + if (sym->identifier != end && *end == '\0') { + if (errno == ERANGE || i > UINT8_MAX) + return error(&sym->location, "Protocol out of range"); + + proto = i; + } else { + int r; + + r = nft_getprotobyname(sym->identifier); + if (r < 0) + return error(&sym->location, "Could not resolve protocol name"); + + proto = r; + } + + *res = constant_expr_alloc(&sym->location, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, BITS_PER_BYTE, + &proto); + return NULL; +} + +const struct datatype inet_protocol_type = { + .type = TYPE_INET_PROTOCOL, + .name = "inet_proto", + .desc = "Internet protocol", + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .print = inet_protocol_type_print, + .json = inet_protocol_type_json, + .parse = inet_protocol_type_parse, + .describe = inet_protocol_type_describe, +}; + +static void inet_service_print(const struct expr *expr, struct output_ctx *octx) +{ + uint16_t port = mpz_get_be16(expr->value); + char name[NFT_SERVNAME_MAXSIZE]; + + if (!nft_getservbyport(port, NULL, name, sizeof(name))) + nft_print(octx, "%hu", ntohs(port)); + else + nft_print(octx, "\"%s\"", name); +} + +void inet_service_type_print(const struct expr *expr, struct output_ctx *octx) +{ + if (nft_output_service(octx)) { + inet_service_print(expr, octx); + return; + } + integer_type_print(expr, octx); +} + +static struct error_record *inet_service_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct addrinfo *ai; + uint16_t port; + uintmax_t i; + char *end; + int err; + + errno = 0; + i = strtoumax(sym->identifier, &end, 0); + if (sym->identifier != end && *end == '\0') { + if (errno == ERANGE || i > UINT16_MAX) + return error(&sym->location, "Service out of range"); + + port = htons(i); + } else { + err = getaddrinfo(NULL, sym->identifier, NULL, &ai); + if (err != 0) + return error(&sym->location, "Could not resolve service: %s", + gai_strerror(err)); + + if (ai->ai_addr->sa_family == AF_INET) { + port = ((struct sockaddr_in *)(void *)ai->ai_addr)->sin_port; + } else { + assert(ai->ai_addr->sa_family == AF_INET6); + port = ((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_port; + } + freeaddrinfo(ai); + } + + *res = constant_expr_alloc(&sym->location, &inet_service_type, + BYTEORDER_BIG_ENDIAN, + sizeof(port) * BITS_PER_BYTE, &port); + return NULL; +} + +const struct datatype inet_service_type = { + .type = TYPE_INET_SERVICE, + .name = "inet_service", + .desc = "internet network service", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = inet_service_type_print, + .json = inet_service_type_json, + .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; + + 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); + break; + } + + /* 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(const struct symbol_table *tbl) +{ + const struct symbolic_constant *s; + + for (s = tbl->symbols; s->identifier != NULL; s++) + xfree(s->identifier); + xfree(tbl); +} + +void mark_table_init(struct nft_ctx *ctx) +{ + ctx->output.tbl.mark = rt_symbol_table_init("/etc/iproute2/rt_marks"); +} + +void mark_table_exit(struct nft_ctx *ctx) +{ + rt_symbol_table_free(ctx->output.tbl.mark); +} + +static void mark_type_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(octx->tbl.mark, expr, true, octx); +} + +static struct error_record *mark_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, ctx->tbl->mark, res); +} + +const struct datatype mark_type = { + .type = TYPE_MARK, + .name = "mark", + .desc = "packet mark", + .size = 4 * BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + .basetype = &integer_type, + .basefmt = "0x%.8Zx", + .print = mark_type_print, + .json = mark_type_json, + .parse = mark_type_parse, + .flags = DTYPE_F_PREFIX, +}; + +static const struct symbol_table icmp_code_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("net-unreachable", ICMP_NET_UNREACH), + SYMBOL("host-unreachable", ICMP_HOST_UNREACH), + SYMBOL("prot-unreachable", ICMP_PROT_UNREACH), + SYMBOL("port-unreachable", ICMP_PORT_UNREACH), + SYMBOL("net-prohibited", ICMP_NET_ANO), + SYMBOL("host-prohibited", ICMP_HOST_ANO), + SYMBOL("admin-prohibited", ICMP_PKT_FILTERED), + SYMBOL("frag-needed", ICMP_FRAG_NEEDED), + SYMBOL_LIST_END + }, +}; + +const struct datatype icmp_code_type = { + .type = TYPE_ICMP_CODE, + .name = "icmp_code", + .desc = "icmp code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .sym_tbl = &icmp_code_tbl, +}; + +static const struct symbol_table icmpv6_code_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("no-route", ICMPV6_NOROUTE), + SYMBOL("admin-prohibited", ICMPV6_ADM_PROHIBITED), + SYMBOL("addr-unreachable", ICMPV6_ADDR_UNREACH), + SYMBOL("port-unreachable", ICMPV6_PORT_UNREACH), + SYMBOL("policy-fail", ICMPV6_POLICY_FAIL), + SYMBOL("reject-route", ICMPV6_REJECT_ROUTE), + SYMBOL_LIST_END + }, +}; + +const struct datatype icmpv6_code_type = { + .type = TYPE_ICMPV6_CODE, + .name = "icmpv6_code", + .desc = "icmpv6 code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .sym_tbl = &icmpv6_code_tbl, +}; + +static const struct symbol_table icmpx_code_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("port-unreachable", NFT_REJECT_ICMPX_PORT_UNREACH), + SYMBOL("admin-prohibited", NFT_REJECT_ICMPX_ADMIN_PROHIBITED), + SYMBOL("no-route", NFT_REJECT_ICMPX_NO_ROUTE), + SYMBOL("host-unreachable", NFT_REJECT_ICMPX_HOST_UNREACH), + SYMBOL_LIST_END + }, +}; + +const struct datatype icmpx_code_type = { + .type = TYPE_ICMPX_CODE, + .name = "icmpx_code", + .desc = "icmpx code", + .size = BITS_PER_BYTE, + .byteorder = BYTEORDER_BIG_ENDIAN, + .basetype = &integer_type, + .sym_tbl = &icmpx_code_tbl, +}; + +void time_print(uint64_t ms, struct output_ctx *octx) +{ + uint64_t days, hours, minutes, seconds; + + if (nft_output_seconds(octx)) { + nft_print(octx, "%" PRIu64 "s", ms / 1000); + return; + } + + days = ms / 86400000; + ms %= 86400000; + + hours = ms / 3600000; + ms %= 3600000; + + minutes = ms / 60000; + ms %= 60000; + + seconds = ms / 1000; + ms %= 1000; + + if (days > 0) + nft_print(octx, "%" PRIu64 "d", days); + if (hours > 0) + nft_print(octx, "%" PRIu64 "h", hours); + if (minutes > 0) + nft_print(octx, "%" PRIu64 "m", minutes); + if (seconds > 0) + nft_print(octx, "%" PRIu64 "s", seconds); + if (ms > 0) + nft_print(octx, "%" PRIu64 "ms", ms); +} + +enum { + DAY = (1 << 0), + HOUR = (1 << 1), + MIN = (1 << 2), + SECS = (1 << 3), + MSECS = (1 << 4), +}; + +static uint32_t str2int(const char *str) +{ + int ret, number; + + ret = sscanf(str, "%d", &number); + return ret == 1 ? number : 0; +} + +struct error_record *time_parse(const struct location *loc, const char *str, + uint64_t *res) +{ + unsigned int max_digits = strlen("12345678"); + int i, len; + unsigned int k = 0; + const char *c; + uint64_t d = 0, h = 0, m = 0, s = 0, ms = 0; + uint32_t mask = 0; + + c = str; + len = strlen(c); + for (i = 0; i < len; i++, c++) { + switch (*c) { + case 'd': + if (mask & DAY) + return error(loc, + "Day has been specified twice"); + + d = str2int(c - k); + k = 0; + mask |= DAY; + break; + case 'h': + if (mask & HOUR) + return error(loc, + "Hour has been specified twice"); + + h = str2int(c - k); + k = 0; + mask |= HOUR; + break; + case 'm': + if (strcmp(c, "ms") == 0) { + if (mask & MSECS) + return error(loc, + "Millisecond has been specified twice"); + ms = str2int(c - k); + c++; + i++; + k = 0; + mask |= MSECS; + break; + } + + if (mask & MIN) + return error(loc, + "Minute has been specified twice"); + + m = str2int(c - k); + k = 0; + mask |= MIN; + break; + case 's': + if (mask & SECS) + return error(loc, + "Second has been specified twice"); + + s = str2int(c - k); + k = 0; + mask |= SECS; + break; + default: + if (!isdigit(*c)) + return error(loc, "wrong time format"); + + if (k++ >= max_digits) + return error(loc, "value too large"); + break; + } + } + + /* default to seconds if no unit was specified */ + if (!mask) + ms = atoi(str) * MSEC_PER_SEC; + else + ms = 24*60*60*MSEC_PER_SEC * d + + 60*60*MSEC_PER_SEC * h + + 60*MSEC_PER_SEC * m + + MSEC_PER_SEC * s + ms; + + *res = ms; + return NULL; +} + + +static void time_type_print(const struct expr *expr, struct output_ctx *octx) +{ + time_print(mpz_get_uint64(expr->value), octx); +} + +static struct error_record *time_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct error_record *erec; + uint32_t s32; + uint64_t s; + + erec = time_parse(&sym->location, sym->identifier, &s); + if (erec != NULL) + return erec; + + if (s > UINT32_MAX) + return error(&sym->location, "value too large"); + + s32 = s; + *res = constant_expr_alloc(&sym->location, &time_type, + BYTEORDER_HOST_ENDIAN, + sizeof(uint32_t) * BITS_PER_BYTE, &s32); + return NULL; +} + +const struct datatype time_type = { + .type = TYPE_TIME, + .name = "time", + .desc = "relative time", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = time_type_print, + .json = time_type_json, + .parse = time_type_parse, +}; + +static struct error_record *concat_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return error(&sym->location, "invalid data type, expected %s", + sym->dtype->desc); +} + +static struct datatype *datatype_alloc(void) +{ + struct datatype *dtype; + + dtype = xzalloc(sizeof(*dtype)); + dtype->flags = DTYPE_F_ALLOC; + dtype->refcnt = 1; + + return dtype; +} + +const struct datatype *datatype_get(const struct datatype *ptr) +{ + struct datatype *dtype = (struct datatype *)ptr; + + if (!dtype) + return NULL; + if (!(dtype->flags & DTYPE_F_ALLOC)) + return dtype; + + dtype->refcnt++; + return dtype; +} + +void __datatype_set(struct expr *expr, const struct datatype *dtype) +{ + const struct datatype *dtype_free; + + dtype_free = expr->dtype; + expr->dtype = dtype; + datatype_free(dtype_free); +} + +void datatype_set(struct expr *expr, const struct datatype *dtype) +{ + if (dtype != expr->dtype) + __datatype_set(expr, datatype_get(dtype)); +} + +struct datatype *datatype_clone(const struct datatype *orig_dtype) +{ + struct datatype *dtype; + + dtype = xmalloc(sizeof(*dtype)); + *dtype = *orig_dtype; + dtype->name = xstrdup(orig_dtype->name); + dtype->desc = xstrdup(orig_dtype->desc); + dtype->flags = DTYPE_F_ALLOC | orig_dtype->flags; + dtype->refcnt = 1; + + return dtype; +} + +void datatype_free(const struct datatype *ptr) +{ + struct datatype *dtype = (struct datatype *)ptr; + + if (!dtype) + return; + if (!(dtype->flags & DTYPE_F_ALLOC)) + return; + + assert(dtype->refcnt != 0); + + if (--dtype->refcnt > 0) + return; + + xfree(dtype->name); + xfree(dtype->desc); + xfree(dtype); +} + +const struct datatype *concat_type_alloc(uint32_t type) +{ + const struct datatype *i; + struct datatype *dtype; + char desc[256] = "concatenation of ("; + char name[256] = ""; + unsigned int size = 0, subtypes = 0, n; + + n = div_round_up(fls(type), TYPE_BITS); + while (n > 0 && concat_subtype_id(type, --n)) { + i = concat_subtype_lookup(type, n); + if (i == NULL) + return NULL; + + if (subtypes != 0) { + strncat(desc, ", ", sizeof(desc) - strlen(desc) - 1); + strncat(name, " . ", sizeof(name) - strlen(name) - 1); + } + strncat(desc, i->desc, sizeof(desc) - strlen(desc) - 1); + strncat(name, i->name, sizeof(name) - strlen(name) - 1); + + size += netlink_padded_len(i->size); + subtypes++; + } + strncat(desc, ")", sizeof(desc) - strlen(desc) - 1); + + dtype = datatype_alloc(); + dtype->type = type; + dtype->size = size; + dtype->subtypes = subtypes; + dtype->name = xstrdup(name); + dtype->desc = xstrdup(desc); + dtype->parse = concat_type_parse; + + return dtype; +} + +const struct datatype *set_datatype_alloc(const struct datatype *orig_dtype, + enum byteorder byteorder) +{ + struct datatype *dtype; + + /* Restrict dynamic datatype allocation to generic integer datatype. */ + if (orig_dtype != &integer_type) + return datatype_get(orig_dtype); + + dtype = datatype_clone(orig_dtype); + dtype->byteorder = byteorder; + + return dtype; +} + +static struct error_record *time_unit_parse(const struct location *loc, + const char *str, uint64_t *unit) +{ + if (strcmp(str, "second") == 0) + *unit = 1ULL; + else if (strcmp(str, "minute") == 0) + *unit = 1ULL * 60; + else if (strcmp(str, "hour") == 0) + *unit = 1ULL * 60 * 60; + else if (strcmp(str, "day") == 0) + *unit = 1ULL * 60 * 60 * 24; + else if (strcmp(str, "week") == 0) + *unit = 1ULL * 60 * 60 * 24 * 7; + else + return error(loc, "Wrong rate format"); + + return NULL; +} + +struct error_record *data_unit_parse(const struct location *loc, + const char *str, uint64_t *rate) +{ + if (strncmp(str, "bytes", strlen("bytes")) == 0) + *rate = 1ULL; + else if (strncmp(str, "kbytes", strlen("kbytes")) == 0) + *rate = 1024; + else if (strncmp(str, "mbytes", strlen("mbytes")) == 0) + *rate = 1024 * 1024; + else + return error(loc, "Wrong rate format"); + + return NULL; +} + +struct error_record *rate_parse(const struct location *loc, const char *str, + uint64_t *rate, uint64_t *unit) +{ + struct error_record *erec; + const char *slash; + + slash = strchr(str, '/'); + if (!slash) + return error(loc, "wrong rate format"); + + erec = data_unit_parse(loc, str, rate); + if (erec != NULL) + return erec; + + erec = time_unit_parse(loc, slash + 1, unit); + if (erec != NULL) + return erec; + + return NULL; +} + +static const struct symbol_table boolean_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("exists", true), + SYMBOL("missing", false), + SYMBOL_LIST_END + }, +}; + +const struct datatype boolean_type = { + .type = TYPE_BOOLEAN, + .name = "boolean", + .desc = "boolean type", + .size = 1, + .basetype = &integer_type, + .sym_tbl = &boolean_tbl, + .json = boolean_type_json, +}; + +static struct error_record *priority_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct error_record *erec; + int num; + + erec = integer_type_parse(ctx, sym, res); + if (!erec) { + num = atoi(sym->identifier); + expr_free(*res); + *res = constant_expr_alloc(&sym->location, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, &num); + } else { + erec_destroy(erec); + *res = constant_expr_alloc(&sym->location, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(sym->identifier) * BITS_PER_BYTE, + sym->identifier); + } + + return NULL; +} + +/* This datatype is not registered via datatype_register() + * since this datatype should not ever be used from either + * rules or elements. + */ +const struct datatype priority_type = { + .type = TYPE_STRING, + .name = "priority", + .desc = "priority type", + .parse = priority_type_parse, +}; + +static struct error_record *policy_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + int policy; + + if (!strcmp(sym->identifier, "accept")) + policy = NF_ACCEPT; + else if (!strcmp(sym->identifier, "drop")) + policy = NF_DROP; + else + return error(&sym->location, "wrong policy"); + + *res = constant_expr_alloc(&sym->location, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, &policy); + return NULL; +} + +/* This datatype is not registered via datatype_register() + * since this datatype should not ever be used from either + * rules or elements. + */ +const struct datatype policy_type = { + .type = TYPE_STRING, + .name = "policy", + .desc = "policy type", + .parse = policy_type_parse, +}; + +#define SYSFS_CGROUPSV2_PATH "/sys/fs/cgroup" + +static const char *cgroupv2_get_path(const char *path, uint64_t id) +{ + const char *cgroup_path = NULL; + char dent_name[PATH_MAX + 1]; + struct dirent *dent; + struct stat st; + DIR *d; + + d = opendir(path); + if (!d) + return NULL; + + while ((dent = readdir(d)) != NULL) { + if (!strcmp(dent->d_name, ".") || + !strcmp(dent->d_name, "..")) + continue; + + snprintf(dent_name, sizeof(dent_name), "%s/%s", + path, dent->d_name); + dent_name[sizeof(dent_name) - 1] = '\0'; + + if (dent->d_ino == id) { + cgroup_path = xstrdup(dent_name); + break; + } + + if (stat(dent_name, &st) >= 0 && S_ISDIR(st.st_mode)) { + cgroup_path = cgroupv2_get_path(dent_name, id); + if (cgroup_path) + break; + } + } + closedir(d); + + return cgroup_path; +} + +static void cgroupv2_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + uint64_t id = mpz_get_uint64(expr->value); + const char *cgroup_path; + + cgroup_path = cgroupv2_get_path(SYSFS_CGROUPSV2_PATH, id); + if (cgroup_path) + nft_print(octx, "\"%s\"", + &cgroup_path[strlen(SYSFS_CGROUPSV2_PATH) + 1]); + else + nft_print(octx, "%" PRIu64, id); + + xfree(cgroup_path); +} + +static struct error_record *cgroupv2_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + char cgroupv2_path[PATH_MAX + 1]; + struct stat st; + uint64_t ino; + + snprintf(cgroupv2_path, sizeof(cgroupv2_path), "%s/%s", + SYSFS_CGROUPSV2_PATH, sym->identifier); + cgroupv2_path[sizeof(cgroupv2_path) - 1] = '\0'; + + if (stat(cgroupv2_path, &st) < 0) + return error(&sym->location, "cgroupv2 path fails: %s", + strerror(errno)); + + ino = st.st_ino; + *res = constant_expr_alloc(&sym->location, &cgroupv2_type, + BYTEORDER_HOST_ENDIAN, + sizeof(ino) * BITS_PER_BYTE, &ino); + return NULL; +} + +const struct datatype cgroupv2_type = { + .type = TYPE_CGROUPV2, + .name = "cgroupsv2", + .desc = "cgroupsv2 path", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 8 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = cgroupv2_type_print, + .parse = cgroupv2_type_parse, +}; diff --git a/src/dccpopt.c b/src/dccpopt.c new file mode 100644 index 0000000..ebb645a --- /dev/null +++ b/src/dccpopt.c @@ -0,0 +1,277 @@ +#include <nft.h> + +#include <stddef.h> + +#include <datatype.h> +#include <dccpopt.h> +#include <expression.h> +#include <nftables.h> +#include <utils.h> + +#define PHT(__token, __offset, __len) \ + PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \ + __offset, __len) + +static const struct proto_hdr_template dccpopt_unknown_template = + PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0); + +/* + * Option DCCP- Section + * Type Length Meaning Data? Reference + * ---- ------ ------- ----- --------- + * 0 1 Padding Y 5.8.1 + * 1 1 Mandatory N 5.8.2 + * 2 1 Slow Receiver Y 11.6 + * 3-31 1 Reserved + * 32 variable Change L N 6.1 + * 33 variable Confirm L N 6.2 + * 34 variable Change R N 6.1 + * 35 variable Confirm R N 6.2 + * 36 variable Init Cookie N 8.1.4 + * 37 3-8 NDP Count Y 7.7 + * 38 variable Ack Vector [Nonce 0] N 11.4 + * 39 variable Ack Vector [Nonce 1] N 11.4 + * 40 variable Data Dropped N 11.7 + * 41 6 Timestamp Y 13.1 + * 42 6/8/10 Timestamp Echo Y 13.3 + * 43 4/6 Elapsed Time N 13.2 + * 44 6 Data Checksum Y 9.3 + * 45-127 variable Reserved + * 128-255 variable CCID-specific options - 10.3 + */ + +static const struct exthdr_desc dccpopt_padding = { + .name = "padding", + .type = DCCPOPT_PADDING, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_mandatory = { + .name = "mandatory", + .type = DCCPOPT_MANDATORY, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_slow_receiver = { + .name = "slow_receiver", + .type = DCCPOPT_SLOW_RECEIVER, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_reserved_short = { + .name = "reserved_short", + .type = DCCPOPT_RESERVED_SHORT, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_change_l = { + .name = "change_l", + .type = DCCPOPT_CHANGE_L, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8) + }, +}; + +static const struct exthdr_desc dccpopt_confirm_l = { + .name = "confirm_l", + .type = DCCPOPT_CONFIRM_L, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_change_r = { + .name = "change_r", + .type = DCCPOPT_CHANGE_R, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_confirm_r = { + .name = "confirm_r", + .type = DCCPOPT_CONFIRM_R, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_init_cookie = { + .name = "init_cookie", + .type = DCCPOPT_INIT_COOKIE, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_ndp_count = { + .name = "ndp_count", + .type = DCCPOPT_NDP_COUNT, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_ack_vector_nonce_0 = { + .name = "ack_vector_nonce_0", + .type = DCCPOPT_ACK_VECTOR_NONCE_0, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_ack_vector_nonce_1 = { + .name = "ack_vector_nonce_1", + .type = DCCPOPT_ACK_VECTOR_NONCE_1, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_data_dropped = { + .name = "data_dropped", + .type = DCCPOPT_DATA_DROPPED, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_timestamp = { + .name = "timestamp", + .type = DCCPOPT_TIMESTAMP, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_timestamp_echo = { + .name = "timestamp_echo", + .type = DCCPOPT_TIMESTAMP_ECHO, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_elapsed_time = { + .name = "elapsed_time", + .type = DCCPOPT_ELAPSED_TIME, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_data_checksum = { + .name = "data_checksum", + .type = DCCPOPT_DATA_CHECKSUM, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_reserved_long = { + .name = "reserved_long", + .type = DCCPOPT_RESERVED_LONG, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +static const struct exthdr_desc dccpopt_ccid_specific = { + .name = "ccid_specific", + .type = DCCPOPT_CCID_SPECIFIC, + .templates = { + [DCCPOPT_FIELD_TYPE] = PHT("type", 0, 8), + }, +}; + +const struct exthdr_desc *dccpopt_protocols[1 + UINT8_MAX] = { + [DCCPOPT_PADDING] = &dccpopt_padding, + [DCCPOPT_MANDATORY] = &dccpopt_mandatory, + [DCCPOPT_SLOW_RECEIVER] = &dccpopt_slow_receiver, + [DCCPOPT_RESERVED_SHORT] = &dccpopt_reserved_short, + [DCCPOPT_CHANGE_L] = &dccpopt_change_l, + [DCCPOPT_CONFIRM_L] = &dccpopt_confirm_l, + [DCCPOPT_CHANGE_R] = &dccpopt_change_r, + [DCCPOPT_CONFIRM_R] = &dccpopt_confirm_r, + [DCCPOPT_INIT_COOKIE] = &dccpopt_init_cookie, + [DCCPOPT_NDP_COUNT] = &dccpopt_ndp_count, + [DCCPOPT_ACK_VECTOR_NONCE_0] = &dccpopt_ack_vector_nonce_0, + [DCCPOPT_ACK_VECTOR_NONCE_1] = &dccpopt_ack_vector_nonce_1, + [DCCPOPT_DATA_DROPPED] = &dccpopt_data_dropped, + [DCCPOPT_TIMESTAMP] = &dccpopt_timestamp, + [DCCPOPT_TIMESTAMP_ECHO] = &dccpopt_timestamp_echo, + [DCCPOPT_ELAPSED_TIME] = &dccpopt_elapsed_time, + [DCCPOPT_DATA_CHECKSUM] = &dccpopt_data_checksum, + [DCCPOPT_RESERVED_LONG] = &dccpopt_reserved_long, + [DCCPOPT_CCID_SPECIFIC] = &dccpopt_ccid_specific, +}; + +const struct exthdr_desc * +dccpopt_find_desc(uint8_t type) +{ + enum dccpopt_types proto_idx = + 3 <= type && type <= 31 ? DCCPOPT_RESERVED_SHORT : + 45 <= type && type <= 127 ? DCCPOPT_RESERVED_LONG : + 128 <= type ? DCCPOPT_CCID_SPECIFIC : type; + + return dccpopt_protocols[proto_idx]; +} + +struct expr * +dccpopt_expr_alloc(const struct location *loc, uint8_t type) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc; + struct expr *expr; + + desc = dccpopt_find_desc(type); + tmpl = &desc->templates[DCCPOPT_FIELD_TYPE]; + + expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE); + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.offset = tmpl->offset; + expr->exthdr.raw_type = type; + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + expr->exthdr.op = NFT_EXTHDR_OP_DCCP; + + return expr; +} + +void +dccpopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset, + unsigned int len) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc; + + assert(expr->etype == EXPR_EXTHDR); + + desc = dccpopt_find_desc(type); + tmpl = &desc->templates[DCCPOPT_FIELD_TYPE]; + + expr->len = len; + datatype_set(expr, &boolean_type); + + expr->exthdr.offset = offset; + expr->exthdr.desc = desc; + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + expr->exthdr.op = NFT_EXTHDR_OP_DCCP; + + /* Make sure that it's the right template based on offset and + * len + */ + if (tmpl->offset != offset || tmpl->len != len) + expr->exthdr.tmpl = &dccpopt_unknown_template; + else + expr->exthdr.tmpl = tmpl; +} diff --git a/src/erec.c b/src/erec.c new file mode 100644 index 0000000..cd9f62b --- /dev/null +++ b/src/erec.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stdio.h> +#include <stdarg.h> + +#include <netlink.h> +#include <gmputil.h> +#include <erec.h> + +static const struct input_descriptor internal_indesc = { + .type = INDESC_INTERNAL, + .name = "internal", +}; + +const struct location internal_location = { + .indesc = &internal_indesc, +}; + +static const char * const 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; + erec->locations[erec->num_locations].indesc = loc->indesc ? + : &internal_indesc; + erec->num_locations++; +} + +void erec_destroy(struct error_record *erec) +{ + xfree(erec->msg); + xfree(erec); +} + +__attribute__((format(printf, 3, 0))) +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); + + if (vasprintf(&erec->msg, fmt, ap) < 0) + erec->msg = NULL; + + return erec; +} + +__attribute__((format(printf, 3, 4))) +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 print_location(FILE *f, const struct input_descriptor *indesc, + const struct location *loc) +{ + const struct input_descriptor *tmp; + const struct location *iloc; + + if (indesc->location.indesc != NULL) { + const char *prefix = "In file included from"; + iloc = &indesc->location; + for (tmp = iloc->indesc; + tmp != NULL && tmp->type != INDESC_INTERNAL; + 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->type != INDESC_BUFFER && indesc->name) { + fprintf(f, "%s:%u:%u-%u: ", indesc->name, + loc->first_line, loc->first_column, + loc->last_column); + } +} + +const char *line_location(const struct input_descriptor *indesc, + const struct location *loc, char *buf, size_t bufsiz) +{ + const char *line = NULL; + FILE *f; + + f = fopen(indesc->name, "r"); + if (!f) + return NULL; + + if (!fseek(f, loc->line_offset, SEEK_SET) && + fread(buf, 1, bufsiz - 1, f) > 0) { + *strchrnul(buf, '\n') = '\0'; + line = buf; + } + fclose(f); + + return line; +} + +void erec_print(struct output_ctx *octx, const struct error_record *erec, + unsigned int debug_mask) +{ + const struct location *loc = erec->locations; + const struct input_descriptor *indesc = loc->indesc; + const char *line = NULL; + char buf[1024] = {}; + char *pbuf = NULL; + unsigned int i, end; + FILE *f; + int l; + + switch (indesc->type) { + case INDESC_BUFFER: + case INDESC_CLI: + line = indesc->data; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_STDIN: + line = indesc->data; + line += loc->line_offset; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_FILE: + line = line_location(indesc, loc, buf, sizeof(buf)); + break; + case INDESC_INTERNAL: + case INDESC_NETLINK: + break; + default: + BUG("invalid input descriptor type %u\n", indesc->type); + } + + f = octx->error_fp; + + 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]; + if (!loc->nle) + continue; + netlink_dump_expr(loc->nle, f, debug_mask); + } + return; + } + + print_location(f, indesc, loc); + + if (error_record_names[erec->type]) + fprintf(f, "%s: ", error_record_names[erec->type]); + fprintf(f, "%s\n", erec->msg); + + if (line) { + fprintf(f, "%s\n", line); + + end = 0; + for (l = erec->num_locations - 1; l >= 0; l--) { + loc = &erec->locations[l]; + end = max(end, loc->last_column); + } + pbuf = xmalloc(end + 1); + memset(pbuf, ' ', end + 1); + for (i = 0; i < end && line[i]; i++) { + if (line[i] == '\t') + pbuf[i] = '\t'; + } + for (l = erec->num_locations - 1; l >= 0; l--) { + loc = &erec->locations[l]; + for (i = loc->first_column ? loc->first_column - 1 : 0; + i < loc->last_column; i++) + pbuf[i] = l ? '~' : '^'; + } + pbuf[end] = '\0'; + fprintf(f, "%s", pbuf); + xfree(pbuf); + } + fprintf(f, "\n"); +} + +void erec_print_list(struct output_ctx *octx, struct list_head *list, + unsigned int debug_mask) +{ + struct error_record *erec, *next; + + list_for_each_entry_safe(erec, next, list, list) { + list_del(&erec->list); + erec_print(octx, erec, debug_mask); + erec_destroy(erec); + } +} + +int __fmtstring(4, 5) __stmt_binary_error(struct eval_ctx *ctx, + const struct location *l1, + const struct location *l2, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, l1, fmt, ap); + if (l2 != NULL) + erec_add_location(erec, l2); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} diff --git a/src/evaluate.c b/src/evaluate.c new file mode 100644 index 0000000..2196e92 --- /dev/null +++ b/src/evaluate.c @@ -0,0 +1,5857 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <linux/netfilter.h> +#include <linux/netfilter_arp.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_synproxy.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter_ipv4.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <errno.h> + +#include <expression.h> +#include <statement.h> +#include <intervals.h> +#include <netlink.h> +#include <time.h> +#include <rule.h> +#include <cache.h> +#include <erec.h> +#include <gmputil.h> +#include <utils.h> +#include <xt.h> + +struct proto_ctx *eval_proto_ctx(struct eval_ctx *ctx) +{ + uint8_t idx = ctx->inner_desc ? 1 : 0; + + return &ctx->_pctx[idx]; +} + +static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr); + +static const char * const byteorder_names[] = { + [BYTEORDER_INVALID] = "invalid", + [BYTEORDER_HOST_ENDIAN] = "host endian", + [BYTEORDER_BIG_ENDIAN] = "big endian", +}; + +#define chain_error(ctx, s1, fmt, args...) \ + __stmt_binary_error(ctx, &(s1)->location, NULL, fmt, ## args) +#define monitor_error(ctx, s1, fmt, args...) \ + __stmt_binary_error(ctx, &(s1)->location, NULL, fmt, ## args) +#define cmd_error(ctx, loc, fmt, args...) \ + __stmt_binary_error(ctx, loc, NULL, fmt, ## args) + +static int __fmtstring(3, 4) set_error(struct eval_ctx *ctx, + const struct set *set, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, &set->location, fmt, ap); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} + +static void key_fix_dtype_byteorder(struct expr *key) +{ + const struct datatype *dtype = key->dtype; + + if (dtype->byteorder == key->byteorder) + return; + + __datatype_set(key, set_datatype_alloc(dtype, key->byteorder)); +} + +static int set_evaluate(struct eval_ctx *ctx, struct set *set); +static struct expr *implicit_set_declaration(struct eval_ctx *ctx, + const char *name, + struct expr *key, + struct expr *data, + struct expr *expr) +{ + struct cmd *cmd; + struct set *set; + struct handle h; + + if (set_is_datamap(expr->set_flags)) + key_fix_dtype_byteorder(key); + + set = set_alloc(&expr->location); + set->flags = NFT_SET_ANONYMOUS | expr->set_flags; + set->handle.set.name = xstrdup(name); + set->key = key; + set->data = data; + set->init = expr; + set->automerge = set->flags & NFT_SET_INTERVAL; + + if (ctx->table != NULL) + list_add_tail(&set->list, &ctx->table->sets); + else { + handle_merge(&set->handle, &ctx->cmd->handle); + memset(&h, 0, sizeof(h)); + handle_merge(&h, &set->handle); + h.set.location = expr->location; + cmd = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &h, &expr->location, set); + cmd->location = set->location; + list_add_tail(&cmd->list, &ctx->cmd->list); + } + + set_evaluate(ctx, set); + + return set_ref_expr_alloc(&expr->location, set); +} + +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("invalid byte order conversion %u => %u\n", + expr->byteorder, byteorder); +} + +static int byteorder_conversion(struct eval_ctx *ctx, struct expr **expr, + enum byteorder byteorder) +{ + enum datatypes basetype; + enum ops op; + + assert(!expr_is_constant(*expr) || expr_is_singleton(*expr)); + + if ((*expr)->byteorder == byteorder) + return 0; + + /* Conversion for EXPR_CONCAT is handled for single composing ranges */ + if ((*expr)->etype == EXPR_CONCAT) { + struct expr *i, *next, *unary; + + list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { + if (i->byteorder == BYTEORDER_BIG_ENDIAN) + continue; + + basetype = expr_basetype(i)->type; + if (basetype == TYPE_STRING) + continue; + + assert(basetype == TYPE_INTEGER); + + op = byteorder_conversion_op(i, byteorder); + unary = unary_expr_alloc(&i->location, op, i); + if (expr_evaluate(ctx, &unary) < 0) + return -1; + + list_replace(&i->list, &unary->list); + } + + return 0; + } + + basetype = expr_basetype(*expr)->type; + switch (basetype) { + case TYPE_INTEGER: + break; + case TYPE_STRING: + return 0; + default: + return expr_error(ctx->msgs, *expr, + "Byteorder mismatch: %s expected %s, %s got %s", + byteorder_names[byteorder], + expr_name(*expr), + byteorder_names[(*expr)->byteorder]); + } + + if (expr_is_constant(*expr) || div_round_up((*expr)->len, BITS_PER_BYTE) < 2) + (*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; +} + +static int table_not_found(struct eval_ctx *ctx) +{ + struct table *table; + + table = table_lookup_fuzzy(&ctx->cmd->handle, &ctx->nft->cache); + if (table == NULL) + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "%s", strerror(ENOENT)); + + return cmd_error(ctx, &ctx->cmd->handle.table.location, + "%s; did you mean table ‘%s’ in family %s?", + strerror(ENOENT), table->handle.table.name, + family2str(table->handle.family)); +} + +static int chain_not_found(struct eval_ctx *ctx) +{ + const struct table *table; + struct chain *chain; + + chain = chain_lookup_fuzzy(&ctx->cmd->handle, &ctx->nft->cache, &table); + if (chain == NULL) + return cmd_error(ctx, &ctx->cmd->handle.chain.location, + "%s", strerror(ENOENT)); + + return cmd_error(ctx, &ctx->cmd->handle.chain.location, + "%s; did you mean chain ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), chain->handle.chain.name, + family2str(chain->handle.family), + table->handle.table.name); +} + +static int set_not_found(struct eval_ctx *ctx, const struct location *loc, + const char *set_name) +{ + const struct table *table; + struct set *set; + + set = set_lookup_fuzzy(set_name, &ctx->nft->cache, &table); + if (set == NULL) + return cmd_error(ctx, loc, "%s", strerror(ENOENT)); + + return cmd_error(ctx, loc, + "%s; did you mean %s ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), + set_is_map(set->flags) ? "map" : "set", + set->handle.set.name, + family2str(set->handle.family), + table->handle.table.name); +} + +static int flowtable_not_found(struct eval_ctx *ctx, const struct location *loc, + const char *ft_name) +{ + const struct table *table; + struct flowtable *ft; + + ft = flowtable_lookup_fuzzy(ft_name, &ctx->nft->cache, &table); + if (!ft) + return cmd_error(ctx, loc, "%s", strerror(ENOENT)); + + return cmd_error(ctx, loc, + "%s; did you mean flowtable ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), ft->handle.flowtable.name, + family2str(ft->handle.family), + table->handle.table.name); +} + +/* + * Symbol expression: parse symbol and evaluate resulting expression. + */ +static int expr_evaluate_symbol(struct eval_ctx *ctx, struct expr **expr) +{ + struct parse_ctx parse_ctx = { + .tbl = &ctx->nft->output.tbl, + .input = &ctx->nft->input, + }; + struct error_record *erec; + struct table *table; + struct set *set; + struct expr *new; + + switch ((*expr)->symtype) { + case SYMBOL_VALUE: + datatype_set(*expr, ctx->ectx.dtype); + erec = symbol_parse(&parse_ctx, *expr, &new); + if (erec != NULL) { + erec_queue(erec, ctx->msgs); + return -1; + } + break; + case SYMBOL_SET: + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + set = set_cache_find(table, (*expr)->identifier); + if (set == NULL || !set->key) + return set_not_found(ctx, &(*expr)->location, + (*expr)->identifier); + + new = set_ref_expr_alloc(&(*expr)->location, set); + break; + } + + expr_free(*expr); + *expr = new; + + return expr_evaluate(ctx, expr); +} + +static int expr_evaluate_string(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE), datalen; + struct expr *value, *prefix; + int data_len = ctx->ectx.len > 0 ? ctx->ectx.len : len + 1; + char data[data_len]; + + if (ctx->ectx.len > 0) { + if (expr->len > ctx->ectx.len) + return expr_error(ctx->msgs, expr, + "String exceeds maximum length of %u", + ctx->ectx.len / BITS_PER_BYTE); + expr->len = ctx->ectx.len; + } + + memset(data + len, 0, data_len - len); + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + + if (strlen(data) == 0) + return expr_error(ctx->msgs, expr, + "Empty string is not allowed"); + + datalen = strlen(data) - 1; + if (data[datalen] != '*') { + /* We need to reallocate the constant expression with the right + * expression length to avoid problems on big endian. + */ + value = constant_expr_alloc(&expr->location, ctx->ectx.dtype, + BYTEORDER_HOST_ENDIAN, + expr->len, data); + expr_free(expr); + *exprp = value; + return 0; + } + + if (datalen == 0) + return expr_error(ctx->msgs, expr, + "All-wildcard strings are not supported"); + + if (data[datalen - 1] == '\\') { + char unescaped_str[data_len]; + + memset(unescaped_str, 0, sizeof(unescaped_str)); + xstrunescape(data, unescaped_str); + + value = constant_expr_alloc(&expr->location, ctx->ectx.dtype, + BYTEORDER_HOST_ENDIAN, + expr->len, unescaped_str); + expr_free(expr); + *exprp = value; + return 0; + } + + data[datalen] = 0; + value = constant_expr_alloc(&expr->location, ctx->ectx.dtype, + BYTEORDER_HOST_ENDIAN, + expr->len, data); + + prefix = prefix_expr_alloc(&expr->location, value, + datalen * BITS_PER_BYTE); + datatype_set(prefix, ctx->ectx.dtype); + prefix->flags |= EXPR_F_CONSTANT; + prefix->byteorder = BYTEORDER_HOST_ENDIAN; + prefix->len = expr->len; + + expr_free(expr); + *exprp = prefix; + return 0; +} + +static int expr_evaluate_integer(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + char *valstr, *rangestr; + uint32_t masklen; + mpz_t mask; + + if (ctx->ectx.maxval > 0 && + mpz_cmp_ui(expr->value, ctx->ectx.maxval) > 0) { + valstr = mpz_get_str(NULL, 10, expr->value); + expr_error(ctx->msgs, expr, + "Value %s exceeds valid range 0-%u", + valstr, ctx->ectx.maxval); + free(valstr); + return -1; + } + + if (ctx->stmt_len > ctx->ectx.len) + masklen = ctx->stmt_len; + else + masklen = ctx->ectx.len; + + mpz_init_bitmask(mask, masklen); + if (mpz_cmp(expr->value, mask) > 0) { + valstr = mpz_get_str(NULL, 10, expr->value); + rangestr = mpz_get_str(NULL, 10, mask); + expr_error(ctx->msgs, expr, + "Value %s exceeds valid range 0-%s", + valstr, rangestr); + free(valstr); + free(rangestr); + mpz_clear(mask); + return -1; + } + expr->byteorder = ctx->ectx.byteorder; + expr->len = masklen; + mpz_clear(mask); + return 0; +} + +static int expr_evaluate_value(struct eval_ctx *ctx, struct expr **expr) +{ + switch (expr_basetype(*expr)->type) { + case TYPE_INTEGER: + if (expr_evaluate_integer(ctx, expr) < 0) + return -1; + break; + case TYPE_STRING: + if (expr_evaluate_string(ctx, expr) < 0) + return -1; + break; + default: + BUG("invalid basetype %s\n", expr_basetype(*expr)->name); + } + return 0; +} + +/* + * Primary expressions determine the datatype context. + */ +static int expr_evaluate_primary(struct eval_ctx *ctx, struct expr **expr) +{ + __expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->byteorder, + (*expr)->len, 0); + return 0; +} + +static int +conflict_resolution_gen_dependency(struct eval_ctx *ctx, int protocol, + const struct expr *expr, + struct stmt **res) +{ + enum proto_bases base = expr->payload.base; + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc = NULL; + struct expr *dep, *left, *right; + struct proto_ctx *pctx; + struct stmt *stmt; + + assert(expr->payload.base == PROTO_BASE_LL_HDR); + + pctx = eval_proto_ctx(ctx); + desc = pctx->protocol[base].desc; + tmpl = &desc->templates[desc->protocol_key]; + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + + right = constant_expr_alloc(&expr->location, tmpl->dtype, + tmpl->dtype->byteorder, tmpl->len, + constant_data_ptr(protocol, tmpl->len)); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + stmt = expr_stmt_alloc(&dep->location, dep); + if (stmt_evaluate(ctx, stmt) < 0) + return expr_error(ctx->msgs, expr, + "dependency statement is invalid"); + + if (ctx->inner_desc) + left->payload.inner_desc = ctx->inner_desc; + + *res = stmt; + return 0; +} + +static uint8_t expr_offset_shift(const struct expr *expr, unsigned int offset, + unsigned int *extra_len) +{ + unsigned int new_offset, len; + int shift; + + new_offset = offset % BITS_PER_BYTE; + len = round_up(expr->len, BITS_PER_BYTE); + shift = len - (new_offset + expr->len); + while (shift < 0) { + shift += BITS_PER_BYTE; + *extra_len += BITS_PER_BYTE; + } + return shift; +} + +static void expr_evaluate_bits(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp, *and, *mask, *rshift, *off; + unsigned masklen, len = expr->len, extra_len = 0; + enum byteorder byteorder; + uint8_t shift; + mpz_t bitmask; + + switch (expr->etype) { + case EXPR_PAYLOAD: + shift = expr_offset_shift(expr, expr->payload.offset, + &extra_len); + break; + case EXPR_EXTHDR: + shift = expr_offset_shift(expr, expr->exthdr.offset, + &extra_len); + break; + default: + BUG("Unknown expression %s\n", expr_name(expr)); + } + + masklen = len + shift; + assert(masklen <= NFT_REG_SIZE * BITS_PER_BYTE); + + mpz_init2(bitmask, masklen); + mpz_bitmask(bitmask, len); + mpz_lshift_ui(bitmask, shift); + + mask = constant_expr_alloc(&expr->location, expr_basetype(expr), + BYTEORDER_HOST_ENDIAN, masklen, NULL); + mpz_set(mask->value, bitmask); + mpz_clear(bitmask); + + and = binop_expr_alloc(&expr->location, OP_AND, expr, mask); + and->dtype = expr->dtype; + and->byteorder = expr->byteorder; + and->len = masklen; + + if (shift) { + if (ctx->stmt_len > 0 && div_round_up(masklen, BITS_PER_BYTE) > 1) { + int op = byteorder_conversion_op(expr, BYTEORDER_HOST_ENDIAN); + and = unary_expr_alloc(&expr->location, op, and); + and->len = masklen; + byteorder = BYTEORDER_HOST_ENDIAN; + } else { + byteorder = expr->byteorder; + } + + off = constant_expr_alloc(&expr->location, + expr_basetype(expr), + BYTEORDER_HOST_ENDIAN, + sizeof(shift), &shift); + + rshift = binop_expr_alloc(&expr->location, OP_RSHIFT, and, off); + rshift->dtype = expr->dtype; + rshift->byteorder = byteorder; + rshift->len = masklen; + + *exprp = rshift; + } else + *exprp = and; + + if (extra_len) + expr->len += extra_len; +} + +static int __expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + + if (expr_evaluate_primary(ctx, exprp) < 0) + return -1; + + if (expr->exthdr.offset % BITS_PER_BYTE != 0 || + expr->len % BITS_PER_BYTE != 0) + expr_evaluate_bits(ctx, exprp); + + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_TCPOPT: { + static const unsigned int max_tcpoptlen = (15 * 4 - 20) * BITS_PER_BYTE; + unsigned int totlen; + + totlen = expr->exthdr.tmpl->len + expr->exthdr.offset; + + if (totlen > max_tcpoptlen) + return expr_error(ctx->msgs, expr, + "offset and size %u exceeds max tcp headerlen (%u)", + totlen, max_tcpoptlen); + break; + } + case NFT_EXTHDR_OP_IPV4: { + static const unsigned int max_ipoptlen = 40 * BITS_PER_BYTE; + unsigned int totlen; + + totlen = expr->exthdr.offset + expr->exthdr.tmpl->len; + + if (totlen > max_ipoptlen) + return expr_error(ctx->msgs, expr, + "offset and size %u exceeds max ip option len (%u)", + totlen, max_ipoptlen); + break; + } + default: + break; + } + + return 0; +} + +/* + * Exthdr expression: check whether dependencies are fulfilled, otherwise + * generate the necessary relational expression and prepend it to the current + * statement. + */ +static int expr_evaluate_exthdr(struct eval_ctx *ctx, struct expr **exprp) +{ + const struct proto_desc *base, *dependency = NULL; + enum proto_bases pb = PROTO_BASE_NETWORK_HDR; + struct expr *expr = *exprp; + struct proto_ctx *pctx; + struct stmt *nstmt; + + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_TCPOPT: + case NFT_EXTHDR_OP_SCTP: + case NFT_EXTHDR_OP_DCCP: + return __expr_evaluate_exthdr(ctx, exprp); + case NFT_EXTHDR_OP_IPV4: + dependency = &proto_ip; + break; + case NFT_EXTHDR_OP_IPV6: + default: + dependency = &proto_ip6; + break; + } + + assert(dependency); + + pctx = eval_proto_ctx(ctx); + base = pctx->protocol[pb].desc; + if (base == dependency) + return __expr_evaluate_exthdr(ctx, exprp); + + if (base) + return expr_error(ctx->msgs, expr, + "cannot use exthdr with %s", base->name); + + if (exthdr_gen_dependency(ctx, expr, dependency, pb - 1, &nstmt) < 0) + return -1; + + list_add(&nstmt->list, &ctx->rule->stmts); + + return __expr_evaluate_exthdr(ctx, exprp); +} + +/* dependency supersede. + * + * 'inet' is a 'phony' l2 dependency used by NFPROTO_INET to fulfil network + * header dependency, i.e. ensure that 'ip saddr 1.2.3.4' only sees ip headers. + * + * If a match expression that depends on a particular L2 header, e.g. ethernet, + * is used, we thus get a conflict since we already have a l2 header dependency. + * + * But in the inet case we can just ignore the conflict since only another + * restriction is added, and these are not mutually exclusive. + * + * Example: inet filter in ip saddr 1.2.3.4 ether saddr a:b:c:d:e:f + * + * ip saddr adds meta dependency on ipv4 packets + * ether saddr adds another dependency on ethernet frames. + */ +static int meta_iiftype_gen_dependency(struct eval_ctx *ctx, + struct expr *payload, struct stmt **res) +{ + struct stmt *nstmt; + uint16_t type; + + if (proto_dev_type(payload->payload.desc, &type) < 0) + return expr_error(ctx->msgs, payload, + "protocol specification is invalid " + "for this family"); + + nstmt = meta_stmt_meta_iiftype(&payload->location, type); + if (stmt_evaluate(ctx, nstmt) < 0) + return expr_error(ctx->msgs, payload, + "dependency statement is invalid"); + + if (ctx->inner_desc) + nstmt->expr->left->meta.inner_desc = ctx->inner_desc; + + *res = nstmt; + return 0; +} + +static bool proto_is_dummy(const struct proto_desc *desc) +{ + return desc == &proto_inet || desc == &proto_netdev; +} + +static int resolve_protocol_conflict(struct eval_ctx *ctx, + const struct proto_desc *desc, + struct expr *payload) +{ + enum proto_bases base = payload->payload.base; + struct stmt *nstmt = NULL; + struct proto_ctx *pctx; + int link, err; + + pctx = eval_proto_ctx(ctx); + + if (payload->payload.base == PROTO_BASE_LL_HDR) { + if (proto_is_dummy(desc)) { + if (ctx->inner_desc) { + proto_ctx_update(pctx, PROTO_BASE_LL_HDR, &payload->location, &proto_eth); + } else { + err = meta_iiftype_gen_dependency(ctx, payload, &nstmt); + if (err < 0) + return err; + + desc = payload->payload.desc; + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + } + } else { + unsigned int i; + + /* payload desc stored in the L2 header stack? No conflict. */ + for (i = 0; i < pctx->stacked_ll_count; i++) { + if (pctx->stacked_ll[i] == payload->payload.desc) + return 0; + } + } + } + + assert(base <= PROTO_BASE_MAX); + /* This payload and the existing context don't match, conflict. */ + if (pctx->protocol[base + 1].desc != NULL) + return 1; + + link = proto_find_num(desc, payload->payload.desc); + if (link < 0 || + conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0) + return 1; + + if (base == PROTO_BASE_LL_HDR) { + unsigned int i; + + for (i = 0; i < pctx->stacked_ll_count; i++) + payload->payload.offset += pctx->stacked_ll[i]->length; + } + + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + + 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 proto_bases base = payload->payload.base; + const struct proto_desc *desc; + struct proto_ctx *pctx; + struct stmt *nstmt; + int err; + + if (expr->etype == EXPR_PAYLOAD && expr->payload.is_raw) + return 0; + + pctx = eval_proto_ctx(ctx); + desc = pctx->protocol[base].desc; + if (desc == NULL) { + if (payload_gen_dependency(ctx, payload, &nstmt) < 0) + return -1; + + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + + desc = pctx->protocol[base].desc; + + if (desc == expr->payload.desc) + goto check_icmp; + + if (base == PROTO_BASE_LL_HDR) { + int link; + + link = proto_find_num(desc, payload->payload.desc); + if (link < 0 || + conflict_resolution_gen_dependency(ctx, link, payload, &nstmt) < 0) + return expr_error(ctx->msgs, payload, + "conflicting protocols specified: %s vs. %s", + desc->name, + payload->payload.desc->name); + + payload->payload.offset += pctx->stacked_ll[0]->length; + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + return 1; + } + goto check_icmp; + } + + if (payload->payload.base == desc->base && + proto_ctx_is_ambiguous(pctx, base)) { + desc = proto_ctx_find_conflict(pctx, base, payload->payload.desc); + assert(desc); + + return expr_error(ctx->msgs, payload, + "conflicting protocols specified: %s vs. %s", + desc->name, + payload->payload.desc->name); + } + + /* No conflict: Same payload protocol as context, adjust offset + * if needed. + */ + if (desc == payload->payload.desc) { + const struct proto_hdr_template *tmpl; + + if (desc->base == PROTO_BASE_LL_HDR) { + unsigned int i; + + for (i = 0; i < pctx->stacked_ll_count; i++) + payload->payload.offset += pctx->stacked_ll[i]->length; + } +check_icmp: + if (desc != &proto_icmp && desc != &proto_icmp6) + return 0; + + tmpl = expr->payload.tmpl; + + if (!tmpl || !tmpl->icmp_dep) + return 0; + + if (payload_gen_icmp_dependency(ctx, expr, &nstmt) < 0) + return -1; + + if (nstmt) + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + + return 0; + } + /* If we already have context and this payload is on the same + * base, try to resolve the protocol conflict. + */ + if (payload->payload.base == desc->base) { + err = resolve_protocol_conflict(ctx, desc, payload); + if (err <= 0) + return err; + + desc = pctx->protocol[base].desc; + if (desc == payload->payload.desc) + return 0; + } + return expr_error(ctx->msgs, payload, + "conflicting protocols specified: %s vs. %s", + pctx->protocol[base].desc->name, + payload->payload.desc->name); +} + +static bool payload_needs_adjustment(const struct expr *expr) +{ + return expr->payload.offset % BITS_PER_BYTE != 0 || + expr->len % BITS_PER_BYTE != 0; +} + +static int expr_evaluate_payload(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + + if (expr->payload.evaluated) + return 0; + + if (__expr_evaluate_payload(ctx, expr) < 0) + return -1; + + if (expr_evaluate_primary(ctx, exprp) < 0) + return -1; + + if (payload_needs_adjustment(expr)) + expr_evaluate_bits(ctx, exprp); + + expr->payload.evaluated = true; + + return 0; +} + +static int expr_evaluate_inner(struct eval_ctx *ctx, struct expr **exprp) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc = NULL; + struct expr *expr = *exprp; + int ret; + + assert(expr->etype == EXPR_PAYLOAD); + + pctx = eval_proto_ctx(ctx); + desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; + + if (desc == NULL && + expr->payload.inner_desc->base < PROTO_BASE_INNER_HDR) { + struct stmt *nstmt; + + if (payload_gen_inner_dependency(ctx, expr, &nstmt) < 0) + return -1; + + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + + proto_ctx_update(pctx, PROTO_BASE_TRANSPORT_HDR, &expr->location, expr->payload.inner_desc); + } + + if (expr->payload.inner_desc->base == PROTO_BASE_INNER_HDR) { + desc = pctx->protocol[expr->payload.inner_desc->base - 1].desc; + if (!desc) { + return expr_error(ctx->msgs, expr, + "no transport protocol specified"); + } + + if (proto_find_num(desc, expr->payload.inner_desc) < 0) { + return expr_error(ctx->msgs, expr, + "unexpected transport protocol %s", + desc->name); + } + + proto_ctx_update(pctx, expr->payload.inner_desc->base, &expr->location, + expr->payload.inner_desc); + } + + if (expr->payload.base != PROTO_BASE_INNER_HDR) + ctx->inner_desc = expr->payload.inner_desc; + + ret = expr_evaluate_payload(ctx, exprp); + + return ret; +} + +static int expr_evaluate_payload_inner(struct eval_ctx *ctx, struct expr **exprp) +{ + int ret; + + if ((*exprp)->payload.inner_desc) + ret = expr_evaluate_inner(ctx, exprp); + else + ret = expr_evaluate_payload(ctx, exprp); + + return ret; +} + +/* + * RT expression: validate protocol dependencies. + */ +static int expr_evaluate_rt(struct eval_ctx *ctx, struct expr **expr) +{ + static const char emsg[] = "cannot determine ip protocol version, use \"ip nexthop\" or \"ip6 nexthop\" instead"; + struct expr *rt = *expr; + struct proto_ctx *pctx; + + pctx = eval_proto_ctx(ctx); + rt_expr_update_type(pctx, rt); + + switch (rt->rt.key) { + case NFT_RT_NEXTHOP4: + if (rt->dtype != &ipaddr_type) + return expr_error(ctx->msgs, rt, "%s", emsg); + if (pctx->family == NFPROTO_IPV6) + return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip"); + break; + case NFT_RT_NEXTHOP6: + if (rt->dtype != &ip6addr_type) + return expr_error(ctx->msgs, rt, "%s", emsg); + if (pctx->family == NFPROTO_IPV4) + return expr_error(ctx->msgs, rt, "%s nexthop will not match", "ip6"); + break; + default: + break; + } + + return expr_evaluate_primary(ctx, expr); +} + +static int ct_gen_nh_dependency(struct eval_ctx *ctx, struct expr *ct) +{ + const struct proto_desc *base, *base_now; + struct expr *left, *right, *dep; + struct stmt *nstmt = NULL; + struct proto_ctx *pctx; + + pctx = eval_proto_ctx(ctx); + base_now = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + + switch (ct->ct.nfproto) { + case NFPROTO_IPV4: + base = &proto_ip; + break; + case NFPROTO_IPV6: + base = &proto_ip6; + break; + default: + base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (base == &proto_ip) + ct->ct.nfproto = NFPROTO_IPV4; + else if (base == &proto_ip) + ct->ct.nfproto = NFPROTO_IPV6; + + if (base) + break; + + return expr_error(ctx->msgs, ct, + "cannot determine ip protocol version, use \"ip %1$caddr\" or \"ip6 %1$caddr\" instead", + ct->ct.key == NFT_CT_SRC ? 's' : 'd'); + } + + /* no additional dependency needed? */ + if (base == base_now) + return 0; + + if (base_now && base_now != base) + return expr_error(ctx->msgs, ct, + "conflicting dependencies: %s vs. %s\n", + base->name, + pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name); + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + return 0; + } + + left = ct_expr_alloc(&ct->location, NFT_CT_L3PROTOCOL, ct->ct.direction); + + right = constant_expr_alloc(&ct->location, left->dtype, + left->dtype->byteorder, left->len, + constant_data_ptr(ct->ct.nfproto, left->len)); + dep = relational_expr_alloc(&ct->location, OP_EQ, left, right); + + relational_expr_pctx_update(pctx, dep); + + nstmt = expr_stmt_alloc(&dep->location, dep); + rule_stmt_insert_at(ctx->rule, nstmt, ctx->stmt); + + return 0; +} + +/* + * CT expression: update the protocol dependant types bases on the protocol + * context. + */ +static int expr_evaluate_ct(struct eval_ctx *ctx, struct expr **expr) +{ + const struct proto_desc *base, *error; + struct expr *ct = *expr; + struct proto_ctx *pctx; + + pctx = eval_proto_ctx(ctx); + base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + + switch (ct->ct.key) { + case NFT_CT_SRC: + case NFT_CT_DST: + ct_gen_nh_dependency(ctx, ct); + break; + case NFT_CT_SRC_IP: + case NFT_CT_DST_IP: + if (base == &proto_ip6) { + error = &proto_ip; + goto err_conflict; + } + break; + case NFT_CT_SRC_IP6: + case NFT_CT_DST_IP6: + if (base == &proto_ip) { + error = &proto_ip6; + goto err_conflict; + } + break; + default: + break; + } + + ct_expr_update_type(pctx, ct); + + return expr_evaluate_primary(ctx, expr); + +err_conflict: + return stmt_binary_error(ctx, ct, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: %s vs. %s", + base->name, error->name); +} + +/* + * Prefix expression: the argument must be a constant value of integer or + * string 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->prefix) < 0) + return -1; + base = prefix->prefix; + + if (!expr_is_constant(base)) + return expr_error(ctx->msgs, prefix, + "Prefix expression is undefined for " + "non-constant expressions"); + + switch (expr_basetype(base)->type) { + case TYPE_INTEGER: + case TYPE_STRING: + break; + default: + return expr_error(ctx->msgs, prefix, + "Prefix expression is undefined for " + "%s types", base->dtype->desc); + } + + if (prefix->prefix_len > base->len) + return expr_error(ctx->msgs, 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, expr_basetype(base), + BYTEORDER_HOST_ENDIAN, base->len, NULL); + switch (expr_basetype(base)->type) { + case TYPE_INTEGER: + mpz_prefixmask(mask->value, base->len, prefix->prefix_len); + break; + case TYPE_STRING: + mpz_bitmask(mask->value, prefix->prefix_len); + break; + } + and = binop_expr_alloc(&prefix->location, OP_AND, base, mask); + prefix->prefix = and; + if (expr_evaluate(ctx, &prefix->prefix) < 0) + return -1; + base = prefix->prefix; + 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->msgs, *expr, range, + "Range expression is undefined for " + "%s types", (*expr)->dtype->desc); + if (!expr_is_constant(*expr)) + return expr_binary_error(ctx->msgs, *expr, range, + "Range is not constant"); + return 0; +} + +static int __expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *range = *expr; + + if (expr_evaluate_range_expr(ctx, range, &range->left) < 0) + return -1; + if (expr_evaluate_range_expr(ctx, range, &range->right) < 0) + return -1; + + return 0; +} + +static int expr_evaluate_range(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *range = *expr, *left, *right; + int rc; + + rc = __expr_evaluate_range(ctx, expr); + if (rc) + return rc; + + left = range->left; + right = range->right; + + if (mpz_cmp(left->value, right->value) > 0) + return expr_error(ctx->msgs, range, + "Range has zero or negative size"); + datatype_set(range, 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 = unary->arg; + enum byteorder byteorder; + + /* unary expression arguments has already been evaluated. */ + + assert(!expr_is_constant(arg)); + assert(expr_basetype(arg)->type == TYPE_INTEGER); + assert(arg->etype != 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("invalid unary operation %u\n", unary->op); + } + + 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->etype == EXPR_VALUE); + assert(right->etype == 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_set(val, left->value); + 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("invalid binary operation %u\n", op->op); + } + + 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; + unsigned int shift = mpz_get_uint32(right->value); + unsigned int max_shift_len; + + if (ctx->stmt_len > left->len) + max_shift_len = ctx->stmt_len; + else + max_shift_len = left->len; + + if (shift >= max_shift_len) + return expr_binary_error(ctx->msgs, right, left, + "%s shift of %u bits is undefined for type of %u bits width", + op->op == OP_LSHIFT ? "Left" : "Right", + shift, max_shift_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; + + datatype_set(op, &integer_type); + op->byteorder = BYTEORDER_HOST_ENDIAN; + op->len = max_shift_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; + const struct datatype *dtype; + enum byteorder byteorder; + unsigned int max_len; + + if (ctx->stmt_len > left->len) { + max_len = ctx->stmt_len; + byteorder = BYTEORDER_HOST_ENDIAN; + dtype = &integer_type; + + /* Both sides need to be in host byte order */ + if (byteorder_conversion(ctx, &op->left, BYTEORDER_HOST_ENDIAN) < 0) + return -1; + + left = op->left; + } else { + max_len = left->len; + byteorder = left->byteorder; + dtype = left->dtype; + } + + if (byteorder_conversion(ctx, &op->right, byteorder) < 0) + return -1; + + datatype_set(op, dtype); + op->byteorder = byteorder; + op->len = max_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]; + unsigned int max_shift_len = ctx->ectx.len; + + if (expr_evaluate(ctx, &op->left) < 0) + return -1; + left = op->left; + + if (op->op == OP_LSHIFT || op->op == OP_RSHIFT) { + if (left->len > max_shift_len) + max_shift_len = left->len; + + __expr_set_context(&ctx->ectx, &integer_type, + left->byteorder, max_shift_len, 0); + } + + if (expr_evaluate(ctx, &op->right) < 0) + return -1; + right = op->right; + + switch (expr_basetype(left)->type) { + case TYPE_INTEGER: + case TYPE_STRING: + break; + default: + return expr_binary_error(ctx->msgs, left, op, + "Binary operation (%s) is undefined " + "for %s types", + sym, left->dtype->desc); + } + + if (expr_is_constant(left) && !expr_is_singleton(left)) + return expr_binary_error(ctx->msgs, left, op, + "Binary operation (%s) is undefined " + "for %s expressions", + sym, expr_name(left)); + + if (!expr_is_constant(right)) + return expr_binary_error(ctx->msgs, right, op, + "Right hand side of binary operation " + "(%s) must be constant", sym); + + if (!expr_is_singleton(right)) + return expr_binary_error(ctx->msgs, left, op, + "Binary operation (%s) is undefined " + "for %s expressions", + sym, expr_name(right)); + + /* The grammar guarantees this */ + assert(datatype_equal(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("invalid binary operation %u\n", op->op); + } +} + +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) +{ + const struct datatype *dtype = ctx->ectx.dtype, *tmp; + uint32_t type = dtype ? dtype->type : 0, ntype = 0; + int off = dtype ? dtype->subtypes : 0; + unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON; + const struct list_head *expressions = NULL; + struct expr *i, *next, *key = NULL; + const struct expr *key_ctx = NULL; + bool runaway = false; + uint32_t size = 0; + + if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) { + key_ctx = ctx->ectx.key; + assert(!list_empty(&ctx->ectx.key->expressions)); + key = list_first_entry(&ctx->ectx.key->expressions, struct expr, list); + expressions = &ctx->ectx.key->expressions; + } + + list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { + enum byteorder bo = BYTEORDER_INVALID; + unsigned dsize_bytes, dsize = 0; + + if (runaway) { + return expr_binary_error(ctx->msgs, *expr, key_ctx, + "too many concatenation components"); + } + + if (i->etype == EXPR_CT && + (i->ct.key == NFT_CT_SRC || + i->ct.key == NFT_CT_DST)) + return expr_error(ctx->msgs, i, + "specify either ip or ip6 for address matching"); + + if (expr_is_constant(*expr) && dtype && off == 0) + return expr_binary_error(ctx->msgs, i, *expr, + "unexpected concat component, " + "expecting %s", + dtype->desc); + + if (key) { + tmp = key->dtype; + dsize = key->len; + bo = key->byteorder; + off--; + } else if (dtype == NULL || off == 0) { + tmp = datatype_lookup(TYPE_INVALID); + } else { + tmp = concat_subtype_lookup(type, --off); + dsize = tmp->size; + bo = tmp->byteorder; + } + + __expr_set_context(&ctx->ectx, tmp, bo, dsize, 0); + + if (list_member_evaluate(ctx, &i) < 0) + return -1; + flags &= i->flags; + + if (!key && i->dtype->type == TYPE_INTEGER) { + struct datatype *clone; + + clone = datatype_clone(i->dtype); + clone->size = i->len; + clone->byteorder = i->byteorder; + __datatype_set(i, clone); + } + + if (dtype == NULL && i->dtype->size == 0) + return expr_binary_error(ctx->msgs, i, *expr, + "can not use variable sized " + "data types (%s) in concat " + "expressions", + i->dtype->name); + if (dsize == 0) /* reload after evaluation or clone above */ + dsize = i->dtype->size; + + ntype = concat_subtype_add(ntype, i->dtype->type); + + dsize_bytes = div_round_up(dsize, BITS_PER_BYTE); + (*expr)->field_len[(*expr)->field_count++] = dsize_bytes; + size += netlink_padded_len(dsize); + if (key && expressions) { + if (list_is_last(&key->list, expressions)) + runaway = true; + + key = list_next_entry(key, list); + } + + ctx->inner_desc = NULL; + } + + (*expr)->flags |= flags; + __datatype_set(*expr, concat_type_alloc(ntype)); + (*expr)->len = size; + + if (off > 0) + return expr_error(ctx->msgs, *expr, + "datatype mismatch, expected %s, " + "expression has type %s", + dtype->desc, (*expr)->dtype->desc); + + expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len); + if (!key_ctx) + ctx->ectx.key = *expr; + else + ctx->ectx.key = key_ctx; + + 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->etype != EXPR_VALUE) + return expr_error(ctx->msgs, i, + "List member must be a constant " + "value"); + if (i->dtype->basetype->type != TYPE_BITMASK) + return expr_error(ctx->msgs, i, + "Basetype of type %s is not bitmask", + i->dtype->desc); + 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_elem(struct eval_ctx *ctx, struct expr *elem) +{ + int num_elem_exprs = 0, num_set_exprs = 0; + struct set *set = ctx->set; + struct stmt *stmt; + + list_for_each_entry(stmt, &elem->stmt_list, list) + num_elem_exprs++; + list_for_each_entry(stmt, &set->stmt_list, list) + num_set_exprs++; + + if (num_elem_exprs > 0) { + struct stmt *set_stmt, *elem_stmt; + + if (num_set_exprs > 0 && num_elem_exprs != num_set_exprs) { + return expr_error(ctx->msgs, elem, + "number of statements mismatch, set expects %d " + "but element has %d", num_set_exprs, + num_elem_exprs); + } else if (num_set_exprs == 0) { + if (!(set->flags & NFT_SET_ANONYMOUS) && + !(set->flags & NFT_SET_EVAL)) { + elem_stmt = list_first_entry(&elem->stmt_list, struct stmt, list); + return stmt_error(ctx, elem_stmt, + "missing statement in %s declaration", + set_is_map(set->flags) ? "map" : "set"); + } + return 0; + } + + set_stmt = list_first_entry(&set->stmt_list, struct stmt, list); + + list_for_each_entry(elem_stmt, &elem->stmt_list, list) { + if (set_stmt->ops != elem_stmt->ops) { + return stmt_error(ctx, elem_stmt, + "statement mismatch, element expects %s, " + "but %s has type %s", + elem_stmt->ops->name, + set_is_map(set->flags) ? "map" : "set", + set_stmt->ops->name); + } + set_stmt = list_next_entry(set_stmt, list); + } + } + + return 0; +} + +static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *elem = *expr; + const struct expr *key; + + if (ctx->set) { + if (__expr_evaluate_set_elem(ctx, elem) < 0) + return -1; + + key = ctx->set->key; + __expr_set_context(&ctx->ectx, key->dtype, key->byteorder, key->len, 0); + ctx->ectx.key = key; + } + + if (expr_evaluate(ctx, &elem->key) < 0) + return -1; + + if (ctx->set && + !(ctx->set->flags & (NFT_SET_ANONYMOUS | NFT_SET_INTERVAL))) { + switch (elem->key->etype) { + case EXPR_PREFIX: + case EXPR_RANGE: + key = elem->key; + goto err_missing_flag; + case EXPR_CONCAT: + list_for_each_entry(key, &elem->key->expressions, list) { + switch (key->etype) { + case EXPR_PREFIX: + case EXPR_RANGE: + goto err_missing_flag; + default: + break; + } + } + break; + default: + break; + } + } + + datatype_set(elem, elem->key->dtype); + elem->len = elem->key->len; + elem->flags = elem->key->flags; + + return 0; + +err_missing_flag: + return expr_error(ctx->msgs, key, + "You must add 'flags interval' to your %s declaration if you want to add %s elements", + set_is_map(ctx->set->flags) ? "map" : "set", expr_name(key)); +} + +static const struct expr *expr_set_elem(const struct expr *expr) +{ + if (expr->etype == EXPR_MAPPING) + return expr->left; + + return expr; +} + +static int interval_set_eval(struct eval_ctx *ctx, struct set *set, + struct expr *init) +{ + int ret; + + if (!init) + return 0; + + ret = 0; + switch (ctx->cmd->op) { + case CMD_CREATE: + case CMD_ADD: + case CMD_INSERT: + if (set->automerge) { + ret = set_automerge(ctx->msgs, ctx->cmd, set, init, + ctx->nft->debug_mask); + } else { + ret = set_overlap(ctx->msgs, set, init); + } + break; + case CMD_DELETE: + case CMD_DESTROY: + ret = set_delete(ctx->msgs, ctx->cmd, set, init, + ctx->nft->debug_mask); + break; + case CMD_GET: + break; + default: + BUG("unhandled op %d\n", ctx->cmd->op); + break; + } + + return ret; +} + +static void expr_evaluate_set_ref(struct eval_ctx *ctx, struct expr *expr) +{ + struct set *set = expr->set; + + expr_set_context(&ctx->ectx, set->key->dtype, set->key->len); + ctx->ectx.key = set->key; +} + +static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *set = *expr, *i, *next; + const struct expr *elem; + + list_for_each_entry_safe(i, next, &set->expressions, list) { + if (list_member_evaluate(ctx, &i) < 0) + return -1; + + if (i->etype == EXPR_MAPPING && + i->left->etype == EXPR_SET_ELEM && + i->left->key->etype == EXPR_SET) { + struct expr *new, *j; + + list_for_each_entry(j, &i->left->key->expressions, list) { + new = mapping_expr_alloc(&i->location, + expr_get(j), + expr_get(i->right)); + list_add_tail(&new->list, &set->expressions); + set->size++; + } + list_del(&i->list); + expr_free(i); + continue; + } + + elem = expr_set_elem(i); + + if (elem->etype == EXPR_SET_ELEM && + elem->key->etype == EXPR_SET_REF) + return expr_error(ctx->msgs, i, + "Set reference cannot be part of another set"); + + if (elem->etype == EXPR_SET_ELEM && + elem->key->etype == EXPR_SET) { + struct expr *new = expr_get(elem->key); + + set->set_flags |= elem->key->set_flags; + list_replace(&i->list, &new->list); + expr_free(i); + i = new; + elem = expr_set_elem(i); + } + + if (!expr_is_constant(i)) + return expr_error(ctx->msgs, i, + "Set member is not constant"); + + if (i->etype == EXPR_SET) { + /* Merge recursive set definitions */ + list_splice_tail_init(&i->expressions, &i->list); + list_del(&i->list); + set->size += i->size - 1; + set->set_flags |= i->set_flags; + expr_free(i); + } else if (!expr_is_singleton(i)) { + set->set_flags |= NFT_SET_INTERVAL; + if (elem->key->etype == EXPR_CONCAT) + set->set_flags |= NFT_SET_CONCAT; + } + } + + if (ctx->set) { + if (ctx->set->flags & NFT_SET_CONCAT) + set->set_flags |= NFT_SET_CONCAT; + } + + set->set_flags |= NFT_SET_CONSTANT; + + datatype_set(set, ctx->ectx.dtype); + set->len = ctx->ectx.len; + set->flags |= EXPR_F_CONSTANT; + + return 0; +} + +static int binop_transfer(struct eval_ctx *ctx, struct expr **expr); + +static void map_set_concat_info(struct expr *map) +{ + map->mappings->set->flags |= map->mappings->set->init->set_flags; + + if (map->mappings->set->flags & NFT_SET_INTERVAL && + map->map->etype == EXPR_CONCAT) { + memcpy(&map->mappings->set->desc.field_len, &map->map->field_len, + sizeof(map->mappings->set->desc.field_len)); + map->mappings->set->desc.field_count = map->map->field_count; + map->mappings->flags |= NFT_SET_CONCAT; + } +} + +static void __mapping_expr_expand(struct expr *i) +{ + struct expr *j, *range, *next; + + assert(i->etype == EXPR_MAPPING); + switch (i->right->etype) { + case EXPR_VALUE: + range = range_expr_alloc(&i->location, expr_get(i->right), expr_get(i->right)); + expr_free(i->right); + i->right = range; + break; + case EXPR_CONCAT: + list_for_each_entry_safe(j, next, &i->right->expressions, list) { + if (j->etype != EXPR_VALUE) + continue; + + range = range_expr_alloc(&j->location, expr_get(j), expr_get(j)); + list_replace(&j->list, &range->list); + expr_free(j); + } + i->right->flags &= ~EXPR_F_SINGLETON; + break; + default: + break; + } +} + +static int mapping_expr_expand(struct eval_ctx *ctx) +{ + struct expr *i; + + if (!set_is_anonymous(ctx->set->flags)) + return 0; + + list_for_each_entry(i, &ctx->set->init->expressions, list) { + if (i->etype != EXPR_MAPPING) + return expr_error(ctx->msgs, i, + "expected mapping, not %s", expr_name(i)); + __mapping_expr_expand(i); + } + + return 0; +} + +static bool datatype_compatible(const struct datatype *a, const struct datatype *b) +{ + return (a->type == TYPE_MARK && + datatype_equal(datatype_basetype(a), datatype_basetype(b))) || + datatype_equal(a, b); +} + +static int expr_evaluate_map(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *map = *expr, *mappings; + struct expr_ctx ectx = ctx->ectx; + struct expr *key, *data; + + if (map->map->etype == EXPR_CT && + (map->map->ct.key == NFT_CT_SRC || + map->map->ct.key == NFT_CT_DST)) + return expr_error(ctx->msgs, map->map, + "specify either ip or ip6 for address matching"); + else if (map->map->etype == EXPR_CONCAT) { + struct expr *i; + + list_for_each_entry(i, &map->map->expressions, list) { + if (i->etype == EXPR_CT && + (i->ct.key == NFT_CT_SRC || + i->ct.key == NFT_CT_DST)) + return expr_error(ctx->msgs, i, + "specify either ip or ip6 for address matching"); + } + } + + expr_set_context(&ctx->ectx, NULL, 0); + ctx->stmt_len = 0; + if (expr_evaluate(ctx, &map->map) < 0) + return -1; + if (expr_is_constant(map->map)) + return expr_error(ctx->msgs, map->map, + "Map expression can not be constant"); + + mappings = map->mappings; + mappings->set_flags |= NFT_SET_MAP; + + switch (map->mappings->etype) { + case EXPR_SET: + if (ctx->ectx.key && ctx->ectx.key->etype == EXPR_CONCAT) { + key = expr_clone(ctx->ectx.key); + } else { + key = constant_expr_alloc(&map->location, + ctx->ectx.dtype, + ctx->ectx.byteorder, + ctx->ectx.len, NULL); + } + + if (ectx.dtype->type == TYPE_VERDICT) { + data = verdict_expr_alloc(&netlink_location, 0, NULL); + } else { + const struct datatype *dtype; + + dtype = set_datatype_alloc(ectx.dtype, ectx.byteorder); + data = constant_expr_alloc(&netlink_location, dtype, + dtype->byteorder, ectx.len, NULL); + datatype_free(dtype); + } + + mappings = implicit_set_declaration(ctx, "__map%d", + key, data, + mappings); + + if (ectx.len && mappings->set->data->len != ectx.len) + BUG("%d vs %d\n", mappings->set->data->len, ectx.len); + + map->mappings = mappings; + + ctx->set = mappings->set; + if (expr_evaluate(ctx, &map->mappings->set->init) < 0) + return -1; + + if (set_is_interval(map->mappings->set->init->set_flags) && + !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0) + return -1; + + expr_set_context(&ctx->ectx, ctx->set->key->dtype, ctx->set->key->len); + if (binop_transfer(ctx, expr) < 0) + return -1; + + if (ctx->set->data->flags & EXPR_F_INTERVAL) { + ctx->set->data->len *= 2; + + if (mapping_expr_expand(ctx)) + return -1; + } + + ctx->set->key->len = ctx->ectx.len; + ctx->set = NULL; + map = *expr; + + map_set_concat_info(map); + break; + case EXPR_SYMBOL: + if (expr_evaluate(ctx, &map->mappings) < 0) + return -1; + if (map->mappings->etype != EXPR_SET_REF || + !set_is_datamap(map->mappings->set->flags)) + return expr_error(ctx->msgs, map->mappings, + "Expression is not a map"); + break; + case EXPR_SET_REF: + /* symbol has been already evaluated to set reference */ + break; + default: + BUG("invalid mapping expression %s\n", + expr_name(map->mappings)); + } + + if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype)) + return expr_binary_error(ctx->msgs, map->mappings, map->map, + "datatype mismatch, map expects %s, " + "mapping expression has type %s", + map->mappings->set->key->dtype->desc, + map->map->dtype->desc); + + datatype_set(map, map->mappings->set->data->dtype); + map->flags |= EXPR_F_CONSTANT; + + /* Data for range lookups needs to be in big endian order */ + if (map->mappings->set->flags & NFT_SET_INTERVAL && + byteorder_conversion(ctx, &map->map, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + + return 0; +} + +static bool data_mapping_has_interval(struct expr *data) +{ + struct expr *i; + + if (data->etype == EXPR_RANGE || + data->etype == EXPR_PREFIX) + return true; + + if (data->etype != EXPR_CONCAT) + return false; + + list_for_each_entry(i, &data->expressions, list) { + if (i->etype == EXPR_RANGE || + i->etype == EXPR_PREFIX) + return true; + } + + return false; +} + +static int expr_evaluate_mapping(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *mapping = *expr; + struct set *set = ctx->set; + uint32_t datalen; + + if (set == NULL) + return expr_error(ctx->msgs, mapping, + "mapping outside of map context"); + if (!set_is_map(set->flags)) + return set_error(ctx, set, "set is not a map"); + + expr_set_context(&ctx->ectx, set->key->dtype, set->key->len); + if (expr_evaluate(ctx, &mapping->left) < 0) + return -1; + if (!expr_is_constant(mapping->left)) + return expr_error(ctx->msgs, mapping->left, + "Key must be a constant"); + mapping->flags |= mapping->left->flags & EXPR_F_SINGLETON; + + assert(set->data != NULL); + if (!set_is_anonymous(set->flags) && + set->data->flags & EXPR_F_INTERVAL) + datalen = set->data->len / 2; + else + datalen = set->data->len; + __expr_set_context(&ctx->ectx, set->data->dtype, + set->data->byteorder, datalen, 0); + + if (expr_evaluate(ctx, &mapping->right) < 0) + return -1; + if (!expr_is_constant(mapping->right)) + return expr_error(ctx->msgs, mapping->right, + "Value must be a constant"); + + if (set_is_anonymous(set->flags) && + data_mapping_has_interval(mapping->right)) + set->data->flags |= EXPR_F_INTERVAL; + + if (!set_is_anonymous(set->flags) && + set->data->flags & EXPR_F_INTERVAL) + __mapping_expr_expand(mapping); + + if (!(set->data->flags & EXPR_F_INTERVAL) && + !expr_is_singleton(mapping->right)) + return expr_error(ctx->msgs, mapping->right, + "Value must be a singleton"); + + mapping->flags |= EXPR_F_CONSTANT; + return 0; +} + +/* We got datatype context via statement. If the basetype is compatible, set + * this expression datatype to the one of the statement to make it datatype + * compatible. This is a more conservative approach than enabling datatype + * compatibility between two different datatypes whose basetype is the same, + * let's revisit this later once users come with valid usecases to generalize + * this. + */ +static void expr_dtype_integer_compatible(struct eval_ctx *ctx, + struct expr *expr) +{ + if (ctx->ectx.dtype && + ctx->ectx.dtype->basetype == &integer_type && + ctx->ectx.len == 4 * BITS_PER_BYTE) { + datatype_set(expr, ctx->ectx.dtype); + expr->len = ctx->ectx.len; + } +} + +static int expr_evaluate_numgen(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + unsigned int maxval; + + expr_dtype_integer_compatible(ctx, expr); + + maxval = expr->numgen.mod + expr->numgen.offset - 1; + __expr_set_context(&ctx->ectx, expr->dtype, expr->byteorder, expr->len, + maxval); + return 0; +} + +static int expr_evaluate_hash(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + unsigned int maxval; + + expr_dtype_integer_compatible(ctx, expr); + + expr_set_context(&ctx->ectx, NULL, 0); + if (expr->hash.expr && + expr_evaluate(ctx, &expr->hash.expr) < 0) + return -1; + + /* expr_evaluate_primary() sets the context to what to the input + * expression to be hashed. Since this input is transformed to a 4 bytes + * integer, restore context to the datatype that results from hashing. + */ + maxval = expr->hash.mod + expr->hash.offset - 1; + __expr_set_context(&ctx->ectx, expr->dtype, expr->byteorder, expr->len, + maxval); + + 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) +{ + int err; + + switch (right->etype) { + case EXPR_VALUE: + break; + case EXPR_SET_ELEM: + return binop_can_transfer(ctx, left, right->key); + case EXPR_RANGE: + err = binop_can_transfer(ctx, left, right->left); + if (err <= 0) + return err; + return binop_can_transfer(ctx, left, right->right); + case EXPR_MAPPING: + return binop_can_transfer(ctx, left, right->left); + default: + return 0; + } + + switch (left->op) { + case OP_LSHIFT: + if (mpz_scan1(right->value, 0) < mpz_get_uint32(left->right->value)) + return expr_binary_error(ctx->msgs, right, left, + "Comparison is always false"); + return 1; + case OP_RSHIFT: + if (ctx->ectx.len < right->len + mpz_get_uint32(left->right->value)) + ctx->ectx.len += mpz_get_uint32(left->right->value); + 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) +{ + int err; + + switch ((*right)->etype) { + case EXPR_MAPPING: + return binop_transfer_one(ctx, left, &(*right)->left); + case EXPR_VALUE: + break; + case EXPR_SET_ELEM: + return binop_transfer_one(ctx, left, &(*right)->key); + case EXPR_RANGE: + err = binop_transfer_one(ctx, left, &(*right)->left); + if (err < 0) + return err; + return binop_transfer_one(ctx, left, &(*right)->right); + default: + return 0; + } + + switch (left->op) { + case OP_LSHIFT: + (*right) = binop_expr_alloc(&(*right)->location, OP_RSHIFT, + *right, expr_get(left->right)); + break; + case OP_RSHIFT: + (*right) = binop_expr_alloc(&(*right)->location, OP_LSHIFT, + *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("invalid binary operation %u\n", left->op); + } + + return expr_evaluate(ctx, right); +} + +static void binop_transfer_handle_lhs(struct expr **expr) +{ + struct expr *tmp, *left = *expr; + unsigned int shift; + + assert(left->etype == EXPR_BINOP); + + switch (left->op) { + case OP_RSHIFT: + /* Mask out the bits the shift would have masked out */ + shift = mpz_get_uint8(left->right->value); + mpz_bitmask(left->right->value, left->left->len); + mpz_lshift_ui(left->right->value, shift); + left->op = OP_AND; + break; + case OP_LSHIFT: + case OP_XOR: + tmp = expr_get(left->left); + datatype_set(tmp, left->dtype); + expr_free(left); + *expr = tmp; + break; + default: + BUG("invalid binop operation %u", left->op); + } +} + +static int __binop_transfer(struct eval_ctx *ctx, + struct expr *left, struct expr **right) +{ + struct expr *i, *next; + int err; + + assert(left->etype == EXPR_BINOP); + + switch ((*right)->etype) { + case EXPR_VALUE: + err = binop_can_transfer(ctx, left, *right); + if (err <= 0) + return err; + if (binop_transfer_one(ctx, left, right) < 0) + return -1; + break; + case EXPR_RANGE: + err = binop_can_transfer(ctx, left, *right); + if (err <= 0) + return err; + if (binop_transfer_one(ctx, left, right) < 0) + return -1; + break; + case EXPR_SET: + list_for_each_entry(i, &(*right)->expressions, list) { + err = binop_can_transfer(ctx, left, i); + if (err <= 0) + return err; + } + list_for_each_entry_safe(i, next, &(*right)->expressions, list) { + list_del(&i->list); + err = binop_transfer_one(ctx, left, &i); + list_add_tail(&i->list, &next->list); + if (err < 0) + return err; + } + break; + case EXPR_SET_REF: + if (!set_is_anonymous((*right)->set->flags)) + return 0; + + return __binop_transfer(ctx, left, &(*right)->set->init); + default: + return 0; + } + + return 1; +} + +static int binop_transfer(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *left = (*expr)->left; + int ret; + + if (left->etype != EXPR_BINOP) + return 0; + + ret = __binop_transfer(ctx, left, &(*expr)->right); + if (ret <= 0) + return ret; + + binop_transfer_handle_lhs(&(*expr)->left); + return 0; +} + +static bool lhs_is_meta_hour(const struct expr *meta) +{ + if (meta->etype != EXPR_META) + return false; + + return meta->meta.key == NFT_META_TIME_HOUR || + meta->meta.key == NFT_META_TIME_DAY; +} + +static void swap_values(struct expr *range) +{ + struct expr *left_tmp; + + left_tmp = range->left; + range->left = range->right; + range->right = left_tmp; +} + +static bool range_needs_swap(const struct expr *range) +{ + const struct expr *right = range->right; + const struct expr *left = range->left; + + return mpz_cmp(left->value, right->value) > 0; +} + +static void optimize_singleton_set(struct expr *rel, struct expr **expr) +{ + struct expr *set = rel->right, *i; + + i = list_first_entry(&set->expressions, struct expr, list); + if (i->etype == EXPR_SET_ELEM && + list_empty(&i->stmt_list)) { + + switch (i->key->etype) { + case EXPR_PREFIX: + case EXPR_RANGE: + case EXPR_VALUE: + rel->right = *expr = i->key; + i->key = NULL; + expr_free(set); + break; + default: + break; + } + } + + if (rel->op == OP_IMPLICIT && + rel->right->dtype->basetype && + rel->right->dtype->basetype->type == TYPE_BITMASK && + rel->right->dtype->type != TYPE_CT_STATE) { + rel->op = OP_EQ; + } +} + +static int expr_evaluate_relational(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *rel = *expr, *left, *right; + struct proto_ctx *pctx; + struct expr *range; + int ret; + + right = rel->right; + if (right->etype == EXPR_SYMBOL && + right->symtype == SYMBOL_SET && + expr_evaluate(ctx, &rel->right) < 0) + return -1; + + if (expr_evaluate(ctx, &rel->left) < 0) + return -1; + left = rel->left; + + pctx = eval_proto_ctx(ctx); + + if (rel->right->etype == EXPR_RANGE && lhs_is_meta_hour(rel->left)) { + ret = __expr_evaluate_range(ctx, &rel->right); + if (ret) + return ret; + + range = rel->right; + + /* + * We may need to do this for proper cross-day ranges, + * e.g. meta hour 23:15-03:22 + */ + if (range_needs_swap(range)) { + if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) + nft_print(&ctx->nft->output, + "Inverting range values for cross-day hour matching\n\n"); + + if (rel->op == OP_EQ || rel->op == OP_IMPLICIT) { + swap_values(range); + rel->op = OP_NEQ; + } else if (rel->op == OP_NEQ) { + swap_values(range); + rel->op = OP_EQ; + } + } + } + + if (expr_evaluate(ctx, &rel->right) < 0) + return -1; + right = rel->right; + + if (!expr_is_constant(right)) + return expr_binary_error(ctx->msgs, 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->msgs, left, right, + "Relational expression (%s) has " + "constant value", + expr_op_symbols[rel->op]); + + if (!datatype_equal(left->dtype, right->dtype)) + return expr_binary_error(ctx->msgs, right, left, + "datatype mismatch, expected %s, " + "expression has type %s", + left->dtype->desc, + right->dtype->desc); + + /* + * Statements like 'ct secmark 12' are parsed as relational, + * disallow constant value on the right hand side. + */ + if (((left->etype == EXPR_META && + left->meta.key == NFT_META_SECMARK) || + (left->etype == EXPR_CT && + left->ct.key == NFT_CT_SECMARK)) && + right->flags & EXPR_F_CONSTANT) + return expr_binary_error(ctx->msgs, right, left, + "Cannot be used with right hand side constant value"); + + switch (rel->op) { + case OP_EQ: + case OP_IMPLICIT: + case OP_NEQ: + if (right->etype == EXPR_SET && right->size == 1) + optimize_singleton_set(rel, &right); + break; + default: + break; + } + + switch (rel->op) { + case OP_EQ: + case OP_IMPLICIT: + /* + * Update protocol context for payload and meta iiftype + * equality expressions. + */ + relational_expr_pctx_update(pctx, rel); + + /* fall through */ + case OP_NEQ: + case OP_NEG: + if (rel->op == OP_NEG) { + if (left->etype == EXPR_BINOP) + return expr_binary_error(ctx->msgs, left, right, + "cannot combine negation with binary expression"); + if (right->etype != EXPR_VALUE || + right->dtype->basetype == NULL || + right->dtype->basetype->type != TYPE_BITMASK) + return expr_binary_error(ctx->msgs, left, right, + "negation can only be used with singleton bitmask values. Did you mean \"!=\"?"); + } + + switch (right->etype) { + case EXPR_RANGE: + 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; + case EXPR_PREFIX: + if (byteorder_conversion(ctx, &right->prefix, left->byteorder) < 0) + return -1; + break; + case EXPR_VALUE: + if (byteorder_conversion(ctx, &rel->right, left->byteorder) < 0) + return -1; + break; + case EXPR_SET: + if (right->size == 0) + return expr_error(ctx->msgs, right, "Set is empty"); + + right = rel->right = + implicit_set_declaration(ctx, "__set%d", + expr_get(left), NULL, + right); + /* fall through */ + case EXPR_SET_REF: + if (rel->left->etype == EXPR_CT && + (rel->left->ct.key == NFT_CT_SRC || + rel->left->ct.key == NFT_CT_DST)) + return expr_error(ctx->msgs, left, + "specify either ip or ip6 for address matching"); + + /* Data for range lookups needs to be in big endian order */ + if (right->set->flags & NFT_SET_INTERVAL && + byteorder_conversion(ctx, &rel->left, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + break; + case EXPR_CONCAT: + return expr_binary_error(ctx->msgs, left, right, + "Use concatenations with sets and maps, not singleton values"); + break; + default: + BUG("invalid expression type %s\n", expr_name(right)); + } + break; + case OP_LT: + case OP_GT: + case OP_LTE: + case OP_GTE: + switch (left->etype) { + case EXPR_CONCAT: + return expr_binary_error(ctx->msgs, left, rel, + "Relational expression (%s) is undefined " + "for %s expressions", + expr_op_symbols[rel->op], + expr_name(left)); + default: + break; + } + + if (!expr_is_singleton(right)) + return expr_binary_error(ctx->msgs, right, rel, + "Relational expression (%s) is undefined " + "for %s expressions", + expr_op_symbols[rel->op], + expr_name(right)); + + 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; + default: + BUG("invalid relational operation %u\n", rel->op); + } + + if (binop_transfer(ctx, expr) < 0) + return -1; + + return 0; +} + +static int expr_evaluate_fib(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp; + + if (expr->flags & EXPR_F_BOOLEAN) { + expr->fib.flags |= NFTA_FIB_F_PRESENT; + datatype_set(expr, &boolean_type); + } + return expr_evaluate_primary(ctx, exprp); +} + +static int expr_evaluate_meta(struct eval_ctx *ctx, struct expr **exprp) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + struct expr *meta = *exprp; + + switch (meta->meta.key) { + case NFT_META_NFPROTO: + if (pctx->family != NFPROTO_INET && + meta->flags & EXPR_F_PROTOCOL) + return expr_error(ctx->msgs, meta, + "meta nfproto is only useful in the inet family"); + break; + case NFT_META_TIME_DAY: + __expr_set_context(&ctx->ectx, meta->dtype, meta->byteorder, + meta->len, 6); + return 0; + default: + break; + } + + return expr_evaluate_primary(ctx, exprp); +} + +static int expr_evaluate_socket(struct eval_ctx *ctx, struct expr **expr) +{ + enum nft_socket_keys key = (*expr)->socket.key; + int maxval = 0; + + if (key == NFT_SOCKET_TRANSPARENT || + key == NFT_SOCKET_WILDCARD) + maxval = 1; + __expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->byteorder, + (*expr)->len, maxval); + return 0; +} + +static int expr_evaluate_osf(struct eval_ctx *ctx, struct expr **expr) +{ + struct netlink_ctx nl_ctx = { + .nft = ctx->nft, + .seqnum = time(NULL), + }; + + nfnl_osf_load_fingerprints(&nl_ctx, 0); + + return expr_evaluate_primary(ctx, expr); +} + +static int expr_evaluate_variable(struct eval_ctx *ctx, struct expr **exprp) +{ + struct symbol *sym = (*exprp)->sym; + struct expr *new; + + /* If variable is reused from different locations in the ruleset, then + * clone expression. + */ + if (sym->refcnt > 2) + new = expr_clone(sym->expr); + else + new = expr_get(sym->expr); + + if (expr_evaluate(ctx, &new) < 0) { + expr_free(new); + return -1; + } + + expr_free(*exprp); + *exprp = new; + + return 0; +} + +static int expr_evaluate_xfrm(struct eval_ctx *ctx, struct expr **exprp) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + struct expr *expr = *exprp; + + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return expr_error(ctx->msgs, expr, "ipsec expression is only" + " valid in ip/ip6/inet tables"); + } + + return expr_evaluate_primary(ctx, exprp); +} + +static int expr_evaluate_flagcmp(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr = *exprp, *binop, *rel; + + if (expr->op != OP_EQ && + expr->op != OP_NEQ) + return expr_error(ctx->msgs, expr, "either == or != is allowed"); + + binop = binop_expr_alloc(&expr->location, OP_AND, + expr_get(expr->flagcmp.expr), + expr_get(expr->flagcmp.mask)); + rel = relational_expr_alloc(&expr->location, expr->op, binop, + expr_get(expr->flagcmp.value)); + expr_free(expr); + *exprp = rel; + + return expr_evaluate(ctx, exprp); +} + +static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) +{ + if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &(*expr)->location, + "Evaluate %s", expr_name(*expr)); + erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); + expr_print(*expr, &ctx->nft->output); + nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + + switch ((*expr)->etype) { + case EXPR_SYMBOL: + return expr_evaluate_symbol(ctx, expr); + case EXPR_VARIABLE: + return expr_evaluate_variable(ctx, expr); + case EXPR_SET_REF: + expr_evaluate_set_ref(ctx, *expr); + return 0; + case EXPR_VALUE: + return expr_evaluate_value(ctx, expr); + case EXPR_EXTHDR: + return expr_evaluate_exthdr(ctx, expr); + case EXPR_VERDICT: + return expr_evaluate_primary(ctx, expr); + case EXPR_META: + return expr_evaluate_meta(ctx, expr); + case EXPR_SOCKET: + return expr_evaluate_socket(ctx, expr); + case EXPR_OSF: + return expr_evaluate_osf(ctx, expr); + case EXPR_FIB: + return expr_evaluate_fib(ctx, expr); + case EXPR_PAYLOAD: + return expr_evaluate_payload_inner(ctx, expr); + case EXPR_RT: + return expr_evaluate_rt(ctx, expr); + case EXPR_CT: + return expr_evaluate_ct(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_SET_ELEM: + return expr_evaluate_set_elem(ctx, expr); + case EXPR_MAP: + return expr_evaluate_map(ctx, expr); + case EXPR_MAPPING: + return expr_evaluate_mapping(ctx, expr); + case EXPR_RELATIONAL: + return expr_evaluate_relational(ctx, expr); + case EXPR_NUMGEN: + return expr_evaluate_numgen(ctx, expr); + case EXPR_HASH: + return expr_evaluate_hash(ctx, expr); + case EXPR_XFRM: + return expr_evaluate_xfrm(ctx, expr); + case EXPR_SET_ELEM_CATCHALL: + return 0; + case EXPR_FLAGCMP: + return expr_evaluate_flagcmp(ctx, expr); + default: + BUG("unknown expression type %s\n", expr_name(*expr)); + } +} + +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_prefix_conversion(struct eval_ctx *ctx, struct expr **expr, + enum byteorder byteorder) +{ + struct expr *mask, *and, *or, *prefix, *base, *range; + int ret; + + prefix = *expr; + base = prefix->prefix; + + if (base->etype != EXPR_VALUE) + return expr_error(ctx->msgs, prefix, + "you cannot specify a prefix here, " + "unknown type %s", base->dtype->name); + + if (!expr_is_constant(base)) + return expr_error(ctx->msgs, prefix, + "Prefix expression is undefined for " + "non-constant expressions"); + + if (expr_basetype(base)->type != TYPE_INTEGER) + return expr_error(ctx->msgs, prefix, + "Prefix expression expected integer value"); + + mask = constant_expr_alloc(&prefix->location, expr_basetype(base), + BYTEORDER_HOST_ENDIAN, base->len, NULL); + + mpz_prefixmask(mask->value, base->len, prefix->prefix_len); + and = binop_expr_alloc(&prefix->location, OP_AND, expr_get(base), mask); + + mask = constant_expr_alloc(&prefix->location, expr_basetype(base), + BYTEORDER_HOST_ENDIAN, base->len, NULL); + mpz_bitmask(mask->value, prefix->len - prefix->prefix_len); + or = binop_expr_alloc(&prefix->location, OP_OR, expr_get(base), mask); + + range = range_expr_alloc(&prefix->location, and, or); + ret = expr_evaluate(ctx, &range); + if (ret < 0) { + expr_free(range); + return ret; + } + + expr_free(*expr); + *expr = range; + return 0; +} + +static int __stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt, + const struct datatype *dtype, unsigned int len, + enum byteorder byteorder, struct expr **expr) +{ + if ((*expr)->etype == EXPR_PAYLOAD && + (*expr)->dtype->type == TYPE_INTEGER && + ((*expr)->dtype->type != datatype_basetype(dtype)->type || + (*expr)->len != len)) + return stmt_binary_error(ctx, *expr, stmt, + "datatype mismatch: expected %s, " + "expression has type %s with length %d", + dtype->desc, (*expr)->dtype->desc, + (*expr)->len); + + if (!datatype_compatible(dtype, (*expr)->dtype)) + return stmt_binary_error(ctx, *expr, stmt, /* verdict vs invalid? */ + "datatype mismatch: expected %s, " + "expression has type %s", + dtype->desc, (*expr)->dtype->desc); + + if (dtype->type == TYPE_MARK && + datatype_equal(datatype_basetype(dtype), datatype_basetype((*expr)->dtype)) && + !expr_is_constant(*expr)) + return byteorder_conversion(ctx, expr, byteorder); + + /* we are setting a value, we can't use a set */ + switch ((*expr)->etype) { + case EXPR_SET: + return stmt_binary_error(ctx, *expr, stmt, + "you cannot use a set here, unknown " + "value to use"); + case EXPR_SET_REF: + return stmt_binary_error(ctx, *expr, stmt, + "you cannot reference a set here, " + "unknown value to use"); + case EXPR_RT: + return byteorder_conversion(ctx, expr, byteorder); + case EXPR_PREFIX: + return stmt_prefix_conversion(ctx, expr, byteorder); + case EXPR_NUMGEN: + if (dtype->type == TYPE_IPADDR) + return byteorder_conversion(ctx, expr, byteorder); + break; + default: + break; + } + + return 0; +} + +static int stmt_evaluate_arg(struct eval_ctx *ctx, struct stmt *stmt, + const struct datatype *dtype, unsigned int len, + enum byteorder byteorder, struct expr **expr) +{ + __expr_set_context(&ctx->ectx, dtype, byteorder, len, 0); + if (expr_evaluate(ctx, expr) < 0) + return -1; + + return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr); +} + +/* like stmt_evaluate_arg, but keep existing context created + * by previous expr_evaluate(). + * + * This is needed for add/update statements: + * ctx->ectx.key has the set key, which may be needed for 'typeof' + * sets: the 'add/update' expression might contain integer data types. + * + * Without the key we cannot derive the element size. + */ +static int stmt_evaluate_key(struct eval_ctx *ctx, struct stmt *stmt, + const struct datatype *dtype, unsigned int len, + enum byteorder byteorder, struct expr **expr) +{ + if (expr_evaluate(ctx, expr) < 0) + return -1; + + return __stmt_evaluate_arg(ctx, stmt, dtype, len, byteorder, expr); +} + +static int stmt_evaluate_verdict(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (stmt_evaluate_arg(ctx, stmt, &verdict_type, 0, 0, &stmt->expr) < 0) + return -1; + + switch (stmt->expr->etype) { + case EXPR_VERDICT: + if (stmt->expr->verdict != NFT_CONTINUE) + stmt->flags |= STMT_F_TERMINAL; + if (stmt->expr->chain != NULL) { + if (stmt_evaluate_arg(ctx, stmt, &string_type, 0, 0, + &stmt->expr->chain) < 0) + return -1; + if (stmt->expr->chain->etype != EXPR_VALUE) { + return expr_error(ctx->msgs, stmt->expr->chain, + "not a value expression"); + } + } + break; + case EXPR_MAP: + break; + default: + BUG("invalid verdict expression %s\n", expr_name(stmt->expr)); + } + return 0; +} + +static bool stmt_evaluate_payload_need_csum(const struct expr *payload) +{ + const struct proto_desc *desc; + + if (payload->payload.base == PROTO_BASE_INNER_HDR) + return true; + + desc = payload->payload.desc; + + return desc && desc->checksum_key; +} + +static int stmt_evaluate_exthdr(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *exthdr; + + if (__expr_evaluate_exthdr(ctx, &stmt->exthdr.expr) < 0) + return -1; + + exthdr = stmt->exthdr.expr; + return stmt_evaluate_arg(ctx, stmt, exthdr->dtype, exthdr->len, + BYTEORDER_BIG_ENDIAN, + &stmt->exthdr.val); +} + +static int stmt_evaluate_payload(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *mask, *and, *xor, *payload_bytes; + unsigned int masklen, extra_len = 0; + unsigned int payload_byte_size, payload_byte_offset; + uint8_t shift_imm, data[NFT_REG_SIZE]; + struct expr *payload; + mpz_t bitmask, ff; + bool need_csum; + + if (stmt->payload.expr->payload.inner_desc) { + return expr_error(ctx->msgs, stmt->payload.expr, + "payload statement for this expression is not supported"); + } + + if (__expr_evaluate_payload(ctx, stmt->payload.expr) < 0) + return -1; + + payload = stmt->payload.expr; + if (stmt_evaluate_arg(ctx, stmt, payload->dtype, payload->len, + payload->byteorder, &stmt->payload.val) < 0) + return -1; + + if (!expr_is_constant(stmt->payload.val) && + byteorder_conversion(ctx, &stmt->payload.val, + payload->byteorder) < 0) + return -1; + + need_csum = stmt_evaluate_payload_need_csum(payload); + + if (!payload_needs_adjustment(payload)) { + + /* We still need to munge the payload in case we have to + * update checksum and the length is not even because + * kernel checksum functions cannot deal with odd lengths. + */ + if (!need_csum || ((payload->len / BITS_PER_BYTE) & 1) == 0) + return 0; + } + + payload_byte_offset = payload->payload.offset / BITS_PER_BYTE; + + shift_imm = expr_offset_shift(payload, payload->payload.offset, + &extra_len); + payload_byte_size = div_round_up(payload->len + extra_len, + BITS_PER_BYTE); + + if (need_csum && payload_byte_size & 1) { + payload_byte_size++; + + if (payload_byte_offset & 1) { /* prefer 16bit aligned fetch */ + payload_byte_offset--; + assert(payload->payload.offset >= BITS_PER_BYTE); + } else { + shift_imm += BITS_PER_BYTE; + } + } + + if (shift_imm) { + struct expr *off, *lshift; + + off = constant_expr_alloc(&payload->location, + expr_basetype(payload), + BYTEORDER_HOST_ENDIAN, + sizeof(shift_imm), &shift_imm); + + lshift = binop_expr_alloc(&payload->location, OP_LSHIFT, + stmt->payload.val, off); + lshift->dtype = payload->dtype; + lshift->byteorder = payload->byteorder; + + stmt->payload.val = lshift; + } + + masklen = payload_byte_size * BITS_PER_BYTE; + mpz_init_bitmask(ff, masklen); + + mpz_init2(bitmask, masklen); + mpz_bitmask(bitmask, payload->len); + mpz_lshift_ui(bitmask, shift_imm); + + mpz_xor(bitmask, ff, bitmask); + mpz_clear(ff); + + assert(sizeof(data) * BITS_PER_BYTE >= masklen); + mpz_export_data(data, bitmask, payload->byteorder, payload_byte_size); + mask = constant_expr_alloc(&payload->location, expr_basetype(payload), + payload->byteorder, masklen, data); + mpz_clear(bitmask); + + payload_bytes = payload_expr_alloc(&payload->location, NULL, 0); + payload_init_raw(payload_bytes, payload->payload.base, + payload_byte_offset * BITS_PER_BYTE, + payload_byte_size * BITS_PER_BYTE); + + payload_bytes->payload.is_raw = 1; + payload_bytes->payload.desc = payload->payload.desc; + payload_bytes->byteorder = payload->byteorder; + + payload->len = payload_bytes->len; + payload->payload.offset = payload_bytes->payload.offset; + + and = binop_expr_alloc(&payload->location, OP_AND, payload_bytes, mask); + + and->dtype = payload_bytes->dtype; + and->byteorder = payload_bytes->byteorder; + and->len = payload_bytes->len; + + xor = binop_expr_alloc(&payload->location, OP_XOR, and, + stmt->payload.val); + xor->dtype = payload->dtype; + xor->byteorder = payload->byteorder; + xor->len = mask->len; + + stmt->payload.val = xor; + + return expr_evaluate(ctx, &stmt->payload.val); +} + +static int stmt_evaluate_meter(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *key, *set, *setref; + struct set *existing_set; + struct table *table; + + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + existing_set = set_cache_find(table, stmt->meter.name); + if (existing_set) + return cmd_error(ctx, &stmt->location, + "%s; meter ‘%s’ overlaps an existing %s ‘%s’ in family %s", + strerror(EEXIST), + stmt->meter.name, + set_is_map(existing_set->flags) ? "map" : "set", + existing_set->handle.set.name, + family2str(existing_set->handle.family)); + + expr_set_context(&ctx->ectx, NULL, 0); + if (expr_evaluate(ctx, &stmt->meter.key) < 0) + return -1; + if (expr_is_constant(stmt->meter.key)) + return expr_error(ctx->msgs, stmt->meter.key, + "Meter key expression can not be constant"); + if (stmt->meter.key->comment) + return expr_error(ctx->msgs, stmt->meter.key, + "Meter key expression can not contain comments"); + + /* Declare an empty set */ + key = stmt->meter.key; + set = set_expr_alloc(&key->location, NULL); + set->set_flags |= NFT_SET_EVAL; + if (key->timeout) + set->set_flags |= NFT_SET_TIMEOUT; + + setref = implicit_set_declaration(ctx, stmt->meter.name, + expr_get(key), NULL, set); + + setref->set->desc.size = stmt->meter.size; + stmt->meter.set = setref; + + if (stmt_evaluate(ctx, stmt->meter.stmt) < 0) + return -1; + if (!(stmt->meter.stmt->flags & STMT_F_STATEFUL)) + return stmt_binary_error(ctx, stmt->meter.stmt, stmt, + "meter statement must be stateful"); + + return 0; +} + +static int stmt_evaluate_meta(struct eval_ctx *ctx, struct stmt *stmt) +{ + int ret; + + ctx->stmt_len = stmt->meta.tmpl->len; + + ret = stmt_evaluate_arg(ctx, stmt, + stmt->meta.tmpl->dtype, + stmt->meta.tmpl->len, + stmt->meta.tmpl->byteorder, + &stmt->meta.expr); + ctx->stmt_len = 0; + + return ret; +} + +static int stmt_evaluate_ct(struct eval_ctx *ctx, struct stmt *stmt) +{ + int ret; + + ctx->stmt_len = stmt->ct.tmpl->len; + + ret = stmt_evaluate_arg(ctx, stmt, + stmt->ct.tmpl->dtype, + stmt->ct.tmpl->len, + stmt->ct.tmpl->byteorder, + &stmt->ct.expr); + ctx->stmt_len = 0; + + if (ret < 0) + return -1; + + if (stmt->ct.key == NFT_CT_SECMARK && expr_is_constant(stmt->ct.expr)) + return stmt_error(ctx, stmt, + "ct secmark must not be set to constant value"); + + return 0; +} + +static int reject_payload_gen_dependency_tcp(struct eval_ctx *ctx, + struct stmt *stmt, + struct expr **payload) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc; + + desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; + if (desc != NULL) + return 0; + *payload = payload_expr_alloc(&stmt->location, &proto_tcp, + TCPHDR_UNSPEC); + return 1; +} + +static int reject_payload_gen_dependency_family(struct eval_ctx *ctx, + struct stmt *stmt, + struct expr **payload) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *base; + + base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (base != NULL) + return 0; + + if (stmt->reject.icmp_code < 0) + return stmt_error(ctx, stmt, "missing icmp error type"); + + /* Generate a network dependency */ + switch (stmt->reject.family) { + case NFPROTO_IPV4: + *payload = payload_expr_alloc(&stmt->location, &proto_ip, + IPHDR_PROTOCOL); + break; + case NFPROTO_IPV6: + *payload = payload_expr_alloc(&stmt->location, &proto_ip6, + IP6HDR_NEXTHDR); + break; + default: + BUG("unknown reject family"); + } + return 1; +} + +static int stmt_reject_gen_dependency(struct eval_ctx *ctx, struct stmt *stmt, + struct expr *expr) +{ + struct expr *payload = NULL; + struct stmt *nstmt; + int ret; + + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + ret = reject_payload_gen_dependency_tcp(ctx, stmt, &payload); + break; + case NFT_REJECT_ICMP_UNREACH: + ret = reject_payload_gen_dependency_family(ctx, stmt, &payload); + break; + default: + BUG("cannot generate reject dependency for type %d", + stmt->reject.type); + } + if (ret <= 0) + return ret; + + if (payload_gen_dependency(ctx, payload, &nstmt) < 0) { + ret = -1; + goto out; + } + + /* + * Unlike payload deps this adds the dependency at the beginning, i.e. + * log ... reject with tcp-reset + * turns into + * meta l4proto tcp log ... reject with tcp-reset + * + * Otherwise we'd log things that won't be rejected. + */ + list_add(&nstmt->list, &ctx->rule->stmts); +out: + xfree(payload); + return ret; +} + +static int stmt_evaluate_reject_inet_family(struct eval_ctx *ctx, + struct stmt *stmt, + const struct proto_desc *desc) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *base; + int protocol; + + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + break; + case NFT_REJECT_ICMPX_UNREACH: + break; + case NFT_REJECT_ICMP_UNREACH: + base = pctx->protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case NFPROTO_IPV4: + case __constant_htons(ETH_P_IP): + if (stmt->reject.family == NFPROTO_IPV4) + break; + return stmt_binary_error(ctx, stmt->reject.expr, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: ip vs ip6"); + case NFPROTO_IPV6: + case __constant_htons(ETH_P_IPV6): + if (stmt->reject.family == NFPROTO_IPV6) + break; + return stmt_binary_error(ctx, stmt->reject.expr, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: ip vs ip6"); + default: + return stmt_error(ctx, stmt, + "cannot infer ICMP reject variant to use: explicit value required.\n"); + } + break; + } + + return 0; +} + +static int stmt_evaluate_reject_inet(struct eval_ctx *ctx, struct stmt *stmt, + struct expr *expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc; + + desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc != NULL && + stmt_evaluate_reject_inet_family(ctx, stmt, desc) < 0) + return -1; + if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) + return 0; + if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0) + return -1; + return 0; +} + +static int stmt_evaluate_reject_bridge_family(struct eval_ctx *ctx, + struct stmt *stmt, + const struct proto_desc *desc) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *base; + int protocol; + + switch (stmt->reject.type) { + case NFT_REJECT_ICMPX_UNREACH: + case NFT_REJECT_TCP_RST: + base = pctx->protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case __constant_htons(ETH_P_IP): + case __constant_htons(ETH_P_IPV6): + break; + default: + return stmt_binary_error(ctx, stmt, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "cannot reject this network family"); + } + break; + case NFT_REJECT_ICMP_UNREACH: + base = pctx->protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case __constant_htons(ETH_P_IP): + if (NFPROTO_IPV4 == stmt->reject.family) + break; + return stmt_binary_error(ctx, stmt->reject.expr, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: ip vs ip6"); + case __constant_htons(ETH_P_IPV6): + if (NFPROTO_IPV6 == stmt->reject.family) + break; + return stmt_binary_error(ctx, stmt->reject.expr, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: ip vs ip6"); + default: + return stmt_binary_error(ctx, stmt, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "cannot reject this network family"); + } + break; + } + + return 0; +} + +static int stmt_evaluate_reject_bridge(struct eval_ctx *ctx, struct stmt *stmt, + struct expr *expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc; + + desc = pctx->protocol[PROTO_BASE_LL_HDR].desc; + if (desc != &proto_eth && desc != &proto_vlan && desc != &proto_netdev) + return __stmt_binary_error(ctx, &stmt->location, NULL, + "cannot reject from this link layer protocol"); + + desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc != NULL && + stmt_evaluate_reject_bridge_family(ctx, stmt, desc) < 0) + return -1; + if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) + return 0; + if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0) + return -1; + return 0; +} + +static int stmt_evaluate_reject_family(struct eval_ctx *ctx, struct stmt *stmt, + struct expr *expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + + switch (pctx->family) { + case NFPROTO_ARP: + return stmt_error(ctx, stmt, "cannot use reject with arp"); + case NFPROTO_IPV4: + case NFPROTO_IPV6: + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + if (stmt_reject_gen_dependency(ctx, stmt, expr) < 0) + return -1; + break; + case NFT_REJECT_ICMPX_UNREACH: + return stmt_binary_error(ctx, stmt->reject.expr, stmt, + "abstracted ICMP unreachable not supported"); + case NFT_REJECT_ICMP_UNREACH: + if (stmt->reject.family == pctx->family) + break; + return stmt_binary_error(ctx, stmt->reject.expr, stmt, + "conflicting protocols specified: ip vs ip6"); + } + break; + case NFPROTO_BRIDGE: + case NFPROTO_NETDEV: + if (stmt_evaluate_reject_bridge(ctx, stmt, expr) < 0) + return -1; + break; + case NFPROTO_INET: + if (stmt_evaluate_reject_inet(ctx, stmt, expr) < 0) + return -1; + break; + } + + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate_reject_default(struct eval_ctx *ctx, + struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc, *base; + int protocol; + + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + stmt->reject.type = NFT_REJECT_ICMP_UNREACH; + stmt->reject.family = pctx->family; + if (pctx->family == NFPROTO_IPV4) + stmt->reject.icmp_code = ICMP_PORT_UNREACH; + else + stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT; + break; + case NFPROTO_INET: + desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc == NULL) { + stmt->reject.type = NFT_REJECT_ICMPX_UNREACH; + stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH; + break; + } + stmt->reject.type = NFT_REJECT_ICMP_UNREACH; + base = pctx->protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case NFPROTO_IPV4: + case __constant_htons(ETH_P_IP): + stmt->reject.family = NFPROTO_IPV4; + stmt->reject.icmp_code = ICMP_PORT_UNREACH; + break; + case NFPROTO_IPV6: + case __constant_htons(ETH_P_IPV6): + stmt->reject.family = NFPROTO_IPV6; + stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT; + break; + } + break; + case NFPROTO_BRIDGE: + case NFPROTO_NETDEV: + desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc == NULL) { + stmt->reject.type = NFT_REJECT_ICMPX_UNREACH; + stmt->reject.icmp_code = NFT_REJECT_ICMPX_PORT_UNREACH; + break; + } + stmt->reject.type = NFT_REJECT_ICMP_UNREACH; + base = pctx->protocol[PROTO_BASE_LL_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case __constant_htons(ETH_P_IP): + stmt->reject.family = NFPROTO_IPV4; + stmt->reject.icmp_code = ICMP_PORT_UNREACH; + break; + case __constant_htons(ETH_P_IPV6): + stmt->reject.family = NFPROTO_IPV6; + stmt->reject.icmp_code = ICMP6_DST_UNREACH_NOPORT; + break; + } + break; + } + return 0; +} + +static int stmt_evaluate_reject_icmp(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct parse_ctx parse_ctx = { + .tbl = &ctx->nft->output.tbl, + .input = &ctx->nft->input, + }; + struct error_record *erec; + struct expr *code; + + erec = symbol_parse(&parse_ctx, stmt->reject.expr, &code); + if (erec != NULL) { + erec_queue(erec, ctx->msgs); + return -1; + } + stmt->reject.icmp_code = mpz_get_uint8(code->value); + expr_free(code); + + return 0; +} + +static int stmt_evaluate_reset(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *desc, *base; + int protonum; + + desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; + if (desc == NULL) + return 0; + + base = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (base == NULL) + base = &proto_inet_service; + + protonum = proto_find_num(base, desc); + switch (protonum) { + case IPPROTO_TCP: + break; + default: + if (stmt->reject.type == NFT_REJECT_TCP_RST) { + return stmt_binary_error(ctx, stmt, + &pctx->protocol[PROTO_BASE_TRANSPORT_HDR], + "you cannot use tcp reset with this protocol"); + } + break; + } + return 0; +} + +static int stmt_evaluate_reject(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *expr = ctx->cmd->expr; + + if (stmt->reject.icmp_code < 0) { + if (stmt_evaluate_reject_default(ctx, stmt) < 0) + return -1; + } else if (stmt->reject.expr != NULL) { + if (stmt_evaluate_reject_icmp(ctx, stmt) < 0) + return -1; + } else { + if (stmt_evaluate_reset(ctx, stmt) < 0) + return -1; + } + + return stmt_evaluate_reject_family(ctx, stmt, expr); +} + +static int nat_evaluate_family(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *nproto; + + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (stmt->nat.family == NFPROTO_UNSPEC) + stmt->nat.family = pctx->family; + return 0; + case NFPROTO_INET: + if (!stmt->nat.addr) { + stmt->nat.family = NFPROTO_INET; + return 0; + } + if (stmt->nat.family != NFPROTO_UNSPEC) + return 0; + + nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + + if (nproto == &proto_ip) + stmt->nat.family = NFPROTO_IPV4; + else if (nproto == &proto_ip6) + stmt->nat.family = NFPROTO_IPV6; + + return 0; + default: + return stmt_error(ctx, stmt, + "NAT is only supported for IPv4/IPv6"); + } +} + +static const struct datatype *get_addr_dtype(uint8_t family) +{ + switch (family) { + case NFPROTO_IPV4: + return &ipaddr_type; + case NFPROTO_IPV6: + return &ip6addr_type; + } + + return &invalid_type; +} + +static int evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt, + struct expr **expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct datatype *dtype; + + dtype = get_addr_dtype(pctx->family); + + return stmt_evaluate_arg(ctx, stmt, dtype, dtype->size, + BYTEORDER_BIG_ENDIAN, + expr); +} + +static bool nat_evaluate_addr_has_th_expr(const struct expr *map) +{ + const struct expr *i, *concat; + + if (!map || map->etype != EXPR_MAP) + return false; + + concat = map->map; + if (concat ->etype != EXPR_CONCAT) + return false; + + list_for_each_entry(i, &concat->expressions, list) { + enum proto_bases base; + + if (i->etype == EXPR_PAYLOAD && + i->payload.base == PROTO_BASE_TRANSPORT_HDR && + i->payload.desc != &proto_th) + return true; + + if ((i->flags & EXPR_F_PROTOCOL) == 0) + continue; + + switch (i->etype) { + case EXPR_META: + base = i->meta.base; + break; + case EXPR_PAYLOAD: + base = i->payload.base; + break; + default: + return false; + } + + if (base == PROTO_BASE_NETWORK_HDR) + return true; + } + + return false; +} + +static int nat_evaluate_transport(struct eval_ctx *ctx, struct stmt *stmt, + struct expr **expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + int err; + + err = stmt_evaluate_arg(ctx, stmt, + &inet_service_type, 2 * BITS_PER_BYTE, + BYTEORDER_BIG_ENDIAN, expr); + if (err < 0) + return err; + + if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL && + !nat_evaluate_addr_has_th_expr(stmt->nat.addr)) + return stmt_binary_error(ctx, *expr, stmt, + "transport protocol mapping is only " + "valid after transport protocol match"); + + return 0; +} + +static const char *stmt_name(const struct stmt *stmt) +{ + switch (stmt->ops->type) { + case STMT_NAT: + switch (stmt->nat.type) { + case NFT_NAT_SNAT: + return "snat"; + case NFT_NAT_DNAT: + return "dnat"; + case NFT_NAT_REDIR: + return "redirect"; + case NFT_NAT_MASQ: + return "masquerade"; + } + break; + default: + break; + } + + return stmt->ops->name; +} + +static int stmt_evaluate_l3proto(struct eval_ctx *ctx, + struct stmt *stmt, uint8_t family) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_desc *nproto; + + nproto = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + + if ((nproto == &proto_ip && family != NFPROTO_IPV4) || + (nproto == &proto_ip6 && family != NFPROTO_IPV6)) + return stmt_binary_error(ctx, stmt, + &pctx->protocol[PROTO_BASE_NETWORK_HDR], + "conflicting protocols specified: %s vs. %s. You must specify ip or ip6 family in %s statement", + pctx->protocol[PROTO_BASE_NETWORK_HDR].desc->name, + family2str(family), + stmt->ops->name); + return 0; +} + +static void expr_family_infer(struct proto_ctx *pctx, const struct expr *expr, + uint8_t *family) +{ + struct expr *i; + + if (expr->etype == EXPR_MAP) { + switch (expr->map->etype) { + case EXPR_CONCAT: + list_for_each_entry(i, &expr->map->expressions, list) { + if (i->etype == EXPR_PAYLOAD) { + if (i->payload.desc == &proto_ip) + *family = NFPROTO_IPV4; + else if (i->payload.desc == &proto_ip6) + *family = NFPROTO_IPV6; + } + } + break; + case EXPR_PAYLOAD: + if (expr->map->payload.desc == &proto_ip) + *family = NFPROTO_IPV4; + else if (expr->map->payload.desc == &proto_ip6) + *family = NFPROTO_IPV6; + break; + default: + break; + } + } +} + +static int stmt_evaluate_addr(struct eval_ctx *ctx, struct stmt *stmt, + uint8_t *family, struct expr **addr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct datatype *dtype; + int err; + + if (pctx->family == NFPROTO_INET) { + if (*family == NFPROTO_INET || + *family == NFPROTO_UNSPEC) + expr_family_infer(pctx, *addr, family); + + dtype = get_addr_dtype(*family); + if (dtype->size == 0) { + return stmt_error(ctx, stmt, + "specify `%s ip' or '%s ip6' in %s table to disambiguate", + stmt_name(stmt), stmt_name(stmt), family2str(pctx->family)); + } + + err = stmt_evaluate_arg(ctx, stmt, dtype, dtype->size, + BYTEORDER_BIG_ENDIAN, addr); + } else { + err = evaluate_addr(ctx, stmt, addr); + } + + return err; +} + +static int stmt_evaluate_nat_map(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + struct expr *one, *two, *data, *tmp; + const struct datatype *dtype = NULL; + const struct datatype *dtype2; + int addr_type; + int err; + + if (stmt->nat.family == NFPROTO_INET) + expr_family_infer(pctx, stmt->nat.addr, &stmt->nat.family); + + switch (stmt->nat.family) { + case NFPROTO_IPV4: + addr_type = TYPE_IPADDR; + break; + case NFPROTO_IPV6: + addr_type = TYPE_IP6ADDR; + break; + default: + return stmt_error(ctx, stmt, + "specify `%s ip' or '%s ip6' in %s table to disambiguate", + stmt_name(stmt), stmt_name(stmt), family2str(pctx->family)); + } + dtype = concat_type_alloc((addr_type << TYPE_BITS) | TYPE_INET_SERVICE); + + expr_set_context(&ctx->ectx, dtype, dtype->size); + if (expr_evaluate(ctx, &stmt->nat.addr)) { + err = -1; + goto out; + } + + if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL && + !nat_evaluate_addr_has_th_expr(stmt->nat.addr)) { + err = stmt_binary_error(ctx, stmt->nat.addr, stmt, + "transport protocol mapping is only " + "valid after transport protocol match"); + goto out; + } + + if (stmt->nat.addr->etype != EXPR_MAP) { + err = 0; + goto out; + } + + data = stmt->nat.addr->mappings->set->data; + if (data->flags & EXPR_F_INTERVAL) + stmt->nat.type_flags |= STMT_NAT_F_INTERVAL; + + datatype_set(data, dtype); + + if (expr_ops(data)->type != EXPR_CONCAT) { + err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size, + BYTEORDER_BIG_ENDIAN, + &stmt->nat.addr); + goto out; + } + + one = list_first_entry(&data->expressions, struct expr, list); + two = list_entry(one->list.next, struct expr, list); + + if (one == two || !list_is_last(&two->list, &data->expressions)) { + err = __stmt_evaluate_arg(ctx, stmt, dtype, dtype->size, + BYTEORDER_BIG_ENDIAN, + &stmt->nat.addr); + goto out; + } + + dtype2 = get_addr_dtype(stmt->nat.family); + tmp = one; + err = __stmt_evaluate_arg(ctx, stmt, dtype2, dtype2->size, + BYTEORDER_BIG_ENDIAN, + &tmp); + if (err < 0) + goto out; + if (tmp != one) + BUG("Internal error: Unexpected alteration of l3 expression"); + + tmp = two; + err = nat_evaluate_transport(ctx, stmt, &tmp); + if (err < 0) + goto out; + if (tmp != two) + BUG("Internal error: Unexpected alteration of l4 expression"); + +out: + datatype_free(dtype); + return err; +} + +static bool nat_concat_map(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *i; + + if (stmt->nat.addr->etype != EXPR_MAP) + return false; + + switch (stmt->nat.addr->mappings->etype) { + case EXPR_SET: + list_for_each_entry(i, &stmt->nat.addr->mappings->expressions, list) { + if (i->etype == EXPR_MAPPING && + i->right->etype == EXPR_CONCAT) { + stmt->nat.type_flags |= STMT_NAT_F_CONCAT; + return true; + } + } + break; + case EXPR_SYMBOL: + /* expr_evaluate_map() see EXPR_SET_REF after this evaluation. */ + if (expr_evaluate(ctx, &stmt->nat.addr->mappings)) + return false; + + if (stmt->nat.addr->mappings->set->data->etype == EXPR_CONCAT || + stmt->nat.addr->mappings->set->data->dtype->subtypes) { + stmt->nat.type_flags |= STMT_NAT_F_CONCAT; + return true; + } + break; + default: + break; + } + + return false; +} + +static int stmt_evaluate_nat(struct eval_ctx *ctx, struct stmt *stmt) +{ + int err; + + err = nat_evaluate_family(ctx, stmt); + if (err < 0) + return err; + + if (stmt->nat.addr != NULL) { + err = stmt_evaluate_l3proto(ctx, stmt, stmt->nat.family); + if (err < 0) + return err; + + if (nat_concat_map(ctx, stmt) || + stmt->nat.type_flags & STMT_NAT_F_CONCAT) { + + err = stmt_evaluate_nat_map(ctx, stmt); + if (err < 0) + return err; + + stmt->flags |= STMT_F_TERMINAL; + return 0; + } + + err = stmt_evaluate_addr(ctx, stmt, &stmt->nat.family, + &stmt->nat.addr); + if (err < 0) + return err; + } + + if (stmt->nat.proto != NULL) { + err = nat_evaluate_transport(ctx, stmt, &stmt->nat.proto); + if (err < 0) + return err; + + stmt->nat.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate_tproxy(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + int err; + + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: /* fallthrough */ + if (stmt->tproxy.family == NFPROTO_UNSPEC) + stmt->tproxy.family = pctx->family; + break; + case NFPROTO_INET: + break; + default: + return stmt_error(ctx, stmt, + "tproxy is only supported for IPv4/IPv6/INET"); + } + + if (pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc == NULL) + return stmt_error(ctx, stmt, "Transparent proxy support requires" + " transport protocol match"); + + if (!stmt->tproxy.addr && !stmt->tproxy.port) + return stmt_error(ctx, stmt, "Either address or port must be specified!"); + + err = stmt_evaluate_l3proto(ctx, stmt, stmt->tproxy.family); + if (err < 0) + return err; + + if (stmt->tproxy.addr != NULL) { + if (stmt->tproxy.addr->etype == EXPR_RANGE) + return stmt_error(ctx, stmt, "Address ranges are not supported for tproxy."); + + err = stmt_evaluate_addr(ctx, stmt, &stmt->tproxy.family, + &stmt->tproxy.addr); + + if (err < 0) + return err; + } + + if (stmt->tproxy.port != NULL) { + if (stmt->tproxy.port->etype == EXPR_RANGE) + return stmt_error(ctx, stmt, "Port ranges are not supported for tproxy."); + err = nat_evaluate_transport(ctx, stmt, &stmt->tproxy.port); + if (err < 0) + return err; + } + + return 0; +} + +static int stmt_evaluate_synproxy(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (stmt->synproxy.flags != 0 && + !(stmt->synproxy.flags & (NF_SYNPROXY_OPT_MSS | + NF_SYNPROXY_OPT_WSCALE | + NF_SYNPROXY_OPT_TIMESTAMP | + NF_SYNPROXY_OPT_SACK_PERM))) + return stmt_error(ctx, stmt, "This flags are not supported for SYNPROXY"); + + return 0; +} + +static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule, + enum cmd_ops op); + +static int stmt_evaluate_chain(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct chain *chain = stmt->chain.chain; + struct cmd *cmd; + + chain->flags |= CHAIN_F_BINDING; + + if (ctx->table != NULL) { + list_add_tail(&chain->list, &ctx->table->chains); + } else { + struct rule *rule, *next; + struct handle h; + + memset(&h, 0, sizeof(h)); + handle_merge(&h, &chain->handle); + h.family = ctx->rule->handle.family; + xfree(h.table.name); + h.table.name = xstrdup(ctx->rule->handle.table.name); + h.chain.location = stmt->location; + h.chain_id = chain->handle.chain_id; + + cmd = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &h, &stmt->location, + chain); + cmd->location = stmt->location; + list_add_tail(&cmd->list, &ctx->cmd->list); + h.chain_id = chain->handle.chain_id; + + list_for_each_entry_safe(rule, next, &chain->rules, list) { + struct eval_ctx rule_ctx = { + .nft = ctx->nft, + .msgs = ctx->msgs, + .cmd = ctx->cmd, + }; + struct handle h2 = {}; + + handle_merge(&rule->handle, &ctx->rule->handle); + xfree(rule->handle.table.name); + rule->handle.table.name = xstrdup(ctx->rule->handle.table.name); + xfree(rule->handle.chain.name); + rule->handle.chain.name = NULL; + rule->handle.chain_id = chain->handle.chain_id; + if (rule_evaluate(&rule_ctx, rule, CMD_INVALID) < 0) + return -1; + + handle_merge(&h2, &rule->handle); + cmd = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &h2, + &rule->location, rule); + list_add_tail(&cmd->list, &ctx->cmd->list); + list_del(&rule->list); + } + } + + return 0; +} + +static int stmt_evaluate_optstrip(struct eval_ctx *ctx, struct stmt *stmt) +{ + return expr_evaluate(ctx, &stmt->optstrip.expr); +} + +static int stmt_evaluate_dup(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + int err; + + switch (pctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (stmt->dup.to == NULL) + return stmt_error(ctx, stmt, + "missing destination address"); + err = evaluate_addr(ctx, stmt, &stmt->dup.to); + if (err < 0) + return err; + + if (stmt->dup.dev != NULL) { + err = stmt_evaluate_arg(ctx, stmt, &ifindex_type, + sizeof(uint32_t) * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, + &stmt->dup.dev); + if (err < 0) + return err; + } + break; + case NFPROTO_NETDEV: + if (stmt->dup.to == NULL) + return stmt_error(ctx, stmt, + "missing destination interface"); + if (stmt->dup.dev != NULL) + return stmt_error(ctx, stmt, "cannot specify device"); + + err = stmt_evaluate_arg(ctx, stmt, &ifindex_type, + sizeof(uint32_t) * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, &stmt->dup.to); + if (err < 0) + return err; + break; + default: + return stmt_error(ctx, stmt, "unsupported family"); + } + return 0; +} + +static int stmt_evaluate_fwd(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct datatype *dtype; + int err, len; + + switch (pctx->family) { + case NFPROTO_NETDEV: + if (stmt->fwd.dev == NULL) + return stmt_error(ctx, stmt, + "missing destination interface"); + + err = stmt_evaluate_arg(ctx, stmt, &ifindex_type, + sizeof(uint32_t) * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, &stmt->fwd.dev); + if (err < 0) + return err; + + if (stmt->fwd.addr != NULL) { + switch (stmt->fwd.family) { + case NFPROTO_IPV4: + dtype = &ipaddr_type; + len = 4 * BITS_PER_BYTE; + break; + case NFPROTO_IPV6: + dtype = &ip6addr_type; + len = 16 * BITS_PER_BYTE; + break; + default: + return stmt_error(ctx, stmt, "missing family"); + } + err = stmt_evaluate_arg(ctx, stmt, dtype, len, + BYTEORDER_BIG_ENDIAN, + &stmt->fwd.addr); + if (err < 0) + return err; + } + break; + default: + return stmt_error(ctx, stmt, "unsupported family"); + } + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate_queue(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (stmt->queue.queue != NULL) { + if (stmt_evaluate_arg(ctx, stmt, &integer_type, 16, + BYTEORDER_HOST_ENDIAN, + &stmt->queue.queue) < 0) + return -1; + + if ((stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) && + stmt->queue.queue->etype != EXPR_RANGE) + return expr_error(ctx->msgs, stmt->queue.queue, + "fanout requires a range to be " + "specified"); + + if (ctx->ectx.maxval > USHRT_MAX) + return expr_error(ctx->msgs, stmt->queue.queue, + "queue expression max value exceeds %u", USHRT_MAX); + } + stmt->flags |= STMT_F_TERMINAL; + return 0; +} + +static int stmt_evaluate_log_prefix(struct eval_ctx *ctx, struct stmt *stmt) +{ + char tmp[NF_LOG_PREFIXLEN] = {}; + char prefix[NF_LOG_PREFIXLEN]; + size_t len = sizeof(prefix); + size_t offset = 0; + struct expr *expr; + + if (stmt->log.prefix->etype != EXPR_LIST) { + if (stmt->log.prefix && + div_round_up(stmt->log.prefix->len, BITS_PER_BYTE) >= NF_LOG_PREFIXLEN) + return expr_error(ctx->msgs, stmt->log.prefix, "log prefix is too long"); + + return 0; + } + + prefix[0] = '\0'; + + list_for_each_entry(expr, &stmt->log.prefix->expressions, list) { + int ret; + + switch (expr->etype) { + case EXPR_VALUE: + expr_to_string(expr, tmp); + ret = snprintf(prefix + offset, len, "%s", tmp); + break; + case EXPR_VARIABLE: + ret = snprintf(prefix + offset, len, "%s", + expr->sym->expr->identifier); + break; + default: + BUG("unknown expression type %s\n", expr_name(expr)); + break; + } + SNPRINTF_BUFFER_SIZE(ret, &len, &offset); + } + + if (len == 0) + return stmt_error(ctx, stmt, "log prefix is too long"); + + expr = constant_expr_alloc(&stmt->log.prefix->location, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(prefix) * BITS_PER_BYTE, prefix); + expr_free(stmt->log.prefix); + stmt->log.prefix = expr; + + return 0; +} + +static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt) +{ + int ret = 0; + + if (stmt->log.flags & (STMT_LOG_GROUP | STMT_LOG_SNAPLEN | + STMT_LOG_QTHRESHOLD)) { + if (stmt->log.flags & STMT_LOG_LEVEL) + return stmt_error(ctx, stmt, + "level and group are mutually exclusive"); + if (stmt->log.logflags) + return stmt_error(ctx, stmt, + "flags and group are mutually exclusive"); + } + if (stmt->log.level == NFT_LOGLEVEL_AUDIT && + (stmt->log.flags & ~STMT_LOG_LEVEL || stmt->log.logflags)) + return stmt_error(ctx, stmt, + "log level audit doesn't support any further options"); + + if (stmt->log.prefix) + ret = stmt_evaluate_log_prefix(ctx, stmt); + + return ret; +} + +static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct set *this_set; + struct stmt *this; + + expr_set_context(&ctx->ectx, NULL, 0); + if (expr_evaluate(ctx, &stmt->set.set) < 0) + return -1; + if (stmt->set.set->etype != EXPR_SET_REF) + return expr_error(ctx->msgs, stmt->set.set, + "Expression does not refer to a set"); + + if (stmt_evaluate_key(ctx, stmt, + stmt->set.set->set->key->dtype, + stmt->set.set->set->key->len, + stmt->set.set->set->key->byteorder, + &stmt->set.key->key) < 0) + return -1; + if (expr_is_constant(stmt->set.key)) + return expr_error(ctx->msgs, stmt->set.key, + "Key expression can not be constant"); + if (stmt->set.key->comment != NULL) + return expr_error(ctx->msgs, stmt->set.key, + "Key expression comments are not supported"); + list_for_each_entry(this, &stmt->set.stmt_list, list) { + if (stmt_evaluate(ctx, this) < 0) + return -1; + if (!(this->flags & STMT_F_STATEFUL)) + return stmt_error(ctx, this, + "statement must be stateful"); + } + + this_set = stmt->set.set->set; + + /* Make sure EVAL flag is set on set definition so that kernel + * picks a set that allows updates from the packet path. + * + * Alternatively we could error out in case 'flags dynamic' was + * not given, but we can repair this here. + */ + this_set->flags |= NFT_SET_EVAL; + return 0; +} + +static int stmt_evaluate_map(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct stmt *this; + + expr_set_context(&ctx->ectx, NULL, 0); + if (expr_evaluate(ctx, &stmt->map.set) < 0) + return -1; + if (stmt->map.set->etype != EXPR_SET_REF) + return expr_error(ctx->msgs, stmt->map.set, + "Expression does not refer to a set"); + + if (stmt_evaluate_key(ctx, stmt, + stmt->map.set->set->key->dtype, + stmt->map.set->set->key->len, + stmt->map.set->set->key->byteorder, + &stmt->map.key->key) < 0) + return -1; + if (expr_is_constant(stmt->map.key)) + return expr_error(ctx->msgs, stmt->map.key, + "Key expression can not be constant"); + if (stmt->map.key->comment != NULL) + return expr_error(ctx->msgs, stmt->map.key, + "Key expression comments are not supported"); + + if (stmt_evaluate_arg(ctx, stmt, + stmt->map.set->set->data->dtype, + stmt->map.set->set->data->len, + stmt->map.set->set->data->byteorder, + &stmt->map.data->key) < 0) + return -1; + if (expr_is_constant(stmt->map.data)) + return expr_error(ctx->msgs, stmt->map.data, + "Data expression can not be constant"); + if (stmt->map.data->comment != NULL) + return expr_error(ctx->msgs, stmt->map.data, + "Data expression comments are not supported"); + if (stmt->map.data->timeout > 0) + return expr_error(ctx->msgs, stmt->map.data, + "Data expression timeouts are not supported"); + + list_for_each_entry(this, &stmt->map.stmt_list, list) { + if (stmt_evaluate(ctx, this) < 0) + return -1; + if (!(this->flags & STMT_F_STATEFUL)) + return stmt_error(ctx, this, + "statement must be stateful"); + } + + return 0; +} + +static int stmt_evaluate_objref_map(struct eval_ctx *ctx, struct stmt *stmt) +{ + struct expr *map = stmt->objref.expr; + struct expr *mappings; + struct expr *key; + + expr_set_context(&ctx->ectx, NULL, 0); + if (expr_evaluate(ctx, &map->map) < 0) + return -1; + if (expr_is_constant(map->map)) + return expr_error(ctx->msgs, map->map, + "Map expression can not be constant"); + + mappings = map->mappings; + mappings->set_flags |= NFT_SET_OBJECT; + + switch (map->mappings->etype) { + case EXPR_SET: + key = constant_expr_alloc(&stmt->location, + ctx->ectx.dtype, + ctx->ectx.byteorder, + ctx->ectx.len, NULL); + + mappings = implicit_set_declaration(ctx, "__objmap%d", + key, NULL, mappings); + mappings->set->objtype = stmt->objref.type; + + map->mappings = mappings; + + ctx->set = mappings->set; + if (expr_evaluate(ctx, &map->mappings->set->init) < 0) + return -1; + + if (set_is_interval(map->mappings->set->init->set_flags) && + !(map->mappings->set->init->set_flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, ctx->set, map->mappings->set->init) < 0) + return -1; + + ctx->set = NULL; + + map_set_concat_info(map); + /* fall through */ + case EXPR_SYMBOL: + if (expr_evaluate(ctx, &map->mappings) < 0) + return -1; + if (map->mappings->etype != EXPR_SET_REF) + return expr_error(ctx->msgs, map->mappings, + "Expression is not a map"); + if (!set_is_objmap(map->mappings->set->flags)) + return expr_error(ctx->msgs, map->mappings, + "Expression is not a map with objects"); + break; + default: + BUG("invalid mapping expression %s\n", + expr_name(map->mappings)); + } + + if (!datatype_compatible(map->mappings->set->key->dtype, map->map->dtype)) + return expr_binary_error(ctx->msgs, map->mappings, map->map, + "datatype mismatch, map expects %s, " + "mapping expression has type %s", + map->mappings->set->key->dtype->desc, + map->map->dtype->desc); + + datatype_set(map, map->mappings->set->data->dtype); + map->flags |= EXPR_F_CONSTANT; + + /* Data for range lookups needs to be in big endian order */ + if (map->mappings->set->flags & NFT_SET_INTERVAL && + byteorder_conversion(ctx, &map->map, BYTEORDER_BIG_ENDIAN) < 0) + return -1; + + return 0; +} + +static int stmt_evaluate_objref(struct eval_ctx *ctx, struct stmt *stmt) +{ + /* We need specific map evaluation for stateful objects. */ + if (stmt->objref.expr->etype == EXPR_MAP) + return stmt_evaluate_objref_map(ctx, stmt); + + if (stmt_evaluate_arg(ctx, stmt, + &string_type, NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, &stmt->objref.expr) < 0) + return -1; + + if (!expr_is_constant(stmt->objref.expr)) + return expr_error(ctx->msgs, stmt->objref.expr, + "Counter expression must be constant"); + + return 0; +} + +int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) +{ + if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + erec = erec_create(EREC_INFORMATIONAL, &stmt->location, + "Evaluate %s", stmt->ops->name); + erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); + stmt_print(stmt, &ctx->nft->output); + nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + + switch (stmt->ops->type) { + case STMT_CONNLIMIT: + case STMT_COUNTER: + case STMT_LAST: + case STMT_LIMIT: + case STMT_QUOTA: + case STMT_NOTRACK: + case STMT_FLOW_OFFLOAD: + return 0; + case STMT_EXPRESSION: + return stmt_evaluate_expr(ctx, stmt); + case STMT_VERDICT: + return stmt_evaluate_verdict(ctx, stmt); + case STMT_PAYLOAD: + return stmt_evaluate_payload(ctx, stmt); + case STMT_EXTHDR: + return stmt_evaluate_exthdr(ctx, stmt); + case STMT_METER: + return stmt_evaluate_meter(ctx, stmt); + case STMT_META: + return stmt_evaluate_meta(ctx, stmt); + case STMT_CT: + return stmt_evaluate_ct(ctx, stmt); + case STMT_LOG: + return stmt_evaluate_log(ctx, stmt); + case STMT_REJECT: + return stmt_evaluate_reject(ctx, stmt); + case STMT_NAT: + return stmt_evaluate_nat(ctx, stmt); + case STMT_TPROXY: + return stmt_evaluate_tproxy(ctx, stmt); + case STMT_QUEUE: + return stmt_evaluate_queue(ctx, stmt); + case STMT_DUP: + return stmt_evaluate_dup(ctx, stmt); + case STMT_FWD: + return stmt_evaluate_fwd(ctx, stmt); + case STMT_SET: + return stmt_evaluate_set(ctx, stmt); + case STMT_OBJREF: + return stmt_evaluate_objref(ctx, stmt); + case STMT_MAP: + return stmt_evaluate_map(ctx, stmt); + case STMT_SYNPROXY: + return stmt_evaluate_synproxy(ctx, stmt); + case STMT_CHAIN: + return stmt_evaluate_chain(ctx, stmt); + case STMT_OPTSTRIP: + return stmt_evaluate_optstrip(ctx, stmt); + default: + BUG("unknown statement type %s\n", stmt->ops->name); + } +} + +static int setelem_evaluate(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + struct set *set; + + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + set = set_cache_find(table, ctx->cmd->handle.set.name); + if (set == NULL) + return set_not_found(ctx, &ctx->cmd->handle.set.location, + ctx->cmd->handle.set.name); + + if (set->key == NULL) + return -1; + + set->existing_set = set; + ctx->set = set; + expr_set_context(&ctx->ectx, set->key->dtype, set->key->len); + if (expr_evaluate(ctx, &cmd->expr) < 0) + return -1; + + cmd->elem.set = set_get(set); + if (set_is_interval(ctx->set->flags)) { + if (!(set->flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, ctx->set, cmd->expr) < 0) + return -1; + + assert(cmd->expr->etype == EXPR_SET); + cmd->expr->set_flags |= NFT_SET_INTERVAL; + } + + ctx->set = NULL; + + return 0; +} + +static int set_key_data_error(struct eval_ctx *ctx, const struct set *set, + const struct datatype *dtype, + const char *name) +{ + const char *hint = ""; + + if (dtype->size == 0) + hint = ". Try \"typeof expression\" instead of \"type datatype\"."; + + return set_error(ctx, set, "unqualified type %s " + "specified in %s definition%s", + dtype->name, name, hint); +} + +static int set_expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr) +{ + unsigned int flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON; + uint32_t ntype = 0, size = 0; + struct expr *i, *next; + + list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { + unsigned dsize_bytes; + + if (i->etype == EXPR_CT && + (i->ct.key == NFT_CT_SRC || + i->ct.key == NFT_CT_DST)) + return expr_error(ctx->msgs, i, + "specify either ip or ip6 for address matching"); + + if (i->etype == EXPR_PAYLOAD && + i->dtype->type == TYPE_INTEGER) { + struct datatype *dtype; + + dtype = datatype_clone(i->dtype); + dtype->size = i->len; + dtype->byteorder = i->byteorder; + __datatype_set(i, dtype); + } + + if (i->dtype->size == 0 && i->len == 0) + return expr_binary_error(ctx->msgs, i, *expr, + "can not use variable sized " + "data types (%s) in concat " + "expressions", + i->dtype->name); + + if (i->dtype->size) + assert(i->len == i->dtype->size); + + flags &= i->flags; + + ntype = concat_subtype_add(ntype, i->dtype->type); + + dsize_bytes = div_round_up(i->len, BITS_PER_BYTE); + (*expr)->field_len[(*expr)->field_count++] = dsize_bytes; + size += netlink_padded_len(i->len); + } + + (*expr)->flags |= flags; + __datatype_set(*expr, concat_type_alloc(ntype)); + (*expr)->len = size; + + expr_set_context(&ctx->ectx, (*expr)->dtype, (*expr)->len); + ctx->ectx.key = *expr; + + return 0; +} + +static int elems_evaluate(struct eval_ctx *ctx, struct set *set) +{ + ctx->set = set; + if (set->init != NULL) { + __expr_set_context(&ctx->ectx, set->key->dtype, + set->key->byteorder, set->key->len, 0); + if (expr_evaluate(ctx, &set->init) < 0) + return -1; + if (set->init->etype != EXPR_SET) + return expr_error(ctx->msgs, set->init, "Set %s: Unexpected initial type %s, missing { }?", + set->handle.set.name, expr_name(set->init)); + } + + if (set_is_interval(ctx->set->flags) && + !(ctx->set->flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, ctx->set, set->init) < 0) + return -1; + + ctx->set = NULL; + + return 0; +} + +static int set_evaluate(struct eval_ctx *ctx, struct set *set) +{ + struct set *existing_set = NULL; + unsigned int num_stmts = 0; + struct table *table; + struct stmt *stmt; + const char *type; + + if (!set_is_anonymous(set->flags)) { + table = table_cache_find(&ctx->nft->cache.table_cache, + set->handle.table.name, + set->handle.family); + if (table == NULL) + return table_not_found(ctx); + + existing_set = set_cache_find(table, set->handle.set.name); + if (!existing_set) + set_cache_add(set_get(set), table); + + if (existing_set && existing_set->flags & NFT_SET_EVAL) { + uint32_t existing_flags = existing_set->flags & ~NFT_SET_EVAL; + uint32_t new_flags = set->flags & ~NFT_SET_EVAL; + + if (existing_flags == new_flags) + set->flags |= NFT_SET_EVAL; + } + } + + if (!(set->flags & NFT_SET_INTERVAL) && set->automerge) + return set_error(ctx, set, "auto-merge only works with interval sets"); + + type = set_is_map(set->flags) ? "map" : "set"; + + if (set->key == NULL) + return set_error(ctx, set, "%s definition does not specify key", + type); + + if (set->key->len == 0) { + if (set->key->etype == EXPR_CONCAT && + set_expr_evaluate_concat(ctx, &set->key) < 0) + return -1; + + if (set->key->len == 0) + return set_key_data_error(ctx, set, + set->key->dtype, type); + } + + if (set->flags & NFT_SET_INTERVAL && set->key->etype == EXPR_CONCAT) { + memcpy(&set->desc.field_len, &set->key->field_len, + sizeof(set->desc.field_len)); + set->desc.field_count = set->key->field_count; + set->flags |= NFT_SET_CONCAT; + } + + if (set_is_datamap(set->flags)) { + if (set->data == NULL) + return set_error(ctx, set, "map definition does not " + "specify mapping data type"); + + if (set->data->etype == EXPR_CONCAT && + set_expr_evaluate_concat(ctx, &set->data) < 0) + return -1; + + if (set->data->flags & EXPR_F_INTERVAL) + set->data->len *= 2; + + if (set->data->len == 0 && set->data->dtype->type != TYPE_VERDICT) + return set_key_data_error(ctx, set, + set->data->dtype, type); + } else if (set_is_objmap(set->flags)) { + assert(set->data == NULL); + set->data = constant_expr_alloc(&netlink_location, &string_type, + BYTEORDER_HOST_ENDIAN, + NFT_OBJ_MAXNAMELEN * BITS_PER_BYTE, + NULL); + + } + + /* Default timeout value implies timeout support */ + if (set->timeout) + set->flags |= NFT_SET_TIMEOUT; + + list_for_each_entry(stmt, &set->stmt_list, list) + num_stmts++; + + if (num_stmts > 1) + set->flags |= NFT_SET_EXPR; + + if (set_is_anonymous(set->flags)) { + if (set_is_interval(set->init->set_flags) && + !(set->init->set_flags & NFT_SET_CONCAT) && + interval_set_eval(ctx, set, set->init) < 0) + return -1; + + return 0; + } + + set->existing_set = existing_set; + + return 0; +} + +static bool evaluate_priority(struct eval_ctx *ctx, struct prio_spec *prio, + int family, int hook) +{ + char prio_str[NFT_NAME_MAXLEN]; + char prio_fst[NFT_NAME_MAXLEN]; + struct location loc; + int priority; + int prio_snd; + char op; + + expr_set_context(&ctx->ectx, &priority_type, NFT_NAME_MAXLEN * BITS_PER_BYTE); + + if (expr_evaluate(ctx, &prio->expr) < 0) + return false; + if (prio->expr->etype != EXPR_VALUE) { + expr_error(ctx->msgs, prio->expr, "%s is not a valid " + "priority expression", expr_name(prio->expr)); + return false; + } + if (prio->expr->dtype->type == TYPE_INTEGER) + return true; + + mpz_export_data(prio_str, prio->expr->value, BYTEORDER_HOST_ENDIAN, + NFT_NAME_MAXLEN); + loc = prio->expr->location; + + if (sscanf(prio_str, "%s %c %d", prio_fst, &op, &prio_snd) < 3) { + priority = std_prio_lookup(prio_str, family, hook); + if (priority == NF_IP_PRI_LAST) + return false; + } else { + priority = std_prio_lookup(prio_fst, family, hook); + if (priority == NF_IP_PRI_LAST) + return false; + if (op == '+') + priority += prio_snd; + else if (op == '-') + priority -= prio_snd; + else + return false; + } + expr_free(prio->expr); + prio->expr = constant_expr_alloc(&loc, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, + &priority); + return true; +} + +static bool evaluate_expr_variable(struct eval_ctx *ctx, struct expr **exprp) +{ + struct expr *expr; + + if (expr_evaluate(ctx, exprp) < 0) + return false; + + expr = *exprp; + if (expr->etype != EXPR_VALUE && + expr->etype != EXPR_SET) { + expr_error(ctx->msgs, expr, "%s is not a valid " + "variable expression", expr_name(expr)); + return false; + } + + return true; +} + +static bool evaluate_device_expr(struct eval_ctx *ctx, struct expr **dev_expr) +{ + struct expr *expr, *next, *key; + LIST_HEAD(tmp); + + if ((*dev_expr)->etype == EXPR_VARIABLE) { + expr_set_context(&ctx->ectx, &ifname_type, + IFNAMSIZ * BITS_PER_BYTE); + if (!evaluate_expr_variable(ctx, dev_expr)) + return false; + } + + if ((*dev_expr)->etype != EXPR_SET && + (*dev_expr)->etype != EXPR_LIST) + return true; + + list_for_each_entry_safe(expr, next, &(*dev_expr)->expressions, list) { + list_del(&expr->list); + + switch (expr->etype) { + case EXPR_VARIABLE: + expr_set_context(&ctx->ectx, &ifname_type, + IFNAMSIZ * BITS_PER_BYTE); + if (!evaluate_expr_variable(ctx, &expr)) + return false; + break; + case EXPR_SET_ELEM: + key = expr_clone(expr->key); + expr_free(expr); + expr = key; + break; + case EXPR_VALUE: + break; + default: + BUG("invalid expression type %s\n", expr_name(expr)); + break; + } + + list_add(&expr->list, &tmp); + } + list_splice_init(&tmp, &(*dev_expr)->expressions); + + return true; +} + +static uint32_t str2hooknum(uint32_t family, const char *hook); + +static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft) +{ + struct table *table; + + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + if (!ft_cache_find(table, ft->handle.flowtable.name)) { + if (!ft->hook.name) + return chain_error(ctx, ft, "missing hook and priority in flowtable declaration"); + + ft_cache_add(flowtable_get(ft), table); + } + + if (ft->hook.name) { + ft->hook.num = str2hooknum(NFPROTO_NETDEV, ft->hook.name); + if (ft->hook.num == NF_INET_NUMHOOKS) + return chain_error(ctx, ft, "invalid hook %s", + ft->hook.name); + if (!evaluate_priority(ctx, &ft->priority, NFPROTO_NETDEV, ft->hook.num)) + return __stmt_binary_error(ctx, &ft->priority.loc, NULL, + "invalid priority expression %s.", + expr_name(ft->priority.expr)); + } + + if (ft->dev_expr && !evaluate_device_expr(ctx, &ft->dev_expr)) + return -1; + + return 0; +} + +/* make src point at dst, either via handle.position or handle.position_id */ +static void link_rules(struct rule *src, struct rule *dst) +{ + static uint32_t ref_id = 0; + + if (dst->handle.handle.id) { + /* dst is in kernel, make src reference it by handle */ + src->handle.position.id = dst->handle.handle.id; + src->handle.position.location = src->handle.index.location; + return; + } + + /* dst is not in kernel, make src reference it by per-transaction ID */ + if (!dst->handle.rule_id) + dst->handle.rule_id = ++ref_id; + src->handle.position_id = dst->handle.rule_id; +} + +static int rule_cache_update(struct eval_ctx *ctx, enum cmd_ops op) +{ + struct rule *rule = ctx->rule, *ref = NULL; + struct table *table; + struct chain *chain; + + table = table_cache_find(&ctx->nft->cache.table_cache, + rule->handle.table.name, + rule->handle.family); + if (!table) + return table_not_found(ctx); + + chain = chain_cache_find(table, rule->handle.chain.name); + if (!chain) + return chain_not_found(ctx); + + if (rule->handle.index.id) { + ref = rule_lookup_by_index(chain, rule->handle.index.id); + if (!ref) + return cmd_error(ctx, &rule->handle.index.location, + "Could not process rule: %s", + strerror(ENOENT)); + + link_rules(rule, ref); + } else if (rule->handle.handle.id) { + ref = rule_lookup(chain, rule->handle.handle.id); + if (!ref) + return cmd_error(ctx, &rule->handle.handle.location, + "Could not process rule: %s", + strerror(ENOENT)); + } else if (rule->handle.position.id) { + ref = rule_lookup(chain, rule->handle.position.id); + if (!ref) + return cmd_error(ctx, &rule->handle.position.location, + "Could not process rule: %s", + strerror(ENOENT)); + } + + switch (op) { + case CMD_INSERT: + rule_get(rule); + if (ref) + list_add_tail(&rule->list, &ref->list); + else + list_add(&rule->list, &chain->rules); + break; + case CMD_ADD: + rule_get(rule); + if (ref) + list_add(&rule->list, &ref->list); + else + list_add_tail(&rule->list, &chain->rules); + break; + case CMD_REPLACE: + rule_get(rule); + list_add(&rule->list, &ref->list); + /* fall through */ + case CMD_DELETE: + list_del(&ref->list); + rule_free(ref); + break; + default: + break; + } + return 0; +} + +static int rule_evaluate(struct eval_ctx *ctx, struct rule *rule, + enum cmd_ops op) +{ + struct stmt *stmt, *tstmt = NULL; + struct error_record *erec; + + proto_ctx_init(&ctx->_pctx[0], rule->handle.family, ctx->nft->debug_mask, false); + /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */ + proto_ctx_init(&ctx->_pctx[1], NFPROTO_BRIDGE, ctx->nft->debug_mask, true); + memset(&ctx->ectx, 0, sizeof(ctx->ectx)); + + ctx->rule = rule; + 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; + + ctx->inner_desc = NULL; + } + + erec = rule_postprocess(rule); + if (erec != NULL) { + erec_queue(erec, ctx->msgs); + return -1; + } + + if (nft_cache_needs_update(&ctx->nft->cache)) + return rule_cache_update(ctx, op); + + return 0; +} + +static uint32_t str2hooknum(uint32_t family, const char *hook) +{ + if (!hook) + return NF_INET_NUMHOOKS; + + switch (family) { + case NFPROTO_INET: + if (!strcmp(hook, "ingress")) + return NF_INET_INGRESS; + /* fall through */ + case NFPROTO_IPV4: + case NFPROTO_BRIDGE: + case NFPROTO_IPV6: + /* These families have overlapping values for each hook */ + if (!strcmp(hook, "prerouting")) + return NF_INET_PRE_ROUTING; + else if (!strcmp(hook, "input")) + return NF_INET_LOCAL_IN; + else if (!strcmp(hook, "forward")) + return NF_INET_FORWARD; + else if (!strcmp(hook, "postrouting")) + return NF_INET_POST_ROUTING; + else if (!strcmp(hook, "output")) + return NF_INET_LOCAL_OUT; + break; + case NFPROTO_ARP: + if (!strcmp(hook, "input")) + return NF_ARP_IN; + else if (!strcmp(hook, "forward")) + return NF_ARP_FORWARD; + else if (!strcmp(hook, "output")) + return NF_ARP_OUT; + break; + case NFPROTO_NETDEV: + if (!strcmp(hook, "ingress")) + return NF_NETDEV_INGRESS; + else if (!strcmp(hook, "egress")) + return NF_NETDEV_EGRESS; + break; + default: + break; + } + + return NF_INET_NUMHOOKS; +} + +static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain) +{ + struct table *table; + + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + if (chain == NULL) { + if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) { + chain = chain_alloc(); + handle_merge(&chain->handle, &ctx->cmd->handle); + chain_cache_add(chain, table); + } + return 0; + } else if (!(chain->flags & CHAIN_F_BINDING)) { + if (!chain_cache_find(table, chain->handle.chain.name)) + chain_cache_add(chain_get(chain), table); + } + + if (chain->flags & CHAIN_F_BASECHAIN) { + chain->hook.num = str2hooknum(chain->handle.family, + chain->hook.name); + if (chain->hook.num == NF_INET_NUMHOOKS) + return __stmt_binary_error(ctx, &chain->hook.loc, NULL, + "The %s family does not support this hook", + family2str(chain->handle.family)); + + if (!evaluate_priority(ctx, &chain->priority, + chain->handle.family, chain->hook.num)) + return __stmt_binary_error(ctx, &chain->priority.loc, NULL, + "invalid priority expression %s in this context.", + expr_name(chain->priority.expr)); + if (chain->policy) { + expr_set_context(&ctx->ectx, &policy_type, + NFT_NAME_MAXLEN * BITS_PER_BYTE); + if (!evaluate_expr_variable(ctx, &chain->policy)) + return chain_error(ctx, chain, "invalid policy expression %s", + expr_name(chain->policy)); + } + } + + if (chain->dev_expr) { + if (!(chain->flags & CHAIN_F_BASECHAIN)) + chain->flags |= CHAIN_F_BASECHAIN; + + if (chain->handle.family == NFPROTO_NETDEV || + (chain->handle.family == NFPROTO_INET && + chain->hook.num == NF_INET_INGRESS)) { + if (chain->dev_expr && + !evaluate_device_expr(ctx, &chain->dev_expr)) + return -1; + } else if (chain->dev_expr) { + return __stmt_binary_error(ctx, &chain->dev_expr->location, NULL, + "This chain type cannot be bound to device"); + } + } + + return 0; +} + +static int ct_expect_evaluate(struct eval_ctx *ctx, struct obj *obj) +{ + struct ct_expect *ct = &obj->ct_expect; + + if (!ct->l4proto || + !ct->dport || + !ct->timeout || + !ct->size) + return __stmt_binary_error(ctx, &obj->location, NULL, + "missing options"); + + return 0; +} + +static int ct_timeout_evaluate(struct eval_ctx *ctx, struct obj *obj) +{ + struct ct_timeout *ct = &obj->ct_timeout; + struct timeout_state *ts, *next; + unsigned int i; + + for (i = 0; i < timeout_protocol[ct->l4proto].array_size; i++) + ct->timeout[i] = timeout_protocol[ct->l4proto].dflt_timeout[i]; + + list_for_each_entry_safe(ts, next, &ct->timeout_list, head) { + if (timeout_str2num(ct->l4proto, ts) < 0) + return __stmt_binary_error(ctx, &ts->location, NULL, + "invalid state for this protocol"); + + ct->timeout[ts->timeout_index] = ts->timeout_value; + list_del(&ts->head); + xfree(ts->timeout_str); + xfree(ts); + } + + return 0; +} + +static int obj_evaluate(struct eval_ctx *ctx, struct obj *obj) +{ + struct table *table; + + table = table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family); + if (!table) + return table_not_found(ctx); + + if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) + obj_cache_add(obj_get(obj), table); + + switch (obj->type) { + case NFT_OBJECT_CT_TIMEOUT: + return ct_timeout_evaluate(ctx, obj); + case NFT_OBJECT_CT_EXPECT: + return ct_expect_evaluate(ctx, obj); + default: + break; + } + + return 0; +} + +static int table_evaluate(struct eval_ctx *ctx, struct table *table) +{ + if (!table_cache_find(&ctx->nft->cache.table_cache, + ctx->cmd->handle.table.name, + ctx->cmd->handle.family)) { + if (!table) { + table = table_alloc(); + handle_merge(&table->handle, &ctx->cmd->handle); + table_cache_add(table, &ctx->nft->cache); + } else { + table_cache_add(table_get(table), &ctx->nft->cache); + } + } + + return 0; +} + +static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + return setelem_evaluate(ctx, cmd); + case CMD_OBJ_SET: + handle_merge(&cmd->set->handle, &cmd->handle); + return set_evaluate(ctx, cmd->set); + case CMD_OBJ_SETELEMS: + return elems_evaluate(ctx, cmd->set); + case CMD_OBJ_RULE: + handle_merge(&cmd->rule->handle, &cmd->handle); + return rule_evaluate(ctx, cmd->rule, cmd->op); + case CMD_OBJ_CHAIN: + return chain_evaluate(ctx, cmd->chain); + case CMD_OBJ_TABLE: + return table_evaluate(ctx, cmd->table); + case CMD_OBJ_FLOWTABLE: + handle_merge(&cmd->flowtable->handle, &cmd->handle); + return flowtable_evaluate(ctx, cmd->flowtable); + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_LIMIT: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_SECMARK: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_SYNPROXY: + handle_merge(&cmd->object->handle, &cmd->handle); + return obj_evaluate(ctx, cmd->object); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static void table_del_cache(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + + if (!cmd->handle.table.name) + return; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return; + + table_cache_del(table); + table_free(table); +} + +static void chain_del_cache(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + struct chain *chain; + + if (!cmd->handle.chain.name) + return; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return; + + chain = chain_cache_find(table, cmd->handle.chain.name); + if (!chain) + return; + + chain_cache_del(chain); + chain_free(chain); +} + +static void set_del_cache(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + struct set *set; + + if (!cmd->handle.set.name) + return; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return; + + set = set_cache_find(table, cmd->handle.set.name); + if (!set) + return; + + set_cache_del(set); + set_free(set); +} + +static void ft_del_cache(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct flowtable *ft; + struct table *table; + + if (!cmd->handle.flowtable.name) + return; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return; + + ft = ft_cache_find(table, cmd->handle.flowtable.name); + if (!ft) + return; + + ft_cache_del(ft); + flowtable_free(ft); +} + +static void obj_del_cache(struct eval_ctx *ctx, struct cmd *cmd, int type) +{ + struct table *table; + struct obj *obj; + + if (!cmd->handle.obj.name) + return; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return; + + obj = obj_cache_find(table, cmd->handle.obj.name, type); + if (!obj) + return; + + obj_cache_del(obj); + obj_free(obj); +} + +static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + return setelem_evaluate(ctx, cmd); + case CMD_OBJ_SET: + set_del_cache(ctx, cmd); + return 0; + case CMD_OBJ_RULE: + return 0; + case CMD_OBJ_CHAIN: + chain_del_cache(ctx, cmd); + return 0; + case CMD_OBJ_TABLE: + table_del_cache(ctx, cmd); + return 0; + case CMD_OBJ_FLOWTABLE: + ft_del_cache(ctx, cmd); + return 0; + case CMD_OBJ_COUNTER: + obj_del_cache(ctx, cmd, NFT_OBJECT_COUNTER); + return 0; + case CMD_OBJ_QUOTA: + obj_del_cache(ctx, cmd, NFT_OBJECT_QUOTA); + return 0; + case CMD_OBJ_CT_HELPER: + obj_del_cache(ctx, cmd, NFT_OBJECT_CT_HELPER); + return 0; + case CMD_OBJ_CT_TIMEOUT: + obj_del_cache(ctx, cmd, NFT_OBJECT_CT_TIMEOUT); + return 0; + case CMD_OBJ_LIMIT: + obj_del_cache(ctx, cmd, NFT_OBJECT_LIMIT); + return 0; + case CMD_OBJ_SECMARK: + obj_del_cache(ctx, cmd, NFT_OBJECT_SECMARK); + return 0; + case CMD_OBJ_CT_EXPECT: + obj_del_cache(ctx, cmd, NFT_OBJECT_CT_EXPECT); + return 0; + case CMD_OBJ_SYNPROXY: + obj_del_cache(ctx, cmd, NFT_OBJECT_SYNPROXY); + return 0; + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static int cmd_evaluate_get(struct eval_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + return setelem_evaluate(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static int obj_not_found(struct eval_ctx *ctx, const struct location *loc, + const char *obj_name) +{ + const struct table *table; + struct obj *obj; + + obj = obj_lookup_fuzzy(obj_name, &ctx->nft->cache, &table); + if (obj == NULL) + return cmd_error(ctx, loc, "%s", strerror(ENOENT)); + + return cmd_error(ctx, loc, + "%s; did you mean obj ‘%s’ in table %s ‘%s’?", + strerror(ENOENT), obj->handle.obj.name, + family2str(obj->handle.family), + table->handle.table.name); +} + +static int cmd_evaluate_list_obj(struct eval_ctx *ctx, const struct cmd *cmd, + uint32_t obj_type) +{ + const struct table *table; + + if (obj_type == NFT_OBJECT_UNSPEC) + obj_type = NFT_OBJECT_COUNTER; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (table == NULL) + return table_not_found(ctx); + + if (!obj_cache_find(table, cmd->handle.obj.name, obj_type)) + return obj_not_found(ctx, &cmd->handle.obj.location, + cmd->handle.obj.name); + + return 0; +} + +static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct flowtable *ft; + struct table *table; + struct set *set; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (cmd->handle.table.name == NULL) + return 0; + + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + return 0; + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return set_not_found(ctx, &ctx->cmd->handle.set.location, + ctx->cmd->handle.set.name); + if ((cmd->obj == CMD_OBJ_SET && !set_is_literal(set->flags)) || + (cmd->obj == CMD_OBJ_MAP && !map_is_literal(set->flags)) || + (cmd->obj == CMD_OBJ_METER && !set_is_meter(set->flags))) + return cmd_error(ctx, &ctx->cmd->handle.set.location, + "%s", strerror(ENOENT)); + + cmd->set = set_get(set); + return 0; + case CMD_OBJ_CHAIN: + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + if (!chain_cache_find(table, cmd->handle.chain.name)) + return chain_not_found(ctx); + + return 0; + case CMD_OBJ_FLOWTABLE: + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + ft = ft_cache_find(table, cmd->handle.flowtable.name); + if (!ft) + return flowtable_not_found(ctx, &ctx->cmd->handle.flowtable.location, + ctx->cmd->handle.flowtable.name); + + return 0; + case CMD_OBJ_QUOTA: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_QUOTA); + case CMD_OBJ_COUNTER: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_COUNTER); + case CMD_OBJ_CT_HELPER: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER); + case CMD_OBJ_CT_TIMEOUT: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT); + case CMD_OBJ_LIMIT: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SECMARK); + case CMD_OBJ_CT_EXPECT: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT); + case CMD_OBJ_SYNPROXY: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY); + case CMD_OBJ_COUNTERS: + case CMD_OBJ_QUOTAS: + case CMD_OBJ_CT_HELPERS: + case CMD_OBJ_LIMITS: + case CMD_OBJ_SETS: + case CMD_OBJ_FLOWTABLES: + case CMD_OBJ_SECMARKS: + case CMD_OBJ_SYNPROXYS: + case CMD_OBJ_CT_TIMEOUTS: + case CMD_OBJ_CT_EXPECTATIONS: + if (cmd->handle.table.name == NULL) + return 0; + if (!table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family)) + return table_not_found(ctx); + + return 0; + case CMD_OBJ_CHAINS: + case CMD_OBJ_RULESET: + case CMD_OBJ_METERS: + case CMD_OBJ_MAPS: + return 0; + case CMD_OBJ_HOOKS: + if (cmd->handle.chain.name) { + int hooknum = str2hooknum(cmd->handle.family, cmd->handle.chain.name); + + if (hooknum == NF_INET_NUMHOOKS) + return chain_not_found(ctx); + + cmd->handle.chain_id = hooknum; + } + return 0; + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static int cmd_evaluate_reset(struct eval_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_COUNTERS: + case CMD_OBJ_QUOTAS: + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: + if (cmd->handle.table.name == NULL) + return 0; + if (!table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family)) + return table_not_found(ctx); + + return 0; + case CMD_OBJ_ELEMENTS: + return setelem_evaluate(ctx, cmd); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + return cmd_evaluate_list(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static void __flush_set_cache(struct set *set) +{ + if (set->init != NULL) { + expr_free(set->init); + set->init = NULL; + } +} + +static int cmd_evaluate_flush(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct cache *table_cache = &ctx->nft->cache.table_cache; + struct table *table; + struct set *set; + + switch (cmd->obj) { + case CMD_OBJ_RULESET: + break; + case CMD_OBJ_TABLE: + /* Flushing a table does not empty the sets in the table nor remove + * any chains. + */ + case CMD_OBJ_CHAIN: + /* Chains don't hold sets */ + break; + case CMD_OBJ_SET: + table = table_cache_find(table_cache, cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return set_not_found(ctx, &ctx->cmd->handle.set.location, + ctx->cmd->handle.set.name); + else if (!set_is_literal(set->flags)) + return cmd_error(ctx, &ctx->cmd->handle.set.location, + "%s", strerror(ENOENT)); + + __flush_set_cache(set); + + return 0; + case CMD_OBJ_MAP: + table = table_cache_find(table_cache, cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return set_not_found(ctx, &ctx->cmd->handle.set.location, + ctx->cmd->handle.set.name); + else if (!map_is_literal(set->flags)) + return cmd_error(ctx, &ctx->cmd->handle.set.location, + "%s", strerror(ENOENT)); + + __flush_set_cache(set); + + return 0; + case CMD_OBJ_METER: + table = table_cache_find(table_cache, cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return set_not_found(ctx, &ctx->cmd->handle.set.location, + ctx->cmd->handle.set.name); + else if (!set_is_meter(set->flags)) + return cmd_error(ctx, &ctx->cmd->handle.set.location, + "%s", strerror(ENOENT)); + + __flush_set_cache(set); + + return 0; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int cmd_evaluate_rename(struct eval_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + + switch (cmd->obj) { + case CMD_OBJ_CHAIN: + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + if (!table) + return table_not_found(ctx); + + if (!chain_cache_find(table, ctx->cmd->handle.chain.name)) + return chain_not_found(ctx); + + break; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +enum { + CMD_MONITOR_EVENT_ANY, + CMD_MONITOR_EVENT_NEW, + CMD_MONITOR_EVENT_DEL, + CMD_MONITOR_EVENT_MAX +}; + +static uint32_t monitor_flags[CMD_MONITOR_EVENT_MAX][CMD_MONITOR_OBJ_MAX] = { + [CMD_MONITOR_EVENT_ANY] = { + [CMD_MONITOR_OBJ_ANY] = 0xffffffff, + [CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_NEWTABLE) | + (1 << NFT_MSG_DELTABLE), + [CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_NEWCHAIN) | + (1 << NFT_MSG_DELCHAIN), + [CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_NEWRULE) | + (1 << NFT_MSG_DELRULE), + [CMD_MONITOR_OBJ_SETS] = (1 << NFT_MSG_NEWSET) | + (1 << NFT_MSG_DELSET), + [CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_NEWSETELEM) | + (1 << NFT_MSG_DELSETELEM), + [CMD_MONITOR_OBJ_RULESET] = (1 << NFT_MSG_NEWTABLE) | + (1 << NFT_MSG_DELTABLE) | + (1 << NFT_MSG_NEWCHAIN) | + (1 << NFT_MSG_DELCHAIN) | + (1 << NFT_MSG_NEWRULE) | + (1 << NFT_MSG_DELRULE) | + (1 << NFT_MSG_NEWSET) | + (1 << NFT_MSG_DELSET) | + (1 << NFT_MSG_NEWSETELEM) | + (1 << NFT_MSG_DELSETELEM) | + (1 << NFT_MSG_NEWOBJ) | + (1 << NFT_MSG_DELOBJ), + [CMD_MONITOR_OBJ_TRACE] = (1 << NFT_MSG_TRACE), + }, + [CMD_MONITOR_EVENT_NEW] = { + [CMD_MONITOR_OBJ_ANY] = (1 << NFT_MSG_NEWTABLE) | + (1 << NFT_MSG_NEWCHAIN) | + (1 << NFT_MSG_NEWRULE) | + (1 << NFT_MSG_NEWSET) | + (1 << NFT_MSG_NEWSETELEM), + [CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_NEWTABLE), + [CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_NEWCHAIN), + [CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_NEWRULE), + [CMD_MONITOR_OBJ_SETS] = (1 << NFT_MSG_NEWSET), + [CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_NEWSETELEM), + [CMD_MONITOR_OBJ_RULESET] = (1 << NFT_MSG_NEWTABLE) | + (1 << NFT_MSG_NEWCHAIN) | + (1 << NFT_MSG_NEWRULE) | + (1 << NFT_MSG_NEWSET) | + (1 << NFT_MSG_NEWSETELEM) | + (1 << NFT_MSG_NEWOBJ), + [CMD_MONITOR_OBJ_TRACE] = 0, + }, + [CMD_MONITOR_EVENT_DEL] = { + [CMD_MONITOR_OBJ_ANY] = (1 << NFT_MSG_DELTABLE) | + (1 << NFT_MSG_DELCHAIN) | + (1 << NFT_MSG_DELRULE) | + (1 << NFT_MSG_DELSET) | + (1 << NFT_MSG_DELSETELEM), + [CMD_MONITOR_OBJ_TABLES] = (1 << NFT_MSG_DELTABLE), + [CMD_MONITOR_OBJ_CHAINS] = (1 << NFT_MSG_DELCHAIN), + [CMD_MONITOR_OBJ_RULES] = (1 << NFT_MSG_DELRULE), + [CMD_MONITOR_OBJ_SETS] = (1 << NFT_MSG_DELSET), + [CMD_MONITOR_OBJ_ELEMS] = (1 << NFT_MSG_DELSETELEM), + [CMD_MONITOR_OBJ_RULESET] = (1 << NFT_MSG_DELTABLE) | + (1 << NFT_MSG_DELCHAIN) | + (1 << NFT_MSG_DELRULE) | + (1 << NFT_MSG_DELSET) | + (1 << NFT_MSG_DELSETELEM) | + (1 << NFT_MSG_DELOBJ), + [CMD_MONITOR_OBJ_TRACE] = 0, + }, +}; + +static int cmd_evaluate_monitor(struct eval_ctx *ctx, struct cmd *cmd) +{ + uint32_t event; + + if (cmd->monitor->event == NULL) + event = CMD_MONITOR_EVENT_ANY; + else if (strcmp(cmd->monitor->event, "new") == 0) + event = CMD_MONITOR_EVENT_NEW; + else if (strcmp(cmd->monitor->event, "destroy") == 0) + event = CMD_MONITOR_EVENT_DEL; + else { + return monitor_error(ctx, cmd->monitor, "invalid event %s", + cmd->monitor->event); + } + + cmd->monitor->flags = monitor_flags[event][cmd->monitor->type]; + return 0; +} + +static int cmd_evaluate_export(struct eval_ctx *ctx, struct cmd *cmd) +{ + if (cmd->markup->format == __NFT_OUTPUT_NOTSUPP) + return cmd_error(ctx, &cmd->location, + "this output type is not supported, use nft -j list ruleset for JSON support instead"); + else if (cmd->markup->format == NFTNL_OUTPUT_JSON) + return cmd_error(ctx, &cmd->location, + "JSON export is no longer supported, use 'nft -j list ruleset' instead"); + + return 0; +} + +static int cmd_evaluate_import(struct eval_ctx *ctx, struct cmd *cmd) +{ + if (cmd->markup->format == __NFT_OUTPUT_NOTSUPP) + return cmd_error(ctx, &cmd->location, + "this output type not supported"); + + return 0; +} + +static const char * const cmd_op_name[] = { + [CMD_INVALID] = "invalid", + [CMD_ADD] = "add", + [CMD_REPLACE] = "replace", + [CMD_CREATE] = "create", + [CMD_INSERT] = "insert", + [CMD_DELETE] = "delete", + [CMD_GET] = "get", + [CMD_LIST] = "list", + [CMD_FLUSH] = "flush", + [CMD_RENAME] = "rename", + [CMD_EXPORT] = "export", + [CMD_MONITOR] = "monitor", + [CMD_DESCRIBE] = "describe", + [CMD_DESTROY] = "destroy", +}; + +static const char *cmd_op_to_name(enum cmd_ops op) +{ + if (op > CMD_DESCRIBE) + return "unknown"; + + return cmd_op_name[op]; +} + +int cmd_evaluate(struct eval_ctx *ctx, struct cmd *cmd) +{ + if (ctx->nft->debug_mask & NFT_DEBUG_EVALUATION) { + struct error_record *erec; + + erec = erec_create(EREC_INFORMATIONAL, &cmd->location, + "Evaluate %s", cmd_op_to_name(cmd->op)); + erec_print(&ctx->nft->output, erec, ctx->nft->debug_mask); + nft_print(&ctx->nft->output, "\n\n"); + erec_destroy(erec); + } + + memset(&ctx->ectx, 0, sizeof(ctx->ectx)); + + ctx->cmd = cmd; + switch (cmd->op) { + case CMD_ADD: + case CMD_REPLACE: + case CMD_CREATE: + case CMD_INSERT: + return cmd_evaluate_add(ctx, cmd); + case CMD_DELETE: + case CMD_DESTROY: + return cmd_evaluate_delete(ctx, cmd); + case CMD_GET: + return cmd_evaluate_get(ctx, cmd); + case CMD_LIST: + return cmd_evaluate_list(ctx, cmd); + case CMD_RESET: + return cmd_evaluate_reset(ctx, cmd); + case CMD_FLUSH: + return cmd_evaluate_flush(ctx, cmd); + case CMD_RENAME: + return cmd_evaluate_rename(ctx, cmd); + case CMD_EXPORT: + return cmd_evaluate_export(ctx, cmd); + case CMD_DESCRIBE: + return 0; + case CMD_MONITOR: + return cmd_evaluate_monitor(ctx, cmd); + case CMD_IMPORT: + return cmd_evaluate_import(ctx, cmd); + default: + BUG("invalid command operation %u\n", cmd->op); + }; +} diff --git a/src/expression.c b/src/expression.c new file mode 100644 index 0000000..a21dfec --- /dev/null +++ b/src/expression.c @@ -0,0 +1,1563 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <limits.h> + +#include <expression.h> +#include <statement.h> +#include <datatype.h> +#include <netlink.h> +#include <rule.h> +#include <gmputil.h> +#include <utils.h> +#include <list.h> +#include <erec.h> +#include <json.h> + +extern const struct expr_ops ct_expr_ops; +extern const struct expr_ops fib_expr_ops; +extern const struct expr_ops hash_expr_ops; +extern const struct expr_ops inner_expr_ops; +extern const struct expr_ops meta_expr_ops; +extern const struct expr_ops numgen_expr_ops; +extern const struct expr_ops osf_expr_ops; +extern const struct expr_ops payload_expr_ops; +extern const struct expr_ops rt_expr_ops; +extern const struct expr_ops socket_expr_ops; +extern const struct expr_ops xfrm_expr_ops; + +struct expr *expr_alloc(const struct location *loc, enum expr_types etype, + const struct datatype *dtype, enum byteorder byteorder, + unsigned int len) +{ + struct expr *expr; + + expr = xzalloc(sizeof(*expr)); + expr->location = *loc; + expr->dtype = datatype_get(dtype); + expr->etype = etype; + expr->byteorder = byteorder; + expr->len = len; + expr->refcnt = 1; + init_list_head(&expr->list); + return expr; +} + +struct expr *expr_clone(const struct expr *expr) +{ + struct expr *new; + + new = expr_alloc(&expr->location, expr->etype, + expr->dtype, expr->byteorder, expr->len); + new->flags = expr->flags; + new->op = expr->op; + expr_ops(expr)->clone(new, expr); + return new; +} + +struct expr *expr_get(struct expr *expr) +{ + expr->refcnt++; + return expr; +} + +static void expr_destroy(struct expr *e) +{ + const struct expr_ops *ops = expr_ops(e); + + if (ops->destroy) + ops->destroy(e); +} + +void expr_free(struct expr *expr) +{ + if (expr == NULL) + return; + if (--expr->refcnt > 0) + return; + + datatype_free(expr->dtype); + + /* EXPR_INVALID expressions lack ->ops structure. + * This happens for compound types. + */ + if (expr->etype != EXPR_INVALID) + expr_destroy(expr); + xfree(expr); +} + +void expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const struct expr_ops *ops = expr_ops(expr); + + if (ops->print) + ops->print(expr, octx); +} + +bool expr_cmp(const struct expr *e1, const struct expr *e2) +{ + assert(e1->flags & EXPR_F_SINGLETON); + assert(e2->flags & EXPR_F_SINGLETON); + + if (e1->etype != e2->etype) + return false; + + return expr_ops(e1)->cmp(e1, e2); +} + +const char *expr_name(const struct expr *e) +{ + return expr_ops(e)->name; +} + +void expr_describe(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype, *edtype = NULL; + unsigned int len = expr->len; + const char *delim = ""; + + if (dtype == &invalid_type && + expr->etype == EXPR_SYMBOL) + edtype = datatype_lookup_byname(expr->identifier); + + if (edtype) { + dtype = edtype; + nft_print(octx, "datatype %s (%s)", + dtype->name, dtype->desc); + len = dtype->size; + } else { + nft_print(octx, "%s expression, datatype %s (%s)", + expr_name(expr), dtype->name, dtype->desc); + + if (dtype == &invalid_type) + return; + } + + if (dtype->basetype != NULL) { + nft_print(octx, " (basetype "); + for (dtype = dtype->basetype; dtype != NULL; + dtype = dtype->basetype) { + nft_print(octx, "%s%s", delim, dtype->desc); + delim = ", "; + } + nft_print(octx, ")"); + } + + if (expr_basetype(expr)->type == TYPE_STRING) { + if (len) + nft_print(octx, ", %u characters", + len / BITS_PER_BYTE); + else + nft_print(octx, ", dynamic length"); + } else + nft_print(octx, ", %u bits", len); + + if (!edtype) + edtype = expr->dtype; + + nft_print(octx, "\n"); + + if (edtype->sym_tbl != NULL) { + nft_print(octx, "\npre-defined symbolic constants "); + if (edtype->sym_tbl->base == BASE_DECIMAL) + nft_print(octx, "(in decimal):\n"); + else + nft_print(octx, "(in hexadecimal):\n"); + symbol_table_print(edtype->sym_tbl, edtype, + expr->byteorder, octx); + } else if (edtype->describe) { + edtype->describe(octx); + } +} + +void expr_to_string(const struct expr *expr, char *string) +{ + int len = expr->len / BITS_PER_BYTE; + + assert(expr->dtype == &string_type); + + mpz_export_data(string, expr->value, BYTEORDER_HOST_ENDIAN, len); +} + +void expr_set_type(struct expr *expr, const struct datatype *dtype, + enum byteorder byteorder) +{ + const struct expr_ops *ops = expr_ops(expr); + + if (ops->set_type) + ops->set_type(expr, dtype, byteorder); + else { + datatype_set(expr, 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 list_head *msgs, + 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, msgs); + return -1; +} + +static void verdict_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + datatype_print(expr, octx); +} + +static bool verdict_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + if (e1->verdict != e2->verdict) + return false; + + if ((e1->verdict == NFT_JUMP || + e1->verdict == NFT_GOTO) && + expr_cmp(e1->chain, e2->chain)) + return true; + + return false; +} + +static void verdict_expr_clone(struct expr *new, const struct expr *expr) +{ + new->verdict = expr->verdict; + if (expr->chain != NULL) + new->chain = expr_clone(expr->chain); +} + +static void verdict_expr_destroy(struct expr *expr) +{ + expr_free(expr->chain); +} + +static int verdict_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + return 0; +} + +static struct expr *verdict_expr_parse_udata(const struct nftnl_udata *attr) +{ + struct expr *e; + + e = symbol_expr_alloc(&internal_location, SYMBOL_VALUE, NULL, "verdict"); + e->dtype = &verdict_type; + e->len = NFT_REG_SIZE * BITS_PER_BYTE; + return e; +} + +static const struct expr_ops verdict_expr_ops = { + .type = EXPR_VERDICT, + .name = "verdict", + .print = verdict_expr_print, + .json = verdict_expr_json, + .cmp = verdict_expr_cmp, + .clone = verdict_expr_clone, + .destroy = verdict_expr_destroy, + .build_udata = verdict_expr_build_udata, + .parse_udata = verdict_expr_parse_udata, +}; + +struct expr *verdict_expr_alloc(const struct location *loc, + int verdict, struct expr *chain) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_VERDICT, &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, struct output_ctx *octx) +{ + nft_print(octx, "%s", expr->identifier); +} + +static void symbol_expr_clone(struct expr *new, const struct expr *expr) +{ + new->symtype = expr->symtype; + new->scope = expr->scope; + new->identifier = xstrdup(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, + .clone = symbol_expr_clone, + .destroy = symbol_expr_destroy, +}; + +struct expr *symbol_expr_alloc(const struct location *loc, + enum symbol_types type, struct scope *scope, + const char *identifier) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_SYMBOL, &invalid_type, + BYTEORDER_INVALID, 0); + expr->symtype = type; + expr->scope = scope; + expr->identifier = xstrdup(identifier); + return expr; +} + +static void variable_expr_print(const struct expr *expr, + struct output_ctx *octx) +{ + nft_print(octx, "$%s", expr->sym->identifier); +} + +static void variable_expr_clone(struct expr *new, const struct expr *expr) +{ + new->scope = expr->scope; + new->sym = expr->sym; + + expr->sym->refcnt++; +} + +static void variable_expr_destroy(struct expr *expr) +{ + expr->sym->refcnt--; +} + +static const struct expr_ops variable_expr_ops = { + .type = EXPR_VARIABLE, + .name = "variable", + .print = variable_expr_print, + .clone = variable_expr_clone, + .destroy = variable_expr_destroy, +}; + +struct expr *variable_expr_alloc(const struct location *loc, + struct scope *scope, struct symbol *sym) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_VARIABLE, &invalid_type, + BYTEORDER_INVALID, 0); + expr->scope = scope; + expr->sym = sym; + return expr; +} + +static void constant_expr_print(const struct expr *expr, + struct output_ctx *octx) +{ + datatype_print(expr, octx); +} + +static bool constant_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return expr_basetype(e1) == expr_basetype(e2) && + !mpz_cmp(e1->value, e2->value); +} + +static void constant_expr_clone(struct expr *new, const struct expr *expr) +{ + mpz_init_set(new->value, expr->value); +} + +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, + .json = constant_expr_json, + .cmp = constant_expr_cmp, + .clone = constant_expr_clone, + .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, EXPR_VALUE, 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->etype == EXPR_VALUE); + assert(e2->etype == 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->etype == 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_lshift_ui(mask, expr->len - len); + + mpz_set(slice->value, expr->value); + mpz_and(slice->value, slice->value, mask); + mpz_rshift_ui(slice->value, expr->len - len); + mpz_clear(mask); + + expr->len -= len; + return slice; +} + +/* + * Allocate a constant expression with a single bit set at position n. + */ +struct expr *flag_expr_alloc(const struct location *loc, + const struct datatype *dtype, + enum byteorder byteorder, + unsigned int len, unsigned long n) +{ + struct expr *expr; + + assert(n < len); + + expr = constant_expr_alloc(loc, dtype, byteorder, len, NULL); + mpz_set_ui(expr->value, 1); + mpz_lshift_ui(expr->value, n); + + return expr; +} + +/* + * Convert an expression of basetype TYPE_BITMASK into a series of inclusive + * OR binop expressions of the individual flag values. + */ +struct expr *bitmask_expr_to_binops(struct expr *expr) +{ + struct expr *binop, *flag; + unsigned long n; + + assert(expr->etype == EXPR_VALUE); + assert(expr->dtype->basetype->type == TYPE_BITMASK); + + n = mpz_popcount(expr->value); + if (n == 0 || n == 1) + return expr; + + binop = NULL; + n = 0; + while ((n = mpz_scan1(expr->value, n)) != ULONG_MAX) { + flag = flag_expr_alloc(&expr->location, expr->dtype, + expr->byteorder, expr->len, n); + if (binop != NULL) + binop = binop_expr_alloc(&expr->location, + OP_OR, binop, flag); + else + binop = flag; + + n++; + } + + expr_free(expr); + return binop; +} + +static void prefix_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->prefix, octx); + nft_print(octx, "/%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->prefix, type, byteorder); +} + +static void prefix_expr_clone(struct expr *new, const struct expr *expr) +{ + new->prefix = expr_clone(expr->prefix); + new->prefix_len = expr->prefix_len; +} + +static void prefix_expr_destroy(struct expr *expr) +{ + expr_free(expr->prefix); +} + +static const struct expr_ops prefix_expr_ops = { + .type = EXPR_PREFIX, + .name = "prefix", + .print = prefix_expr_print, + .json = prefix_expr_json, + .set_type = prefix_expr_set_type, + .clone = prefix_expr_clone, + .destroy = prefix_expr_destroy, +}; + +struct expr *prefix_expr_alloc(const struct location *loc, + struct expr *expr, unsigned int prefix_len) +{ + struct expr *prefix; + + prefix = expr_alloc(loc, EXPR_PREFIX, &invalid_type, + BYTEORDER_INVALID, 0); + prefix->prefix = 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] = "==", + [OP_NEQ] = "!=", + [OP_LT] = "<", + [OP_GT] = ">", + [OP_LTE] = "<=", + [OP_GTE] = ">=", + [OP_NEG] = "!", +}; + +static void unary_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->arg, octx); +} + +static void unary_expr_clone(struct expr *new, const struct expr *expr) +{ + new->arg = expr_clone(expr->arg); +} + +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, + .json = unary_expr_json, + .clone = unary_expr_clone, + .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, EXPR_UNARY, &invalid_type, + BYTEORDER_INVALID, 0); + expr->op = op; + expr->arg = arg; + return expr; +} + +static uint8_t expr_binop_precedence[OP_MAX + 1] = { + [OP_LSHIFT] = 1, + [OP_RSHIFT] = 1, + [OP_AND] = 2, + [OP_XOR] = 3, + [OP_OR] = 4, +}; + +static void binop_arg_print(const struct expr *op, const struct expr *arg, + struct output_ctx *octx) +{ + bool prec = false; + + if (arg->etype == EXPR_BINOP && + expr_binop_precedence[op->op] != 0 && + expr_binop_precedence[op->op] < expr_binop_precedence[arg->op]) + prec = 1; + + if (prec) + nft_print(octx, "("); + expr_print(arg, octx); + if (prec) + nft_print(octx, ")"); +} + +bool must_print_eq_op(const struct expr *expr) +{ + if (expr->right->dtype->basetype != NULL && + expr->right->dtype->basetype->type == TYPE_BITMASK && + expr->right->etype == EXPR_VALUE) + return true; + + return expr->left->etype == EXPR_BINOP; +} + +static void binop_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + binop_arg_print(expr, expr->left, octx); + + if (expr_op_symbols[expr->op] && + (expr->op != OP_EQ || must_print_eq_op(expr))) + nft_print(octx, " %s ", expr_op_symbols[expr->op]); + else + nft_print(octx, " "); + + binop_arg_print(expr, expr->right, octx); +} + +static void binop_expr_clone(struct expr *new, const struct expr *expr) +{ + new->left = expr_clone(expr->left); + new->right = expr_clone(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, + .json = binop_expr_json, + .clone = binop_expr_clone, + .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, EXPR_BINOP, left->dtype, + left->byteorder, 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, + .json = relational_expr_json, + .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, EXPR_RELATIONAL, &verdict_type, + BYTEORDER_INVALID, 0); + expr->left = left; + expr->op = op; + expr->right = right; + + if (right->dtype == &boolean_type) + left->flags |= EXPR_F_BOOLEAN; + + return expr; +} + +void relational_expr_pctx_update(struct proto_ctx *ctx, + const struct expr *expr) +{ + const struct expr *left = expr->left, *right = expr->right; + const struct expr_ops *ops; + const struct expr *i; + + assert(expr->etype == EXPR_RELATIONAL); + assert(expr->op == OP_EQ || expr->op == OP_IMPLICIT); + + ops = expr_ops(left); + if (ops->pctx_update && + (left->flags & EXPR_F_PROTOCOL)) { + if (expr_is_singleton(right)) + ops->pctx_update(ctx, &expr->location, left, right); + else if (right->etype == EXPR_SET) { + list_for_each_entry(i, &right->expressions, list) { + if (i->etype == EXPR_SET_ELEM && + i->key->etype == EXPR_VALUE) + ops->pctx_update(ctx, &expr->location, left, i->key); + } + } + } +} + +static void range_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + + octx->flags &= ~(NFT_CTX_OUTPUT_SERVICE | + NFT_CTX_OUTPUT_REVERSEDNS | + NFT_CTX_OUTPUT_GUID); + octx->flags |= NFT_CTX_OUTPUT_NUMERIC_ALL; + expr_print(expr->left, octx); + nft_print(octx, "-"); + expr_print(expr->right, octx); + octx->flags = flags; +} + +static void range_expr_clone(struct expr *new, const struct expr *expr) +{ + new->left = expr_clone(expr->left); + new->right = expr_clone(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, + .json = range_expr_json, + .clone = range_expr_clone, + .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, EXPR_RANGE, &invalid_type, + BYTEORDER_INVALID, 0); + expr->left = left; + expr->right = right; + return expr; +} + +struct expr *compound_expr_alloc(const struct location *loc, + enum expr_types etype) +{ + struct expr *expr; + + expr = expr_alloc(loc, etype, &invalid_type, BYTEORDER_INVALID, 0); + init_list_head(&expr->expressions); + return expr; +} + +static void compound_expr_clone(struct expr *new, const struct expr *expr) +{ + struct expr *i; + + init_list_head(&new->expressions); + list_for_each_entry(i, &expr->expressions, list) + compound_expr_add(new, expr_clone(i)); +} + +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, + struct output_ctx *octx) +{ + const struct expr *i; + const char *d = ""; + + list_for_each_entry(i, &expr->expressions, list) { + nft_print(octx, "%s", d); + expr_print(i, octx); + 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_destroy(struct expr *expr) +{ + compound_expr_destroy(expr); +} + +static void concat_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + compound_expr_print(expr, " . ", octx); +} + +#define NFTNL_UDATA_SET_KEY_CONCAT_NEST 0 +#define NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX NFT_REG32_SIZE + +#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE 0 +#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA 1 +#define NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX 2 + +static struct expr *expr_build_udata_recurse(struct expr *e) +{ + switch (e->etype) { + case EXPR_BINOP: + return e->left; + default: + break; + } + + return e; +} + +static int concat_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *concat_expr) +{ + struct nftnl_udata *nest; + struct expr *expr, *tmp; + unsigned int i = 0; + + list_for_each_entry_safe(expr, tmp, &concat_expr->expressions, list) { + struct nftnl_udata *nest_expr; + int err; + + expr = expr_build_udata_recurse(expr); + if (!expr_ops(expr)->build_udata || i >= NFT_REG32_SIZE) + return -1; + + nest = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_NEST + i); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE, expr->etype); + nest_expr = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA); + err = expr_ops(expr)->build_udata(udbuf, expr); + if (err < 0) + return err; + nftnl_udata_nest_end(udbuf, nest_expr); + nftnl_udata_nest_end(udbuf, nest); + i++; + } + + return 0; +} + +static int concat_parse_udata_nest(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + if (type >= NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX) + return -1; + + if (len <= sizeof(uint32_t)) + return -1; + + ud[type] = attr; + return 0; +} + +static int concat_parse_udata_nested(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE: + if (len != sizeof(uint32_t)) + return -1; + break; + case NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA: + if (len <= sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *concat_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST_MAX] = {}; + const struct datatype *dtype; + struct expr *concat_expr; + uint32_t dt = 0, len = 0; + unsigned int i; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + concat_parse_udata_nest, ud); + if (err < 0) + return NULL; + + concat_expr = concat_expr_alloc(&internal_location); + if (!concat_expr) + return NULL; + + for (i = 0; i < array_size(ud); i++) { + const struct nftnl_udata *nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_MAX]; + const struct nftnl_udata *nested, *subdata; + const struct expr_ops *ops; + struct expr *expr; + uint32_t etype; + + if (ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i] == NULL) + break; + + nested = ud[NFTNL_UDATA_SET_KEY_CONCAT_NEST + i]; + err = nftnl_udata_parse(nftnl_udata_get(nested), nftnl_udata_len(nested), + concat_parse_udata_nested, nest_ud); + if (err < 0) + goto err_free; + + etype = nftnl_udata_get_u32(nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_TYPE]); + ops = expr_ops_by_type_u32(etype); + if (!ops || !ops->parse_udata) + goto err_free; + + subdata = nest_ud[NFTNL_UDATA_SET_KEY_CONCAT_SUB_DATA]; + expr = ops->parse_udata(subdata); + if (!expr) + goto err_free; + + dt = concat_subtype_add(dt, expr->dtype->type); + compound_expr_add(concat_expr, expr); + len += netlink_padded_len(expr->len); + } + + dtype = concat_type_alloc(dt); + if (!dtype) + goto err_free; + + __datatype_set(concat_expr, dtype); + concat_expr->len = len; + + return concat_expr; + +err_free: + expr_free(concat_expr); + return NULL; +} + +static const struct expr_ops concat_expr_ops = { + .type = EXPR_CONCAT, + .name = "concat", + .print = concat_expr_print, + .json = concat_expr_json, + .clone = compound_expr_clone, + .destroy = concat_expr_destroy, + .build_udata = concat_expr_build_udata, + .parse_udata = concat_expr_parse_udata, +}; + +struct expr *concat_expr_alloc(const struct location *loc) +{ + return compound_expr_alloc(loc, EXPR_CONCAT); +} + +static void list_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + compound_expr_print(expr, ",", octx); +} + +static const struct expr_ops list_expr_ops = { + .type = EXPR_LIST, + .name = "list", + .print = list_expr_print, + .json = list_expr_json, + .clone = compound_expr_clone, + .destroy = compound_expr_destroy, +}; + +struct expr *list_expr_alloc(const struct location *loc) +{ + return compound_expr_alloc(loc, EXPR_LIST); +} + +static const char *calculate_delim(const struct expr *expr, int *count) +{ + const char *newline = ",\n\t\t\t "; + const char *singleline = ", "; + + if (set_is_anonymous(expr->set_flags)) + return singleline; + + if (!expr->dtype) + return newline; + + switch (expr->dtype->type) { + case TYPE_NFPROTO: + case TYPE_INTEGER: + case TYPE_ARPOP: + case TYPE_INET_PROTOCOL: + case TYPE_INET_SERVICE: + case TYPE_TCP_FLAG: + case TYPE_DCCP_PKTTYPE: + case TYPE_MARK: + case TYPE_IFINDEX: + case TYPE_CLASSID: + case TYPE_UID: + case TYPE_GID: + case TYPE_CT_DIR: + if (*count < 5) + return singleline; + *count = 0; + break; + case TYPE_IPADDR: + case TYPE_CT_STATE: + case TYPE_CT_STATUS: + case TYPE_PKTTYPE: + if (*count < 2) + return singleline; + *count = 0; + break; + + default: + break; + } + + return newline; +} + +static void set_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const struct expr *i; + const char *d = ""; + int count = 0; + + nft_print(octx, "{ "); + + list_for_each_entry(i, &expr->expressions, list) { + nft_print(octx, "%s", d); + expr_print(i, octx); + count++; + d = calculate_delim(expr, &count); + } + + nft_print(octx, " }"); +} + +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, + .json = set_expr_json, + .set_type = set_expr_set_type, + .clone = compound_expr_clone, + .destroy = compound_expr_destroy, +}; + +struct expr *set_expr_alloc(const struct location *loc, const struct set *set) +{ + struct expr *set_expr = compound_expr_alloc(loc, EXPR_SET); + + if (!set) + return set_expr; + + set_expr->set_flags = set->flags; + datatype_set(set_expr, set->key->dtype); + + return set_expr; +} + +static void mapping_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->left, octx); + nft_print(octx, " : "); + expr_print(expr->right, octx); +} + +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_clone(struct expr *new, const struct expr *expr) +{ + new->left = expr_clone(expr->left); + new->right = expr_clone(expr->right); +} + +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, + .json = mapping_expr_json, + .set_type = mapping_expr_set_type, + .clone = mapping_expr_clone, + .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, EXPR_MAPPING, from->dtype, + from->byteorder, 0); + expr->left = from; + expr->right = to; + return expr; +} + +static bool __set_expr_is_vmap(const struct expr *mappings) +{ + const struct expr *mapping; + + if (list_empty(&mappings->expressions)) + return false; + + mapping = list_first_entry(&mappings->expressions, struct expr, list); + if (mapping->etype == EXPR_MAPPING && + mapping->right->etype == EXPR_VERDICT) + return true; + + return false; +} + +static bool set_expr_is_vmap(const struct expr *expr) +{ + + if (expr->mappings->etype == EXPR_SET) + return __set_expr_is_vmap(expr->mappings); + + return false; +} + +static void map_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->map, octx); + if ((expr->mappings->etype == EXPR_SET_REF && + expr->mappings->set->data->dtype->type == TYPE_VERDICT) || + set_expr_is_vmap(expr)) + nft_print(octx, " vmap "); + else + nft_print(octx, " map "); + + expr_print(expr->mappings, octx); +} + +static void map_expr_clone(struct expr *new, const struct expr *expr) +{ + new->map = expr_clone(expr->map); + new->mappings = expr_clone(expr->mappings); +} + +static void map_expr_destroy(struct expr *expr) +{ + expr_free(expr->map); + expr_free(expr->mappings); +} + +static const struct expr_ops map_expr_ops = { + .type = EXPR_MAP, + .name = "map", + .print = map_expr_print, + .json = map_expr_json, + .clone = map_expr_clone, + .destroy = map_expr_destroy, +}; + +struct expr *map_expr_alloc(const struct location *loc, struct expr *arg, + struct expr *mappings) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_MAP, &invalid_type, BYTEORDER_INVALID, 0); + expr->map = arg; + expr->mappings = mappings; + return expr; +} + +static void set_ref_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + if (set_is_meter(expr->set->flags)) + nft_print(octx, "%s", expr->set->handle.set.name); + else if (set_is_anonymous(expr->set->flags)) + expr_print(expr->set->init, octx); + else + nft_print(octx, "@%s", expr->set->handle.set.name); +} + +static void set_ref_expr_clone(struct expr *new, const struct expr *expr) +{ + new->set = set_get(expr->set); +} + +static void set_ref_expr_destroy(struct expr *expr) +{ + set_free(expr->set); +} + +static const struct expr_ops set_ref_expr_ops = { + .type = EXPR_SET_REF, + .name = "set reference", + .print = set_ref_expr_print, + .json = set_ref_expr_json, + .clone = set_ref_expr_clone, + .destroy = set_ref_expr_destroy, +}; + +struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_SET_REF, set->key->dtype, 0, 0); + expr->set = set_get(set); + expr->flags |= EXPR_F_CONSTANT; + return expr; +} + +static void set_elem_expr_print(const struct expr *expr, + struct output_ctx *octx) +{ + struct stmt *stmt; + + expr_print(expr->key, octx); + list_for_each_entry(stmt, &expr->stmt_list, list) { + nft_print(octx, " "); + stmt_print(stmt, octx); + } + if (expr->timeout) { + nft_print(octx, " timeout "); + time_print(expr->timeout, octx); + } + if (!nft_output_stateless(octx) && expr->expiration) { + nft_print(octx, " expires "); + time_print(expr->expiration, octx); + } + if (expr->comment) + nft_print(octx, " comment \"%s\"", expr->comment); +} + +static void set_elem_expr_destroy(struct expr *expr) +{ + struct stmt *stmt, *next; + + xfree(expr->comment); + expr_free(expr->key); + list_for_each_entry_safe(stmt, next, &expr->stmt_list, list) + stmt_free(stmt); +} + +static void __set_elem_expr_clone(struct expr *new, const struct expr *expr) +{ + new->expiration = expr->expiration; + new->timeout = expr->timeout; + if (expr->comment) + new->comment = xstrdup(expr->comment); + init_list_head(&new->stmt_list); +} + +static void set_elem_expr_clone(struct expr *new, const struct expr *expr) +{ + new->key = expr_clone(expr->key); + __set_elem_expr_clone(new, expr); +} + +static const struct expr_ops set_elem_expr_ops = { + .type = EXPR_SET_ELEM, + .name = "set element", + .clone = set_elem_expr_clone, + .print = set_elem_expr_print, + .json = set_elem_expr_json, + .destroy = set_elem_expr_destroy, +}; + +struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_SET_ELEM, key->dtype, + key->byteorder, key->len); + expr->key = key; + init_list_head(&expr->stmt_list); + + return expr; +} + +static void set_elem_catchall_expr_print(const struct expr *expr, + struct output_ctx *octx) +{ + nft_print(octx, "*"); +} + +static void set_elem_catchall_expr_clone(struct expr *new, const struct expr *expr) +{ + __set_elem_expr_clone(new, expr); +} + +static const struct expr_ops set_elem_catchall_expr_ops = { + .type = EXPR_SET_ELEM_CATCHALL, + .name = "catch-all set element", + .print = set_elem_catchall_expr_print, + .json = set_elem_catchall_expr_json, + .clone = set_elem_catchall_expr_clone, +}; + +struct expr *set_elem_catchall_expr_alloc(const struct location *loc) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_SET_ELEM_CATCHALL, &invalid_type, + BYTEORDER_INVALID, 0); + expr->flags = EXPR_F_CONSTANT | EXPR_F_SINGLETON; + + return expr; +} + +static void flagcmp_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + expr_print(expr->flagcmp.expr, octx); + + if (expr->op == OP_NEQ) + nft_print(octx, " != "); + else + nft_print(octx, " "); + + expr_print(expr->flagcmp.value, octx); + nft_print(octx, " / "); + expr_print(expr->flagcmp.mask, octx); +} + +static void flagcmp_expr_clone(struct expr *new, const struct expr *expr) +{ + new->flagcmp.expr = expr_clone(expr->flagcmp.expr); + new->flagcmp.mask = expr_clone(expr->flagcmp.mask); + new->flagcmp.value = expr_clone(expr->flagcmp.value); +} + +static void flagcmp_expr_destroy(struct expr *expr) +{ + expr_free(expr->flagcmp.expr); + expr_free(expr->flagcmp.mask); + expr_free(expr->flagcmp.value); +} + +static const struct expr_ops flagcmp_expr_ops = { + .type = EXPR_FLAGCMP, + .name = "flags comparison", + .print = flagcmp_expr_print, + .json = flagcmp_expr_json, + .clone = flagcmp_expr_clone, + .destroy = flagcmp_expr_destroy, +}; + +struct expr *flagcmp_expr_alloc(const struct location *loc, enum ops op, + struct expr *match, struct expr *mask, + struct expr *value) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_FLAGCMP, match->dtype, match->byteorder, + match->len); + expr->op = op; + expr->flagcmp.expr = match; + expr->flagcmp.mask = mask; + /* json output needs this operation for compatibility */ + expr->flagcmp.mask->op = OP_OR; + expr->flagcmp.value = value; + + return expr; +} + +void range_expr_value_low(mpz_t rop, const struct expr *expr) +{ + switch (expr->etype) { + case EXPR_VALUE: + return mpz_set(rop, expr->value); + case EXPR_PREFIX: + return range_expr_value_low(rop, expr->prefix); + case EXPR_RANGE: + return range_expr_value_low(rop, expr->left); + case EXPR_MAPPING: + return range_expr_value_low(rop, expr->left); + case EXPR_SET_ELEM: + return range_expr_value_low(rop, expr->key); + default: + BUG("invalid range expression type %s\n", expr_name(expr)); + } +} + +void range_expr_value_high(mpz_t rop, const struct expr *expr) +{ + mpz_t tmp; + + switch (expr->etype) { + case EXPR_VALUE: + return mpz_set(rop, expr->value); + case EXPR_PREFIX: + range_expr_value_low(rop, expr->prefix); + assert(expr->len >= expr->prefix_len); + mpz_init_bitmask(tmp, expr->len - expr->prefix_len); + mpz_add(rop, rop, tmp); + mpz_clear(tmp); + return; + case EXPR_RANGE: + return range_expr_value_high(rop, expr->right); + case EXPR_MAPPING: + return range_expr_value_high(rop, expr->left); + case EXPR_SET_ELEM: + return range_expr_value_high(rop, expr->key); + default: + BUG("invalid range expression type %s\n", expr_name(expr)); + } +} + +static const struct expr_ops *__expr_ops_by_type(enum expr_types etype) +{ + switch (etype) { + case EXPR_INVALID: break; + case EXPR_VERDICT: return &verdict_expr_ops; + case EXPR_SYMBOL: return &symbol_expr_ops; + case EXPR_VARIABLE: return &variable_expr_ops; + case EXPR_VALUE: return &constant_expr_ops; + case EXPR_PREFIX: return &prefix_expr_ops; + case EXPR_RANGE: return &range_expr_ops; + case EXPR_PAYLOAD: return &payload_expr_ops; + case EXPR_EXTHDR: return &exthdr_expr_ops; + case EXPR_META: return &meta_expr_ops; + case EXPR_SOCKET: return &socket_expr_ops; + case EXPR_OSF: return &osf_expr_ops; + case EXPR_CT: return &ct_expr_ops; + case EXPR_CONCAT: return &concat_expr_ops; + case EXPR_LIST: return &list_expr_ops; + case EXPR_SET: return &set_expr_ops; + case EXPR_SET_REF: return &set_ref_expr_ops; + case EXPR_SET_ELEM: return &set_elem_expr_ops; + case EXPR_MAPPING: return &mapping_expr_ops; + case EXPR_MAP: return &map_expr_ops; + case EXPR_UNARY: return &unary_expr_ops; + case EXPR_BINOP: return &binop_expr_ops; + case EXPR_RELATIONAL: return &relational_expr_ops; + case EXPR_NUMGEN: return &numgen_expr_ops; + case EXPR_HASH: return &hash_expr_ops; + case EXPR_RT: return &rt_expr_ops; + case EXPR_FIB: return &fib_expr_ops; + case EXPR_XFRM: return &xfrm_expr_ops; + case EXPR_SET_ELEM_CATCHALL: return &set_elem_catchall_expr_ops; + case EXPR_FLAGCMP: return &flagcmp_expr_ops; + } + + return NULL; +} + +const struct expr_ops *expr_ops(const struct expr *e) +{ + const struct expr_ops *ops; + + ops = __expr_ops_by_type(e->etype); + if (!ops) + BUG("Unknown expression type %d\n", e->etype); + + return ops; +} + +const struct expr_ops *expr_ops_by_type_u32(uint32_t value) +{ + if (value > EXPR_MAX) + return NULL; + + return __expr_ops_by_type(value); +} diff --git a/src/exthdr.c b/src/exthdr.c new file mode 100644 index 0000000..60c7cd1 --- /dev/null +++ b/src/exthdr.c @@ -0,0 +1,610 @@ +/* + * Exthdr expression protocol and type definitions and related functions. + * + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <netinet/in.h> +#include <netinet/ip6.h> + +#include <utils.h> +#include <headers.h> +#include <expression.h> +#include <statement.h> +#include <sctp_chunk.h> + +static const struct exthdr_desc *exthdr_definitions[PROTO_DESC_MAX + 1] = { + [EXTHDR_DESC_HBH] = &exthdr_hbh, + [EXTHDR_DESC_RT] = &exthdr_rt, + [EXTHDR_DESC_RT0] = &exthdr_rt0, + [EXTHDR_DESC_RT2] = &exthdr_rt2, + [EXTHDR_DESC_SRH] = &exthdr_rt4, + [EXTHDR_DESC_FRAG] = &exthdr_frag, + [EXTHDR_DESC_DST] = &exthdr_dst, + [EXTHDR_DESC_MH] = &exthdr_mh, +}; + +static const struct exthdr_desc *exthdr_find_desc(enum exthdr_desc_id desc_id) +{ + if (desc_id >= EXTHDR_DESC_UNKNOWN && + desc_id <= EXTHDR_DESC_MAX) + return exthdr_definitions[desc_id]; + + return NULL; +} + +static void exthdr_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const char *name = expr->exthdr.desc ? + expr->exthdr.desc->name : "unknown-exthdr"; + + if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) { + /* Offset calculation is a bit hacky at this point. + * There might be a tcp option one day with another + * multiplicator + */ + unsigned int offset = expr->exthdr.offset / 64; + + if (expr->exthdr.desc == NULL) { + if (expr->exthdr.offset == 0 && + expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) { + nft_print(octx, "tcp option %d", expr->exthdr.raw_type); + return; + } + + nft_print(octx, "tcp option @%u,%u,%u", expr->exthdr.raw_type, + expr->exthdr.offset, expr->len); + return; + } + + nft_print(octx, "tcp option %s", name); + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + return; + if (offset) + nft_print(octx, "%d", offset); + nft_print(octx, " %s", expr->exthdr.tmpl->token); + } else if (expr->exthdr.op == NFT_EXTHDR_OP_IPV4) { + nft_print(octx, "ip option %s", name); + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + return; + nft_print(octx, " %s", expr->exthdr.tmpl->token); + } else if (expr->exthdr.op == NFT_EXTHDR_OP_SCTP) { + nft_print(octx, "sctp chunk %s", expr->exthdr.desc->name); + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + return; + nft_print(octx, " %s", expr->exthdr.tmpl->token); + } else if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) { + nft_print(octx, "dccp option %d", expr->exthdr.raw_type); + return; + } else { + if (expr->exthdr.flags & NFT_EXTHDR_F_PRESENT) + nft_print(octx, "exthdr %s", name); + else { + nft_print(octx, "%s %s", name, + expr->exthdr.tmpl->token); + } + } +} + +static bool exthdr_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->exthdr.desc == e2->exthdr.desc && + e1->exthdr.tmpl == e2->exthdr.tmpl && + e1->exthdr.op == e2->exthdr.op && + e1->exthdr.raw_type == e2->exthdr.raw_type && + e1->exthdr.flags == e2->exthdr.flags; +} + +static void exthdr_expr_clone(struct expr *new, const struct expr *expr) +{ + new->exthdr.desc = expr->exthdr.desc; + new->exthdr.tmpl = expr->exthdr.tmpl; + new->exthdr.offset = expr->exthdr.offset; + new->exthdr.op = expr->exthdr.op; + new->exthdr.flags = expr->exthdr.flags; + new->exthdr.raw_type = expr->exthdr.raw_type; +} + +#define NFTNL_UDATA_EXTHDR_DESC 0 +#define NFTNL_UDATA_EXTHDR_TYPE 1 +#define NFTNL_UDATA_EXTHDR_OP 2 +#define NFTNL_UDATA_EXTHDR_MAX 3 + +static int exthdr_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_EXTHDR_DESC: + case NFTNL_UDATA_EXTHDR_TYPE: + case NFTNL_UDATA_EXTHDR_OP: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *exthdr_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_EXTHDR_MAX + 1] = {}; + enum nft_exthdr_op op = NFT_EXTHDR_OP_IPV6; + const struct exthdr_desc *desc; + unsigned int type; + uint32_t desc_id; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + exthdr_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_EXTHDR_DESC] || + !ud[NFTNL_UDATA_EXTHDR_TYPE]) + return NULL; + + if (ud[NFTNL_UDATA_EXTHDR_OP]) + op = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_OP]); + + desc_id = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_DESC]); + type = nftnl_udata_get_u32(ud[NFTNL_UDATA_EXTHDR_TYPE]); + + switch (op) { + case NFT_EXTHDR_OP_IPV6: + desc = exthdr_find_desc(desc_id); + + return exthdr_expr_alloc(&internal_location, desc, type); + case NFT_EXTHDR_OP_TCPOPT: + return tcpopt_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_IPV4: + return ipopt_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_SCTP: + return sctp_chunk_expr_alloc(&internal_location, + desc_id, type); + case NFT_EXTHDR_OP_DCCP: + return dccpopt_expr_alloc(&internal_location, type); + case __NFT_EXTHDR_OP_MAX: + return NULL; + } + + return NULL; +} + +static unsigned int expr_exthdr_type(const struct exthdr_desc *desc, + const struct proto_hdr_template *tmpl) +{ + return (unsigned int)(tmpl - &desc->templates[0]); +} + +static int exthdr_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + const struct proto_hdr_template *tmpl = expr->exthdr.tmpl; + const struct exthdr_desc *desc = expr->exthdr.desc; + unsigned int type = expr_exthdr_type(desc, tmpl); + enum nft_exthdr_op op = expr->exthdr.op; + + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_TYPE, type); + switch (op) { + case NFT_EXTHDR_OP_IPV6: + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, desc->id); + break; + case NFT_EXTHDR_OP_TCPOPT: + case NFT_EXTHDR_OP_IPV4: + case NFT_EXTHDR_OP_SCTP: + case NFT_EXTHDR_OP_DCCP: + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_OP, op); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_EXTHDR_DESC, expr->exthdr.raw_type); + break; + default: + return -1; + } + + return 0; +} + +const struct expr_ops exthdr_expr_ops = { + .type = EXPR_EXTHDR, + .name = "exthdr", + .print = exthdr_expr_print, + .json = exthdr_expr_json, + .cmp = exthdr_expr_cmp, + .clone = exthdr_expr_clone, + .build_udata = exthdr_expr_build_udata, + .parse_udata = exthdr_expr_parse_udata, +}; + +static const struct proto_hdr_template exthdr_unknown_template = + PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0); + +struct expr *exthdr_expr_alloc(const struct location *loc, + const struct exthdr_desc *desc, + uint8_t type) +{ + const struct proto_hdr_template *tmpl; + struct expr *expr; + + if (desc != NULL) + tmpl = &desc->templates[type]; + else + tmpl = &exthdr_unknown_template; + + expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, tmpl->len); + expr->exthdr.desc = desc; + expr->exthdr.raw_type = desc ? desc->type : 0; + expr->exthdr.tmpl = tmpl; + expr->exthdr.offset = tmpl->offset; + return expr; +} + +static void exthdr_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + expr_print(stmt->exthdr.expr, octx); + nft_print(octx, " set "); + expr_print(stmt->exthdr.val, octx); +} + +static void exthdr_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->exthdr.expr); + expr_free(stmt->exthdr.val); +} + +static const struct stmt_ops exthdr_stmt_ops = { + .type = STMT_EXTHDR, + .name = "exthdr", + .print = exthdr_stmt_print, + .json = exthdr_stmt_json, + .destroy = exthdr_stmt_destroy, +}; + +struct stmt *exthdr_stmt_alloc(const struct location *loc, + struct expr *expr, struct expr *val) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &exthdr_stmt_ops); + stmt->exthdr.expr = expr; + stmt->exthdr.val = val; + return stmt; +} + +static const struct exthdr_desc *exthdr_protocols[UINT8_MAX + 1] = { + [IPPROTO_HOPOPTS] = &exthdr_hbh, + [IPPROTO_ROUTING] = &exthdr_rt, + [IPPROTO_FRAGMENT] = &exthdr_frag, + [IPPROTO_DSTOPTS] = &exthdr_dst, + [IPPROTO_MH] = &exthdr_mh, +}; + +const struct exthdr_desc *exthdr_find_proto(uint8_t proto) +{ + assert(exthdr_protocols[proto]); + + return exthdr_protocols[proto]; +} + +static const struct proto_hdr_template * +exthdr_rt_find(struct expr *expr, const struct exthdr_desc *desc) +{ + const struct proto_hdr_template *tmpl; + unsigned int i; + + for (i = 0; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset == expr->exthdr.offset && + tmpl->len == expr->len) { + expr->exthdr.desc = desc; + return tmpl; + } + } + + return NULL; +} + +void exthdr_init_raw(struct expr *expr, uint8_t type, + unsigned int offset, unsigned int len, + enum nft_exthdr_op op, uint32_t flags) +{ + const struct proto_hdr_template *tmpl = &exthdr_unknown_template; + unsigned int i; + + assert(expr->etype == EXPR_EXTHDR); + expr->exthdr.raw_type = type; + + if (op == NFT_EXTHDR_OP_TCPOPT) + return tcpopt_init_raw(expr, type, offset, len, flags); + if (op == NFT_EXTHDR_OP_IPV4) + return ipopt_init_raw(expr, type, offset, len, flags, true); + if (op == NFT_EXTHDR_OP_SCTP) + return sctp_chunk_init_raw(expr, type, offset, len, flags); + if (op == NFT_EXTHDR_OP_DCCP) + return dccpopt_init_raw(expr, type, offset, len); + + expr->len = len; + expr->exthdr.flags = flags; + expr->exthdr.offset = offset; + expr->exthdr.desc = NULL; + + expr->exthdr.desc = exthdr_protocols[type]; + + if (expr->exthdr.desc == NULL) + goto out; + + for (i = 0; i < array_size(expr->exthdr.desc->templates); i++) { + tmpl = &expr->exthdr.desc->templates[i]; + if (tmpl->offset == offset && tmpl->len == len) + goto out; + } + + if (expr->exthdr.desc == &exthdr_rt) { + tmpl = exthdr_rt_find(expr, &exthdr_rt4); + if (tmpl) + goto out; + tmpl = exthdr_rt_find(expr, &exthdr_rt0); + if (tmpl) + goto out; + tmpl = exthdr_rt_find(expr, &exthdr_rt2); + if (tmpl) + goto out; + } + + tmpl = &exthdr_unknown_template; + out: + expr->exthdr.tmpl = tmpl; + if (flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + else + datatype_set(expr, tmpl->dtype); +} + +static unsigned int mask_length(const struct expr *mask) +{ + unsigned long off = mpz_scan1(mask->value, 0); + + return mpz_scan0(mask->value, off + 1); +} + +bool exthdr_find_template(struct expr *expr, const struct expr *mask, unsigned int *shift) +{ + unsigned int off, mask_offset, mask_len; + bool found; + + mask_offset = mpz_scan1(mask->value, 0); + mask_len = mask_length(mask); + + off = expr->exthdr.offset; + off += round_up(mask->len, BITS_PER_BYTE) - mask_len; + + /* Handle ip options after the offset and mask have been calculated. */ + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_IPV4: + found = ipopt_find_template(expr, off, mask_len - mask_offset); + break; + case NFT_EXTHDR_OP_TCPOPT: + found = tcpopt_find_template(expr, off, mask_len - mask_offset); + break; + case NFT_EXTHDR_OP_IPV6: + exthdr_init_raw(expr, expr->exthdr.raw_type, + off, mask_len - mask_offset, expr->exthdr.op, 0); + + /* still failed to find a template... Bug. */ + if (expr->exthdr.tmpl == &exthdr_unknown_template) + return false; + found = true; + break; + default: + found = false; + break; + } + + if (found) + *shift = mask_offset; + + return found; +} + +#define HDR_TEMPLATE(__name, __dtype, __type, __member) \ + PROTO_HDR_TEMPLATE(__name, __dtype, \ + BYTEORDER_BIG_ENDIAN, \ + 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", + .id = EXTHDR_DESC_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 = { + .name = "rt2", + .id = EXTHDR_DESC_RT2, + .type = IPPROTO_ROUTING, + .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 = { + .name = "rt0", + .id = EXTHDR_DESC_RT0, + .type = IPPROTO_ROUTING, + .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[1], &ip6addr_type), + // ... + }, +}; + +#define RT4_FIELD(__name, __member, __dtype) \ + HDR_TEMPLATE(__name, __dtype, struct ip6_rt4, __member) + +const struct exthdr_desc exthdr_rt4 = { + .name = "srh", + .id = EXTHDR_DESC_SRH, + .type = IPPROTO_ROUTING, + .templates = { + [RT4HDR_LASTENT] = RT4_FIELD("last-entry", ip6r4_last_entry, &integer_type), + [RT4HDR_FLAGS] = RT4_FIELD("flags", ip6r4_flags, &integer_type), + [RT4HDR_TAG] = RT4_FIELD("tag", ip6r4_tag, &integer_type), + [RT4HDR_SID_1] = RT4_FIELD("sid[1]", ip6r4_segments[0], &ip6addr_type), + [RT4HDR_SID_1 + 1] = RT4_FIELD("sid[2]", ip6r4_segments[1], &ip6addr_type), + // ... + }, +}; + + +#define RT_FIELD(__name, __member, __dtype) \ + HDR_TEMPLATE(__name, __dtype, struct ip6_rthdr, __member) + +const struct exthdr_desc exthdr_rt = { + .name = "rt", + .id = EXTHDR_DESC_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", + .id = EXTHDR_DESC_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] = PROTO_HDR_TEMPLATE("frag-off", &integer_type, + BYTEORDER_BIG_ENDIAN, + 16, 13), + [FRAGHDR_RESERVED2] = PROTO_HDR_TEMPLATE("reserved2", &integer_type, + BYTEORDER_BIG_ENDIAN, + 29, 2), + [FRAGHDR_MFRAGS] = PROTO_HDR_TEMPLATE("more-fragments", &integer_type, + BYTEORDER_BIG_ENDIAN, + 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", + .id = EXTHDR_DESC_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 = { + .base = BASE_DECIMAL, + .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 + }, +}; + +const struct datatype mh_type_type = { + .type = TYPE_MH_TYPE, + .name = "mh_type", + .desc = "Mobility Header Type", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &mh_type_tbl, +}; + +const struct exthdr_desc exthdr_mh = { + .name = "mh", + .id = EXTHDR_DESC_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/fib.c b/src/fib.c new file mode 100644 index 0000000..e95271c --- /dev/null +++ b/src/fib.c @@ -0,0 +1,202 @@ +/* + * FIB expression. + * + * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <erec.h> +#include <expression.h> +#include <datatype.h> +#include <gmputil.h> +#include <utils.h> +#include <fib.h> + +#include <linux/rtnetlink.h> +#include <net/if.h> + +static const char *fib_result[NFT_FIB_RESULT_MAX + 1] = { + [NFT_FIB_RESULT_OIF] = "oif", + [NFT_FIB_RESULT_OIFNAME] = "oifname", + [NFT_FIB_RESULT_ADDRTYPE] = "type", +}; + +static const struct symbol_table addrtype_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("unspec", RTN_UNSPEC), + SYMBOL("unicast", RTN_UNICAST), + SYMBOL("local", RTN_LOCAL), + SYMBOL("broadcast", RTN_BROADCAST), + SYMBOL("anycast", RTN_ANYCAST), + SYMBOL("multicast", RTN_MULTICAST), + SYMBOL("blackhole", RTN_BLACKHOLE), + SYMBOL("unreachable", RTN_UNREACHABLE), + SYMBOL("prohibit", RTN_PROHIBIT), + SYMBOL_LIST_END + } +}; + +const struct datatype fib_addr_type = { + .type = TYPE_FIB_ADDR, + .name = "fib_addrtype", + .desc = "fib address type", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &addrtype_tbl, +}; + +const char *fib_result_str(enum nft_fib_result result) +{ + if (result <= NFT_FIB_RESULT_MAX) + return fib_result[result]; + + return "unknown"; +} + +static void __fib_expr_print_f(unsigned int *flags, unsigned int f, + const char *s, struct output_ctx *octx) +{ + if ((*flags & f) == 0) + return; + + nft_print(octx, "%s", s); + *flags &= ~f; + if (*flags) + nft_print(octx, " . "); +} + +static void fib_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; + + nft_print(octx, "fib "); + __fib_expr_print_f(&flags, NFTA_FIB_F_SADDR, "saddr", octx); + __fib_expr_print_f(&flags, NFTA_FIB_F_DADDR, "daddr", octx); + __fib_expr_print_f(&flags, NFTA_FIB_F_MARK, "mark", octx); + __fib_expr_print_f(&flags, NFTA_FIB_F_IIF, "iif", octx); + __fib_expr_print_f(&flags, NFTA_FIB_F_OIF, "oif", octx); + + if (flags) + nft_print(octx, "0x%x", flags); + + nft_print(octx, " %s", fib_result_str(expr->fib.result)); +} + +static bool fib_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->fib.result == e2->fib.result && + e1->fib.flags == e2->fib.flags; +} + +static void fib_expr_clone(struct expr *new, const struct expr *expr) +{ + new->fib.result = expr->fib.result; + new->fib.flags= expr->fib.flags; +} + +#define NFTNL_UDATA_FIB_RESULT 0 +#define NFTNL_UDATA_FIB_FLAGS 1 +#define NFTNL_UDATA_FIB_MAX 2 + +static int fib_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_FIB_RESULT, expr->fib.result); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_FIB_FLAGS, expr->fib.flags); + + return 0; +} + +static int fib_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_FIB_RESULT: + case NFTNL_UDATA_FIB_FLAGS: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *fib_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_FIB_MAX + 1] = {}; + uint32_t flags, result; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + fib_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_FIB_RESULT] || + !ud[NFTNL_UDATA_FIB_FLAGS]) + return NULL; + + result = nftnl_udata_get_u32(ud[NFTNL_UDATA_FIB_RESULT]); + flags = nftnl_udata_get_u32(ud[NFTNL_UDATA_FIB_FLAGS]); + + return fib_expr_alloc(&internal_location, flags, result); +} + +const struct expr_ops fib_expr_ops = { + .type = EXPR_FIB, + .name = "fib", + .print = fib_expr_print, + .json = fib_expr_json, + .cmp = fib_expr_cmp, + .clone = fib_expr_clone, + .parse_udata = fib_expr_parse_udata, + .build_udata = fib_expr_build_udata, +}; + +struct expr *fib_expr_alloc(const struct location *loc, + unsigned int flags, unsigned int result) +{ + const struct datatype *type; + unsigned int len = 4 * BITS_PER_BYTE; + struct expr *expr; + + switch (result) { + case NFT_FIB_RESULT_OIF: + type = &ifindex_type; + break; + case NFT_FIB_RESULT_OIFNAME: + type = &string_type; + len = IFNAMSIZ * BITS_PER_BYTE; + break; + case NFT_FIB_RESULT_ADDRTYPE: + type = &fib_addr_type; + break; + default: + BUG("Unknown result %d\n", result); + } + + if (flags & NFTA_FIB_F_PRESENT) + type = &boolean_type; + + expr = expr_alloc(loc, EXPR_FIB, type, + BYTEORDER_HOST_ENDIAN, len); + + expr->fib.result = result; + expr->fib.flags = flags; + + return expr; +} diff --git a/src/gmputil.c b/src/gmputil.c new file mode 100644 index 0000000..cb26b55 --- /dev/null +++ b/src/gmputil.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> + +#include <nftables.h> +#include <datatype.h> +#include <utils.h> + +void mpz_bitmask(mpz_t rop, unsigned int width) +{ + mpz_set_ui(rop, 0); + mpz_setbit(rop, width); + mpz_sub_ui(rop, rop, 1); +} + +void mpz_init_bitmask(mpz_t rop, unsigned int width) +{ + mpz_init2(rop, width); + mpz_bitmask(rop, width); +} + +void mpz_prefixmask(mpz_t rop, unsigned int width, unsigned int prefix_len) +{ + mpz_bitmask(rop, prefix_len); + mpz_lshift_ui(rop, width - prefix_len); +} + +void mpz_lshift_ui(mpz_t rop, unsigned int n) +{ + mpz_mul_2exp(rop, rop, n); +} + +void mpz_rshift_ui(mpz_t rop, unsigned int n) +{ + mpz_tdiv_q_2exp(rop, rop, n); +} + +#define mpz_get_type(type, endian, op) \ +({ \ + type ret = 0; \ + size_t cnt; \ + mpz_export(&ret, &cnt, MPZ_LSWF, sizeof(ret), endian, 0, op); \ + assert(cnt <= 1); \ + ret; \ + }) + +uint64_t mpz_get_uint64(const mpz_t op) +{ + return mpz_get_type(uint64_t, MPZ_HOST_ENDIAN, op); +} + +uint32_t mpz_get_uint32(const mpz_t op) +{ + return mpz_get_type(uint32_t, MPZ_HOST_ENDIAN, op); +} + +uint16_t mpz_get_uint16(const mpz_t op) +{ + return mpz_get_type(uint16_t, MPZ_HOST_ENDIAN, op); +} + +uint8_t mpz_get_uint8(const mpz_t op) +{ + return mpz_get_type(uint8_t, MPZ_HOST_ENDIAN, op); +} + +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_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_HWO; + endian = MPZ_HOST_ENDIAN; + break; + } + + memset(data, 0, len); + mpz_export(data, NULL, order, 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_HWO; + 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); +} + +#ifndef HAVE_LIBGMP +/* mini-gmp doesn't have a gmp_printf so we use our own minimal + * variant here which is able to format a single mpz_t. + */ +int mpz_vfprintf(FILE *fp, const char *f, va_list args) +{ + const mpz_t *value = va_arg(args, const mpz_t *); + int n = 0; + + while (*f) { + if (*f != '%') { + if (fputc(*f, fp) != *f) + return -1; + + ++n; + } else { + unsigned long prec = 0; + int base; + size_t len; + char *str; + bool ok; + + if (*++f == '.') + prec = strtoul(++f, (char**)&f, 10); + + if (*f++ != 'Z') + return -1; + + if (*f == 'u') + base = 10; + else if (*f == 'x') + base = 16; + else + return -1; + + len = mpz_sizeinbase(*value, base); + while (prec-- > len) { + if (fputc('0', fp) != '0') + return -1; + + ++n; + } + + str = mpz_get_str(NULL, base, *value); + ok = str && fwrite(str, 1, len, fp) == len; + free(str); + + if (!ok) + return -1; + + n += len; + } + ++f; + } + return n; +} +#endif diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000..1c8c00a --- /dev/null +++ b/src/hash.c @@ -0,0 +1,165 @@ +/* + * Hash expression definitions. + * + * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <expression.h> +#include <datatype.h> +#include <gmputil.h> +#include <hash.h> +#include <utils.h> + +static void hash_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + switch (expr->hash.type) { + case NFT_HASH_SYM: + nft_print(octx, "symhash"); + break; + case NFT_HASH_JENKINS: + default: + nft_print(octx, "jhash "); + expr_print(expr->hash.expr, octx); + } + + nft_print(octx, " mod %u", expr->hash.mod); + if (expr->hash.seed_set) + nft_print(octx, " seed 0x%x", expr->hash.seed); + if (expr->hash.offset) + nft_print(octx, " offset %u", expr->hash.offset); +} + +static bool hash_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return (!e1->hash.expr || + expr_cmp(e1->hash.expr, e2->hash.expr)) && + e1->hash.mod == e2->hash.mod && + e1->hash.seed_set == e2->hash.seed_set && + e1->hash.seed == e2->hash.seed && + e1->hash.offset == e2->hash.offset && + e1->hash.type == e2->hash.type; +} + +static void hash_expr_clone(struct expr *new, const struct expr *expr) +{ + if (expr->hash.expr) + new->hash.expr = expr_clone(expr->hash.expr); + new->hash.mod = expr->hash.mod; + new->hash.seed_set = expr->hash.seed_set; + new->hash.seed = expr->hash.seed; + new->hash.offset = expr->hash.offset; + new->hash.type = expr->hash.type; +} + +static void hash_expr_destroy(struct expr *expr) +{ + expr_free(expr->hash.expr); +} + +#define NFTNL_UDATA_HASH_TYPE 0 +#define NFTNL_UDATA_HASH_OFFSET 1 +#define NFTNL_UDATA_HASH_MOD 2 +#define NFTNL_UDATA_HASH_SEED 3 +#define NFTNL_UDATA_HASH_SEED_SET 4 +#define NFTNL_UDATA_HASH_MAX 5 + +static int hash_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_HASH_TYPE, expr->hash.type); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_HASH_OFFSET, expr->hash.offset); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_HASH_MOD, expr->hash.mod); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_HASH_SEED, expr->hash.seed); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_HASH_SEED_SET, expr->hash.seed_set); + + return 0; +} + +static int hash_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_HASH_TYPE: + case NFTNL_UDATA_HASH_OFFSET: + case NFTNL_UDATA_HASH_SEED: + case NFTNL_UDATA_HASH_SEED_SET: + case NFTNL_UDATA_HASH_MOD: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *hash_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_HASH_MAX + 1] = {}; + uint32_t type, seed, seed_set, mod, offset; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + hash_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_HASH_TYPE] || + !ud[NFTNL_UDATA_HASH_OFFSET] || + !ud[NFTNL_UDATA_HASH_SEED] || + !ud[NFTNL_UDATA_HASH_MOD] || + !ud[NFTNL_UDATA_HASH_SEED_SET]) + return NULL; + + type = nftnl_udata_get_u32(ud[NFTNL_UDATA_HASH_TYPE]); + offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_HASH_OFFSET]); + seed = nftnl_udata_get_u32(ud[NFTNL_UDATA_HASH_SEED]); + seed_set = nftnl_udata_get_u32(ud[NFTNL_UDATA_HASH_SEED_SET]); + mod = nftnl_udata_get_u32(ud[NFTNL_UDATA_HASH_MOD]); + + return hash_expr_alloc(&internal_location, mod, seed_set, seed, + offset, type); +} + +const struct expr_ops hash_expr_ops = { + .type = EXPR_HASH, + .name = "hash", + .print = hash_expr_print, + .json = hash_expr_json, + .cmp = hash_expr_cmp, + .clone = hash_expr_clone, + .destroy = hash_expr_destroy, + .parse_udata = hash_expr_parse_udata, + .build_udata = hash_expr_build_udata, +}; + +struct expr *hash_expr_alloc(const struct location *loc, + uint32_t mod, + bool seed_set, uint32_t seed, + uint32_t offset, + enum nft_hash_types type) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_HASH, &integer_type, + BYTEORDER_HOST_ENDIAN, 4 * BITS_PER_BYTE); + expr->hash.mod = mod; + expr->hash.seed_set = seed_set; + expr->hash.seed = seed; + expr->hash.offset = offset; + expr->hash.type = type; + + return expr; +} diff --git a/src/iface.c b/src/iface.c new file mode 100644 index 0000000..428acaa --- /dev/null +++ b/src/iface.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <stdio.h> +#include <net/if.h> +#include <time.h> +#include <errno.h> + +#include <libmnl/libmnl.h> +#include <linux/rtnetlink.h> + +#include <nftables.h> +#include <list.h> +#include <netlink.h> +#include <iface.h> + +static LIST_HEAD(iface_list); +static bool iface_cache_init; + +static int data_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, IFLA_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case IFLA_IFNAME: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + netlink_abi_error(); + break; + default: + return MNL_CB_OK; + } + tb[type] = attr; + return MNL_CB_OK; +} + +static int data_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[IFLA_MAX + 1] = {}; + struct ifinfomsg *ifm = mnl_nlmsg_get_payload(nlh); + struct iface *iface; + + iface = xmalloc(sizeof(struct iface)); + iface->ifindex = ifm->ifi_index; + mnl_attr_parse(nlh, sizeof(*ifm), data_attr_cb, tb); + snprintf(iface->name, IFNAMSIZ, "%s", mnl_attr_get_str(tb[IFLA_IFNAME])); + list_add(&iface->list, &iface_list); + + return MNL_CB_OK; +} + +static int iface_mnl_talk(struct mnl_socket *nl, uint32_t portid) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + bool eintr = false; + uint32_t seq; + int ret; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = RTM_GETLINK; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_seq = seq = time(NULL); + rt = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_PACKET; + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + return -1; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, seq, portid, data_cb, NULL); + if (ret == 0) + break; + if (ret < 0) { + if (errno != EINTR) + return ret; + + /* process all pending messages before reporting EINTR */ + eintr = true; + } + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + } + + if (eintr) { + ret = -1; + errno = EINTR; + } + + return ret; +} + +void iface_cache_update(void) +{ + struct mnl_socket *nl; + uint32_t portid; + int ret; + + nl = mnl_socket_open(NETLINK_ROUTE); + if (nl == NULL) + netlink_init_error(); + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + netlink_init_error(); + + portid = mnl_socket_get_portid(nl); + + do { + ret = iface_mnl_talk(nl, portid); + } while (ret < 0 && errno == EINTR); + + if (ret == -1) + netlink_init_error(); + + mnl_socket_close(nl); + + iface_cache_init = true; +} + +void iface_cache_release(void) +{ + struct iface *iface, *next; + + if (!iface_cache_init) + return; + + list_for_each_entry_safe(iface, next, &iface_list, list) { + list_del(&iface->list); + free(iface); + } + iface_cache_init = false; +} + +unsigned int nft_if_nametoindex(const char *name) +{ + struct iface *iface; + + if (!iface_cache_init) + iface_cache_update(); + + list_for_each_entry(iface, &iface_list, list) { + if (strncmp(name, iface->name, IFNAMSIZ) == 0) + return iface->ifindex; + } + return 0; +} + +char *nft_if_indextoname(unsigned int ifindex, char *name) +{ + struct iface *iface; + + if (!iface_cache_init) + iface_cache_update(); + + list_for_each_entry(iface, &iface_list, list) { + if (iface->ifindex == ifindex) { + snprintf(name, IFNAMSIZ, "%s", iface->name); + return name; + } + } + return NULL; +} diff --git a/src/intervals.c b/src/intervals.c new file mode 100644 index 0000000..85de019 --- /dev/null +++ b/src/intervals.c @@ -0,0 +1,750 @@ +/* + * Copyright (c) 2022 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <expression.h> +#include <intervals.h> +#include <rule.h> + +static void setelem_expr_to_range(struct expr *expr) +{ + unsigned char data[sizeof(struct in6_addr) * BITS_PER_BYTE]; + struct expr *key, *value; + mpz_t rop; + + assert(expr->etype == EXPR_SET_ELEM); + + switch (expr->key->etype) { + case EXPR_SET_ELEM_CATCHALL: + case EXPR_RANGE: + break; + case EXPR_PREFIX: + mpz_init(rop); + mpz_bitmask(rop, expr->key->len - expr->key->prefix_len); + if (expr_basetype(expr)->type == TYPE_STRING) + mpz_switch_byteorder(expr->key->prefix->value, expr->len / BITS_PER_BYTE); + + mpz_ior(rop, rop, expr->key->prefix->value); + mpz_export_data(data, rop, expr->key->prefix->byteorder, + expr->key->prefix->len / BITS_PER_BYTE); + mpz_clear(rop); + value = constant_expr_alloc(&expr->location, + expr->key->prefix->dtype, + expr->key->prefix->byteorder, + expr->key->prefix->len, data); + key = range_expr_alloc(&expr->location, + expr_get(expr->key->prefix), + value); + expr_free(expr->key); + expr->key = key; + break; + case EXPR_VALUE: + if (expr_basetype(expr)->type == TYPE_STRING) + mpz_switch_byteorder(expr->key->value, expr->len / BITS_PER_BYTE); + + key = range_expr_alloc(&expr->location, + expr_clone(expr->key), + expr_get(expr->key)); + expr_free(expr->key); + expr->key = key; + break; + default: + BUG("unhandled key type %d\n", expr->key->etype); + } +} + +struct set_automerge_ctx { + struct set *set; + struct expr *init; + struct expr *purge; + unsigned int debug_mask; +}; + +static void purge_elem(struct set_automerge_ctx *ctx, struct expr *i) +{ + if (ctx->debug_mask & NFT_DEBUG_SEGTREE) { + pr_gmp_debug("remove: [%Zx-%Zx]\n", + i->key->left->value, + i->key->right->value); + } + list_move_tail(&i->list, &ctx->purge->expressions); +} + +static void remove_overlapping_range(struct set_automerge_ctx *ctx, + struct expr *prev, struct expr *i) +{ + if (i->flags & EXPR_F_KERNEL) { + purge_elem(ctx, i); + return; + } + list_del(&i->list); + expr_free(i); + ctx->init->size--; +} + +struct range { + mpz_t low; + mpz_t high; +}; + +static bool merge_ranges(struct set_automerge_ctx *ctx, + struct expr *prev, struct expr *i, + struct range *prev_range, struct range *range) +{ + if (prev->flags & EXPR_F_KERNEL) { + purge_elem(ctx, prev); + expr_free(i->key->left); + i->key->left = expr_get(prev->key->left); + mpz_set(prev_range->high, range->high); + return true; + } else if (i->flags & EXPR_F_KERNEL) { + purge_elem(ctx, i); + expr_free(prev->key->right); + prev->key->right = expr_get(i->key->right); + mpz_set(prev_range->high, range->high); + } else { + expr_free(prev->key->right); + prev->key->right = expr_get(i->key->right); + mpz_set(prev_range->high, range->high); + list_del(&i->list); + expr_free(i); + ctx->init->size--; + } + return false; +} + +static void set_sort_splice(struct expr *init, struct set *set) +{ + struct set *existing_set = set->existing_set; + + set_to_range(init); + list_expr_sort(&init->expressions); + + if (!existing_set) + return; + + if (existing_set->init) { + set_to_range(existing_set->init); + list_splice_sorted(&existing_set->init->expressions, + &init->expressions); + init_list_head(&existing_set->init->expressions); + } else { + existing_set->init = set_expr_alloc(&internal_location, set); + } +} + +static void setelem_automerge(struct set_automerge_ctx *ctx) +{ + struct expr *i, *next, *prev = NULL; + struct range range, prev_range; + mpz_t rop; + + mpz_init(prev_range.low); + mpz_init(prev_range.high); + mpz_init(range.low); + mpz_init(range.high); + mpz_init(rop); + + list_for_each_entry_safe(i, next, &ctx->init->expressions, list) { + if (i->key->etype == EXPR_SET_ELEM_CATCHALL) + continue; + + range_expr_value_low(range.low, i); + range_expr_value_high(range.high, i); + + if (!prev) { + prev = i; + mpz_set(prev_range.low, range.low); + mpz_set(prev_range.high, range.high); + continue; + } + + if (mpz_cmp(prev_range.low, range.low) <= 0 && + mpz_cmp(prev_range.high, range.high) >= 0) { + remove_overlapping_range(ctx, prev, i); + continue; + } else if (mpz_cmp(range.low, prev_range.high) <= 0) { + if (merge_ranges(ctx, prev, i, &prev_range, &range)) + prev = i; + continue; + } else if (ctx->set->automerge) { + mpz_sub(rop, range.low, prev_range.high); + /* two contiguous ranges */ + if (mpz_cmp_ui(rop, 1) == 0) { + if (merge_ranges(ctx, prev, i, &prev_range, &range)) + prev = i; + continue; + } + } + + prev = i; + mpz_set(prev_range.low, range.low); + mpz_set(prev_range.high, range.high); + } + + mpz_clear(prev_range.low); + mpz_clear(prev_range.high); + mpz_clear(range.low); + mpz_clear(range.high); + mpz_clear(rop); +} + +static struct expr *interval_expr_key(struct expr *i) +{ + struct expr *elem; + + switch (i->etype) { + case EXPR_MAPPING: + elem = i->left; + break; + case EXPR_SET_ELEM: + elem = i; + break; + default: + BUG("unhandled expression type %d\n", i->etype); + return NULL; + } + + return elem; +} + +void set_to_range(struct expr *init) +{ + struct expr *i, *elem; + + list_for_each_entry(i, &init->expressions, list) { + elem = interval_expr_key(i); + setelem_expr_to_range(elem); + } +} + +int set_automerge(struct list_head *msgs, struct cmd *cmd, struct set *set, + struct expr *init, unsigned int debug_mask) +{ + struct set *existing_set = set->existing_set; + struct set_automerge_ctx ctx = { + .set = set, + .init = init, + .debug_mask = debug_mask, + }; + struct expr *i, *next, *clone; + struct cmd *purge_cmd; + struct handle h = {}; + + if (set->flags & NFT_SET_MAP) { + set_to_range(init); + list_expr_sort(&init->expressions); + return 0; + } + + set_sort_splice(init, set); + + ctx.purge = set_expr_alloc(&internal_location, set); + + setelem_automerge(&ctx); + + list_for_each_entry_safe(i, next, &init->expressions, list) { + if (i->flags & EXPR_F_KERNEL) { + list_move_tail(&i->list, &existing_set->init->expressions); + } else if (existing_set) { + if (debug_mask & NFT_DEBUG_SEGTREE) { + pr_gmp_debug("add: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + } + clone = expr_clone(i); + clone->flags |= EXPR_F_KERNEL; + list_add_tail(&clone->list, &existing_set->init->expressions); + } + } + + if (list_empty(&ctx.purge->expressions)) { + expr_free(ctx.purge); + return 0; + } + + handle_merge(&h, &set->handle); + purge_cmd = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &h, &init->location, ctx.purge); + purge_cmd->elem.set = set_get(set); + list_add_tail(&purge_cmd->list, &cmd->list); + + return 0; +} + +static void remove_elem(struct expr *prev, struct set *set, struct expr *purge) +{ + struct expr *clone; + + if (prev->flags & EXPR_F_KERNEL) { + clone = expr_clone(prev); + list_move_tail(&clone->list, &purge->expressions); + } +} + +static void __adjust_elem_left(struct set *set, struct expr *prev, struct expr *i) +{ + prev->flags &= ~EXPR_F_KERNEL; + expr_free(prev->key->left); + prev->key->left = expr_get(i->key->right); + mpz_add_ui(prev->key->left->value, prev->key->left->value, 1); + list_move(&prev->list, &set->existing_set->init->expressions); +} + +static void adjust_elem_left(struct set *set, struct expr *prev, struct expr *i, + struct expr *purge) +{ + remove_elem(prev, set, purge); + __adjust_elem_left(set, prev, i); + + list_del(&i->list); + expr_free(i); +} + +static void __adjust_elem_right(struct set *set, struct expr *prev, struct expr *i) +{ + prev->flags &= ~EXPR_F_KERNEL; + expr_free(prev->key->right); + prev->key->right = expr_get(i->key->left); + mpz_sub_ui(prev->key->right->value, prev->key->right->value, 1); + list_move(&prev->list, &set->existing_set->init->expressions); +} + +static void adjust_elem_right(struct set *set, struct expr *prev, struct expr *i, + struct expr *purge) +{ + remove_elem(prev, set, purge); + __adjust_elem_right(set, prev, i); + + list_del(&i->list); + expr_free(i); +} + +static void split_range(struct set *set, struct expr *prev, struct expr *i, + struct expr *purge) +{ + struct expr *clone; + + if (prev->flags & EXPR_F_KERNEL) { + clone = expr_clone(prev); + list_move_tail(&clone->list, &purge->expressions); + } + + prev->flags &= ~EXPR_F_KERNEL; + clone = expr_clone(prev); + expr_free(clone->key->left); + clone->key->left = expr_get(i->key->right); + mpz_add_ui(clone->key->left->value, i->key->right->value, 1); + list_add_tail(&clone->list, &set->existing_set->init->expressions); + + expr_free(prev->key->right); + prev->key->right = expr_get(i->key->left); + mpz_sub_ui(prev->key->right->value, i->key->left->value, 1); + list_move(&prev->list, &set->existing_set->init->expressions); + + list_del(&i->list); + expr_free(i); +} + +static int setelem_adjust(struct set *set, struct expr *purge, + struct range *prev_range, struct range *range, + struct expr *prev, struct expr *i) +{ + if (mpz_cmp(prev_range->low, range->low) == 0 && + mpz_cmp(prev_range->high, range->high) > 0) { + if (i->flags & EXPR_F_REMOVE) + adjust_elem_left(set, prev, i, purge); + } else if (mpz_cmp(prev_range->low, range->low) < 0 && + mpz_cmp(prev_range->high, range->high) == 0) { + if (i->flags & EXPR_F_REMOVE) + adjust_elem_right(set, prev, i, purge); + } else if (mpz_cmp(prev_range->low, range->low) < 0 && + mpz_cmp(prev_range->high, range->high) > 0) { + if (i->flags & EXPR_F_REMOVE) + split_range(set, prev, i, purge); + } else { + return -1; + } + + return 0; +} + +static int setelem_delete(struct list_head *msgs, struct set *set, + struct expr *purge, struct expr *elems, + unsigned int debug_mask) +{ + struct expr *i, *next, *prev = NULL; + struct range range, prev_range; + int err = 0; + mpz_t rop; + + mpz_init(prev_range.low); + mpz_init(prev_range.high); + mpz_init(range.low); + mpz_init(range.high); + mpz_init(rop); + + list_for_each_entry_safe(i, next, &elems->expressions, list) { + if (i->key->etype == EXPR_SET_ELEM_CATCHALL) + continue; + + range_expr_value_low(range.low, i); + range_expr_value_high(range.high, i); + + if (!prev && i->flags & EXPR_F_REMOVE) { + expr_error(msgs, i, "element does not exist"); + err = -1; + goto err; + } + + if (!(i->flags & EXPR_F_REMOVE)) { + prev = i; + mpz_set(prev_range.low, range.low); + mpz_set(prev_range.high, range.high); + continue; + } + + if (mpz_cmp(prev_range.low, range.low) == 0 && + mpz_cmp(prev_range.high, range.high) == 0) { + if (i->flags & EXPR_F_REMOVE) { + if (prev->flags & EXPR_F_KERNEL) + list_move_tail(&prev->list, &purge->expressions); + + list_del(&i->list); + expr_free(i); + } + } else if (set->automerge) { + if (setelem_adjust(set, purge, &prev_range, &range, prev, i) < 0) { + expr_error(msgs, i, "element does not exist"); + err = -1; + goto err; + } + } else if (i->flags & EXPR_F_REMOVE) { + expr_error(msgs, i, "element does not exist"); + err = -1; + goto err; + } + prev = NULL; + } +err: + mpz_clear(prev_range.low); + mpz_clear(prev_range.high); + mpz_clear(range.low); + mpz_clear(range.high); + mpz_clear(rop); + + return err; +} + +static void automerge_delete(struct list_head *msgs, struct set *set, + struct expr *init, unsigned int debug_mask) +{ + struct set_automerge_ctx ctx = { + .set = set, + .init = init, + .debug_mask = debug_mask, + }; + + ctx.purge = set_expr_alloc(&internal_location, set); + list_expr_sort(&init->expressions); + setelem_automerge(&ctx); + expr_free(ctx.purge); +} + +static int __set_delete(struct list_head *msgs, struct expr *i, struct set *set, + struct expr *init, struct set *existing_set, + unsigned int debug_mask) +{ + i->flags |= EXPR_F_REMOVE; + list_move(&i->list, &existing_set->init->expressions); + list_expr_sort(&existing_set->init->expressions); + + return setelem_delete(msgs, set, init, existing_set->init, debug_mask); +} + +/* detection for unexisting intervals already exists in Linux kernels >= 5.7. */ +int set_delete(struct list_head *msgs, struct cmd *cmd, struct set *set, + struct expr *init, unsigned int debug_mask) +{ + struct set *existing_set = set->existing_set; + struct expr *i, *next, *add, *clone; + struct handle h = {}; + struct cmd *add_cmd; + LIST_HEAD(del_list); + int err; + + set_to_range(init); + if (set->automerge) + automerge_delete(msgs, set, init, debug_mask); + + if (existing_set->init) { + set_to_range(existing_set->init); + } else { + existing_set->init = set_expr_alloc(&internal_location, set); + } + + list_splice_init(&init->expressions, &del_list); + + list_for_each_entry_safe(i, next, &del_list, list) { + err = __set_delete(msgs, i, set, init, existing_set, debug_mask); + if (err < 0) { + list_splice(&del_list, &init->expressions); + return err; + } + } + + add = set_expr_alloc(&internal_location, set); + list_for_each_entry(i, &existing_set->init->expressions, list) { + if (!(i->flags & EXPR_F_KERNEL)) { + clone = expr_clone(i); + list_add_tail(&clone->list, &add->expressions); + i->flags |= EXPR_F_KERNEL; + } + } + + if (debug_mask & NFT_DEBUG_SEGTREE) { + list_for_each_entry(i, &init->expressions, list) + pr_gmp_debug("remove: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + list_for_each_entry(i, &add->expressions, list) + pr_gmp_debug("add: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + list_for_each_entry(i, &existing_set->init->expressions, list) + pr_gmp_debug("existing: [%Zx-%Zx]\n", + i->key->left->value, i->key->right->value); + } + + if (list_empty(&add->expressions)) { + expr_free(add); + return 0; + } + + handle_merge(&h, &cmd->handle); + add_cmd = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &h, &cmd->location, add); + add_cmd->elem.set = set_get(set); + list_add(&add_cmd->list, &cmd->list); + + return 0; +} + +static int setelem_overlap(struct list_head *msgs, struct set *set, + struct expr *init) +{ + struct expr *i, *next, *elem, *prev = NULL; + struct range range, prev_range; + int err = 0; + mpz_t rop; + + mpz_init(prev_range.low); + mpz_init(prev_range.high); + mpz_init(range.low); + mpz_init(range.high); + mpz_init(rop); + + list_for_each_entry_safe(elem, next, &init->expressions, list) { + i = interval_expr_key(elem); + + if (i->key->etype == EXPR_SET_ELEM_CATCHALL) + continue; + + range_expr_value_low(range.low, i); + range_expr_value_high(range.high, i); + + if (!prev) { + prev = elem; + mpz_set(prev_range.low, range.low); + mpz_set(prev_range.high, range.high); + continue; + } + + if (mpz_cmp(prev_range.low, range.low) == 0 && + mpz_cmp(prev_range.high, range.high) == 0) + goto next; + + if (mpz_cmp(prev_range.low, range.low) <= 0 && + mpz_cmp(prev_range.high, range.high) >= 0) { + if (prev->flags & EXPR_F_KERNEL) + expr_error(msgs, i, "interval overlaps with an existing one"); + else if (elem->flags & EXPR_F_KERNEL) + expr_error(msgs, prev, "interval overlaps with an existing one"); + else + expr_binary_error(msgs, i, prev, + "conflicting intervals specified"); + err = -1; + goto err_out; + } else if (mpz_cmp(range.low, prev_range.high) <= 0) { + if (prev->flags & EXPR_F_KERNEL) + expr_error(msgs, i, "interval overlaps with an existing one"); + else if (elem->flags & EXPR_F_KERNEL) + expr_error(msgs, prev, "interval overlaps with an existing one"); + else + expr_binary_error(msgs, i, prev, + "conflicting intervals specified"); + err = -1; + goto err_out; + } +next: + prev = elem; + mpz_set(prev_range.low, range.low); + mpz_set(prev_range.high, range.high); + } + +err_out: + mpz_clear(prev_range.low); + mpz_clear(prev_range.high); + mpz_clear(range.low); + mpz_clear(range.high); + mpz_clear(rop); + + return err; +} + +/* overlap detection for intervals already exists in Linux kernels >= 5.7. */ +int set_overlap(struct list_head *msgs, struct set *set, struct expr *init) +{ + struct set *existing_set = set->existing_set; + struct expr *i, *n, *clone; + int err; + + set_sort_splice(init, set); + + err = setelem_overlap(msgs, set, init); + + list_for_each_entry_safe(i, n, &init->expressions, list) { + if (i->flags & EXPR_F_KERNEL) + list_move_tail(&i->list, &existing_set->init->expressions); + else if (existing_set) { + clone = expr_clone(i); + clone->flags |= EXPR_F_KERNEL; + list_add_tail(&clone->list, &existing_set->init->expressions); + } + } + + return err; +} + +static bool segtree_needs_first_segment(const struct set *set, + const struct expr *init, bool add) +{ + if (add && !set->root) { + /* Add the first segment in four situations: + * + * 1) This is an anonymous set. + * 2) This set exists and it is empty. + * 3) New empty set and, separately, new elements are added. + * 4) This set is created with a number of initial elements. + */ + if ((set_is_anonymous(set->flags)) || + (set->init && set->init->size == 0) || + (set->init == NULL && init) || + (set->init == init)) { + return true; + } + } + /* This is an update for a set that already contains elements, so don't + * add the first non-matching elements otherwise we hit EEXIST. + */ + return false; +} + +int set_to_intervals(const struct set *set, struct expr *init, bool add) +{ + struct expr *i, *n, *prev = NULL, *elem, *newelem = NULL, *root, *expr; + LIST_HEAD(intervals); + uint32_t flags; + mpz_t p, q; + + mpz_init2(p, set->key->len); + mpz_init2(q, set->key->len); + + list_for_each_entry_safe(i, n, &init->expressions, list) { + flags = 0; + + elem = interval_expr_key(i); + + if (elem->key->etype == EXPR_SET_ELEM_CATCHALL) + continue; + + if (!prev && segtree_needs_first_segment(set, init, add) && + mpz_cmp_ui(elem->key->left->value, 0)) { + mpz_set_ui(p, 0); + expr = constant_expr_alloc(&internal_location, + set->key->dtype, + set->key->byteorder, + set->key->len, NULL); + mpz_set(expr->value, p); + root = set_elem_expr_alloc(&internal_location, expr); + if (i->etype == EXPR_MAPPING) { + root = mapping_expr_alloc(&internal_location, + root, + expr_get(i->right)); + } + root->flags |= EXPR_F_INTERVAL_END; + list_add(&root->list, &intervals); + init->size++; + } + + if (newelem) { + mpz_set(p, interval_expr_key(newelem)->key->value); + if (set->key->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(p, set->key->len / BITS_PER_BYTE); + + if (!(set->flags & NFT_SET_ANONYMOUS) || + mpz_cmp(p, elem->key->left->value) != 0) + list_add_tail(&newelem->list, &intervals); + else + expr_free(newelem); + } + newelem = NULL; + + if (mpz_scan0(elem->key->right->value, 0) != set->key->len) { + mpz_add_ui(p, elem->key->right->value, 1); + expr = constant_expr_alloc(&elem->key->location, set->key->dtype, + set->key->byteorder, set->key->len, + NULL); + mpz_set(expr->value, p); + if (set->key->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE); + + newelem = set_elem_expr_alloc(&expr->location, expr); + if (i->etype == EXPR_MAPPING) { + newelem = mapping_expr_alloc(&expr->location, + newelem, + expr_get(i->right)); + } + newelem->flags |= EXPR_F_INTERVAL_END; + } else { + flags = NFTNL_SET_ELEM_F_INTERVAL_OPEN; + } + + expr = constant_expr_alloc(&elem->key->location, set->key->dtype, + set->key->byteorder, set->key->len, NULL); + + mpz_set(expr->value, elem->key->left->value); + if (set->key->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, set->key->len / BITS_PER_BYTE); + + expr_free(elem->key); + elem->key = expr; + i->elem_flags |= flags; + init->size++; + list_move_tail(&i->list, &intervals); + + prev = i; + } + + if (newelem) + list_add_tail(&newelem->list, &intervals); + + list_splice_init(&intervals, &init->expressions); + + mpz_clear(p); + mpz_clear(q); + + return 0; +} diff --git a/src/ipopt.c b/src/ipopt.c new file mode 100644 index 0000000..37f779d --- /dev/null +++ b/src/ipopt.c @@ -0,0 +1,145 @@ +#include <nft.h> + + +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> + +#include <utils.h> +#include <headers.h> +#include <expression.h> +#include <ipopt.h> + +static const struct proto_hdr_template ipopt_unknown_template = + PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0); + +#define PHT(__token, __offset, __len) \ + PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \ + __offset, __len) +static const struct exthdr_desc ipopt_lsrr = { + .name = "lsrr", + .type = IPOPT_LSRR, + .templates = { + [IPOPT_FIELD_TYPE] = PHT("type", 0, 8), + [IPOPT_FIELD_LENGTH] = PHT("length", 8, 8), + [IPOPT_FIELD_PTR] = PHT("ptr", 16, 8), + [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32), + }, +}; + +static const struct exthdr_desc ipopt_rr = { + .name = "rr", + .type = IPOPT_RR, + .templates = { + [IPOPT_FIELD_TYPE] = PHT("type", 0, 8), + [IPOPT_FIELD_LENGTH] = PHT("length", 8, 8), + [IPOPT_FIELD_PTR] = PHT("ptr", 16, 8), + [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32), + }, +}; + +static const struct exthdr_desc ipopt_ssrr = { + .name = "ssrr", + .type = IPOPT_SSRR, + .templates = { + [IPOPT_FIELD_TYPE] = PHT("type", 0, 8), + [IPOPT_FIELD_LENGTH] = PHT("length", 8, 8), + [IPOPT_FIELD_PTR] = PHT("ptr", 16, 8), + [IPOPT_FIELD_ADDR_0] = PHT("addr", 24, 32), + }, +}; + +static const struct exthdr_desc ipopt_ra = { + .name = "ra", + .type = IPOPT_RA, + .templates = { + [IPOPT_FIELD_TYPE] = PHT("type", 0, 8), + [IPOPT_FIELD_LENGTH] = PHT("length", 8, 8), + [IPOPT_FIELD_VALUE] = PHT("value", 16, 16), + }, +}; + +const struct exthdr_desc *ipopt_protocols[UINT8_MAX] = { + [IPOPT_LSRR] = &ipopt_lsrr, + [IPOPT_RR] = &ipopt_rr, + [IPOPT_SSRR] = &ipopt_ssrr, + [IPOPT_RA] = &ipopt_ra, +}; + +struct expr *ipopt_expr_alloc(const struct location *loc, uint8_t type, + uint8_t field) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc; + struct expr *expr; + + desc = ipopt_protocols[type]; + tmpl = &desc->templates[field]; + if (!tmpl) + return NULL; + + if (!tmpl->len) + return NULL; + + expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, tmpl->len); + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.op = NFT_EXTHDR_OP_IPV4; + expr->exthdr.offset = tmpl->offset; + expr->exthdr.raw_type = desc->type; + + return expr; +} + +void ipopt_init_raw(struct expr *expr, uint8_t type, unsigned int offset, + unsigned int len, uint32_t flags, bool set_unknown) +{ + const struct proto_hdr_template *tmpl; + unsigned int i; + + assert(expr->etype == EXPR_EXTHDR); + + expr->len = len; + expr->exthdr.flags = flags; + expr->exthdr.offset = offset; + + assert(type < array_size(ipopt_protocols)); + expr->exthdr.desc = ipopt_protocols[type]; + expr->exthdr.flags = flags; + + for (i = 0; i < array_size(expr->exthdr.desc->templates); ++i) { + tmpl = &expr->exthdr.desc->templates[i]; + + /* Make sure that it's the right template based on offset and len */ + if (tmpl->offset != offset || tmpl->len != len) + continue; + + if (flags & NFT_EXTHDR_F_PRESENT) + expr->dtype = &boolean_type; + else + expr->dtype = tmpl->dtype; + expr->exthdr.tmpl = tmpl; + expr->exthdr.op = NFT_EXTHDR_OP_IPV4; + break; + } + if (i == array_size(expr->exthdr.desc->templates) && set_unknown) { + expr->exthdr.tmpl = &ipopt_unknown_template; + expr->exthdr.op = NFT_EXTHDR_OP_IPV4; + } +} + +bool ipopt_find_template(struct expr *expr, unsigned int offset, + unsigned int len) +{ + if (expr->exthdr.tmpl != &ipopt_unknown_template) + return false; + + ipopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0, false); + + if (expr->exthdr.tmpl == &ipopt_unknown_template) + return false; + + return true; +} diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..068c423 --- /dev/null +++ b/src/json.c @@ -0,0 +1,2090 @@ +/* + * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <stdio.h> + +#include <expression.h> +#include <list.h> +#include <netlink.h> +#include <rule.h> +#include <rt.h> +#include "nftutils.h" + +#include <netdb.h> +#include <netinet/icmp6.h> +#include <netinet/in.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_synproxy.h> +#include <linux/xfrm.h> +#include <pwd.h> +#include <grp.h> +#include <jansson.h> +#include <syslog.h> + +#ifdef DEBUG +#define __json_pack json_pack +#define json_pack(...) ({ \ + json_t *__out = __json_pack(__VA_ARGS__); \ + assert(__out); \ + __out; \ +}) +#endif + +static json_t *expr_print_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct expr_ops *ops; + char buf[1024]; + FILE *fp; + + ops = expr_ops(expr); + if (ops->json) + return ops->json(expr, octx); + + fprintf(stderr, "warning: expr ops %s have no json callback\n", + expr_name(expr)); + + fp = octx->output_fp; + octx->output_fp = fmemopen(buf, 1024, "w"); + + ops->print(expr, octx); + + fclose(octx->output_fp); + octx->output_fp = fp; + + return json_pack("s", buf); +} + +static json_t *set_dtype_json(const struct expr *key) +{ + char *namedup = xstrdup(key->dtype->name), *tok; + json_t *root = NULL; + char *tok_safe; + + tok = strtok_r(namedup, " .", &tok_safe); + while (tok) { + json_t *jtok = json_string(tok); + if (!root) + root = jtok; + else if (json_is_string(root)) + root = json_pack("[o, o]", root, jtok); + else + json_array_append_new(root, jtok); + tok = strtok_r(NULL, " .", &tok_safe); + } + xfree(namedup); + return root; +} + +static json_t *stmt_print_json(const struct stmt *stmt, struct output_ctx *octx) +{ + char buf[1024]; + FILE *fp; + + if (stmt->ops->json) + return stmt->ops->json(stmt, octx); + + fprintf(stderr, "warning: stmt ops %s have no json callback\n", + stmt->ops->name); + + fp = octx->output_fp; + octx->output_fp = fmemopen(buf, 1024, "w"); + + stmt->ops->print(stmt, octx); + + fclose(octx->output_fp); + octx->output_fp = fp; + + return json_pack("s", buf); +} + +static json_t *set_stmt_list_json(const struct list_head *stmt_list, + struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + json_t *root, *tmp; + struct stmt *i; + + root = json_array(); + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + + list_for_each_entry(i, stmt_list, list) { + tmp = stmt_print_json(i, octx); + json_array_append_new(root, tmp); + } + octx->flags = flags; + + return root; +} + +static json_t *set_print_json(struct output_ctx *octx, const struct set *set) +{ + json_t *root, *tmp; + const char *type, *datatype_ext = NULL; + + if (set_is_datamap(set->flags)) { + type = "map"; + datatype_ext = set->data->dtype->name; + } else if (set_is_objmap(set->flags)) { + type = "map"; + datatype_ext = obj_type_name(set->objtype); + } else if (set_is_meter(set->flags)) { + type = "meter"; + } else { + type = "set"; + } + + root = json_pack("{s:s, s:s, s:s, s:o, s:I}", + "family", family2str(set->handle.family), + "name", set->handle.set.name, + "table", set->handle.table.name, + "type", set_dtype_json(set->key), + "handle", set->handle.handle.id); + + if (set->comment) + json_object_set_new(root, "comment", json_string(set->comment)); + if (datatype_ext) + json_object_set_new(root, "map", json_string(datatype_ext)); + + if (!(set->flags & (NFT_SET_CONSTANT))) { + if (set->policy != NFT_SET_POL_PERFORMANCE) { + tmp = json_pack("s", set_policy2str(set->policy)); + json_object_set_new(root, "policy", tmp); + } + if (set->desc.size) { + tmp = json_pack("i", set->desc.size); + json_object_set_new(root, "size", tmp); + } + } + + tmp = json_array(); + if (set->flags & NFT_SET_CONSTANT) + json_array_append_new(tmp, json_pack("s", "constant")); + if (set->flags & NFT_SET_INTERVAL) + json_array_append_new(tmp, json_pack("s", "interval")); + if (set->flags & NFT_SET_TIMEOUT) + json_array_append_new(tmp, json_pack("s", "timeout")); + if (set->flags & NFT_SET_EVAL) + json_array_append_new(tmp, json_pack("s", "dynamic")); + + if (json_array_size(tmp) > 0) { + json_object_set_new(root, "flags", tmp); + } else { + if (json_array_size(tmp)) + json_object_set(root, "flags", json_array_get(tmp, 0)); + json_decref(tmp); + } + + if (set->timeout) { + tmp = json_integer(set->timeout / 1000); + json_object_set_new(root, "timeout", tmp); + } + if (set->gc_int) { + tmp = json_pack("i", set->gc_int / 1000); + json_object_set_new(root, "gc-interval", tmp); + } + + if (!nft_output_terse(octx) && set->init && set->init->size > 0) { + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &set->init->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + json_object_set_new(root, "elem", array); + } + + if (!list_empty(&set->stmt_list)) { + json_object_set_new(root, "stmt", + set_stmt_list_json(&set->stmt_list, octx)); + } + + return json_pack("{s:o}", type, root); +} + +/* XXX: Merge with set_print_json()? */ +static json_t *element_print_json(struct output_ctx *octx, + const struct set *set) +{ + json_t *root = expr_print_json(set->init, octx); + + return json_pack("{s: {s:s, s:s, s:s, s:o}}", "element", + "family", family2str(set->handle.family), + "table", set->handle.table.name, + "name", set->handle.set.name, + "elem", root); +} + +static json_t *rule_print_json(struct output_ctx *octx, + const struct rule *rule) +{ + const struct stmt *stmt; + json_t *root, *tmp; + + root = json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(rule->handle.family), + "table", rule->handle.table.name, + "chain", rule->handle.chain.name, + "handle", rule->handle.handle.id); + if (rule->comment) + json_object_set_new(root, "comment", + json_string(rule->comment)); + + tmp = json_array(); + list_for_each_entry(stmt, &rule->stmts, list) + json_array_append_new(tmp, stmt_print_json(stmt, octx)); + + if (json_array_size(tmp)) + json_object_set_new(root, "expr", tmp); + else { + fprintf(stderr, "rule without statements?!\n"); + json_decref(tmp); + } + + return json_pack("{s:o}", "rule", root); +} + +static json_t *chain_print_json(const struct chain *chain) +{ + int priority, policy, n = 0; + struct expr *dev, *expr; + json_t *root, *tmp; + + root = json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(chain->handle.family), + "table", chain->handle.table.name, + "name", chain->handle.chain.name, + "handle", chain->handle.handle.id); + + if (chain->comment) + json_object_set_new(root, "comment", json_string(chain->comment)); + + if (chain->flags & CHAIN_F_BASECHAIN) { + mpz_export_data(&priority, chain->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + mpz_export_data(&policy, chain->policy->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + tmp = json_pack("{s:s, s:s, s:i, s:s}", + "type", chain->type.str, + "hook", hooknum2str(chain->handle.family, + chain->hook.num), + "prio", priority, + "policy", chain_policy2str(policy)); + if (chain->dev_expr) { + list_for_each_entry(expr, &chain->dev_expr->expressions, list) { + dev = expr; + n++; + } + } + + if (n == 1) { + json_object_set_new(tmp, "dev", + json_string(dev->identifier)); + } + json_object_update(root, tmp); + json_decref(tmp); + } + + return json_pack("{s:o}", "chain", root); +} + +static json_t *proto_name_json(uint8_t proto) +{ + char name[NFT_PROTONAME_MAXSIZE]; + + if (nft_getprotobynumber(proto, name, sizeof(name))) + return json_string(name); + return json_integer(proto); +} + +static json_t *timeout_policy_json(uint8_t l4, const uint32_t *timeout) +{ + json_t *root = NULL; + unsigned int i; + + for (i = 0; i < timeout_protocol[l4].array_size; i++) { + if (timeout[i] == timeout_protocol[l4].dflt_timeout[i]) + continue; + + if (!root) + root = json_object(); + json_object_set_new(root, timeout_protocol[l4].state_to_name[i], + json_integer(timeout[i])); + } + return root ? : json_null(); +} + +static json_t *obj_print_json(const struct obj *obj) +{ + const char *rate_unit = NULL, *burst_unit = NULL; + const char *type = obj_type_name(obj->type); + json_t *root, *tmp, *flags; + uint64_t rate, burst; + + root = json_pack("{s:s, s:s, s:s, s:I}", + "family", family2str(obj->handle.family), + "name", obj->handle.obj.name, + "table", obj->handle.table.name, + "handle", obj->handle.handle.id); + + if (obj->comment) { + tmp = json_pack("{s:s}", "comment", obj->comment); + json_object_update(root, tmp); + json_decref(tmp); + } + + switch (obj->type) { + case NFT_OBJECT_COUNTER: + tmp = json_pack("{s:I, s:I}", + "packets", obj->counter.packets, + "bytes", obj->counter.bytes); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_QUOTA: + tmp = json_pack("{s:I, s:I, s:b}", + "bytes", obj->quota.bytes, + "used", obj->quota.used, + "inv", obj->quota.flags & NFT_QUOTA_F_INV); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_SECMARK: + tmp = json_pack("{s:s}", + "context", obj->secmark.ctx); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_CT_HELPER: + tmp = json_pack("{s:s, s:o, s:s}", + "type", obj->ct_helper.name, "protocol", + proto_name_json(obj->ct_helper.l4proto), + "l3proto", family2str(obj->ct_helper.l3proto)); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_CT_TIMEOUT: + tmp = timeout_policy_json(obj->ct_timeout.l4proto, + obj->ct_timeout.timeout); + tmp = json_pack("{s:o, s:s, s:o}", + "protocol", + proto_name_json(obj->ct_timeout.l4proto), + "l3proto", family2str(obj->ct_timeout.l3proto), + "policy", tmp); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_CT_EXPECT: + tmp = json_pack("{s:o, s:I, s:I, s:I, s:s}", + "protocol", + proto_name_json(obj->ct_expect.l4proto), + "dport", obj->ct_expect.dport, + "timeout", obj->ct_expect.timeout, + "size", obj->ct_expect.size, + "l3proto", family2str(obj->ct_expect.l3proto)); + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_LIMIT: + rate = obj->limit.rate; + burst = obj->limit.burst; + + if (obj->limit.type == NFT_LIMIT_PKT_BYTES) { + rate_unit = get_rate(obj->limit.rate, &rate); + burst_unit = get_rate(obj->limit.burst, &burst); + } + + tmp = json_pack("{s:I, s:s}", + "rate", rate, + "per", get_unit(obj->limit.unit)); + + if (obj->limit.flags & NFT_LIMIT_F_INV) + json_object_set_new(tmp, "inv", json_true()); + if (rate_unit) + json_object_set_new(tmp, "rate_unit", + json_string(rate_unit)); + if (burst) { + json_object_set_new(tmp, "burst", json_integer(burst)); + if (burst_unit) + json_object_set_new(tmp, "burst_unit", + json_string(burst_unit)); + } + + json_object_update(root, tmp); + json_decref(tmp); + break; + case NFT_OBJECT_SYNPROXY: + flags = json_array(); + tmp = json_pack("{s:i, s:i}", + "mss", obj->synproxy.mss, + "wscale", obj->synproxy.wscale); + if (obj->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP) + json_array_append_new(flags, json_string("timestamp")); + if (obj->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); + + if (json_array_size(flags) > 0) + json_object_set_new(tmp, "flags", flags); + else + json_decref(flags); + + json_object_update(root, tmp); + json_decref(tmp); + break; + } + + return json_pack("{s:o}", type, root); +} + +static json_t *flowtable_print_json(const struct flowtable *ftable) +{ + json_t *root, *devs = NULL; + int i, priority; + + mpz_export_data(&priority, ftable->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + root = json_pack("{s:s, s:s, s:s, s:I, s:s, s:i}", + "family", family2str(ftable->handle.family), + "name", ftable->handle.flowtable.name, + "table", ftable->handle.table.name, + "handle", ftable->handle.handle.id, + "hook", hooknum2str(NFPROTO_NETDEV, ftable->hook.num), + "prio", priority); + + for (i = 0; i < ftable->dev_array_len; i++) { + const char *dev = ftable->dev_array[i]; + if (!devs) + devs = json_string(dev); + else if (json_is_string(devs)) + devs = json_pack("[o, s]", devs, dev); + else + json_array_append_new(devs, json_string(dev)); + } + if (devs) + json_object_set_new(root, "dev", devs); + + return json_pack("{s:o}", "flowtable", root); +} + +static json_t *table_flags_json(const struct table *table) +{ + uint32_t flags = table->flags; + json_t *root = json_array(), *tmp; + int i = 0; + + while (flags) { + if (flags & 0x1) { + tmp = json_string(table_flag_name(i)); + json_array_append_new(root, tmp); + } + flags >>= 1; + i++; + } + switch (json_array_size(root)) { + case 0: + json_decref(root); + return NULL; + case 1: + json_unpack(root, "[o]", &tmp); + json_decref(root); + root = tmp; + break; + } + return root; +} + +static json_t *table_print_json(const struct table *table) +{ + json_t *root, *tmp; + + root = json_pack("{s:s, s:s, s:I}", + "family", family2str(table->handle.family), + "name", table->handle.table.name, + "handle", table->handle.handle.id); + + tmp = table_flags_json(table); + if (tmp) + json_object_set_new(root, "flags", tmp); + + if (table->comment) + json_object_set_new(root, "comment", json_string(table->comment)); + + return json_pack("{s:o}", "table", root); +} + +json_t *flagcmp_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *left; + + left = json_pack("{s:[o, o]}", expr_op_symbols[OP_AND], + expr_print_json(expr->flagcmp.expr, octx), + expr_print_json(expr->flagcmp.mask, octx)); + + return json_pack("{s:{s:s, s:o, s:o}}", "match", + "op", expr_op_symbols[expr->op] ? : "in", + "left", left, + "right", expr_print_json(expr->flagcmp.value, octx)); +} + +json_t *binop_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:[o, o]}", expr_op_symbols[expr->op], + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); +} + +json_t *relational_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:{s:s, s:o, s:o}}", "match", + "op", expr_op_symbols[expr->op] ? : "in", + "left", expr_print_json(expr->left, octx), + "right", expr_print_json(expr->right, octx)); +} + +json_t *range_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + json_t *root; + + octx->flags &= ~NFT_CTX_OUTPUT_SERVICE; + octx->flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO; + root = json_pack("{s:[o, o]}", "range", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); + octx->flags = flags; + + return root; +} + +json_t *meta_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:{s:s}}", "meta", + "key", meta_templates[expr->meta.key].token); +} + +json_t *payload_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root; + + if (payload_is_known(expr)) { + if (expr->payload.inner_desc) { + root = json_pack("{s:s, s:s, s:s}", + "tunnel", expr->payload.inner_desc->name, + "protocol", expr->payload.desc->name, + "field", expr->payload.tmpl->token); + } else { + root = json_pack("{s:s, s:s}", + "protocol", expr->payload.desc->name, + "field", expr->payload.tmpl->token); + } + } else { + root = json_pack("{s:s, s:i, s:i}", + "base", proto_base_tokens[expr->payload.base], + "offset", expr->payload.offset, + "len", expr->len); + } + + return json_pack("{s:o}", "payload", root); +} + +json_t *ct_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *dirstr = ct_dir2str(expr->ct.direction); + enum nft_ct_keys key = expr->ct.key; + json_t *root; + + root = json_pack("{s:s}", "key", ct_templates[key].token); + + if (expr->ct.direction < 0) + goto out; + + if (dirstr) + json_object_set_new(root, "dir", json_string(dirstr)); +out: + return json_pack("{s:o}", "ct", root); +} + +json_t *concat_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + return json_pack("{s:o}", "concat", array); +} + +json_t *set_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + return json_pack("{s:o}", "set", array); +} + +json_t *set_ref_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + if (set_is_anonymous(expr->set->flags)) { + return expr_print_json(expr->set->init, octx); + } else { + return json_pack("s+", "@", expr->set->handle.set.name); + } +} + +json_t *set_elem_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root = expr_print_json(expr->key, octx); + struct stmt *stmt; + json_t *tmp; + + if (!root) + return NULL; + + /* these element attributes require formal set elem syntax */ + if (expr->timeout || expr->expiration || expr->comment || + !list_empty(&expr->stmt_list)) { + root = json_pack("{s:o}", "val", root); + + if (expr->timeout) { + tmp = json_integer(expr->timeout / 1000); + json_object_set_new(root, "timeout", tmp); + } + if (expr->expiration) { + tmp = json_integer(expr->expiration / 1000); + json_object_set_new(root, "expires", tmp); + } + if (expr->comment) { + tmp = json_string(expr->comment); + json_object_set_new(root, "comment", tmp); + } + list_for_each_entry(stmt, &expr->stmt_list, list) { + tmp = stmt_print_json(stmt, octx); + /* XXX: detect and complain about clashes? */ + json_object_update_missing(root, tmp); + json_decref(tmp); + /* TODO: only one statement per element. */ + break; + } + return json_pack("{s:o}", "elem", root); + } + + return root; +} + +json_t *prefix_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root = expr_print_json(expr->prefix, octx); + + return json_pack("{s:{s:o, s:i}}", "prefix", + "addr", root, + "len", expr->prefix_len); +} + +json_t *list_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *array = json_array(); + const struct expr *i; + + list_for_each_entry(i, &expr->expressions, list) + json_array_append_new(array, expr_print_json(i, octx)); + + //return json_pack("{s:s, s:o}", "type", "list", "val", array); + return array; +} + +json_t *unary_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return expr_print_json(expr->arg, octx); +} + +json_t *mapping_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("[o, o]", + expr_print_json(expr->left, octx), + expr_print_json(expr->right, octx)); +} + +json_t *map_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *type = "map"; + + if (expr->mappings->etype == EXPR_SET_REF && + expr->mappings->set->data->dtype->type == TYPE_VERDICT) + type = "vmap"; + + return json_pack("{s:{s:o, s:o}}", type, + "key", expr_print_json(expr->map, octx), + "data", expr_print_json(expr->mappings, octx)); +} + +json_t *exthdr_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *desc = expr->exthdr.desc ? + expr->exthdr.desc->name : NULL; + const char *field = expr->exthdr.tmpl->token; + json_t *root; + bool is_exists = expr->exthdr.flags & NFT_EXTHDR_F_PRESENT; + + if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) { + static const char *offstrs[] = { "", "1", "2", "3" }; + unsigned int offset = expr->exthdr.offset / 64; + const char *offstr = ""; + + if (desc) { + if (offset < 4) + offstr = offstrs[offset]; + + root = json_pack("{s:s+}", "name", desc, offstr); + + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); + } else { + root = json_pack("{s:i, s:i, s:i}", + "base", expr->exthdr.raw_type, + "offset", expr->exthdr.offset, + "len", expr->len); + } + + return json_pack("{s:o}", "tcp option", root); + } + + if (expr->exthdr.op == NFT_EXTHDR_OP_DCCP) { + root = json_pack("{s:i}", "type", expr->exthdr.raw_type); + return json_pack("{s:o}", "dccp option", root); + } + + root = json_pack("{s:s}", "name", desc); + if (!is_exists) + json_object_set_new(root, "field", json_string(field)); + + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_IPV4: + return json_pack("{s:o}", "ip option", root); + case NFT_EXTHDR_OP_SCTP: + return json_pack("{s:o}", "sctp chunk", root); + default: + return json_pack("{s:o}", "exthdr", root); + } +} + +json_t *verdict_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct { + int verdict; + const char *name; + bool chain; + } verdict_tbl[] = { + { NFT_CONTINUE, "continue", false }, + { NFT_BREAK, "break", false }, + { NFT_JUMP, "jump", true }, + { NFT_GOTO, "goto", true }, + { NFT_RETURN, "return", false }, + { NF_ACCEPT, "accept", false }, + { NF_DROP, "drop", false }, + { NF_QUEUE, "queue", false }, + }; + const char *name = NULL; + json_t *chain = NULL; + unsigned int i; + + for (i = 0; i < array_size(verdict_tbl); i++) { + if (expr->verdict == verdict_tbl[i].verdict) { + name = verdict_tbl[i].name; + if (verdict_tbl[i].chain && expr->chain) + chain = expr_print_json(expr->chain, octx); + break; + } + } + if (!name) { + BUG("Unknown verdict %d.", expr->verdict); + return NULL; + } + if (chain) + return json_pack("{s:{s:o}}", name, "target", chain); + else + return json_pack("{s:n}", name); +} + +json_t *rt_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *key = rt_templates[expr->rt.key].token; + json_t *root = json_pack("{s:s}", "key", key); + const char *family = NULL; + + switch (expr->rt.key) { + case NFT_RT_NEXTHOP4: + family = "ip"; + break; + case NFT_RT_NEXTHOP6: + family = "ip6"; + break; + default: + break; + } + + if (family) + json_object_set_new(root, "family", json_string(family)); + + return json_pack("{s:o}", "rt", root); +} + +json_t *numgen_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *mode; + + switch (expr->numgen.type) { + case NFT_NG_INCREMENTAL: + mode = "inc"; + break; + case NFT_NG_RANDOM: + mode = "random"; + break; + default: + mode = "unknown"; + break; + } + + return json_pack("{s:{s:s, s:i, s:i}}", "numgen", + "mode", mode, + "mod", expr->numgen.mod, + "offset", expr->numgen.offset); +} + +json_t *hash_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *type; + json_t *root, *jexpr = NULL; + + switch (expr->hash.type) { + case NFT_HASH_SYM: + type = "symhash"; + break; + case NFT_HASH_JENKINS: + default: + type = "jhash"; + jexpr = expr_print_json(expr->hash.expr, octx); + break; + } + + root = json_pack("{s:i}", "mod", expr->hash.mod); + if (expr->hash.seed_set) + json_object_set_new(root, "seed", + json_integer(expr->hash.seed)); + if (expr->hash.offset) + json_object_set_new(root, "offset", + json_integer(expr->hash.offset)); + if (jexpr) + json_object_set_new(root, "expr", jexpr); + + return json_pack("{s:o}", type, root); +} + +json_t *fib_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *fib_flags[] = { "saddr", "daddr", "mark", "iif", "oif" }; + unsigned int flags = expr->fib.flags & ~NFTA_FIB_F_PRESENT; + json_t *root; + + root = json_pack("{s:s}", "result", fib_result_str(expr->fib.result)); + + if (flags) { + json_t *tmp = json_array(); + unsigned int i; + + for (i = 0; i < array_size(fib_flags); i++) { + if (flags & (1 << i)) { + json_array_append_new(tmp, json_string(fib_flags[i])); + flags &= ~(1 << i); + } + } + if (flags) + json_array_append_new(tmp, json_integer(flags)); + json_object_set_new(root, "flags", tmp); + } + return json_pack("{s:o}", "fib", root); +} + +static json_t *symbolic_constant_json(const struct symbol_table *tbl, + const struct expr *expr, + struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + const struct symbolic_constant *s; + uint64_t val = 0; + + /* Export the data in the correct byteorder for comparison */ + assert(expr->len / BITS_PER_BYTE <= sizeof(val)); + mpz_export_data(constant_data_ptr(val, expr->len), expr->value, + expr->byteorder, len); + + for (s = tbl->symbols; s->identifier != NULL; s++) { + if (val == s->value) + break; + } + if (!s->identifier) + return expr_basetype(expr)->json(expr, octx); + + if (nft_output_numeric_symbol(octx)) + return json_integer(val); + else + return json_string(s->identifier); +} + +json_t *set_elem_catchall_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_string("*"); +} + +static json_t *datatype_json(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype; + + do { + if (dtype->json) + return dtype->json(expr, octx); + if (dtype->sym_tbl) + return symbolic_constant_json(dtype->sym_tbl, + expr, octx); + if (dtype->print) { + char buf[1024]; + FILE *ofp = octx->output_fp; + + octx->output_fp = fmemopen(buf, 1024, "w"); + dtype->print(expr, octx); + fclose(octx->output_fp); + octx->output_fp = ofp; + + if (buf[0] == '"') { + memmove(buf, buf + 1, strlen(buf)); + *strchrnul(buf, '"') = '\0'; + } + + return json_string(buf); + } + } while ((dtype = dtype->basetype)); + + BUG("datatype %s has no print method or symbol table\n", + expr->dtype->name); +} + +json_t *constant_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return datatype_json(expr, octx); +} + +json_t *socket_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_pack("{s:{s:s}}", "socket", "key", + socket_templates[expr->socket.key].token); +} + +json_t *osf_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + json_t *root; + + if (expr->osf.flags & NFT_OSF_F_VERSION) + root = json_pack("{s:s}", "key", "version"); + else + root = json_pack("{s:s}", "key", "name"); + + switch (expr->osf.ttl) { + case 1: + json_object_set_new(root, "ttl", json_string("loose")); + break; + case 2: + json_object_set_new(root, "ttl", json_string("skip")); + break; + } + + return json_pack("{s:o}", "osf", root); +} + +json_t *xfrm_expr_json(const struct expr *expr, struct output_ctx *octx) +{ + const char *name = xfrm_templates[expr->xfrm.key].token; + const char *family = NULL; + const char *dirstr; + json_t *root; + + switch (expr->xfrm.direction) { + case XFRM_POLICY_IN: + dirstr = "in"; + break; + case XFRM_POLICY_OUT: + dirstr = "out"; + break; + default: + return NULL; + } + + switch (expr->xfrm.key) { + case NFT_XFRM_KEY_UNSPEC: + case NFT_XFRM_KEY_SPI: + case NFT_XFRM_KEY_REQID: + case __NFT_XFRM_KEY_MAX: + break; + case NFT_XFRM_KEY_DADDR_IP4: + case NFT_XFRM_KEY_SADDR_IP4: + family = "ip"; + break; + case NFT_XFRM_KEY_DADDR_IP6: + case NFT_XFRM_KEY_SADDR_IP6: + family = "ip6"; + break; + } + + root = json_pack("{s:s}", "key", name); + + if (family) + json_object_set_new(root, "family", json_string(family)); + + json_object_set_new(root, "dir", json_string(dirstr)); + json_object_set_new(root, "spnum", json_integer(expr->xfrm.spnum)); + + return json_pack("{s:o}", "ipsec", root); +} + +json_t *integer_type_json(const struct expr *expr, struct output_ctx *octx) +{ + char buf[1024] = "0x"; + + if (mpz_fits_ulong_p(expr->value)) + return json_integer(mpz_get_ui(expr->value)); + + mpz_get_str(buf + 2, 16, expr->value); + return json_string(buf); +} + +json_t *string_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + char data[len+1]; + + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + data[len] = '\0'; + + return json_string(data); +} + +json_t *boolean_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + uint64_t val = 0; + + /* Export the data in the correct byteorder for comparison */ + assert(expr->len / BITS_PER_BYTE <= sizeof(val)); + mpz_export_data(constant_data_ptr(val, expr->len), expr->value, + expr->byteorder, len); + + return json_boolean((int)val); +} + +json_t *inet_protocol_type_json(const struct expr *expr, + struct output_ctx *octx) +{ + if (!nft_output_numeric_proto(octx)) { + char name[NFT_PROTONAME_MAXSIZE]; + + if (nft_getprotobynumber(mpz_get_uint8(expr->value), name, sizeof(name))) + return json_string(name); + } + return integer_type_json(expr, octx); +} + +json_t *inet_service_type_json(const struct expr *expr, struct output_ctx *octx) +{ + uint16_t port = mpz_get_be16(expr->value); + char name[NFT_SERVNAME_MAXSIZE]; + + if (!nft_output_service(octx) || + !nft_getservbyport(port, NULL, name, sizeof(name))) + return json_integer(ntohs(port)); + + return json_string(name); +} + +json_t *mark_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_json(octx->tbl.mark, expr, octx); +} + +json_t *devgroup_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_json(octx->tbl.devgroup, expr, octx); +} + +json_t *ct_label_type_json(const struct expr *expr, struct output_ctx *octx) +{ + unsigned long bit = mpz_scan1(expr->value, 0); + const char *labelstr = ct_label2str(octx->tbl.ct_label, bit); + + if (labelstr) + return json_string(labelstr); + + /* can happen when connlabel.conf is altered after rules were added */ + return json_integer(bit); +} + +json_t *time_type_json(const struct expr *expr, struct output_ctx *octx) +{ + return json_integer(mpz_get_uint64(expr->value) / MSEC_PER_SEC); +} + +json_t *uid_type_json(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t uid = mpz_get_uint32(expr->value); + + if (nft_output_guid(octx)) { + struct passwd *pw = getpwuid(uid); + + if (pw) + return json_string(pw->pw_name); + } + return json_integer(uid); +} + +json_t *gid_type_json(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t gid = mpz_get_uint32(expr->value); + + if (nft_output_guid(octx)) { + struct group *gr = getgrgid(gid); + + if (gr) + return json_string(gr->gr_name); + } + return json_integer(gid); +} + +json_t *expr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return expr_print_json(stmt->expr, octx); +} + +json_t *flow_offload_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s:{s:s, s:s+}}", "flow", + "op", "add", "flowtable", + "@", stmt->flow.table_name); +} + +json_t *payload_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s: {s:o, s:o}}", "mangle", + "key", expr_print_json(stmt->payload.expr, octx), + "value", expr_print_json(stmt->payload.val, octx)); +} + +json_t *exthdr_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s: {s:o, s:o}}", "mangle", + "key", expr_print_json(stmt->exthdr.expr, octx), + "value", expr_print_json(stmt->exthdr.val, octx)); +} + +json_t *quota_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *data_unit; + uint64_t bytes; + json_t *root; + + data_unit = get_rate(stmt->quota.bytes, &bytes); + root = json_pack("{s:I, s:s}", + "val", bytes, + "val_unit", data_unit); + + if (stmt->quota.flags & NFT_QUOTA_F_INV) + json_object_set_new(root, "inv", json_true()); + if (!nft_output_stateless(octx) && stmt->quota.used) { + data_unit = get_rate(stmt->quota.used, &bytes); + json_object_set_new(root, "used", json_integer(bytes)); + json_object_set_new(root, "used_unit", json_string(data_unit)); + } + + return json_pack("{s:o}", "quota", root); +} + +json_t *ct_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + struct expr expr = { + .ct = { + .key = stmt->ct.key, + .direction = stmt->ct.direction, + .nfproto = 0, + }, + }; + + return json_pack("{s:{s:o, s:o}}", "mangle", + "key", ct_expr_json(&expr, octx), + "value", expr_print_json(stmt->ct.expr, octx)); +} + +json_t *limit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *rate_unit = NULL, *burst_unit = NULL; + bool inv = stmt->limit.flags & NFT_LIMIT_F_INV; + uint64_t burst = stmt->limit.burst; + uint64_t rate = stmt->limit.rate; + json_t *root; + + if (stmt->limit.type == NFT_LIMIT_PKT_BYTES) { + rate_unit = get_rate(stmt->limit.rate, &rate); + burst_unit = get_rate(stmt->limit.burst, &burst); + } + + root = json_pack("{s:I, s:I, s:s}", + "rate", rate, + "burst", burst, + "per", get_unit(stmt->limit.unit)); + if (inv) + json_object_set_new(root, "inv", json_boolean(inv)); + if (rate_unit) + json_object_set_new(root, "rate_unit", json_string(rate_unit)); + if (burst_unit) + json_object_set_new(root, "burst_unit", + json_string(burst_unit)); + + return json_pack("{s:o}", "limit", root); +} + +json_t *fwd_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *tmp; + + root = json_pack("{s:o}", "dev", expr_print_json(stmt->fwd.dev, octx)); + + if (stmt->fwd.addr) { + tmp = json_string(family2str(stmt->fwd.family)); + json_object_set_new(root, "family", tmp); + + tmp = expr_print_json(stmt->fwd.addr, octx); + json_object_set_new(root, "addr", tmp); + } + + return json_pack("{s:o}", "fwd", root); +} + +json_t *notrack_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s:n}", "notrack"); +} + +json_t *dup_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + if (stmt->dup.to) { + root = json_pack("{s:o}", "addr", expr_print_json(stmt->dup.to, octx)); + if (stmt->dup.dev) + json_object_set_new(root, "dev", + expr_print_json(stmt->dup.dev, octx)); + } else { + root = json_null(); + } + return json_pack("{s:o}", "dup", root); +} + +json_t *meta_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + root = json_pack("{s:{s:s}}", "meta", + "key", meta_templates[stmt->meta.key].token); + root = json_pack("{s:o, s:o}", + "key", root, + "value", expr_print_json(stmt->meta.expr, octx)); + + return json_pack("{s:o}", "mangle", root); +} + +json_t *log_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(), *flags; + + if (stmt->log.flags & STMT_LOG_PREFIX) { + char prefix[NF_LOG_PREFIXLEN] = {}; + + expr_to_string(stmt->log.prefix, prefix); + json_object_set_new(root, "prefix", json_string(prefix)); + } + if (stmt->log.flags & STMT_LOG_GROUP) + json_object_set_new(root, "group", + json_integer(stmt->log.group)); + if (stmt->log.flags & STMT_LOG_SNAPLEN) + json_object_set_new(root, "snaplen", + json_integer(stmt->log.snaplen)); + if (stmt->log.flags & STMT_LOG_QTHRESHOLD) + json_object_set_new(root, "queue-threshold", + json_integer(stmt->log.qthreshold)); + if ((stmt->log.flags & STMT_LOG_LEVEL) && + stmt->log.level != LOG_WARNING) + json_object_set_new(root, "level", + json_string(log_level(stmt->log.level))); + + flags = json_array(); + + if ((stmt->log.logflags & NF_LOG_MASK) == NF_LOG_MASK) { + json_array_append_new(flags, json_string("all")); + } else { + if (stmt->log.logflags & NF_LOG_TCPSEQ) + json_array_append_new(flags, + json_string("tcp sequence")); + if (stmt->log.logflags & NF_LOG_TCPOPT) + json_array_append_new(flags, + json_string("tcp options")); + if (stmt->log.logflags & NF_LOG_IPOPT) + json_array_append_new(flags, json_string("ip options")); + if (stmt->log.logflags & NF_LOG_UID) + json_array_append_new(flags, json_string("skuid")); + if (stmt->log.logflags & NF_LOG_MACDECODE) + json_array_append_new(flags, json_string("ether")); + } + if (json_array_size(flags) > 1) { + json_object_set_new(root, "flags", flags); + } else { + if (json_array_size(flags)) + json_object_set(root, "flags", + json_array_get(flags, 0)); + json_decref(flags); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "log", root); +} + +static json_t *nat_flags_json(uint32_t flags) +{ + json_t *array = json_array(); + + if (flags & NF_NAT_RANGE_PROTO_RANDOM) + json_array_append_new(array, json_string("random")); + if (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) + json_array_append_new(array, json_string("fully-random")); + if (flags & NF_NAT_RANGE_PERSISTENT) + json_array_append_new(array, json_string("persistent")); + if (flags & NF_NAT_RANGE_NETMAP) + json_array_append_new(array, json_string("netmap")); + return array; +} + +static json_t *nat_type_flags_json(uint32_t type_flags) +{ + json_t *array = json_array(); + + if (type_flags & STMT_NAT_F_PREFIX) + json_array_append_new(array, json_string("prefix")); + + return array; +} + +static void nat_stmt_add_array(json_t *root, const char *name, json_t *array) +{ + if (json_array_size(array) > 1) { + json_object_set_new(root, name, array); + } else { + if (json_array_size(array)) + json_object_set(root, name, + json_array_get(array, 0)); + json_decref(array); + } +} + +json_t *nat_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(); + json_t *array = nat_flags_json(stmt->nat.flags); + + switch (stmt->nat.family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + json_object_set_new(root, "family", + json_string(family2str(stmt->nat.family))); + break; + } + + if (stmt->nat.addr) + json_object_set_new(root, "addr", + expr_print_json(stmt->nat.addr, octx)); + + if (stmt->nat.proto) + json_object_set_new(root, "port", + expr_print_json(stmt->nat.proto, octx)); + + nat_stmt_add_array(root, "flags", array); + + if (stmt->nat.type_flags) { + array = nat_type_flags_json(stmt->nat.type_flags); + + nat_stmt_add_array(root, "type_flags", array); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", nat_etype2str(stmt->nat.type), root); +} + +json_t *reject_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *jexpr = NULL; + const char *type = NULL; + + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + type = "tcp reset"; + break; + case NFT_REJECT_ICMPX_UNREACH: + type = "icmpx"; + jexpr = expr_print_json(stmt->reject.expr, octx); + break; + case NFT_REJECT_ICMP_UNREACH: + switch (stmt->reject.family) { + case NFPROTO_IPV4: + type = "icmp"; + jexpr = expr_print_json(stmt->reject.expr, octx); + break; + case NFPROTO_IPV6: + type = "icmpv6"; + jexpr = expr_print_json(stmt->reject.expr, octx); + break; + } + } + + if (!type && !jexpr) + return json_pack("{s:n}", "reject"); + + root = json_object(); + if (type) + json_object_set_new(root, "type", json_string(type)); + if (jexpr) + json_object_set_new(root, "expr", jexpr); + + return json_pack("{s:o}", "reject", root); +} + +json_t *counter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + if (nft_output_stateless(octx)) + return json_pack("{s:n}", "counter"); + + return json_pack("{s:{s:I, s:I}}", "counter", + "packets", stmt->counter.packets, + "bytes", stmt->counter.bytes); +} + +json_t *last_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + if (nft_output_stateless(octx) || stmt->last.set == 0) + return json_pack("{s:n}", "last"); + + return json_pack("{s:{s:I}}", "last", "used", stmt->last.used); +} + +json_t *set_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + root = json_pack("{s:s, s:o, s:s+}", + "op", set_stmt_op_names[stmt->set.op], + "elem", expr_print_json(stmt->set.key, octx), + "set", "@", stmt->set.set->set->handle.set.name); + + if (!list_empty(&stmt->set.stmt_list)) { + json_object_set_new(root, "stmt", + set_stmt_list_json(&stmt->set.stmt_list, + octx)); + } + + return json_pack("{s:o}", "set", root); +} + +json_t *map_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root; + + root = json_pack("{s:s, s:o, s:o, s:s+}", + "op", set_stmt_op_names[stmt->map.op], + "elem", expr_print_json(stmt->map.key, octx), + "data", expr_print_json(stmt->map.data, octx), + "map", "@", stmt->map.set->set->handle.set.name); + + if (!list_empty(&stmt->map.stmt_list)) { + json_object_set_new(root, "stmt", + set_stmt_list_json(&stmt->map.stmt_list, + octx)); + } + + return json_pack("{s:o}", "map", root); +} + +json_t *objref_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *name; + + if (stmt->objref.type > NFT_OBJECT_MAX) + name = "unknown"; + else + name = objref_type_name(stmt->objref.type); + + return json_pack("{s:o}", name, expr_print_json(stmt->objref.expr, octx)); +} + +json_t *meter_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + json_t *root, *tmp; + + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + tmp = stmt_print_json(stmt->meter.stmt, octx); + octx->flags = flags; + + root = json_pack("{s:o, s:o, s:i}", + "key", expr_print_json(stmt->meter.key, octx), + "stmt", tmp, + "size", stmt->meter.size); + if (stmt->meter.set) { + tmp = json_string(stmt->meter.set->set->handle.set.name); + json_object_set_new(root, "name", tmp); + } + + return json_pack("{s:o}", "meter", root); +} + +json_t *queue_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root, *flags; + + root = json_object(); + + if (stmt->queue.queue) + json_object_set_new(root, "num", + expr_print_json(stmt->queue.queue, octx)); + + flags = json_array(); + if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) + json_array_append_new(flags, json_string("bypass")); + if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) + json_array_append_new(flags, json_string("fanout")); + if (json_array_size(flags) > 1) { + json_object_set_new(root, "flags", flags); + } else { + if (json_array_size(flags)) + json_object_set(root, "flags", + json_array_get(flags, 0)); + json_decref(flags); + } + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "queue", root); +} + +json_t *verdict_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return expr_print_json(stmt->expr, octx); +} + +json_t *connlimit_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_pack("{s:i}", "val", stmt->connlimit.count); + + if (stmt->connlimit.flags & NFT_CONNLIMIT_F_INV) + json_object_set_new(root, "inv", json_true()); + + return json_pack("{s:o}", "ct count", root); +} + +json_t *tproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *tmp, *root = json_object(); + + if (stmt->tproxy.table_family == NFPROTO_INET && + stmt->tproxy.family != NFPROTO_UNSPEC) { + tmp = json_string(family2str(stmt->tproxy.family)); + json_object_set_new(root, "family", tmp); + } + + if (stmt->tproxy.addr) { + tmp = expr_print_json(stmt->tproxy.addr, octx); + json_object_set_new(root, "addr", tmp); + } + + if (stmt->tproxy.port) { + tmp = expr_print_json(stmt->tproxy.port, octx); + json_object_set_new(root, "port", tmp); + } + + return json_pack("{s:o}", "tproxy", root); +} + +json_t *synproxy_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + json_t *root = json_object(), *flags = json_array(); + + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_MSS) + json_object_set_new(root, "mss", + json_integer(stmt->synproxy.mss)); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_WSCALE) + json_object_set_new(root, "wscale", + json_integer(stmt->synproxy.wscale)); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_TIMESTAMP) + json_array_append_new(flags, json_string("timestamp")); + if (stmt->synproxy.flags & NF_SYNPROXY_OPT_SACK_PERM) + json_array_append_new(flags, json_string("sack-perm")); + + if (json_array_size(flags) > 0) + json_object_set_new(root, "flags", flags); + else + json_decref(flags); + + if (!json_object_size(root)) { + json_decref(root); + root = json_null(); + } + + return json_pack("{s:o}", "synproxy", root); +} + +json_t *optstrip_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + return json_pack("{s:o}", "reset", + expr_print_json(stmt->optstrip.expr, octx)); +} + +json_t *xt_stmt_json(const struct stmt *stmt, struct output_ctx *octx) +{ + static const char *xt_typename[NFT_XT_MAX] = { + [NFT_XT_MATCH] = "match", + [NFT_XT_TARGET] = "target", + [NFT_XT_WATCHER] = "watcher", + }; + + return json_pack("{s:{s:s, s:s}}", "xt", + "type", xt_typename[stmt->xt.type], + "name", stmt->xt.name); +} + +static json_t *table_print_json_full(struct netlink_ctx *ctx, + struct table *table) +{ + json_t *root = json_array(), *rules = json_array(), *tmp; + struct flowtable *flowtable; + struct chain *chain; + struct rule *rule; + struct obj *obj; + struct set *set; + + tmp = table_print_json(table); + json_array_append_new(root, tmp); + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { + tmp = obj_print_json(obj); + json_array_append_new(root, tmp); + } + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (set_is_anonymous(set->flags)) + continue; + tmp = set_print_json(&ctx->nft->output, set); + json_array_append_new(root, tmp); + } + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { + tmp = flowtable_print_json(flowtable); + json_array_append_new(root, tmp); + } + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + tmp = chain_print_json(chain); + json_array_append_new(root, tmp); + + list_for_each_entry(rule, &chain->rules, list) { + tmp = rule_print_json(&ctx->nft->output, rule); + json_array_append_new(rules, tmp); + } + } + + json_array_extend(root, rules); + json_decref(rules); + + return root; +} + +static json_t *do_list_ruleset_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + unsigned int family = cmd->handle.family; + json_t *root = json_array(), *tmp; + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + + tmp = table_print_json_full(ctx, table); + json_array_extend(root, tmp); + json_decref(tmp); + } + + return root; +} + +static json_t *do_list_tables_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + unsigned int family = cmd->handle.family; + json_t *root = json_array(); + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + + json_array_append_new(root, table_print_json(table)); + } + + return root; +} + +static json_t *do_list_table_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + return table_print_json_full(ctx, table); +} + +static json_t *do_list_chain_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + json_t *root = json_array(); + struct chain *chain; + struct rule *rule; + + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + if (chain->handle.family != cmd->handle.family || + strcmp(cmd->handle.chain.name, chain->handle.chain.name)) + continue; + + json_array_append_new(root, chain_print_json(chain)); + + list_for_each_entry(rule, &chain->rules, list) { + json_t *tmp = rule_print_json(&ctx->nft->output, rule); + + json_array_append_new(root, tmp); + } + } + + return root; +} + +static json_t *do_list_chains_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + json_t *root = json_array(); + struct table *table; + struct chain *chain; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + json_t *tmp = chain_print_json(chain); + + json_array_append_new(root, tmp); + } + } + + return root; +} + +static json_t *do_list_set_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + struct set *set = cmd->set; + + if (!set) { + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return json_null(); + } + + return json_pack("[o]", set_print_json(&ctx->nft->output, set)); +} + +static json_t *do_list_sets_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct output_ctx *octx = &ctx->nft->output; + json_t *root = json_array(); + struct table *table; + struct set *set; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cmd->obj == CMD_OBJ_SETS && + !set_is_literal(set->flags)) + continue; + if (cmd->obj == CMD_OBJ_METERS && + !set_is_meter(set->flags)) + continue; + if (cmd->obj == CMD_OBJ_MAPS && + !map_is_literal(set->flags)) + continue; + json_array_append_new(root, set_print_json(octx, set)); + } + } + + return root; +} + +static json_t *do_list_obj_json(struct netlink_ctx *ctx, + struct cmd *cmd, uint32_t type) +{ + json_t *root = json_array(); + struct table *table; + struct obj *obj; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + if (cmd->handle.table.name && + strcmp(cmd->handle.table.name, table->handle.table.name)) + continue; + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { + if (obj->type != type || + (cmd->handle.obj.name && + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + + json_array_append_new(root, obj_print_json(obj)); + } + } + + return root; +} + +static json_t *do_list_flowtable_json(struct netlink_ctx *ctx, + struct cmd *cmd, struct table *table) +{ + json_t *root = json_array(); + struct flowtable *ft; + + ft = ft_cache_find(table, cmd->handle.flowtable.name); + if (!ft) + return json_null(); + + json_array_append_new(root, flowtable_print_json(ft)); + + return root; +} + +static json_t *do_list_flowtables_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + json_t *root = json_array(), *tmp; + struct flowtable *flowtable; + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { + tmp = flowtable_print_json(flowtable); + json_array_append_new(root, tmp); + } + } + + return root; +} + +static json_t *generate_json_metainfo(void) +{ + return json_pack("{s: {s:s, s:s, s:i}}", "metainfo", + "version", PACKAGE_VERSION, + "release_name", RELEASE_NAME, + "json_schema_version", JSON_SCHEMA_VERSION); +} + +int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table = NULL; + json_t *root; + + if (cmd->handle.table.name) + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (!cmd->handle.table.name) { + root = do_list_tables_json(ctx, cmd); + break; + } + root = do_list_table_json(ctx, cmd, table); + break; + case CMD_OBJ_CHAIN: + root = do_list_chain_json(ctx, cmd, table); + break; + case CMD_OBJ_CHAINS: + root = do_list_chains_json(ctx, cmd); + break; + case CMD_OBJ_SETS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_SET: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_RULES: + case CMD_OBJ_RULESET: + root = do_list_ruleset_json(ctx, cmd); + break; + case CMD_OBJ_METERS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_METER: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_MAPS: + root = do_list_sets_json(ctx, cmd); + break; + case CMD_OBJ_MAP: + root = do_list_set_json(ctx, cmd, table); + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_COUNTERS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_COUNTER); + break; + case CMD_OBJ_QUOTA: + case CMD_OBJ_QUOTAS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_QUOTA); + break; + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_HELPERS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_CT_HELPER); + break; + case CMD_OBJ_LIMIT: + case CMD_OBJ_LIMITS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_LIMIT); + break; + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_SECMARK); + break; + case CMD_OBJ_FLOWTABLE: + root = do_list_flowtable_json(ctx, cmd, table); + break; + case CMD_OBJ_FLOWTABLES: + root = do_list_flowtables_json(ctx, cmd); + break; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + if (!json_is_array(root)) { + json_t *tmp = json_array(); + + json_array_append_new(tmp, root); + root = tmp; + } + + json_array_insert_new(root, 0, generate_json_metainfo()); + + root = json_pack("{s:o}", "nftables", root); + json_dumpf(root, ctx->nft->output.output_fp, 0); + json_decref(root); + fprintf(ctx->nft->output.output_fp, "\n"); + fflush(ctx->nft->output.output_fp); + return 0; +} + +static void monitor_print_json(struct netlink_mon_handler *monh, + const char *cmd, json_t *obj) +{ + struct nft_ctx *nft = monh->ctx->nft; + + obj = json_pack("{s:o}", cmd, obj); + if (nft_output_echo(&nft->output) && !nft->json_root) { + json_array_append_new(nft->json_echo, obj); + } else { + json_dumpf(obj, nft->output.output_fp, 0); + json_decref(obj); + } +} + +void monitor_print_table_json(struct netlink_mon_handler *monh, + const char *cmd, struct table *t) +{ + monitor_print_json(monh, cmd, table_print_json(t)); +} + +void monitor_print_chain_json(struct netlink_mon_handler *monh, + const char *cmd, struct chain *c) +{ + monitor_print_json(monh, cmd, chain_print_json(c)); +} + +void monitor_print_set_json(struct netlink_mon_handler *monh, + const char *cmd, struct set *s) +{ + struct output_ctx *octx = &monh->ctx->nft->output; + + monitor_print_json(monh, cmd, set_print_json(octx, s)); +} + +void monitor_print_element_json(struct netlink_mon_handler *monh, + const char *cmd, struct set *s) +{ + struct output_ctx *octx = &monh->ctx->nft->output; + + monitor_print_json(monh, cmd, element_print_json(octx, s)); +} + +void monitor_print_obj_json(struct netlink_mon_handler *monh, + const char *cmd, struct obj *o) +{ + monitor_print_json(monh, cmd, obj_print_json(o)); +} + +void monitor_print_rule_json(struct netlink_mon_handler *monh, + const char *cmd, struct rule *r) +{ + struct output_ctx *octx = &monh->ctx->nft->output; + + monitor_print_json(monh, cmd, rule_print_json(octx, r)); +} + +void json_alloc_echo(struct nft_ctx *nft) +{ + nft->json_echo = json_array(); + if (!nft->json_echo) + memory_allocation_error(); +} diff --git a/src/libnftables.c b/src/libnftables.c new file mode 100644 index 0000000..41f54c0 --- /dev/null +++ b/src/libnftables.c @@ -0,0 +1,810 @@ +/* + * Copyright (c) 2017 Eric Leblond <eric@regit.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables/libnftables.h> +#include <erec.h> +#include <mnl.h> +#include <parser.h> +#include <utils.h> +#include <iface.h> +#include <cmd.h> +#include <errno.h> +#include <sys/stat.h> + +static int nft_netlink(struct nft_ctx *nft, + struct list_head *cmds, struct list_head *msgs) +{ + uint32_t batch_seqnum, seqnum = 0, last_seqnum = UINT32_MAX, num_cmds = 0; + struct netlink_ctx ctx = { + .nft = nft, + .msgs = msgs, + .list = LIST_HEAD_INIT(ctx.list), + .batch = mnl_batch_init(), + }; + struct cmd *cmd; + struct mnl_err *err, *tmp; + LIST_HEAD(err_list); + int ret = 0; + + if (list_empty(cmds)) + goto out; + + batch_seqnum = mnl_batch_begin(ctx.batch, mnl_seqnum_alloc(&seqnum)); + list_for_each_entry(cmd, cmds, list) { + ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(&seqnum); + ret = do_command(&ctx, cmd); + if (ret < 0) { + netlink_io_error(&ctx, &cmd->location, + "Could not process rule: %s", + strerror(errno)); + goto out; + } + num_cmds++; + } + if (!nft->check) + mnl_batch_end(ctx.batch, mnl_seqnum_alloc(&seqnum)); + + if (!mnl_batch_ready(ctx.batch)) + goto out; + + ret = mnl_batch_talk(&ctx, &err_list, num_cmds); + if (ret < 0) { + if (ctx.maybe_emsgsize && errno == EMSGSIZE) { + netlink_io_error(&ctx, NULL, + "Could not process rule: %s\n" + "Please, rise /proc/sys/net/core/wmem_max on the host namespace. Hint: %d bytes", + strerror(errno), round_pow_2(ctx.maybe_emsgsize)); + goto out; + } + netlink_io_error(&ctx, NULL, + "Could not process rule: %s", strerror(errno)); + goto out; + } + + if (!list_empty(&err_list)) + ret = -1; + + list_for_each_entry_safe(err, tmp, &err_list, head) { + /* cmd seqnums are monotonic: only reset the starting position + * if the error seqnum is lower than the previous one. + */ + if (err->seqnum < last_seqnum) + cmd = list_first_entry(cmds, struct cmd, list); + + list_for_each_entry_from(cmd, cmds, list) { + last_seqnum = cmd->seqnum; + if (err->seqnum == cmd->seqnum || + err->seqnum == batch_seqnum) { + nft_cmd_error(&ctx, cmd, err); + errno = err->err; + if (err->seqnum == cmd->seqnum) { + mnl_err_list_free(err); + break; + } + } + } + + if (&cmd->list == cmds) { + /* not found, rewind */ + last_seqnum = UINT32_MAX; + } + } + /* nfnetlink uses the first netlink message header in the batch whose + * sequence number is zero to report for EOPNOTSUPP and EPERM errors in + * some scenarios. Now it is safe to release pending errors here. + */ + list_for_each_entry_safe(err, tmp, &err_list, head) + mnl_err_list_free(err); +out: + mnl_batch_reset(ctx.batch); + return ret; +} + +static void nft_init(struct nft_ctx *ctx) +{ + mark_table_init(ctx); + realm_table_rt_init(ctx); + devgroup_table_init(ctx); + ct_label_table_init(ctx); +} + +static void nft_exit(struct nft_ctx *ctx) +{ + cache_free(&ctx->cache.table_cache); + ct_label_table_exit(ctx); + realm_table_rt_exit(ctx); + devgroup_table_exit(ctx); + mark_table_exit(ctx); +} + +EXPORT_SYMBOL(nft_ctx_add_var); +int nft_ctx_add_var(struct nft_ctx *ctx, const char *var) +{ + char *separator = strchr(var, '='); + int pcount = ctx->num_vars; + struct nft_vars *tmp; + const char *value; + + if (!separator) + return -1; + + tmp = xrealloc(ctx->vars, (pcount + 1) * sizeof(struct nft_vars)); + + *separator = '\0'; + value = separator + 1; + + ctx->vars = tmp; + ctx->vars[pcount].key = xstrdup(var); + ctx->vars[pcount].value = xstrdup(value); + ctx->num_vars++; + + return 0; +} + +EXPORT_SYMBOL(nft_ctx_clear_vars); +void nft_ctx_clear_vars(struct nft_ctx *ctx) +{ + unsigned int i; + + for (i = 0; i < ctx->num_vars; i++) { + xfree(ctx->vars[i].key); + xfree(ctx->vars[i].value); + } + ctx->num_vars = 0; + xfree(ctx->vars); +} + +EXPORT_SYMBOL(nft_ctx_add_include_path); +int nft_ctx_add_include_path(struct nft_ctx *ctx, const char *path) +{ + char **tmp; + int pcount = ctx->num_include_paths; + + tmp = xrealloc(ctx->include_paths, (pcount + 1) * sizeof(char *)); + + ctx->include_paths = tmp; + + if (asprintf(&ctx->include_paths[pcount], "%s", path) < 0) + return -1; + + ctx->num_include_paths++; + return 0; +} + +EXPORT_SYMBOL(nft_ctx_clear_include_paths); +void nft_ctx_clear_include_paths(struct nft_ctx *ctx) +{ + while (ctx->num_include_paths) + xfree(ctx->include_paths[--ctx->num_include_paths]); + + xfree(ctx->include_paths); + ctx->include_paths = NULL; +} + +EXPORT_SYMBOL(nft_ctx_new); +struct nft_ctx *nft_ctx_new(uint32_t flags) +{ + struct nft_ctx *ctx; + +#ifdef HAVE_LIBXTABLES + xt_init(); +#endif + + ctx = xzalloc(sizeof(struct nft_ctx)); + nft_init(ctx); + + ctx->state = xzalloc(sizeof(struct parser_state)); + nft_ctx_add_include_path(ctx, DEFAULT_INCLUDE_PATH); + ctx->parser_max_errors = 10; + cache_init(&ctx->cache.table_cache); + ctx->top_scope = scope_alloc(); + ctx->flags = flags; + ctx->output.output_fp = stdout; + ctx->output.error_fp = stderr; + init_list_head(&ctx->vars_ctx.indesc_list); + + ctx->nf_sock = nft_mnl_socket_open(); + + return ctx; +} + +static ssize_t cookie_write(void *cptr, const char *buf, size_t buflen) +{ + struct cookie *cookie = cptr; + + if (!cookie->buflen) { + cookie->buflen = buflen + 1; + cookie->buf = xmalloc(cookie->buflen); + } else if (cookie->pos + buflen >= cookie->buflen) { + size_t newlen = cookie->buflen * 2; + + while (newlen <= cookie->pos + buflen) + newlen *= 2; + + cookie->buf = xrealloc(cookie->buf, newlen); + cookie->buflen = newlen; + } + memcpy(cookie->buf + cookie->pos, buf, buflen); + cookie->pos += buflen; + cookie->buf[cookie->pos] = '\0'; + + return buflen; +} + +static int init_cookie(struct cookie *cookie) +{ + cookie_io_functions_t cookie_fops = { + .write = cookie_write, + }; + + if (cookie->orig_fp) { /* just rewind buffer */ + if (cookie->buflen) { + cookie->pos = 0; + cookie->buf[0] = '\0'; + } + return 0; + } + + cookie->orig_fp = cookie->fp; + + cookie->fp = fopencookie(cookie, "w", cookie_fops); + if (!cookie->fp) { + cookie->fp = cookie->orig_fp; + cookie->orig_fp = NULL; + return 1; + } + + return 0; +} + +static int exit_cookie(struct cookie *cookie) +{ + if (!cookie->orig_fp) + return 1; + + fclose(cookie->fp); + cookie->fp = cookie->orig_fp; + cookie->orig_fp = NULL; + free(cookie->buf); + cookie->buf = NULL; + cookie->buflen = 0; + cookie->pos = 0; + return 0; +} + +EXPORT_SYMBOL(nft_ctx_buffer_output); +int nft_ctx_buffer_output(struct nft_ctx *ctx) +{ + return init_cookie(&ctx->output.output_cookie); +} + +EXPORT_SYMBOL(nft_ctx_unbuffer_output); +int nft_ctx_unbuffer_output(struct nft_ctx *ctx) +{ + return exit_cookie(&ctx->output.output_cookie); +} + +EXPORT_SYMBOL(nft_ctx_buffer_error); +int nft_ctx_buffer_error(struct nft_ctx *ctx) +{ + return init_cookie(&ctx->output.error_cookie); +} + +EXPORT_SYMBOL(nft_ctx_unbuffer_error); +int nft_ctx_unbuffer_error(struct nft_ctx *ctx) +{ + return exit_cookie(&ctx->output.error_cookie); +} + +static const char *get_cookie_buffer(struct cookie *cookie) +{ + fflush(cookie->fp); + + /* This is a bit tricky: Rewind the buffer for future use and return + * the old content at the same time. Therefore return an empty string + * if buffer position is zero, otherwise just rewind buffer position + * and return the unmodified buffer. */ + + if (!cookie->pos) + return ""; + + cookie->pos = 0; + return cookie->buf; +} + +EXPORT_SYMBOL(nft_ctx_get_output_buffer); +const char *nft_ctx_get_output_buffer(struct nft_ctx *ctx) +{ + return get_cookie_buffer(&ctx->output.output_cookie); +} + +EXPORT_SYMBOL(nft_ctx_get_error_buffer); +const char *nft_ctx_get_error_buffer(struct nft_ctx *ctx) +{ + return get_cookie_buffer(&ctx->output.error_cookie); +} + +EXPORT_SYMBOL(nft_ctx_free); +void nft_ctx_free(struct nft_ctx *ctx) +{ + mnl_socket_close(ctx->nf_sock); + + exit_cookie(&ctx->output.output_cookie); + exit_cookie(&ctx->output.error_cookie); + iface_cache_release(); + nft_cache_release(&ctx->cache); + nft_ctx_clear_vars(ctx); + nft_ctx_clear_include_paths(ctx); + scope_free(ctx->top_scope); + xfree(ctx->state); + nft_exit(ctx); + xfree(ctx); +} + +EXPORT_SYMBOL(nft_ctx_set_output); +FILE *nft_ctx_set_output(struct nft_ctx *ctx, FILE *fp) +{ + FILE *old = ctx->output.output_fp; + + if (!fp || ferror(fp)) + return NULL; + + ctx->output.output_fp = fp; + + return old; +} + +EXPORT_SYMBOL(nft_ctx_set_error); +FILE *nft_ctx_set_error(struct nft_ctx *ctx, FILE *fp) +{ + FILE *old = ctx->output.error_fp; + + if (!fp || ferror(fp)) + return NULL; + + ctx->output.error_fp = fp; + + return old; +} + +EXPORT_SYMBOL(nft_ctx_get_dry_run); +bool nft_ctx_get_dry_run(struct nft_ctx *ctx) +{ + return ctx->check; +} + +EXPORT_SYMBOL(nft_ctx_set_dry_run); +void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry) +{ + ctx->check = dry; +} + +EXPORT_SYMBOL(nft_ctx_get_optimize); +uint32_t nft_ctx_get_optimize(struct nft_ctx *ctx) +{ + return ctx->optimize_flags; +} + +EXPORT_SYMBOL(nft_ctx_set_optimize); +void nft_ctx_set_optimize(struct nft_ctx *ctx, uint32_t flags) +{ + ctx->optimize_flags = flags; +} + +EXPORT_SYMBOL(nft_ctx_input_get_flags); +unsigned int nft_ctx_input_get_flags(struct nft_ctx *ctx) +{ + return ctx->input.flags; +} + +EXPORT_SYMBOL(nft_ctx_input_set_flags); +unsigned int nft_ctx_input_set_flags(struct nft_ctx *ctx, unsigned int flags) +{ + unsigned int old_flags; + + old_flags = ctx->input.flags; + ctx->input.flags = flags; + return old_flags; +} + +EXPORT_SYMBOL(nft_ctx_output_get_flags); +unsigned int nft_ctx_output_get_flags(struct nft_ctx *ctx) +{ + return ctx->output.flags; +} + +EXPORT_SYMBOL(nft_ctx_output_set_flags); +void nft_ctx_output_set_flags(struct nft_ctx *ctx, unsigned int flags) +{ + ctx->output.flags = flags; +} + +EXPORT_SYMBOL(nft_ctx_output_get_debug); +unsigned int nft_ctx_output_get_debug(struct nft_ctx *ctx) +{ + return ctx->debug_mask; +} +EXPORT_SYMBOL(nft_ctx_output_set_debug); +void nft_ctx_output_set_debug(struct nft_ctx *ctx, unsigned int mask) +{ + ctx->debug_mask = mask; +} + +static const struct input_descriptor indesc_cmdline = { + .type = INDESC_BUFFER, + .name = "<cmdline>", +}; + +static int nft_parse_bison_buffer(struct nft_ctx *nft, const char *buf, + struct list_head *msgs, struct list_head *cmds, + const struct input_descriptor *indesc) +{ + int ret; + + parser_init(nft, nft->state, msgs, cmds, nft->top_scope); + nft->scanner = scanner_init(nft->state); + scanner_push_buffer(nft->scanner, indesc, buf); + + ret = nft_parse(nft, nft->scanner, nft->state); + if (ret != 0 || nft->state->nerrs > 0) + return -1; + + return 0; +} + +static char *stdin_to_buffer(void) +{ + unsigned int bufsiz = 16384, consumed = 0; + int numbytes; + char *buf; + + buf = xmalloc(bufsiz); + + numbytes = read(STDIN_FILENO, buf, bufsiz); + while (numbytes > 0) { + consumed += numbytes; + if (consumed == bufsiz) { + bufsiz *= 2; + buf = xrealloc(buf, bufsiz); + } + numbytes = read(STDIN_FILENO, buf + consumed, bufsiz - consumed); + } + buf[consumed] = '\0'; + + return buf; +} + +static const struct input_descriptor indesc_stdin = { + .type = INDESC_STDIN, + .name = "/dev/stdin", +}; + +static int nft_parse_bison_filename(struct nft_ctx *nft, const char *filename, + struct list_head *msgs, struct list_head *cmds) +{ + int ret; + + if (nft->stdin_buf) + return nft_parse_bison_buffer(nft, nft->stdin_buf, msgs, cmds, + &indesc_stdin); + + parser_init(nft, nft->state, msgs, cmds, nft->top_scope); + nft->scanner = scanner_init(nft->state); + if (scanner_read_file(nft, filename, &internal_location) < 0) + return -1; + + ret = nft_parse(nft, nft->scanner, nft->state); + if (ret != 0 || nft->state->nerrs > 0) + return -1; + + return 0; +} + +static int nft_evaluate(struct nft_ctx *nft, struct list_head *msgs, + struct list_head *cmds) +{ + struct nft_cache_filter *filter; + struct cmd *cmd, *next; + bool collapsed = false; + unsigned int flags; + int err = 0; + + filter = nft_cache_filter_init(); + if (nft_cache_evaluate(nft, cmds, msgs, filter, &flags) < 0) { + nft_cache_filter_fini(filter); + return -1; + } + if (nft_cache_update(nft, flags, msgs, filter) < 0) { + nft_cache_filter_fini(filter); + return -1; + } + + nft_cache_filter_fini(filter); + + if (nft_cmd_collapse(cmds)) + collapsed = true; + + list_for_each_entry(cmd, cmds, list) { + if (cmd->op != CMD_ADD) + continue; + + nft_cmd_expand(cmd); + } + + list_for_each_entry_safe(cmd, next, cmds, list) { + struct eval_ctx ectx = { + .nft = nft, + .msgs = msgs, + }; + + if (cmd_evaluate(&ectx, cmd) < 0 && + ++nft->state->nerrs == nft->parser_max_errors) { + err = -1; + break; + } + } + + if (collapsed) + nft_cmd_uncollapse(cmds); + + if (err < 0 || nft->state->nerrs) + return -1; + + return 0; +} + +EXPORT_SYMBOL(nft_run_cmd_from_buffer); +int nft_run_cmd_from_buffer(struct nft_ctx *nft, const char *buf) +{ + int rc = -EINVAL, parser_rc; + struct cmd *cmd, *next; + LIST_HEAD(msgs); + LIST_HEAD(cmds); + char *nlbuf; + + nlbuf = xzalloc(strlen(buf) + 2); + sprintf(nlbuf, "%s\n", buf); + + if (nft_output_json(&nft->output) || nft_input_json(&nft->input)) + rc = nft_parse_json_buffer(nft, nlbuf, &msgs, &cmds); + if (rc == -EINVAL) + rc = nft_parse_bison_buffer(nft, nlbuf, &msgs, &cmds, + &indesc_cmdline); + + parser_rc = rc; + + rc = nft_evaluate(nft, &msgs, &cmds); + if (rc < 0) { + if (errno == EPERM) { + fprintf(stderr, "%s (you must be root)\n", + strerror(errno)); + } + goto err; + } + + if (parser_rc) { + rc = parser_rc; + goto err; + } + + if (nft_netlink(nft, &cmds, &msgs) != 0) + rc = -1; +err: + erec_print_list(&nft->output, &msgs, nft->debug_mask); + list_for_each_entry_safe(cmd, next, &cmds, list) { + list_del(&cmd->list); + cmd_free(cmd); + } + iface_cache_release(); + if (nft->scanner) { + scanner_destroy(nft); + nft->scanner = NULL; + } + free(nlbuf); + + if (!rc && + nft_output_json(&nft->output) && + nft_output_echo(&nft->output)) + json_print_echo(nft); + + if (rc || nft->check) + nft_cache_release(&nft->cache); + + return rc; +} + +static int load_cmdline_vars(struct nft_ctx *ctx, struct list_head *msgs) +{ + unsigned int bufsize, ret, i, offset = 0; + LIST_HEAD(cmds); + char *buf; + int rc; + + if (ctx->num_vars == 0) + return 0; + + bufsize = 1024; + buf = xzalloc(bufsize + 1); + for (i = 0; i < ctx->num_vars; i++) { +retry: + ret = snprintf(buf + offset, bufsize - offset, + "define %s=%s; ", + ctx->vars[i].key, ctx->vars[i].value); + if (ret >= bufsize - offset) { + bufsize *= 2; + buf = xrealloc(buf, bufsize + 1); + goto retry; + } + offset += ret; + } + snprintf(buf + offset, bufsize - offset, "\n"); + + rc = nft_parse_bison_buffer(ctx, buf, msgs, &cmds, &indesc_cmdline); + + assert(list_empty(&cmds)); + /* Stash the buffer that contains the variable definitions and zap the + * list of input descriptors before releasing the scanner state, + * otherwise error reporting path walks over released objects. + */ + ctx->vars_ctx.buf = buf; + list_splice_init(&ctx->state->indesc_list, &ctx->vars_ctx.indesc_list); + scanner_destroy(ctx); + ctx->scanner = NULL; + + return rc; +} + +/* need to use stat() to, fopen() will block for named fifos and + * libjansson makes no checks before or after open either. + */ +static struct error_record *filename_is_useable(struct nft_ctx *nft, const char *name) +{ + unsigned int type; + struct stat sb; + int err; + + err = stat(name, &sb); + if (err) + return error(&internal_location, "Could not open file \"%s\": %s\n", + name, strerror(errno)); + + type = sb.st_mode & S_IFMT; + + if (type == S_IFREG || type == S_IFIFO) + return NULL; + + if (type == S_IFCHR && 0 == strcmp(name, "/dev/stdin")) + return NULL; + + return error(&internal_location, "Not a regular file: \"%s\"\n", name); +} + +static int __nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename) +{ + struct error_record *erec; + struct cmd *cmd, *next; + int rc, parser_rc; + LIST_HEAD(msgs); + LIST_HEAD(cmds); + + erec = filename_is_useable(nft, filename); + if (erec) { + erec_print(&nft->output, erec, nft->debug_mask); + erec_destroy(erec); + return -1; + } + + rc = load_cmdline_vars(nft, &msgs); + if (rc < 0) + goto err; + + rc = -EINVAL; + if (nft_output_json(&nft->output) || nft_input_json(&nft->input)) + rc = nft_parse_json_filename(nft, filename, &msgs, &cmds); + if (rc == -EINVAL) + rc = nft_parse_bison_filename(nft, filename, &msgs, &cmds); + + parser_rc = rc; + + if (nft->optimize_flags) + nft_optimize(nft, &cmds); + + rc = nft_evaluate(nft, &msgs, &cmds); + if (rc < 0) + goto err; + + if (parser_rc) { + rc = parser_rc; + goto err; + } + + if (nft_netlink(nft, &cmds, &msgs) != 0) + rc = -1; +err: + erec_print_list(&nft->output, &msgs, nft->debug_mask); + list_for_each_entry_safe(cmd, next, &cmds, list) { + list_del(&cmd->list); + cmd_free(cmd); + } + iface_cache_release(); + if (nft->scanner) { + scanner_destroy(nft); + nft->scanner = NULL; + } + if (!list_empty(&nft->vars_ctx.indesc_list)) { + struct input_descriptor *indesc, *next; + + list_for_each_entry_safe(indesc, next, &nft->vars_ctx.indesc_list, list) { + if (indesc->name) + xfree(indesc->name); + + xfree(indesc); + } + } + xfree(nft->vars_ctx.buf); + + if (!rc && + nft_output_json(&nft->output) && + nft_output_echo(&nft->output)) + json_print_echo(nft); + + if (rc || nft->check) + nft_cache_release(&nft->cache); + + scope_release(nft->state->scopes[0]); + + return rc; +} + +static int nft_run_optimized_file(struct nft_ctx *nft, const char *filename) +{ + uint32_t optimize_flags; + bool check; + int ret; + + check = nft->check; + nft->check = true; + optimize_flags = nft->optimize_flags; + nft->optimize_flags = 0; + + /* First check the original ruleset loads fine as is. */ + ret = __nft_run_cmd_from_filename(nft, filename); + if (ret < 0) + return ret; + + nft->check = check; + nft->optimize_flags = optimize_flags; + + return __nft_run_cmd_from_filename(nft, filename); +} + +EXPORT_SYMBOL(nft_run_cmd_from_filename); +int nft_run_cmd_from_filename(struct nft_ctx *nft, const char *filename) +{ + int ret; + + if (!strcmp(filename, "-")) + filename = "/dev/stdin"; + + if (!strcmp(filename, "/dev/stdin") && + !nft_output_json(&nft->output)) + nft->stdin_buf = stdin_to_buffer(); + + if (nft->optimize_flags) { + ret = nft_run_optimized_file(nft, filename); + xfree(nft->stdin_buf); + return ret; + } + + ret = __nft_run_cmd_from_filename(nft, filename); + xfree(nft->stdin_buf); + + return ret; +} diff --git a/src/libnftables.map b/src/libnftables.map new file mode 100644 index 0000000..9369f44 --- /dev/null +++ b/src/libnftables.map @@ -0,0 +1,40 @@ +LIBNFTABLES_1 { +global: + nft_ctx_add_include_path; + nft_ctx_clear_include_paths; + nft_ctx_new; + nft_ctx_buffer_output; + nft_ctx_unbuffer_output; + nft_ctx_buffer_error; + nft_ctx_unbuffer_error; + nft_ctx_get_output_buffer; + nft_ctx_get_error_buffer; + nft_ctx_free; + nft_ctx_set_output; + nft_ctx_set_error; + nft_ctx_get_dry_run; + nft_ctx_set_dry_run; + nft_ctx_output_get_flags; + nft_ctx_output_set_flags; + nft_ctx_output_get_debug; + nft_ctx_output_set_debug; + nft_run_cmd_from_buffer; + nft_run_cmd_from_filename; + +local: *; +}; + +LIBNFTABLES_2 { + nft_ctx_add_var; + nft_ctx_clear_vars; +} LIBNFTABLES_1; + +LIBNFTABLES_3 { + nft_ctx_set_optimize; + nft_ctx_get_optimize; +} LIBNFTABLES_2; + +LIBNFTABLES_4 { + nft_ctx_input_get_flags; + nft_ctx_input_set_flags; +} LIBNFTABLES_3; diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9485b19 --- /dev/null +++ b/src/main.c @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <getopt.h> +#include <fcntl.h> +#include <sys/types.h> + +#include <nftables/libnftables.h> +#include <utils.h> +#include <cli.h> + +static struct nft_ctx *nft; + +enum opt_indices { + /* General options */ + IDX_HELP, + IDX_VERSION, + IDX_VERSION_LONG, + /* Ruleset input handling */ + IDX_FILE, +#define IDX_RULESET_INPUT_START IDX_FILE + IDX_DEFINE, + IDX_INTERACTIVE, + IDX_INCLUDEPATH, + IDX_CHECK, + IDX_OPTIMIZE, +#define IDX_RULESET_INPUT_END IDX_OPTIMIZE + /* Ruleset list formatting */ + IDX_HANDLE, +#define IDX_RULESET_LIST_START IDX_HANDLE + IDX_STATELESS, + IDX_TERSE, + IDX_SERVICE, + IDX_REVERSEDNS, + IDX_GUID, + IDX_NUMERIC, + IDX_NUMERIC_PRIO, + IDX_NUMERIC_PROTO, + IDX_NUMERIC_TIME, +#define IDX_RULESET_LIST_END IDX_NUMERIC_TIME + /* Command output formatting */ + IDX_ECHO, +#define IDX_CMD_OUTPUT_START IDX_ECHO + IDX_JSON, + IDX_DEBUG, +#define IDX_CMD_OUTPUT_END IDX_DEBUG +}; + +enum opt_vals { + OPT_HELP = 'h', + OPT_VERSION = 'v', + OPT_VERSION_LONG = 'V', + OPT_CHECK = 'c', + OPT_FILE = 'f', + OPT_DEFINE = 'D', + OPT_INTERACTIVE = 'i', + OPT_INCLUDEPATH = 'I', + OPT_JSON = 'j', + OPT_NUMERIC = 'n', + OPT_STATELESS = 's', + OPT_IP2NAME = 'N', + OPT_SERVICE = 'S', + OPT_DEBUG = 'd', + OPT_HANDLE_OUTPUT = 'a', + OPT_ECHO = 'e', + OPT_GUID = 'u', + OPT_NUMERIC_PRIO = 'y', + OPT_NUMERIC_PROTO = 'p', + OPT_NUMERIC_TIME = 'T', + OPT_TERSE = 't', + OPT_OPTIMIZE = 'o', + OPT_INVALID = '?', +}; + +struct nft_opt { + const char *name; + enum opt_vals val; + const char *arg; + const char *help; +}; + +#define NFT_OPT(n, v, a, h) \ + (struct nft_opt) { .name = n, .val = v, .arg = a, .help = h } + +static const struct nft_opt nft_options[] = { + [IDX_HELP] = NFT_OPT("help", OPT_HELP, NULL, + "Show this help"), + [IDX_VERSION] = NFT_OPT("version", OPT_VERSION, NULL, + "Show version information"), + [IDX_VERSION_LONG] = NFT_OPT(NULL, OPT_VERSION_LONG, NULL, + "Show extended version information"), + [IDX_FILE] = NFT_OPT("file", OPT_FILE, "<filename>", + "Read input from <filename>"), + [IDX_DEFINE] = NFT_OPT("define", OPT_DEFINE, "<name=value>", + "Define variable, e.g. --define foo=1.2.3.4"), + [IDX_INTERACTIVE] = NFT_OPT("interactive", OPT_INTERACTIVE, NULL, + "Read input from interactive CLI"), + [IDX_INCLUDEPATH] = NFT_OPT("includepath", OPT_INCLUDEPATH, "<directory>", + "Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH), + [IDX_CHECK] = NFT_OPT("check", OPT_CHECK, NULL, + "Check commands validity without actually applying the changes."), + [IDX_HANDLE] = NFT_OPT("handle", OPT_HANDLE_OUTPUT, NULL, + "Output rule handle."), + [IDX_STATELESS] = NFT_OPT("stateless", OPT_STATELESS, NULL, + "Omit stateful information of ruleset."), + [IDX_TERSE] = NFT_OPT("terse", OPT_TERSE, NULL, + "Omit contents of sets."), + [IDX_SERVICE] = NFT_OPT("service", OPT_SERVICE, NULL, + "Translate ports to service names as described in /etc/services."), + [IDX_REVERSEDNS] = NFT_OPT("reversedns", OPT_IP2NAME, NULL, + "Translate IP addresses to names."), + [IDX_GUID] = NFT_OPT("guid", OPT_GUID, NULL, + "Print UID/GID as defined in /etc/passwd and /etc/group."), + [IDX_NUMERIC] = NFT_OPT("numeric", OPT_NUMERIC, NULL, + "Print fully numerical output."), + [IDX_NUMERIC_PRIO] = NFT_OPT("numeric-priority", OPT_NUMERIC_PRIO, NULL, + "Print chain priority numerically."), + [IDX_NUMERIC_PROTO] = NFT_OPT("numeric-protocol", OPT_NUMERIC_PROTO, NULL, + "Print layer 4 protocols numerically."), + [IDX_NUMERIC_TIME] = NFT_OPT("numeric-time", OPT_NUMERIC_TIME, NULL, + "Print time values numerically."), + [IDX_ECHO] = NFT_OPT("echo", OPT_ECHO, NULL, + "Echo what has been added, inserted or replaced."), + [IDX_JSON] = NFT_OPT("json", OPT_JSON, NULL, + "Format output in JSON"), + [IDX_DEBUG] = NFT_OPT("debug", OPT_DEBUG, "<level [,level...]>", + "Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"), + [IDX_OPTIMIZE] = NFT_OPT("optimize", OPT_OPTIMIZE, NULL, + "Optimize ruleset"), +}; + +#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0])) + +static const char *get_optstring(void) +{ + static char optstring[2 * NR_NFT_OPTIONS + 2]; + + if (!optstring[0]) { + size_t i, j; + + optstring[0] = '+'; + for (i = 0, j = 1; i < NR_NFT_OPTIONS && j < sizeof(optstring); i++) + j += snprintf(optstring + j, sizeof(optstring) - j, "%c%s", + nft_options[i].val, + nft_options[i].arg ? ":" : ""); + + assert(j < sizeof(optstring)); + } + return optstring; +} + +static const struct option *get_options(void) +{ + static struct option options[NR_NFT_OPTIONS + 1]; + + if (!options[0].name) { + size_t i, j; + + for (i = 0, j = 0; i < NR_NFT_OPTIONS; ++i) { + if (nft_options[i].name) { + options[j].name = nft_options[i].name; + options[j].val = nft_options[i].val; + options[j].has_arg = nft_options[i].arg != NULL; + j++; + } + } + } + return options; +} + +static void print_option(const struct nft_opt *opt) +{ + char optbuf[35] = ""; + int i; + + i = snprintf(optbuf, sizeof(optbuf), " -%c", opt->val); + if (opt->name) + i += snprintf(optbuf + i, sizeof(optbuf) - i, ", --%s", + opt->name); + if (opt->arg) + i += snprintf(optbuf + i, sizeof(optbuf) - i, " %s", opt->arg); + + printf("%-34s%s\n", optbuf, opt->help); +} + +static void show_help(const char *name) +{ + int i; + + printf("Usage: %s [ options ] [ cmds... ]\n" + "\n" + "Options (general):\n", name); + + print_option(&nft_options[IDX_HELP]); + print_option(&nft_options[IDX_VERSION]); + print_option(&nft_options[IDX_VERSION_LONG]); + + printf("\n" + "Options (ruleset input handling):" + "\n"); + + for (i = IDX_RULESET_INPUT_START; i <= IDX_RULESET_INPUT_END; i++) + print_option(&nft_options[i]); + + printf("\n" + "Options (ruleset list formatting):" + "\n"); + + for (i = IDX_RULESET_LIST_START; i <= IDX_RULESET_LIST_END; i++) + print_option(&nft_options[i]); + + printf("\n" + "Options (command output formatting):" + "\n"); + + for (i = IDX_CMD_OUTPUT_START; i <= IDX_CMD_OUTPUT_END; i++) + print_option(&nft_options[i]); + + fputs("\n", stdout); +} + +static void show_version(void) +{ + const char *cli, *minigmp, *json, *xt; + +#if defined(HAVE_LIBREADLINE) + cli = "readline"; +#elif defined(HAVE_LIBEDIT) + cli = "editline"; +#elif defined(HAVE_LIBLINENOISE) + cli = "linenoise"; +#else + cli = "no"; +#endif + +#if defined(HAVE_MINIGMP) + minigmp = "yes"; +#else + minigmp = "no"; +#endif + +#if defined(HAVE_JSON) + json = "yes"; +#else + json = "no"; +#endif + +#if defined(HAVE_XTABLES) + xt = "yes"; +#else + xt = "no"; +#endif + + printf("%s v%s (%s)\n" + " cli: %s\n" + " json: %s\n" + " minigmp: %s\n" + " libxtables: %s\n", + PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME, + cli, json, minigmp, xt); +} + +static const struct { + const char *name; + enum nft_debug_level level; +} debug_param[] = { + { + .name = "scanner", + .level = NFT_DEBUG_SCANNER, + }, + { + .name = "parser", + .level = NFT_DEBUG_PARSER, + }, + { + .name = "eval", + .level = NFT_DEBUG_EVALUATION, + }, + { + .name = "netlink", + .level = NFT_DEBUG_NETLINK, + }, + { + .name = "mnl", + .level = NFT_DEBUG_MNL, + }, + { + .name = "proto-ctx", + .level = NFT_DEBUG_PROTO_CTX, + }, + { + .name = "segtree", + .level = NFT_DEBUG_SEGTREE, + }, + { + .name = "all", + .level = ~0, + }, +}; + +static void nft_options_error(int argc, char * const argv[], int pos) +{ + int i; + + fprintf(stderr, "Error: syntax error, options must be specified before commands\n"); + for (i = 0; i < argc; i++) + fprintf(stderr, "%s ", argv[i]); + printf("\n%4c%*s\n", '^', pos - 2, "~~"); +} + +static bool nft_options_check(int argc, char * const argv[]) +{ + bool skip = false, nonoption = false; + int pos = 0, i; + + for (i = 1; i < argc; i++) { + pos += strlen(argv[i - 1]) + 1; + if (argv[i][0] == '{') { + break; + } else if (skip) { + skip = false; + continue; + } else if (argv[i][0] == '-') { + if (nonoption) { + nft_options_error(argc, argv, pos); + return false; + } else if (argv[i][1] == 'd' || + argv[i][1] == 'I' || + argv[i][1] == 'f' || + argv[i][1] == 'D' || + !strcmp(argv[i], "--debug") || + !strcmp(argv[i], "--includepath") || + !strcmp(argv[i], "--define") || + !strcmp(argv[i], "--file")) { + skip = true; + continue; + } + } else if (argv[i][0] != '-') { + nonoption = true; + } + } + + return true; +} + +int main(int argc, char * const *argv) +{ + const struct option *options = get_options(); + bool interactive = false, define = false; + const char *optstring = get_optstring(); + unsigned int output_flags = 0; + int i, val, rc = EXIT_SUCCESS; + unsigned int debug_mask; + char *filename = NULL; + unsigned int len; + + /* nftables cannot be used with setuid in a safe way. */ + if (getuid() != geteuid()) + _exit(111); + + if (!nft_options_check(argc, argv)) + exit(EXIT_FAILURE); + + nft = nft_ctx_new(NFT_CTX_DEFAULT); + + while (1) { + val = getopt_long(argc, argv, optstring, options, NULL); + if (val == -1) + break; + + switch (val) { + case OPT_HELP: + show_help(argv[0]); + goto out; + case OPT_VERSION: + printf("%s v%s (%s)\n", + PACKAGE_NAME, PACKAGE_VERSION, RELEASE_NAME); + goto out; + case OPT_VERSION_LONG: + show_version(); + goto out; + case OPT_DEFINE: + if (nft_ctx_add_var(nft, optarg)) { + fprintf(stderr, + "Failed to define variable '%s'\n", + optarg); + goto out_fail; + } + define = true; + break; + case OPT_CHECK: + nft_ctx_set_dry_run(nft, true); + break; + case OPT_FILE: + if (interactive) { + fprintf(stderr, + "Error: -i/--interactive and -f/--file options cannot be combined\n"); + goto out_fail; + } + filename = optarg; + break; + case OPT_INTERACTIVE: + if (filename) { + fprintf(stderr, + "Error: -i/--interactive and -f/--file options cannot be combined\n"); + goto out_fail; + } + interactive = true; + break; + case OPT_INCLUDEPATH: + if (nft_ctx_add_include_path(nft, optarg)) { + fprintf(stderr, + "Failed to add include path '%s'\n", + optarg); + goto out_fail; + } + break; + case OPT_NUMERIC: + output_flags |= NFT_CTX_OUTPUT_NUMERIC_ALL; + break; + case OPT_STATELESS: + output_flags |= NFT_CTX_OUTPUT_STATELESS; + break; + case OPT_IP2NAME: + output_flags |= NFT_CTX_OUTPUT_REVERSEDNS; + break; + case OPT_SERVICE: + output_flags |= NFT_CTX_OUTPUT_SERVICE; + break; + case OPT_DEBUG: + debug_mask = nft_ctx_output_get_debug(nft); + for (;;) { + unsigned int i; + char *end; + + end = strchr(optarg, ','); + if (end) + *end = '\0'; + + for (i = 0; i < array_size(debug_param); i++) { + if (strcmp(debug_param[i].name, optarg)) + continue; + debug_mask |= debug_param[i].level; + break; + } + + if (i == array_size(debug_param)) { + fprintf(stderr, "invalid debug parameter `%s'\n", + optarg); + goto out_fail; + } + + if (end == NULL) + break; + optarg = end + 1; + } + nft_ctx_output_set_debug(nft, debug_mask); + break; + case OPT_HANDLE_OUTPUT: + output_flags |= NFT_CTX_OUTPUT_HANDLE; + break; + case OPT_ECHO: + output_flags |= NFT_CTX_OUTPUT_ECHO; + break; + case OPT_JSON: +#ifdef HAVE_LIBJANSSON + output_flags |= NFT_CTX_OUTPUT_JSON; +#else + fprintf(stderr, "JSON support not compiled-in\n"); + goto out_fail; +#endif + break; + case OPT_GUID: + output_flags |= NFT_CTX_OUTPUT_GUID; + break; + case OPT_NUMERIC_PRIO: + output_flags |= NFT_CTX_OUTPUT_NUMERIC_PRIO; + break; + case OPT_NUMERIC_PROTO: + output_flags |= NFT_CTX_OUTPUT_NUMERIC_PROTO; + break; + case OPT_NUMERIC_TIME: + output_flags |= NFT_CTX_OUTPUT_NUMERIC_TIME; + break; + case OPT_TERSE: + output_flags |= NFT_CTX_OUTPUT_TERSE; + break; + case OPT_OPTIMIZE: + nft_ctx_set_optimize(nft, 0x1); + break; + case OPT_INVALID: + goto out_fail; + } + } + + if (!filename && define) { + fprintf(stderr, "Error: -D/--define can only be used with -f/--filename\n"); + goto out_fail; + } + + nft_ctx_output_set_flags(nft, output_flags); + + if (optind != argc) { + char *buf; + + for (len = 0, i = optind; i < argc; i++) + len += strlen(argv[i]) + strlen(" "); + + buf = calloc(1, len); + if (buf == NULL) { + fprintf(stderr, "%s:%u: Memory allocation failure\n", + __FILE__, __LINE__); + goto out_fail; + } + for (i = optind; i < argc; i++) { + strcat(buf, argv[i]); + if (i + 1 < argc) + strcat(buf, " "); + } + rc = !!nft_run_cmd_from_buffer(nft, buf); + free(buf); + } else if (filename != NULL) { + rc = !!nft_run_cmd_from_filename(nft, filename); + } else if (interactive) { + if (cli_init(nft) < 0) { + fprintf(stderr, "%s: interactive CLI not supported in this build\n", + argv[0]); + goto out_fail; + } + } else { + fprintf(stderr, "%s: no command specified\n", argv[0]); + goto out_fail; + } + +out: + nft_ctx_free(nft); + return rc; +out_fail: + nft_ctx_free(nft); + return EXIT_FAILURE; +} diff --git a/src/mergesort.c b/src/mergesort.c new file mode 100644 index 0000000..4d0e280 --- /dev/null +++ b/src/mergesort.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2017 Elise Lennion <elise.lennion@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <expression.h> +#include <gmputil.h> +#include <list.h> + +static void concat_expr_msort_value(const struct expr *expr, mpz_t value) +{ + unsigned int len = 0, ilen; + const struct expr *i; + char data[512]; + + list_for_each_entry(i, &expr->expressions, list) { + ilen = div_round_up(i->len, BITS_PER_BYTE); + mpz_export_data(data + len, i->value, i->byteorder, ilen); + len += ilen; + } + + mpz_import_data(value, data, BYTEORDER_HOST_ENDIAN, len); +} + +static mpz_srcptr expr_msort_value(const struct expr *expr, mpz_t value) +{ + switch (expr->etype) { + case EXPR_SET_ELEM: + return expr_msort_value(expr->key, value); + case EXPR_BINOP: + case EXPR_MAPPING: + case EXPR_RANGE: + return expr_msort_value(expr->left, value); + case EXPR_VALUE: + return expr->value; + case EXPR_CONCAT: + concat_expr_msort_value(expr, value); + break; + case EXPR_SET_ELEM_CATCHALL: + /* max value to ensure listing shows it in the last position */ + mpz_bitmask(value, expr->len); + break; + default: + BUG("Unknown expression %s\n", expr_name(expr)); + } + return value; +} + +static int expr_msort_cmp(const struct expr *e1, const struct expr *e2) +{ + mpz_srcptr value1; + mpz_srcptr value2; + mpz_t value1_tmp; + mpz_t value2_tmp; + int ret; + + mpz_init(value1_tmp); + mpz_init(value2_tmp); + value1 = expr_msort_value(e1, value1_tmp); + value2 = expr_msort_value(e2, value2_tmp); + ret = mpz_cmp(value1, value2); + mpz_clear(value1_tmp); + mpz_clear(value2_tmp); + + return ret; +} + +void list_splice_sorted(struct list_head *list, struct list_head *head) +{ + struct list_head *h = head->next; + struct list_head *l = list->next; + + while (l != list) { + if (h == head || + expr_msort_cmp(list_entry(l, typeof(struct expr), list), + list_entry(h, typeof(struct expr), list)) < 0) { + l = l->next; + list_add_tail(l->prev, h); + continue; + } + + h = h->next; + } +} + +static void list_cut_middle(struct list_head *list, struct list_head *head) +{ + struct list_head *s = head->next; + struct list_head *e = head->prev; + + while (e != s) { + e = e->prev; + + if (e != s) + s = s->next; + } + + __list_cut_position(list, head, s); +} + +void list_expr_sort(struct list_head *head) +{ + struct list_head *list; + LIST_HEAD(temp); + + list = &temp; + + if (list_empty(head) || list_is_singular(head)) + return; + + list_cut_middle(list, head); + + list_expr_sort(head); + list_expr_sort(list); + + list_splice_sorted(list, head); +} diff --git a/src/meta.c b/src/meta.c new file mode 100644 index 0000000..b578d5e --- /dev/null +++ b/src/meta.c @@ -0,0 +1,1043 @@ +/* + * Meta expression/statement related definition and types. + * + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <stdio.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <pwd.h> +#include <grp.h> +#include <arpa/inet.h> +#include <linux/netfilter.h> +#include <linux/pkt_sched.h> +#include <linux/if_packet.h> +#include <time.h> + +#include <nftables.h> +#include <expression.h> +#include <statement.h> +#include <datatype.h> +#include <meta.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> +#include <iface.h> +#include <json.h> + +static void tchandle_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + uint32_t handle = mpz_get_uint32(expr->value); + + switch(handle) { + case TC_H_ROOT: + nft_print(octx, "root"); + break; + case TC_H_UNSPEC: + nft_print(octx, "none"); + break; + default: + nft_print(octx, "%0x:%0x", + TC_H_MAJ(handle) >> 16, + TC_H_MIN(handle)); + break; + } +} + +static struct error_record *tchandle_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + uint32_t handle; + char *str = NULL; + + if (strcmp(sym->identifier, "root") == 0) + handle = TC_H_ROOT; + else if (strcmp(sym->identifier, "none") == 0) + handle = TC_H_UNSPEC; + else if (strchr(sym->identifier, ':')) { + uint32_t tmp; + char *colon; + + str = xstrdup(sym->identifier); + + colon = strchr(str, ':'); + if (!colon) + goto err; + + *colon = '\0'; + + errno = 0; + tmp = strtoull(str, NULL, 16); + if (errno != 0) + goto err; + + handle = (tmp << 16); + if (str[strlen(str) - 1] == ':') + goto out; + + errno = 0; + tmp = strtoull(colon + 1, NULL, 16); + if (errno != 0) + goto err; + + handle |= tmp; + } else { + handle = strtoull(sym->identifier, NULL, 0); + } +out: + xfree(str); + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(handle) * BITS_PER_BYTE, &handle); + return NULL; +err: + xfree(str); + return error(&sym->location, "Could not parse %s", sym->dtype->desc); +} + +const struct datatype tchandle_type = { + .type = TYPE_CLASSID, + .name = "classid", + .desc = "TC classid", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = tchandle_type_print, + .parse = tchandle_type_parse, +}; + +static void ifindex_type_print(const struct expr *expr, struct output_ctx *octx) +{ + char name[IFNAMSIZ]; + int ifindex; + + ifindex = mpz_get_uint32(expr->value); + if (nft_if_indextoname(ifindex, name)) + nft_print(octx, "\"%s\"", name); + else + nft_print(octx, "%d", ifindex); +} + +static struct error_record *ifindex_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + int ifindex; + + ifindex = nft_if_nametoindex(sym->identifier); + if (ifindex == 0) { + char *end; + long res; + + errno = 0; + res = strtol(sym->identifier, &end, 10); + + if (res < 0 || res > INT_MAX || *end || errno) + return error(&sym->location, "Interface does not exist"); + + ifindex = (int)res; + } + + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(ifindex) * BITS_PER_BYTE, &ifindex); + return NULL; +} + +const struct datatype ifindex_type = { + .type = TYPE_IFINDEX, + .name = "iface_index", + .desc = "network interface index", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = ifindex_type_print, + .parse = ifindex_type_parse, +}; + +static const struct symbol_table arphrd_tbl = { + .base = BASE_HEXADECIMAL, + .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 = "iface_type", + .desc = "network interface type", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &arphrd_tbl, +}; + +static void uid_type_print(const struct expr *expr, struct output_ctx *octx) +{ + struct passwd *pw; + + if (nft_output_guid(octx)) { + uint32_t uid = mpz_get_uint32(expr->value); + + pw = getpwuid(uid); + if (pw != NULL) + nft_print(octx, "\"%s\"", pw->pw_name); + else + nft_print(octx, "%d", uid); + return; + } + expr_basetype(expr)->print(expr, octx); +} + +static struct error_record *uid_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct passwd *pw; + uid_t uid; + char *endptr = NULL; + + pw = getpwnam(sym->identifier); + if (pw != NULL) + uid = pw->pw_uid; + else { + uint64_t _uid = strtoull(sym->identifier, &endptr, 10); + + if (_uid > UINT32_MAX) + return error(&sym->location, "Value too large"); + else if (*endptr) + return error(&sym->location, "User does not exist"); + uid = _uid; + } + + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(pw->pw_uid) * BITS_PER_BYTE, &uid); + return NULL; +} + +const struct datatype uid_type = { + .type = TYPE_UID, + .name = "uid", + .desc = "user ID", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(uid_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = uid_type_print, + .json = uid_type_json, + .parse = uid_type_parse, +}; + +static void gid_type_print(const struct expr *expr, struct output_ctx *octx) +{ + struct group *gr; + + if (nft_output_guid(octx)) { + uint32_t gid = mpz_get_uint32(expr->value); + + gr = getgrgid(gid); + if (gr != NULL) + nft_print(octx, "\"%s\"", gr->gr_name); + else + nft_print(octx, "%u", gid); + return; + } + expr_basetype(expr)->print(expr, octx); +} + +static struct error_record *gid_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct group *gr; + gid_t gid; + char *endptr = NULL; + + gr = getgrnam(sym->identifier); + if (gr != NULL) + gid = gr->gr_gid; + else { + uint64_t _gid = strtoull(sym->identifier, &endptr, 0); + + if (_gid > UINT32_MAX) + return error(&sym->location, "Value too large"); + else if (*endptr) + return error(&sym->location, "Group does not exist"); + gid = _gid; + } + + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(gr->gr_gid) * BITS_PER_BYTE, &gid); + return NULL; +} + +const struct datatype gid_type = { + .type = TYPE_GID, + .name = "gid", + .desc = "group ID", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(gid_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = gid_type_print, + .json = gid_type_json, + .parse = gid_type_parse, +}; + +static const struct symbol_table pkttype_type_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("host", PACKET_HOST), + SYMBOL("unicast", PACKET_HOST), /* backwards compat */ + SYMBOL("broadcast", PACKET_BROADCAST), + SYMBOL("multicast", PACKET_MULTICAST), + SYMBOL("other", PACKET_OTHERHOST), + SYMBOL_LIST_END, + }, +}; + +static void pkttype_type_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(&pkttype_type_tbl, expr, false, octx); +} + +const struct datatype pkttype_type = { + .type = TYPE_PKTTYPE, + .name = "pkt_type", + .desc = "packet type", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .print = pkttype_type_print, + .sym_tbl = &pkttype_type_tbl, +}; + +void devgroup_table_init(struct nft_ctx *ctx) +{ + ctx->output.tbl.devgroup = rt_symbol_table_init("/etc/iproute2/group"); +} + +void devgroup_table_exit(struct nft_ctx *ctx) +{ + rt_symbol_table_free(ctx->output.tbl.devgroup); +} + +static void devgroup_type_print(const struct expr *expr, + struct output_ctx *octx) +{ + return symbolic_constant_print(octx->tbl.devgroup, expr, true, octx); +} + +static struct error_record *devgroup_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, ctx->tbl->devgroup, res); +} + +const struct datatype devgroup_type = { + .type = TYPE_DEVGROUP, + .name = "devgroup", + .desc = "devgroup name", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = devgroup_type_print, + .json = devgroup_type_json, + .parse = devgroup_type_parse, + .flags = DTYPE_F_PREFIX, +}; + +const struct datatype ifname_type = { + .type = TYPE_IFNAME, + .name = "ifname", + .desc = "network interface name", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = IFNAMSIZ * BITS_PER_BYTE, + .basetype = &string_type, +}; + +static void date_type_print(const struct expr *expr, struct output_ctx *octx) +{ + uint64_t tstamp64 = mpz_get_uint64(expr->value); + char timestr[21]; + time_t tstamp; + struct tm tm; + + /* Convert from nanoseconds to seconds */ + tstamp64 /= 1000000000L; + + /* Obtain current tm, to add tm_gmtoff to the timestamp */ + tstamp = tstamp64; + if (localtime_r(&tstamp, &tm)) + tstamp64 += tm.tm_gmtoff; + + tstamp = tstamp64; + if (gmtime_r(&tstamp, &tm) && + strftime(timestr, sizeof(timestr) - 1, "%Y-%m-%d %T", &tm)) + nft_print(octx, "\"%s\"", timestr); + else + nft_print(octx, "Error converting timestamp to printed time"); +} + +static bool parse_iso_date(uint64_t *tstamp, const char *sym) +{ + struct tm cur_tm; + struct tm tm; + time_t ts; + + memset(&tm, 0, sizeof(struct tm)); + + if (strptime(sym, "%Y-%m-%d %T", &tm)) + goto success; + if (strptime(sym, "%Y-%m-%d %R", &tm)) + goto success; + if (strptime(sym, "%Y-%m-%d", &tm)) + goto success; + + return false; + +success: + /* + * Overwriting TZ is problematic if we're parsing hour types in this same process, + * hence I'd rather use timegm() which doesn't take into account the TZ env variable, + * even though it's Linux-specific. + */ + ts = timegm(&tm); + + if (ts == (time_t) -1) + return false; + + /* Obtain current tm as well (at the specified time), so that we can substract tm_gmtoff */ + if (!localtime_r(&ts, &cur_tm)) + return false; + + /* Substract tm_gmtoff to get the current time */ + *tstamp = ts - cur_tm.tm_gmtoff; + + return true; +} + +static struct error_record *date_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + const char *endptr = sym->identifier; + uint64_t tstamp; + + if (parse_iso_date(&tstamp, sym->identifier)) + goto success; + + tstamp = strtoul(sym->identifier, (char **) &endptr, 10); + if (*endptr == '\0' && endptr != sym->identifier) + goto success; + + return error(&sym->location, "Cannot parse date"); + +success: + /* Convert to nanoseconds */ + tstamp *= 1000000000L; + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(uint64_t) * BITS_PER_BYTE, + &tstamp); + return NULL; +} + +static const struct symbol_table day_type_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("Sunday", 0), + SYMBOL("Monday", 1), + SYMBOL("Tuesday", 2), + SYMBOL("Wednesday", 3), + SYMBOL("Thursday", 4), + SYMBOL("Friday", 5), + SYMBOL("Saturday", 6), + SYMBOL_LIST_END, + }, +}; + +static void day_type_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(&day_type_tbl, expr, true, octx); +} + +#define SECONDS_PER_DAY (60 * 60 * 24) + +static void hour_type_print(const struct expr *expr, struct output_ctx *octx) +{ + uint32_t seconds = mpz_get_uint32(expr->value), minutes, hours; + struct tm cur_tm; + time_t ts; + + /* Obtain current tm, so that we can add tm_gmtoff */ + ts = time(NULL); + if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm)) + seconds = (seconds + cur_tm.tm_gmtoff) % SECONDS_PER_DAY; + + minutes = seconds / 60; + seconds %= 60; + hours = minutes / 60; + minutes %= 60; + + nft_print(octx, "\"%02d:%02d", hours, minutes); + if (seconds) + nft_print(octx, ":%02d", seconds); + nft_print(octx, "\""); +} + +static struct error_record *hour_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + struct error_record *er; + struct tm cur_tm_data; + struct tm *cur_tm; + uint32_t result; + uint64_t tmp; + char *endptr; + struct tm tm; + time_t ts; + + memset(&tm, 0, sizeof(struct tm)); + + /* First, try to parse it as a number */ + result = strtoul(sym->identifier, (char **) &endptr, 10); + if (*endptr == '\0' && endptr != sym->identifier) + goto success; + + result = 0; + + /* Obtain current tm, so that we can substract tm_gmtoff */ + ts = time(NULL); + if (ts != ((time_t) -1) && localtime_r(&ts, &cur_tm_data)) + cur_tm = &cur_tm_data; + else + cur_tm = NULL; + + endptr = strptime(sym->identifier, "%T", &tm); + if (endptr && *endptr == '\0') + goto convert; + + endptr = strptime(sym->identifier, "%R", &tm); + if (endptr && *endptr == '\0') + goto convert; + + if (endptr && *endptr) + return error(&sym->location, "Can't parse trailing input: \"%s\"\n", endptr); + + if ((er = time_parse(&sym->location, sym->identifier, &tmp)) == NULL) { + result = tmp / 1000; + goto convert; + } + + return er; + +convert: + /* Convert the hour to the number of seconds since midnight */ + if (result == 0) + result = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec; + + /* Substract tm_gmtoff to get the current time */ + if (cur_tm) { + if ((long int) result >= cur_tm->tm_gmtoff) + result = (result - cur_tm->tm_gmtoff) % 86400; + else + result = 86400 - cur_tm->tm_gmtoff + result; + } + +success: + *res = constant_expr_alloc(&sym->location, sym->dtype, + BYTEORDER_HOST_ENDIAN, + sizeof(uint32_t) * BITS_PER_BYTE, + &result); + return NULL; +} + +const struct datatype date_type = { + .type = TYPE_TIME_DATE, + .name = "time", + .desc = "Relative time of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(uint64_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = date_type_print, + .parse = date_type_parse, +}; + +const struct datatype day_type = { + .type = TYPE_TIME_DAY, + .name = "day", + .desc = "Day of week of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 1 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = day_type_print, + .sym_tbl = &day_type_tbl, +}; + +const struct datatype hour_type = { + .type = TYPE_TIME_HOUR, + .name = "hour", + .desc = "Hour of day of packet reception", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = sizeof(uint32_t) * BITS_PER_BYTE, + .basetype = &integer_type, + .print = hour_type_print, + .parse = hour_type_parse, +}; + +const struct meta_template meta_templates[] = { + [NFT_META_LEN] = META_TEMPLATE("length", &integer_type, + 4 * 8, BYTEORDER_HOST_ENDIAN), + [NFT_META_PROTOCOL] = META_TEMPLATE("protocol", ðertype_type, + 2 * 8, BYTEORDER_BIG_ENDIAN), + [NFT_META_NFPROTO] = META_TEMPLATE("nfproto", &nfproto_type, + 1 * 8, BYTEORDER_HOST_ENDIAN), + [NFT_META_L4PROTO] = META_TEMPLATE("l4proto", &inet_protocol_type, + 1 * 8, BYTEORDER_HOST_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", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [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", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [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_BRI_IIFNAME] = META_TEMPLATE("ibrname", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_BRI_OIFNAME] = META_TEMPLATE("obrname", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_PKTTYPE] = META_TEMPLATE("pkttype", &pkttype_type, + BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_CPU] = META_TEMPLATE("cpu", &integer_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_IIFGROUP] = META_TEMPLATE("iifgroup", &devgroup_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_OIFGROUP] = META_TEMPLATE("oifgroup", &devgroup_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_CGROUP] = META_TEMPLATE("cgroup", &integer_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_PRANDOM] = META_TEMPLATE("random", &integer_type, + 4 * BITS_PER_BYTE, + BYTEORDER_BIG_ENDIAN), /* avoid conversion; doesn't have endianess */ + [NFT_META_SECPATH] = META_TEMPLATE("ipsec", &boolean_type, + BITS_PER_BYTE, BYTEORDER_HOST_ENDIAN), + [NFT_META_IIFKIND] = META_TEMPLATE("iifkind", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_OIFKIND] = META_TEMPLATE("oifkind", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_BRI_IIFPVID] = META_TEMPLATE("ibrpvid", &integer_type, + 2 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_BRI_IIFVPROTO] = META_TEMPLATE("ibrvproto", ðertype_type, + 2 * BITS_PER_BYTE, + BYTEORDER_BIG_ENDIAN), + [NFT_META_TIME_NS] = META_TEMPLATE("time", &date_type, + 8 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_TIME_DAY] = META_TEMPLATE("day", &day_type, + 1 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_TIME_HOUR] = META_TEMPLATE("hour", &hour_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_SECMARK] = META_TEMPLATE("secmark", &integer_type, + 32, BYTEORDER_HOST_ENDIAN), + [NFT_META_SDIF] = META_TEMPLATE("sdif", &ifindex_type, + sizeof(int) * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_SDIFNAME] = META_TEMPLATE("sdifname", &ifname_type, + IFNAMSIZ * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN), + [NFT_META_BRI_BROUTE] = META_TEMPLATE("broute", &integer_type, + 1 , BYTEORDER_HOST_ENDIAN), +}; + +static bool meta_key_is_unqualified(enum nft_meta_keys key) +{ + switch (key) { + case NFT_META_IIF: + case NFT_META_OIF: + case NFT_META_IIFNAME: + case NFT_META_OIFNAME: + case NFT_META_IIFGROUP: + case NFT_META_OIFGROUP: + return true; + default: + return false; + } +} + +static void meta_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const char *token = "unknown"; + uint32_t key = expr->meta.key; + + if (key < array_size(meta_templates)) + token = meta_templates[key].token; + + if (meta_key_is_unqualified(key)) + nft_print(octx, "%s", token); + else + nft_print(octx, "meta %s", token); +} + +static bool meta_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->meta.key == e2->meta.key; +} + +static void meta_expr_clone(struct expr *new, const struct expr *expr) +{ + new->meta.key = expr->meta.key; + new->meta.base = expr->meta.base; + new->meta.inner_desc = expr->meta.inner_desc; +} + +/** + * meta_expr_pctx_update - update protocol context based on meta match + * + * @ctx: protocol context + * @expr: relational meta expression + * + * Update LL protocol context based on IIFTYPE meta match in non-LL hooks. + */ +static void meta_expr_pctx_update(struct proto_ctx *ctx, + const struct location *loc, + const struct expr *left, + const struct expr *right) +{ + const struct hook_proto_desc *h = &hook_proto_desc[ctx->family]; + const struct proto_desc *desc; + uint8_t protonum; + + switch (left->meta.key) { + case NFT_META_IIFTYPE: + if (h->base < PROTO_BASE_NETWORK_HDR && + ctx->family != NFPROTO_INET && + ctx->family != NFPROTO_NETDEV) + return; + + desc = proto_dev_desc(mpz_get_uint16(right->value)); + if (desc == NULL) + desc = &proto_unknown; + + proto_ctx_update(ctx, PROTO_BASE_LL_HDR, loc, desc); + break; + case NFT_META_NFPROTO: + protonum = mpz_get_uint8(right->value); + if (protonum == NFPROTO_IPV4 && h->desc == &proto_ip) + break; + else if (protonum == NFPROTO_IPV6 && h->desc == &proto_ip6) + break; + + desc = proto_find_upper(h->desc, protonum); + if (desc == NULL) { + desc = &proto_unknown; + + if (protonum == ctx->family && + h->base == PROTO_BASE_NETWORK_HDR) + desc = h->desc; + } + + proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc); + break; + case NFT_META_L4PROTO: + desc = proto_find_upper(&proto_inet_service, + mpz_get_uint8(right->value)); + if (desc == NULL) + desc = &proto_unknown; + + proto_ctx_update(ctx, PROTO_BASE_TRANSPORT_HDR, loc, desc); + break; + case NFT_META_PROTOCOL: + if (h->base != PROTO_BASE_LL_HDR) + return; + + if (ctx->family != NFPROTO_NETDEV && + ctx->family != NFPROTO_BRIDGE) + return; + + desc = proto_find_upper(h->desc, ntohs(mpz_get_uint16(right->value))); + if (desc == NULL) + desc = &proto_unknown; + + proto_ctx_update(ctx, PROTO_BASE_NETWORK_HDR, loc, desc); + break; + default: + break; + } +} + +#define NFTNL_UDATA_META_KEY 0 +#define NFTNL_UDATA_META_INNER_DESC 1 +#define NFTNL_UDATA_META_MAX 2 + +static int meta_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_KEY, expr->meta.key); + + if (expr->meta.inner_desc) { + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_META_INNER_DESC, + expr->meta.inner_desc->id); + } + + return 0; +} + +static int meta_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_META_KEY: + case NFTNL_UDATA_META_INNER_DESC: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *meta_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_META_MAX + 1] = {}; + const struct proto_desc *desc; + struct expr *expr; + uint32_t key; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + meta_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_META_KEY]) + return NULL; + + key = nftnl_udata_get_u32(ud[NFTNL_UDATA_META_KEY]); + + expr = meta_expr_alloc(&internal_location, key); + + if (ud[NFTNL_UDATA_META_INNER_DESC]) { + desc = find_proto_desc(ud[NFTNL_UDATA_META_INNER_DESC]); + expr->meta.inner_desc = desc; + } + + return expr; +} + +const struct expr_ops meta_expr_ops = { + .type = EXPR_META, + .name = "meta", + .print = meta_expr_print, + .json = meta_expr_json, + .cmp = meta_expr_cmp, + .clone = meta_expr_clone, + .pctx_update = meta_expr_pctx_update, + .build_udata = meta_expr_build_udata, + .parse_udata = meta_expr_parse_udata, +}; + +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, EXPR_META, tmpl->dtype, + tmpl->byteorder, tmpl->len); + expr->meta.key = key; + + switch (key) { + case NFT_META_IIFTYPE: + expr->flags |= EXPR_F_PROTOCOL; + break; + case NFT_META_NFPROTO: + expr->flags |= EXPR_F_PROTOCOL; + expr->meta.base = PROTO_BASE_LL_HDR; + break; + case NFT_META_L4PROTO: + expr->flags |= EXPR_F_PROTOCOL; + expr->meta.base = PROTO_BASE_NETWORK_HDR; + break; + case NFT_META_PROTOCOL: + expr->flags |= EXPR_F_PROTOCOL; + expr->meta.base = PROTO_BASE_LL_HDR; + break; + default: + break; + } + + return expr; +} + +static void meta_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + const char *token = "unknown"; + uint32_t key = stmt->meta.key; + + if (key < array_size(meta_templates)) + token = meta_templates[key].token; + + if (meta_key_is_unqualified(stmt->meta.key)) + nft_print(octx, "%s set ", token); + else + nft_print(octx, "meta %s set ", token); + + expr_print(stmt->meta.expr, octx); +} + +static void meta_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->meta.expr); +} + +static const struct stmt_ops meta_stmt_ops = { + .type = STMT_META, + .name = "meta", + .print = meta_stmt_print, + .json = meta_stmt_json, + .destroy = meta_stmt_destroy, +}; + +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.expr = expr; + + if (key < array_size(meta_templates)) + stmt->meta.tmpl = &meta_templates[key]; + + return stmt; +} + +/* + * @expr: payload expression + * @res: dependency expression + * + * Generate a NFT_META_IIFTYPE expression to check for ethernet frames. + * Only works on input path. + */ +struct stmt *meta_stmt_meta_iiftype(const struct location *loc, uint16_t type) +{ + struct expr *dep, *left, *right; + + left = meta_expr_alloc(loc, NFT_META_IIFTYPE); + right = constant_expr_alloc(loc, &arphrd_type, + BYTEORDER_HOST_ENDIAN, + 2 * BITS_PER_BYTE, &type); + + dep = relational_expr_alloc(loc, OP_EQ, left, right); + return expr_stmt_alloc(&dep->location, dep); +} + +struct error_record *meta_key_parse(const struct location *loc, + const char *str, + unsigned int *value) +{ + const char *sep = ""; + size_t offset = 0; + unsigned int i; + char buf[1024]; + size_t len; + + for (i = 0; i < array_size(meta_templates); i++) { + if (!meta_templates[i].token || strcmp(meta_templates[i].token, str)) + continue; + + *value = i; + return NULL; + } + + /* Backwards compat hack */ + if (strcmp(str, "ibriport") == 0) { + *value = NFT_META_BRI_IIFNAME; + return NULL; + } else if (strcmp(str, "obriport") == 0) { + *value = NFT_META_BRI_OIFNAME; + return NULL; + } else if (strcmp(str, "secpath") == 0) { + *value = NFT_META_SECPATH; + return NULL; + } + + len = (int)sizeof(buf); + + for (i = 0; i < array_size(meta_templates); i++) { + int ret; + + if (!meta_templates[i].token) + continue; + + if (offset) + sep = ", "; + + ret = snprintf(buf+offset, len, "%s%s", sep, meta_templates[i].token); + SNPRINTF_BUFFER_SIZE(ret, &len, &offset); + assert(len > 0); + } + + return error(loc, "syntax error, unexpected %s, known keys are %s", str, buf); +} diff --git a/src/mini-gmp.c b/src/mini-gmp.c new file mode 100644 index 0000000..186dc3a --- /dev/null +++ b/src/mini-gmp.c @@ -0,0 +1,4412 @@ +/* mini-gmp, a minimalistic implementation of a GNU GMP subset. + + Contributed to the GNU project by Niels Möller + +Copyright 1991-1997, 1999-2016 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * 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. + +or both in parallel, as here. + +The GNU MP Library 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 copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + +/* NOTE: All functions in this file which are not declared in + mini-gmp.h are internal, and are not intended to be compatible + neither with GMP nor with future versions of mini-gmp. */ + +/* Much of the material copied from GMP files, including: gmp-impl.h, + longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c, + mpn/generic/lshift.c, mpn/generic/mul_1.c, + mpn/generic/mul_basecase.c, mpn/generic/rshift.c, + mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c, + mpn/generic/submul_1.c. */ + +#include <nft.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> + +#include "mini-gmp.h" + + +/* Macros */ +#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) + +#define GMP_LIMB_MAX (~ (mp_limb_t) 0) +#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1)) + +#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2)) +#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1) + +#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT) +#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1)) + +#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x)) +#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1)) + +#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b)) + +#define GMP_CMP(a,b) (((a) > (b)) - ((a) < (b))) + +#define gmp_assert_nocarry(x) do { \ + mp_limb_t __cy = (x); \ + assert (__cy == 0); \ + } while (0) + +#define gmp_clz(count, x) do { \ + mp_limb_t __clz_x = (x); \ + unsigned __clz_c; \ + for (__clz_c = 0; \ + (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \ + __clz_c += 8) \ + __clz_x <<= 8; \ + for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \ + __clz_x <<= 1; \ + (count) = __clz_c; \ + } while (0) + +#define gmp_ctz(count, x) do { \ + mp_limb_t __ctz_x = (x); \ + unsigned __ctz_c = 0; \ + gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \ + (count) = GMP_LIMB_BITS - 1 - __ctz_c; \ + } while (0) + +#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) + (bl); \ + (sh) = (ah) + (bh) + (__x < (al)); \ + (sl) = __x; \ + } while (0) + +#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \ + do { \ + mp_limb_t __x; \ + __x = (al) - (bl); \ + (sh) = (ah) - (bh) - ((al) < (bl)); \ + (sl) = __x; \ + } while (0) + +#define gmp_umul_ppmm(w1, w0, u, v) \ + do { \ + mp_limb_t __x0, __x1, __x2, __x3; \ + unsigned __ul, __vl, __uh, __vh; \ + mp_limb_t __u = (u), __v = (v); \ + \ + __ul = __u & GMP_LLIMB_MASK; \ + __uh = __u >> (GMP_LIMB_BITS / 2); \ + __vl = __v & GMP_LLIMB_MASK; \ + __vh = __v >> (GMP_LIMB_BITS / 2); \ + \ + __x0 = (mp_limb_t) __ul * __vl; \ + __x1 = (mp_limb_t) __ul * __vh; \ + __x2 = (mp_limb_t) __uh * __vl; \ + __x3 = (mp_limb_t) __uh * __vh; \ + \ + __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \ + \ + (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \ + (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \ + } while (0) + +#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ + do { \ + mp_limb_t _qh, _ql, _r, _mask; \ + gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ + gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ + _r = (nl) - _qh * (d); \ + _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ + _qh += _mask; \ + _r += _mask & (d); \ + if (_r >= (d)) \ + { \ + _r -= (d); \ + _qh++; \ + } \ + \ + (r) = _r; \ + (q) = _qh; \ + } while (0) + +#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ + do { \ + mp_limb_t _q0, _t1, _t0, _mask; \ + gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \ + gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ + \ + /* Compute the two most significant limbs of n - q'd */ \ + (r1) = (n1) - (d1) * (q); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ + gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ + (q)++; \ + \ + /* Conditionally adjust q and the remainders */ \ + _mask = - (mp_limb_t) ((r1) >= _q0); \ + (q) += _mask; \ + gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \ + if ((r1) >= (d1)) \ + { \ + if ((r1) > (d1) || (r0) >= (d0)) \ + { \ + (q)++; \ + gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \ + } \ + } \ + } while (0) + +/* Swap macros. */ +#define MP_LIMB_T_SWAP(x, y) \ + do { \ + mp_limb_t __mp_limb_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_limb_t_swap__tmp; \ + } while (0) +#define MP_SIZE_T_SWAP(x, y) \ + do { \ + mp_size_t __mp_size_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_size_t_swap__tmp; \ + } while (0) +#define MP_BITCNT_T_SWAP(x,y) \ + do { \ + mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_bitcnt_t_swap__tmp; \ + } while (0) +#define MP_PTR_SWAP(x, y) \ + do { \ + mp_ptr __mp_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_ptr_swap__tmp; \ + } while (0) +#define MP_SRCPTR_SWAP(x, y) \ + do { \ + mp_srcptr __mp_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mp_srcptr_swap__tmp; \ + } while (0) + +#define MPN_PTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_PTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) +#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \ + do { \ + MP_SRCPTR_SWAP (xp, yp); \ + MP_SIZE_T_SWAP (xs, ys); \ + } while(0) + +#define MPZ_PTR_SWAP(x, y) \ + do { \ + mpz_ptr __mpz_ptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_ptr_swap__tmp; \ + } while (0) +#define MPZ_SRCPTR_SWAP(x, y) \ + do { \ + mpz_srcptr __mpz_srcptr_swap__tmp = (x); \ + (x) = (y); \ + (y) = __mpz_srcptr_swap__tmp; \ + } while (0) + +const int mp_bits_per_limb = GMP_LIMB_BITS; + + +/* Memory allocation and other helper functions. */ +static void +gmp_die (const char *msg) +{ + fprintf (stderr, "%s\n", msg); + abort(); +} + +static void * +gmp_default_alloc (size_t size) +{ + void *p; + + assert (size > 0); + + p = malloc (size); + if (!p) + gmp_die("gmp_default_alloc: Virtual memory exhausted."); + + return p; +} + +static void * +gmp_default_realloc (void *old, size_t old_size, size_t new_size) +{ + void * p; + + p = realloc (old, new_size); + + if (!p) + gmp_die("gmp_default_realloc: Virtual memory exhausted."); + + return p; +} + +static void +gmp_default_free (void *p, size_t size) +{ + free (p); +} + +static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc; +static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc; +static void (*gmp_free_func) (void *, size_t) = gmp_default_free; + +void +mp_get_memory_functions (void *(**alloc_func) (size_t), + void *(**realloc_func) (void *, size_t, size_t), + void (**free_func) (void *, size_t)) +{ + if (alloc_func) + *alloc_func = gmp_allocate_func; + + if (realloc_func) + *realloc_func = gmp_reallocate_func; + + if (free_func) + *free_func = gmp_free_func; +} + +void +mp_set_memory_functions (void *(*alloc_func) (size_t), + void *(*realloc_func) (void *, size_t, size_t), + void (*free_func) (void *, size_t)) +{ + if (!alloc_func) + alloc_func = gmp_default_alloc; + if (!realloc_func) + realloc_func = gmp_default_realloc; + if (!free_func) + free_func = gmp_default_free; + + gmp_allocate_func = alloc_func; + gmp_reallocate_func = realloc_func; + gmp_free_func = free_func; +} + +#define gmp_xalloc(size) ((*gmp_allocate_func)((size))) +#define gmp_free(p) ((*gmp_free_func) ((p), 0)) + +static mp_ptr +gmp_xalloc_limbs (mp_size_t size) +{ + return (mp_ptr) gmp_xalloc (size * sizeof (mp_limb_t)); +} + +static mp_ptr +gmp_xrealloc_limbs (mp_ptr old, mp_size_t size) +{ + assert (size > 0); + return (mp_ptr) (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t)); +} + + +/* MPN interface */ + +void +mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + mp_size_t i; + for (i = 0; i < n; i++) + d[i] = s[i]; +} + +void +mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n) +{ + while (--n >= 0) + d[n] = s[n]; +} + +int +mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + while (--n >= 0) + { + if (ap[n] != bp[n]) + return ap[n] > bp[n] ? 1 : -1; + } + return 0; +} + +static int +mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + if (an != bn) + return an < bn ? -1 : 1; + else + return mpn_cmp (ap, bp, an); +} + +static mp_size_t +mpn_normalized_size (mp_srcptr xp, mp_size_t n) +{ + while (n > 0 && xp[n-1] == 0) + --n; + return n; +} + +int +mpn_zero_p(mp_srcptr rp, mp_size_t n) +{ + return mpn_normalized_size (rp, n) == 0; +} + +void +mpn_zero (mp_ptr rp, mp_size_t n) +{ + while (--n >= 0) + rp[n] = 0; +} + +mp_limb_t +mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + i = 0; + do + { + mp_limb_t r = ap[i] + b; + /* Carry out */ + b = (r < b); + rp[i] = r; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b, r; + a = ap[i]; b = bp[i]; + r = a + cy; + cy = (r < cy); + r += b; + cy += (r < b); + rp[i] = r; + } + return cy; +} + +mp_limb_t +mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_add_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b) +{ + mp_size_t i; + + assert (n > 0); + + i = 0; + do + { + mp_limb_t a = ap[i]; + /* Carry out */ + mp_limb_t cy = a < b; + rp[i] = a - b; + b = cy; + } + while (++i < n); + + return b; +} + +mp_limb_t +mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mp_size_t i; + mp_limb_t cy; + + for (i = 0, cy = 0; i < n; i++) + { + mp_limb_t a, b; + a = ap[i]; b = bp[i]; + b += cy; + cy = (b < cy); + cy += (a < b); + rp[i] = a - b; + } + return cy; +} + +mp_limb_t +mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn) +{ + mp_limb_t cy; + + assert (an >= bn); + + cy = mpn_sub_n (rp, ap, bp, bn); + if (an > bn) + cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy); + return cy; +} + +mp_limb_t +mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl + lpl; + cl += lpl < rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl) +{ + mp_limb_t ul, cl, hpl, lpl, rl; + + assert (n >= 1); + + cl = 0; + do + { + ul = *up++; + gmp_umul_ppmm (hpl, lpl, ul, vl); + + lpl += cl; + cl = (lpl < cl) + hpl; + + rl = *rp; + lpl = rl - lpl; + cl += lpl > rl; + *rp++ = lpl; + } + while (--n != 0); + + return cl; +} + +mp_limb_t +mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn) +{ + assert (un >= vn); + assert (vn >= 1); + + /* We first multiply by the low order limb. This result can be + stored, not added, to rp. We also avoid a loop for zeroing this + way. */ + + rp[un] = mpn_mul_1 (rp, up, un, vp[0]); + + /* Now accumulate the product of up[] and the next higher limb from + vp[]. */ + + while (--vn >= 1) + { + rp += 1, vp += 1; + rp[un] = mpn_addmul_1 (rp, up, un, vp[0]); + } + return rp[un]; +} + +void +mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n) +{ + mpn_mul (rp, ap, n, bp, n); +} + +void +mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n) +{ + mpn_mul (rp, ap, n, ap, n); +} + +mp_limb_t +mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + up += n; + rp += n; + + tnc = GMP_LIMB_BITS - cnt; + low_limb = *--up; + retval = low_limb >> tnc; + high_limb = (low_limb << cnt); + + while (--n != 0) + { + low_limb = *--up; + *--rp = high_limb | (low_limb >> tnc); + high_limb = (low_limb << cnt); + } + *--rp = high_limb; + + return retval; +} + +mp_limb_t +mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt) +{ + mp_limb_t high_limb, low_limb; + unsigned int tnc; + mp_limb_t retval; + + assert (n >= 1); + assert (cnt >= 1); + assert (cnt < GMP_LIMB_BITS); + + tnc = GMP_LIMB_BITS - cnt; + high_limb = *up++; + retval = (high_limb << tnc); + low_limb = high_limb >> cnt; + + while (--n != 0) + { + high_limb = *up++; + *rp++ = low_limb | (high_limb << tnc); + low_limb = high_limb >> cnt; + } + *rp = low_limb; + + return retval; +} + +static mp_bitcnt_t +mpn_common_scan (mp_limb_t limb, mp_size_t i, mp_srcptr up, mp_size_t un, + mp_limb_t ux) +{ + unsigned cnt; + + assert (ux == 0 || ux == GMP_LIMB_MAX); + assert (0 <= i && i <= un ); + + while (limb == 0) + { + i++; + if (i == un) + return (ux == 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS); + limb = ux ^ up[i]; + } + gmp_ctz (cnt, limb); + return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt; +} + +mp_bitcnt_t +mpn_scan1 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan ( ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, 0); +} + +mp_bitcnt_t +mpn_scan0 (mp_srcptr ptr, mp_bitcnt_t bit) +{ + mp_size_t i; + i = bit / GMP_LIMB_BITS; + + return mpn_common_scan (~ptr[i] & (GMP_LIMB_MAX << (bit % GMP_LIMB_BITS)), + i, ptr, i, GMP_LIMB_MAX); +} + +void +mpn_com (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (--n >= 0) + *rp++ = ~ *up++; +} + +mp_limb_t +mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) +{ + while (*up == 0) + { + *rp = 0; + if (!--n) + return 0; + ++up; ++rp; + } + *rp = - *up; + mpn_com (++rp, ++up, --n); + return 1; +} + + +/* MPN division interface. */ + +/* The 3/2 inverse is defined as + + m = floor( (B^3-1) / (B u1 + u0)) - B +*/ +mp_limb_t +mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0) +{ + mp_limb_t r, p, m, ql; + unsigned ul, uh, qh; + + assert (u1 >= GMP_LIMB_HIGHBIT); + + /* For notation, let b denote the half-limb base, so that B = b^2. + Split u1 = b uh + ul. */ + ul = u1 & GMP_LLIMB_MASK; + uh = u1 >> (GMP_LIMB_BITS / 2); + + /* Approximation of the high half of quotient. Differs from the 2/1 + inverse of the half limb uh, since we have already subtracted + u0. */ + qh = ~u1 / uh; + + /* Adjust to get a half-limb 3/2 inverse, i.e., we want + + qh' = floor( (b^3 - 1) / u) - b = floor ((b^3 - b u - 1) / u + = floor( (b (~u) + b-1) / u), + + and the remainder + + r = b (~u) + b-1 - qh (b uh + ul) + = b (~u - qh uh) + b-1 - qh ul + + Subtraction of qh ul may underflow, which implies adjustments. + But by normalization, 2 u >= B > qh ul, so we need to adjust by + at most 2. + */ + + r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK; + + p = (mp_limb_t) qh * ul; + /* Adjustment steps taken from udiv_qrnnd_c */ + if (r < p) + { + qh--; + r += u1; + if (r >= u1) /* i.e. we didn't get carry when adding to r */ + if (r < p) + { + qh--; + r += u1; + } + } + r -= p; + + /* Low half of the quotient is + + ql = floor ( (b r + b-1) / u1). + + This is a 3/2 division (on half-limbs), for which qh is a + suitable inverse. */ + + p = (r >> (GMP_LIMB_BITS / 2)) * qh + r; + /* Unlike full-limb 3/2, we can add 1 without overflow. For this to + work, it is essential that ql is a full mp_limb_t. */ + ql = (p >> (GMP_LIMB_BITS / 2)) + 1; + + /* By the 3/2 trick, we don't need the high half limb. */ + r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1; + + if (r >= (p << (GMP_LIMB_BITS / 2))) + { + ql--; + r += u1; + } + m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql; + if (r >= u1) + { + m++; + r -= u1; + } + + /* Now m is the 2/1 invers of u1. If u0 > 0, adjust it to become a + 3/2 inverse. */ + if (u0 > 0) + { + mp_limb_t th, tl; + r = ~r; + r += u0; + if (r < u0) + { + m--; + if (r >= u1) + { + m--; + r -= u1; + } + r -= u1; + } + gmp_umul_ppmm (th, tl, u0, m); + r += th; + if (r < th) + { + m--; + m -= ((r > u1) | ((r == u1) & (tl > u0))); + } + } + + return m; +} + +struct gmp_div_inverse +{ + /* Normalization shift count. */ + unsigned shift; + /* Normalized divisor (d0 unused for mpn_div_qr_1) */ + mp_limb_t d1, d0; + /* Inverse, for 2/1 or 3/2. */ + mp_limb_t di; +}; + +static void +mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d) +{ + unsigned shift; + + assert (d > 0); + gmp_clz (shift, d); + inv->shift = shift; + inv->d1 = d << shift; + inv->di = mpn_invert_limb (inv->d1); +} + +static void +mpn_div_qr_2_invert (struct gmp_div_inverse *inv, + mp_limb_t d1, mp_limb_t d0) +{ + unsigned shift; + + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 <<= shift; + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); +} + +static void +mpn_div_qr_invert (struct gmp_div_inverse *inv, + mp_srcptr dp, mp_size_t dn) +{ + assert (dn > 0); + + if (dn == 1) + mpn_div_qr_1_invert (inv, dp[0]); + else if (dn == 2) + mpn_div_qr_2_invert (inv, dp[1], dp[0]); + else + { + unsigned shift; + mp_limb_t d1, d0; + + d1 = dp[dn-1]; + d0 = dp[dn-2]; + assert (d1 > 0); + gmp_clz (shift, d1); + inv->shift = shift; + if (shift > 0) + { + d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift)); + d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift)); + } + inv->d1 = d1; + inv->d0 = d0; + inv->di = mpn_invert_3by2 (d1, d0); + } +} + +/* Not matching current public gmp interface, rather corresponding to + the sbpi1_div_* functions. */ +static mp_limb_t +mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + mp_limb_t d, di; + mp_limb_t r; + mp_ptr tp = NULL; + + if (inv->shift > 0) + { + tp = gmp_xalloc_limbs (nn); + r = mpn_lshift (tp, np, nn, inv->shift); + np = tp; + } + else + r = 0; + + d = inv->d1; + di = inv->di; + while (--nn >= 0) + { + mp_limb_t q; + + gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di); + if (qp) + qp[nn] = q; + } + if (inv->shift > 0) + gmp_free (tp); + + return r >> inv->shift; +} + +static mp_limb_t +mpn_div_qr_1 (mp_ptr qp, mp_srcptr np, mp_size_t nn, mp_limb_t d) +{ + assert (d > 0); + + /* Special case for powers of two. */ + if ((d & (d-1)) == 0) + { + mp_limb_t r = np[0] & (d-1); + if (qp) + { + if (d <= 1) + mpn_copyi (qp, np, nn); + else + { + unsigned shift; + gmp_ctz (shift, d); + mpn_rshift (qp, np, nn, shift); + } + } + return r; + } + else + { + struct gmp_div_inverse inv; + mpn_div_qr_1_invert (&inv, d); + return mpn_div_qr_1_preinv (qp, np, nn, &inv); + } +} + +static void +mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr rp, mp_srcptr np, mp_size_t nn, + const struct gmp_div_inverse *inv) +{ + unsigned shift; + mp_size_t i; + mp_limb_t d1, d0, di, r1, r0; + mp_ptr tp; + + assert (nn >= 2); + shift = inv->shift; + d1 = inv->d1; + d0 = inv->d0; + di = inv->di; + + if (shift > 0) + { + tp = gmp_xalloc_limbs (nn); + r1 = mpn_lshift (tp, np, nn, shift); + np = tp; + } + else + r1 = 0; + + r0 = np[nn - 1]; + + i = nn - 2; + do + { + mp_limb_t n0, q; + n0 = np[i]; + gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di); + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + if (shift > 0) + { + assert ((r0 << (GMP_LIMB_BITS - shift)) == 0); + r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift)); + r1 >>= shift; + + gmp_free (tp); + } + + rp[1] = r1; + rp[0] = r0; +} + +#if 0 +static void +mpn_div_qr_2 (mp_ptr qp, mp_ptr rp, mp_srcptr np, mp_size_t nn, + mp_limb_t d1, mp_limb_t d0) +{ + struct gmp_div_inverse inv; + assert (nn >= 2); + + mpn_div_qr_2_invert (&inv, d1, d0); + mpn_div_qr_2_preinv (qp, rp, np, nn, &inv); +} +#endif + +static void +mpn_div_qr_pi1 (mp_ptr qp, + mp_ptr np, mp_size_t nn, mp_limb_t n1, + mp_srcptr dp, mp_size_t dn, + mp_limb_t dinv) +{ + mp_size_t i; + + mp_limb_t d1, d0; + mp_limb_t cy, cy1; + mp_limb_t q; + + assert (dn > 2); + assert (nn >= dn); + + d1 = dp[dn - 1]; + d0 = dp[dn - 2]; + + assert ((d1 & GMP_LIMB_HIGHBIT) != 0); + /* Iteration variable is the index of the q limb. + * + * We divide <n1, np[dn-1+i], np[dn-2+i], np[dn-3+i],..., np[i]> + * by <d1, d0, dp[dn-3], ..., dp[0] > + */ + + i = nn - dn; + do + { + mp_limb_t n0 = np[dn-1+i]; + + if (n1 == d1 && n0 == d0) + { + q = GMP_LIMB_MAX; + mpn_submul_1 (np+i, dp, dn, q); + n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */ + } + else + { + gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv); + + cy = mpn_submul_1 (np + i, dp, dn-2, q); + + cy1 = n0 < cy; + n0 = n0 - cy; + cy = n1 < cy1; + n1 = n1 - cy1; + np[dn-2+i] = n0; + + if (cy != 0) + { + n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1); + q--; + } + } + + if (qp) + qp[i] = q; + } + while (--i >= 0); + + np[dn - 1] = n1; +} + +static void +mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn, + mp_srcptr dp, mp_size_t dn, + const struct gmp_div_inverse *inv) +{ + assert (dn > 0); + assert (nn >= dn); + + if (dn == 1) + np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv); + else if (dn == 2) + mpn_div_qr_2_preinv (qp, np, np, nn, inv); + else + { + mp_limb_t nh; + unsigned shift; + + assert (inv->d1 == dp[dn-1]); + assert (inv->d0 == dp[dn-2]); + assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0); + + shift = inv->shift; + if (shift > 0) + nh = mpn_lshift (np, np, nn, shift); + else + nh = 0; + + mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di); + + if (shift > 0) + gmp_assert_nocarry (mpn_rshift (np, np, dn, shift)); + } +} + +static void +mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) +{ + struct gmp_div_inverse inv; + mp_ptr tp = NULL; + + assert (dn > 0); + assert (nn >= dn); + + mpn_div_qr_invert (&inv, dp, dn); + if (dn > 2 && inv.shift > 0) + { + tp = gmp_xalloc_limbs (dn); + gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift)); + dp = tp; + } + mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv); + if (tp) + gmp_free (tp); +} + + +/* MPN base conversion. */ +static unsigned +mpn_base_power_of_two_p (unsigned b) +{ + switch (b) + { + case 2: return 1; + case 4: return 2; + case 8: return 3; + case 16: return 4; + case 32: return 5; + case 64: return 6; + case 128: return 7; + case 256: return 8; + default: return 0; + } +} + +struct mpn_base_info +{ + /* bb is the largest power of the base which fits in one limb, and + exp is the corresponding exponent. */ + unsigned exp; + mp_limb_t bb; +}; + +static void +mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b) +{ + mp_limb_t m; + mp_limb_t p; + unsigned exp; + + m = GMP_LIMB_MAX / b; + for (exp = 1, p = b; p <= m; exp++) + p *= b; + + info->exp = exp; + info->bb = p; +} + +static mp_bitcnt_t +mpn_limb_size_in_base_2 (mp_limb_t u) +{ + unsigned shift; + + assert (u > 0); + gmp_clz (shift, u); + return GMP_LIMB_BITS - shift; +} + +static size_t +mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un) +{ + unsigned char mask; + size_t sn, j; + mp_size_t i; + unsigned shift; + + sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]) + + bits - 1) / bits; + + mask = (1U << bits) - 1; + + for (i = 0, j = sn, shift = 0; j-- > 0;) + { + unsigned char digit = up[i] >> shift; + + shift += bits; + + if (shift >= GMP_LIMB_BITS && ++i < un) + { + shift -= GMP_LIMB_BITS; + digit |= up[i] << (bits - shift); + } + sp[j] = digit & mask; + } + return sn; +} + +/* We generate digits from the least significant end, and reverse at + the end. */ +static size_t +mpn_limb_get_str (unsigned char *sp, mp_limb_t w, + const struct gmp_div_inverse *binv) +{ + mp_size_t i; + for (i = 0; w > 0; i++) + { + mp_limb_t h, l, r; + + h = w >> (GMP_LIMB_BITS - binv->shift); + l = w << binv->shift; + + gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di); + assert ( (r << (GMP_LIMB_BITS - binv->shift)) == 0); + r >>= binv->shift; + + sp[i] = r; + } + return i; +} + +static size_t +mpn_get_str_other (unsigned char *sp, + int base, const struct mpn_base_info *info, + mp_ptr up, mp_size_t un) +{ + struct gmp_div_inverse binv; + size_t sn; + size_t i; + + mpn_div_qr_1_invert (&binv, base); + + sn = 0; + + if (un > 1) + { + struct gmp_div_inverse bbinv; + mpn_div_qr_1_invert (&bbinv, info->bb); + + do + { + mp_limb_t w; + size_t done; + w = mpn_div_qr_1_preinv (up, up, un, &bbinv); + un -= (up[un-1] == 0); + done = mpn_limb_get_str (sp + sn, w, &binv); + + for (sn += done; done < info->exp; done++) + sp[sn++] = 0; + } + while (un > 1); + } + sn += mpn_limb_get_str (sp + sn, up[0], &binv); + + /* Reverse order */ + for (i = 0; 2*i + 1 < sn; i++) + { + unsigned char t = sp[i]; + sp[i] = sp[sn - i - 1]; + sp[sn - i - 1] = t; + } + + return sn; +} + +size_t +mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un) +{ + unsigned bits; + + assert (un > 0); + assert (up[un-1] > 0); + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_get_str_bits (sp, bits, up, un); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_get_str_other (sp, base, &info, up, un); + } +} + +static mp_size_t +mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn, + unsigned bits) +{ + mp_size_t rn; + size_t j; + unsigned shift; + + for (j = sn, rn = 0, shift = 0; j-- > 0; ) + { + if (shift == 0) + { + rp[rn++] = sp[j]; + shift += bits; + } + else + { + rp[rn-1] |= (mp_limb_t) sp[j] << shift; + shift += bits; + if (shift >= GMP_LIMB_BITS) + { + shift -= GMP_LIMB_BITS; + if (shift > 0) + rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift); + } + } + } + rn = mpn_normalized_size (rp, rn); + return rn; +} + +/* Result is usually normalized, except for all-zero input, in which + case a single zero limb is written at *RP, and 1 is returned. */ +static mp_size_t +mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn, + mp_limb_t b, const struct mpn_base_info *info) +{ + mp_size_t rn; + mp_limb_t w; + unsigned k; + size_t j; + + assert (sn > 0); + + k = 1 + (sn - 1) % info->exp; + + j = 0; + w = sp[j++]; + while (--k != 0) + w = w * b + sp[j++]; + + rp[0] = w; + + for (rn = 1; j < sn;) + { + mp_limb_t cy; + + w = sp[j++]; + for (k = 1; k < info->exp; k++) + w = w * b + sp[j++]; + + cy = mpn_mul_1 (rp, rp, rn, info->bb); + cy += mpn_add_1 (rp, rp, rn, w); + if (cy > 0) + rp[rn++] = cy; + } + assert (j == sn); + + return rn; +} + +mp_size_t +mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) +{ + unsigned bits; + + if (sn == 0) + return 0; + + bits = mpn_base_power_of_two_p (base); + if (bits) + return mpn_set_str_bits (rp, sp, sn, bits); + else + { + struct mpn_base_info info; + + mpn_get_base_info (&info, base); + return mpn_set_str_other (rp, sp, sn, base, &info); + } +} + + +/* MPZ interface */ +void +mpz_init (mpz_t r) +{ + static const mp_limb_t dummy_limb = 0xc1a0; + + r->_mp_alloc = 0; + r->_mp_size = 0; + r->_mp_d = (mp_ptr) &dummy_limb; +} + +/* The utility of this function is a bit limited, since many functions + assigns the result variable using mpz_swap. */ +void +mpz_init2 (mpz_t r, mp_bitcnt_t bits) +{ + mp_size_t rn; + + bits -= (bits != 0); /* Round down, except if 0 */ + rn = 1 + bits / GMP_LIMB_BITS; + + r->_mp_alloc = rn; + r->_mp_size = 0; + r->_mp_d = gmp_xalloc_limbs (rn); +} + +void +mpz_clear (mpz_t r) +{ + if (r->_mp_alloc) + gmp_free (r->_mp_d); +} + +static mp_ptr +mpz_realloc (mpz_t r, mp_size_t size) +{ + size = GMP_MAX (size, 1); + + if (r->_mp_alloc) + r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size); + else + r->_mp_d = gmp_xalloc_limbs (size); + r->_mp_alloc = size; + + if (GMP_ABS (r->_mp_size) > size) + r->_mp_size = 0; + + return r->_mp_d; +} + +/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */ +#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ + ? mpz_realloc(z,n) \ + : (z)->_mp_d) + +/* MPZ assignment and basic conversions. */ +void +mpz_set_si (mpz_t r, signed long int x) +{ + if (x >= 0) + mpz_set_ui (r, x); + else /* (x < 0) */ + { + r->_mp_size = -1; + MPZ_REALLOC (r, 1)[0] = GMP_NEG_CAST (unsigned long int, x); + } +} + +void +mpz_set_ui (mpz_t r, unsigned long int x) +{ + if (x > 0) + { + r->_mp_size = 1; + MPZ_REALLOC (r, 1)[0] = x; + } + else + r->_mp_size = 0; +} + +void +mpz_set (mpz_t r, const mpz_t x) +{ + /* Allow the NOP r == x */ + if (r != x) + { + mp_size_t n; + mp_ptr rp; + + n = GMP_ABS (x->_mp_size); + rp = MPZ_REALLOC (r, n); + + mpn_copyi (rp, x->_mp_d, n); + r->_mp_size = x->_mp_size; + } +} + +void +mpz_init_set_si (mpz_t r, signed long int x) +{ + mpz_init (r); + mpz_set_si (r, x); +} + +void +mpz_init_set_ui (mpz_t r, unsigned long int x) +{ + mpz_init (r); + mpz_set_ui (r, x); +} + +void +mpz_init_set (mpz_t r, const mpz_t x) +{ + mpz_init (r); + mpz_set (r, x); +} + +int +mpz_fits_slong_p (const mpz_t u) +{ + mp_size_t us = u->_mp_size; + + if (us == 1) + return u->_mp_d[0] < GMP_LIMB_HIGHBIT; + else if (us == -1) + return u->_mp_d[0] <= GMP_LIMB_HIGHBIT; + else + return (us == 0); +} + +int +mpz_fits_ulong_p (const mpz_t u) +{ + mp_size_t us = u->_mp_size; + + return (us == (us > 0)); +} + +long int +mpz_get_si (const mpz_t u) +{ + if (u->_mp_size < 0) + /* This expression is necessary to properly handle 0x80000000 */ + return -1 - (long) ((u->_mp_d[0] - 1) & ~GMP_LIMB_HIGHBIT); + else + return (long) (mpz_get_ui (u) & ~GMP_LIMB_HIGHBIT); +} + +unsigned long int +mpz_get_ui (const mpz_t u) +{ + return u->_mp_size == 0 ? 0 : u->_mp_d[0]; +} + +size_t +mpz_size (const mpz_t u) +{ + return GMP_ABS (u->_mp_size); +} + +mp_limb_t +mpz_getlimbn (const mpz_t u, mp_size_t n) +{ + if (n >= 0 && n < GMP_ABS (u->_mp_size)) + return u->_mp_d[n]; + else + return 0; +} + +void +mpz_realloc2 (mpz_t x, mp_bitcnt_t n) +{ + mpz_realloc (x, 1 + (n - (n != 0)) / GMP_LIMB_BITS); +} + +mp_srcptr +mpz_limbs_read (mpz_srcptr x) +{ + return x->_mp_d; +} + +mp_ptr +mpz_limbs_modify (mpz_t x, mp_size_t n) +{ + assert (n > 0); + return MPZ_REALLOC (x, n); +} + +mp_ptr +mpz_limbs_write (mpz_t x, mp_size_t n) +{ + return mpz_limbs_modify (x, n); +} + +void +mpz_limbs_finish (mpz_t x, mp_size_t xs) +{ + mp_size_t xn; + xn = mpn_normalized_size (x->_mp_d, GMP_ABS (xs)); + x->_mp_size = xs < 0 ? -xn : xn; +} + +mpz_srcptr +mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) +{ + x->_mp_alloc = 0; + x->_mp_d = (mp_ptr) xp; + mpz_limbs_finish (x, xs); + return x; +} + + +/* Conversions and comparison to double. */ +void +mpz_set_d (mpz_t r, double x) +{ + int sign; + mp_ptr rp; + mp_size_t rn, i; + double B; + double Bi; + mp_limb_t f; + + /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is + zero or infinity. */ + if (x != x || x == x * 0.5) + { + r->_mp_size = 0; + return; + } + + sign = x < 0.0 ; + if (sign) + x = - x; + + if (x < 1.0) + { + r->_mp_size = 0; + return; + } + B = 2.0 * (double) GMP_LIMB_HIGHBIT; + Bi = 1.0 / B; + for (rn = 1; x >= B; rn++) + x *= Bi; + + rp = MPZ_REALLOC (r, rn); + + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + i = rn-1; + rp[i] = f; + while (--i >= 0) + { + x = B * x; + f = (mp_limb_t) x; + x -= f; + assert (x < 1.0); + rp[i] = f; + } + + r->_mp_size = sign ? - rn : rn; +} + +void +mpz_init_set_d (mpz_t r, double x) +{ + mpz_init (r); + mpz_set_d (r, x); +} + +double +mpz_get_d (const mpz_t u) +{ + mp_size_t un; + double x; + double B = 2.0 * (double) GMP_LIMB_HIGHBIT; + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + return 0.0; + + x = u->_mp_d[--un]; + while (un > 0) + x = B*x + u->_mp_d[--un]; + + if (u->_mp_size < 0) + x = -x; + + return x; +} + +int +mpz_cmpabs_d (const mpz_t x, double d) +{ + mp_size_t xn; + double B, Bi; + mp_size_t i; + + xn = x->_mp_size; + d = GMP_ABS (d); + + if (xn != 0) + { + xn = GMP_ABS (xn); + + B = 2.0 * (double) GMP_LIMB_HIGHBIT; + Bi = 1.0 / B; + + /* Scale d so it can be compared with the top limb. */ + for (i = 1; i < xn; i++) + d *= Bi; + + if (d >= B) + return -1; + + /* Compare floor(d) to top limb, subtract and cancel when equal. */ + for (i = xn; i-- > 0;) + { + mp_limb_t f, xl; + + f = (mp_limb_t) d; + xl = x->_mp_d[i]; + if (xl > f) + return 1; + else if (xl < f) + return -1; + d = B * (d - f); + } + } + return - (d > 0.0); +} + +int +mpz_cmp_d (const mpz_t x, double d) +{ + if (x->_mp_size < 0) + { + if (d >= 0.0) + return -1; + else + return -mpz_cmpabs_d (x, d); + } + else + { + if (d < 0.0) + return 1; + else + return mpz_cmpabs_d (x, d); + } +} + + +/* MPZ comparisons and the like. */ +int +mpz_sgn (const mpz_t u) +{ + return GMP_CMP (u->_mp_size, 0); +} + +int +mpz_cmp_si (const mpz_t u, long v) +{ + mp_size_t usize = u->_mp_size; + + if (usize < -1) + return -1; + else if (v >= 0) + return mpz_cmp_ui (u, v); + else if (usize >= 0) + return 1; + else /* usize == -1 */ + return GMP_CMP (GMP_NEG_CAST (mp_limb_t, v), u->_mp_d[0]); +} + +int +mpz_cmp_ui (const mpz_t u, unsigned long v) +{ + mp_size_t usize = u->_mp_size; + + if (usize > 1) + return 1; + else if (usize < 0) + return -1; + else + return GMP_CMP (mpz_get_ui (u), v); +} + +int +mpz_cmp (const mpz_t a, const mpz_t b) +{ + mp_size_t asize = a->_mp_size; + mp_size_t bsize = b->_mp_size; + + if (asize != bsize) + return (asize < bsize) ? -1 : 1; + else if (asize >= 0) + return mpn_cmp (a->_mp_d, b->_mp_d, asize); + else + return mpn_cmp (b->_mp_d, a->_mp_d, -asize); +} + +int +mpz_cmpabs_ui (const mpz_t u, unsigned long v) +{ + if (GMP_ABS (u->_mp_size) > 1) + return 1; + else + return GMP_CMP (mpz_get_ui (u), v); +} + +int +mpz_cmpabs (const mpz_t u, const mpz_t v) +{ + return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size), + v->_mp_d, GMP_ABS (v->_mp_size)); +} + +void +mpz_abs (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = GMP_ABS (r->_mp_size); +} + +void +mpz_neg (mpz_t r, const mpz_t u) +{ + mpz_set (r, u); + r->_mp_size = -r->_mp_size; +} + +void +mpz_swap (mpz_t u, mpz_t v) +{ + MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size); + MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc); + MP_PTR_SWAP (u->_mp_d, v->_mp_d); +} + + +/* MPZ addition and subtraction */ + +/* Adds to the absolute value. Returns new size, but doesn't store it. */ +static mp_size_t +mpz_abs_add_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mp_size_t an; + mp_ptr rp; + mp_limb_t cy; + + an = GMP_ABS (a->_mp_size); + if (an == 0) + { + MPZ_REALLOC (r, 1)[0] = b; + return b > 0; + } + + rp = MPZ_REALLOC (r, an + 1); + + cy = mpn_add_1 (rp, a->_mp_d, an, b); + rp[an] = cy; + an += cy; + + return an; +} + +/* Subtract from the absolute value. Returns new size, (or -1 on underflow), + but doesn't store it. */ +static mp_size_t +mpz_abs_sub_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_ptr rp; + + if (an == 0) + { + MPZ_REALLOC (r, 1)[0] = b; + return -(b > 0); + } + rp = MPZ_REALLOC (r, an); + if (an == 1 && a->_mp_d[0] < b) + { + rp[0] = b - a->_mp_d[0]; + return -1; + } + else + { + gmp_assert_nocarry (mpn_sub_1 (rp, a->_mp_d, an, b)); + return mpn_normalized_size (rp, an); + } +} + +void +mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + if (a->_mp_size >= 0) + r->_mp_size = mpz_abs_add_ui (r, a, b); + else + r->_mp_size = -mpz_abs_sub_ui (r, a, b); +} + +void +mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b) +{ + if (a->_mp_size < 0) + r->_mp_size = -mpz_abs_add_ui (r, a, b); + else + r->_mp_size = mpz_abs_sub_ui (r, a, b); +} + +void +mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b) +{ + if (b->_mp_size < 0) + r->_mp_size = mpz_abs_add_ui (r, b, a); + else + r->_mp_size = -mpz_abs_sub_ui (r, b, a); +} + +static mp_size_t +mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + mp_ptr rp; + mp_limb_t cy; + + if (an < bn) + { + MPZ_SRCPTR_SWAP (a, b); + MP_SIZE_T_SWAP (an, bn); + } + + rp = MPZ_REALLOC (r, an + 1); + cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn); + + rp[an] = cy; + + return an + cy; +} + +static mp_size_t +mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t an = GMP_ABS (a->_mp_size); + mp_size_t bn = GMP_ABS (b->_mp_size); + int cmp; + mp_ptr rp; + + cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn); + if (cmp > 0) + { + rp = MPZ_REALLOC (r, an); + gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn)); + return mpn_normalized_size (rp, an); + } + else if (cmp < 0) + { + rp = MPZ_REALLOC (r, bn); + gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an)); + return -mpn_normalized_size (rp, bn); + } + else + return 0; +} + +void +mpz_add (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_add (r, a, b); + else + rn = mpz_abs_sub (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + +void +mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) +{ + mp_size_t rn; + + if ( (a->_mp_size ^ b->_mp_size) >= 0) + rn = mpz_abs_sub (r, a, b); + else + rn = mpz_abs_add (r, a, b); + + r->_mp_size = a->_mp_size >= 0 ? rn : - rn; +} + + +/* MPZ multiplication */ +void +mpz_mul_si (mpz_t r, const mpz_t u, long int v) +{ + if (v < 0) + { + mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v)); + mpz_neg (r, r); + } + else + mpz_mul_ui (r, u, (unsigned long int) v); +} + +void +mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mp_size_t un, us; + mp_ptr tp; + mp_limb_t cy; + + us = u->_mp_size; + + if (us == 0 || v == 0) + { + r->_mp_size = 0; + return; + } + + un = GMP_ABS (us); + + tp = MPZ_REALLOC (r, un + 1); + cy = mpn_mul_1 (tp, u->_mp_d, un, v); + tp[un] = cy; + + un += (cy > 0); + r->_mp_size = (us < 0) ? - un : un; +} + +void +mpz_mul (mpz_t r, const mpz_t u, const mpz_t v) +{ + int sign; + mp_size_t un, vn, rn; + mpz_t t; + mp_ptr tp; + + un = u->_mp_size; + vn = v->_mp_size; + + if (un == 0 || vn == 0) + { + r->_mp_size = 0; + return; + } + + sign = (un ^ vn) < 0; + + un = GMP_ABS (un); + vn = GMP_ABS (vn); + + mpz_init2 (t, (un + vn) * GMP_LIMB_BITS); + + tp = t->_mp_d; + if (un >= vn) + mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn); + else + mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un); + + rn = un + vn; + rn -= tp[rn-1] == 0; + + t->_mp_size = sign ? - rn : rn; + mpz_swap (r, t); + mpz_clear (t); +} + +void +mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits) +{ + mp_size_t un, rn; + mp_size_t limbs; + unsigned shift; + mp_ptr rp; + + un = GMP_ABS (u->_mp_size); + if (un == 0) + { + r->_mp_size = 0; + return; + } + + limbs = bits / GMP_LIMB_BITS; + shift = bits % GMP_LIMB_BITS; + + rn = un + limbs + (shift > 0); + rp = MPZ_REALLOC (r, rn); + if (shift > 0) + { + mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift); + rp[rn-1] = cy; + rn -= (cy == 0); + } + else + mpn_copyd (rp + limbs, u->_mp_d, un); + + mpn_zero (rp, limbs); + + r->_mp_size = (u->_mp_size < 0) ? - rn : rn; +} + +void +mpz_addmul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init (t); + mpz_mul_ui (t, u, v); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul_ui (mpz_t r, const mpz_t u, unsigned long int v) +{ + mpz_t t; + mpz_init (t); + mpz_mul_ui (t, u, v); + mpz_sub (r, r, t); + mpz_clear (t); +} + +void +mpz_addmul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_add (r, r, t); + mpz_clear (t); +} + +void +mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t t; + mpz_init (t); + mpz_mul (t, u, v); + mpz_sub (r, r, t); + mpz_clear (t); +} + + +/* MPZ division */ +enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; + +/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */ +static int +mpz_div_qr (mpz_t q, mpz_t r, + const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode) +{ + mp_size_t ns, ds, nn, dn, qs; + ns = n->_mp_size; + ds = d->_mp_size; + + if (ds == 0) + gmp_die("mpz_div_qr: Divide by zero."); + + if (ns == 0) + { + if (q) + q->_mp_size = 0; + if (r) + r->_mp_size = 0; + return 0; + } + + nn = GMP_ABS (ns); + dn = GMP_ABS (ds); + + qs = ds ^ ns; + + if (nn < dn) + { + if (mode == GMP_DIV_CEIL && qs >= 0) + { + /* q = 1, r = n - d */ + if (r) + mpz_sub (r, n, d); + if (q) + mpz_set_ui (q, 1); + } + else if (mode == GMP_DIV_FLOOR && qs < 0) + { + /* q = -1, r = n + d */ + if (r) + mpz_add (r, n, d); + if (q) + mpz_set_si (q, -1); + } + else + { + /* q = 0, r = d */ + if (r) + mpz_set (r, n); + if (q) + q->_mp_size = 0; + } + return 1; + } + else + { + mp_ptr np, qp; + mp_size_t qn, rn; + mpz_t tq, tr; + + mpz_init_set (tr, n); + np = tr->_mp_d; + + qn = nn - dn + 1; + + if (q) + { + mpz_init2 (tq, qn * GMP_LIMB_BITS); + qp = tq->_mp_d; + } + else + qp = NULL; + + mpn_div_qr (qp, np, nn, d->_mp_d, dn); + + if (qp) + { + qn -= (qp[qn-1] == 0); + + tq->_mp_size = qs < 0 ? -qn : qn; + } + rn = mpn_normalized_size (np, dn); + tr->_mp_size = ns < 0 ? - rn : rn; + + if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0) + { + if (q) + mpz_sub_ui (tq, tq, 1); + if (r) + mpz_add (tr, tr, d); + } + else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0) + { + if (q) + mpz_add_ui (tq, tq, 1); + if (r) + mpz_sub (tr, tr, d); + } + + if (q) + { + mpz_swap (tq, q); + mpz_clear (tq); + } + if (r) + mpz_swap (tr, r); + + mpz_clear (tr); + + return rn != 0; + } +} + +void +mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC); +} + +void +mpz_mod (mpz_t r, const mpz_t n, const mpz_t d) +{ + mpz_div_qr (NULL, r, n, d, d->_mp_size >= 0 ? GMP_DIV_FLOOR : GMP_DIV_CEIL); +} + +static void +mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t un, qn; + mp_size_t limb_cnt; + mp_ptr qp; + int adjust; + + un = u->_mp_size; + if (un == 0) + { + q->_mp_size = 0; + return; + } + limb_cnt = bit_index / GMP_LIMB_BITS; + qn = GMP_ABS (un) - limb_cnt; + bit_index %= GMP_LIMB_BITS; + + if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */ + /* Note: Below, the final indexing at limb_cnt is valid because at + that point we have qn > 0. */ + adjust = (qn <= 0 + || !mpn_zero_p (u->_mp_d, limb_cnt) + || (u->_mp_d[limb_cnt] + & (((mp_limb_t) 1 << bit_index) - 1))); + else + adjust = 0; + + if (qn <= 0) + qn = 0; + else + { + qp = MPZ_REALLOC (q, qn); + + if (bit_index != 0) + { + mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index); + qn -= qp[qn - 1] == 0; + } + else + { + mpn_copyi (qp, u->_mp_d + limb_cnt, qn); + } + } + + q->_mp_size = qn; + + if (adjust) + mpz_add_ui (q, q, 1); + if (un < 0) + mpz_neg (q, q); +} + +static void +mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index, + enum mpz_div_round_mode mode) +{ + mp_size_t us, un, rn; + mp_ptr rp; + mp_limb_t mask; + + us = u->_mp_size; + if (us == 0 || bit_index == 0) + { + r->_mp_size = 0; + return; + } + rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + assert (rn > 0); + + rp = MPZ_REALLOC (r, rn); + un = GMP_ABS (us); + + mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index); + + if (rn > un) + { + /* Quotient (with truncation) is zero, and remainder is + non-zero */ + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* Have to negate and sign extend. */ + mp_size_t i; + + gmp_assert_nocarry (! mpn_neg (rp, u->_mp_d, un)); + for (i = un; i < rn - 1; i++) + rp[i] = GMP_LIMB_MAX; + + rp[rn-1] = mask; + us = -us; + } + else + { + /* Just copy */ + if (r != u) + mpn_copyi (rp, u->_mp_d, un); + + rn = un; + } + } + else + { + if (r != u) + mpn_copyi (rp, u->_mp_d, rn - 1); + + rp[rn-1] = u->_mp_d[rn-1] & mask; + + if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */ + { + /* If r != 0, compute 2^{bit_count} - r. */ + mpn_neg (rp, rp, rn); + + rp[rn-1] &= mask; + + /* us is not used for anything else, so we can modify it + here to indicate flipped sign. */ + us = -us; + } + } + rn = mpn_normalized_size (rp, rn); + r->_mp_size = us < 0 ? -rn : rn; +} + +void +mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL); +} + +void +mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR); +} + +void +mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt) +{ + mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC); +} + +void +mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d) +{ + gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_p (const mpz_t n, const mpz_t d) +{ + return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + +int +mpz_congruent_p (const mpz_t a, const mpz_t b, const mpz_t m) +{ + mpz_t t; + int res; + + /* a == b (mod 0) iff a == b */ + if (mpz_sgn (m) == 0) + return (mpz_cmp (a, b) == 0); + + mpz_init (t); + mpz_sub (t, a, b); + res = mpz_divisible_p (t, m); + mpz_clear (t); + + return res; +} + +static unsigned long +mpz_div_qr_ui (mpz_t q, mpz_t r, + const mpz_t n, unsigned long d, enum mpz_div_round_mode mode) +{ + mp_size_t ns, qn; + mp_ptr qp; + mp_limb_t rl; + mp_size_t rs; + + ns = n->_mp_size; + if (ns == 0) + { + if (q) + q->_mp_size = 0; + if (r) + r->_mp_size = 0; + return 0; + } + + qn = GMP_ABS (ns); + if (q) + qp = MPZ_REALLOC (q, qn); + else + qp = NULL; + + rl = mpn_div_qr_1 (qp, n->_mp_d, qn, d); + assert (rl < d); + + rs = rl > 0; + rs = (ns < 0) ? -rs : rs; + + if (rl > 0 && ( (mode == GMP_DIV_FLOOR && ns < 0) + || (mode == GMP_DIV_CEIL && ns >= 0))) + { + if (q) + gmp_assert_nocarry (mpn_add_1 (qp, qp, qn, 1)); + rl = d - rl; + rs = -rs; + } + + if (r) + { + MPZ_REALLOC (r, 1)[0] = rl; + r->_mp_size = rs; + } + if (q) + { + qn -= (qp[qn-1] == 0); + assert (qn == 0 || qp[qn-1] > 0); + + q->_mp_size = (ns < 0) ? - qn : qn; + } + + return rl; +} + +unsigned long +mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL); +} +unsigned long +mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} +unsigned long +mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_cdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL); +} + +unsigned long +mpz_fdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR); +} + +unsigned long +mpz_tdiv_ui (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC); +} + +unsigned long +mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR); +} + +void +mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d) +{ + gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC)); +} + +int +mpz_divisible_ui_p (const mpz_t n, unsigned long d) +{ + return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; +} + + +/* GCD */ +static mp_limb_t +mpn_gcd_11 (mp_limb_t u, mp_limb_t v) +{ + unsigned shift; + + assert ( (u | v) > 0); + + if (u == 0) + return v; + else if (v == 0) + return u; + + gmp_ctz (shift, u | v); + + u >>= shift; + v >>= shift; + + if ( (u & 1) == 0) + MP_LIMB_T_SWAP (u, v); + + while ( (v & 1) == 0) + v >>= 1; + + while (u != v) + { + if (u > v) + { + u -= v; + do + u >>= 1; + while ( (u & 1) == 0); + } + else + { + v -= u; + do + v >>= 1; + while ( (v & 1) == 0); + } + } + return u << shift; +} + +unsigned long +mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v) +{ + mp_size_t un; + + if (v == 0) + { + if (g) + mpz_abs (g, u); + } + else + { + un = GMP_ABS (u->_mp_size); + if (un != 0) + v = mpn_gcd_11 (mpn_div_qr_1 (NULL, u->_mp_d, un, v), v); + + if (g) + mpz_set_ui (g, v); + } + + return v; +} + +static mp_bitcnt_t +mpz_make_odd (mpz_t r) +{ + mp_bitcnt_t shift; + + assert (r->_mp_size > 0); + /* Count trailing zeros, equivalent to mpn_scan1, because we know that there is a 1 */ + shift = mpn_common_scan (r->_mp_d[0], 0, r->_mp_d, 0, 0); + mpz_tdiv_q_2exp (r, r, shift); + + return shift; +} + +void +mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv; + mp_bitcnt_t uz, vz, gz; + + if (u->_mp_size == 0) + { + mpz_abs (g, v); + return; + } + if (v->_mp_size == 0) + { + mpz_abs (g, u); + return; + } + + mpz_init (tu); + mpz_init (tv); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + if (tu->_mp_size < tv->_mp_size) + mpz_swap (tu, tv); + + mpz_tdiv_r (tu, tu, tv); + if (tu->_mp_size == 0) + { + mpz_swap (g, tv); + } + else + for (;;) + { + int c; + + mpz_make_odd (tu); + c = mpz_cmp (tu, tv); + if (c == 0) + { + mpz_swap (g, tu); + break; + } + if (c < 0) + mpz_swap (tu, tv); + + if (tv->_mp_size == 1) + { + mp_limb_t vl = tv->_mp_d[0]; + mp_limb_t ul = mpz_tdiv_ui (tu, vl); + mpz_set_ui (g, mpn_gcd_11 (ul, vl)); + break; + } + mpz_sub (tu, tu, tv); + } + mpz_clear (tu); + mpz_clear (tv); + mpz_mul_2exp (g, g, gz); +} + +void +mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v) +{ + mpz_t tu, tv, s0, s1, t0, t1; + mp_bitcnt_t uz, vz, gz; + mp_bitcnt_t power; + + if (u->_mp_size == 0) + { + /* g = 0 u + sgn(v) v */ + signed long sign = mpz_sgn (v); + mpz_abs (g, v); + if (s) + mpz_set_ui (s, 0); + if (t) + mpz_set_si (t, sign); + return; + } + + if (v->_mp_size == 0) + { + /* g = sgn(u) u + 0 v */ + signed long sign = mpz_sgn (u); + mpz_abs (g, u); + if (s) + mpz_set_si (s, sign); + if (t) + mpz_set_ui (t, 0); + return; + } + + mpz_init (tu); + mpz_init (tv); + mpz_init (s0); + mpz_init (s1); + mpz_init (t0); + mpz_init (t1); + + mpz_abs (tu, u); + uz = mpz_make_odd (tu); + mpz_abs (tv, v); + vz = mpz_make_odd (tv); + gz = GMP_MIN (uz, vz); + + uz -= gz; + vz -= gz; + + /* Cofactors corresponding to odd gcd. gz handled later. */ + if (tu->_mp_size < tv->_mp_size) + { + mpz_swap (tu, tv); + MPZ_SRCPTR_SWAP (u, v); + MPZ_PTR_SWAP (s, t); + MP_BITCNT_T_SWAP (uz, vz); + } + + /* Maintain + * + * u = t0 tu + t1 tv + * v = s0 tu + s1 tv + * + * where u and v denote the inputs with common factors of two + * eliminated, and det (s0, t0; s1, t1) = 2^p. Then + * + * 2^p tu = s1 u - t1 v + * 2^p tv = -s0 u + t0 v + */ + + /* After initial division, tu = q tv + tu', we have + * + * u = 2^uz (tu' + q tv) + * v = 2^vz tv + * + * or + * + * t0 = 2^uz, t1 = 2^uz q + * s0 = 0, s1 = 2^vz + */ + + mpz_setbit (t0, uz); + mpz_tdiv_qr (t1, tu, tu, tv); + mpz_mul_2exp (t1, t1, uz); + + mpz_setbit (s1, vz); + power = uz + vz; + + if (tu->_mp_size > 0) + { + mp_bitcnt_t shift; + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + power += shift; + + for (;;) + { + int c; + c = mpz_cmp (tu, tv); + if (c == 0) + break; + + if (c < 0) + { + /* tv = tv' + tu + * + * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv' + * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */ + + mpz_sub (tv, tv, tu); + mpz_add (t0, t0, t1); + mpz_add (s0, s0, s1); + + shift = mpz_make_odd (tv); + mpz_mul_2exp (t1, t1, shift); + mpz_mul_2exp (s1, s1, shift); + } + else + { + mpz_sub (tu, tu, tv); + mpz_add (t1, t0, t1); + mpz_add (s1, s0, s1); + + shift = mpz_make_odd (tu); + mpz_mul_2exp (t0, t0, shift); + mpz_mul_2exp (s0, s0, shift); + } + power += shift; + } + } + + /* Now tv = odd part of gcd, and -s0 and t0 are corresponding + cofactors. */ + + mpz_mul_2exp (tv, tv, gz); + mpz_neg (s0, s0); + + /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To + adjust cofactors, we need u / g and v / g */ + + mpz_divexact (s1, v, tv); + mpz_abs (s1, s1); + mpz_divexact (t1, u, tv); + mpz_abs (t1, t1); + + while (power-- > 0) + { + /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */ + if (mpz_odd_p (s0) || mpz_odd_p (t0)) + { + mpz_sub (s0, s0, s1); + mpz_add (t0, t0, t1); + } + mpz_divexact_ui (s0, s0, 2); + mpz_divexact_ui (t0, t0, 2); + } + + /* Arrange so that |s| < |u| / 2g */ + mpz_add (s1, s0, s1); + if (mpz_cmpabs (s0, s1) > 0) + { + mpz_swap (s0, s1); + mpz_sub (t0, t0, t1); + } + if (u->_mp_size < 0) + mpz_neg (s0, s0); + if (v->_mp_size < 0) + mpz_neg (t0, t0); + + mpz_swap (g, tv); + if (s) + mpz_swap (s, s0); + if (t) + mpz_swap (t, t0); + + mpz_clear (tu); + mpz_clear (tv); + mpz_clear (s0); + mpz_clear (s1); + mpz_clear (t0); + mpz_clear (t1); +} + +void +mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v) +{ + mpz_t g; + + if (u->_mp_size == 0 || v->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + mpz_init (g); + + mpz_gcd (g, u, v); + mpz_divexact (g, u, g); + mpz_mul (r, g, v); + + mpz_clear (g); + mpz_abs (r, r); +} + +void +mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v) +{ + if (v == 0 || u->_mp_size == 0) + { + r->_mp_size = 0; + return; + } + + v /= mpz_gcd_ui (NULL, u, v); + mpz_mul_ui (r, u, v); + + mpz_abs (r, r); +} + +int +mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) +{ + mpz_t g, tr; + int invertible; + + if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0) + return 0; + + mpz_init (g); + mpz_init (tr); + + mpz_gcdext (g, tr, NULL, u, m); + invertible = (mpz_cmp_ui (g, 1) == 0); + + if (invertible) + { + if (tr->_mp_size < 0) + { + if (m->_mp_size >= 0) + mpz_add (tr, tr, m); + else + mpz_sub (tr, tr, m); + } + mpz_swap (r, tr); + } + + mpz_clear (g); + mpz_clear (tr); + return invertible; +} + + +/* Higher level operations (sqrt, pow and root) */ + +void +mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e) +{ + unsigned long bit; + mpz_t tr; + mpz_init_set_ui (tr, 1); + + bit = GMP_ULONG_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (e & bit) + mpz_mul (tr, tr, b); + bit >>= 1; + } + while (bit > 0); + + mpz_swap (r, tr); + mpz_clear (tr); +} + +void +mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e) +{ + mpz_t b; + mpz_pow_ui (r, mpz_roinit_n (b, &blimb, 1), e); +} + +void +mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m) +{ + mpz_t tr; + mpz_t base; + mp_size_t en, mn; + mp_srcptr mp; + struct gmp_div_inverse minv; + unsigned shift; + mp_ptr tp = NULL; + + en = GMP_ABS (e->_mp_size); + mn = GMP_ABS (m->_mp_size); + if (mn == 0) + gmp_die ("mpz_powm: Zero modulo."); + + if (en == 0) + { + mpz_set_ui (r, 1); + return; + } + + mp = m->_mp_d; + mpn_div_qr_invert (&minv, mp, mn); + shift = minv.shift; + + if (shift > 0) + { + /* To avoid shifts, we do all our reductions, except the final + one, using a *normalized* m. */ + minv.shift = 0; + + tp = gmp_xalloc_limbs (mn); + gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift)); + mp = tp; + } + + mpz_init (base); + + if (e->_mp_size < 0) + { + if (!mpz_invert (base, b, m)) + gmp_die ("mpz_powm: Negative exponent and non-invertible base."); + } + else + { + mp_size_t bn; + mpz_abs (base, b); + + bn = base->_mp_size; + if (bn >= mn) + { + mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv); + bn = mn; + } + + /* We have reduced the absolute value. Now take care of the + sign. Note that we get zero represented non-canonically as + m. */ + if (b->_mp_size < 0) + { + mp_ptr bp = MPZ_REALLOC (base, mn); + gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn)); + bn = mn; + } + base->_mp_size = mpn_normalized_size (base->_mp_d, bn); + } + mpz_init_set_ui (tr, 1); + + while (--en >= 0) + { + mp_limb_t w = e->_mp_d[en]; + mp_limb_t bit; + + bit = GMP_LIMB_HIGHBIT; + do + { + mpz_mul (tr, tr, tr); + if (w & bit) + mpz_mul (tr, tr, base); + if (tr->_mp_size > mn) + { + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + bit >>= 1; + } + while (bit > 0); + } + + /* Final reduction */ + if (tr->_mp_size >= mn) + { + minv.shift = shift; + mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv); + tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn); + } + if (tp) + gmp_free (tp); + + mpz_swap (r, tr); + mpz_clear (tr); + mpz_clear (base); +} + +void +mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m) +{ + mpz_t e; + mpz_powm (r, b, mpz_roinit_n (e, &elimb, 1), m); +} + +/* x=trunc(y^(1/z)), r=y-x^z */ +void +mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z) +{ + int sgn; + mpz_t t, u; + + sgn = y->_mp_size < 0; + if ((~z & sgn) != 0) + gmp_die ("mpz_rootrem: Negative argument, with even root."); + if (z == 0) + gmp_die ("mpz_rootrem: Zeroth root."); + + if (mpz_cmpabs_ui (y, 1) <= 0) { + if (x) + mpz_set (x, y); + if (r) + r->_mp_size = 0; + return; + } + + mpz_init (u); + mpz_init (t); + mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1); + + if (z == 2) /* simplify sqrt loop: z-1 == 1 */ + do { + mpz_swap (u, t); /* u = x */ + mpz_tdiv_q (t, y, u); /* t = y/x */ + mpz_add (t, t, u); /* t = y/x + x */ + mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + else /* z != 2 */ { + mpz_t v; + + mpz_init (v); + if (sgn) + mpz_neg (t, t); + + do { + mpz_swap (u, t); /* u = x */ + mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */ + mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */ + mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */ + mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */ + mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */ + } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */ + + mpz_clear (v); + } + + if (r) { + mpz_pow_ui (t, u, z); + mpz_sub (r, y, t); + } + if (x) + mpz_swap (x, u); + mpz_clear (u); + mpz_clear (t); +} + +int +mpz_root (mpz_t x, const mpz_t y, unsigned long z) +{ + int res; + mpz_t r; + + mpz_init (r); + mpz_rootrem (x, r, y, z); + res = r->_mp_size == 0; + mpz_clear (r); + + return res; +} + +/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */ +void +mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u) +{ + mpz_rootrem (s, r, u, 2); +} + +void +mpz_sqrt (mpz_t s, const mpz_t u) +{ + mpz_rootrem (s, NULL, u, 2); +} + +int +mpz_perfect_square_p (const mpz_t u) +{ + if (u->_mp_size <= 0) + return (u->_mp_size == 0); + else + return mpz_root (NULL, u, 2); +} + +int +mpn_perfect_square_p (mp_srcptr p, mp_size_t n) +{ + mpz_t t; + + assert (n > 0); + assert (p [n-1] != 0); + return mpz_root (NULL, mpz_roinit_n (t, p, n), 2); +} + +mp_size_t +mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) +{ + mpz_t s, r, u; + mp_size_t res; + + assert (n > 0); + assert (p [n-1] != 0); + + mpz_init (r); + mpz_init (s); + mpz_rootrem (s, r, mpz_roinit_n (u, p, n), 2); + + assert (s->_mp_size == (n+1)/2); + mpn_copyd (sp, s->_mp_d, s->_mp_size); + mpz_clear (s); + res = r->_mp_size; + if (rp) + mpn_copyd (rp, r->_mp_d, res); + mpz_clear (r); + return res; +} + +/* Combinatorics */ + +void +mpz_fac_ui (mpz_t x, unsigned long n) +{ + mpz_set_ui (x, n + (n == 0)); + while (n > 2) + mpz_mul_ui (x, x, --n); +} + +void +mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) +{ + mpz_t t; + + mpz_set_ui (r, k <= n); + + if (k > (n >> 1)) + k = (k <= n) ? n - k : 0; + + mpz_init (t); + mpz_fac_ui (t, k); + + for (; k > 0; k--) + mpz_mul_ui (r, r, n--); + + mpz_divexact (r, r, t); + mpz_clear (t); +} + + +/* Primality testing */ +static int +gmp_millerrabin (const mpz_t n, const mpz_t nm1, mpz_t y, + const mpz_t q, mp_bitcnt_t k) +{ + assert (k > 0); + + /* Caller must initialize y to the base. */ + mpz_powm (y, y, q, n); + + if (mpz_cmp_ui (y, 1) == 0 || mpz_cmp (y, nm1) == 0) + return 1; + + while (--k > 0) + { + mpz_powm_ui (y, y, 2, n); + if (mpz_cmp (y, nm1) == 0) + return 1; + /* y == 1 means that the previous y was a non-trivial square root + of 1 (mod n). y == 0 means that n is a power of the base. + In either case, n is not prime. */ + if (mpz_cmp_ui (y, 1) <= 0) + return 0; + } + return 0; +} + +/* This product is 0xc0cfd797, and fits in 32 bits. */ +#define GMP_PRIME_PRODUCT \ + (3UL*5UL*7UL*11UL*13UL*17UL*19UL*23UL*29UL) + +/* Bit (p+1)/2 is set, for each odd prime <= 61 */ +#define GMP_PRIME_MASK 0xc96996dcUL + +int +mpz_probab_prime_p (const mpz_t n, int reps) +{ + mpz_t nm1; + mpz_t q; + mpz_t y; + mp_bitcnt_t k; + int is_prime; + int j; + + /* Note that we use the absolute value of n only, for compatibility + with the real GMP. */ + if (mpz_even_p (n)) + return (mpz_cmpabs_ui (n, 2) == 0) ? 2 : 0; + + /* Above test excludes n == 0 */ + assert (n->_mp_size != 0); + + if (mpz_cmpabs_ui (n, 64) < 0) + return (GMP_PRIME_MASK >> (n->_mp_d[0] >> 1)) & 2; + + if (mpz_gcd_ui (NULL, n, GMP_PRIME_PRODUCT) != 1) + return 0; + + /* All prime factors are >= 31. */ + if (mpz_cmpabs_ui (n, 31*31) < 0) + return 2; + + /* Use Miller-Rabin, with a deterministic sequence of bases, a[j] = + j^2 + j + 41 using Euler's polynomial. We potentially stop early, + if a[j] >= n - 1. Since n >= 31*31, this can happen only if reps > + 30 (a[30] == 971 > 31*31 == 961). */ + + mpz_init (nm1); + mpz_init (q); + mpz_init (y); + + /* Find q and k, where q is odd and n = 1 + 2**k * q. */ + nm1->_mp_size = mpz_abs_sub_ui (nm1, n, 1); + k = mpz_scan1 (nm1, 0); + mpz_tdiv_q_2exp (q, nm1, k); + + for (j = 0, is_prime = 1; is_prime & (j < reps); j++) + { + mpz_set_ui (y, (unsigned long) j*j+j+41); + if (mpz_cmp (y, nm1) >= 0) + { + /* Don't try any further bases. This "early" break does not affect + the result for any reasonable reps value (<=5000 was tested) */ + assert (j >= 30); + break; + } + is_prime = gmp_millerrabin (n, nm1, y, q, k); + } + mpz_clear (nm1); + mpz_clear (q); + mpz_clear (y); + + return is_prime; +} + + +/* Logical operations and bit manipulation. */ + +/* Numbers are treated as if represented in two's complement (and + infinitely sign extended). For a negative values we get the two's + complement from -x = ~x + 1, where ~ is bitwise complement. + Negation transforms + + xxxx10...0 + + into + + yyyy10...0 + + where yyyy is the bitwise complement of xxxx. So least significant + bits, up to and including the first one bit, are unchanged, and + the more significant bits are all complemented. + + To change a bit from zero to one in a negative number, subtract the + corresponding power of two from the absolute value. This can never + underflow. To change a bit from one to zero, add the corresponding + power of two, and this might overflow. E.g., if x = -001111, the + two's complement is 110001. Clearing the least significant bit, we + get two's complement 110000, and -010000. */ + +int +mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t limb_index; + unsigned shift; + mp_size_t ds; + mp_size_t dn; + mp_limb_t w; + int bit; + + ds = d->_mp_size; + dn = GMP_ABS (ds); + limb_index = bit_index / GMP_LIMB_BITS; + if (limb_index >= dn) + return ds < 0; + + shift = bit_index % GMP_LIMB_BITS; + w = d->_mp_d[limb_index]; + bit = (w >> shift) & 1; + + if (ds < 0) + { + /* d < 0. Check if any of the bits below is set: If so, our bit + must be complemented. */ + if (shift > 0 && (w << (GMP_LIMB_BITS - shift)) > 0) + return bit ^ 1; + while (--limb_index >= 0) + if (d->_mp_d[limb_index] > 0) + return bit ^ 1; + } + return bit; +} + +static void +mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_limb_t bit; + mp_ptr dp; + + dn = GMP_ABS (d->_mp_size); + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + if (limb_index >= dn) + { + mp_size_t i; + /* The bit should be set outside of the end of the number. + We have to increase the size of the number. */ + dp = MPZ_REALLOC (d, limb_index + 1); + + dp[limb_index] = bit; + for (i = dn; i < limb_index; i++) + dp[i] = 0; + dn = limb_index + 1; + } + else + { + mp_limb_t cy; + + dp = d->_mp_d; + + cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit); + if (cy > 0) + { + dp = MPZ_REALLOC (d, dn + 1); + dp[dn++] = cy; + } + } + + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +static void +mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index) +{ + mp_size_t dn, limb_index; + mp_ptr dp; + mp_limb_t bit; + + dn = GMP_ABS (d->_mp_size); + dp = d->_mp_d; + + limb_index = bit_index / GMP_LIMB_BITS; + bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS); + + assert (limb_index < dn); + + gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index, + dn - limb_index, bit)); + dn = mpn_normalized_size (dp, dn); + d->_mp_size = (d->_mp_size < 0) ? - dn : dn; +} + +void +mpz_setbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (!mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_add_bit (d, bit_index); + else + mpz_abs_sub_bit (d, bit_index); + } +} + +void +mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index)) + { + if (d->_mp_size >= 0) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); + } +} + +void +mpz_combit (mpz_t d, mp_bitcnt_t bit_index) +{ + if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0)) + mpz_abs_sub_bit (d, bit_index); + else + mpz_abs_add_bit (d, bit_index); +} + +void +mpz_com (mpz_t r, const mpz_t u) +{ + mpz_neg (r, u); + mpz_sub_ui (r, r, 1); +} + +void +mpz_and (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + r->_mp_size = 0; + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc & vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is positive, higher limbs don't matter. */ + rn = vx ? un : vn; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul & vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul & vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_ior (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, rn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc | vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + /* If the smaller input is negative, by sign extension higher limbs + don't matter. */ + rn = vx ? vn : un; + + rp = MPZ_REALLOC (r, rn + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = ( (ul | vl) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < rn; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = ( (ul | vx) ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[rn++] = rc; + else + rn = mpn_normalized_size (rp, rn); + + r->_mp_size = rx ? -rn : rn; +} + +void +mpz_xor (mpz_t r, const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_ptr up, vp, rp; + + mp_limb_t ux, vx, rx; + mp_limb_t uc, vc, rc; + mp_limb_t ul, vl, rl; + + un = GMP_ABS (u->_mp_size); + vn = GMP_ABS (v->_mp_size); + if (un < vn) + { + MPZ_SRCPTR_SWAP (u, v); + MP_SIZE_T_SWAP (un, vn); + } + if (vn == 0) + { + mpz_set (r, u); + return; + } + + uc = u->_mp_size < 0; + vc = v->_mp_size < 0; + rc = uc ^ vc; + + ux = -uc; + vx = -vc; + rx = -rc; + + rp = MPZ_REALLOC (r, un + (mp_size_t) rc); + + up = u->_mp_d; + vp = v->_mp_d; + + i = 0; + do + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + vl = (vp[i] ^ vx) + vc; + vc = vl < vc; + + rl = (ul ^ vl ^ rx) + rc; + rc = rl < rc; + rp[i] = rl; + } + while (++i < vn); + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ ux) + uc; + uc = ul < uc; + + rl = (ul ^ ux) + rc; + rc = rl < rc; + rp[i] = rl; + } + if (rc) + rp[un++] = rc; + else + un = mpn_normalized_size (rp, un); + + r->_mp_size = rx ? -un : un; +} + +static unsigned +gmp_popcount_limb (mp_limb_t x) +{ + unsigned c; + + /* Do 16 bits at a time, to avoid limb-sized constants. */ + for (c = 0; x > 0; x >>= 16) + { + unsigned w = ((x >> 1) & 0x5555) + (x & 0x5555); + w = ((w >> 2) & 0x3333) + (w & 0x3333); + w = ((w >> 4) & 0x0f0f) + (w & 0x0f0f); + w = (w >> 8) + (w & 0x00ff); + c += w; + } + return c; +} + +mp_bitcnt_t +mpn_popcount (mp_srcptr p, mp_size_t n) +{ + mp_size_t i; + mp_bitcnt_t c; + + for (c = 0, i = 0; i < n; i++) + c += gmp_popcount_limb (p[i]); + + return c; +} + +mp_bitcnt_t +mpz_popcount (const mpz_t u) +{ + mp_size_t un; + + un = u->_mp_size; + + if (un < 0) + return ~(mp_bitcnt_t) 0; + + return mpn_popcount (u->_mp_d, un); +} + +mp_bitcnt_t +mpz_hamdist (const mpz_t u, const mpz_t v) +{ + mp_size_t un, vn, i; + mp_limb_t uc, vc, ul, vl, comp; + mp_srcptr up, vp; + mp_bitcnt_t c; + + un = u->_mp_size; + vn = v->_mp_size; + + if ( (un ^ vn) < 0) + return ~(mp_bitcnt_t) 0; + + comp = - (uc = vc = (un < 0)); + if (uc) + { + assert (vn < 0); + un = -un; + vn = -vn; + } + + up = u->_mp_d; + vp = v->_mp_d; + + if (un < vn) + MPN_SRCPTR_SWAP (up, un, vp, vn); + + for (i = 0, c = 0; i < vn; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + vl = (vp[i] ^ comp) + vc; + vc = vl < vc; + + c += gmp_popcount_limb (ul ^ vl); + } + assert (vc == 0); + + for (; i < un; i++) + { + ul = (up[i] ^ comp) + uc; + uc = ul < uc; + + c += gmp_popcount_limb (ul ^ comp); + } + + return c; +} + +mp_bitcnt_t +mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit + for u<0. Notice this test picks up any u==0 too. */ + if (i >= un) + return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit); + + up = u->_mp_d; + ux = 0; + limb = up[i]; + + if (starting_bit != 0) + { + if (us < 0) + { + ux = mpn_zero_p (up, i); + limb = ~ limb + ux; + ux = - (mp_limb_t) (limb >= ux); + } + + /* Mask to 0 all bits before starting_bit, thus ignoring them. */ + limb &= (GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS)); + } + + return mpn_common_scan (limb, i, up, un, ux); +} + +mp_bitcnt_t +mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) +{ + mp_ptr up; + mp_size_t us, un, i; + mp_limb_t limb, ux; + + us = u->_mp_size; + ux = - (mp_limb_t) (us >= 0); + un = GMP_ABS (us); + i = starting_bit / GMP_LIMB_BITS; + + /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for + u<0. Notice this test picks up all cases of u==0 too. */ + if (i >= un) + return (ux ? starting_bit : ~(mp_bitcnt_t) 0); + + up = u->_mp_d; + limb = up[i] ^ ux; + + if (ux == 0) + limb -= mpn_zero_p (up, i); /* limb = ~(~limb + zero_p) */ + + /* Mask all bits before starting_bit, thus ignoring them. */ + limb &= (GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS)); + + return mpn_common_scan (limb, i, up, un, ux); +} + + +/* MPZ base conversion. */ + +size_t +mpz_sizeinbase (const mpz_t u, int base) +{ + mp_size_t un; + mp_srcptr up; + mp_ptr tp; + mp_bitcnt_t bits; + struct gmp_div_inverse bi; + size_t ndigits; + + assert (base >= 2); + assert (base <= 36); + + un = GMP_ABS (u->_mp_size); + if (un == 0) + return 1; + + up = u->_mp_d; + + bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]); + switch (base) + { + case 2: + return bits; + case 4: + return (bits + 1) / 2; + case 8: + return (bits + 2) / 3; + case 16: + return (bits + 3) / 4; + case 32: + return (bits + 4) / 5; + /* FIXME: Do something more clever for the common case of base + 10. */ + } + + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, up, un); + mpn_div_qr_1_invert (&bi, base); + + ndigits = 0; + do + { + ndigits++; + mpn_div_qr_1_preinv (tp, tp, un, &bi); + un -= (tp[un-1] == 0); + } + while (un > 0); + + gmp_free (tp); + return ndigits; +} + +char * +mpz_get_str (char *sp, int base, const mpz_t u) +{ + unsigned bits; + const char *digits; + mp_size_t un; + size_t i, sn; + + if (base >= 0) + { + digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + } + else + { + base = -base; + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + } + if (base <= 1) + base = 10; + if (base > 36) + return NULL; + + sn = 1 + mpz_sizeinbase (u, base); + if (!sp) + sp = (char *) gmp_xalloc (1 + sn); + + un = GMP_ABS (u->_mp_size); + + if (un == 0) + { + sp[0] = '0'; + sp[1] = '\0'; + return sp; + } + + i = 0; + + if (u->_mp_size < 0) + sp[i++] = '-'; + + bits = mpn_base_power_of_two_p (base); + + if (bits) + /* Not modified in this case. */ + sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un); + else + { + struct mpn_base_info info; + mp_ptr tp; + + mpn_get_base_info (&info, base); + tp = gmp_xalloc_limbs (un); + mpn_copyi (tp, u->_mp_d, un); + + sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un); + gmp_free (tp); + } + + for (; i < sn; i++) + sp[i] = digits[(unsigned char) sp[i]]; + + sp[sn] = '\0'; + return sp; +} + +int +mpz_set_str (mpz_t r, const char *sp, int base) +{ + unsigned bits; + mp_size_t rn, alloc; + mp_ptr rp; + size_t dn; + int sign; + unsigned char *dp; + + assert (base == 0 || (base >= 2 && base <= 36)); + + while (isspace( (unsigned char) *sp)) + sp++; + + sign = (*sp == '-'); + sp += sign; + + if (base == 0) + { + if (sp[0] == '0') + { + if (sp[1] == 'x' || sp[1] == 'X') + { + base = 16; + sp += 2; + } + else if (sp[1] == 'b' || sp[1] == 'B') + { + base = 2; + sp += 2; + } + else + base = 8; + } + else + base = 10; + } + + if (!*sp) + { + r->_mp_size = 0; + return -1; + } + dp = (unsigned char *) gmp_xalloc (strlen (sp)); + + for (dn = 0; *sp; sp++) + { + unsigned digit; + + if (isspace ((unsigned char) *sp)) + continue; + else if (*sp >= '0' && *sp <= '9') + digit = *sp - '0'; + else if (*sp >= 'a' && *sp <= 'z') + digit = *sp - 'a' + 10; + else if (*sp >= 'A' && *sp <= 'Z') + digit = *sp - 'A' + 10; + else + digit = base; /* fail */ + + if (digit >= (unsigned) base) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + + dp[dn++] = digit; + } + + if (!dn) + { + gmp_free (dp); + r->_mp_size = 0; + return -1; + } + bits = mpn_base_power_of_two_p (base); + + if (bits > 0) + { + alloc = (dn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_bits (rp, dp, dn, bits); + } + else + { + struct mpn_base_info info; + mpn_get_base_info (&info, base); + alloc = (dn + info.exp - 1) / info.exp; + rp = MPZ_REALLOC (r, alloc); + rn = mpn_set_str_other (rp, dp, dn, base, &info); + /* Normalization, needed for all-zero input. */ + assert (rn > 0); + rn -= rp[rn-1] == 0; + } + assert (rn <= alloc); + gmp_free (dp); + + r->_mp_size = sign ? - rn : rn; + + return 0; +} + +int +mpz_init_set_str (mpz_t r, const char *sp, int base) +{ + mpz_init (r); + return mpz_set_str (r, sp, base); +} + +size_t +mpz_out_str (FILE *stream, int base, const mpz_t x) +{ + char *str; + size_t len; + + str = mpz_get_str (NULL, base, x); + len = strlen (str); + len = fwrite (str, 1, len, stream); + gmp_free (str); + return len; +} + + +static int +gmp_detect_endian (void) +{ + static const int i = 2; + const unsigned char *p = (const unsigned char *) &i; + return 1 - *p; +} + +/* Import and export. Does not support nails. */ +void +mpz_import (mpz_t r, size_t count, int order, size_t size, int endian, + size_t nails, const void *src) +{ + const unsigned char *p; + ptrdiff_t word_step; + mp_ptr rp; + mp_size_t rn; + + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes already copied to this limb (starting from + the low end). */ + size_t bytes; + /* The index where the limb should be stored, when completed. */ + mp_size_t i; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) src; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t); + rp = MPZ_REALLOC (r, rn); + + for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step) + { + size_t j; + for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) + { + limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT); + if (bytes == sizeof(mp_limb_t)) + { + rp[i++] = limb; + bytes = 0; + limb = 0; + } + } + } + assert (i + (bytes > 0) == rn); + if (limb != 0) + rp[i++] = limb; + else + i = mpn_normalized_size (rp, i); + + r->_mp_size = i; +} + +void * +mpz_export (void *r, size_t *countp, int order, size_t size, int endian, + size_t nails, const mpz_t u) +{ + size_t count; + mp_size_t un; + + if (nails != 0) + gmp_die ("mpz_import: Nails not supported."); + + assert (order == 1 || order == -1); + assert (endian >= -1 && endian <= 1); + assert (size > 0 || u->_mp_size == 0); + + un = u->_mp_size; + count = 0; + if (un != 0) + { + size_t k; + unsigned char *p; + ptrdiff_t word_step; + /* The current (partial) limb. */ + mp_limb_t limb; + /* The number of bytes left to to in this limb. */ + size_t bytes; + /* The index where the limb was read. */ + mp_size_t i; + + un = GMP_ABS (un); + + /* Count bytes in top limb. */ + limb = u->_mp_d[un-1]; + assert (limb != 0); + + k = 0; + do { + k++; limb >>= CHAR_BIT; + } while (limb != 0); + + count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size; + + if (!r) + r = gmp_xalloc (count * size); + + if (endian == 0) + endian = gmp_detect_endian (); + + p = (unsigned char *) r; + + word_step = (order != endian) ? 2 * size : 0; + + /* Process bytes from the least significant end, so point p at the + least significant word. */ + if (order == 1) + { + p += size * (count - 1); + word_step = - word_step; + } + + /* And at least significant byte of that word. */ + if (endian == 1) + p += (size - 1); + + for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step) + { + size_t j; + for (j = 0; j < size; j++, p -= (ptrdiff_t) endian) + { + if (bytes == 0) + { + if (i < un) + limb = u->_mp_d[i++]; + bytes = sizeof (mp_limb_t); + } + *p = limb; + limb >>= CHAR_BIT; + bytes--; + } + } + assert (i == un); + assert (k == count); + } + + if (countp) + *countp = count; + + return r; +} diff --git a/src/misspell.c b/src/misspell.c new file mode 100644 index 0000000..c1e58a0 --- /dev/null +++ b/src/misspell.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <limits.h> +#include <utils.h> +#include <misspell.h> + +enum string_distance_function { + DELETION = 0, /* m1 */ + INSERTION, /* m2 */ + TRANSFORMATION, /* m3 */ +}; +#define DISTANCE_MAX (TRANSFORMATION + 1) + +static unsigned int min_distance(unsigned int *cost) +{ + unsigned int min = UINT_MAX; + int k; + + for (k = 0; k < DISTANCE_MAX; k++) { + if (cost[k] < min) + min = cost[k]; + } + + return min; +} + +/* A simple implementation of "The string-to-string correction problem (1974)" + * by Robert A. Wagner. + */ +static unsigned int string_distance(const char *a, const char *b) +{ + unsigned int len_a = strlen(a); + unsigned int len_b = strlen(b); + unsigned int *distance; + unsigned int i, j, ret; + + distance = xzalloc((len_a + 1) * (len_b + 1) * sizeof(unsigned int)); + +#define DISTANCE(__i, __j) distance[(__i) * len_b + (__j)] + + for (i = 0; i <= len_a; i++) + DISTANCE(i, 0) = i; + for (j = 0; j <= len_b; j++) + DISTANCE(0, j) = j; + + for (i = 1; i <= len_a; i++) { + for (j = 1; j <= len_b; j++) { + unsigned int subcost = (a[i] == b[j]) ? 0 : 1; + unsigned int cost[3]; + + cost[DELETION] = DISTANCE(i - 1, j) + 1; + cost[INSERTION] = DISTANCE(i, j - 1) + 1; + cost[TRANSFORMATION] = DISTANCE(i - 1, j - 1) + subcost; + DISTANCE(i, j) = min_distance(cost); + + if (i > 1 && j > 1 && + a[i] == b[j - 1] && + a[i - 1] == b[j]) + DISTANCE(i, j) = + min(DISTANCE(i, j), + DISTANCE(i - 2, j - 2) + subcost); + } + } + + ret = DISTANCE(len_a, len_b); + + xfree(distance); + + return ret; +} + +void string_misspell_init(struct string_misspell_state *st) +{ + st->obj = NULL; + st->min_distance = UINT_MAX; +} + +int string_misspell_update(const char *a, const char *b, + void *obj, struct string_misspell_state *st) +{ + unsigned int len_a, len_b, max_len, min_len, distance, threshold; + + len_a = strlen(a); + len_b = strlen(b); + + max_len = max(len_a, len_b); + min_len = min(len_a, len_b); + + if (max_len <= 1) + return 0; + + if (max_len - min_len <= 1) + threshold = max(div_round_up(max_len, 3), 1); + else + threshold = div_round_up(max_len + 2, 3); + + distance = string_distance(a, b); + if (distance > threshold) + return 0; + else if (distance < st->min_distance) { + st->min_distance = distance; + st->obj = obj; + return 1; + } + return 0; +} diff --git a/src/mnl.c b/src/mnl.c new file mode 100644 index 0000000..0fb36bd --- /dev/null +++ b/src/mnl.c @@ -0,0 +1,2669 @@ +/* + * Copyright (c) 2013-2017 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <libmnl/libmnl.h> +#include <libnftnl/common.h> +#include <libnftnl/ruleset.h> +#include <libnftnl/table.h> +#include <libnftnl/chain.h> +#include <libnftnl/rule.h> +#include <libnftnl/expr.h> +#include <libnftnl/set.h> +#include <libnftnl/object.h> +#include <libnftnl/flowtable.h> +#include <libnftnl/batch.h> +#include <libnftnl/udata.h> + +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_hook.h> +#include <linux/netfilter/nf_tables.h> + +#include <mnl.h> +#include <cmd.h> +#include <net/if.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <errno.h> +#include <utils.h> +#include <nftables.h> +#include <linux/netfilter.h> +#include <linux/netfilter_arp.h> + +struct basehook { + struct list_head list; + const char *module_name; + const char *hookfn; + const char *table; + const char *chain; + int family; + int chain_family; + uint32_t num; + int prio; +}; + +struct mnl_socket *nft_mnl_socket_open(void) +{ + struct mnl_socket *nf_sock; + int one = 1; + + nf_sock = mnl_socket_open(NETLINK_NETFILTER); + if (!nf_sock) + netlink_init_error(); + + if (fcntl(mnl_socket_get_fd(nf_sock), F_SETFL, O_NONBLOCK)) + netlink_init_error(); + + mnl_socket_setsockopt(nf_sock, NETLINK_EXT_ACK, &one, sizeof(one)); + + return nf_sock; +} + +uint32_t mnl_seqnum_alloc(unsigned int *seqnum) +{ + return (*seqnum)++; +} + +/* The largest nf_tables netlink message is the set element message, which + * contains the NFTA_SET_ELEM_LIST_ELEMENTS attribute. This attribute is + * a nest that describes the set elements. Given that the netlink attribute + * length (nla_len) is 16 bits, the largest message is a bit larger than + * 64 KBytes. + */ +#define NFT_NLMSG_MAXSIZE (UINT16_MAX + getpagesize()) + +static int +nft_mnl_recv(struct netlink_ctx *ctx, uint32_t portid, + int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data) +{ + char buf[NFT_NLMSG_MAXSIZE]; + bool eintr = false; + int ret; + + ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, ctx->seqnum, portid, cb, cb_data); + if (ret == 0) + break; + if (ret < 0) { + if (errno == EAGAIN) { + ret = 0; + break; + } + if (errno != EINTR) + break; + + /* process all pending messages before reporting EINTR */ + eintr = true; + } + ret = mnl_socket_recvfrom(ctx->nft->nf_sock, buf, sizeof(buf)); + } + if (eintr) { + ret = -1; + errno = EINTR; + } + return ret; +} + +int +nft_mnl_talk(struct netlink_ctx *ctx, const void *data, unsigned int len, + int (*cb)(const struct nlmsghdr *nlh, void *data), void *cb_data) +{ + uint32_t portid = mnl_socket_get_portid(ctx->nft->nf_sock); + + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) + mnl_nlmsg_fprintf(ctx->nft->output.output_fp, data, len, + sizeof(struct nfgenmsg)); + + if (mnl_socket_sendto(ctx->nft->nf_sock, data, len) < 0) + return -1; + + return nft_mnl_recv(ctx, portid, cb, cb_data); +} + +/* + * Rule-set consistency check across several netlink dumps + */ +static uint32_t nft_genid; + +static int genid_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nfgenmsg *nfh = mnl_nlmsg_get_payload(nlh); + + nft_genid = ntohs(nfh->res_id); + + return MNL_CB_OK; +} + +uint32_t mnl_genid_get(struct netlink_ctx *ctx) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETGEN, AF_UNSPEC, 0, ctx->seqnum); + /* Skip error checking, old kernels sets res_id field to zero. */ + nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, genid_cb, NULL); + + return nft_genid; +} + +static uint16_t nft_genid_u16(uint32_t genid) +{ + return genid & 0xffff; +} + +static int check_genid(const struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfh = mnl_nlmsg_get_payload(nlh); + + if (nft_genid_u16(nft_genid) != ntohs(nfh->res_id)) { + errno = EINTR; + return -1; + } + return 0; +} + +/* + * Batching + */ + +/* Selected batch page is 2 Mbytes long to support loading a ruleset of 3.5M + * rules matching on source and destination address as well as input and output + * interfaces. This is what legacy iptables supports. + */ +#define BATCH_PAGE_SIZE 2 * 1024 * 1024 + +struct nftnl_batch *mnl_batch_init(void) +{ + struct nftnl_batch *batch; + + batch = nftnl_batch_alloc(BATCH_PAGE_SIZE, NFT_NLMSG_MAXSIZE); + if (batch == NULL) + memory_allocation_error(); + + return batch; +} + +static void mnl_nft_batch_continue(struct nftnl_batch *batch) +{ + if (nftnl_batch_update(batch) < 0) + memory_allocation_error(); +} + +uint32_t mnl_batch_begin(struct nftnl_batch *batch, uint32_t seqnum) +{ + nftnl_batch_begin(nftnl_batch_buffer(batch), seqnum); + mnl_nft_batch_continue(batch); + + return seqnum; +} + +void mnl_batch_end(struct nftnl_batch *batch, uint32_t seqnum) +{ + nftnl_batch_end(nftnl_batch_buffer(batch), seqnum); + mnl_nft_batch_continue(batch); +} + +bool mnl_batch_ready(struct nftnl_batch *batch) +{ + /* Check if the batch only contains the initial and trailing batch + * messages. In that case, the batch is empty. + */ + return nftnl_batch_buffer_len(batch) != + (NLMSG_HDRLEN + sizeof(struct nfgenmsg)) * 2; +} + +void mnl_batch_reset(struct nftnl_batch *batch) +{ + nftnl_batch_free(batch); +} + +static void mnl_err_list_node_add(struct list_head *err_list, int error, + int seqnum, uint32_t offset, + const char *errmsg) +{ + struct mnl_err *err = xmalloc(sizeof(struct mnl_err)); + + err->seqnum = seqnum; + err->offset = offset; + err->err = error; + list_add_tail(&err->head, err_list); +} + +void mnl_err_list_free(struct mnl_err *err) +{ + list_del(&err->head); + xfree(err); +} + +static void mnl_set_sndbuffer(struct netlink_ctx *ctx) +{ + struct mnl_socket *nl = ctx->nft->nf_sock; + struct nftnl_batch *batch = ctx->batch; + socklen_t len = sizeof(int); + int sndnlbuffsiz = 0; + int newbuffsiz; + + getsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF, + &sndnlbuffsiz, &len); + + newbuffsiz = nftnl_batch_iovec_len(batch) * BATCH_PAGE_SIZE; + if (newbuffsiz <= sndnlbuffsiz) + return; + + /* Rise sender buffer length to avoid hitting -EMSGSIZE */ + setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUF, + &newbuffsiz, sizeof(socklen_t)); + + /* unpriviledged containers check for CAP_NET_ADMIN on the init_user_ns. */ + if (setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_SNDBUFFORCE, + &newbuffsiz, sizeof(socklen_t)) < 0) { + if (errno == EPERM) + ctx->maybe_emsgsize = newbuffsiz; + } +} + +static unsigned int nlsndbufsiz; + +static int mnl_set_rcvbuffer(const struct mnl_socket *nl, socklen_t bufsiz) +{ + socklen_t len = sizeof(nlsndbufsiz); + int ret; + + if (!nlsndbufsiz) { + getsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUF, + &nlsndbufsiz, &len); + } + + if (nlsndbufsiz >= bufsiz) + return 0; + + ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUFFORCE, + &bufsiz, sizeof(socklen_t)); + if (ret < 0) { + /* If this doesn't work, try to reach the system wide maximum + * (or whatever the user requested). + */ + ret = setsockopt(mnl_socket_get_fd(nl), SOL_SOCKET, SO_RCVBUF, + &bufsiz, sizeof(socklen_t)); + } + + return ret; +} + +static void mnl_nft_batch_to_msg(struct netlink_ctx *ctx, struct msghdr *msg, + const struct sockaddr_nl *snl, + struct iovec *iov, unsigned int iov_len) +{ + msg->msg_name = (struct sockaddr_nl *)snl; + msg->msg_namelen = sizeof(*snl); + msg->msg_iov = iov; + msg->msg_iovlen = iov_len; + + nftnl_batch_iovec(ctx->batch, iov, iov_len); +} + +static ssize_t mnl_nft_socket_sendmsg(struct netlink_ctx *ctx, + const struct msghdr *msg) +{ + uint32_t iov_len = msg->msg_iovlen; + struct iovec *iov = msg->msg_iov; + unsigned int i; + + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) { + for (i = 0; i < iov_len; i++) { + mnl_nlmsg_fprintf(ctx->nft->output.output_fp, + iov[i].iov_base, iov[i].iov_len, + sizeof(struct nfgenmsg)); + } + } + + return sendmsg(mnl_socket_get_fd(ctx->nft->nf_sock), msg, 0); +} + +static int err_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + uint16_t type; + + if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) + return MNL_CB_ERROR; + + type = mnl_attr_get_type(attr); + switch (type) { + case NLMSGERR_ATTR_OFFS: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int mnl_batch_extack_cb(const struct nlmsghdr *nlh, void *data) +{ + struct netlink_cb_data *cb_data = data; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + unsigned int hlen = sizeof(*err); + const char *msg = NULL; + uint32_t off = 0; + int errval; + + if (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) + return MNL_CB_ERROR; + + if (err->error < 0) + errval = -err->error; + else + errval = err->error; + + if (errval == 0) + return MNL_CB_STOP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + hlen += mnl_nlmsg_get_payload_len(&err->msg); + + if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK) + return MNL_CB_ERROR; + + if (tb[NLMSGERR_ATTR_OFFS]) + off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]); + + mnl_err_list_node_add(cb_data->err_list, errval, + nlh->nlmsg_seq, off, msg); + return MNL_CB_ERROR; +} + +#define NFT_MNL_ECHO_RCVBUFF_DEFAULT (MNL_SOCKET_BUFFER_SIZE * 1024U) +#define NFT_MNL_ACK_MAXSIZE ((sizeof(struct nlmsghdr) + \ + sizeof(struct nfgenmsg) + (1 << 16)) + \ + MNL_SOCKET_BUFFER_SIZE) + +int mnl_batch_talk(struct netlink_ctx *ctx, struct list_head *err_list, + uint32_t num_cmds) +{ + struct mnl_socket *nl = ctx->nft->nf_sock; + int ret, fd = mnl_socket_get_fd(nl), portid = mnl_socket_get_portid(nl); + uint32_t iov_len = nftnl_batch_iovec_len(ctx->batch); + char rcv_buf[NFT_MNL_ACK_MAXSIZE]; + const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + struct timeval tv = { + .tv_sec = 0, + .tv_usec = 0 + }; + struct iovec iov[iov_len]; + struct msghdr msg = {}; + unsigned int rcvbufsiz; + fd_set readfds; + static mnl_cb_t cb_ctl_array[NLMSG_MIN_TYPE] = { + [NLMSG_ERROR] = mnl_batch_extack_cb, + }; + struct netlink_cb_data cb_data = { + .err_list = err_list, + .nl_ctx = ctx, + }; + + mnl_set_sndbuffer(ctx); + + mnl_nft_batch_to_msg(ctx, &msg, &snl, iov, iov_len); + + rcvbufsiz = num_cmds * 1024; + if (nft_output_echo(&ctx->nft->output)) { + if (rcvbufsiz < NFT_MNL_ECHO_RCVBUFF_DEFAULT) + rcvbufsiz = NFT_MNL_ECHO_RCVBUFF_DEFAULT; + } + + mnl_set_rcvbuffer(ctx->nft->nf_sock, rcvbufsiz); + + ret = mnl_nft_socket_sendmsg(ctx, &msg); + if (ret == -1) + return -1; + + /* receive and digest all the acknowledgments from the kernel. */ + while (true) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + ret = select(fd + 1, &readfds, NULL, NULL, &tv); + if (ret == -1) + return -1; + + if (!FD_ISSET(fd, &readfds)) + break; + + ret = mnl_socket_recvfrom(nl, rcv_buf, sizeof(rcv_buf)); + if (ret == -1) + return -1; + + /* Continue on error, make sure we get all acknowledgments */ + ret = mnl_cb_run2(rcv_buf, ret, 0, portid, + netlink_echo_callback, &cb_data, + cb_ctl_array, MNL_ARRAY_SIZE(cb_ctl_array)); + } + return 0; +} + +struct mnl_nft_rule_build_ctx { + struct netlink_linearize_ctx *lctx; + struct nlmsghdr *nlh; + struct cmd *cmd; +}; + +static int mnl_nft_expr_build_cb(struct nftnl_expr *nle, void *data) +{ + struct mnl_nft_rule_build_ctx *ctx = data; + struct nlmsghdr *nlh = ctx->nlh; + struct cmd *cmd = ctx->cmd; + struct nft_expr_loc *eloc; + struct nlattr *nest; + + eloc = nft_expr_loc_find(nle, ctx->lctx); + if (eloc) + cmd_add_loc(cmd, nlh->nlmsg_len, eloc->loc); + + nest = mnl_attr_nest_start(nlh, NFTA_LIST_ELEM); + nftnl_expr_build_payload(nlh, nle); + mnl_attr_nest_end(nlh, nest); + + nftnl_rule_del_expr(nle); + nftnl_expr_free(nle); + + return 0; +} + +static void mnl_nft_rule_build_ctx_init(struct mnl_nft_rule_build_ctx *rule_ctx, + struct nlmsghdr *nlh, + struct cmd *cmd, + struct netlink_linearize_ctx *lctx) +{ + memset(rule_ctx, 0, sizeof(*rule_ctx)); + rule_ctx->nlh = nlh; + rule_ctx->cmd = cmd; + rule_ctx->lctx = lctx; +} + +int mnl_nft_rule_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct mnl_nft_rule_build_ctx rule_ctx; + struct netlink_linearize_ctx lctx; + struct rule *rule = cmd->rule; + struct handle *h = &rule->handle; + struct nftnl_rule *nlr; + struct nlmsghdr *nlh; + struct nlattr *nest; + + nlr = nftnl_rule_alloc(); + if (!nlr) + memory_allocation_error(); + + nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family); + if (h->position.id) + nftnl_rule_set_u64(nlr, NFTNL_RULE_POSITION, h->position.id); + if (h->rule_id) + nftnl_rule_set_u32(nlr, NFTNL_RULE_ID, h->rule_id); + if (h->position_id) + nftnl_rule_set_u32(nlr, NFTNL_RULE_POSITION_ID, h->position_id); + + netlink_linearize_init(&lctx, nlr); + netlink_linearize_rule(ctx, rule, &lctx); + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWRULE, + cmd->handle.family, + NLM_F_CREATE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location); + mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location); + + if (h->chain_id) + mnl_attr_put_u32(nlh, NFTA_RULE_CHAIN_ID, htonl(h->chain_id)); + else + mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name); + + mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx); + + nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS); + nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx); + mnl_attr_nest_end(nlh, nest); + + nftnl_rule_nlmsg_build_payload(nlh, nlr); + nftnl_rule_free(nlr); + netlink_linearize_fini(&lctx); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_rule_replace(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct mnl_nft_rule_build_ctx rule_ctx; + struct netlink_linearize_ctx lctx; + struct rule *rule = cmd->rule; + struct handle *h = &rule->handle; + unsigned int flags = 0; + struct nftnl_rule *nlr; + struct nlmsghdr *nlh; + struct nlattr *nest; + + if (nft_output_echo(&ctx->nft->output)) + flags |= NLM_F_ECHO; + + nlr = nftnl_rule_alloc(); + if (!nlr) + memory_allocation_error(); + + nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family); + + netlink_linearize_init(&lctx, nlr); + netlink_linearize_rule(ctx, rule, &lctx); + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWRULE, + cmd->handle.family, + NLM_F_REPLACE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location); + mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location); + mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location); + mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id)); + + mnl_nft_rule_build_ctx_init(&rule_ctx, nlh, cmd, &lctx); + + nest = mnl_attr_nest_start(nlh, NFTA_RULE_EXPRESSIONS); + nftnl_expr_foreach(nlr, mnl_nft_expr_build_cb, &rule_ctx); + mnl_attr_nest_end(nlh, nest); + + nftnl_rule_nlmsg_build_payload(nlh, nlr); + nftnl_rule_free(nlr); + netlink_linearize_fini(&lctx); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_rule_del(struct netlink_ctx *ctx, struct cmd *cmd) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELRULE; + struct handle *h = &cmd->handle; + struct nftnl_rule *nlr; + struct nlmsghdr *nlh; + + nlr = nftnl_rule_alloc(); + if (!nlr) + memory_allocation_error(); + + nftnl_rule_set_u32(nlr, NFTNL_RULE_FAMILY, h->family); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYRULE; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + msg_type, + nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY), + 0, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location); + mnl_attr_put_strz(nlh, NFTA_RULE_TABLE, h->table.name); + if (h->chain.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, &h->chain.location); + mnl_attr_put_strz(nlh, NFTA_RULE_CHAIN, h->chain.name); + } + if (h->handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &h->handle.location); + mnl_attr_put_u64(nlh, NFTA_RULE_HANDLE, htobe64(h->handle.id)); + } + + nftnl_rule_nlmsg_build_payload(nlh, nlr); + nftnl_rule_free(nlr); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +/* + * Rule + */ + +static int rule_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_rule_list *nlr_list = data; + struct nftnl_rule *r; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + r = nftnl_rule_alloc(); + if (r == NULL) + memory_allocation_error(); + + if (nftnl_rule_nlmsg_parse(nlh, r) < 0) + goto err_free; + + nftnl_rule_list_add_tail(r, nlr_list); + return MNL_CB_OK; + +err_free: + nftnl_rule_free(r); + return MNL_CB_OK; +} + +struct nftnl_rule_list *mnl_nft_rule_dump(struct netlink_ctx *ctx, int family, + const char *table, const char *chain, + uint64_t rule_handle, + bool dump, bool reset) +{ + uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_rule_list *nlr_list; + struct nftnl_rule *nlr = NULL; + struct nlmsghdr *nlh; + int msg_type, ret; + + if (reset) + msg_type = NFT_MSG_GETRULE_RESET; + else + msg_type = NFT_MSG_GETRULE; + + if (table) { + nlr = nftnl_rule_alloc(); + if (!nlr) + memory_allocation_error(); + + nftnl_rule_set_str(nlr, NFTNL_RULE_TABLE, table); + if (chain) + nftnl_rule_set_str(nlr, NFTNL_RULE_CHAIN, chain); + if (rule_handle) + nftnl_rule_set_u64(nlr, NFTNL_RULE_HANDLE, rule_handle); + } + + nlr_list = nftnl_rule_list_alloc(); + if (nlr_list == NULL) + memory_allocation_error(); + + nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family, + nl_flags, ctx->seqnum); + if (nlr) { + nftnl_rule_nlmsg_build_payload(nlh, nlr); + nftnl_rule_free(nlr); + } + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, rule_cb, nlr_list); + if (ret < 0) + goto err; + + return nlr_list; +err: + nftnl_rule_list_free(nlr_list); + return NULL; +} + +/* + * Chain + */ + +struct nft_dev { + const char *ifname; + const struct location *location; +}; + +static void nft_dev_add(struct nft_dev *dev_array, const struct expr *expr, int i) +{ + unsigned int ifname_len; + char ifname[IFNAMSIZ]; + + ifname_len = div_round_up(expr->len, BITS_PER_BYTE); + memset(ifname, 0, sizeof(ifname)); + mpz_export_data(ifname, expr->value, BYTEORDER_HOST_ENDIAN, ifname_len); + dev_array[i].ifname = xstrdup(ifname); + dev_array[i].location = &expr->location; +} + +static struct nft_dev *nft_dev_array(const struct expr *dev_expr, int *num_devs) +{ + struct nft_dev *dev_array; + int i = 0, len = 1; + struct expr *expr; + + switch (dev_expr->etype) { + case EXPR_SET: + case EXPR_LIST: + list_for_each_entry(expr, &dev_expr->expressions, list) + len++; + + dev_array = xmalloc(sizeof(struct nft_dev) * len); + + list_for_each_entry(expr, &dev_expr->expressions, list) { + nft_dev_add(dev_array, expr, i); + i++; + } + break; + case EXPR_VALUE: + len++; + dev_array = xmalloc(sizeof(struct nft_dev) * len); + nft_dev_add(dev_array, dev_expr, i); + i++; + break; + default: + assert(0); + } + + dev_array[i].ifname = NULL; + *num_devs = i; + + return dev_array; +} + +static void nft_dev_array_free(const struct nft_dev *dev_array) +{ + int i = 0; + + while (dev_array[i].ifname != NULL) + xfree(dev_array[i++].ifname); + + xfree(dev_array); +} + +static void mnl_nft_chain_devs_build(struct nlmsghdr *nlh, struct cmd *cmd) +{ + const struct expr *dev_expr = cmd->chain->dev_expr; + const struct nft_dev *dev_array; + struct nlattr *nest_dev; + int i, num_devs = 0; + + dev_array = nft_dev_array(dev_expr, &num_devs); + if (num_devs == 1) { + cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[0].location); + mnl_attr_put_strz(nlh, NFTA_HOOK_DEV, dev_array[0].ifname); + } else { + nest_dev = mnl_attr_nest_start(nlh, NFTA_HOOK_DEVS); + for (i = 0; i < num_devs; i++) { + cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location); + mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname); + mnl_attr_nest_end(nlh, nest_dev); + } + } + nft_dev_array_free(dev_array); +} + +int mnl_nft_chain_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct nftnl_udata_buf *udbuf; + struct nftnl_chain *nlc; + struct nlmsghdr *nlh; + int priority, policy; + + nlc = nftnl_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + + nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family); + + if (cmd->chain) { + if (cmd->chain->flags & CHAIN_F_HW_OFFLOAD) { + nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, + CHAIN_F_HW_OFFLOAD); + } + if (cmd->chain->comment) { + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udbuf) + memory_allocation_error(); + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_CHAIN_COMMENT, cmd->chain->comment)) + memory_allocation_error(); + nftnl_chain_set_data(nlc, NFTNL_CHAIN_USERDATA, nftnl_udata_buf_data(udbuf), + nftnl_udata_buf_len(udbuf)); + nftnl_udata_buf_free(udbuf); + } + } + netlink_dump_chain(nlc, ctx); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWCHAIN, + cmd->handle.family, + NLM_F_CREATE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location); + + if (!cmd->chain || !(cmd->chain->flags & CHAIN_F_BINDING)) { + mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name); + } else { + if (cmd->handle.chain.name) + mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, + cmd->handle.chain.name); + + mnl_attr_put_u32(nlh, NFTA_CHAIN_ID, htonl(cmd->handle.chain_id)); + if (cmd->chain->flags) + nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FLAGS, cmd->chain->flags); + } + + if (cmd->chain && cmd->chain->policy) { + mpz_export_data(&policy, cmd->chain->policy->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->policy->location); + mnl_attr_put_u32(nlh, NFTA_CHAIN_POLICY, htonl(policy)); + } + + nftnl_chain_unset(nlc, NFTNL_CHAIN_TYPE); + + nftnl_chain_nlmsg_build_payload(nlh, nlc); + + if (cmd->chain && cmd->chain->flags & CHAIN_F_BASECHAIN) { + struct nlattr *nest; + + if (cmd->chain->type.str) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->chain->type.loc); + mnl_attr_put_strz(nlh, NFTA_CHAIN_TYPE, cmd->chain->type.str); + } + + nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK); + + if (cmd->chain->type.str) { + mnl_attr_put_u32(nlh, NFTA_HOOK_HOOKNUM, htonl(cmd->chain->hook.num)); + mpz_export_data(&priority, cmd->chain->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + mnl_attr_put_u32(nlh, NFTA_HOOK_PRIORITY, htonl(priority)); + } + + if (cmd->chain && cmd->chain->dev_expr) + mnl_nft_chain_devs_build(nlh, cmd); + + mnl_attr_nest_end(nlh, nest); + } + + nftnl_chain_free(nlc); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_chain_rename(struct netlink_ctx *ctx, const struct cmd *cmd, + const struct chain *chain) +{ + const char *name = cmd->arg; + struct nftnl_chain *nlc; + struct nlmsghdr *nlh; + + nlc = nftnl_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + + nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family); + nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, cmd->handle.table.name); + nftnl_chain_set_u64(nlc, NFTNL_CHAIN_HANDLE, chain->handle.handle.id); + nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, name); + + netlink_dump_chain(nlc, ctx); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWCHAIN, + cmd->handle.family, + 0, ctx->seqnum); + nftnl_chain_nlmsg_build_payload(nlh, nlc); + nftnl_chain_free(nlc); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_chain_del(struct netlink_ctx *ctx, struct cmd *cmd) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELCHAIN; + struct nftnl_chain *nlc; + struct nlmsghdr *nlh; + + nlc = nftnl_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + + nftnl_chain_set_u32(nlc, NFTNL_CHAIN_FAMILY, cmd->handle.family); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYCHAIN; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + msg_type, + cmd->handle.family, + 0, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_CHAIN_TABLE, cmd->handle.table.name); + if (cmd->handle.chain.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.chain.location); + mnl_attr_put_strz(nlh, NFTA_CHAIN_NAME, cmd->handle.chain.name); + } else if (cmd->handle.handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location); + mnl_attr_put_u64(nlh, NFTA_CHAIN_HANDLE, + htobe64(cmd->handle.handle.id)); + } + + if (cmd->op == CMD_DELETE && + cmd->chain && cmd->chain->dev_expr) { + struct nlattr *nest; + + nest = mnl_attr_nest_start(nlh, NFTA_CHAIN_HOOK); + mnl_nft_chain_devs_build(nlh, cmd); + mnl_attr_nest_end(nlh, nest); + } + + nftnl_chain_nlmsg_build_payload(nlh, nlc); + nftnl_chain_free(nlc); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +static int chain_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_chain_list *nlc_list = data; + struct nftnl_chain *c; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + c = nftnl_chain_alloc(); + if (c == NULL) + memory_allocation_error(); + + if (nftnl_chain_nlmsg_parse(nlh, c) < 0) + goto err_free; + + nftnl_chain_list_add_tail(c, nlc_list); + return MNL_CB_OK; + +err_free: + nftnl_chain_free(c); + return MNL_CB_OK; +} + +struct nftnl_chain_list *mnl_nft_chain_dump(struct netlink_ctx *ctx, + int family, const char *table, + const char *chain) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_chain_list *nlc_list; + struct nftnl_chain *nlc = NULL; + struct nlmsghdr *nlh; + int ret; + + nlc_list = nftnl_chain_list_alloc(); + if (nlc_list == NULL) + memory_allocation_error(); + + if (table && chain) { + nlc = nftnl_chain_alloc(); + if (!nlc) + memory_allocation_error(); + + nftnl_chain_set_str(nlc, NFTNL_CHAIN_TABLE, table); + nftnl_chain_set_str(nlc, NFTNL_CHAIN_NAME, chain); + } + + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETCHAIN, family, + nlc ? NLM_F_ACK : NLM_F_DUMP, ctx->seqnum); + if (nlc) { + nftnl_chain_nlmsg_build_payload(nlh, nlc); + nftnl_chain_free(nlc); + } + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, chain_cb, nlc_list); + if (ret < 0 && errno != ENOENT) + goto err; + + return nlc_list; +err: + nftnl_chain_list_free(nlc_list); + return NULL; +} + +/* + * Table + */ +int mnl_nft_table_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct nftnl_udata_buf *udbuf; + struct nftnl_table *nlt; + struct nlmsghdr *nlh; + + nlt = nftnl_table_alloc(); + if (nlt == NULL) + memory_allocation_error(); + + nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family); + if (cmd->table) { + nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, cmd->table->flags); + + if (cmd->table->comment) { + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udbuf) + memory_allocation_error(); + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_TABLE_COMMENT, cmd->table->comment)) + memory_allocation_error(); + nftnl_table_set_data(nlt, NFTNL_TABLE_USERDATA, nftnl_udata_buf_data(udbuf), + nftnl_udata_buf_len(udbuf)); + nftnl_udata_buf_free(udbuf); + } + } else { + nftnl_table_set_u32(nlt, NFTNL_TABLE_FLAGS, 0); + } + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWTABLE, + cmd->handle.family, + flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name); + nftnl_table_nlmsg_build_payload(nlh, nlt); + nftnl_table_free(nlt); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_table_del(struct netlink_ctx *ctx, struct cmd *cmd) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELTABLE; + struct nftnl_table *nlt; + struct nlmsghdr *nlh; + + nlt = nftnl_table_alloc(); + if (nlt == NULL) + memory_allocation_error(); + + nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, cmd->handle.family); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYTABLE; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), msg_type, + cmd->handle.family, 0, ctx->seqnum); + + if (cmd->handle.table.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_TABLE_NAME, cmd->handle.table.name); + } else if (cmd->handle.handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location); + mnl_attr_put_u64(nlh, NFTA_TABLE_HANDLE, + htobe64(cmd->handle.handle.id)); + } + nftnl_table_nlmsg_build_payload(nlh, nlt); + nftnl_table_free(nlt); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +static int table_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_table_list *nlt_list = data; + struct nftnl_table *t; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + t = nftnl_table_alloc(); + if (t == NULL) + memory_allocation_error(); + + if (nftnl_table_nlmsg_parse(nlh, t) < 0) + goto err_free; + + nftnl_table_list_add_tail(t, nlt_list); + return MNL_CB_OK; + +err_free: + nftnl_table_free(t); + return MNL_CB_OK; +} + +struct nftnl_table_list *mnl_nft_table_dump(struct netlink_ctx *ctx, + int family, const char *table) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_table_list *nlt_list; + struct nftnl_table *nlt = NULL; + int flags = NLM_F_DUMP; + struct nlmsghdr *nlh; + int ret; + + nlt_list = nftnl_table_list_alloc(); + if (nlt_list == NULL) + return NULL; + + if (table) { + nlt = nftnl_table_alloc(); + if (!nlt) + memory_allocation_error(); + + nftnl_table_set_u32(nlt, NFTNL_TABLE_FAMILY, family); + nftnl_table_set_str(nlt, NFTNL_TABLE_NAME, table); + flags = NLM_F_ACK; + } + + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETTABLE, family, + flags, ctx->seqnum); + if (nlt) { + nftnl_table_nlmsg_build_payload(nlh, nlt); + nftnl_table_free(nlt); + } + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, table_cb, nlt_list); + if (ret < 0 && errno != ENOENT) + goto err; + + return nlt_list; +err: + nftnl_table_list_free(nlt_list); + return NULL; +} + +static void set_key_expression(struct netlink_ctx *ctx, + struct expr *expr, uint32_t set_flags, + struct nftnl_udata_buf *udbuf, + unsigned int type) +{ + struct nftnl_udata *nest1, *nest2; + + if (!expr_ops(expr)->build_udata) + return; + + nest1 = nftnl_udata_nest_start(udbuf, type); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_TYPEOF_EXPR, expr->etype); + nest2 = nftnl_udata_nest_start(udbuf, NFTNL_UDATA_SET_TYPEOF_DATA); + expr_ops(expr)->build_udata(udbuf, expr); + nftnl_udata_nest_end(udbuf, nest2); + nftnl_udata_nest_end(udbuf, nest1); +} + +/* + * Set + */ +int mnl_nft_set_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct handle *h = &cmd->handle; + struct nftnl_udata_buf *udbuf; + struct set *set = cmd->set; + struct nftnl_set *nls; + struct nlmsghdr *nlh; + struct stmt *stmt; + int num_stmts = 0; + + nls = nftnl_set_alloc(); + if (!nls) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + nftnl_set_set_u32(nls, NFTNL_SET_ID, h->set_id); + + nftnl_set_set_u32(nls, NFTNL_SET_FLAGS, set->flags); + nftnl_set_set_u32(nls, NFTNL_SET_KEY_TYPE, + dtype_map_to_kernel(set->key->dtype)); + nftnl_set_set_u32(nls, NFTNL_SET_KEY_LEN, + div_round_up(set->key->len, BITS_PER_BYTE)); + if (set_is_datamap(set->flags)) { + nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE, + dtype_map_to_kernel(set->data->dtype)); + nftnl_set_set_u32(nls, NFTNL_SET_DATA_LEN, + div_round_up(set->data->len, BITS_PER_BYTE)); + } + if (set_is_objmap(set->flags)) + nftnl_set_set_u32(nls, NFTNL_SET_OBJ_TYPE, set->objtype); + + if (set->timeout) + nftnl_set_set_u64(nls, NFTNL_SET_TIMEOUT, set->timeout); + if (set->gc_int) + nftnl_set_set_u32(nls, NFTNL_SET_GC_INTERVAL, set->gc_int); + + nftnl_set_set_u32(nls, NFTNL_SET_ID, set->handle.set_id); + + if (!(set->flags & NFT_SET_CONSTANT)) { + if (set->policy != NFT_SET_POL_PERFORMANCE) + nftnl_set_set_u32(nls, NFTNL_SET_POLICY, set->policy); + + if (set->desc.size != 0) + nftnl_set_set_u32(nls, NFTNL_SET_DESC_SIZE, + set->desc.size); + } else if (set->init) { + nftnl_set_set_u32(nls, NFTNL_SET_DESC_SIZE, set->init->size); + } + + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udbuf) + memory_allocation_error(); + if (!nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEYBYTEORDER, + set->key->byteorder)) + memory_allocation_error(); + + if (set_is_datamap(set->flags) && + !nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATABYTEORDER, + set->data->byteorder)) + memory_allocation_error(); + + if (set->automerge && + !nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_MERGE_ELEMENTS, + set->automerge)) + memory_allocation_error(); + + set_key_expression(ctx, set->key, set->flags, udbuf, NFTNL_UDATA_SET_KEY_TYPEOF); + if (set->data) { + set_key_expression(ctx, set->data, set->flags, udbuf, NFTNL_UDATA_SET_DATA_TYPEOF); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_DATA_INTERVAL, + !!(set->data->flags & EXPR_F_INTERVAL)); + } + + if (set->desc.field_len[0]) { + nftnl_set_set_data(nls, NFTNL_SET_DESC_CONCAT, + set->desc.field_len, + set->desc.field_count * + sizeof(set->desc.field_len[0])); + } + + if (set->comment) { + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_SET_COMMENT, set->comment)) + memory_allocation_error(); + } + + nftnl_set_set_data(nls, NFTNL_SET_USERDATA, nftnl_udata_buf_data(udbuf), + nftnl_udata_buf_len(udbuf)); + nftnl_udata_buf_free(udbuf); + + list_for_each_entry(stmt, &set->stmt_list, list) + num_stmts++; + + if (num_stmts == 1) { + list_for_each_entry(stmt, &set->stmt_list, list) { + nftnl_set_set_data(nls, NFTNL_SET_EXPR, + netlink_gen_stmt_stateful(stmt), 0); + break; + } + } else if (num_stmts > 1) { + list_for_each_entry(stmt, &set->stmt_list, list) + nftnl_set_add_expr(nls, netlink_gen_stmt_stateful(stmt)); + } + + netlink_dump_set(nls, ctx); + + nftnl_set_unset(nls, NFTNL_SET_TABLE); + nftnl_set_unset(nls, NFTNL_SET_NAME); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWSET, + h->family, + NLM_F_CREATE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &h->table.location); + mnl_attr_put_strz(nlh, NFTA_SET_TABLE, h->table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &h->set.location); + mnl_attr_put_strz(nlh, NFTA_SET_NAME, h->set.name); + + nftnl_set_nlmsg_build_payload(nlh, nls); + nftnl_set_free(nls); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_set_del(struct netlink_ctx *ctx, struct cmd *cmd) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELSET; + const struct handle *h = &cmd->handle; + struct nftnl_set *nls; + struct nlmsghdr *nlh; + + nls = nftnl_set_alloc(); + if (!nls) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYSET; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + msg_type, + h->family, + 0, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_SET_TABLE, cmd->handle.table.name); + if (h->set.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.set.location); + mnl_attr_put_strz(nlh, NFTA_SET_NAME, cmd->handle.set.name); + } else if (h->handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location); + mnl_attr_put_u64(nlh, NFTA_SET_HANDLE, + htobe64(cmd->handle.handle.id)); + } + + nftnl_set_nlmsg_build_payload(nlh, nls); + nftnl_set_free(nls); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +static int set_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_set_list *nls_list = data; + struct nftnl_set *s; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + s = nftnl_set_alloc(); + if (s == NULL) + memory_allocation_error(); + + if (nftnl_set_nlmsg_parse(nlh, s) < 0) + goto err_free; + + nftnl_set_list_add_tail(s, nls_list); + return MNL_CB_OK; + +err_free: + nftnl_set_free(s); + return MNL_CB_OK; +} + +struct nftnl_set_list * +mnl_nft_set_dump(struct netlink_ctx *ctx, int family, + const char *table, const char *set) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_set_list *nls_list; + int flags = NLM_F_DUMP; + struct nlmsghdr *nlh; + struct nftnl_set *s; + int ret; + + s = nftnl_set_alloc(); + if (s == NULL) + memory_allocation_error(); + + if (table != NULL) + nftnl_set_set_str(s, NFTNL_SET_TABLE, table); + if (set) { + nftnl_set_set_str(s, NFTNL_SET_NAME, set); + flags = NLM_F_ACK; + } + + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETSET, family, + flags, ctx->seqnum); + nftnl_set_nlmsg_build_payload(nlh, s); + nftnl_set_free(s); + + nls_list = nftnl_set_list_alloc(); + if (nls_list == NULL) + memory_allocation_error(); + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_cb, nls_list); + if (ret < 0 && errno != ENOENT) + goto err; + + return nls_list; +err: + nftnl_set_list_free(nls_list); + return NULL; +} + +int mnl_nft_obj_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct obj *obj = cmd->object; + struct nftnl_udata_buf *udbuf; + struct nftnl_obj *nlo; + struct nlmsghdr *nlh; + + nlo = nftnl_obj_alloc(); + if (!nlo) + memory_allocation_error(); + + nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, obj->type); + + if (obj->comment) { + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udbuf) + memory_allocation_error(); + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_OBJ_COMMENT, obj->comment)) + memory_allocation_error(); + nftnl_obj_set_data(nlo, NFTNL_OBJ_USERDATA, nftnl_udata_buf_data(udbuf), + nftnl_udata_buf_len(udbuf)); + nftnl_udata_buf_free(udbuf); + } + + switch (obj->type) { + case NFT_OBJECT_COUNTER: + nftnl_obj_set_u64(nlo, NFTNL_OBJ_CTR_PKTS, + obj->counter.packets); + nftnl_obj_set_u64(nlo, NFTNL_OBJ_CTR_BYTES, + obj->counter.bytes); + break; + case NFT_OBJECT_QUOTA: + nftnl_obj_set_u64(nlo, NFTNL_OBJ_QUOTA_BYTES, + obj->quota.bytes); + nftnl_obj_set_u64(nlo, NFTNL_OBJ_QUOTA_CONSUMED, + obj->quota.used); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS, + obj->quota.flags); + break; + case NFT_OBJECT_LIMIT: + nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_RATE, obj->limit.rate); + nftnl_obj_set_u64(nlo, NFTNL_OBJ_LIMIT_UNIT, obj->limit.unit); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_BURST, obj->limit.burst); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_TYPE, obj->limit.type); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS, obj->limit.flags); + break; + case NFT_OBJECT_CT_HELPER: + nftnl_obj_set_str(nlo, NFTNL_OBJ_CT_HELPER_NAME, + obj->ct_helper.name); + nftnl_obj_set_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO, + obj->ct_helper.l4proto); + if (obj->ct_helper.l3proto) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO, + obj->ct_helper.l3proto); + break; + case NFT_OBJECT_CT_TIMEOUT: + nftnl_obj_set_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO, + obj->ct_timeout.l4proto); + if (obj->ct_timeout.l3proto) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO, + obj->ct_timeout.l3proto); + nftnl_obj_set_data(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY, + obj->ct_timeout.timeout, + sizeof(obj->ct_timeout.timeout)); + break; + case NFT_OBJECT_CT_EXPECT: + if (obj->ct_expect.l3proto) + nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_EXPECT_L3PROTO, + obj->ct_expect.l3proto); + nftnl_obj_set_u8(nlo, NFTNL_OBJ_CT_EXPECT_L4PROTO, + obj->ct_expect.l4proto); + nftnl_obj_set_u16(nlo, NFTNL_OBJ_CT_EXPECT_DPORT, + obj->ct_expect.dport); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_CT_EXPECT_TIMEOUT, + obj->ct_expect.timeout); + nftnl_obj_set_u8(nlo, NFTNL_OBJ_CT_EXPECT_SIZE, + obj->ct_expect.size); + break; + case NFT_OBJECT_SECMARK: + nftnl_obj_set_str(nlo, NFTNL_OBJ_SECMARK_CTX, + obj->secmark.ctx); + break; + case NFT_OBJECT_SYNPROXY: + nftnl_obj_set_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS, + obj->synproxy.mss); + nftnl_obj_set_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE, + obj->synproxy.wscale); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS, + obj->synproxy.flags); + break; + default: + BUG("Unknown type %d\n", obj->type); + break; + } + netlink_dump_obj(nlo, ctx); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWOBJ, cmd->handle.family, + NLM_F_CREATE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location); + mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name); + + nftnl_obj_nlmsg_build_payload(nlh, nlo); + nftnl_obj_free(nlo); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_obj_del(struct netlink_ctx *ctx, struct cmd *cmd, int type) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELOBJ; + struct nftnl_obj *nlo; + struct nlmsghdr *nlh; + + nlo = nftnl_obj_alloc(); + if (!nlo) + memory_allocation_error(); + + nftnl_obj_set_u32(nlo, NFTNL_OBJ_FAMILY, cmd->handle.family); + nftnl_obj_set_u32(nlo, NFTNL_OBJ_TYPE, type); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYOBJ; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + msg_type, cmd->handle.family, + 0, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_OBJ_TABLE, cmd->handle.table.name); + + if (cmd->handle.obj.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.obj.location); + mnl_attr_put_strz(nlh, NFTA_OBJ_NAME, cmd->handle.obj.name); + } else if (cmd->handle.handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location); + mnl_attr_put_u64(nlh, NFTA_OBJ_HANDLE, + htobe64(cmd->handle.handle.id)); + } + + nftnl_obj_nlmsg_build_payload(nlh, nlo); + nftnl_obj_free(nlo); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +static int obj_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_obj_list *nln_list = data; + struct nftnl_obj *n; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + n = nftnl_obj_alloc(); + if (n == NULL) + memory_allocation_error(); + + if (nftnl_obj_nlmsg_parse(nlh, n) < 0) + goto err_free; + + nftnl_obj_list_add_tail(n, nln_list); + return MNL_CB_OK; + +err_free: + nftnl_obj_free(n); + return MNL_CB_OK; +} + + +struct nftnl_obj_list * +mnl_nft_obj_dump(struct netlink_ctx *ctx, int family, + const char *table, const char *name, uint32_t type, bool dump, + bool reset) +{ + uint16_t nl_flags = dump ? NLM_F_DUMP : NLM_F_ACK; + struct nftnl_obj_list *nln_list; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct nftnl_obj *n; + int msg_type, ret; + + if (reset) + msg_type = NFT_MSG_GETOBJ_RESET; + else + msg_type = NFT_MSG_GETOBJ; + + n = nftnl_obj_alloc(); + if (n == NULL) + memory_allocation_error(); + + nlh = nftnl_nlmsg_build_hdr(buf, msg_type, family, + nl_flags, ctx->seqnum); + if (table != NULL) + nftnl_obj_set_str(n, NFTNL_OBJ_TABLE, table); + if (name != NULL) + nftnl_obj_set_str(n, NFTNL_OBJ_NAME, name); + if (type != NFT_OBJECT_UNSPEC) + nftnl_obj_set_u32(n, NFTNL_OBJ_TYPE, type); + nftnl_obj_nlmsg_build_payload(nlh, n); + nftnl_obj_free(n); + + nln_list = nftnl_obj_list_alloc(); + if (nln_list == NULL) + memory_allocation_error(); + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, obj_cb, nln_list); + if (ret < 0) + goto err; + + return nln_list; +err: + nftnl_obj_list_free(nln_list); + return NULL; +} + +/* + * Set elements + */ +static int set_elem_cb(const struct nlmsghdr *nlh, void *data) +{ + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + nftnl_set_elems_nlmsg_parse(nlh, data); + return MNL_CB_OK; +} + +static bool mnl_nft_attr_nest_overflow(struct nlmsghdr *nlh, + const struct nlattr *from, + const struct nlattr *to) +{ + int len = (void *)to + to->nla_len - (void *)from; + + /* The attribute length field is 16 bits long, thus the maximum payload + * that an attribute can convey is UINT16_MAX. In case of overflow, + * discard the last attribute that did not fit into the nest. + */ + if (len > UINT16_MAX) { + nlh->nlmsg_len -= to->nla_len; + return true; + } + return false; +} + +static void netlink_dump_setelem(const struct nftnl_set_elem *nlse, + struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + char buf[4096]; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_set_elem_snprintf(buf, sizeof(buf), nlse, NFTNL_OUTPUT_DEFAULT, 0); + fprintf(fp, "\t%s", buf); +} + +static void netlink_dump_setelem_done(struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + fprintf(fp, "\n"); +} + +static int mnl_nft_setelem_batch(const struct nftnl_set *nls, struct cmd *cmd, + struct nftnl_batch *batch, + enum nf_tables_msg_types msg_type, + unsigned int flags, uint32_t seqnum, + const struct expr *set, + struct netlink_ctx *ctx) +{ + struct nlattr *nest1, *nest2; + struct nftnl_set_elem *nlse; + struct nlmsghdr *nlh; + struct expr *expr = NULL; + int i = 0; + + if (msg_type == NFT_MSG_NEWSETELEM) + flags |= NLM_F_CREATE; + + if (set) + expr = list_first_entry(&set->expressions, struct expr, list); + +next: + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(batch), msg_type, + nftnl_set_get_u32(nls, NFTNL_SET_FAMILY), + flags, seqnum); + + if (nftnl_set_is_set(nls, NFTNL_SET_TABLE)) { + mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_TABLE, + nftnl_set_get_str(nls, NFTNL_SET_TABLE)); + } + if (nftnl_set_is_set(nls, NFTNL_SET_NAME)) { + mnl_attr_put_strz(nlh, NFTA_SET_ELEM_LIST_SET, + nftnl_set_get_str(nls, NFTNL_SET_NAME)); + } + if (nftnl_set_is_set(nls, NFTNL_SET_ID)) { + mnl_attr_put_u32(nlh, NFTA_SET_ELEM_LIST_SET_ID, + htonl(nftnl_set_get_u32(nls, NFTNL_SET_ID))); + } + + if (!set || list_empty(&set->expressions)) + return 0; + + assert(expr); + nest1 = mnl_attr_nest_start(nlh, NFTA_SET_ELEM_LIST_ELEMENTS); + list_for_each_entry_from(expr, &set->expressions, list) { + nlse = alloc_nftnl_setelem(set, expr); + + cmd_add_loc(cmd, nlh->nlmsg_len, &expr->location); + nest2 = mnl_attr_nest_start(nlh, ++i); + nftnl_set_elem_nlmsg_build_payload(nlh, nlse); + mnl_attr_nest_end(nlh, nest2); + + netlink_dump_setelem(nlse, ctx); + nftnl_set_elem_free(nlse); + if (mnl_nft_attr_nest_overflow(nlh, nest1, nest2)) { + mnl_attr_nest_end(nlh, nest1); + mnl_nft_batch_continue(batch); + goto next; + } + } + mnl_attr_nest_end(nlh, nest1); + mnl_nft_batch_continue(batch); + netlink_dump_setelem_done(ctx); + + return 0; +} + +int mnl_nft_setelem_add(struct netlink_ctx *ctx, struct cmd *cmd, + const struct set *set, const struct expr *expr, + unsigned int flags) +{ + const struct handle *h = &set->handle; + struct nftnl_set *nls; + int err; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + if (h->set_id) + nftnl_set_set_u32(nls, NFTNL_SET_ID, h->set_id); + if (set_is_datamap(set->flags)) + nftnl_set_set_u32(nls, NFTNL_SET_DATA_TYPE, + dtype_map_to_kernel(set->data->dtype)); + + netlink_dump_set(nls, ctx); + + err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, NFT_MSG_NEWSETELEM, + flags, ctx->seqnum, expr, ctx); + nftnl_set_free(nls); + + return err; +} + +int mnl_nft_setelem_flush(struct netlink_ctx *ctx, const struct cmd *cmd) +{ + const struct handle *h = &cmd->handle; + struct nftnl_set *nls; + struct nlmsghdr *nlh; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + if (h->handle.id) + nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id); + + netlink_dump_set(nls, ctx); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_DELSETELEM, + h->family, + 0, ctx->seqnum); + nftnl_set_elems_nlmsg_build_payload(nlh, nls); + nftnl_set_free(nls); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_setelem_del(struct netlink_ctx *ctx, struct cmd *cmd, + const struct handle *h, const struct expr *init) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELSETELEM; + struct nftnl_set *nls; + int err; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + if (h->set.name) + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + else if (h->handle.id) + nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id); + + netlink_dump_set(nls, ctx); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYSETELEM; + + err = mnl_nft_setelem_batch(nls, cmd, ctx->batch, msg_type, 0, + ctx->seqnum, init, ctx); + nftnl_set_free(nls); + + return err; +} + +struct nftnl_set *mnl_nft_setelem_get_one(struct netlink_ctx *ctx, + struct nftnl_set *nls_in, + bool reset) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_set *nls_out; + struct nlmsghdr *nlh; + int msg_type; + int err; + + if (reset) + msg_type = NFT_MSG_GETSETELEM_RESET; + else + msg_type = NFT_MSG_GETSETELEM; + + nlh = nftnl_nlmsg_build_hdr(buf, msg_type, + nftnl_set_get_u32(nls_in, NFTNL_SET_FAMILY), + NLM_F_ACK, ctx->seqnum); + nftnl_set_elems_nlmsg_build_payload(nlh, nls_in); + + nls_out = nftnl_set_alloc(); + if (!nls_out) + return NULL; + + nftnl_set_set_str(nls_out, NFTNL_SET_TABLE, + nftnl_set_get_str(nls_in, NFTNL_SET_TABLE)); + nftnl_set_set_str(nls_out, NFTNL_SET_NAME, + nftnl_set_get_str(nls_in, NFTNL_SET_NAME)); + + err = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_elem_cb, nls_out); + if (err < 0) { + nftnl_set_free(nls_out); + return NULL; + } + + return nls_out; +} + +int mnl_nft_setelem_get(struct netlink_ctx *ctx, struct nftnl_set *nls, + bool reset) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + int msg_type; + + if (reset) + msg_type = NFT_MSG_GETSETELEM_RESET; + else + msg_type = NFT_MSG_GETSETELEM; + + nlh = nftnl_nlmsg_build_hdr(buf, msg_type, + nftnl_set_get_u32(nls, NFTNL_SET_FAMILY), + NLM_F_DUMP, ctx->seqnum); + nftnl_set_elems_nlmsg_build_payload(nlh, nls); + + return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, set_elem_cb, nls); +} + +static int flowtable_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nftnl_flowtable_list *nln_list = data; + struct nftnl_flowtable *n; + + if (check_genid(nlh) < 0) + return MNL_CB_ERROR; + + n = nftnl_flowtable_alloc(); + if (n == NULL) + memory_allocation_error(); + + if (nftnl_flowtable_nlmsg_parse(nlh, n) < 0) + goto err_free; + + nftnl_flowtable_list_add_tail(n, nln_list); + return MNL_CB_OK; + +err_free: + nftnl_flowtable_free(n); + return MNL_CB_OK; +} + +struct nftnl_flowtable_list * +mnl_nft_flowtable_dump(struct netlink_ctx *ctx, int family, + const char *table, const char *ft) +{ + struct nftnl_flowtable_list *nln_list; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nftnl_flowtable *n; + int flags = NLM_F_DUMP; + struct nlmsghdr *nlh; + int ret; + + n = nftnl_flowtable_alloc(); + if (n == NULL) + memory_allocation_error(); + + if (table != NULL) + nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_TABLE, table); + if (ft) { + nftnl_flowtable_set_str(n, NFTNL_FLOWTABLE_NAME, ft); + flags = NLM_F_ACK; + } + nlh = nftnl_nlmsg_build_hdr(buf, NFT_MSG_GETFLOWTABLE, family, + flags, ctx->seqnum); + nftnl_flowtable_nlmsg_build_payload(nlh, n); + nftnl_flowtable_free(n); + + nln_list = nftnl_flowtable_list_alloc(); + if (nln_list == NULL) + memory_allocation_error(); + + ret = nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, flowtable_cb, nln_list); + if (ret < 0 && errno != ENOENT) + goto err; + + return nln_list; +err: + nftnl_flowtable_list_free(nln_list); + return NULL; +} + +static void mnl_nft_ft_devs_build(struct nlmsghdr *nlh, struct cmd *cmd) +{ + const struct expr *dev_expr = cmd->flowtable->dev_expr; + const struct nft_dev *dev_array; + struct nlattr *nest_dev; + int i, num_devs= 0; + + dev_array = nft_dev_array(dev_expr, &num_devs); + nest_dev = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK_DEVS); + for (i = 0; i < num_devs; i++) { + cmd_add_loc(cmd, nlh->nlmsg_len, dev_array[i].location); + mnl_attr_put_strz(nlh, NFTA_DEVICE_NAME, dev_array[i].ifname); + } + + mnl_attr_nest_end(nlh, nest_dev); + nft_dev_array_free(dev_array); +} + +int mnl_nft_flowtable_add(struct netlink_ctx *ctx, struct cmd *cmd, + unsigned int flags) +{ + struct nftnl_flowtable *flo; + struct nlmsghdr *nlh; + struct nlattr *nest; + int priority; + + flo = nftnl_flowtable_alloc(); + if (!flo) + memory_allocation_error(); + + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY, + cmd->handle.family); + + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FLAGS, + cmd->flowtable->flags); + + netlink_dump_flowtable(flo, ctx); + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + NFT_MSG_NEWFLOWTABLE, cmd->handle.family, + NLM_F_CREATE | flags, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name); + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.flowtable.location); + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, cmd->handle.flowtable.name); + + nftnl_flowtable_nlmsg_build_payload(nlh, flo); + + nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK); + + if (cmd->flowtable && cmd->flowtable->priority.expr) { + mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_NUM, htonl(cmd->flowtable->hook.num)); + mpz_export_data(&priority, cmd->flowtable->priority.expr->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + mnl_attr_put_u32(nlh, NFTA_FLOWTABLE_HOOK_PRIORITY, htonl(priority)); + } + + if (cmd->flowtable->dev_expr) + mnl_nft_ft_devs_build(nlh, cmd); + + mnl_attr_nest_end(nlh, nest); + + nftnl_flowtable_free(flo); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +int mnl_nft_flowtable_del(struct netlink_ctx *ctx, struct cmd *cmd) +{ + enum nf_tables_msg_types msg_type = NFT_MSG_DELFLOWTABLE; + struct nftnl_flowtable *flo; + struct nlmsghdr *nlh; + struct nlattr *nest; + + flo = nftnl_flowtable_alloc(); + if (!flo) + memory_allocation_error(); + + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_FAMILY, + cmd->handle.family); + + if (cmd->op == CMD_DESTROY) + msg_type = NFT_MSG_DESTROYFLOWTABLE; + + nlh = nftnl_nlmsg_build_hdr(nftnl_batch_buffer(ctx->batch), + msg_type, cmd->handle.family, + 0, ctx->seqnum); + + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.table.location); + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_TABLE, cmd->handle.table.name); + + if (cmd->handle.flowtable.name) { + cmd_add_loc(cmd, nlh->nlmsg_len, + &cmd->handle.flowtable.location); + mnl_attr_put_strz(nlh, NFTA_FLOWTABLE_NAME, + cmd->handle.flowtable.name); + } else if (cmd->handle.handle.id) { + cmd_add_loc(cmd, nlh->nlmsg_len, &cmd->handle.handle.location); + mnl_attr_put_u64(nlh, NFTA_FLOWTABLE_HANDLE, + htobe64(cmd->handle.handle.id)); + } + + nftnl_flowtable_nlmsg_build_payload(nlh, flo); + + if (cmd->op == CMD_DELETE && + cmd->flowtable && cmd->flowtable->dev_expr) { + nest = mnl_attr_nest_start(nlh, NFTA_FLOWTABLE_HOOK); + mnl_nft_ft_devs_build(nlh, cmd); + mnl_attr_nest_end(nlh, nest); + } + + nftnl_flowtable_free(flo); + + mnl_nft_batch_continue(ctx->batch); + + return 0; +} + +/* + * events + */ +#define NFTABLES_NLEVENT_BUFSIZ (1 << 24) + +int mnl_nft_event_listener(struct mnl_socket *nf_sock, unsigned int debug_mask, + struct output_ctx *octx, + int (*cb)(const struct nlmsghdr *nlh, void *data), + void *cb_data) +{ + /* Set netlink socket buffer size to 16 Mbytes to reduce chances of + * message loss due to ENOBUFS. + */ + unsigned int bufsiz = NFTABLES_NLEVENT_BUFSIZ; + int fd = mnl_socket_get_fd(nf_sock); + char buf[NFT_NLMSG_MAXSIZE]; + fd_set readfds; + int ret; + + ret = mnl_set_rcvbuffer(nf_sock, bufsiz); + if (ret < 0) + nft_print(octx, "# Cannot set up netlink receive socket buffer size to %u bytes, falling back to %u bytes\n", + NFTABLES_NLEVENT_BUFSIZ, bufsiz); + + while (1) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + ret = select(fd + 1, &readfds, NULL, NULL, NULL); + if (ret < 0) + return -1; + + if (FD_ISSET(fd, &readfds)) { + ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf)); + if (ret < 0) { + if (errno == ENOBUFS) { + nft_print(octx, "# ERROR: We lost some netlink events!\n"); + continue; + } + nft_print(octx, "# ERROR: %s\n", + strerror(errno)); + break; + } + } + + if (debug_mask & NFT_DEBUG_MNL) { + mnl_nlmsg_fprintf(octx->output_fp, buf, sizeof(buf), + sizeof(struct nfgenmsg)); + } + ret = mnl_cb_run(buf, ret, 0, 0, cb, cb_data); + if (ret <= 0) + break; + } + return ret; +} + +static struct basehook *basehook_alloc(void) +{ + return xzalloc(sizeof(struct basehook)); +} + +static void basehook_free(struct basehook *b) +{ + list_del(&b->list); + xfree(b->module_name); + xfree(b->hookfn); + xfree(b->chain); + xfree(b->table); + xfree(b); +} + +static void basehook_list_add_tail(struct basehook *b, struct list_head *head) +{ + struct basehook *hook; + + list_for_each_entry(hook, head, list) { + if (hook->family != b->family) + continue; + if (hook->num != b->num) + continue; + if (hook->prio < b->prio) + continue; + + list_add(&b->list, &hook->list); + return; + } + + list_add_tail(&b->list, head); +} + +static int dump_nf_attr_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFNLA_HOOK_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFNLA_HOOK_HOOKNUM: + case NFNLA_HOOK_PRIORITY: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + case NFNLA_HOOK_DEV: + if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0) + return MNL_CB_ERROR; + break; + case NFNLA_HOOK_MODULE_NAME: + case NFNLA_HOOK_FUNCTION_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + break; + case NFNLA_HOOK_CHAIN_INFO: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + default: + return MNL_CB_OK; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int dump_nf_chain_info_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFNLA_HOOK_INFO_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFNLA_HOOK_INFO_DESC: + if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0) + return MNL_CB_ERROR; + break; + case NFNLA_HOOK_INFO_TYPE: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + default: + return MNL_CB_OK; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int dump_nf_attr_chain_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFNLA_CHAIN_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFNLA_CHAIN_TABLE: + case NFNLA_CHAIN_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + return MNL_CB_ERROR; + break; + case NFNLA_CHAIN_FAMILY: + if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) + return MNL_CB_ERROR; + break; + default: + return MNL_CB_OK; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +static int dump_nf_attr_bpf_cb(const struct nlattr *attr, void *data) +{ + int type = mnl_attr_get_type(attr); + const struct nlattr **tb = data; + + if (mnl_attr_type_valid(attr, NFNLA_HOOK_BPF_MAX) < 0) + return MNL_CB_OK; + + switch(type) { + case NFNLA_HOOK_BPF_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + return MNL_CB_ERROR; + break; + default: + return MNL_CB_OK; + } + + tb[type] = attr; + return MNL_CB_OK; +} + +struct dump_nf_hook_data { + struct list_head *hook_list; + int family; +}; + +static int dump_nf_hooks(const struct nlmsghdr *nlh, void *_data) +{ + const struct nfgenmsg *nfg = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[NFNLA_HOOK_MAX + 1] = {}; + struct dump_nf_hook_data *data = _data; + struct basehook *hook; + + /* NB: Don't check the nft generation ID, this is not + * an nftables subsystem. + */ + if (mnl_attr_parse(nlh, sizeof(*nfg), dump_nf_attr_cb, tb) < 0) + return -1; + + if (!tb[NFNLA_HOOK_PRIORITY]) + netlink_abi_error(); + + hook = basehook_alloc(); + hook->prio = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_PRIORITY])); + + if (tb[NFNLA_HOOK_FUNCTION_NAME]) + hook->hookfn = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_FUNCTION_NAME])); + + if (tb[NFNLA_HOOK_MODULE_NAME]) + hook->module_name = xstrdup(mnl_attr_get_str(tb[NFNLA_HOOK_MODULE_NAME])); + + if (tb[NFNLA_HOOK_CHAIN_INFO]) { + struct nlattr *nested[NFNLA_HOOK_INFO_MAX + 1] = {}; + uint32_t type; + + if (mnl_attr_parse_nested(tb[NFNLA_HOOK_CHAIN_INFO], + dump_nf_chain_info_cb, nested) < 0) { + basehook_free(hook); + return -1; + } + + type = ntohl(mnl_attr_get_u32(nested[NFNLA_HOOK_INFO_TYPE])); + if (type == NFNL_HOOK_TYPE_NFTABLES) { + struct nlattr *info[NFNLA_CHAIN_MAX + 1] = {}; + const char *tablename, *chainname; + + if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], + dump_nf_attr_chain_cb, + info) < 0) { + basehook_free(hook); + return -1; + } + + tablename = mnl_attr_get_str(info[NFNLA_CHAIN_TABLE]); + chainname = mnl_attr_get_str(info[NFNLA_CHAIN_NAME]); + if (tablename && chainname) { + hook->table = xstrdup(tablename); + hook->chain = xstrdup(chainname); + } + hook->chain_family = mnl_attr_get_u8(info[NFNLA_CHAIN_FAMILY]); + } else if (type == NFNL_HOOK_TYPE_BPF) { + struct nlattr *info[NFNLA_HOOK_BPF_MAX + 1] = {}; + + if (mnl_attr_parse_nested(nested[NFNLA_HOOK_INFO_DESC], + dump_nf_attr_bpf_cb, info) < 0) { + basehook_free(hook); + return -1; + } + + if (info[NFNLA_HOOK_BPF_ID]) { + char tmpbuf[16]; + + snprintf(tmpbuf, sizeof(tmpbuf), "id %u", + ntohl(mnl_attr_get_u32(info[NFNLA_HOOK_BPF_ID]))); + + hook->chain = xstrdup(tmpbuf); + } + } + } + if (tb[NFNLA_HOOK_HOOKNUM]) + hook->num = ntohl(mnl_attr_get_u32(tb[NFNLA_HOOK_HOOKNUM])); + + hook->family = nfg->nfgen_family; + + /* Netdev hooks potentially interfer with this family datapath. */ + if (hook->family == NFPROTO_NETDEV) { + switch (data->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + case NFPROTO_BRIDGE: + hook->family = data->family; + hook->num = NF_INET_INGRESS; + break; + case NFPROTO_ARP: + if (hook->chain_family == NFPROTO_NETDEV) { + hook->family = data->family; + hook->num = __NF_ARP_INGRESS; + } + break; + } + } + + basehook_list_add_tail(hook, data->hook_list); + + return MNL_CB_OK; +} + +static struct nlmsghdr *nf_hook_dump_request(char *buf, uint8_t family, uint32_t seq) +{ + struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf); + struct nfgenmsg *nfg; + + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlh->nlmsg_type = NFNL_SUBSYS_HOOK << 8; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = family; + nfg->version = NFNETLINK_V0; + + return nlh; +} + +static int __mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, uint8_t query_family, + uint8_t family, uint8_t hooknum, + const char *devname, + struct list_head *hook_list) +{ + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct dump_nf_hook_data data = { + .hook_list = hook_list, + .family = query_family, + }; + struct nlmsghdr *nlh; + + nlh = nf_hook_dump_request(buf, family, ctx->seqnum); + if (devname) + mnl_attr_put_strz(nlh, NFNLA_HOOK_DEV, devname); + + mnl_attr_put_u32(nlh, NFNLA_HOOK_HOOKNUM, htonl(hooknum)); + + return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, dump_nf_hooks, &data); +} + +static void print_hooks(struct netlink_ctx *ctx, int family, struct list_head *hook_list) +{ + struct basehook *hook, *tmp, *prev = NULL; + bool same, family_in_use = false; + int prio; + FILE *fp; + + fp = ctx->nft->output.output_fp; + + list_for_each_entry_safe(hook, tmp, hook_list, list) { + if (hook->family == family) { + family_in_use = true; + break; + } + } + + if (!family_in_use) + return; + + fprintf(fp, "family %s {\n", family2str(family)); + + list_for_each_entry_safe(hook, tmp, hook_list, list) { + if (hook->family != family) + continue; + + if (prev) { + if (prev->num == hook->num) { + fprintf(fp, "\n"); + same = true; + } else { + same = false; + fprintf(fp, "\n\t}\n"); + } + } else { + same = false; + } + prev = hook; + + if (!same) { + fprintf(fp, "\thook %s {\n", + hooknum2str(family, hook->num)); + } + + prio = hook->prio; + if (prio < 0) + fprintf(fp, "\t\t%011d", prio); /* outputs a '-' sign */ + else if (prio == 0) + fprintf(fp, "\t\t %010u", prio); + else + fprintf(fp, "\t\t+%010u", prio); + + if (hook->table && hook->chain) + fprintf(fp, " chain %s %s %s", family2str(hook->chain_family), hook->table, hook->chain); + else if (hook->hookfn && hook->chain) + fprintf(fp, " %s %s", hook->hookfn, hook->chain); + else if (hook->hookfn) { + fprintf(fp, " %s", hook->hookfn); + } + if (hook->module_name) + fprintf(fp, " [%s]", hook->module_name); + } + + fprintf(fp, "\n\t}\n"); + fprintf(fp, "}\n"); +} + +#define HOOK_FAMILY_MAX 5 + +static uint8_t hook_family[HOOK_FAMILY_MAX] = { + NFPROTO_IPV4, + NFPROTO_IPV6, + NFPROTO_BRIDGE, + NFPROTO_ARP, +}; + +static int mnl_nft_dump_nf(struct netlink_ctx *ctx, int family, int hook, + const char *devname, struct list_head *hook_list, + int *ret) +{ + int i, err; + + /* show ingress in first place in hook listing. */ + err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list); + if (err < 0) + *ret = err; + + for (i = 0; i <= NF_INET_POST_ROUTING; i++) { + err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list); + if (err < 0) + *ret = err; + } + + return err; +} + +static int mnl_nft_dump_nf_arp(struct netlink_ctx *ctx, int family, int hook, + const char *devname, struct list_head *hook_list, + int *ret) +{ + int err; + + /* show ingress in first place in hook listing. */ + err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list); + if (err < 0) + *ret = err; + + err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_IN, devname, hook_list); + if (err < 0) + *ret = err; + err = __mnl_nft_dump_nf_hooks(ctx, family, family, NF_ARP_OUT, devname, hook_list); + if (err < 0) + *ret = err; + + return err; +} + +static int mnl_nft_dump_nf_netdev(struct netlink_ctx *ctx, int family, int hook, + const char *devname, struct list_head *hook_list, + int *ret) +{ + int err; + + err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list); + if (err < 0) + *ret = err; + + return err; +} + +static int mnl_nft_dump_nf_decnet(struct netlink_ctx *ctx, int family, int hook, + const char *devname, struct list_head *hook_list, + int *ret) +{ + int i, err; + + /* show ingress in first place in hook listing. */ + err = __mnl_nft_dump_nf_hooks(ctx, family, NFPROTO_NETDEV, NF_NETDEV_INGRESS, devname, hook_list); + if (err < 0) + *ret = err; + +#define NF_DN_NUMHOOKS 7 + for (i = 0; i < NF_DN_NUMHOOKS; i++) { + err = __mnl_nft_dump_nf_hooks(ctx, family, family, i, devname, hook_list); + if (err < 0) { + *ret = err; + return err; + } + } + + return err; +} + +static void release_hook_list(struct list_head *hook_list) +{ + struct basehook *hook, *next; + + list_for_each_entry_safe(hook, next, hook_list, list) + basehook_free(hook); +} + +int mnl_nft_dump_nf_hooks(struct netlink_ctx *ctx, int family, int hook, const char *devname) +{ + LIST_HEAD(hook_list); + unsigned int i; + int ret; + + errno = 0; + ret = 0; + + switch (family) { + case NFPROTO_UNSPEC: + mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret); + mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret); + mnl_nft_dump_nf(ctx, NFPROTO_BRIDGE, hook, devname, &hook_list, &ret); + mnl_nft_dump_nf_decnet(ctx, NFPROTO_DECNET, hook, devname, &hook_list, &ret); + break; + case NFPROTO_INET: + mnl_nft_dump_nf(ctx, NFPROTO_IPV4, hook, devname, &hook_list, &ret); + mnl_nft_dump_nf(ctx, NFPROTO_IPV6, hook, devname, &hook_list, &ret); + break; + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_BRIDGE: + mnl_nft_dump_nf(ctx, family, hook, devname, &hook_list, &ret); + break; + case NFPROTO_ARP: + mnl_nft_dump_nf_arp(ctx, family, hook, devname, &hook_list, &ret); + break; + case NFPROTO_NETDEV: + mnl_nft_dump_nf_netdev(ctx, family, hook, devname, &hook_list, &ret); + break; + case NFPROTO_DECNET: + mnl_nft_dump_nf_decnet(ctx, family, hook, devname, &hook_list, &ret); + break; + } + + switch (family) { + case NFPROTO_UNSPEC: + for (i = 0; i < HOOK_FAMILY_MAX; i++) + print_hooks(ctx, hook_family[i], &hook_list); + break; + case NFPROTO_INET: + print_hooks(ctx, NFPROTO_IPV4, &hook_list); + print_hooks(ctx, NFPROTO_IPV6, &hook_list); + break; + default: + print_hooks(ctx, family, &hook_list); + break; + } + + release_hook_list(&hook_list); + ret = 0; + + return ret; +} diff --git a/src/monitor.c b/src/monitor.c new file mode 100644 index 0000000..82762a0 --- /dev/null +++ b/src/monitor.c @@ -0,0 +1,1014 @@ +/* + * Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <fcntl.h> +#include <errno.h> +#include <libmnl/libmnl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#include <libnftnl/table.h> +#include <libnftnl/trace.h> +#include <libnftnl/chain.h> +#include <libnftnl/expr.h> +#include <libnftnl/object.h> +#include <libnftnl/set.h> +#include <libnftnl/flowtable.h> +#include <libnftnl/udata.h> +#include <libnftnl/ruleset.h> +#include <libnftnl/common.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter.h> + +#include <nftables.h> +#include <netlink.h> +#include <mnl.h> +#include <expression.h> +#include <statement.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> +#include <iface.h> +#include <json.h> + +enum { + NFT_OF_EVENT_ADD, + NFT_OF_EVENT_INSERT, + NFT_OF_EVENT_DEL, + NFT_OF_EVENT_CREATE, +}; + +#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) + +struct nftnl_table *netlink_table_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + + nlt = nftnl_table_alloc(); + if (nlt == NULL) + memory_allocation_error(); + if (nftnl_table_nlmsg_parse(nlh, nlt) < 0) + netlink_abi_error(); + + return nlt; +} + +struct nftnl_chain *netlink_chain_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_chain *nlc; + + nlc = nftnl_chain_alloc(); + if (nlc == NULL) + memory_allocation_error(); + if (nftnl_chain_nlmsg_parse(nlh, nlc) < 0) + netlink_abi_error(); + + return nlc; +} + +struct nftnl_set *netlink_set_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_set *nls; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + if (nftnl_set_nlmsg_parse(nlh, nls) < 0) + netlink_abi_error(); + + return nls; +} + +static struct nftnl_set *netlink_setelem_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_set *nls; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + if (nftnl_set_elems_nlmsg_parse(nlh, nls) < 0) + netlink_abi_error(); + + return nls; +} + +struct nftnl_rule *netlink_rule_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_rule *nlr; + + nlr = nftnl_rule_alloc(); + if (nlr == NULL) + memory_allocation_error(); + if (nftnl_rule_nlmsg_parse(nlh, nlr) < 0) + netlink_abi_error(); + + return nlr; +} + +struct nftnl_obj *netlink_obj_alloc(const struct nlmsghdr *nlh) +{ + struct nftnl_obj *nlo; + + nlo = nftnl_obj_alloc(); + if (nlo == NULL) + memory_allocation_error(); + if (nftnl_obj_nlmsg_parse(nlh, nlo) < 0) + netlink_abi_error(); + + return nlo; +} + +static uint32_t netlink_msg2nftnl_of(uint32_t type, uint16_t flags) +{ + switch (type) { + case NFT_MSG_NEWRULE: + if (flags & NLM_F_APPEND) + return NFT_OF_EVENT_ADD; + else + return NFT_OF_EVENT_INSERT; + case NFT_MSG_NEWTABLE: + case NFT_MSG_NEWCHAIN: + case NFT_MSG_NEWSET: + case NFT_MSG_NEWSETELEM: + case NFT_MSG_NEWOBJ: + case NFT_MSG_NEWFLOWTABLE: + if (flags & NLM_F_EXCL) + return NFT_OF_EVENT_CREATE; + else + return NFT_OF_EVENT_ADD; + case NFT_MSG_DELTABLE: + case NFT_MSG_DELCHAIN: + case NFT_MSG_DELSET: + case NFT_MSG_DELSETELEM: + case NFT_MSG_DELRULE: + case NFT_MSG_DELOBJ: + case NFT_MSG_DELFLOWTABLE: + return NFTNL_OF_EVENT_DEL; + } + + return 0; +} + +static const char *nftnl_of2cmd(uint32_t of) +{ + switch (of) { + case NFT_OF_EVENT_ADD: + return "add"; + case NFT_OF_EVENT_CREATE: + return "create"; + case NFT_OF_EVENT_INSERT: + return "insert"; + case NFT_OF_EVENT_DEL: + return "delete"; + default: + return "???"; + } +} + +static const char *netlink_msg2cmd(uint32_t type, uint16_t flags) +{ + return nftnl_of2cmd(netlink_msg2nftnl_of(type, flags)); +} + +static void nlr_for_each_set(struct nftnl_rule *nlr, + void (*cb)(struct set *s, void *data), + void *data, struct nft_cache *cache) +{ + struct nftnl_expr_iter *nlrei; + struct nftnl_expr *nlre; + const char *set_name, *table; + const char *name; + struct set *s; + uint32_t family; + + nlrei = nftnl_expr_iter_create(nlr); + if (nlrei == NULL) + memory_allocation_error(); + + family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); + + nlre = nftnl_expr_iter_next(nlrei); + while (nlre != NULL) { + name = nftnl_expr_get_str(nlre, NFTNL_EXPR_NAME); + if (strcmp(name, "lookup") != 0) + goto next; + + set_name = nftnl_expr_get_str(nlre, NFTNL_EXPR_LOOKUP_SET); + s = set_lookup_global(family, table, set_name, cache); + if (s == NULL) + goto next; + + cb(s, data); +next: + nlre = nftnl_expr_iter_next(nlrei); + } + nftnl_expr_iter_destroy(nlrei); +} + +static int netlink_events_table_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_table *nlt; + struct table *t; + const char *cmd; + + nlt = netlink_table_alloc(nlh); + t = netlink_delinearize_table(monh->ctx, nlt); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s table ", cmd); + + nft_mon_print(monh, "%s %s", family2str(t->handle.family), + t->handle.table.name); + + if (t->flags & TABLE_F_DORMANT) + nft_mon_print(monh, " { flags dormant; }"); + + if (nft_output_handle(&monh->ctx->nft->output)) + nft_mon_print(monh, " # handle %" PRIu64 "", + t->handle.handle.id); + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + monitor_print_table_json(monh, cmd, t); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + table_free(t); + nftnl_table_free(nlt); + return MNL_CB_OK; +} + +static int netlink_events_chain_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_chain *nlc; + struct chain *c; + const char *cmd; + + nlc = netlink_chain_alloc(nlh); + c = netlink_delinearize_chain(monh->ctx, nlc); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s ", cmd); + + switch (type) { + case NFT_MSG_NEWCHAIN: + chain_print_plain(c, &monh->ctx->nft->output); + break; + case NFT_MSG_DELCHAIN: + if (c->dev_array_len > 0) + chain_print_plain(c, &monh->ctx->nft->output); + else + nft_mon_print(monh, "chain %s %s %s", + family2str(c->handle.family), + c->handle.table.name, + c->handle.chain.name); + break; + } + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + monitor_print_chain_json(monh, cmd, c); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + chain_free(c); + nftnl_chain_free(nlc); + return MNL_CB_OK; +} + +static int netlink_events_set_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_set *nls; + const char *family, *cmd; + struct set *set; + uint32_t flags; + + nls = netlink_set_alloc(nlh); + flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (set_is_anonymous(flags)) + goto out; + + set = netlink_delinearize_set(monh->ctx, nls); + if (set == NULL) { + nftnl_set_free(nls); + return MNL_CB_ERROR; + } + family = family2str(set->handle.family); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s ", cmd); + + switch (type) { + case NFT_MSG_NEWSET: + set_print_plain(set, &monh->ctx->nft->output); + break; + case NFT_MSG_DELSET: + nft_mon_print(monh, "set %s %s %s", family, + set->handle.table.name, + set->handle.set.name); + break; + } + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + monitor_print_set_json(monh, cmd, set); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + set_free(set); +out: + nftnl_set_free(nls); + return MNL_CB_OK; +} + +/* returns true if the event should be ignored (i.e. null element) */ +static bool netlink_event_ignore_range_event(struct nftnl_set_elem *nlse) +{ + uint32_t flags = 0; + + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS)) + flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS); + if (!(flags & NFT_SET_ELEM_INTERVAL_END)) + return false; + + if (nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_KEY) != 0) + return false; + + return true; +} + +static bool set_elem_is_open_interval(struct expr *elem) +{ + switch (elem->etype) { + case EXPR_SET_ELEM: + return elem->elem_flags & NFTNL_SET_ELEM_F_INTERVAL_OPEN; + case EXPR_MAPPING: + return set_elem_is_open_interval(elem->left); + default: + return false; + } +} + +/* returns true if the we cached the range element */ +static bool netlink_event_range_cache(struct set *cached_set, + struct set *dummyset) +{ + struct expr *elem; + + /* not an interval ? */ + if (!(cached_set->flags & NFT_SET_INTERVAL)) + return false; + + /* if cache exists, dummyset must contain the other end of the range */ + if (cached_set->rg_cache) { + compound_expr_add(dummyset->init, cached_set->rg_cache); + cached_set->rg_cache = NULL; + goto out_decompose; + } + + /* don't cache half-open range elements */ + elem = list_entry(dummyset->init->expressions.prev, struct expr, list); + if (!set_elem_is_open_interval(elem)) { + cached_set->rg_cache = expr_clone(elem); + return true; + } + +out_decompose: + interval_map_decompose(dummyset->init); + return false; +} + +static int netlink_events_setelem_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_set_elems_iter *nlsei; + struct nftnl_set_elem *nlse; + struct nftnl_set *nls; + struct set *dummyset; + struct set *set; + const char *setname, *table, *cmd; + uint32_t family; + + nls = netlink_setelem_alloc(nlh); + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache); + if (set == NULL) { + fprintf(stderr, "W: Received event for an unknown set.\n"); + goto out; + } + + if (set_is_anonymous(set->flags)) + goto out; + + /* we want to 'delinearize' the set_elem, but don't + * modify the original cached set. This path is only + * used by named sets, so use a dummy set. + */ + dummyset = set_alloc(monh->loc); + handle_merge(&dummyset->handle, &set->handle); + dummyset->key = expr_clone(set->key); + if (set->data) + dummyset->data = expr_clone(set->data); + dummyset->flags = set->flags; + dummyset->init = set_expr_alloc(monh->loc, set); + + nlsei = nftnl_set_elems_iter_create(nls); + if (nlsei == NULL) + memory_allocation_error(); + + nlse = nftnl_set_elems_iter_next(nlsei); + while (nlse != NULL) { + if (netlink_event_ignore_range_event(nlse)) { + set_free(dummyset); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + if (netlink_delinearize_setelem(nlse, dummyset, + &monh->ctx->nft->cache) < 0) { + set_free(dummyset); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + nlse = nftnl_set_elems_iter_next(nlsei); + } + nftnl_set_elems_iter_destroy(nlsei); + + if (netlink_event_range_cache(set, dummyset)) { + set_free(dummyset); + goto out; + } + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s element %s %s %s ", + cmd, family2str(family), table, setname); + expr_print(dummyset->init, &monh->ctx->nft->output); + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + dummyset->handle.family = family; + dummyset->handle.set.name = setname; + dummyset->handle.table.name = table; + monitor_print_element_json(monh, cmd, dummyset); + /* prevent set_free() from trying to free those */ + dummyset->handle.set.name = NULL; + dummyset->handle.table.name = NULL; + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + set_free(dummyset); +out: + nftnl_set_free(nls); + return MNL_CB_OK; +} + +static int netlink_events_obj_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + const char *family, *cmd; + struct nftnl_obj *nlo; + struct obj *obj; + + nlo = netlink_obj_alloc(nlh); + + obj = netlink_delinearize_obj(monh->ctx, nlo); + if (!obj) { + nftnl_obj_free(nlo); + return MNL_CB_ERROR; + } + family = family2str(obj->handle.family); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + nft_mon_print(monh, "%s ", cmd); + + switch (type) { + case NFT_MSG_NEWOBJ: + obj_print_plain(obj, &monh->ctx->nft->output); + break; + case NFT_MSG_DELOBJ: + nft_mon_print(monh, "%s %s %s %s", + obj_type_name(obj->type), + family, + obj->handle.table.name, + obj->handle.obj.name); + break; + } + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + monitor_print_obj_json(monh, cmd, obj); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + obj_free(obj); + nftnl_obj_free(nlo); + return MNL_CB_OK; +} + +static void rule_map_decompose_cb(struct set *s, void *data) +{ + if (!set_is_anonymous(s->flags)) + return; + + if (set_is_non_concat_range(s)) + interval_map_decompose(s->init); + else if (set_is_interval(s->flags)) + concat_range_aggregate(s->init); +} + +static int netlink_events_rule_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + const char *family, *cmd; + struct nftnl_rule *nlr; + struct rule *r; + + nlr = netlink_rule_alloc(nlh); + r = netlink_delinearize_rule(monh->ctx, nlr); + if (!r) { + fprintf(stderr, "W: Received event for an unknown table.\n"); + goto out_free_nlr; + } + nlr_for_each_set(nlr, rule_map_decompose_cb, NULL, + &monh->ctx->nft->cache); + cmd = netlink_msg2cmd(type, nlh->nlmsg_flags); + + switch (monh->format) { + case NFTNL_OUTPUT_DEFAULT: + family = family2str(r->handle.family); + + nft_mon_print(monh, "%s rule %s %s %s ", + cmd, + family, + r->handle.table.name, + r->handle.chain.name); + if (r->handle.position.id) { + nft_mon_print(monh, "handle %" PRIu64" ", + r->handle.position.id); + } + switch (type) { + case NFT_MSG_NEWRULE: + rule_print(r, &monh->ctx->nft->output); + + break; + case NFT_MSG_DELRULE: + nft_mon_print(monh, "handle %" PRIu64, + r->handle.handle.id); + break; + } + nft_mon_print(monh, "\n"); + break; + case NFTNL_OUTPUT_JSON: + monitor_print_rule_json(monh, cmd, r); + if (!nft_output_echo(&monh->ctx->nft->output)) + nft_mon_print(monh, "\n"); + break; + } + rule_free(r); +out_free_nlr: + nftnl_rule_free(nlr); + return MNL_CB_OK; +} + +static void netlink_events_cache_addtable(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + struct table *t; + + nlt = netlink_table_alloc(nlh); + t = netlink_delinearize_table(monh->ctx, nlt); + nftnl_table_free(nlt); + + table_cache_add(t, &monh->ctx->nft->cache); +} + +static void netlink_events_cache_deltable(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + struct table *t; + struct handle h; + + nlt = netlink_table_alloc(nlh); + h.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); + h.table.name = nftnl_table_get_str(nlt, NFTNL_TABLE_NAME); + + t = table_cache_find(&monh->ctx->nft->cache.table_cache, + h.table.name, h.family); + if (t == NULL) + goto out; + + table_cache_del(t); + table_free(t); +out: + nftnl_table_free(nlt); +} + +static void netlink_events_cache_addset(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct netlink_ctx set_tmpctx; + struct nftnl_set *nls; + struct table *t; + struct set *s; + LIST_HEAD(msgs); + + memset(&set_tmpctx, 0, sizeof(set_tmpctx)); + init_list_head(&set_tmpctx.list); + init_list_head(&msgs); + set_tmpctx.nft = monh->ctx->nft; + set_tmpctx.msgs = &msgs; + + nls = netlink_set_alloc(nlh); + s = netlink_delinearize_set(&set_tmpctx, nls); + if (s == NULL) + goto out; + s->init = set_expr_alloc(monh->loc, s); + + t = table_cache_find(&monh->ctx->nft->cache.table_cache, + s->handle.table.name, s->handle.family); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache set: table not found.\n"); + set_free(s); + goto out; + } + + if (nft_output_echo(&monh->ctx->nft->output) && + !set_is_anonymous(s->flags)) { + set_free(s); + goto out; + } + + set_cache_add(s, t); +out: + nftnl_set_free(nls); +} + +static void netlink_events_cache_addsetelem(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_set_elems_iter *nlsei; + struct nftnl_set_elem *nlse; + struct nftnl_set *nls; + struct set *set; + const char *table, *setname; + uint32_t family; + + nls = netlink_setelem_alloc(nlh); + family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + table = nftnl_set_get_str(nls, NFTNL_SET_TABLE); + setname = nftnl_set_get_str(nls, NFTNL_SET_NAME); + + set = set_lookup_global(family, table, setname, &monh->ctx->nft->cache); + if (set == NULL) { + fprintf(stderr, + "W: Unable to cache set_elem. Set not found.\n"); + goto out; + } + + if (nft_output_echo(&monh->ctx->nft->output) && + !set_is_anonymous(set->flags)) + goto out; + + nlsei = nftnl_set_elems_iter_create(nls); + if (nlsei == NULL) + memory_allocation_error(); + + nlse = nftnl_set_elems_iter_next(nlsei); + while (nlse != NULL) { + if (netlink_delinearize_setelem(nlse, set, + &monh->ctx->nft->cache) < 0) { + fprintf(stderr, + "W: Unable to cache set_elem. " + "Delinearize failed.\n"); + nftnl_set_elems_iter_destroy(nlsei); + goto out; + } + nlse = nftnl_set_elems_iter_next(nlsei); + } + nftnl_set_elems_iter_destroy(nlsei); +out: + nftnl_set_free(nls); +} + +static void netlink_events_cache_delset_cb(struct set *s, + void *data) +{ + set_cache_del(s); + set_free(s); +} + +static void netlink_events_cache_delsets(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_rule *nlr = netlink_rule_alloc(nlh); + + nlr_for_each_set(nlr, netlink_events_cache_delset_cb, NULL, + &monh->ctx->nft->cache); + nftnl_rule_free(nlr); +} + +static void netlink_events_cache_addobj(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct netlink_ctx obj_tmpctx; + struct nftnl_obj *nlo; + struct table *t; + struct obj *obj; + LIST_HEAD(msgs); + + memset(&obj_tmpctx, 0, sizeof(obj_tmpctx)); + init_list_head(&obj_tmpctx.list); + init_list_head(&msgs); + obj_tmpctx.msgs = &msgs; + + nlo = netlink_obj_alloc(nlh); + obj = netlink_delinearize_obj(&obj_tmpctx, nlo); + if (obj == NULL) + goto out; + + t = table_cache_find(&monh->ctx->nft->cache.table_cache, + obj->handle.table.name, obj->handle.family); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + obj_free(obj); + goto out; + } + + obj_cache_add(obj, t); +out: + nftnl_obj_free(nlo); +} + +static void netlink_events_cache_delobj(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh) +{ + struct nftnl_obj *nlo; + const char *name; + struct obj *obj; + struct handle h; + struct table *t; + uint32_t type; + + nlo = netlink_obj_alloc(nlh); + h.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); + h.table.name = nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE); + + name = nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME); + type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE); + h.handle.id = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); + + t = table_cache_find(&monh->ctx->nft->cache.table_cache, + h.table.name, h.family); + if (t == NULL) { + fprintf(stderr, "W: Unable to cache object: table not found.\n"); + goto out; + } + + obj = obj_cache_find(t, name, type); + if (obj == NULL) { + fprintf(stderr, "W: Unable to find object in cache\n"); + goto out; + } + + obj_cache_del(obj); + obj_free(obj); +out: + nftnl_obj_free(nlo); +} + +static void netlink_events_cache_update(struct netlink_mon_handler *monh, + const struct nlmsghdr *nlh, int type) +{ + if (nft_output_echo(&monh->ctx->nft->output) && + type != NFT_MSG_NEWSET && type != NFT_MSG_NEWSETELEM) + return; + + switch (type) { + case NFT_MSG_NEWTABLE: + netlink_events_cache_addtable(monh, nlh); + break; + case NFT_MSG_DELTABLE: + netlink_events_cache_deltable(monh, nlh); + break; + case NFT_MSG_NEWSET: + netlink_events_cache_addset(monh, nlh); + break; + case NFT_MSG_NEWSETELEM: + netlink_events_cache_addsetelem(monh, nlh); + break; + case NFT_MSG_DELRULE: + /* there are no notification for anon-set deletion */ + netlink_events_cache_delsets(monh, nlh); + break; + case NFT_MSG_NEWOBJ: + netlink_events_cache_addobj(monh, nlh); + break; + case NFT_MSG_DELOBJ: + netlink_events_cache_delobj(monh, nlh); + break; + } +} + +/* only those which could be useful listening to events */ +static const char *const nftnl_msg_types[NFT_MSG_MAX] = { + [NFT_MSG_NEWTABLE] = "NFT_MSG_NEWTABLE", + [NFT_MSG_DELTABLE] = "NFT_MSG_DELTABLE", + [NFT_MSG_NEWCHAIN] = "NFT_MSG_NEWCHAIN", + [NFT_MSG_DELCHAIN] = "NFT_MSG_DELCHAIN", + [NFT_MSG_NEWSET] = "NFT_MSG_NEWSET", + [NFT_MSG_DELSET] = "NFT_MSG_DELSET", + [NFT_MSG_NEWSETELEM] = "NFT_MSG_NEWSETELEM", + [NFT_MSG_DELSETELEM] = "NFT_MSG_DELSETELEM", + [NFT_MSG_NEWRULE] = "NFT_MSG_NEWRULE", + [NFT_MSG_DELRULE] = "NFT_MSG_DELRULE", + [NFT_MSG_TRACE] = "NFT_MSG_TRACE", + [NFT_MSG_NEWGEN] = "NFT_MSG_NEWGEN", + [NFT_MSG_NEWOBJ] = "NFT_MSG_NEWOBJ", + [NFT_MSG_DELOBJ] = "NFT_MSG_DELOBJ", +}; + +static const char *nftnl_msgtype2str(uint16_t type) +{ + if (type >= NFT_MSG_MAX || !nftnl_msg_types[type]) + return "unknown"; + + return nftnl_msg_types[type]; +} + +static void netlink_events_debug(uint16_t type, unsigned int debug_mask) +{ + if (!(debug_mask & NFT_DEBUG_NETLINK)) + return; + + printf("netlink event: %s\n", nftnl_msgtype2str(type)); +} + +static int netlink_events_newgen_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + const struct nlattr *attr; + char name[256] = ""; + int genid = -1, pid = -1; + + if (monh->format != NFTNL_OUTPUT_DEFAULT) + return MNL_CB_OK; + + mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) { + switch (mnl_attr_get_type(attr)) { + case NFTA_GEN_ID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + genid = ntohl(mnl_attr_get_u32(attr)); + break; + case NFTA_GEN_PROC_NAME: + if (mnl_attr_validate(attr, MNL_TYPE_NUL_STRING) < 0) + break; + snprintf(name, sizeof(name), "%s", mnl_attr_get_str(attr)); + break; + case NFTA_GEN_PROC_PID: + if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) + break; + pid = ntohl(mnl_attr_get_u32(attr)); + break; + } + } + if (genid >= 0) { + nft_mon_print(monh, "# new generation %d", genid); + if (pid >= 0) + nft_mon_print(monh, " by process %d (%s)", pid, name); + + nft_mon_print(monh, "\n"); + } + + return MNL_CB_OK; +} + +static int netlink_events_cb(const struct nlmsghdr *nlh, void *data) +{ + int ret = MNL_CB_OK; + uint16_t type = NFNL_MSG_TYPE(nlh->nlmsg_type); + struct netlink_mon_handler *monh = (struct netlink_mon_handler *)data; + + netlink_events_debug(type, monh->ctx->nft->debug_mask); + netlink_events_cache_update(monh, nlh, type); + + if (!(monh->monitor_flags & (1 << type))) + return ret; + + switch (type) { + case NFT_MSG_NEWTABLE: + case NFT_MSG_DELTABLE: + ret = netlink_events_table_cb(nlh, type, monh); + break; + case NFT_MSG_NEWCHAIN: + case NFT_MSG_DELCHAIN: + ret = netlink_events_chain_cb(nlh, type, monh); + break; + case NFT_MSG_NEWSET: + case NFT_MSG_DELSET: /* nft {add|delete} set */ + ret = netlink_events_set_cb(nlh, type, monh); + break; + case NFT_MSG_NEWSETELEM: + case NFT_MSG_DELSETELEM: /* nft {add|delete} element */ + ret = netlink_events_setelem_cb(nlh, type, monh); + break; + case NFT_MSG_NEWRULE: + case NFT_MSG_DELRULE: + ret = netlink_events_rule_cb(nlh, type, monh); + break; + case NFT_MSG_TRACE: + ret = netlink_events_trace_cb(nlh, type, monh); + break; + case NFT_MSG_NEWOBJ: + case NFT_MSG_DELOBJ: + ret = netlink_events_obj_cb(nlh, type, monh); + break; + case NFT_MSG_NEWGEN: + ret = netlink_events_newgen_cb(nlh, type, monh); + break; + } + + return ret; +} + +int netlink_echo_callback(const struct nlmsghdr *nlh, void *data) +{ + struct netlink_cb_data *nl_cb_data = data; + struct netlink_ctx *ctx = nl_cb_data->nl_ctx; + struct nft_ctx *nft = ctx->nft; + + struct netlink_mon_handler echo_monh = { + .format = NFTNL_OUTPUT_DEFAULT, + .ctx = ctx, + .loc = &netlink_location, + .monitor_flags = 0xffffffff, + }; + + if (!nft_output_echo(&echo_monh.ctx->nft->output)) + return MNL_CB_OK; + + if (nft_output_json(&nft->output)) { + if (nft->json_root) + return json_events_cb(nlh, &echo_monh); + if (!nft->json_echo) + json_alloc_echo(nft); + echo_monh.format = NFTNL_OUTPUT_JSON; + } + + return netlink_events_cb(nlh, &echo_monh); +} + +int netlink_monitor(struct netlink_mon_handler *monhandler, + struct mnl_socket *nf_sock) +{ + int group; + + if (monhandler->monitor_flags & (1 << NFT_MSG_TRACE)) { + group = NFNLGRP_NFTRACE; + if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(int)) < 0) + return -1; + } + if (monhandler->monitor_flags & ~(1 << NFT_MSG_TRACE)) { + group = NFNLGRP_NFTABLES; + if (mnl_socket_setsockopt(nf_sock, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(int)) < 0) + return -1; + } + + return mnl_nft_event_listener(nf_sock, monhandler->ctx->nft->debug_mask, + &monhandler->ctx->nft->output, + netlink_events_cb, monhandler); +} diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..120a8ba --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,2240 @@ +/* + * Copyright (c) 2008-2012 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <nft.h> + +#include <errno.h> +#include <libmnl/libmnl.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <inttypes.h> + +#include <libnftnl/table.h> +#include <libnftnl/trace.h> +#include <libnftnl/chain.h> +#include <libnftnl/expr.h> +#include <libnftnl/object.h> +#include <libnftnl/set.h> +#include <libnftnl/flowtable.h> +#include <libnftnl/udata.h> +#include <libnftnl/ruleset.h> +#include <libnftnl/common.h> +#include <libnftnl/udata.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter.h> + +#include <nftables.h> +#include <parser.h> +#include <netlink.h> +#include <mnl.h> +#include <expression.h> +#include <statement.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> +#include <iface.h> + +#define nft_mon_print(monh, ...) nft_print(&monh->ctx->nft->output, __VA_ARGS__) + +const struct input_descriptor indesc_netlink = { + .name = "netlink", + .type = INDESC_NETLINK, +}; + +const struct location netlink_location = { + .indesc = &indesc_netlink, +}; + +void __noreturn __netlink_abi_error(const char *file, int line, + const char *reason) +{ + fprintf(stderr, "E: Contact urgently your Linux kernel vendor. " + "Netlink ABI is broken: %s:%d %s\n", file, line, reason); + abort(); +} + +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 = &netlink_location; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, loc, fmt, ap); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} + +void __noreturn __netlink_init_error(const char *filename, int line, + const char *reason) +{ + fprintf(stderr, "%s:%d: Unable to initialize Netlink socket: %s\n", + filename, line, reason); + exit(NFT_EXIT_NONL); +} + +struct nftnl_expr *alloc_nft_expr(const char *name) +{ + struct nftnl_expr *nle; + + nle = nftnl_expr_alloc(name); + if (nle == NULL) + memory_allocation_error(); + + return nle; +} +static void netlink_gen_key(const struct expr *expr, + struct nft_data_linearize *data); +static void __netlink_gen_data(const struct expr *expr, + struct nft_data_linearize *data, bool expand); + +struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, + const struct expr *expr) +{ + const struct expr *elem, *data; + struct nftnl_set_elem *nlse; + struct nft_data_linearize nld; + struct nftnl_udata_buf *udbuf = NULL; + uint32_t flags = 0; + int num_exprs = 0; + struct stmt *stmt; + struct expr *key; + + nlse = nftnl_set_elem_alloc(); + if (nlse == NULL) + memory_allocation_error(); + + data = NULL; + if (expr->etype == EXPR_MAPPING) { + elem = expr->left; + if (!(expr->flags & EXPR_F_INTERVAL_END)) + data = expr->right; + } else { + elem = expr; + } + if (elem->etype != EXPR_SET_ELEM) + BUG("Unexpected expression type: got %d\n", elem->etype); + + key = elem->key; + + switch (key->etype) { + case EXPR_SET_ELEM_CATCHALL: + break; + default: + if (set->set_flags & NFT_SET_INTERVAL && + key->etype == EXPR_CONCAT && key->field_count > 1) { + key->flags |= EXPR_F_INTERVAL; + netlink_gen_key(key, &nld); + key->flags &= ~EXPR_F_INTERVAL; + + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len); + + key->flags |= EXPR_F_INTERVAL_END; + netlink_gen_key(key, &nld); + key->flags &= ~EXPR_F_INTERVAL_END; + + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END, + &nld.value, nld.len); + } else { + netlink_gen_key(key, &nld); + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len); + } + break; + } + + if (elem->timeout) + nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_TIMEOUT, + elem->timeout); + if (elem->expiration) + nftnl_set_elem_set_u64(nlse, NFTNL_SET_ELEM_EXPIRATION, + elem->expiration); + list_for_each_entry(stmt, &elem->stmt_list, list) + num_exprs++; + + if (num_exprs == 1) { + list_for_each_entry(stmt, &elem->stmt_list, list) { + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_EXPR, + netlink_gen_stmt_stateful(stmt), 0); + } + } else if (num_exprs > 1) { + list_for_each_entry(stmt, &elem->stmt_list, list) { + nftnl_set_elem_add_expr(nlse, + netlink_gen_stmt_stateful(stmt)); + } + } + if (elem->comment || expr->elem_flags) { + udbuf = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udbuf) + memory_allocation_error(); + } + if (elem->comment) { + if (!nftnl_udata_put_strz(udbuf, NFTNL_UDATA_SET_ELEM_COMMENT, + elem->comment)) + memory_allocation_error(); + } + if (expr->elem_flags) { + if (!nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_ELEM_FLAGS, + expr->elem_flags)) + memory_allocation_error(); + } + if (udbuf) { + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_USERDATA, + nftnl_udata_buf_data(udbuf), + nftnl_udata_buf_len(udbuf)); + nftnl_udata_buf_free(udbuf); + } + if (set_is_datamap(set->set_flags) && data != NULL) { + __netlink_gen_data(data, &nld, !(data->flags & EXPR_F_SINGLETON)); + switch (data->etype) { + case EXPR_VERDICT: + nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_VERDICT, + data->verdict); + if (data->chain != NULL) + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_CHAIN, + nld.chain, strlen(nld.chain)); + break; + case EXPR_CONCAT: + assert(nld.len > 0); + /* fallthrough */ + case EXPR_VALUE: + case EXPR_RANGE: + case EXPR_PREFIX: + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_DATA, + nld.value, nld.len); + break; + default: + BUG("unexpected set element expression\n"); + break; + } + } + if (set_is_objmap(set->set_flags) && data != NULL) { + netlink_gen_data(data, &nld); + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_OBJREF, + nld.value, nld.len); + } + + if (expr->flags & EXPR_F_INTERVAL_END) + flags |= NFT_SET_ELEM_INTERVAL_END; + if (key->etype == EXPR_SET_ELEM_CATCHALL) + flags |= NFT_SET_ELEM_CATCHALL; + + if (flags) + nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_FLAGS, flags); + + return nlse; +} + +void netlink_gen_raw_data(const mpz_t value, enum byteorder byteorder, + unsigned int len, struct nft_data_linearize *data) +{ + assert(len > 0); + mpz_export_data(data->value, value, byteorder, len); + data->len = len; +} + +static int netlink_export_pad(unsigned char *data, const mpz_t v, + const struct expr *i) +{ + mpz_export_data(data, v, i->byteorder, + div_round_up(i->len, BITS_PER_BYTE)); + + return netlink_padded_len(i->len) / BITS_PER_BYTE; +} + +static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, + unsigned char *data) +{ + struct expr *expr; + + switch (i->etype) { + case EXPR_RANGE: + if (flags & EXPR_F_INTERVAL_END) + expr = i->right; + else + expr = i->left; + + if (expr_basetype(expr)->type == TYPE_INTEGER && + expr->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + i = expr; + break; + case EXPR_PREFIX: + if (flags & EXPR_F_INTERVAL_END) { + int count; + mpz_t v; + + mpz_init_bitmask(v, i->len - i->prefix_len); + + if (i->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(v, i->len / BITS_PER_BYTE); + + mpz_add(v, i->prefix->value, v); + count = netlink_export_pad(data, v, i); + mpz_clear(v); + return count; + } + return netlink_export_pad(data, i->prefix->value, i); + case EXPR_VALUE: + /* Switch byteorder only once for singleton values when the set + * contains concatenation of intervals. + */ + if (!(flags & EXPR_F_INTERVAL)) + break; + + expr = (struct expr *)i; + if (expr_basetype(expr)->type == TYPE_INTEGER && + expr->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + break; + default: + BUG("invalid expression type '%s' in set", expr_ops(i)->name); + } + + return netlink_export_pad(data, i->value, i); +} + +static void netlink_gen_concat_key(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = expr->len / BITS_PER_BYTE, offset = 0; + unsigned char data[len]; + const struct expr *i; + + memset(data, 0, len); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_key(expr->flags, i, data + offset); + + memcpy(nld->value, data, len); + nld->len = len; +} + +static int __netlink_gen_concat_data(int end, const struct expr *i, + unsigned char *data) +{ + switch (i->etype) { + case EXPR_RANGE: + i = end ? i->right : i->left; + break; + case EXPR_PREFIX: + if (end) { + int count; + mpz_t v; + + mpz_init_bitmask(v, i->len - i->prefix_len); + mpz_add(v, i->prefix->value, v); + count = netlink_export_pad(data, v, i); + mpz_clear(v); + return count; + } + return netlink_export_pad(data, i->prefix->value, i); + case EXPR_VALUE: + break; + default: + BUG("invalid expression type '%s' in set", expr_ops(i)->name); + } + + return netlink_export_pad(data, i->value, i); +} + +static void __netlink_gen_concat_expand(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2, offset = 0; + unsigned char data[len]; + const struct expr *i; + + memset(data, 0, len); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_data(false, i, data + offset); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_data(true, i, data + offset); + + memcpy(nld->value, data, len); + nld->len = len; +} + +static void __netlink_gen_concat(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = expr->len / BITS_PER_BYTE, offset = 0; + unsigned char data[len]; + const struct expr *i; + + memset(data, 0, len); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_data(expr->flags, i, data + offset); + + memcpy(nld->value, data, len); + nld->len = len; +} + +static void netlink_gen_concat_data(const struct expr *expr, + struct nft_data_linearize *nld, bool expand) +{ + if (expand) + __netlink_gen_concat_expand(expr, nld); + else + __netlink_gen_concat(expr, nld); +} + +static void netlink_gen_constant_data(const struct expr *expr, + struct nft_data_linearize *data) +{ + assert(expr->etype == EXPR_VALUE); + netlink_gen_raw_data(expr->value, expr->byteorder, + div_round_up(expr->len, BITS_PER_BYTE), data); +} + +static void netlink_gen_chain(const struct expr *expr, + struct nft_data_linearize *data) +{ + char chain[NFT_CHAIN_MAXNAMELEN]; + unsigned int len; + + len = expr->chain->len / BITS_PER_BYTE; + + if (!len) + BUG("chain length is 0"); + + if (len > sizeof(chain)) + BUG("chain is too large (%u, %u max)", + len, (unsigned int)sizeof(chain)); + + memset(chain, 0, sizeof(chain)); + + mpz_export_data(chain, expr->chain->value, + BYTEORDER_HOST_ENDIAN, len); + snprintf(data->chain, NFT_CHAIN_MAXNAMELEN, "%s", chain); +} + +static void netlink_gen_verdict(const struct expr *expr, + struct nft_data_linearize *data) +{ + + data->verdict = expr->verdict; + + switch (expr->verdict) { + case NFT_JUMP: + case NFT_GOTO: + if (expr->chain) + netlink_gen_chain(expr, data); + else + data->chain_id = expr->chain_id; + break; + } +} + +static void netlink_gen_range(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = div_round_up(expr->left->len, BITS_PER_BYTE) * 2; + unsigned char data[len]; + unsigned int offset = 0; + + memset(data, 0, len); + offset = netlink_export_pad(data, expr->left->value, expr->left); + netlink_export_pad(data + offset, expr->right->value, expr->right); + memcpy(nld->value, data, len); + nld->len = len; +} + +static void netlink_gen_prefix(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2; + unsigned char data[len]; + int offset; + mpz_t v; + + offset = netlink_export_pad(data, expr->prefix->value, expr); + mpz_init_bitmask(v, expr->len - expr->prefix_len); + mpz_add(v, expr->prefix->value, v); + netlink_export_pad(data + offset, v, expr->prefix); + mpz_clear(v); + + memcpy(nld->value, data, len); + nld->len = len; +} + +static void netlink_gen_key(const struct expr *expr, + struct nft_data_linearize *data) +{ + switch (expr->etype) { + case EXPR_VALUE: + return netlink_gen_constant_data(expr, data); + case EXPR_CONCAT: + return netlink_gen_concat_key(expr, data); + case EXPR_RANGE: + return netlink_gen_range(expr, data); + case EXPR_PREFIX: + return netlink_gen_prefix(expr, data); + default: + BUG("invalid data expression type %s\n", expr_name(expr)); + } +} + +static void __netlink_gen_data(const struct expr *expr, + struct nft_data_linearize *data, bool expand) +{ + switch (expr->etype) { + case EXPR_VALUE: + return netlink_gen_constant_data(expr, data); + case EXPR_CONCAT: + return netlink_gen_concat_data(expr, data, expand); + case EXPR_VERDICT: + return netlink_gen_verdict(expr, data); + case EXPR_RANGE: + return netlink_gen_range(expr, data); + case EXPR_PREFIX: + return netlink_gen_prefix(expr, data); + default: + BUG("invalid data expression type %s\n", expr_name(expr)); + } +} + +void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data) +{ + __netlink_gen_data(expr, data, false); +} + +struct expr *netlink_alloc_value(const struct location *loc, + const struct nft_data_delinearize *nld) +{ + return constant_expr_alloc(loc, &invalid_type, BYTEORDER_INVALID, + nld->len * BITS_PER_BYTE, nld->value); +} + +static struct expr *netlink_alloc_verdict(const struct location *loc, + const struct nft_data_delinearize *nld) +{ + struct expr *chain; + + switch (nld->verdict) { + case NFT_JUMP: + case NFT_GOTO: + chain = constant_expr_alloc(loc, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(nld->chain) * BITS_PER_BYTE, + nld->chain); + break; + default: + chain = NULL; + break; + } + + return verdict_expr_alloc(loc, nld->verdict, chain); +} + +struct expr *netlink_alloc_data(const struct location *loc, + const struct nft_data_delinearize *nld, + enum nft_registers dreg) +{ + switch (dreg) { + case NFT_REG_VERDICT: + return netlink_alloc_verdict(loc, nld); + default: + return netlink_alloc_value(loc, nld); + } +} + +void netlink_dump_rule(const struct nftnl_rule *nlr, struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_rule_fprintf(fp, nlr, 0, 0); + fprintf(fp, "\n"); +} + +void netlink_dump_expr(const struct nftnl_expr *nle, + FILE *fp, unsigned int debug_mask) +{ + if (!(debug_mask & NFT_DEBUG_NETLINK)) + return; + + nftnl_expr_fprintf(fp, nle, 0, 0); + fprintf(fp, "\n"); +} + +void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_chain_fprintf(fp, nlc, 0, 0); + fprintf(fp, "\n"); +} + +static int chain_parse_udata_cb(const struct nftnl_udata *attr, void *data) +{ + unsigned char *value = nftnl_udata_get(attr); + uint8_t type = nftnl_udata_type(attr); + const struct nftnl_udata **tb = data; + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_CHAIN_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static int qsort_device_cmp(const void *a, const void *b) +{ + const char **x = (const char **)a; + const char **y = (const char **)b; + + return strcmp(*x, *y); +} + +struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, + const struct nftnl_chain *nlc) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {}; + int priority, policy, len = 0, i; + const char * const *dev_array; + struct chain *chain; + const char *udata; + uint32_t ulen; + + chain = chain_alloc(); + chain->handle.family = + nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); + chain->handle.table.name = + xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE)); + chain->handle.chain.name = + xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); + chain->handle.handle.id = + nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE); + if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS)) + chain->flags = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FLAGS); + + if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_HOOKNUM) && + nftnl_chain_is_set(nlc, NFTNL_CHAIN_PRIO) && + nftnl_chain_is_set(nlc, NFTNL_CHAIN_TYPE) && + nftnl_chain_is_set(nlc, NFTNL_CHAIN_POLICY)) { + chain->hook.num = + nftnl_chain_get_u32(nlc, NFTNL_CHAIN_HOOKNUM); + chain->hook.name = + hooknum2str(chain->handle.family, chain->hook.num); + priority = nftnl_chain_get_s32(nlc, NFTNL_CHAIN_PRIO); + chain->priority.expr = + constant_expr_alloc(&netlink_location, + &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, + &priority); + chain->type.str = + xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TYPE)); + policy = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_POLICY); + chain->policy = constant_expr_alloc(&netlink_location, + &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, + &policy); + nftnl_chain_get_u32(nlc, NFTNL_CHAIN_POLICY); + if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_DEV)) { + chain->dev_array = xmalloc(sizeof(char *) * 2); + chain->dev_array_len = 1; + chain->dev_array[0] = + xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_DEV)); + chain->dev_array[1] = NULL; + } else if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_DEVICES)) { + dev_array = nftnl_chain_get(nlc, NFTNL_CHAIN_DEVICES); + while (dev_array[len]) + len++; + + chain->dev_array = xmalloc((len + 1)* sizeof(char *)); + for (i = 0; i < len; i++) + chain->dev_array[i] = xstrdup(dev_array[i]); + + chain->dev_array[i] = NULL; + chain->dev_array_len = len; + } + chain->flags |= CHAIN_F_BASECHAIN; + + if (chain->dev_array_len) { + qsort(chain->dev_array, chain->dev_array_len, + sizeof(char *), qsort_device_cmp); + } + } + + if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_USERDATA)) { + udata = nftnl_chain_get_data(nlc, NFTNL_CHAIN_USERDATA, &ulen); + if (nftnl_udata_parse(udata, ulen, chain_parse_udata_cb, ud) < 0) { + netlink_io_error(ctx, NULL, "Cannot parse userdata"); + chain_free(chain); + return NULL; + } + if (ud[NFTNL_UDATA_CHAIN_COMMENT]) + chain->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_CHAIN_COMMENT])); + } + + return chain; +} + +static int table_parse_udata_cb(const struct nftnl_udata *attr, void *data) +{ + unsigned char *value = nftnl_udata_get(attr); + const struct nftnl_udata **tb = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_TABLE_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +struct table *netlink_delinearize_table(struct netlink_ctx *ctx, + const struct nftnl_table *nlt) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_TABLE_MAX + 1] = {}; + struct table *table; + const char *udata; + uint32_t ulen; + + table = table_alloc(); + table->handle.family = nftnl_table_get_u32(nlt, NFTNL_TABLE_FAMILY); + table->handle.table.name = xstrdup(nftnl_table_get_str(nlt, NFTNL_TABLE_NAME)); + table->flags = nftnl_table_get_u32(nlt, NFTNL_TABLE_FLAGS); + table->handle.handle.id = nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE); + table->owner = nftnl_table_get_u32(nlt, NFTNL_TABLE_OWNER); + + if (nftnl_table_is_set(nlt, NFTNL_TABLE_USERDATA)) { + udata = nftnl_table_get_data(nlt, NFTNL_TABLE_USERDATA, &ulen); + if (nftnl_udata_parse(udata, ulen, table_parse_udata_cb, ud) < 0) { + netlink_io_error(ctx, NULL, "Cannot parse userdata"); + table_free(table); + return NULL; + } + if (ud[NFTNL_UDATA_TABLE_COMMENT]) + table->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_TABLE_COMMENT])); + } + + return table; +} + +static int list_table_cb(struct nftnl_table *nlt, void *arg) +{ + struct netlink_ctx *ctx = arg; + struct table *table; + + table = netlink_delinearize_table(ctx, nlt); + list_add_tail(&table->list, &ctx->list); + + return 0; +} + +int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h, + const struct nft_cache_filter *filter) +{ + struct nftnl_table_list *table_cache; + uint32_t family = h->family; + const char *table = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + } + + table_cache = mnl_nft_table_dump(ctx, family, table); + if (table_cache == NULL) { + if (errno == EINTR) + return -1; + + return -1; + } + + ctx->data = h; + nftnl_table_list_foreach(table_cache, list_table_cb, ctx); + nftnl_table_list_free(table_cache); + return 0; +} + +enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype) +{ + switch (dtype->type) { + case TYPE_VERDICT: + return NFT_DATA_VERDICT; + default: + return dtype->type; + } +} + +static const struct datatype *dtype_map_from_kernel(enum nft_data_types type) +{ + /* The function always returns ownership of a reference. But for + * &verdict_Type and datatype_lookup(), those are static instances, + * we can omit the datatype_get() call. + */ + switch (type) { + case NFT_DATA_VERDICT: + return &verdict_type; + default: + if (type & ~TYPE_MASK) + return concat_type_alloc(type); + return datatype_lookup((enum datatypes) type); + } +} + +void netlink_dump_set(const struct nftnl_set *nls, struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_set_fprintf(fp, nls, 0, 0); + fprintf(fp, "\n"); +} + +static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data) +{ + unsigned char *value = nftnl_udata_get(attr); + const struct nftnl_udata **tb = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_KEYBYTEORDER: + case NFTNL_UDATA_SET_DATABYTEORDER: + case NFTNL_UDATA_SET_MERGE_ELEMENTS: + case NFTNL_UDATA_SET_DATA_INTERVAL: + if (len != sizeof(uint32_t)) + return -1; + break; + case NFTNL_UDATA_SET_KEY_TYPEOF: + case NFTNL_UDATA_SET_DATA_TYPEOF: + if (len < 3) + return -1; + break; + case NFTNL_UDATA_SET_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static int set_key_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **tb = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_TYPEOF_EXPR: + if (len != sizeof(uint32_t)) + return -1; + break; + case NFTNL_UDATA_SET_TYPEOF_DATA: + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static struct expr *set_make_key(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_TYPEOF_MAX + 1] = {}; + const struct expr_ops *ops; + struct expr *expr; + uint32_t etype; + int err; + + if (!attr) + return NULL; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + set_key_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_SET_TYPEOF_EXPR] || + !ud[NFTNL_UDATA_SET_TYPEOF_DATA]) + return NULL; + + etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]); + ops = expr_ops_by_type_u32(etype); + if (!ops) + return NULL; + + expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]); + if (!expr) + return NULL; + + return expr; +} + +static bool set_udata_key_valid(const struct expr *e, uint32_t len) +{ + if (!e) + return false; + + return div_round_up(e->len, BITS_PER_BYTE) == len / BITS_PER_BYTE; +} + +struct setelem_parse_ctx { + struct set *set; + struct nft_cache *cache; + struct list_head stmt_list; +}; + +static int set_elem_parse_expressions(struct nftnl_expr *e, void *data) +{ + struct setelem_parse_ctx *setelem_parse_ctx = data; + struct nft_cache *cache = setelem_parse_ctx->cache; + struct set *set = setelem_parse_ctx->set; + struct stmt *stmt; + + stmt = netlink_parse_set_expr(set, cache, e); + list_add_tail(&stmt->list, &setelem_parse_ctx->stmt_list); + + return 0; +} + +struct set *netlink_delinearize_set(struct netlink_ctx *ctx, + const struct nftnl_set *nls) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {}; + enum byteorder keybyteorder = BYTEORDER_INVALID; + enum byteorder databyteorder = BYTEORDER_INVALID; + struct setelem_parse_ctx set_parse_ctx; + const struct datatype *datatype = NULL; + const struct datatype *keytype = NULL; + const struct datatype *dtype2 = NULL; + const struct datatype *dtype = NULL; + struct expr *typeof_expr_data = NULL; + struct expr *typeof_expr_key = NULL; + const char *udata, *comment = NULL; + uint32_t flags, key, objtype = 0; + uint32_t data_interval = 0; + bool automerge = false; + struct set *set; + uint32_t ulen; + uint32_t klen; + + if (nftnl_set_is_set(nls, NFTNL_SET_USERDATA)) { + udata = nftnl_set_get_data(nls, NFTNL_SET_USERDATA, &ulen); + if (nftnl_udata_parse(udata, ulen, set_parse_udata_cb, ud) < 0) { + netlink_io_error(ctx, NULL, "Cannot parse userdata"); + return NULL; + } + +#define GET_U32_UDATA(var, attr) \ + if (ud[attr]) \ + var = nftnl_udata_get_u32(ud[attr]) + + GET_U32_UDATA(keybyteorder, NFTNL_UDATA_SET_KEYBYTEORDER); + GET_U32_UDATA(databyteorder, NFTNL_UDATA_SET_DATABYTEORDER); + GET_U32_UDATA(automerge, NFTNL_UDATA_SET_MERGE_ELEMENTS); + GET_U32_UDATA(data_interval, NFTNL_UDATA_SET_DATA_INTERVAL); + +#undef GET_U32_UDATA + typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]); + if (ud[NFTNL_UDATA_SET_DATA_TYPEOF]) + typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]); + if (ud[NFTNL_UDATA_SET_COMMENT]) + comment = nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT]); + } + + key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE); + keytype = dtype_map_from_kernel(key); + if (keytype == NULL) { + netlink_io_error(ctx, NULL, "Unknown data type in set key %u", + key); + return NULL; + } + + flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (set_is_datamap(flags)) { + uint32_t data; + + data = nftnl_set_get_u32(nls, NFTNL_SET_DATA_TYPE); + datatype = dtype_map_from_kernel(data); + if (datatype == NULL) { + netlink_io_error(ctx, NULL, + "Unknown data type in set key %u", + data); + set = NULL; + goto out; + } + } + + if (set_is_objmap(flags)) { + objtype = nftnl_set_get_u32(nls, NFTNL_SET_OBJ_TYPE); + assert(!datatype); + datatype = &string_type; + } + + set = set_alloc(&netlink_location); + set->handle.family = nftnl_set_get_u32(nls, NFTNL_SET_FAMILY); + set->handle.table.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_TABLE)); + set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME)); + set->automerge = automerge; + if (comment) + set->comment = xstrdup(comment); + + init_list_head(&set_parse_ctx.stmt_list); + + if (nftnl_set_is_set(nls, NFTNL_SET_EXPR)) { + const struct nftnl_expr *nle; + struct stmt *stmt; + + nle = nftnl_set_get(nls, NFTNL_SET_EXPR); + stmt = netlink_parse_set_expr(set, &ctx->nft->cache, nle); + list_add_tail(&stmt->list, &set_parse_ctx.stmt_list); + } else if (nftnl_set_is_set(nls, NFTNL_SET_EXPRESSIONS)) { + set_parse_ctx.cache = &ctx->nft->cache; + set_parse_ctx.set = set; + nftnl_set_expr_foreach(nls, set_elem_parse_expressions, + &set_parse_ctx); + } + list_splice_tail(&set_parse_ctx.stmt_list, &set->stmt_list); + + if (datatype) { + uint32_t dlen; + + dtype2 = set_datatype_alloc(datatype, databyteorder); + klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE; + + dlen = data_interval ? klen / 2 : klen; + + if (set_udata_key_valid(typeof_expr_data, dlen)) { + typeof_expr_data->len = klen; + set->data = typeof_expr_data; + typeof_expr_data = NULL; + } else { + set->data = constant_expr_alloc(&netlink_location, + dtype2, + databyteorder, klen, + NULL); + + /* Can't use 'typeof' keyword, so discard key too */ + expr_free(typeof_expr_key); + typeof_expr_key = NULL; + } + + if (data_interval) + set->data->flags |= EXPR_F_INTERVAL; + } + + dtype = set_datatype_alloc(keytype, keybyteorder); + klen = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE; + + if (set_udata_key_valid(typeof_expr_key, klen)) { + set->key = typeof_expr_key; + typeof_expr_key = NULL; + set->key_typeof_valid = true; + } else { + set->key = constant_expr_alloc(&netlink_location, dtype, + keybyteorder, klen, + NULL); + } + + set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + set->handle.handle.id = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE); + + set->objtype = objtype; + + if (nftnl_set_is_set(nls, NFTNL_SET_TIMEOUT)) + set->timeout = nftnl_set_get_u64(nls, NFTNL_SET_TIMEOUT); + if (nftnl_set_is_set(nls, NFTNL_SET_GC_INTERVAL)) + set->gc_int = nftnl_set_get_u32(nls, NFTNL_SET_GC_INTERVAL); + + if (nftnl_set_is_set(nls, NFTNL_SET_POLICY)) + set->policy = nftnl_set_get_u32(nls, NFTNL_SET_POLICY); + + if (nftnl_set_is_set(nls, NFTNL_SET_DESC_SIZE)) + set->desc.size = nftnl_set_get_u32(nls, NFTNL_SET_DESC_SIZE); + + if (nftnl_set_is_set(nls, NFTNL_SET_DESC_CONCAT)) { + uint32_t len = NFT_REG32_COUNT; + const uint8_t *data; + + data = nftnl_set_get_data(nls, NFTNL_SET_DESC_CONCAT, &len); + if (data) { + memcpy(set->desc.field_len, data, len); + set->desc.field_count = len; + } + } + +out: + expr_free(typeof_expr_data); + expr_free(typeof_expr_key); + datatype_free(datatype); + datatype_free(keytype); + datatype_free(dtype2); + datatype_free(dtype); + return set; +} + +void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls) +{ + struct nftnl_set_elem *nlse; + const struct expr *expr; + + list_for_each_entry(expr, &set->expressions, list) { + nlse = alloc_nftnl_setelem(set, expr); + nftnl_set_elem_add(nls, nlse); + } +} + +static bool range_expr_is_prefix(const struct expr *range, uint32_t *prefix_len) +{ + const struct expr *right = range->right; + const struct expr *left = range->left; + uint32_t len = left->len; + unsigned long n1, n2; + uint32_t plen; + mpz_t bitmask; + + mpz_init2(bitmask, left->len); + mpz_xor(bitmask, left->value, right->value); + + n1 = mpz_scan0(bitmask, 0); + if (n1 == ULONG_MAX) + goto not_a_prefix; + + n2 = mpz_scan1(bitmask, n1 + 1); + if (n2 < len) + goto not_a_prefix; + + plen = len - n1; + + if (mpz_scan1(left->value, 0) < len - plen) + goto not_a_prefix; + + mpz_clear(bitmask); + *prefix_len = plen; + + return true; + +not_a_prefix: + mpz_clear(bitmask); + + return false; +} + +struct expr *range_expr_to_prefix(struct expr *range) +{ + struct expr *prefix; + uint32_t prefix_len; + + if (range_expr_is_prefix(range, &prefix_len)) { + prefix = prefix_expr_alloc(&range->location, + expr_get(range->left), + prefix_len); + expr_free(range); + return prefix; + } + + return range; +} + +static struct expr *range_expr_reduce(struct expr *range) +{ + struct expr *expr; + + if (!mpz_cmp(range->left->value, range->right->value)) { + expr = expr_get(range->left); + expr_free(range); + return expr; + } + + if (range->left->dtype->type != TYPE_IPADDR && + range->left->dtype->type != TYPE_IP6ADDR) + return range; + + return range_expr_to_prefix(range); +} + +static struct expr *netlink_parse_interval_elem(const struct set *set, + struct expr *expr) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + const struct datatype *dtype = set->data->dtype; + struct expr *range, *left, *right; + char data[len]; + + mpz_export_data(data, expr->value, dtype->byteorder, len); + left = constant_expr_alloc(&internal_location, dtype, + dtype->byteorder, + (len / 2) * BITS_PER_BYTE, &data[0]); + right = constant_expr_alloc(&internal_location, dtype, + dtype->byteorder, + (len / 2) * BITS_PER_BYTE, &data[len / 2]); + range = range_expr_alloc(&expr->location, left, right); + expr_free(expr); + + return range_expr_to_prefix(range); +} + +static struct expr *concat_elem_expr(const struct set *set, struct expr *key, + const struct datatype *dtype, + struct expr *data, int *off) +{ + const struct datatype *subtype; + unsigned int sub_length; + struct expr *expr; + + if (key) { + (*off)--; + sub_length = round_up(key->len, BITS_PER_BYTE); + + expr = constant_expr_splice(data, sub_length); + expr->dtype = datatype_get(key->dtype); + expr->byteorder = key->byteorder; + expr->len = key->len; + } else { + subtype = concat_subtype_lookup(dtype->type, --(*off)); + sub_length = round_up(subtype->size, BITS_PER_BYTE); + expr = constant_expr_splice(data, sub_length); + expr->dtype = subtype; + expr->byteorder = subtype->byteorder; + } + + if (expr_basetype(expr)->type == TYPE_STRING || + (!(set->flags & NFT_SET_INTERVAL) && + expr->byteorder == BYTEORDER_HOST_ENDIAN)) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + if (expr->dtype->basetype != NULL && + expr->dtype->basetype->type == TYPE_BITMASK) + expr = bitmask_expr_to_binops(expr); + + data->len -= netlink_padding_len(sub_length); + + return expr; +} + +static struct expr *netlink_parse_concat_elem_key(const struct set *set, + struct expr *data) +{ + const struct datatype *dtype = set->key->dtype; + struct expr *concat, *expr, *n = NULL; + int off = dtype->subtypes; + + if (set->key->etype == EXPR_CONCAT) + n = list_first_entry(&set->key->expressions, struct expr, list); + + concat = concat_expr_alloc(&data->location); + while (off > 0) { + expr = concat_elem_expr(set, n, dtype, data, &off); + compound_expr_add(concat, expr); + if (set->key->etype == EXPR_CONCAT) + n = list_next_entry(n, list); + } + + expr_free(data); + + return concat; +} + +static struct expr *netlink_parse_concat_elem(const struct set *set, + struct expr *data) +{ + const struct datatype *dtype = set->data->dtype; + struct expr *concat, *expr, *left, *range; + struct list_head expressions; + int off = dtype->subtypes; + + init_list_head(&expressions); + + concat = concat_expr_alloc(&data->location); + while (off > 0) { + expr = concat_elem_expr(set, NULL, dtype, data, &off); + list_add_tail(&expr->list, &expressions); + } + + if (set->data->flags & EXPR_F_INTERVAL) { + assert(!list_empty(&expressions)); + + off = dtype->subtypes; + + while (off > 0) { + left = list_first_entry(&expressions, struct expr, list); + + expr = concat_elem_expr(set, NULL, dtype, data, &off); + list_del(&left->list); + + range = range_expr_alloc(&data->location, left, expr); + range = range_expr_reduce(range); + compound_expr_add(concat, range); + } + assert(list_empty(&expressions)); + } else { + list_splice_tail(&expressions, &concat->expressions); + } + + expr_free(data); + + return concat; +} + +static int set_elem_parse_udata_cb(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **tb = data; + unsigned char *value = nftnl_udata_get(attr); + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_ELEM_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + case NFTNL_UDATA_SET_ELEM_FLAGS: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static void set_elem_parse_udata(struct nftnl_set_elem *nlse, + struct expr *expr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_ELEM_MAX + 1] = {}; + const void *data; + uint32_t len; + + data = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_USERDATA, &len); + if (nftnl_udata_parse(data, len, set_elem_parse_udata_cb, ud)) + return; + + if (ud[NFTNL_UDATA_SET_ELEM_COMMENT]) + expr->comment = + xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_SET_ELEM_COMMENT])); + if (ud[NFTNL_UDATA_SET_ELEM_FLAGS]) + expr->elem_flags = + nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_ELEM_FLAGS]); +} + +int netlink_delinearize_setelem(struct nftnl_set_elem *nlse, + struct set *set, struct nft_cache *cache) +{ + struct setelem_parse_ctx setelem_parse_ctx = { + .set = set, + .cache = cache, + }; + struct nft_data_delinearize nld; + struct expr *expr, *key, *data; + uint32_t flags = 0; + + init_list_head(&setelem_parse_ctx.stmt_list); + + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY)) + nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_KEY, &nld.len); + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_FLAGS)) + flags = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_FLAGS); + +key_end: + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY)) { + key = netlink_alloc_value(&netlink_location, &nld); + datatype_set(key, set->key->dtype); + key->byteorder = set->key->byteorder; + if (set->key->dtype->subtypes) + key = netlink_parse_concat_elem_key(set, key); + + if (!(set->flags & NFT_SET_INTERVAL) && + key->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE); + + if (key->dtype->basetype != NULL && + key->dtype->basetype->type == TYPE_BITMASK) + key = bitmask_expr_to_binops(key); + } else if (flags & NFT_SET_ELEM_CATCHALL) { + key = set_elem_catchall_expr_alloc(&netlink_location); + datatype_set(key, set->key->dtype); + key->byteorder = set->key->byteorder; + key->len = set->key->len; + } else { + BUG("Unexpected set element with no key\n"); + } + + expr = set_elem_expr_alloc(&netlink_location, key); + expr->flags |= EXPR_F_KERNEL; + + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT)) + expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT); + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPIRATION)) + expr->expiration = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_EXPIRATION); + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_USERDATA)) + set_elem_parse_udata(nlse, expr); + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPR)) { + const struct nftnl_expr *nle; + struct stmt *stmt; + + nle = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_EXPR, NULL); + stmt = netlink_parse_set_expr(set, cache, nle); + list_add_tail(&stmt->list, &setelem_parse_ctx.stmt_list); + } else if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_EXPRESSIONS)) { + nftnl_set_elem_expr_foreach(nlse, set_elem_parse_expressions, + &setelem_parse_ctx); + } + list_splice_tail_init(&setelem_parse_ctx.stmt_list, &expr->stmt_list); + + if (flags & NFT_SET_ELEM_INTERVAL_END) { + expr->flags |= EXPR_F_INTERVAL_END; + if (mpz_cmp_ui(set->key->value, 0) == 0) + set->root = true; + } + + if (set_is_datamap(set->flags)) { + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_DATA)) { + nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_DATA, + &nld.len); + } else if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_CHAIN)) { + nld.chain = nftnl_set_elem_get_str(nlse, NFTNL_SET_ELEM_CHAIN); + nld.verdict = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_VERDICT); + } else if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_VERDICT)) { + nld.verdict = nftnl_set_elem_get_u32(nlse, NFTNL_SET_ELEM_VERDICT); + } else + goto out; + + data = netlink_alloc_data(&netlink_location, &nld, + set->data->dtype->type == TYPE_VERDICT ? + NFT_REG_VERDICT : NFT_REG_1); + datatype_set(data, set->data->dtype); + data->byteorder = set->data->byteorder; + + if (set->data->dtype->subtypes) { + data = netlink_parse_concat_elem(set, data); + } else if (set->data->flags & EXPR_F_INTERVAL) + data = netlink_parse_interval_elem(set, data); + + if (data->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE); + + expr = mapping_expr_alloc(&netlink_location, expr, data); + } + if (set_is_objmap(set->flags)) { + if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_OBJREF)) { + nld.value = nftnl_set_elem_get(nlse, + NFTNL_SET_ELEM_OBJREF, + &nld.len); + } else + goto out; + + data = netlink_alloc_value(&netlink_location, &nld); + data->dtype = &string_type; + data->byteorder = BYTEORDER_HOST_ENDIAN; + mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE); + expr = mapping_expr_alloc(&netlink_location, expr, data); + } +out: + compound_expr_add(set->init, expr); + + if (!(flags & NFT_SET_ELEM_INTERVAL_END) && + nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_KEY_END)) { + flags |= NFT_SET_ELEM_INTERVAL_END; + nld.value = nftnl_set_elem_get(nlse, NFTNL_SET_ELEM_KEY_END, + &nld.len); + goto key_end; + } + + return 0; +} + +static int list_setelem_cb(struct nftnl_set_elem *nlse, void *arg) +{ + struct netlink_ctx *ctx = arg; + return netlink_delinearize_setelem(nlse, ctx->set, &ctx->nft->cache); +} + +static int list_setelem_debug_cb(struct nftnl_set_elem *nlse, void *arg) +{ + int r; + + r = list_setelem_cb(nlse, arg); + if (r == 0) { + struct netlink_ctx *ctx = arg; + FILE *fp = ctx->nft->output.output_fp; + + fprintf(fp, "\t"); + nftnl_set_elem_fprintf(fp, nlse, 0, 0); + fprintf(fp, "\n"); + } + + return r; +} + +static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (fp && (ctx->nft->debug_mask & NFT_DEBUG_NETLINK)) { + const char *table, *name; + uint32_t family = nftnl_set_get_u32(s, NFTNL_SET_FAMILY); + + table = nftnl_set_get_str(s, NFTNL_SET_TABLE); + name = nftnl_set_get_str(s, NFTNL_SET_NAME); + + fprintf(fp, "%s %s @%s\n", family2str(family), table, name); + + return nftnl_set_elem_foreach(s, list_setelem_debug_cb, ctx); + } + + return nftnl_set_elem_foreach(s, list_setelem_cb, ctx); +} + +int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h, + struct set *set, bool reset) +{ + struct nftnl_set *nls; + int err; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + if (h->handle.id) + nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id); + + err = mnl_nft_setelem_get(ctx, nls, reset); + if (err < 0) { + nftnl_set_free(nls); + if (errno == EINTR) + return -1; + + return 0; + } + + ctx->set = set; + set->init = set_expr_alloc(&internal_location, set); + list_setelements(nls, ctx); + + if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1) + concat_range_aggregate(set->init); + else if (set->flags & NFT_SET_INTERVAL) + interval_map_decompose(set->init); + else + list_expr_sort(&ctx->set->init->expressions); + + nftnl_set_free(nls); + ctx->set = NULL; + + return 0; +} + +int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc, struct set *cache_set, + struct set *set, struct expr *init, bool reset) +{ + struct nftnl_set *nls, *nls_out = NULL; + int err = 0; + + nls = nftnl_set_alloc(); + if (nls == NULL) + memory_allocation_error(); + + nftnl_set_set_u32(nls, NFTNL_SET_FAMILY, h->family); + nftnl_set_set_str(nls, NFTNL_SET_TABLE, h->table.name); + nftnl_set_set_str(nls, NFTNL_SET_NAME, h->set.name); + if (h->handle.id) + nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id); + + alloc_setelem_cache(init, nls); + + netlink_dump_set(nls, ctx); + + nls_out = mnl_nft_setelem_get_one(ctx, nls, reset); + if (!nls_out) { + nftnl_set_free(nls); + return -1; + } + + ctx->set = set; + set->init = set_expr_alloc(loc, set); + list_setelements(nls_out, ctx); + + if (set->flags & NFT_SET_INTERVAL && set->desc.field_count > 1) + concat_range_aggregate(set->init); + else if (set->flags & NFT_SET_INTERVAL) + err = get_set_decompose(cache_set, set); + else + list_expr_sort(&ctx->set->init->expressions); + + nftnl_set_free(nls); + nftnl_set_free(nls_out); + ctx->set = NULL; + + return err; +} + +void netlink_dump_obj(struct nftnl_obj *nln, struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_obj_fprintf(fp, nln, 0, 0); + fprintf(fp, "\n"); +} + +static int obj_parse_udata_cb(const struct nftnl_udata *attr, void *data) +{ + unsigned char *value = nftnl_udata_get(attr); + uint8_t type = nftnl_udata_type(attr); + const struct nftnl_udata **tb = data; + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_OBJ_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, + struct nftnl_obj *nlo) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {}; + const char *udata; + struct obj *obj; + uint32_t type; + uint32_t ulen; + + obj = obj_alloc(&netlink_location); + obj->handle.family = nftnl_obj_get_u32(nlo, NFTNL_OBJ_FAMILY); + obj->handle.table.name = + xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_TABLE)); + obj->handle.obj.name = + xstrdup(nftnl_obj_get_str(nlo, NFTNL_OBJ_NAME)); + obj->handle.handle.id = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); + if (nftnl_obj_is_set(nlo, NFTNL_OBJ_USERDATA)) { + udata = nftnl_obj_get_data(nlo, NFTNL_OBJ_USERDATA, &ulen); + if (nftnl_udata_parse(udata, ulen, obj_parse_udata_cb, ud) < 0) { + netlink_io_error(ctx, NULL, "Cannot parse userdata"); + obj_free(obj); + return NULL; + } + if (ud[NFTNL_UDATA_OBJ_COMMENT]) + obj->comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_OBJ_COMMENT])); + } + + type = nftnl_obj_get_u32(nlo, NFTNL_OBJ_TYPE); + switch (type) { + case NFT_OBJECT_COUNTER: + obj->counter.packets = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_PKTS); + obj->counter.bytes = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_CTR_BYTES); + break; + case NFT_OBJECT_QUOTA: + obj->quota.bytes = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_BYTES); + obj->quota.used = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_QUOTA_CONSUMED); + obj->quota.flags = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS); + break; + case NFT_OBJECT_SECMARK: + snprintf(obj->secmark.ctx, sizeof(obj->secmark.ctx), "%s", + nftnl_obj_get_str(nlo, NFTNL_OBJ_SECMARK_CTX)); + break; + case NFT_OBJECT_CT_HELPER: + snprintf(obj->ct_helper.name, sizeof(obj->ct_helper.name), "%s", + nftnl_obj_get_str(nlo, NFTNL_OBJ_CT_HELPER_NAME)); + obj->ct_helper.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_HELPER_L3PROTO); + obj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO); + break; + case NFT_OBJECT_CT_TIMEOUT: + init_list_head(&obj->ct_timeout.timeout_list); + obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO); + obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO); + memcpy(obj->ct_timeout.timeout, + nftnl_obj_get(nlo, NFTNL_OBJ_CT_TIMEOUT_ARRAY), + NFTNL_CTTIMEOUT_ARRAY_MAX * sizeof(uint32_t)); + break; + case NFT_OBJECT_LIMIT: + obj->limit.rate = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_RATE); + obj->limit.unit = + nftnl_obj_get_u64(nlo, NFTNL_OBJ_LIMIT_UNIT); + obj->limit.burst = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_BURST); + obj->limit.type = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_TYPE); + obj->limit.flags = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_LIMIT_FLAGS); + break; + case NFT_OBJECT_CT_EXPECT: + obj->ct_expect.l3proto = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_EXPECT_L3PROTO); + obj->ct_expect.l4proto = + nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_EXPECT_L4PROTO); + obj->ct_expect.dport = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_EXPECT_DPORT); + obj->ct_expect.timeout = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_CT_EXPECT_TIMEOUT); + obj->ct_expect.size = + nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_EXPECT_SIZE); + break; + case NFT_OBJECT_SYNPROXY: + obj->synproxy.mss = + nftnl_obj_get_u16(nlo, NFTNL_OBJ_SYNPROXY_MSS); + obj->synproxy.wscale = + nftnl_obj_get_u8(nlo, NFTNL_OBJ_SYNPROXY_WSCALE); + obj->synproxy.flags = + nftnl_obj_get_u32(nlo, NFTNL_OBJ_SYNPROXY_FLAGS); + break; + } + obj->type = type; + + return obj; +} + +void netlink_dump_flowtable(struct nftnl_flowtable *flo, + struct netlink_ctx *ctx) +{ + FILE *fp = ctx->nft->output.output_fp; + + if (!(ctx->nft->debug_mask & NFT_DEBUG_NETLINK) || !fp) + return; + + nftnl_flowtable_fprintf(fp, flo, 0, 0); + fprintf(fp, "\n"); +} + +static int list_obj_cb(struct nftnl_obj *nls, void *arg) +{ + struct netlink_ctx *ctx = arg; + struct obj *obj; + + obj = netlink_delinearize_obj(ctx, nls); + if (obj == NULL) + return -1; + list_add_tail(&obj->list, &ctx->list); + return 0; +} + +int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd, + uint32_t type, bool dump) +{ + const struct handle *h = &cmd->handle; + struct nftnl_obj_list *obj_cache; + int err; + + obj_cache = mnl_nft_obj_dump(ctx, h->family, + h->table.name, h->obj.name, type, dump, true); + if (obj_cache == NULL) + return -1; + + err = nftnl_obj_list_foreach(obj_cache, list_obj_cb, ctx); + nftnl_obj_list_free(obj_cache); + return err; +} + +int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd, + bool dump) +{ + const struct handle *h = &cmd->handle; + struct nft_cache_filter f = { + .list.table = h->table.name, + .list.chain = h->chain.name, + .list.rule_handle = h->handle.id, + }; + struct rule *rule, *next, *crule, *cnext; + struct table *table; + struct chain *chain; + int ret; + + ret = rule_cache_dump(ctx, h, &f, dump, true); + + list_for_each_entry_safe(rule, next, &ctx->list, list) { + table = table_cache_find(&ctx->nft->cache.table_cache, + rule->handle.table.name, + rule->handle.family); + if (!table) + continue; + + chain = chain_cache_find(table, rule->handle.chain.name); + if (!chain) + continue; + + list_del(&rule->list); + list_for_each_entry_safe(crule, cnext, &chain->rules, list) { + if (crule->handle.handle.id != rule->handle.handle.id) + continue; + + list_replace(&crule->list, &rule->list); + rule_free(crule); + rule = NULL; + break; + } + if (rule) { + list_add_tail(&rule->list, &chain->rules); + } + } + list_for_each_entry_safe(rule, next, &ctx->list, list) { + list_del(&rule->list); + rule_free(rule); + } + + return ret; +} + +struct flowtable * +netlink_delinearize_flowtable(struct netlink_ctx *ctx, + struct nftnl_flowtable *nlo) +{ + struct flowtable *flowtable; + const char * const *dev_array; + int len = 0, i, priority; + + flowtable = flowtable_alloc(&netlink_location); + flowtable->handle.family = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FAMILY); + flowtable->handle.table.name = + xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_TABLE)); + flowtable->handle.flowtable.name = + xstrdup(nftnl_flowtable_get_str(nlo, NFTNL_FLOWTABLE_NAME)); + flowtable->handle.handle.id = + nftnl_flowtable_get_u64(nlo, NFTNL_FLOWTABLE_HANDLE); + if (nftnl_flowtable_is_set(nlo, NFTNL_FLOWTABLE_FLAGS)) + flowtable->flags = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS); + dev_array = nftnl_flowtable_get(nlo, NFTNL_FLOWTABLE_DEVICES); + while (dev_array[len]) + len++; + + if (len) + flowtable->dev_array = xmalloc(len * sizeof(char *)); + for (i = 0; i < len; i++) + flowtable->dev_array[i] = xstrdup(dev_array[i]); + + flowtable->dev_array_len = len; + + if (flowtable->dev_array_len) { + qsort(flowtable->dev_array, flowtable->dev_array_len, + sizeof(char *), qsort_device_cmp); + } + + priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); + flowtable->priority.expr = + constant_expr_alloc(&netlink_location, + &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, + &priority); + flowtable->hook.num = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM); + flowtable->flags = + nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_FLAGS); + + return flowtable; +} + +static int list_flowtable_cb(struct nftnl_flowtable *nls, void *arg) +{ + struct netlink_ctx *ctx = arg; + struct flowtable *flowtable; + + flowtable = netlink_delinearize_flowtable(ctx, nls); + if (flowtable == NULL) + return -1; + list_add_tail(&flowtable->list, &ctx->list); + return 0; +} + +int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h) +{ + struct nftnl_flowtable_list *flowtable_cache; + int err; + + flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, + h->table.name, NULL); + if (flowtable_cache == NULL) { + if (errno == EINTR) + return -1; + + return 0; + } + + err = nftnl_flowtable_list_foreach(flowtable_cache, list_flowtable_cb, ctx); + nftnl_flowtable_list_free(flowtable_cache); + return err; +} + +static void trace_print_hdr(const struct nftnl_trace *nlt, + struct output_ctx *octx) +{ + nft_print(octx, "trace id %08x %s ", + nftnl_trace_get_u32(nlt, NFTNL_TRACE_ID), + family2str(nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY))); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_TABLE)) + nft_print(octx, "%s ", + nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE)); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_CHAIN)) + nft_print(octx, "%s ", + nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN)); +} + +static void trace_print_expr(const struct nftnl_trace *nlt, unsigned int attr, + struct expr *lhs, struct output_ctx *octx) +{ + struct expr *rhs, *rel; + const void *data; + uint32_t len; + + data = nftnl_trace_get_data(nlt, attr, &len); + rhs = constant_expr_alloc(&netlink_location, + lhs->dtype, lhs->byteorder, + len * BITS_PER_BYTE, data); + rel = relational_expr_alloc(&netlink_location, OP_EQ, lhs, rhs); + + expr_print(rel, octx); + nft_print(octx, " "); + expr_free(rel); +} + +static void trace_print_verdict(const struct nftnl_trace *nlt, + struct output_ctx *octx) +{ + struct expr *chain_expr = NULL; + const char *chain = NULL; + unsigned int verdict; + struct expr *expr; + + verdict = nftnl_trace_get_u32(nlt, NFTNL_TRACE_VERDICT); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_JUMP_TARGET)) { + chain = xstrdup(nftnl_trace_get_str(nlt, NFTNL_TRACE_JUMP_TARGET)); + chain_expr = constant_expr_alloc(&netlink_location, + &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(chain) * BITS_PER_BYTE, + chain); + } + expr = verdict_expr_alloc(&netlink_location, verdict, chain_expr); + + nft_print(octx, "verdict "); + expr_print(expr, octx); + expr_free(expr); +} + +static void trace_print_policy(const struct nftnl_trace *nlt, + struct output_ctx *octx) +{ + unsigned int policy; + struct expr *expr; + + policy = nftnl_trace_get_u32(nlt, NFTNL_TRACE_POLICY); + + expr = verdict_expr_alloc(&netlink_location, policy, NULL); + + nft_print(octx, "policy "); + expr_print(expr, octx); + expr_free(expr); +} + +static struct rule *trace_lookup_rule(const struct nftnl_trace *nlt, + uint64_t rule_handle, + struct nft_cache *cache) +{ + struct chain *chain; + struct table *table; + struct handle h; + + h.family = nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY); + h.table.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_TABLE); + h.chain.name = nftnl_trace_get_str(nlt, NFTNL_TRACE_CHAIN); + + if (!h.table.name) + return NULL; + + table = table_cache_find(&cache->table_cache, h.table.name, h.family); + if (!table) + return NULL; + + chain = chain_cache_find(table, h.chain.name); + if (!chain) + return NULL; + + return rule_lookup(chain, rule_handle); +} + +static void trace_print_rule(const struct nftnl_trace *nlt, + struct output_ctx *octx, struct nft_cache *cache) +{ + uint64_t rule_handle; + struct rule *rule; + + rule_handle = nftnl_trace_get_u64(nlt, NFTNL_TRACE_RULE_HANDLE); + rule = trace_lookup_rule(nlt, rule_handle, cache); + + trace_print_hdr(nlt, octx); + + if (rule) { + nft_print(octx, "rule "); + rule_print(rule, octx); + } else { + nft_print(octx, "unknown rule handle %" PRIu64, rule_handle); + } + + nft_print(octx, " ("); + trace_print_verdict(nlt, octx); + nft_print(octx, ")\n"); +} + +static void trace_gen_stmts(struct list_head *stmts, + struct proto_ctx *ctx, struct payload_dep_ctx *pctx, + const struct nftnl_trace *nlt, unsigned int attr, + enum proto_bases base) +{ + struct list_head unordered = LIST_HEAD_INIT(unordered); + struct list_head list; + struct expr *rel, *lhs, *rhs, *tmp, *nexpr; + struct stmt *stmt; + const struct proto_desc *desc; + const void *hdr; + uint32_t hlen; + unsigned int n; + + if (!nftnl_trace_is_set(nlt, attr)) + return; + hdr = nftnl_trace_get_data(nlt, attr, &hlen); + + lhs = payload_expr_alloc(&netlink_location, NULL, 0); + payload_init_raw(lhs, base, 0, hlen * BITS_PER_BYTE); + rhs = constant_expr_alloc(&netlink_location, + &invalid_type, BYTEORDER_INVALID, + hlen * BITS_PER_BYTE, hdr); + +restart: + init_list_head(&list); + payload_expr_expand(&list, lhs, ctx); + expr_free(lhs); + + desc = NULL; + list_for_each_entry_safe(lhs, nexpr, &list, list) { + if (desc && desc != ctx->protocol[base].desc) { + /* Chained protocols */ + lhs->payload.offset = 0; + if (ctx->protocol[base].desc == NULL) + break; + goto restart; + } + + tmp = constant_expr_splice(rhs, lhs->len); + expr_set_type(tmp, lhs->dtype, lhs->byteorder); + if (tmp->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(tmp->value, tmp->len / BITS_PER_BYTE); + + /* Skip unknown and filtered expressions */ + desc = lhs->payload.desc; + if (lhs->dtype == &invalid_type || + desc->checksum_key == payload_hdr_field(lhs) || + desc->format.filter & (1 << payload_hdr_field(lhs))) { + expr_free(lhs); + expr_free(tmp); + continue; + } + + rel = relational_expr_alloc(&lhs->location, OP_EQ, lhs, tmp); + stmt = expr_stmt_alloc(&rel->location, rel); + list_add_tail(&stmt->list, &unordered); + + desc = ctx->protocol[base].desc; + relational_expr_pctx_update(ctx, rel); + } + + expr_free(rhs); + + n = 0; +next: + list_for_each_entry(stmt, &unordered, list) { + enum proto_bases b = base; + + rel = stmt->expr; + lhs = rel->left; + + /* Move statements to result list in defined order */ + desc = lhs->payload.desc; + if (desc->format.order[n] && + desc->format.order[n] != payload_hdr_field(lhs)) + continue; + + list_move_tail(&stmt->list, stmts); + n++; + + if (payload_is_stacked(desc, rel)) + b--; + + /* Don't strip 'icmp type' from payload dump. */ + if (pctx->icmp_type == 0) + payload_dependency_kill(pctx, lhs, ctx->family); + if (lhs->flags & EXPR_F_PROTOCOL) + payload_dependency_store(pctx, stmt, b); + + goto next; + } +} + +static void trace_print_packet(const struct nftnl_trace *nlt, + struct output_ctx *octx) +{ + struct list_head stmts = LIST_HEAD_INIT(stmts); + const struct proto_desc *ll_desc; + struct payload_dep_ctx pctx = {}; + struct proto_ctx ctx; + uint16_t dev_type; + uint32_t nfproto; + struct stmt *stmt, *next; + + trace_print_hdr(nlt, octx); + + nft_print(octx, "packet: "); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_IIF)) + trace_print_expr(nlt, NFTNL_TRACE_IIF, + meta_expr_alloc(&netlink_location, + NFT_META_IIF), octx); + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_OIF)) + trace_print_expr(nlt, NFTNL_TRACE_OIF, + meta_expr_alloc(&netlink_location, + NFT_META_OIF), octx); + + proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false); + ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc; + if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) && + nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) { + nfproto = nftnl_trace_get_u32(nlt, NFTNL_TRACE_NFPROTO); + + proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, NULL); + proto_ctx_update(&ctx, PROTO_BASE_NETWORK_HDR, &netlink_location, + proto_find_upper(ll_desc, nfproto)); + } + if (ctx.protocol[PROTO_BASE_LL_HDR].desc == NULL && + nftnl_trace_is_set(nlt, NFTNL_TRACE_IIFTYPE)) { + dev_type = nftnl_trace_get_u16(nlt, NFTNL_TRACE_IIFTYPE); + proto_ctx_update(&ctx, PROTO_BASE_LL_HDR, &netlink_location, + proto_dev_desc(dev_type)); + } + + trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_LL_HEADER, + PROTO_BASE_LL_HDR); + trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_NETWORK_HEADER, + PROTO_BASE_NETWORK_HDR); + trace_gen_stmts(&stmts, &ctx, &pctx, nlt, NFTNL_TRACE_TRANSPORT_HEADER, + PROTO_BASE_TRANSPORT_HDR); + + list_for_each_entry_safe(stmt, next, &stmts, list) { + stmt_print(stmt, octx); + nft_print(octx, " "); + stmt_free(stmt); + } + nft_print(octx, "\n"); +} + +int netlink_events_trace_cb(const struct nlmsghdr *nlh, int type, + struct netlink_mon_handler *monh) +{ + struct nftnl_trace *nlt; + + assert(type == NFT_MSG_TRACE); + + nlt = nftnl_trace_alloc(); + if (!nlt) + memory_allocation_error(); + + if (nftnl_trace_nlmsg_parse(nlh, nlt) < 0) + netlink_abi_error(); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_LL_HEADER) || + nftnl_trace_is_set(nlt, NFTNL_TRACE_NETWORK_HEADER)) + trace_print_packet(nlt, &monh->ctx->nft->output); + + switch (nftnl_trace_get_u32(nlt, NFTNL_TRACE_TYPE)) { + case NFT_TRACETYPE_RULE: + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_RULE_HANDLE)) + trace_print_rule(nlt, &monh->ctx->nft->output, + &monh->ctx->nft->cache); + break; + case NFT_TRACETYPE_POLICY: + trace_print_hdr(nlt, &monh->ctx->nft->output); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_POLICY)) { + trace_print_policy(nlt, &monh->ctx->nft->output); + nft_mon_print(monh, " "); + } + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) + trace_print_expr(nlt, NFTNL_TRACE_MARK, + meta_expr_alloc(&netlink_location, + NFT_META_MARK), + &monh->ctx->nft->output); + nft_mon_print(monh, "\n"); + break; + case NFT_TRACETYPE_RETURN: + trace_print_hdr(nlt, &monh->ctx->nft->output); + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_VERDICT)) { + trace_print_verdict(nlt, &monh->ctx->nft->output); + nft_mon_print(monh, " "); + } + + if (nftnl_trace_is_set(nlt, NFTNL_TRACE_MARK)) + trace_print_expr(nlt, NFTNL_TRACE_MARK, + meta_expr_alloc(&netlink_location, + NFT_META_MARK), + &monh->ctx->nft->output); + nft_mon_print(monh, "\n"); + break; + } + + nftnl_trace_free(nlt); + return MNL_CB_OK; +} diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c new file mode 100644 index 0000000..e214510 --- /dev/null +++ b/src/netlink_delinearize.c @@ -0,0 +1,3512 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <nft.h> + +#include <limits.h> +#include <linux/netfilter/nf_tables.h> +#include <arpa/inet.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter.h> +#include <net/ethernet.h> +#include <netlink.h> +#include <rule.h> +#include <statement.h> +#include <expression.h> +#include <gmputil.h> +#include <utils.h> +#include <erec.h> +#include <sys/socket.h> +#include <libnftnl/udata.h> +#include <cache.h> +#include <xt.h> + +struct dl_proto_ctx *dl_proto_ctx(struct rule_pp_ctx *ctx) +{ + return ctx->dl; +} + +static struct dl_proto_ctx *dl_proto_ctx_outer(struct rule_pp_ctx *ctx) +{ + return &ctx->_dl[0]; +} + +static int netlink_parse_expr(const struct nftnl_expr *nle, + struct netlink_parse_ctx *ctx); + +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 unsigned int netlink_parse_register(const struct nftnl_expr *nle, + unsigned int attr) +{ + unsigned int reg; + + reg = nftnl_expr_get_u32(nle, attr); + /* Translate 128bit registers to corresponding 32bit registers */ + if (reg >= NFT_REG_1 && reg <= NFT_REG_4) + reg = 1 + (reg - NFT_REG_1) * (NFT_REG_SIZE / NFT_REG32_SIZE); + else if (reg >= NFT_REG32_00) + reg = 1 + reg - NFT_REG32_00; + + return reg; +} + +static void netlink_set_register(struct netlink_parse_ctx *ctx, + enum nft_registers reg, + struct expr *expr) +{ + if (reg == NFT_REG_VERDICT || reg > MAX_REGS) { + netlink_error(ctx, &expr->location, + "Invalid destination register %u", reg); + expr_free(expr); + return; + } + + expr_free(ctx->registers[reg]); + + 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 > MAX_REGS) { + netlink_error(ctx, loc, "Invalid source register %u", reg); + return NULL; + } + + expr = ctx->registers[reg]; + if (expr != NULL) + expr = expr_clone(expr); + + return expr; +} + +static void netlink_release_registers(struct netlink_parse_ctx *ctx) +{ + int i; + + for (i = 0; i <= MAX_REGS; i++) + expr_free(ctx->registers[i]); +} + +static struct expr *netlink_parse_concat_expr(struct netlink_parse_ctx *ctx, + const struct location *loc, + unsigned int reg, + unsigned int len) +{ + struct expr *concat, *expr; + unsigned int consumed; + + concat = concat_expr_alloc(loc); + while (len > 0) { + expr = netlink_get_register(ctx, loc, reg); + if (expr == NULL) { + netlink_error(ctx, loc, + "Relational expression size mismatch"); + goto err; + } + compound_expr_add(concat, expr); + + consumed = netlink_padded_len(expr->len); + assert(consumed > 0); + len -= consumed; + reg += netlink_register_space(expr->len); + } + return concat; + +err: + expr_free(concat); + return NULL; +} + +static struct expr *netlink_parse_concat_key(struct netlink_parse_ctx *ctx, + const struct location *loc, + unsigned int reg, + const struct expr *key) +{ + uint32_t type = key->dtype->type; + unsigned int n, len = key->len; + struct expr *concat, *expr; + unsigned int consumed; + + concat = concat_expr_alloc(loc); + n = div_round_up(fls(type), TYPE_BITS); + + while (len > 0) { + const struct datatype *i; + + expr = netlink_get_register(ctx, loc, reg); + if (expr == NULL) { + netlink_error(ctx, loc, + "Concat expression size mismatch"); + goto err; + } + + if (n > 0 && concat_subtype_id(type, --n)) { + i = concat_subtype_lookup(type, n); + + expr_set_type(expr, i, i->byteorder); + } + + compound_expr_add(concat, expr); + + consumed = netlink_padded_len(expr->len); + assert(consumed > 0); + len -= consumed; + reg += netlink_register_space(expr->len); + } + + return concat; + +err: + expr_free(concat); + return NULL; +} + +static struct expr *netlink_parse_concat_data(struct netlink_parse_ctx *ctx, + const struct location *loc, + unsigned int reg, + unsigned int len, + struct expr *data) +{ + struct expr *concat, *expr, *i; + + concat = concat_expr_alloc(loc); + while (len > 0) { + expr = netlink_get_register(ctx, loc, reg); + if (expr == NULL) { + netlink_error(ctx, loc, + "Relational expression size mismatch"); + goto err; + } + i = constant_expr_splice(data, expr->len); + data->len -= netlink_padding_len(expr->len); + compound_expr_add(concat, i); + + len -= netlink_padded_len(expr->len); + reg += netlink_register_space(expr->len); + expr_free(expr); + } + return concat; + +err: + expr_free(concat); + return NULL; +} + +static void netlink_parse_chain_verdict(struct netlink_parse_ctx *ctx, + const struct location *loc, + struct expr *expr, + enum nft_verdicts verdict) +{ + char chain_name[NFT_CHAIN_MAXNAMELEN] = {}; + struct chain *chain; + + expr_chain_export(expr->chain, chain_name); + chain = chain_binding_lookup(ctx->table, chain_name); + + /* Special case: 'nft list chain x y' needs to pull in implicit chains */ + if (!chain && !strncmp(chain_name, "__chain", strlen("__chain"))) { + nft_chain_cache_update(ctx->nlctx, ctx->table, chain_name); + chain = chain_binding_lookup(ctx->table, chain_name); + } + + if (chain) { + ctx->stmt = chain_stmt_alloc(loc, chain, verdict); + expr_free(expr); + } else { + ctx->stmt = verdict_stmt_alloc(loc, expr); + } +} + +static void netlink_parse_immediate(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct nft_data_delinearize nld; + enum nft_registers dreg; + struct expr *expr; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_VERDICT)) { + nld.verdict = nftnl_expr_get_u32(nle, NFTNL_EXPR_IMM_VERDICT); + if (nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_CHAIN)) { + nld.chain = nftnl_expr_get(nle, NFTNL_EXPR_IMM_CHAIN, + &nld.len); + } + } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_DATA)) { + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_IMM_DATA, &nld.len); + } + + dreg = netlink_parse_register(nle, NFTNL_EXPR_IMM_DREG); + expr = netlink_alloc_data(loc, &nld, dreg); + + if (dreg == NFT_REG_VERDICT) { + switch (expr->verdict) { + case NFT_JUMP: + netlink_parse_chain_verdict(ctx, loc, expr, NFT_JUMP); + break; + case NFT_GOTO: + netlink_parse_chain_verdict(ctx, loc, expr, NFT_GOTO); + break; + default: + ctx->stmt = verdict_stmt_alloc(loc, expr); + break; + } + } else { + netlink_set_register(ctx, dreg, expr); + } +} + +static void netlink_parse_xfrm(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + enum nft_xfrm_keys key; + struct expr *expr; + uint32_t spnum; + uint8_t dir; + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_XFRM_KEY); + dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_XFRM_DIR); + spnum = nftnl_expr_get_u32(nle, NFTNL_EXPR_XFRM_SPNUM); + expr = xfrm_expr_alloc(loc, dir, spnum, key); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_XFRM_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static enum ops netlink_parse_range_op(const struct nftnl_expr *nle) +{ + switch (nftnl_expr_get_u32(nle, NFTNL_EXPR_RANGE_OP)) { + case NFT_RANGE_EQ: + return OP_EQ; + case NFT_RANGE_NEQ: + return OP_NEQ; + default: + return OP_INVALID; + } +} + +static void netlink_parse_range(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct expr *expr, *left, *right, *from, *to; + struct nft_data_delinearize nld; + enum nft_registers sreg; + enum ops op; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_RANGE_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "Relational expression has no left hand side"); + + op = netlink_parse_range_op(nle); + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_RANGE_FROM_DATA, &nld.len); + from = netlink_alloc_value(loc, &nld); + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_RANGE_TO_DATA, &nld.len); + to = netlink_alloc_value(loc, &nld); + + right = range_expr_alloc(loc, from, to); + expr = relational_expr_alloc(loc, op, left, right); + ctx->stmt = expr_stmt_alloc(loc, expr); +} + +static enum ops netlink_parse_cmp_op(const struct nftnl_expr *nle) +{ + switch (nftnl_expr_get_u32(nle, NFTNL_EXPR_CMP_OP)) { + 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 nftnl_expr *nle) +{ + struct nft_data_delinearize nld; + enum nft_registers sreg; + struct expr *expr, *left, *right, *tmp; + enum ops op; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_CMP_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "Relational expression has no left " + "hand side"); + + op = netlink_parse_cmp_op(nle); + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_CMP_DATA, &nld.len); + right = netlink_alloc_value(loc, &nld); + + if (left->len > right->len && + expr_basetype(left) != &string_type) { + mpz_lshift_ui(right->value, left->len - right->len); + right = prefix_expr_alloc(loc, right, right->len); + right->prefix->len = left->len; + } else if (left->len > 0 && left->len < right->len) { + expr_free(left); + left = netlink_parse_concat_expr(ctx, loc, sreg, right->len); + if (left == NULL) + goto err_free; + tmp = netlink_parse_concat_data(ctx, loc, sreg, right->len, right); + if (tmp == NULL) + goto err_free; + expr_free(right); + right = tmp; + } + + expr = relational_expr_alloc(loc, op, left, right); + ctx->stmt = expr_stmt_alloc(loc, expr); + return; +err_free: + expr_free(left); + expr_free(right); +} + +static void netlink_parse_lookup(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg, dreg; + const char *name; + struct expr *expr, *left, *right; + struct set *set; + uint32_t flag; + + name = nftnl_expr_get_str(nle, NFTNL_EXPR_LOOKUP_SET); + set = set_cache_find(ctx->table, name); + if (set == NULL) + return netlink_error(ctx, loc, + "Unknown set '%s' in lookup expression", + name); + + sreg = netlink_parse_register(nle, NFTNL_EXPR_LOOKUP_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "Lookup expression has no left hand side"); + + if (left->len < set->key->len) { + expr_free(left); + left = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len); + if (left == NULL) + return; + } + + right = set_ref_expr_alloc(loc, set); + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOOKUP_DREG)) { + dreg = netlink_parse_register(nle, NFTNL_EXPR_LOOKUP_DREG); + expr = map_expr_alloc(loc, left, right); + if (dreg != NFT_REG_VERDICT) + return netlink_set_register(ctx, dreg, expr); + } else { + expr = relational_expr_alloc(loc, OP_EQ, left, right); + } + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOOKUP_FLAGS)) { + flag = nftnl_expr_get_u32(nle, NFTNL_EXPR_LOOKUP_FLAGS); + if (flag & NFT_LOOKUP_F_INV) + expr->op = OP_NEQ; + } + + ctx->stmt = expr_stmt_alloc(loc, expr); +} + +static struct expr *netlink_parse_bitwise_bool(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle, + enum nft_registers sreg, + struct expr *left) + +{ + struct nft_data_delinearize nld; + struct expr *expr, *mask, *xor, *or; + mpz_t m, x, o; + + expr = left; + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_BITWISE_MASK, &nld.len); + mask = netlink_alloc_value(loc, &nld); + mpz_init_set(m, mask->value); + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_BITWISE_XOR, &nld.len); + xor = netlink_alloc_value(loc, &nld); + 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 (left->len > 0 && mpz_scan0(m, 0) >= left->len) { + /* mask encompasses the entire value */ + expr_free(mask); + } else { + mpz_set(mask->value, m); + expr = binop_expr_alloc(loc, OP_AND, expr, mask); + expr->len = left->len; + } + + 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)) { + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_BITWISE_XOR, + &nld.len); + + or = netlink_alloc_value(loc, &nld); + 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); + + return expr; +} + +static struct expr *netlink_parse_bitwise_shift(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle, + enum ops op, + enum nft_registers sreg, + struct expr *left) +{ + struct nft_data_delinearize nld; + struct expr *expr, *right; + + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_BITWISE_DATA, &nld.len); + right = netlink_alloc_value(loc, &nld); + right->byteorder = BYTEORDER_HOST_ENDIAN; + + expr = binop_expr_alloc(loc, op, left, right); + expr->len = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_LEN) * BITS_PER_BYTE; + + return expr; +} + +static void netlink_parse_bitwise(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg, dreg; + struct expr *expr, *left; + enum nft_bitwise_ops op; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_BITWISE_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "Bitwise expression has no left " + "hand side"); + + op = nftnl_expr_get_u32(nle, NFTNL_EXPR_BITWISE_OP); + + switch (op) { + case NFT_BITWISE_BOOL: + expr = netlink_parse_bitwise_bool(ctx, loc, nle, sreg, + left); + break; + case NFT_BITWISE_LSHIFT: + expr = netlink_parse_bitwise_shift(ctx, loc, nle, OP_LSHIFT, + sreg, left); + break; + case NFT_BITWISE_RSHIFT: + expr = netlink_parse_bitwise_shift(ctx, loc, nle, OP_RSHIFT, + sreg, left); + break; + default: + BUG("invalid bitwise operation %u\n", op); + } + + dreg = netlink_parse_register(nle, NFTNL_EXPR_BITWISE_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_byteorder(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg, dreg; + struct expr *expr, *arg; + enum ops op; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_BYTEORDER_SREG); + arg = netlink_get_register(ctx, loc, sreg); + if (arg == NULL) + return netlink_error(ctx, loc, + "Byteorder expression has no left " + "hand side"); + + switch (nftnl_expr_get_u32(nle, NFTNL_EXPR_BYTEORDER_OP)) { + case NFT_BYTEORDER_NTOH: + op = OP_NTOH; + break; + case NFT_BYTEORDER_HTON: + op = OP_HTON; + break; + default: + BUG("invalid byteorder operation %u\n", + nftnl_expr_get_u32(nle, NFTNL_EXPR_BYTEORDER_OP)); + } + + expr = unary_expr_alloc(loc, op, arg); + expr->len = arg->len; + + dreg = netlink_parse_register(nle, NFTNL_EXPR_BYTEORDER_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_payload_expr(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + uint32_t base, offset, len; + struct expr *expr; + + base = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_BASE) + 1; + + if (base == NFT_PAYLOAD_TUN_HEADER + 1) + base = NFT_PAYLOAD_INNER_HEADER + 1; + + offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE; + len = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE; + + expr = payload_expr_alloc(loc, NULL, 0); + payload_init_raw(expr, base, offset, len); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_DREG); + + if (ctx->inner) + ctx->inner_reg = dreg; + + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_inner(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + const struct proto_desc *inner_desc; + const struct nftnl_expr *inner_nle; + uint32_t hdrsize, flags, type; + struct expr *expr; + + hdrsize = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_HDRSIZE); + type = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_TYPE); + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_INNER_FLAGS); + + inner_nle = nftnl_expr_get(nle, NFTNL_EXPR_INNER_EXPR, NULL); + if (!inner_nle) { + netlink_error(ctx, loc, "Could not parse inner expression"); + return; + } + + inner_desc = proto_find_inner(type, hdrsize, flags); + + ctx->inner = true; + if (netlink_parse_expr(inner_nle, ctx) < 0) { + ctx->inner = false; + return; + } + ctx->inner = false; + + expr = netlink_get_register(ctx, loc, ctx->inner_reg); + assert(expr); + + if (expr->etype == EXPR_PAYLOAD && + expr->payload.base == PROTO_BASE_INNER_HDR) { + const struct proto_hdr_template *tmpl; + unsigned int i; + + for (i = 1; i < array_size(inner_desc->templates); i++) { + tmpl = &inner_desc->templates[i]; + + if (tmpl->len == 0) + return; + + if (tmpl->offset != expr->payload.offset || + tmpl->len != expr->len) + continue; + + expr->payload.desc = inner_desc; + expr->payload.tmpl = tmpl; + break; + } + } + + switch (expr->etype) { + case EXPR_PAYLOAD: + expr->payload.inner_desc = inner_desc; + break; + case EXPR_META: + expr->meta.inner_desc = inner_desc; + break; + default: + assert(0); + break; + } + + netlink_set_register(ctx, ctx->inner_reg, expr); +} + +static void netlink_parse_payload_stmt(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg; + uint32_t base, offset, len; + struct expr *expr, *val; + struct stmt *stmt; + + base = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_BASE) + 1; + offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET) * BITS_PER_BYTE; + len = nftnl_expr_get_u32(nle, NFTNL_EXPR_PAYLOAD_LEN) * BITS_PER_BYTE; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_PAYLOAD_SREG); + val = netlink_get_register(ctx, loc, sreg); + if (val == NULL) + return netlink_error(ctx, loc, + "payload statement has no expression"); + + expr = payload_expr_alloc(loc, NULL, 0); + payload_init_raw(expr, base, offset, len); + + stmt = payload_stmt_alloc(loc, expr, val); + rule_stmt_append(ctx->rule, stmt); +} + +static void netlink_parse_payload(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + if (nftnl_expr_is_set(nle, NFTNL_EXPR_PAYLOAD_DREG)) + netlink_parse_payload_expr(ctx, loc, nle); + else + netlink_parse_payload_stmt(ctx, loc, nle); +} + +static void netlink_parse_exthdr(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + uint32_t offset, len, flags; + enum nft_registers dreg; + enum nft_exthdr_op op; + uint8_t type; + struct expr *expr; + + type = nftnl_expr_get_u8(nle, NFTNL_EXPR_EXTHDR_TYPE); + offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET) * BITS_PER_BYTE; + len = nftnl_expr_get_u32(nle, NFTNL_EXPR_EXTHDR_LEN) * BITS_PER_BYTE; + op = nftnl_expr_get_u32(nle, NFTNL_EXPR_EXTHDR_OP); + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_EXTHDR_FLAGS); + + expr = exthdr_expr_alloc(loc, NULL, 0); + exthdr_init_raw(expr, type, offset, len, op, flags); + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_EXTHDR_DREG)) { + dreg = netlink_parse_register(nle, NFTNL_EXPR_EXTHDR_DREG); + netlink_set_register(ctx, dreg, expr); + } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_EXTHDR_SREG)) { + enum nft_registers sreg; + struct stmt *stmt; + struct expr *val; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_EXTHDR_SREG); + val = netlink_get_register(ctx, loc, sreg); + if (val == NULL) { + expr_free(expr); + return netlink_error(ctx, loc, + "exthdr statement has no expression"); + } + + expr_set_type(val, expr->dtype, expr->byteorder); + + stmt = exthdr_stmt_alloc(loc, expr, val); + rule_stmt_append(ctx->rule, stmt); + } else { + struct stmt *stmt = optstrip_stmt_alloc(loc, expr); + + rule_stmt_append(ctx->rule, stmt); + } +} + +static void netlink_parse_hash(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg, dreg; + struct expr *expr, *hexpr; + uint32_t mod, seed, len, offset; + enum nft_hash_types type; + bool seed_set; + + type = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_TYPE); + offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_OFFSET); + seed_set = nftnl_expr_is_set(nle, NFTNL_EXPR_HASH_SEED); + seed = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_SEED); + mod = nftnl_expr_get_u32(nle, NFTNL_EXPR_HASH_MODULUS); + + expr = hash_expr_alloc(loc, mod, seed_set, seed, offset, type); + + if (type != NFT_HASH_SYM) { + sreg = netlink_parse_register(nle, NFTNL_EXPR_HASH_SREG); + hexpr = netlink_get_register(ctx, loc, sreg); + + if (hexpr == NULL) { + netlink_error(ctx, loc, + "hash statement has no expression"); + goto out_err; + } + len = nftnl_expr_get_u32(nle, + NFTNL_EXPR_HASH_LEN) * BITS_PER_BYTE; + if (hexpr->len < len) { + expr_free(hexpr); + hexpr = netlink_parse_concat_expr(ctx, loc, sreg, len); + if (hexpr == NULL) + goto out_err; + } + expr->hash.expr = hexpr; + } + + dreg = netlink_parse_register(nle, NFTNL_EXPR_HASH_DREG); + netlink_set_register(ctx, dreg, expr); + return; +out_err: + expr_free(expr); +} + +static void netlink_parse_fib(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + struct expr *expr; + uint32_t flags, result; + + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_FIB_FLAGS); + result = nftnl_expr_get_u32(nle, NFTNL_EXPR_FIB_RESULT); + + expr = fib_expr_alloc(loc, flags, result); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_FIB_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_meta_expr(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + uint32_t key; + struct expr *expr; + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_META_KEY); + expr = meta_expr_alloc(loc, key); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_META_DREG); + if (ctx->inner) + ctx->inner_reg = dreg; + + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_socket(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + uint32_t key, level; + struct expr * expr; + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_KEY); + level = nftnl_expr_get_u32(nle, NFTNL_EXPR_SOCKET_LEVEL); + expr = socket_expr_alloc(loc, key, level); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_SOCKET_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_osf(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + struct expr *expr; + uint32_t flags; + uint8_t ttl; + + ttl = nftnl_expr_get_u8(nle, NFTNL_EXPR_OSF_TTL); + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_OSF_FLAGS); + expr = osf_expr_alloc(loc, ttl, flags); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_OSF_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_meta_stmt(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg; + uint32_t key; + struct stmt *stmt; + struct expr *expr; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_META_SREG); + expr = netlink_get_register(ctx, loc, sreg); + if (expr == NULL) + return netlink_error(ctx, loc, + "meta statement has no expression"); + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_META_KEY); + stmt = meta_stmt_alloc(loc, key, expr); + + if (stmt->meta.tmpl) + expr_set_type(expr, stmt->meta.tmpl->dtype, stmt->meta.tmpl->byteorder); + + ctx->stmt = stmt; +} + +static void netlink_parse_meta(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + if (nftnl_expr_is_set(nle, NFTNL_EXPR_META_DREG)) + netlink_parse_meta_expr(ctx, loc, nle); + else + netlink_parse_meta_stmt(ctx, loc, nle); +} + +static void netlink_parse_rt(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers dreg; + uint32_t key; + struct expr *expr; + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_RT_KEY); + expr = rt_expr_alloc(loc, key, false); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_RT_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_numgen(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + uint32_t type, until, offset; + enum nft_registers dreg; + struct expr *expr; + + type = nftnl_expr_get_u32(nle, NFTNL_EXPR_NG_TYPE); + until = nftnl_expr_get_u32(nle, NFTNL_EXPR_NG_MODULUS); + offset = nftnl_expr_get_u32(nle, NFTNL_EXPR_NG_OFFSET); + + expr = numgen_expr_alloc(loc, type, until, offset); + dreg = netlink_parse_register(nle, NFTNL_EXPR_NG_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_notrack(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + ctx->stmt = notrack_stmt_alloc(loc); +} + +static void netlink_parse_flow_offload(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + const char *table_name; + + table_name = xstrdup(nftnl_expr_get_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME)); + ctx->stmt = flow_offload_stmt_alloc(loc, table_name); +} + +static void netlink_parse_ct_stmt(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers sreg; + uint32_t key; + struct stmt *stmt; + struct expr *expr; + int8_t dir = -1; + + sreg = netlink_parse_register(nle, NFTNL_EXPR_CT_SREG); + expr = netlink_get_register(ctx, loc, sreg); + if (expr == NULL) + return netlink_error(ctx, loc, + "ct statement has no expression"); + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_CT_DIR)) + dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_CT_DIR); + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_CT_KEY); + stmt = ct_stmt_alloc(loc, key, dir, expr); + expr_set_type(expr, stmt->ct.tmpl->dtype, stmt->ct.tmpl->byteorder); + + ctx->stmt = stmt; +} + +static void netlink_parse_ct_expr(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct expr *expr = NULL; + enum nft_registers dreg; + int8_t dir = -1; + uint32_t key; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_CT_DIR)) + dir = nftnl_expr_get_u8(nle, NFTNL_EXPR_CT_DIR); + + key = nftnl_expr_get_u32(nle, NFTNL_EXPR_CT_KEY); + expr = ct_expr_alloc(loc, key, dir); + + dreg = netlink_parse_register(nle, NFTNL_EXPR_CT_DREG); + netlink_set_register(ctx, dreg, expr); +} + +static void netlink_parse_ct(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + if (nftnl_expr_is_set(nle, NFTNL_EXPR_CT_DREG)) + netlink_parse_ct_expr(ctx, loc, nle); + else + netlink_parse_ct_stmt(ctx, loc, nle); +} + +static void netlink_parse_connlimit(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = connlimit_stmt_alloc(loc); + stmt->connlimit.count = + nftnl_expr_get_u32(nle, NFTNL_EXPR_CONNLIMIT_COUNT); + stmt->connlimit.flags = + nftnl_expr_get_u32(nle, NFTNL_EXPR_CONNLIMIT_FLAGS); + + ctx->stmt = stmt; +} + +static void netlink_parse_counter(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = counter_stmt_alloc(loc); + stmt->counter.packets = nftnl_expr_get_u64(nle, NFTNL_EXPR_CTR_PACKETS); + stmt->counter.bytes = nftnl_expr_get_u64(nle, NFTNL_EXPR_CTR_BYTES); + + ctx->stmt = stmt; +} + +static void netlink_parse_last(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = last_stmt_alloc(loc); + stmt->last.used = nftnl_expr_get_u64(nle, NFTNL_EXPR_LAST_MSECS); + stmt->last.set = nftnl_expr_get_u32(nle, NFTNL_EXPR_LAST_SET); + + ctx->stmt = stmt; +} + +static void netlink_parse_log(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + const char *prefix; + + stmt = log_stmt_alloc(loc); + prefix = nftnl_expr_get_str(nle, NFTNL_EXPR_LOG_PREFIX); + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_PREFIX)) { + stmt->log.prefix = constant_expr_alloc(&internal_location, + &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen(prefix) + 1) * BITS_PER_BYTE, + prefix); + stmt->log.flags |= STMT_LOG_PREFIX; + } + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_GROUP)) { + stmt->log.group = nftnl_expr_get_u16(nle, NFTNL_EXPR_LOG_GROUP); + stmt->log.flags |= STMT_LOG_GROUP; + } + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_SNAPLEN)) { + stmt->log.snaplen = + nftnl_expr_get_u32(nle, NFTNL_EXPR_LOG_SNAPLEN); + stmt->log.flags |= STMT_LOG_SNAPLEN; + } + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_QTHRESHOLD)) { + stmt->log.qthreshold = + nftnl_expr_get_u16(nle, NFTNL_EXPR_LOG_QTHRESHOLD); + stmt->log.flags |= STMT_LOG_QTHRESHOLD; + } + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_LEVEL)) { + stmt->log.level = + nftnl_expr_get_u32(nle, NFTNL_EXPR_LOG_LEVEL); + stmt->log.flags |= STMT_LOG_LEVEL; + } + if (nftnl_expr_is_set(nle, NFTNL_EXPR_LOG_FLAGS)) { + stmt->log.logflags = + nftnl_expr_get_u32(nle, NFTNL_EXPR_LOG_FLAGS); + } + + ctx->stmt = stmt; +} + +static void netlink_parse_limit(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = limit_stmt_alloc(loc); + stmt->limit.rate = nftnl_expr_get_u64(nle, NFTNL_EXPR_LIMIT_RATE); + stmt->limit.unit = nftnl_expr_get_u64(nle, NFTNL_EXPR_LIMIT_UNIT); + stmt->limit.type = nftnl_expr_get_u32(nle, NFTNL_EXPR_LIMIT_TYPE); + stmt->limit.burst = nftnl_expr_get_u32(nle, NFTNL_EXPR_LIMIT_BURST); + stmt->limit.flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_LIMIT_FLAGS); + + ctx->stmt = stmt; +} + +static void netlink_parse_quota(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = quota_stmt_alloc(loc); + stmt->quota.bytes = nftnl_expr_get_u64(nle, NFTNL_EXPR_QUOTA_BYTES); + stmt->quota.used = + nftnl_expr_get_u64(nle, NFTNL_EXPR_QUOTA_CONSUMED); + stmt->quota.flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_QUOTA_FLAGS); + + ctx->stmt = stmt; +} + +static void netlink_parse_reject(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *expr) +{ + struct stmt *stmt; + uint8_t icmp_code; + + stmt = reject_stmt_alloc(loc); + stmt->reject.type = nftnl_expr_get_u32(expr, NFTNL_EXPR_REJECT_TYPE); + icmp_code = nftnl_expr_get_u8(expr, NFTNL_EXPR_REJECT_CODE); + stmt->reject.icmp_code = icmp_code; + stmt->reject.expr = constant_expr_alloc(loc, &integer_type, + BYTEORDER_HOST_ENDIAN, 8, + &icmp_code); + ctx->stmt = stmt; +} + +static bool is_nat_addr_map(const struct expr *addr, uint8_t family, + struct stmt *stmt) +{ + const struct expr *mappings, *data; + const struct set *set; + + if (!addr || + expr_ops(addr)->type != EXPR_MAP) + return false; + + mappings = addr->right; + if (expr_ops(mappings)->type != EXPR_SET_REF) + return false; + + set = mappings->set; + data = set->data; + + if (!(data->flags & EXPR_F_INTERVAL)) + return false; + + stmt->nat.family = family; + + /* if we're dealing with an address:address map, + * the length will be bit_sizeof(addr) + 32 (one register). + */ + switch (family) { + case NFPROTO_IPV4: + if (data->len == 32 + 32) { + stmt->nat.type_flags |= STMT_NAT_F_INTERVAL; + return true; + } else if (data->len == 32 + 32 + 32 + 32) { + stmt->nat.type_flags |= STMT_NAT_F_INTERVAL | + STMT_NAT_F_CONCAT; + return true; + } + break; + case NFPROTO_IPV6: + if (data->len == 128 + 128) { + stmt->nat.type_flags |= STMT_NAT_F_INTERVAL; + return true; + } else if (data->len == 128 + 32 + 128 + 32) { + stmt->nat.type_flags |= STMT_NAT_F_INTERVAL | + STMT_NAT_F_CONCAT; + return true; + } + } + + return false; +} + +static bool is_nat_proto_map(const struct expr *addr, uint8_t family) +{ + const struct expr *mappings, *data; + const struct set *set; + + if (!addr || + expr_ops(addr)->type != EXPR_MAP) + return false; + + mappings = addr->right; + if (expr_ops(mappings)->type != EXPR_SET_REF) + return false; + + set = mappings->set; + data = set->data; + + /* if we're dealing with an address:inet_service map, + * the length will be bit_sizeof(addr) + 32 (one register). + */ + switch (family) { + case NFPROTO_IPV4: + return data->len == 32 + 32; + case NFPROTO_IPV6: + return data->len == 128 + 32; + } + + return false; +} + +static void netlink_parse_nat(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + struct expr *addr, *proto; + enum nft_registers reg1, reg2; + int family; + + stmt = nat_stmt_alloc(loc, + nftnl_expr_get_u32(nle, NFTNL_EXPR_NAT_TYPE)); + + family = nftnl_expr_get_u32(nle, NFTNL_EXPR_NAT_FAMILY); + + if (ctx->table->handle.family == NFPROTO_INET) + stmt->nat.family = family; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_NAT_FLAGS)) + stmt->nat.flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_NAT_FLAGS); + + if (stmt->nat.flags & NF_NAT_RANGE_NETMAP) + stmt->nat.type_flags |= STMT_NAT_F_PREFIX; + + addr = NULL; + reg1 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MIN); + if (reg1) { + addr = netlink_get_register(ctx, loc, reg1); + if (addr == NULL) { + netlink_error(ctx, loc, + "NAT statement has no address expression"); + goto out_err; + } + + if (family == NFPROTO_IPV4) + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + else + expr_set_type(addr, &ip6addr_type, + BYTEORDER_BIG_ENDIAN); + stmt->nat.addr = addr; + } + + if (is_nat_addr_map(addr, family, stmt)) { + stmt->nat.family = family; + ctx->stmt = stmt; + return; + } + + reg2 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX); + if (reg2 && reg2 != reg1) { + addr = netlink_get_register(ctx, loc, reg2); + if (addr == NULL) { + netlink_error(ctx, loc, + "NAT statement has no address expression"); + goto out_err; + } + + if (family == NFPROTO_IPV4) + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + else + expr_set_type(addr, &ip6addr_type, + BYTEORDER_BIG_ENDIAN); + if (stmt->nat.addr != NULL) { + addr = range_expr_alloc(loc, stmt->nat.addr, addr); + addr = range_expr_to_prefix(addr); + } + stmt->nat.addr = addr; + } + + if (is_nat_proto_map(addr, family)) { + stmt->nat.family = family; + stmt->nat.type_flags |= STMT_NAT_F_CONCAT; + ctx->stmt = stmt; + return; + } + + reg1 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_PROTO_MIN); + if (reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) { + netlink_error(ctx, loc, + "NAT statement has no proto expression"); + goto out_err; + } + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.proto = proto; + } + + reg2 = netlink_parse_register(nle, NFTNL_EXPR_NAT_REG_PROTO_MAX); + if (reg2 && reg2 != reg1) { + proto = netlink_get_register(ctx, loc, reg2); + if (proto == NULL) { + netlink_error(ctx, loc, + "NAT statement has no proto expression"); + goto out_err; + } + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + if (stmt->nat.proto != NULL) + proto = range_expr_alloc(loc, stmt->nat.proto, proto); + stmt->nat.proto = proto; + } + + ctx->stmt = stmt; + return; +out_err: + stmt_free(stmt); +} + +static void netlink_parse_synproxy(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + + stmt = synproxy_stmt_alloc(loc); + stmt->synproxy.mss = nftnl_expr_get_u16(nle, NFTNL_EXPR_SYNPROXY_MSS); + stmt->synproxy.wscale = nftnl_expr_get_u8(nle, + NFTNL_EXPR_SYNPROXY_WSCALE); + stmt->synproxy.flags = nftnl_expr_get_u32(nle, + NFTNL_EXPR_SYNPROXY_FLAGS); + + ctx->stmt = stmt; +} + +static void netlink_parse_tproxy(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + struct expr *addr, *port; + enum nft_registers reg; + + stmt = tproxy_stmt_alloc(loc); + stmt->tproxy.family = nftnl_expr_get_u32(nle, NFTNL_EXPR_TPROXY_FAMILY); + stmt->tproxy.table_family = ctx->table->handle.family; + + reg = netlink_parse_register(nle, NFTNL_EXPR_TPROXY_REG_ADDR); + if (reg) { + addr = netlink_get_register(ctx, loc, reg); + if (addr == NULL) + goto err; + + switch (stmt->tproxy.family) { + case NFPROTO_IPV4: + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + break; + case NFPROTO_IPV6: + expr_set_type(addr, &ip6addr_type, BYTEORDER_BIG_ENDIAN); + break; + default: + netlink_error(ctx, loc, + "tproxy address must be IPv4 or IPv6"); + goto err; + } + stmt->tproxy.addr = addr; + } + + reg = netlink_parse_register(nle, NFTNL_EXPR_TPROXY_REG_PORT); + if (reg) { + port = netlink_get_register(ctx, loc, reg); + if (port == NULL) + goto err; + expr_set_type(port, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->tproxy.port = port; + } + + ctx->stmt = stmt; + return; +err: + stmt_free(stmt); +} + +static void netlink_parse_masq(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers reg1, reg2; + struct expr *proto; + struct stmt *stmt; + uint32_t flags = 0; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_MASQ_FLAGS)) + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_MASQ_FLAGS); + + stmt = nat_stmt_alloc(loc, NFT_NAT_MASQ); + stmt->nat.flags = flags; + + reg1 = netlink_parse_register(nle, NFTNL_EXPR_MASQ_REG_PROTO_MIN); + if (reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) { + netlink_error(ctx, loc, + "MASQUERADE statement has no proto expression"); + goto out_err; + } + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.proto = proto; + } + + reg2 = netlink_parse_register(nle, NFTNL_EXPR_MASQ_REG_PROTO_MAX); + if (reg2 && reg2 != reg1) { + proto = netlink_get_register(ctx, loc, reg2); + if (proto == NULL) { + netlink_error(ctx, loc, + "MASQUERADE statement has no proto expression"); + goto out_err; + } + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + if (stmt->nat.proto != NULL) + proto = range_expr_alloc(loc, stmt->nat.proto, proto); + stmt->nat.proto = proto; + } + + ctx->stmt = stmt; + return; +out_err: + stmt_free(stmt); +} + +static void netlink_parse_redir(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct stmt *stmt; + struct expr *proto; + enum nft_registers reg1, reg2; + uint32_t flags; + + stmt = nat_stmt_alloc(loc, NFT_NAT_REDIR); + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_REDIR_FLAGS)) { + flags = nftnl_expr_get_u32(nle, NFTNL_EXPR_REDIR_FLAGS); + stmt->nat.flags = flags; + } + + reg1 = netlink_parse_register(nle, NFTNL_EXPR_REDIR_REG_PROTO_MIN); + if (reg1) { + proto = netlink_get_register(ctx, loc, reg1); + if (proto == NULL) { + netlink_error(ctx, loc, + "redirect statement has no proto expression"); + goto out_err; + } + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + stmt->nat.proto = proto; + } + + reg2 = netlink_parse_register(nle, NFTNL_EXPR_REDIR_REG_PROTO_MAX); + if (reg2 && reg2 != reg1) { + proto = netlink_get_register(ctx, loc, reg2); + if (proto == NULL) { + netlink_error(ctx, loc, + "redirect statement has no proto expression"); + goto out_err; + } + + expr_set_type(proto, &inet_service_type, BYTEORDER_BIG_ENDIAN); + if (stmt->nat.proto != NULL) + proto = range_expr_alloc(loc, stmt->nat.proto, + proto); + stmt->nat.proto = proto; + } + + ctx->stmt = stmt; + return; +out_err: + stmt_free(stmt); +} + +static void netlink_parse_dup(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers reg1, reg2; + struct expr *addr, *dev; + struct stmt *stmt; + + stmt = dup_stmt_alloc(loc); + + reg1 = netlink_parse_register(nle, NFTNL_EXPR_DUP_SREG_ADDR); + if (reg1) { + addr = netlink_get_register(ctx, loc, reg1); + if (addr == NULL) { + netlink_error(ctx, loc, + "DUP statement has no destination expression"); + goto out_err; + } + + switch (ctx->table->handle.family) { + case NFPROTO_IPV4: + expr_set_type(addr, &ipaddr_type, BYTEORDER_BIG_ENDIAN); + break; + case NFPROTO_IPV6: + expr_set_type(addr, &ip6addr_type, + BYTEORDER_BIG_ENDIAN); + break; + } + stmt->dup.to = addr; + } + + reg2 = netlink_parse_register(nle, NFTNL_EXPR_DUP_SREG_DEV); + if (reg2) { + dev = netlink_get_register(ctx, loc, reg2); + if (dev == NULL) { + netlink_error(ctx, loc, + "DUP statement has no output expression"); + goto out_err; + } + + expr_set_type(dev, &ifindex_type, BYTEORDER_HOST_ENDIAN); + if (stmt->dup.to == NULL) + stmt->dup.to = dev; + else + stmt->dup.dev = dev; + } + + ctx->stmt = stmt; + return; +out_err: + stmt_free(stmt); +} + +static void netlink_parse_fwd(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + enum nft_registers reg1, reg2; + struct expr *dev, *addr; + struct stmt *stmt; + + stmt = fwd_stmt_alloc(loc); + + reg1 = netlink_parse_register(nle, NFTNL_EXPR_FWD_SREG_DEV); + if (reg1) { + dev = netlink_get_register(ctx, loc, reg1); + if (dev == NULL) { + netlink_error(ctx, loc, + "fwd statement has no output expression"); + goto out_err; + } + + expr_set_type(dev, &ifindex_type, BYTEORDER_HOST_ENDIAN); + stmt->fwd.dev = dev; + } + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_FWD_NFPROTO)) { + stmt->fwd.family = + nftnl_expr_get_u32(nle, NFTNL_EXPR_FWD_NFPROTO); + } + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_FWD_SREG_ADDR)) { + reg2 = netlink_parse_register(nle, NFTNL_EXPR_FWD_SREG_ADDR); + if (reg2) { + addr = netlink_get_register(ctx, loc, reg2); + if (addr == NULL) { + netlink_error(ctx, loc, + "fwd statement has no output expression"); + goto out_err; + } + + switch (stmt->fwd.family) { + case AF_INET: + expr_set_type(addr, &ipaddr_type, + BYTEORDER_BIG_ENDIAN); + break; + case AF_INET6: + expr_set_type(addr, &ip6addr_type, + BYTEORDER_BIG_ENDIAN); + break; + default: + netlink_error(ctx, loc, + "fwd statement has no family"); + goto out_err; + } + stmt->fwd.addr = addr; + } + } + + ctx->stmt = stmt; + return; +out_err: + stmt_free(stmt); +} + +static void netlink_parse_queue(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct expr *expr; + uint16_t flags; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_QUEUE_SREG_QNUM)) { + enum nft_registers reg = netlink_parse_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM); + + expr = netlink_get_register(ctx, loc, reg); + if (!expr) { + netlink_error(ctx, loc, "queue statement has no sreg expression"); + return; + } + } else { + uint16_t total = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_TOTAL); + uint16_t num = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_NUM); + + expr = constant_expr_alloc(loc, &integer_type, + BYTEORDER_HOST_ENDIAN, 16, &num); + + if (total > 1) { + struct expr *high; + + total += num - 1; + high = constant_expr_alloc(loc, &integer_type, + BYTEORDER_HOST_ENDIAN, 16, &total); + expr = range_expr_alloc(loc, expr, high); + } + } + + flags = nftnl_expr_get_u16(nle, NFTNL_EXPR_QUEUE_FLAGS); + ctx->stmt = queue_stmt_alloc(loc, expr, flags); +} + +struct dynset_parse_ctx { + struct netlink_parse_ctx *nlctx; + const struct location *loc; + struct list_head stmt_list; +}; + +static int dynset_parse_expressions(struct nftnl_expr *e, void *data) +{ + struct dynset_parse_ctx *dynset_parse_ctx = data; + struct netlink_parse_ctx *ctx = dynset_parse_ctx->nlctx; + const struct location *loc = dynset_parse_ctx->loc; + struct stmt *stmt; + + if (netlink_parse_expr(e, ctx) < 0 || !ctx->stmt) { + netlink_error(ctx, loc, "Could not parse dynset stmt"); + return -1; + } + stmt = ctx->stmt; + + list_add_tail(&stmt->list, &dynset_parse_ctx->stmt_list); + + return 0; +} + +static void netlink_parse_dynset(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + struct dynset_parse_ctx dynset_parse_ctx = { + .nlctx = ctx, + .loc = loc, + }; + struct expr *expr, *expr_data = NULL; + enum nft_registers sreg, sreg_data; + struct stmt *stmt, *dstmt, *next; + const struct nftnl_expr *dnle; + struct set *set; + const char *name; + + init_list_head(&dynset_parse_ctx.stmt_list); + + name = nftnl_expr_get_str(nle, NFTNL_EXPR_DYNSET_SET_NAME); + set = set_cache_find(ctx->table, name); + if (set == NULL) + return netlink_error(ctx, loc, + "Unknown set '%s' in dynset statement", + name); + + sreg = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY); + expr = netlink_get_register(ctx, loc, sreg); + if (expr == NULL) + return netlink_error(ctx, loc, + "Dynset statement has no key expression"); + + if (expr->len < set->key->len) { + expr_free(expr); + expr = netlink_parse_concat_key(ctx, loc, sreg, set->key); + if (expr == NULL) + return; + } else if (expr->dtype == &invalid_type) { + expr_set_type(expr, datatype_get(set->key->dtype), set->key->byteorder); + } + + expr = set_elem_expr_alloc(&expr->location, expr); + expr->timeout = nftnl_expr_get_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT); + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPR)) { + dstmt = NULL; + dnle = nftnl_expr_get(nle, NFTNL_EXPR_DYNSET_EXPR, NULL); + if (dnle != NULL) { + if (netlink_parse_expr(dnle, ctx) < 0) + goto out_err; + if (ctx->stmt == NULL) { + netlink_error(ctx, loc, + "Could not parse dynset stmt"); + goto out_err; + } + dstmt = ctx->stmt; + list_add_tail(&dstmt->list, + &dynset_parse_ctx.stmt_list); + } + } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS)) { + if (nftnl_expr_expr_foreach(nle, dynset_parse_expressions, + &dynset_parse_ctx) < 0) + goto out_err; + } + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_DYNSET_SREG_DATA)) { + sreg_data = netlink_parse_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA); + expr_data = netlink_get_register(ctx, loc, sreg_data); + + if (expr_data && expr_data->len < set->data->len) { + expr_free(expr_data); + expr_data = netlink_parse_concat_expr(ctx, loc, sreg_data, set->data->len); + if (expr_data == NULL) + netlink_error(ctx, loc, + "Could not parse dynset map data expressions"); + } + } + + if (expr_data != NULL) { + expr_set_type(expr_data, set->data->dtype, set->data->byteorder); + stmt = map_stmt_alloc(loc); + stmt->map.set = set_ref_expr_alloc(loc, set); + stmt->map.key = expr; + stmt->map.data = expr_data; + stmt->map.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP); + list_splice_tail(&dynset_parse_ctx.stmt_list, + &stmt->map.stmt_list); + } else { + if (!list_empty(&dynset_parse_ctx.stmt_list) && + set_is_anonymous(set->flags)) { + stmt = meter_stmt_alloc(loc); + stmt->meter.set = set_ref_expr_alloc(loc, set); + stmt->meter.key = expr; + stmt->meter.stmt = list_first_entry(&dynset_parse_ctx.stmt_list, + struct stmt, list); + stmt->meter.size = set->desc.size; + } else { + stmt = set_stmt_alloc(loc); + stmt->set.set = set_ref_expr_alloc(loc, set); + stmt->set.op = nftnl_expr_get_u32(nle, NFTNL_EXPR_DYNSET_OP); + stmt->set.key = expr; + list_splice_tail(&dynset_parse_ctx.stmt_list, + &stmt->set.stmt_list); + } + } + + ctx->stmt = stmt; + return; +out_err: + list_for_each_entry_safe(dstmt, next, &dynset_parse_ctx.stmt_list, list) + stmt_free(dstmt); + + expr_free(expr); +} + +static void netlink_parse_objref(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + uint32_t type = nftnl_expr_get_u32(nle, NFTNL_EXPR_OBJREF_IMM_TYPE); + struct expr *expr; + struct stmt *stmt; + + if (nftnl_expr_is_set(nle, NFTNL_EXPR_OBJREF_IMM_NAME)) { + struct nft_data_delinearize nld; + + type = nftnl_expr_get_u32(nle, NFTNL_EXPR_OBJREF_IMM_TYPE); + nld.value = nftnl_expr_get(nle, NFTNL_EXPR_OBJREF_IMM_NAME, + &nld.len); + expr = netlink_alloc_value(&netlink_location, &nld); + datatype_set(expr, &string_type); + expr->byteorder = BYTEORDER_HOST_ENDIAN; + } else if (nftnl_expr_is_set(nle, NFTNL_EXPR_OBJREF_SET_SREG)) { + struct expr *left, *right; + enum nft_registers sreg; + const char *name; + struct set *set; + + name = nftnl_expr_get_str(nle, NFTNL_EXPR_OBJREF_SET_NAME); + set = set_cache_find(ctx->table, name); + if (set == NULL) + return netlink_error(ctx, loc, + "Unknown set '%s' in objref expression", + name); + + sreg = netlink_parse_register(nle, NFTNL_EXPR_OBJREF_SET_SREG); + left = netlink_get_register(ctx, loc, sreg); + if (left == NULL) + return netlink_error(ctx, loc, + "objref expression has no left hand side"); + + if (left->len < set->key->len) { + expr_free(left); + left = netlink_parse_concat_expr(ctx, loc, sreg, set->key->len); + if (left == NULL) + return; + } + + right = set_ref_expr_alloc(loc, set); + expr = map_expr_alloc(loc, left, right); + expr_set_type(expr, &string_type, BYTEORDER_HOST_ENDIAN); + type = set->objtype; + } else { + netlink_error(ctx, loc, "unknown objref expression type %u", + type); + return; + } + + stmt = objref_stmt_alloc(loc); + stmt->objref.type = type; + stmt->objref.expr = expr; + ctx->stmt = stmt; +} + +struct expr_handler { + const char *name; + void (*parse)(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle); +}; + +static const struct expr_handler netlink_parsers[] = { + { .name = "immediate", .parse = netlink_parse_immediate }, + { .name = "cmp", .parse = netlink_parse_cmp }, + { .name = "lookup", .parse = netlink_parse_lookup }, + { .name = "bitwise", .parse = netlink_parse_bitwise }, + { .name = "byteorder", .parse = netlink_parse_byteorder }, + { .name = "payload", .parse = netlink_parse_payload }, + { .name = "inner", .parse = netlink_parse_inner }, + { .name = "exthdr", .parse = netlink_parse_exthdr }, + { .name = "meta", .parse = netlink_parse_meta }, + { .name = "socket", .parse = netlink_parse_socket }, + { .name = "osf", .parse = netlink_parse_osf }, + { .name = "rt", .parse = netlink_parse_rt }, + { .name = "ct", .parse = netlink_parse_ct }, + { .name = "connlimit", .parse = netlink_parse_connlimit }, + { .name = "counter", .parse = netlink_parse_counter }, + { .name = "last", .parse = netlink_parse_last }, + { .name = "log", .parse = netlink_parse_log }, + { .name = "limit", .parse = netlink_parse_limit }, + { .name = "range", .parse = netlink_parse_range }, + { .name = "reject", .parse = netlink_parse_reject }, + { .name = "nat", .parse = netlink_parse_nat }, + { .name = "tproxy", .parse = netlink_parse_tproxy }, + { .name = "notrack", .parse = netlink_parse_notrack }, + { .name = "masq", .parse = netlink_parse_masq }, + { .name = "redir", .parse = netlink_parse_redir }, + { .name = "dup", .parse = netlink_parse_dup }, + { .name = "queue", .parse = netlink_parse_queue }, + { .name = "dynset", .parse = netlink_parse_dynset }, + { .name = "fwd", .parse = netlink_parse_fwd }, + { .name = "target", .parse = netlink_parse_target }, + { .name = "match", .parse = netlink_parse_match }, + { .name = "objref", .parse = netlink_parse_objref }, + { .name = "quota", .parse = netlink_parse_quota }, + { .name = "numgen", .parse = netlink_parse_numgen }, + { .name = "hash", .parse = netlink_parse_hash }, + { .name = "fib", .parse = netlink_parse_fib }, + { .name = "tcpopt", .parse = netlink_parse_exthdr }, + { .name = "flow_offload", .parse = netlink_parse_flow_offload }, + { .name = "xfrm", .parse = netlink_parse_xfrm }, + { .name = "synproxy", .parse = netlink_parse_synproxy }, +}; + +static int netlink_parse_expr(const struct nftnl_expr *nle, + struct netlink_parse_ctx *ctx) +{ + const char *type = nftnl_expr_get_str(nle, NFTNL_EXPR_NAME); + struct location loc; + unsigned int i; + + memset(&loc, 0, sizeof(loc)); + loc.indesc = &indesc_netlink; + loc.nle = nle; + + for (i = 0; i < array_size(netlink_parsers); i++) { + if (strcmp(type, netlink_parsers[i].name)) + continue; + + netlink_parsers[i].parse(ctx, &loc, nle); + + return 0; + } + netlink_error(ctx, &loc, "unknown expression type '%s'", type); + + return 0; +} + +static int netlink_parse_rule_expr(struct nftnl_expr *nle, void *arg) +{ + struct netlink_parse_ctx *ctx = arg; + int err; + + err = netlink_parse_expr(nle, ctx); + if (err < 0) + return err; + if (ctx->stmt != NULL) { + rule_stmt_append(ctx->rule, ctx->stmt); + ctx->stmt = NULL; + } + return 0; +} + +struct stmt *netlink_parse_set_expr(const struct set *set, + const struct nft_cache *cache, + const struct nftnl_expr *nle) +{ + struct netlink_parse_ctx ctx, *pctx = &ctx; + struct handle h = {}; + + handle_merge(&h, &set->handle); + pctx->rule = rule_alloc(&netlink_location, &h); + pctx->table = table_cache_find(&cache->table_cache, + set->handle.table.name, + set->handle.family); + assert(pctx->table != NULL); + + if (netlink_parse_expr(nle, pctx) < 0) + return NULL; + + init_list_head(&pctx->rule->stmts); + rule_free(pctx->rule); + + return pctx->stmt; +} + +static bool meta_outer_may_dependency_kill(struct rule_pp_ctx *ctx, + const struct expr *expr) +{ + struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx); + struct stmt *stmt = dl_outer->pdctx.pdeps[expr->payload.inner_desc->base]; + struct expr *dep; + uint8_t l4proto; + + if (!stmt) + return false; + + dep = stmt->expr; + + if (dep->left->meta.key != NFT_META_L4PROTO) + return false; + + l4proto = mpz_get_uint8(dep->right->value); + + switch (l4proto) { + case IPPROTO_GRE: + if (expr->payload.inner_desc == &proto_gre || + expr->payload.inner_desc == &proto_gretap) + return true; + break; + default: + break; + } + + return false; +} + +static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp); + +static void payload_match_expand(struct rule_pp_ctx *ctx, + struct expr *expr, + struct expr *payload) +{ + struct expr *left = payload, *right = expr->right, *tmp; + struct list_head list = LIST_HEAD_INIT(list); + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + enum proto_bases base = left->payload.base; + struct expr *nexpr = NULL; + struct stmt *nstmt; + + payload_expr_expand(&list, left, &dl->pctx); + + list_for_each_entry(left, &list, list) { + tmp = constant_expr_splice(right, left->len); + expr_set_type(tmp, left->dtype, left->byteorder); + + if (left->payload.tmpl && (left->len < left->payload.tmpl->len)) { + mpz_lshift_ui(tmp->value, left->payload.tmpl->len - left->len); + tmp->len = left->payload.tmpl->len; + tmp = prefix_expr_alloc(&tmp->location, tmp, left->len); + } + + nexpr = relational_expr_alloc(&expr->location, expr->op, + left, tmp); + if (expr->op == OP_EQ) + relational_expr_pctx_update(&dl->pctx, nexpr); + + nstmt = expr_stmt_alloc(&ctx->stmt->location, nexpr); + list_add_tail(&nstmt->list, &ctx->stmt->list); + + assert(left->etype == EXPR_PAYLOAD); + assert(left->payload.base); + assert(base == left->payload.base); + + if (expr->left->payload.inner_desc) { + if (expr->left->payload.inner_desc == expr->left->payload.desc) { + nexpr->left->payload.desc = expr->left->payload.desc; + nexpr->left->payload.tmpl = expr->left->payload.tmpl; + } + nexpr->left->payload.inner_desc = expr->left->payload.inner_desc; + + if (meta_outer_may_dependency_kill(ctx, expr->left)) { + struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx); + + payload_dependency_release(&dl_outer->pdctx, expr->left->payload.inner_desc->base); + } + } + + if (payload_is_stacked(dl->pctx.protocol[base].desc, nexpr)) + base--; + + /* Remember the first payload protocol expression to + * kill it later on if made redundant by a higher layer + * payload expression. + */ + payload_dependency_kill(&dl->pdctx, nexpr->left, + dl->pctx.family); + if (expr->op == OP_EQ && left->flags & EXPR_F_PROTOCOL) + payload_dependency_store(&dl->pdctx, nstmt, base); + } + list_del(&ctx->stmt->list); + stmt_free(ctx->stmt); + ctx->stmt = NULL; +} + +static void payload_icmp_check(struct rule_pp_ctx *rctx, struct expr *expr, const struct expr *value) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(rctx); + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc; + uint8_t icmp_type; + unsigned int i; + + assert(expr->etype == EXPR_PAYLOAD); + assert(value->etype == EXPR_VALUE); + + if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR) + return; + + /* icmp(v6) type is 8 bit, if value is smaller or larger, this is not + * a protocol dependency. + */ + if (expr->len != 8 || value->len != 8 || dl->pctx.th_dep.icmp.type) + return; + + desc = dl->pctx.protocol[expr->payload.base].desc; + if (desc == NULL) + return; + + /* not icmp? ignore. */ + if (desc != &proto_icmp && desc != &proto_icmp6) + return; + + assert(desc->base == expr->payload.base); + + icmp_type = mpz_get_uint8(value->value); + + for (i = 1; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + + if (tmpl->len == 0) + return; + + if (tmpl->offset != expr->payload.offset || + tmpl->len != expr->len) + continue; + + /* Matches but doesn't load a protocol key -> ignore. */ + if (desc->protocol_key != i) + return; + + expr->payload.desc = desc; + expr->payload.tmpl = tmpl; + dl->pctx.th_dep.icmp.type = icmp_type; + return; + } +} + +static void payload_match_postprocess(struct rule_pp_ctx *ctx, + struct expr *expr, + struct expr *payload) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + + switch (expr->op) { + case OP_EQ: + case OP_NEQ: + if (expr->right->etype == EXPR_VALUE) { + payload_match_expand(ctx, expr, payload); + break; + } else if (expr->right->etype == EXPR_SET_REF) { + struct set *set = expr->right->set; + + if (set_is_anonymous(set->flags) && + set->init && + !list_empty(&set->init->expressions)) { + struct expr *elem; + + elem = list_first_entry(&set->init->expressions, struct expr, list); + + if (elem->etype == EXPR_SET_ELEM && + elem->key->etype == EXPR_VALUE) + payload_icmp_check(ctx, payload, elem->key); + } + } + /* Fall through */ + default: + payload_expr_complete(payload, &dl->pctx); + expr_set_type(expr->right, payload->dtype, + payload->byteorder); + payload_dependency_kill(&dl->pdctx, payload, dl->pctx.family); + break; + } +} + +static uint8_t ether_type_to_nfproto(uint16_t l3proto) +{ + switch(l3proto) { + case ETH_P_IP: + return NFPROTO_IPV4; + case ETH_P_IPV6: + return NFPROTO_IPV6; + default: + break; + } + + return NFPROTO_UNSPEC; +} + +static bool __meta_dependency_may_kill(const struct expr *dep, uint8_t *nfproto) +{ + uint16_t l3proto; + + switch (dep->left->etype) { + case EXPR_META: + switch (dep->left->meta.key) { + case NFT_META_NFPROTO: + *nfproto = mpz_get_uint8(dep->right->value); + break; + case NFT_META_PROTOCOL: + l3proto = mpz_get_uint16(dep->right->value); + *nfproto = ether_type_to_nfproto(l3proto); + break; + default: + return true; + } + break; + case EXPR_PAYLOAD: + if (dep->left->payload.base != PROTO_BASE_LL_HDR) + return true; + + if (dep->left->dtype != ðertype_type) + return true; + + l3proto = mpz_get_uint16(dep->right->value); + *nfproto = ether_type_to_nfproto(l3proto); + break; + default: + return true; + } + + return false; +} + +/* We have seen a protocol key expression that restricts matching at the network + * base, leave it in place since this is meaningful in bridge, inet and netdev + * families. Exceptions are ICMP and ICMPv6 where this code assumes that can + * only happen with IPv4 and IPv6. + */ +static bool meta_may_dependency_kill(struct payload_dep_ctx *ctx, + unsigned int family, + const struct expr *expr) +{ + uint8_t l4proto, nfproto = NFPROTO_UNSPEC; + struct expr *dep = payload_dependency_get(ctx, PROTO_BASE_NETWORK_HDR); + + if (!dep) + return true; + + if (__meta_dependency_may_kill(dep, &nfproto)) + return true; + + switch (family) { + case NFPROTO_INET: + case NFPROTO_NETDEV: + case NFPROTO_BRIDGE: + break; + case NFPROTO_IPV4: + case NFPROTO_IPV6: + return family == nfproto; + default: + return true; + } + + if (expr->left->meta.key != NFT_META_L4PROTO) + return true; + + l4proto = mpz_get_uint8(expr->right->value); + + switch (l4proto) { + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + break; + default: + return false; + } + + if ((nfproto == NFPROTO_IPV4 && l4proto == IPPROTO_ICMPV6) || + (nfproto == NFPROTO_IPV6 && l4proto == IPPROTO_ICMP)) + return false; + + return true; +} + +static void ct_meta_common_postprocess(struct rule_pp_ctx *ctx, + const struct expr *expr, + enum proto_bases base) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + const struct expr *left = expr->left; + struct expr *right = expr->right; + + if (right->etype == EXPR_SET || right->etype == EXPR_SET_REF) + expr_set_type(right, left->dtype, left->byteorder); + + switch (expr->op) { + case OP_EQ: + if (expr->right->etype == EXPR_RANGE || + expr->right->etype == EXPR_SET || + expr->right->etype == EXPR_SET_REF) + break; + + relational_expr_pctx_update(&dl->pctx, expr); + + if (base < PROTO_BASE_TRANSPORT_HDR) { + if (payload_dependency_exists(&dl->pdctx, base) && + meta_may_dependency_kill(&dl->pdctx, + dl->pctx.family, expr)) + payload_dependency_release(&dl->pdctx, base); + + if (left->flags & EXPR_F_PROTOCOL) + payload_dependency_store(&dl->pdctx, ctx->stmt, base); + } + break; + default: + break; + } +} + +static void meta_match_postprocess(struct rule_pp_ctx *ctx, + const struct expr *expr) +{ + const struct expr *left = expr->left; + + ct_meta_common_postprocess(ctx, expr, left->meta.base); +} + +static void ct_match_postprocess(struct rule_pp_ctx *ctx, + const struct expr *expr) +{ + const struct expr *left = expr->left; + + ct_meta_common_postprocess(ctx, expr, left->ct.base); +} + +/* Convert a bitmask to a prefix length */ +static unsigned int expr_mask_to_prefix(const struct expr *expr) +{ + unsigned long n; + + n = mpz_scan1(expr->value, 0); + if (n == ULONG_MAX) + return 0; + return mpz_scan0(expr->value, n + 1) - n; +} + +/* Return true if a bitmask can be expressed as a prefix length */ +static bool expr_mask_is_prefix(const struct expr *expr) +{ + unsigned long n1, n2; + + n1 = mpz_scan1(expr->value, 0); + if (n1 == ULONG_MAX) + return true; + n2 = mpz_scan0(expr->value, n1 + 1); + if (n2 < expr->len || n2 == ULONG_MAX) + return false; + return true; +} + +/* Convert a series of inclusive OR expressions into a list */ +static struct expr *binop_tree_to_list(struct expr *list, struct expr *expr) +{ + if (expr->etype == EXPR_BINOP && expr->op == OP_OR) { + if (list == NULL) + list = list_expr_alloc(&expr->location); + list = binop_tree_to_list(list, expr->left); + list = binop_tree_to_list(list, expr->right); + } else { + if (list == NULL) + return expr_get(expr); + compound_expr_add(list, expr_get(expr)); + } + + return list; +} + +static void binop_adjust_one(const struct expr *binop, struct expr *value, + unsigned int shift) +{ + struct expr *left = binop->left; + + assert(value->len >= binop->right->len); + + mpz_rshift_ui(value->value, shift); + switch (left->etype) { + case EXPR_PAYLOAD: + case EXPR_EXTHDR: + value->len = left->len; + break; + default: + BUG("unknown expression type %s\n", expr_name(left)); + break; + } +} + +static void binop_adjust(const struct expr *binop, struct expr *right, + unsigned int shift) +{ + struct expr *i; + + switch (right->etype) { + case EXPR_VALUE: + binop_adjust_one(binop, right, shift); + break; + case EXPR_SET_REF: + if (!set_is_anonymous(right->set->flags)) + break; + + list_for_each_entry(i, &right->set->init->expressions, list) { + switch (i->key->etype) { + case EXPR_VALUE: + binop_adjust_one(binop, i->key, shift); + break; + case EXPR_RANGE: + binop_adjust_one(binop, i->key->left, shift); + binop_adjust_one(binop, i->key->right, shift); + break; + case EXPR_SET_ELEM: + binop_adjust(binop, i->key->key, shift); + break; + default: + BUG("unknown expression type %s\n", expr_name(i->key)); + } + } + break; + case EXPR_RANGE: + binop_adjust_one(binop, right->left, shift); + binop_adjust_one(binop, right->right, shift); + break; + default: + BUG("unknown expression type %s\n", expr_name(right)); + break; + } +} + +static void __binop_postprocess(struct rule_pp_ctx *ctx, + struct expr *expr, + struct expr *left, + struct expr *mask, + struct expr **expr_binop) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + struct expr *binop = *expr_binop; + unsigned int shift; + + assert(binop->etype == EXPR_BINOP); + + if ((left->etype == EXPR_PAYLOAD && + payload_expr_trim(left, mask, &dl->pctx, &shift)) || + (left->etype == EXPR_EXTHDR && + exthdr_find_template(left, mask, &shift))) { + struct expr *right = NULL; + + /* mask is implicit, binop needs to be removed. + * + * Fix all values of the expression according to the mask + * and then process the payload instruction using the real + * sizes and offsets we're interested in. + * + * Finally, convert the expression to 1) by replacing + * the binop with the binop payload/exthdr expression. + */ + switch (expr->etype) { + case EXPR_BINOP: + case EXPR_RELATIONAL: + right = expr->right; + binop_adjust(binop, right, shift); + break; + case EXPR_MAP: + right = expr->mappings; + binop_adjust(binop, right, shift); + break; + default: + break; + } + + assert(binop->left == left); + *expr_binop = expr_get(left); + + if (left->etype == EXPR_PAYLOAD) + payload_match_postprocess(ctx, expr, left); + else if (left->etype == EXPR_EXTHDR && right) + expr_set_type(right, left->dtype, left->byteorder); + + expr_free(binop); + } +} + +static void binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr, + struct expr **expr_binop) +{ + struct expr *binop = *expr_binop; + struct expr *left = binop->left; + struct expr *mask = binop->right; + + __binop_postprocess(ctx, expr, left, mask, expr_binop); +} + +static void map_binop_postprocess(struct rule_pp_ctx *ctx, struct expr *expr) +{ + struct expr *binop = expr->map; + + if (binop->op != OP_AND) + return; + + if (binop->left->etype == EXPR_PAYLOAD && + binop->right->etype == EXPR_VALUE) + binop_postprocess(ctx, expr, &expr->map); +} + +static bool is_shift_by_zero(const struct expr *binop) +{ + struct expr *rhs; + + if (binop->op != OP_RSHIFT && binop->op != OP_LSHIFT) + return false; + + rhs = binop->right; + if (rhs->etype != EXPR_VALUE || rhs->len > 64) + return false; + + return mpz_get_uint64(rhs->value) == 0; +} + +static void relational_binop_postprocess(struct rule_pp_ctx *ctx, + struct expr **exprp) +{ + struct expr *expr = *exprp, *binop = expr->left, *right = expr->right; + + if (binop->op == OP_AND && (expr->op == OP_NEQ || expr->op == OP_EQ) && + right->dtype->basetype && + right->dtype->basetype->type == TYPE_BITMASK) { + switch (right->etype) { + case EXPR_VALUE: + if (!mpz_cmp_ui(right->value, 0)) { + /* Flag comparison: data & flags != 0 + * + * Split the flags into a list of flag values and convert the + * op to OP_EQ. + */ + expr_free(right); + + expr->left = expr_get(binop->left); + expr->right = binop_tree_to_list(NULL, binop->right); + switch (expr->op) { + case OP_NEQ: + expr->op = OP_IMPLICIT; + break; + case OP_EQ: + expr->op = OP_NEG; + break; + default: + BUG("unknown operation type %d\n", expr->op); + } + expr_free(binop); + } else if (binop->right->etype == EXPR_VALUE && + right->etype == EXPR_VALUE && + !mpz_cmp(right->value, binop->right->value)) { + /* Skip flag / flag representation for: + * data & flag == flag + * data & flag != flag + */ + ; + } else { + *exprp = flagcmp_expr_alloc(&expr->location, expr->op, + expr_get(binop->left), + binop_tree_to_list(NULL, binop->right), + expr_get(right)); + expr_free(expr); + } + break; + case EXPR_BINOP: + *exprp = flagcmp_expr_alloc(&expr->location, expr->op, + expr_get(binop->left), + binop_tree_to_list(NULL, binop->right), + binop_tree_to_list(NULL, right)); + expr_free(expr); + break; + default: + break; + } + } else if (binop->left->dtype->flags & DTYPE_F_PREFIX && + binop->op == OP_AND && expr->right->etype == EXPR_VALUE && + expr_mask_is_prefix(binop->right)) { + expr->left = expr_get(binop->left); + expr->right = prefix_expr_alloc(&expr->location, + expr_get(right), + expr_mask_to_prefix(binop->right)); + expr_free(right); + expr_free(binop); + } else if (binop->op == OP_AND && + binop->right->etype == EXPR_VALUE) { + /* + * This *might* be a payload match testing header fields that + * have non byte divisible offsets and/or bit lengths. + * + * Thus we need to deal with two different cases. + * + * 1 the simple version: + * relation + * payload value|setlookup + * + * expr: relation, left: payload, right: value, e.g. tcp dport == 22. + * + * 2. The '&' version (this is what we're looking at now). + * relation + * binop value1|setlookup + * payload value2 + * + * expr: relation, left: binop, right: value, e.g. + * ip saddr 10.0.0.0/8 + * + * payload_expr_trim will figure out if the mask is needed to match + * templates. + */ + binop_postprocess(ctx, expr, &expr->left); + } else if (binop->op == OP_RSHIFT && binop->left->op == OP_AND && + binop->right->etype == EXPR_VALUE && binop->left->right->etype == EXPR_VALUE) { + /* Handle 'ip version @s4' and similar, i.e. set lookups where the lhs needs + * fixups to mask out unwanted bits AND a shift. + */ + + binop_postprocess(ctx, binop, &binop->left); + if (is_shift_by_zero(binop)) { + struct expr *lhs = binop->left; + + expr_get(lhs); + expr_free(binop); + expr->left = lhs; + } + } +} + +static bool payload_binop_postprocess(struct rule_pp_ctx *ctx, + struct expr **exprp) +{ + struct expr *expr = *exprp; + + if (expr->op != OP_RSHIFT) + return false; + + if (expr->left->etype == EXPR_UNARY) { + /* + * If the payload value was originally in a different byte-order + * from the payload expression, there will be a byte-order + * conversion to remove. + */ + struct expr *left = expr_get(expr->left->arg); + expr_free(expr->left); + expr->left = left; + } + + if (expr->left->etype != EXPR_BINOP || expr->left->op != OP_AND) + return false; + + if (expr->left->left->etype != EXPR_PAYLOAD) + return false; + + expr_set_type(expr->right, &integer_type, + BYTEORDER_HOST_ENDIAN); + expr_postprocess(ctx, &expr->right); + + binop_postprocess(ctx, expr, &expr->left); + *exprp = expr_get(expr->left); + expr_free(expr); + + return true; +} + +static struct expr *string_wildcard_expr_alloc(struct location *loc, + const struct expr *mask, + const struct expr *expr) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + char data[len + 2]; + int pos; + + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + pos = div_round_up(expr_mask_to_prefix(mask), BITS_PER_BYTE); + data[pos] = '*'; + data[pos + 1] = '\0'; + + return constant_expr_alloc(loc, expr->dtype, BYTEORDER_HOST_ENDIAN, + expr->len + BITS_PER_BYTE, data); +} + +/* This calculates the string length and checks if it is nul-terminated, this + * function is quite a hack :) + */ +static bool __expr_postprocess_string(struct expr **exprp) +{ + struct expr *expr = *exprp; + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + char data[len + 1]; + + mpz_export_data(data, expr->value, BYTEORDER_HOST_ENDIAN, len); + + if (data[len - 1] != '\0') + return false; + + len = strlen(data); + if (len && data[len - 1] == '*') { + data[len - 1] = '\\'; + data[len] = '*'; + data[len + 1] = '\0'; + expr = constant_expr_alloc(&expr->location, expr->dtype, + BYTEORDER_HOST_ENDIAN, + (len + 2) * BITS_PER_BYTE, data); + expr_free(*exprp); + *exprp = expr; + } + + return true; +} + +static struct expr *expr_postprocess_string(struct expr *expr) +{ + struct expr *mask, *out; + + assert(expr_basetype(expr)->type == TYPE_STRING); + if (__expr_postprocess_string(&expr)) + return expr; + + mask = constant_expr_alloc(&expr->location, &integer_type, + BYTEORDER_HOST_ENDIAN, + expr->len + BITS_PER_BYTE, NULL); + mpz_clear(mask->value); + mpz_init_bitmask(mask->value, expr->len); + out = string_wildcard_expr_alloc(&expr->location, mask, expr); + expr_free(expr); + expr_free(mask); + return out; +} + +static void expr_postprocess(struct rule_pp_ctx *ctx, struct expr **exprp) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + struct expr *expr = *exprp, *i; + + switch (expr->etype) { + case EXPR_MAP: + switch (expr->map->etype) { + case EXPR_BINOP: + map_binop_postprocess(ctx, expr); + break; + default: + break; + } + + expr_postprocess(ctx, &expr->map); + expr_postprocess(ctx, &expr->mappings); + break; + case EXPR_MAPPING: + expr_postprocess(ctx, &expr->left); + expr_postprocess(ctx, &expr->right); + break; + case EXPR_SET: + list_for_each_entry(i, &expr->expressions, list) + expr_postprocess(ctx, &i); + break; + case EXPR_CONCAT: { + unsigned int type = expr->dtype->type, ntype = 0; + int off = expr->dtype->subtypes; + const struct datatype *dtype; + LIST_HEAD(tmp); + struct expr *n; + + ctx->flags |= RULE_PP_IN_CONCATENATION; + list_for_each_entry_safe(i, n, &expr->expressions, list) { + if (type) { + dtype = concat_subtype_lookup(type, --off); + expr_set_type(i, dtype, dtype->byteorder); + } + list_del(&i->list); + expr_postprocess(ctx, &i); + list_add_tail(&i->list, &tmp); + + ntype = concat_subtype_add(ntype, i->dtype->type); + } + ctx->flags &= ~RULE_PP_IN_CONCATENATION; + list_splice(&tmp, &expr->expressions); + __datatype_set(expr, concat_type_alloc(ntype)); + break; + } + case EXPR_UNARY: + expr_postprocess(ctx, &expr->arg); + expr_set_type(expr, expr->arg->dtype, !expr->arg->byteorder); + break; + case EXPR_BINOP: + if (payload_binop_postprocess(ctx, exprp)) + break; + + expr_postprocess(ctx, &expr->left); + switch (expr->op) { + case OP_LSHIFT: + case OP_RSHIFT: + expr_set_type(expr->right, &integer_type, + BYTEORDER_HOST_ENDIAN); + break; + case OP_AND: + if (expr->right->len > expr->left->len) { + expr_set_type(expr->right, expr->left->dtype, + BYTEORDER_HOST_ENDIAN); + } else { + expr_set_type(expr->right, expr->left->dtype, + expr->left->byteorder); + } + + /* Do not process OP_AND in ordinary rule context. + * + * Removal needs to be performed as part of the relational + * operation because the RHS constant might need to be adjusted + * (shifted). + * + * This is different in set element context or concatenations: + * There is no relational operation (eq, neq and so on), thus + * it needs to be processed right away. + */ + if ((ctx->flags & RULE_PP_REMOVE_OP_AND) && + expr->left->etype == EXPR_PAYLOAD && + expr->right->etype == EXPR_VALUE) { + __binop_postprocess(ctx, expr, expr->left, expr->right, exprp); + return; + } + break; + default: + if (expr->right->len > expr->left->len) { + expr_set_type(expr->right, expr->left->dtype, + BYTEORDER_HOST_ENDIAN); + } else { + expr_set_type(expr->right, expr->left->dtype, + expr->left->byteorder); + } + } + expr_postprocess(ctx, &expr->right); + + switch (expr->op) { + case OP_LSHIFT: + case OP_RSHIFT: + expr_set_type(expr, &xinteger_type, + BYTEORDER_HOST_ENDIAN); + break; + default: + expr_set_type(expr, expr->left->dtype, + expr->left->byteorder); + } + + break; + case EXPR_RELATIONAL: + switch (expr->left->etype) { + case EXPR_PAYLOAD: + payload_match_postprocess(ctx, expr, expr->left); + return; + case EXPR_CONCAT: + if (expr->right->etype == EXPR_SET_REF) { + assert(expr->left->dtype == &invalid_type); + assert(expr->right->dtype != &invalid_type); + + datatype_set(expr->left, expr->right->dtype); + } + expr_postprocess(ctx, &expr->left); + break; + default: + expr_postprocess(ctx, &expr->left); + break; + } + + expr_set_type(expr->right, expr->left->dtype, expr->left->byteorder); + expr_postprocess(ctx, &expr->right); + + switch (expr->left->etype) { + case EXPR_CT: + ct_match_postprocess(ctx, expr); + break; + case EXPR_META: + meta_match_postprocess(ctx, expr); + break; + case EXPR_BINOP: + relational_binop_postprocess(ctx, exprp); + break; + default: + break; + } + break; + case EXPR_PAYLOAD: + payload_expr_complete(expr, &dl->pctx); + if (expr->payload.inner_desc) { + if (meta_outer_may_dependency_kill(ctx, expr)) { + struct dl_proto_ctx *dl_outer = dl_proto_ctx_outer(ctx); + + payload_dependency_release(&dl_outer->pdctx, expr->payload.inner_desc->base); + } + } + payload_dependency_kill(&dl->pdctx, expr, dl->pctx.family); + break; + case EXPR_VALUE: + // FIXME + if (expr->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + if (expr_basetype(expr)->type == TYPE_STRING) + *exprp = expr_postprocess_string(expr); + + expr = *exprp; + if (expr->dtype->basetype != NULL && + expr->dtype->basetype->type == TYPE_BITMASK) + *exprp = bitmask_expr_to_binops(expr); + + break; + case EXPR_RANGE: + expr_postprocess(ctx, &expr->left); + expr_postprocess(ctx, &expr->right); + break; + case EXPR_PREFIX: + expr_postprocess(ctx, &expr->prefix); + break; + case EXPR_SET_ELEM: + ctx->flags |= RULE_PP_IN_SET_ELEM; + expr_postprocess(ctx, &expr->key); + ctx->flags &= ~RULE_PP_IN_SET_ELEM; + break; + case EXPR_EXTHDR: + exthdr_dependency_kill(&dl->pdctx, expr, dl->pctx.family); + break; + case EXPR_SET_REF: + case EXPR_META: + case EXPR_RT: + case EXPR_VERDICT: + case EXPR_NUMGEN: + case EXPR_FIB: + case EXPR_SOCKET: + case EXPR_OSF: + case EXPR_XFRM: + break; + case EXPR_HASH: + if (expr->hash.expr) + expr_postprocess(ctx, &expr->hash.expr); + break; + case EXPR_CT: + ct_expr_update_type(&dl->pctx, expr); + break; + default: + BUG("unknown expression type %s\n", expr_name(expr)); + } +} + +static void stmt_reject_postprocess(struct rule_pp_ctx *rctx) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(rctx); + const struct proto_desc *desc, *base; + struct stmt *stmt = rctx->stmt; + int protocol; + + switch (dl->pctx.family) { + case NFPROTO_IPV4: + stmt->reject.family = dl->pctx.family; + datatype_set(stmt->reject.expr, &icmp_code_type); + if (stmt->reject.type == NFT_REJECT_TCP_RST && + payload_dependency_exists(&dl->pdctx, + PROTO_BASE_TRANSPORT_HDR)) + payload_dependency_release(&dl->pdctx, + PROTO_BASE_TRANSPORT_HDR); + break; + case NFPROTO_IPV6: + stmt->reject.family = dl->pctx.family; + datatype_set(stmt->reject.expr, &icmpv6_code_type); + if (stmt->reject.type == NFT_REJECT_TCP_RST && + payload_dependency_exists(&dl->pdctx, + PROTO_BASE_TRANSPORT_HDR)) + payload_dependency_release(&dl->pdctx, + PROTO_BASE_TRANSPORT_HDR); + break; + case NFPROTO_INET: + case NFPROTO_BRIDGE: + case NFPROTO_NETDEV: + if (stmt->reject.type == NFT_REJECT_ICMPX_UNREACH) { + datatype_set(stmt->reject.expr, &icmpx_code_type); + break; + } + + /* always print full icmp(6) name, simple 'reject' might be ambiguious + * because ipv4 vs. ipv6 info might be lost + */ + stmt->reject.verbose_print = 1; + + base = dl->pctx.protocol[PROTO_BASE_LL_HDR].desc; + desc = dl->pctx.protocol[PROTO_BASE_NETWORK_HDR].desc; + protocol = proto_find_num(base, desc); + switch (protocol) { + case NFPROTO_IPV4: /* INET */ + case __constant_htons(ETH_P_IP): /* BRIDGE, NETDEV */ + stmt->reject.family = NFPROTO_IPV4; + datatype_set(stmt->reject.expr, &icmp_code_type); + break; + case NFPROTO_IPV6: /* INET */ + case __constant_htons(ETH_P_IPV6): /* BRIDGE, NETDEV */ + stmt->reject.family = NFPROTO_IPV6; + datatype_set(stmt->reject.expr, &icmpv6_code_type); + break; + default: + break; + } + + if (payload_dependency_exists(&dl->pdctx, PROTO_BASE_NETWORK_HDR)) + payload_dependency_release(&dl->pdctx, + PROTO_BASE_NETWORK_HDR); + break; + default: + break; + } +} + +static bool expr_may_merge_range(struct expr *expr, struct expr *prev, + enum ops *op) +{ + struct expr *left, *prev_left; + + if (prev->etype == EXPR_RELATIONAL && + expr->etype == EXPR_RELATIONAL) { + /* ct and meta needs an unary to swap byteorder, in this case + * we have to explore the inner branch in this tree. + */ + if (expr->left->etype == EXPR_UNARY) + left = expr->left->arg; + else + left = expr->left; + + if (prev->left->etype == EXPR_UNARY) + prev_left = prev->left->arg; + else + prev_left = prev->left; + + if (left->etype == prev_left->etype) { + if (expr->op == OP_LTE && prev->op == OP_GTE) { + *op = OP_EQ; + return true; + } else if (expr->op == OP_GT && prev->op == OP_LT) { + *op = OP_NEQ; + return true; + } + } + } + + return false; +} + +static void expr_postprocess_range(struct rule_pp_ctx *ctx, enum ops op) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + struct stmt *nstmt, *stmt = ctx->stmt; + struct expr *nexpr, *rel; + + nexpr = range_expr_alloc(&dl->pdctx.prev->location, + expr_clone(dl->pdctx.prev->expr->right), + expr_clone(stmt->expr->right)); + expr_set_type(nexpr, stmt->expr->right->dtype, + stmt->expr->right->byteorder); + + rel = relational_expr_alloc(&dl->pdctx.prev->location, op, + expr_clone(stmt->expr->left), nexpr); + + nstmt = expr_stmt_alloc(&stmt->location, rel); + list_add_tail(&nstmt->list, &stmt->list); + + list_del(&dl->pdctx.prev->list); + stmt_free(dl->pdctx.prev); + + list_del(&stmt->list); + stmt_free(stmt); + ctx->stmt = nstmt; +} + +static void stmt_expr_postprocess(struct rule_pp_ctx *ctx) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + enum ops op; + + expr_postprocess(ctx, &ctx->stmt->expr); + + if (dl->pdctx.prev && ctx->stmt && + ctx->stmt->ops->type == dl->pdctx.prev->ops->type && + expr_may_merge_range(ctx->stmt->expr, dl->pdctx.prev->expr, &op)) + expr_postprocess_range(ctx, op); +} + +static void stmt_payload_binop_pp(struct rule_pp_ctx *ctx, struct expr *binop) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + struct expr *payload = binop->left; + struct expr *mask = binop->right; + unsigned int shift; + + assert(payload->etype == EXPR_PAYLOAD); + if (payload_expr_trim(payload, mask, &dl->pctx, &shift)) { + binop_adjust(binop, mask, shift); + payload_expr_complete(payload, &dl->pctx); + expr_set_type(mask, payload->dtype, + payload->byteorder); + } +} + +/** + * stmt_payload_binop_postprocess - decode payload set binop + * + * @ctx: rule postprocessing context + * + * This helper has to be called if expr_postprocess() failed to + * decode the payload operation. + * + * Usually a failure to decode means that userspace had to munge + * the original payload expression because it has an odd size or + * a non-byte divisible offset/length. + * + * If that was the case, the 'value' expression is not a value but + * a binop expression with a munged payload expression on the left + * and a mask to clear the real payload offset/length. + * + * So chech if we have one of the following binops: + * I) + * binop (|) + * binop(&) value/set + * payload value(mask) + * + * This is the normal case, the | RHS is the value the user wants + * to set, the & RHS is the mask value that discards bits we need + * to clear but retains everything unrelated to the set operation. + * + * IIa) + * binop (&) + * payload mask + * + * User specified a zero set value -- netlink bitwise decoding + * discarded the redundant "| 0" part. This is identical to I), + * we can just set value to 0 after we inferred the real payload size. + * + * IIb) + * binop (|) + * payload value/set + * + * This happens when user wants to set all bits, netlink bitwise + * decoding changed '(payload & mask) ^ bits_to_set' into + * 'payload | bits_to_set', discarding the redundant "& 0xfff...". + */ +static void stmt_payload_binop_postprocess(struct rule_pp_ctx *ctx) +{ + struct expr *expr, *binop, *payload, *value, *mask; + struct stmt *stmt = ctx->stmt; + mpz_t bitmask; + + expr = stmt->payload.val; + + if (expr->etype != EXPR_BINOP) + return; + + switch (expr->left->etype) { + case EXPR_BINOP: {/* I? */ + mpz_t tmp; + + if (expr->op != OP_OR) + return; + + value = expr->right; + if (value->etype != EXPR_VALUE) + return; + + binop = expr->left; + if (binop->op != OP_AND) + return; + + payload = binop->left; + if (payload->etype != EXPR_PAYLOAD) + return; + + if (!payload_expr_cmp(stmt->payload.expr, payload)) + return; + + mask = binop->right; + if (mask->etype != EXPR_VALUE) + return; + + mpz_init(tmp); + mpz_set(tmp, mask->value); + + mpz_init_bitmask(bitmask, payload->len); + mpz_xor(bitmask, bitmask, mask->value); + mpz_xor(bitmask, bitmask, value->value); + mpz_set(mask->value, bitmask); + mpz_clear(bitmask); + + binop_postprocess(ctx, expr, &expr->left); + if (!payload_is_known(payload)) { + mpz_set(mask->value, tmp); + mpz_clear(tmp); + return; + } + + mpz_clear(tmp); + expr_free(stmt->payload.expr); + stmt->payload.expr = expr_get(payload); + stmt->payload.val = expr_get(expr->right); + expr_free(expr); + break; + } + case EXPR_PAYLOAD: /* II? */ + value = expr->right; + if (value->etype != EXPR_VALUE) + return; + + switch (expr->op) { + case OP_AND: /* IIa */ + payload = expr->left; + mpz_init_bitmask(bitmask, payload->len); + mpz_xor(bitmask, bitmask, value->value); + mpz_set(value->value, bitmask); + mpz_clear(bitmask); + break; + case OP_OR: /* IIb */ + break; + default: /* No idea */ + return; + } + + stmt_payload_binop_pp(ctx, expr); + if (!payload_is_known(expr->left)) + return; + + expr_free(stmt->payload.expr); + + switch (expr->op) { + case OP_AND: + /* Mask was used to match payload, i.e. + * user asked to set zero value. + */ + mpz_set_ui(value->value, 0); + break; + default: + break; + } + + stmt->payload.expr = expr_get(expr->left); + stmt->payload.val = expr_get(expr->right); + expr_free(expr); + break; + default: /* No idea */ + break; + } +} + +static void stmt_payload_postprocess(struct rule_pp_ctx *ctx) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(ctx); + struct stmt *stmt = ctx->stmt; + + payload_expr_complete(stmt->payload.expr, &dl->pctx); + if (!payload_is_known(stmt->payload.expr)) + stmt_payload_binop_postprocess(ctx); + + expr_postprocess(ctx, &stmt->payload.expr); + + expr_set_type(stmt->payload.val, + stmt->payload.expr->dtype, + stmt->payload.expr->byteorder); + + expr_postprocess(ctx, &stmt->payload.val); +} + +static void stmt_queue_postprocess(struct rule_pp_ctx *ctx) +{ + struct stmt *stmt = ctx->stmt; + struct expr *e = stmt->queue.queue; + + if (e == NULL || e->etype == EXPR_VALUE || + e->etype == EXPR_RANGE) + return; + + expr_postprocess(ctx, &stmt->queue.queue); +} + +/* + * We can only remove payload dependencies if they occur without + * a statement with side effects in between. + * + * For instance: + * 'ip protocol tcp tcp dport 22 counter' is same as + * 'tcp dport 22 counter'. + * + * 'ip protocol tcp counter tcp dport 22' cannot be written as + * 'counter tcp dport 22' (that would be counter ip protocol tcp, but + * that counts every packet, not just ip/tcp). + */ +static void +rule_maybe_reset_payload_deps(struct payload_dep_ctx *pdctx, enum stmt_types t) +{ + if (t == STMT_EXPRESSION) + return; + + payload_dependency_reset(pdctx); +} + +static bool has_inner_desc(const struct expr *expr) +{ + struct expr *i; + + switch (expr->etype) { + case EXPR_BINOP: + return has_inner_desc(expr->left); + case EXPR_CONCAT: + list_for_each_entry(i, &expr->expressions, list) { + if (has_inner_desc(i)) + return true; + } + break; + case EXPR_META: + return expr->meta.inner_desc; + case EXPR_PAYLOAD: + return expr->payload.inner_desc; + case EXPR_SET_ELEM: + return has_inner_desc(expr->key); + default: + break; + } + + return false; +} + +static struct dl_proto_ctx *rule_update_dl_proto_ctx(struct rule_pp_ctx *rctx) +{ + const struct stmt *stmt = rctx->stmt; + bool inner = false; + + switch (stmt->ops->type) { + case STMT_EXPRESSION: + if (has_inner_desc(stmt->expr->left)) + inner = true; + break; + case STMT_SET: + if (has_inner_desc(stmt->set.key)) + inner = true; + break; + default: + break; + } + + if (inner) + rctx->dl = &rctx->_dl[1]; + else + rctx->dl = &rctx->_dl[0]; + + return rctx->dl; +} + +static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *rule) +{ + struct stmt *stmt, *next; + struct dl_proto_ctx *dl; + struct rule_pp_ctx rctx; + struct expr *expr; + + memset(&rctx, 0, sizeof(rctx)); + proto_ctx_init(&rctx._dl[0].pctx, rule->handle.family, ctx->debug_mask, false); + /* use NFPROTO_BRIDGE to set up proto_eth as base protocol. */ + proto_ctx_init(&rctx._dl[1].pctx, NFPROTO_BRIDGE, ctx->debug_mask, true); + + list_for_each_entry_safe(stmt, next, &rule->stmts, list) { + enum stmt_types type = stmt->ops->type; + + rctx.stmt = stmt; + dl = rule_update_dl_proto_ctx(&rctx); + + switch (type) { + case STMT_EXPRESSION: + stmt_expr_postprocess(&rctx); + break; + case STMT_PAYLOAD: + stmt_payload_postprocess(&rctx); + break; + case STMT_METER: + expr_postprocess(&rctx, &stmt->meter.key); + break; + case STMT_META: + if (stmt->meta.expr != NULL) + expr_postprocess(&rctx, &stmt->meta.expr); + break; + case STMT_CT: + if (stmt->ct.expr != NULL) { + expr_postprocess(&rctx, &stmt->ct.expr); + + if (stmt->ct.expr->etype == EXPR_BINOP && + stmt->ct.key == NFT_CT_EVENTMASK) { + expr = binop_tree_to_list(NULL, stmt->ct.expr); + expr_free(stmt->ct.expr); + stmt->ct.expr = expr; + } + } + break; + case STMT_NAT: + if (stmt->nat.addr != NULL) + expr_postprocess(&rctx, &stmt->nat.addr); + if (stmt->nat.proto != NULL) + expr_postprocess(&rctx, &stmt->nat.proto); + break; + case STMT_TPROXY: + if (stmt->tproxy.addr) + expr_postprocess(&rctx, &stmt->tproxy.addr); + if (stmt->tproxy.port) { + payload_dependency_reset(&dl->pdctx); + expr_postprocess(&rctx, &stmt->tproxy.port); + } + break; + case STMT_REJECT: + stmt_reject_postprocess(&rctx); + break; + case STMT_SET: + expr_postprocess(&rctx, &stmt->set.key); + break; + case STMT_MAP: + expr_postprocess(&rctx, &stmt->map.key); + expr_postprocess(&rctx, &stmt->map.data); + break; + case STMT_DUP: + if (stmt->dup.to != NULL) + expr_postprocess(&rctx, &stmt->dup.to); + if (stmt->dup.dev != NULL) + expr_postprocess(&rctx, &stmt->dup.dev); + break; + case STMT_FWD: + expr_postprocess(&rctx, &stmt->fwd.dev); + if (stmt->fwd.addr != NULL) + expr_postprocess(&rctx, &stmt->fwd.addr); + break; + case STMT_XT: + stmt_xt_postprocess(&rctx, stmt, rule); + break; + case STMT_OBJREF: + expr_postprocess(&rctx, &stmt->objref.expr); + break; + case STMT_QUEUE: + stmt_queue_postprocess(&rctx); + break; + default: + break; + } + + dl->pdctx.prev = rctx.stmt; + + rule_maybe_reset_payload_deps(&dl->pdctx, type); + } +} + +static int parse_rule_udata_cb(const struct nftnl_udata *attr, void *data) +{ + unsigned char *value = nftnl_udata_get(attr); + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + const struct nftnl_udata **tb = data; + + switch (type) { + case NFTNL_UDATA_RULE_COMMENT: + if (value[len - 1] != '\0') + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static char *nftnl_rule_get_comment(const struct nftnl_rule *nlr) +{ + const struct nftnl_udata *tb[NFTNL_UDATA_RULE_MAX + 1] = {}; + const void *data; + uint32_t len; + + if (!nftnl_rule_is_set(nlr, NFTNL_RULE_USERDATA)) + return NULL; + + data = nftnl_rule_get_data(nlr, NFTNL_RULE_USERDATA, &len); + + if (nftnl_udata_parse(data, len, parse_rule_udata_cb, tb) < 0) + return NULL; + + if (!tb[NFTNL_UDATA_RULE_COMMENT]) + return NULL; + + return xstrdup(nftnl_udata_get(tb[NFTNL_UDATA_RULE_COMMENT])); +} + +struct rule *netlink_delinearize_rule(struct netlink_ctx *ctx, + struct nftnl_rule *nlr) +{ + struct netlink_parse_ctx _ctx, *pctx = &_ctx; + struct handle h; + + memset(&_ctx, 0, sizeof(_ctx)); + _ctx.msgs = ctx->msgs; + _ctx.debug_mask = ctx->nft->debug_mask; + _ctx.nlctx = ctx; + + memset(&h, 0, sizeof(h)); + h.family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); + h.table.name = xstrdup(nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE)); + h.chain.name = xstrdup(nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN)); + h.handle.id = nftnl_rule_get_u64(nlr, NFTNL_RULE_HANDLE); + + if (nftnl_rule_is_set(nlr, NFTNL_RULE_POSITION)) + h.position.id = nftnl_rule_get_u64(nlr, NFTNL_RULE_POSITION); + + pctx->rule = rule_alloc(&netlink_location, &h); + pctx->table = table_cache_find(&ctx->nft->cache.table_cache, + h.table.name, h.family); + if (!pctx->table) { + errno = ENOENT; + return NULL; + } + + pctx->rule->comment = nftnl_rule_get_comment(nlr); + + nftnl_expr_foreach(nlr, netlink_parse_rule_expr, pctx); + + rule_parse_postprocess(pctx, pctx->rule); + netlink_release_registers(pctx); + return pctx->rule; +} diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c new file mode 100644 index 0000000..0c62341 --- /dev/null +++ b/src/netlink_linearize.c @@ -0,0 +1,1789 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org> + * + * 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 <nft.h> + +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_log.h> + +#include <rule.h> +#include <statement.h> +#include <expression.h> +#include <netlink.h> +#include <gmputil.h> +#include <utils.h> +#include <netinet/in.h> + +#include <linux/netfilter.h> +#include <libnftnl/udata.h> + +struct nft_expr_loc *nft_expr_loc_find(const struct nftnl_expr *nle, + struct netlink_linearize_ctx *ctx) +{ + struct nft_expr_loc *eloc; + uint32_t hash; + + hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE; + list_for_each_entry(eloc, &ctx->expr_loc_htable[hash], hlist) { + if (eloc->nle == nle) + return eloc; + } + + return NULL; +} + +static void nft_expr_loc_add(const struct nftnl_expr *nle, + const struct location *loc, + struct netlink_linearize_ctx *ctx) +{ + struct nft_expr_loc *eloc; + uint32_t hash; + + eloc = xmalloc(sizeof(*eloc)); + eloc->nle = nle; + eloc->loc = loc; + hash = (uint64_t)nle % NFT_EXPR_LOC_HSIZE; + list_add_tail(&eloc->hlist, &ctx->expr_loc_htable[hash]); +} + +static void netlink_put_register(struct nftnl_expr *nle, + uint32_t attr, uint32_t reg) +{ + /* Convert to 128 bit register numbers if possible for compatibility */ + if (reg != NFT_REG_VERDICT) { + reg -= NFT_REG_1; + if (reg % (NFT_REG_SIZE / NFT_REG32_SIZE) == 0) + reg = NFT_REG_1 + reg / (NFT_REG_SIZE / NFT_REG32_SIZE); + else + reg += NFT_REG32_00; + } + + nftnl_expr_set_u32(nle, attr, reg); +} + +static enum nft_registers __get_register(struct netlink_linearize_ctx *ctx, + unsigned int size) +{ + unsigned int reg, n; + + n = netlink_register_space(size); + if (ctx->reg_low + n > NFT_REG_1 + NFT_REG32_15 - NFT_REG32_00 + 1) + BUG("register reg_low %u invalid\n", ctx->reg_low); + + reg = ctx->reg_low; + ctx->reg_low += n; + return reg; +} + +static void __release_register(struct netlink_linearize_ctx *ctx, + unsigned int size) +{ + unsigned int n; + + n = netlink_register_space(size); + if (ctx->reg_low < NFT_REG_1 + n) + BUG("register reg_low %u invalid\n", ctx->reg_low); + + ctx->reg_low -= n; +} + +static enum nft_registers get_register(struct netlink_linearize_ctx *ctx, + const struct expr *expr) +{ + if (expr && expr->etype == EXPR_CONCAT) + return __get_register(ctx, expr->len); + else + return __get_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); +} + +static void release_register(struct netlink_linearize_ctx *ctx, + const struct expr *expr) +{ + if (expr && expr->etype == EXPR_CONCAT) + __release_register(ctx, expr->len); + else + __release_register(ctx, NFT_REG_SIZE * BITS_PER_BYTE); +} + +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); + dreg += netlink_register_space(i->len); + } +} + +static void nft_rule_add_expr(struct netlink_linearize_ctx *ctx, + struct nftnl_expr *nle, + const struct location *loc) +{ + nft_expr_loc_add(nle, loc, ctx); + nftnl_rule_add_expr(ctx->nlr, nle); +} + +static void netlink_gen_fib(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("fib"); + netlink_put_register(nle, NFTNL_EXPR_FIB_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_RESULT, expr->fib.result); + nftnl_expr_set_u32(nle, NFTNL_EXPR_FIB_FLAGS, expr->fib.flags); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_hash(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + enum nft_registers sreg; + struct nftnl_expr *nle; + + nle = alloc_nft_expr("hash"); + + if (expr->hash.expr) { + sreg = get_register(ctx, expr->hash.expr); + netlink_gen_expr(ctx, expr->hash.expr, sreg); + release_register(ctx, expr->hash.expr); + netlink_put_register(nle, NFTNL_EXPR_HASH_SREG, sreg); + + nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_LEN, + div_round_up(expr->hash.expr->len, + BITS_PER_BYTE)); + } + netlink_put_register(nle, NFTNL_EXPR_HASH_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_MODULUS, expr->hash.mod); + if (expr->hash.seed_set) + nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_SEED, expr->hash.seed); + nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_OFFSET, expr->hash.offset); + nftnl_expr_set_u32(nle, NFTNL_EXPR_HASH_TYPE, expr->hash.type); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static struct nftnl_expr * +__netlink_gen_payload(const struct expr *expr, enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("payload"); + netlink_put_register(nle, NFTNL_EXPR_PAYLOAD_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_BASE, + expr->payload.base - 1); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET, + expr->payload.offset / BITS_PER_BYTE); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN, + div_round_up(expr->len, BITS_PER_BYTE)); + + return nle; +} + +static struct nftnl_expr * +__netlink_gen_meta(const struct expr *expr, enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("meta"); + netlink_put_register(nle, NFTNL_EXPR_META_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, expr->meta.key); + + return nle; +} + +static struct nftnl_expr *netlink_gen_inner_expr(const struct expr *expr, + enum nft_registers dreg) +{ + struct expr *_expr = (struct expr *)expr; + struct nftnl_expr *nle; + + switch (expr->etype) { + case EXPR_PAYLOAD: + if (expr->payload.base == NFT_PAYLOAD_INNER_HEADER + 1) + _expr->payload.base = NFT_PAYLOAD_TUN_HEADER + 1; + + nle = __netlink_gen_payload(expr, dreg); + break; + case EXPR_META: + nle = __netlink_gen_meta(expr, dreg); + break; + default: + assert(0); + break; + } + + return nle; +} + +static void netlink_gen_inner(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg, + const struct proto_desc *desc) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("inner"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_HDRSIZE, desc->inner.hdrsize); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_FLAGS, desc->inner.flags); + nftnl_expr_set_u32(nle, NFTNL_EXPR_INNER_TYPE, desc->inner.type); + nftnl_expr_set(nle, NFTNL_EXPR_INNER_EXPR, netlink_gen_inner_expr(expr, dreg), 0); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_payload(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + if (expr->payload.inner_desc) { + netlink_gen_inner(ctx, expr, dreg, expr->payload.inner_desc); + return; + } + + nle = __netlink_gen_payload(expr, dreg); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_exthdr(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + unsigned int offset = expr->exthdr.offset; + struct nftnl_expr *nle; + + nle = alloc_nft_expr("exthdr"); + netlink_put_register(nle, NFTNL_EXPR_EXTHDR_DREG, dreg); + nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, + expr->exthdr.raw_type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN, + div_round_up(expr->len, BITS_PER_BYTE)); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_FLAGS, expr->exthdr.flags); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_meta(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + if (expr->meta.inner_desc) { + netlink_gen_inner(ctx, expr, dreg, expr->meta.inner_desc); + return; + } + + nle = __netlink_gen_meta(expr, dreg); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_rt(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("rt"); + netlink_put_register(nle, NFTNL_EXPR_RT_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_RT_KEY, expr->rt.key); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_socket(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("socket"); + netlink_put_register(nle, NFTNL_EXPR_SOCKET_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_KEY, expr->socket.key); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SOCKET_LEVEL, expr->socket.level); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_osf(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("osf"); + netlink_put_register(nle, NFTNL_EXPR_OSF_DREG, dreg); + nftnl_expr_set_u8(nle, NFTNL_EXPR_OSF_TTL, expr->osf.ttl); + nftnl_expr_set_u32(nle, NFTNL_EXPR_OSF_FLAGS, expr->osf.flags); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_numgen(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("numgen"); + netlink_put_register(nle, NFTNL_EXPR_NG_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_TYPE, expr->numgen.type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_MODULUS, expr->numgen.mod); + nftnl_expr_set_u32(nle, NFTNL_EXPR_NG_OFFSET, expr->numgen.offset); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_ct(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("ct"); + netlink_put_register(nle, NFTNL_EXPR_CT_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CT_KEY, expr->ct.key); + if (expr->ct.direction >= 0) + nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR, + expr->ct.direction); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_map(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + enum nft_registers sreg; + int regspace = 0; + + assert(expr->mappings->etype == EXPR_SET_REF); + + if (dreg == NFT_REG_VERDICT) + sreg = get_register(ctx, expr->map); + else + sreg = dreg; + + /* suppress assert in netlink_gen_expr */ + if (expr->map->etype == EXPR_CONCAT) { + regspace = netlink_register_space(expr->map->len); + ctx->reg_low += regspace; + } + + netlink_gen_expr(ctx, expr->map, sreg); + ctx->reg_low -= regspace; + + nle = alloc_nft_expr("lookup"); + netlink_put_register(nle, NFTNL_EXPR_LOOKUP_SREG, sreg); + netlink_put_register(nle, NFTNL_EXPR_LOOKUP_DREG, dreg); + nftnl_expr_set_str(nle, NFTNL_EXPR_LOOKUP_SET, + expr->mappings->set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_SET_ID, + expr->mappings->set->handle.set_id); + + if (dreg == NFT_REG_VERDICT) + release_register(ctx, expr->map); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + enum nft_registers sreg; + + assert(expr->right->etype == EXPR_SET_REF); + assert(dreg == NFT_REG_VERDICT); + + sreg = get_register(ctx, expr->left); + netlink_gen_expr(ctx, expr->left, sreg); + + nle = alloc_nft_expr("lookup"); + netlink_put_register(nle, NFTNL_EXPR_LOOKUP_SREG, sreg); + nftnl_expr_set_str(nle, NFTNL_EXPR_LOOKUP_SET, + expr->right->set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_SET_ID, + expr->right->set->handle.set_id); + if (expr->op == OP_NEQ) + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOOKUP_FLAGS, NFT_LOOKUP_F_INV); + + release_register(ctx, expr->left); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static enum nft_cmp_ops netlink_gen_cmp_op(enum ops op) +{ + switch (op) { + case OP_EQ: + case OP_IMPLICIT: + 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("invalid comparison operation %u\n", op); + } +} + +static struct expr *netlink_gen_prefix(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers sreg) +{ + struct nft_data_linearize nld, zero = {}; + struct nftnl_expr *nle; + mpz_t mask; + + mpz_init(mask); + mpz_prefixmask(mask, expr->right->len, expr->right->prefix_len); + netlink_gen_raw_data(mask, expr->right->byteorder, + expr->right->len / BITS_PER_BYTE, &nld); + mpz_clear(mask); + + zero.len = nld.len; + + nle = alloc_nft_expr("bitwise"); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, nld.len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld.value, nld.len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &zero.value, zero.len); + nft_rule_add_expr(ctx, nle, &expr->location); + + return expr->right->prefix; +} + +static void netlink_gen_range(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct expr *range = expr->right; + struct nftnl_expr *nle; + enum nft_registers sreg; + struct nft_data_linearize nld; + + assert(dreg == NFT_REG_VERDICT); + + sreg = get_register(ctx, expr->left); + netlink_gen_expr(ctx, expr->left, sreg); + + switch (expr->op) { + case OP_NEQ: + nle = alloc_nft_expr("range"); + netlink_put_register(nle, NFTNL_EXPR_RANGE_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_RANGE_OP, NFT_RANGE_NEQ); + netlink_gen_data(range->left, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_RANGE_FROM_DATA, + nld.value, nld.len); + netlink_gen_data(range->right, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_RANGE_TO_DATA, + nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + break; + case OP_EQ: + case OP_IMPLICIT: + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, + netlink_gen_cmp_op(OP_GTE)); + netlink_gen_data(range->left, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, + netlink_gen_cmp_op(OP_LTE)); + netlink_gen_data(range->right, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + break; + default: + BUG("invalid range operation %u\n", expr->op); + + } + + release_register(ctx, expr->left); +} + +static void netlink_gen_flagcmp(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + struct nft_data_linearize nld, nld2; + enum nft_registers sreg; + unsigned int len; + mpz_t zero; + + assert(dreg == NFT_REG_VERDICT); + + sreg = get_register(ctx, expr->left); + netlink_gen_expr(ctx, expr->left, sreg); + len = div_round_up(expr->left->len, BITS_PER_BYTE); + + mpz_init_set_ui(zero, 0); + + netlink_gen_raw_data(zero, expr->right->byteorder, len, &nld); + netlink_gen_data(expr->right, &nld2); + + if (expr->left->etype == EXPR_BINOP) { + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld2.value, nld2.len); + nft_rule_add_expr(ctx, nle, &expr->location); + } else { + nle = alloc_nft_expr("bitwise"); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, sreg); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, &nld2.value, nld2.len); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, &nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + if (expr->op == OP_NEG) + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_EQ); + else + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, NFT_CMP_NEQ); + + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, nld.len); + nft_rule_add_expr(ctx, nle, &expr->location); + } + + mpz_clear(zero); + release_register(ctx, expr->left); +} + +static void netlink_gen_relational(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nft_data_linearize nld; + struct nftnl_expr *nle; + enum nft_registers sreg; + struct expr *right; + int len; + + assert(dreg == NFT_REG_VERDICT); + + switch (expr->op) { + case OP_IMPLICIT: + case OP_EQ: + case OP_NEQ: + case OP_LT: + case OP_GT: + case OP_LTE: + case OP_GTE: + case OP_NEG: + break; + default: + BUG("invalid relational operation %u\n", expr->op); + } + + switch (expr->right->etype) { + case EXPR_RANGE: + return netlink_gen_range(ctx, expr, dreg); + case EXPR_SET: + case EXPR_SET_REF: + return netlink_gen_lookup(ctx, expr, dreg); + case EXPR_LIST: + return netlink_gen_flagcmp(ctx, expr, dreg); + case EXPR_PREFIX: + sreg = get_register(ctx, expr->left); + if (expr_basetype(expr->left)->type != TYPE_STRING && + (expr->right->byteorder != BYTEORDER_BIG_ENDIAN || + !expr->right->prefix_len || + expr->right->prefix_len % BITS_PER_BYTE)) { + len = div_round_up(expr->right->len, BITS_PER_BYTE); + netlink_gen_expr(ctx, expr->left, sreg); + right = netlink_gen_prefix(ctx, expr, sreg); + } else { + len = div_round_up(expr->right->prefix_len, BITS_PER_BYTE); + right = expr->right->prefix; + expr->left->len = expr->right->prefix_len; + netlink_gen_expr(ctx, expr->left, sreg); + } + break; + default: + if ((expr->op == OP_IMPLICIT || expr->op == OP_NEG) && + expr->right->dtype->basetype != NULL && + expr->right->dtype->basetype->type == TYPE_BITMASK) + return netlink_gen_flagcmp(ctx, expr, dreg); + + sreg = get_register(ctx, expr->left); + len = div_round_up(expr->right->len, BITS_PER_BYTE); + right = expr->right; + netlink_gen_expr(ctx, expr->left, sreg); + break; + } + + nle = alloc_nft_expr("cmp"); + netlink_put_register(nle, NFTNL_EXPR_CMP_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CMP_OP, + netlink_gen_cmp_op(expr->op)); + netlink_gen_data(right, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_CMP_DATA, nld.value, len); + release_register(ctx, expr->left); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +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_shift(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + enum nft_bitwise_ops op = expr->op == OP_LSHIFT ? + NFT_BITWISE_LSHIFT : NFT_BITWISE_RSHIFT; + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + struct nft_data_linearize nld; + struct nftnl_expr *nle; + + netlink_gen_expr(ctx, expr->left, dreg); + + nle = alloc_nft_expr("bitwise"); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, dreg); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, op); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len); + + netlink_gen_raw_data(expr->right->value, expr->right->byteorder, + sizeof(uint32_t), &nld); + + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_DATA, nld.value, + nld.len); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_bitwise(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + struct nft_data_linearize 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 = (struct expr *) expr; + while (left->etype == EXPR_BINOP && left->left != NULL && + (left->op == OP_AND || left->op == OP_OR || left->op == OP_XOR)) + 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("invalid binary operation %u\n", i->op); + } + } + + len = div_round_up(expr->len, BITS_PER_BYTE); + + nle = alloc_nft_expr("bitwise"); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_SREG, dreg); + netlink_put_register(nle, NFTNL_EXPR_BITWISE_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_OP, NFT_BITWISE_BOOL); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BITWISE_LEN, len); + + netlink_gen_raw_data(mask, expr->byteorder, len, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_MASK, nld.value, nld.len); + netlink_gen_raw_data(xor, expr->byteorder, len, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_BITWISE_XOR, nld.value, nld.len); + + mpz_clear(tmp); + mpz_clear(val); + mpz_clear(xor); + mpz_clear(mask); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_binop(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + switch(expr->op) { + case OP_LSHIFT: + case OP_RSHIFT: + netlink_gen_shift(ctx, expr, dreg); + break; + default: + netlink_gen_bitwise(ctx, expr, dreg); + break; + } +} + +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_NTOH; + default: + BUG("invalid unary operation %u\n", op); + } +} + +static void netlink_gen_unary(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + int byte_size; + + if ((expr->arg->len % 64) == 0) + byte_size = 8; + else if ((expr->arg->len % 32) == 0) + byte_size = 4; + else + byte_size = 2; + + netlink_gen_expr(ctx, expr->arg, dreg); + + nle = alloc_nft_expr("byteorder"); + netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_SREG, dreg); + netlink_put_register(nle, NFTNL_EXPR_BYTEORDER_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_LEN, + div_round_up(expr->len, BITS_PER_BYTE)); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_SIZE, + byte_size); + nftnl_expr_set_u32(nle, NFTNL_EXPR_BYTEORDER_OP, + netlink_gen_unary_op(expr->op)); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_immediate(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + const struct location *loc = &expr->location; + struct nft_data_linearize nld; + struct nftnl_expr *nle; + + nle = alloc_nft_expr("immediate"); + netlink_put_register(nle, NFTNL_EXPR_IMM_DREG, dreg); + netlink_gen_data(expr, &nld); + switch (expr->etype) { + case EXPR_VALUE: + nftnl_expr_set(nle, NFTNL_EXPR_IMM_DATA, nld.value, nld.len); + break; + case EXPR_VERDICT: + if (expr->chain) { + nftnl_expr_set_str(nle, NFTNL_EXPR_IMM_CHAIN, + nld.chain); + loc = &expr->chain->location; + } else if (expr->chain_id) { + nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_CHAIN_ID, + nld.chain_id); + } + nftnl_expr_set_u32(nle, NFTNL_EXPR_IMM_VERDICT, nld.verdict); + break; + default: + break; + } + nft_rule_add_expr(ctx, nle, loc); +} + +static void netlink_gen_xfrm(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("xfrm"); + netlink_put_register(nle, NFTNL_EXPR_XFRM_DREG, dreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_KEY, expr->xfrm.key); + nftnl_expr_set_u8(nle, NFTNL_EXPR_XFRM_DIR, expr->xfrm.direction); + nftnl_expr_set_u32(nle, NFTNL_EXPR_XFRM_SPNUM, expr->xfrm.spnum); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, + const struct expr *expr, + enum nft_registers dreg) +{ + assert(dreg < ctx->reg_low); + + switch (expr->etype) { + 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_RT: + return netlink_gen_rt(ctx, expr, dreg); + case EXPR_CT: + return netlink_gen_ct(ctx, expr, dreg); + case EXPR_SET_ELEM: + return netlink_gen_expr(ctx, expr->key, dreg); + case EXPR_NUMGEN: + return netlink_gen_numgen(ctx, expr, dreg); + case EXPR_HASH: + return netlink_gen_hash(ctx, expr, dreg); + case EXPR_FIB: + return netlink_gen_fib(ctx, expr, dreg); + case EXPR_SOCKET: + return netlink_gen_socket(ctx, expr, dreg); + case EXPR_OSF: + return netlink_gen_osf(ctx, expr, dreg); + case EXPR_XFRM: + return netlink_gen_xfrm(ctx, expr, dreg); + default: + BUG("unknown expression type %s\n", expr_name(expr)); + } +} + +static void netlink_gen_objref_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct expr *expr = stmt->objref.expr; + struct nft_data_linearize nld; + struct nftnl_expr *nle; + uint32_t sreg_key; + + nle = alloc_nft_expr("objref"); + switch (expr->etype) { + case EXPR_MAP: + sreg_key = get_register(ctx, expr->map); + netlink_gen_expr(ctx, expr->map, sreg_key); + release_register(ctx, expr->map); + + nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_SET_SREG, sreg_key); + nftnl_expr_set_str(nle, NFTNL_EXPR_OBJREF_SET_NAME, + expr->mappings->set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_SET_ID, + expr->mappings->set->handle.set_id); + break; + case EXPR_VALUE: + netlink_gen_data(stmt->objref.expr, &nld); + nftnl_expr_set(nle, NFTNL_EXPR_OBJREF_IMM_NAME, + nld.value, nld.len); + nftnl_expr_set_u32(nle, NFTNL_EXPR_OBJREF_IMM_TYPE, + stmt->objref.type); + break; + default: + BUG("unsupported expression %u\n", expr->etype); + } + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static struct nftnl_expr *netlink_gen_connlimit_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("connlimit"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CONNLIMIT_COUNT, + stmt->connlimit.count); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CONNLIMIT_FLAGS, + stmt->connlimit.flags); + + return nle; +} + +static struct nftnl_expr *netlink_gen_counter_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("counter"); + if (stmt->counter.packets) { + nftnl_expr_set_u64(nle, NFTNL_EXPR_CTR_PACKETS, + stmt->counter.packets); + } + if (stmt->counter.bytes) { + nftnl_expr_set_u64(nle, NFTNL_EXPR_CTR_BYTES, + stmt->counter.bytes); + } + + return nle; +} + +static struct nftnl_expr *netlink_gen_limit_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("limit"); + nftnl_expr_set_u64(nle, NFTNL_EXPR_LIMIT_RATE, stmt->limit.rate); + nftnl_expr_set_u64(nle, NFTNL_EXPR_LIMIT_UNIT, stmt->limit.unit); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LIMIT_TYPE, stmt->limit.type); + if (stmt->limit.burst > 0) + nftnl_expr_set_u32(nle, NFTNL_EXPR_LIMIT_BURST, + stmt->limit.burst); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LIMIT_FLAGS, stmt->limit.flags); + + return nle; +} + +static struct nftnl_expr *netlink_gen_quota_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("quota"); + nftnl_expr_set_u64(nle, NFTNL_EXPR_QUOTA_BYTES, stmt->quota.bytes); + nftnl_expr_set_u64(nle, NFTNL_EXPR_QUOTA_CONSUMED, stmt->quota.used); + nftnl_expr_set_u32(nle, NFTNL_EXPR_QUOTA_FLAGS, stmt->quota.flags); + + return nle; +} + +static struct nftnl_expr *netlink_gen_last_stmt(const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("last"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_LAST_SET, stmt->last.set); + nftnl_expr_set_u64(nle, NFTNL_EXPR_LAST_MSECS, stmt->last.used); + + return nle; +} + +struct nftnl_expr *netlink_gen_stmt_stateful(const struct stmt *stmt) +{ + switch (stmt->ops->type) { + case STMT_CONNLIMIT: + return netlink_gen_connlimit_stmt(stmt); + case STMT_COUNTER: + return netlink_gen_counter_stmt(stmt); + case STMT_LIMIT: + return netlink_gen_limit_stmt(stmt); + case STMT_QUOTA: + return netlink_gen_quota_stmt(stmt); + case STMT_LAST: + return netlink_gen_last_stmt(stmt); + default: + BUG("unknown stateful statement type %s\n", stmt->ops->name); + } +} + +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 bool payload_needs_l4csum_update_pseudohdr(const struct expr *expr, + const struct proto_desc *desc) +{ + int i; + + for (i = 0; i < PROTO_HDRS_MAX; i++) { + if (payload_hdr_field(expr) == desc->pseudohdr[i]) + return true; + } + return false; +} + +static void netlink_gen_exthdr_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + const struct expr *expr; + enum nft_registers sreg; + unsigned int offset; + + sreg = get_register(ctx, stmt->exthdr.val); + netlink_gen_expr(ctx, stmt->exthdr.val, sreg); + release_register(ctx, stmt->exthdr.val); + + expr = stmt->exthdr.expr; + + offset = expr->exthdr.offset; + + nle = alloc_nft_expr("exthdr"); + netlink_put_register(nle, NFTNL_EXPR_EXTHDR_SREG, sreg); + nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, + expr->exthdr.raw_type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OFFSET, offset / BITS_PER_BYTE); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_LEN, + div_round_up(expr->len, BITS_PER_BYTE)); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_payload_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + const struct proto_desc *desc; + const struct expr *expr; + enum nft_registers sreg; + unsigned int csum_off; + + sreg = get_register(ctx, stmt->payload.val); + netlink_gen_expr(ctx, stmt->payload.val, sreg); + release_register(ctx, stmt->payload.val); + + expr = stmt->payload.expr; + + csum_off = 0; + desc = expr->payload.desc; + if (desc != NULL && desc->checksum_key) + csum_off = desc->templates[desc->checksum_key].offset; + + nle = alloc_nft_expr("payload"); + netlink_put_register(nle, NFTNL_EXPR_PAYLOAD_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_BASE, + expr->payload.base - 1); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_OFFSET, + expr->payload.offset / BITS_PER_BYTE); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_LEN, + expr->len / BITS_PER_BYTE); + if (csum_off) { + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_TYPE, + desc->checksum_type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_CSUM_OFFSET, + csum_off / BITS_PER_BYTE); + } + if ((expr->payload.base == PROTO_BASE_NETWORK_HDR && desc && + payload_needs_l4csum_update_pseudohdr(expr, desc)) || + expr->payload.base == PROTO_BASE_INNER_HDR) + nftnl_expr_set_u32(nle, NFTNL_EXPR_PAYLOAD_FLAGS, + NFT_PAYLOAD_L4CSUM_PSEUDOHDR); + + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_meta_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers sreg; + + sreg = get_register(ctx, stmt->meta.expr); + netlink_gen_expr(ctx, stmt->meta.expr, sreg); + release_register(ctx, stmt->meta.expr); + + nle = alloc_nft_expr("meta"); + netlink_put_register(nle, NFTNL_EXPR_META_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_META_KEY, stmt->meta.key); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_log_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("log"); + if (stmt->log.prefix != NULL) { + char prefix[NF_LOG_PREFIXLEN] = {}; + + expr_to_string(stmt->log.prefix, prefix); + nftnl_expr_set_str(nle, NFTNL_EXPR_LOG_PREFIX, prefix); + } + if (stmt->log.flags & STMT_LOG_GROUP) { + nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_GROUP, stmt->log.group); + if (stmt->log.flags & STMT_LOG_SNAPLEN) + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_SNAPLEN, + stmt->log.snaplen); + if (stmt->log.flags & STMT_LOG_QTHRESHOLD) + nftnl_expr_set_u16(nle, NFTNL_EXPR_LOG_QTHRESHOLD, + stmt->log.qthreshold); + } else { + if (stmt->log.flags & STMT_LOG_LEVEL) + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_LEVEL, + stmt->log.level); + if (stmt->log.logflags) + nftnl_expr_set_u32(nle, NFTNL_EXPR_LOG_FLAGS, + stmt->log.logflags); + } + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_reject_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("reject"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_REJECT_TYPE, stmt->reject.type); + if (stmt->reject.icmp_code != -1) + nftnl_expr_set_u8(nle, NFTNL_EXPR_REJECT_CODE, + stmt->reject.icmp_code); + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static unsigned int nat_addrlen(uint8_t family) +{ + switch (family) { + case NFPROTO_IPV4: return 32; + case NFPROTO_IPV6: return 128; + } + + BUG("invalid nat family %u\n", family); + return 0; +} + +static void netlink_gen_nat_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers amin_reg, amax_reg; + enum nft_registers pmin_reg, pmax_reg; + uint8_t family = 0; + int registers = 0; + int nftnl_flag_attr; + int nftnl_reg_pmin, nftnl_reg_pmax; + + switch (stmt->nat.type) { + case NFT_NAT_SNAT: + case NFT_NAT_DNAT: + nle = alloc_nft_expr("nat"); + nftnl_expr_set_u32(nle, NFTNL_EXPR_NAT_TYPE, stmt->nat.type); + + family = stmt->nat.family; + nftnl_expr_set_u32(nle, NFTNL_EXPR_NAT_FAMILY, family); + + nftnl_flag_attr = NFTNL_EXPR_NAT_FLAGS; + nftnl_reg_pmin = NFTNL_EXPR_NAT_REG_PROTO_MIN; + nftnl_reg_pmax = NFTNL_EXPR_NAT_REG_PROTO_MAX; + break; + case NFT_NAT_MASQ: + nle = alloc_nft_expr("masq"); + + nftnl_flag_attr = NFTNL_EXPR_MASQ_FLAGS; + nftnl_reg_pmin = NFTNL_EXPR_MASQ_REG_PROTO_MIN; + nftnl_reg_pmax = NFTNL_EXPR_MASQ_REG_PROTO_MAX; + break; + case NFT_NAT_REDIR: + nle = alloc_nft_expr("redir"); + + nftnl_flag_attr = NFTNL_EXPR_REDIR_FLAGS; + nftnl_reg_pmin = NFTNL_EXPR_REDIR_REG_PROTO_MIN; + nftnl_reg_pmax = NFTNL_EXPR_REDIR_REG_PROTO_MAX; + break; + default: + BUG("unknown nat type %d\n", stmt->nat.type); + break; + } + + if (stmt->nat.flags != 0) + nftnl_expr_set_u32(nle, nftnl_flag_attr, stmt->nat.flags); + + if (stmt->nat.addr) { + amin_reg = get_register(ctx, NULL); + registers++; + + if (stmt->nat.addr->etype == EXPR_RANGE) { + amax_reg = get_register(ctx, NULL); + registers++; + + netlink_gen_expr(ctx, stmt->nat.addr->left, amin_reg); + netlink_gen_expr(ctx, stmt->nat.addr->right, amax_reg); + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MIN, + amin_reg); + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, + amax_reg); + } else { + netlink_gen_expr(ctx, stmt->nat.addr, amin_reg); + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MIN, + amin_reg); + if (stmt->nat.addr->etype == EXPR_MAP && + stmt->nat.addr->mappings->set->data->flags & EXPR_F_INTERVAL) { + amin_reg += netlink_register_space(nat_addrlen(family)); + if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) { + netlink_put_register(nle, nftnl_reg_pmin, + amin_reg); + } else { + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, + amin_reg); + } + } + } + + if (stmt->nat.type_flags & STMT_NAT_F_CONCAT) { + /* nat_stmt evaluation step doesn't allow + * STMT_NAT_F_CONCAT && stmt->nat.proto. + */ + assert(stmt->nat.proto == NULL); + + pmin_reg = amin_reg; + + if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) { + pmin_reg += netlink_register_space(nat_addrlen(family)); + netlink_put_register(nle, NFTNL_EXPR_NAT_REG_ADDR_MAX, + pmin_reg); + } + + /* if STMT_NAT_F_CONCAT is set, the mapped type is a + * concatenation of 'addr . inet_service'. + * The map lookup will then return the + * concatenated value, so we need to skip + * the address and use the register that + * will hold the inet_service part. + */ + pmin_reg += netlink_register_space(nat_addrlen(family)); + if (stmt->nat.type_flags & STMT_NAT_F_INTERVAL) + netlink_put_register(nle, nftnl_reg_pmax, pmin_reg); + else + netlink_put_register(nle, nftnl_reg_pmin, pmin_reg); + } + } + + if (stmt->nat.proto) { + pmin_reg = get_register(ctx, NULL); + registers++; + + if (stmt->nat.proto->etype == EXPR_RANGE) { + pmax_reg = get_register(ctx, NULL); + registers++; + + netlink_gen_expr(ctx, stmt->nat.proto->left, pmin_reg); + netlink_gen_expr(ctx, stmt->nat.proto->right, pmax_reg); + netlink_put_register(nle, nftnl_reg_pmin, pmin_reg); + netlink_put_register(nle, nftnl_reg_pmax, pmax_reg); + } else { + netlink_gen_expr(ctx, stmt->nat.proto, pmin_reg); + netlink_put_register(nle, nftnl_reg_pmin, pmin_reg); + } + } + + while (registers > 0) { + release_register(ctx, NULL); + registers--; + } + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_tproxy_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers addr_reg; + enum nft_registers port_reg; + int registers = 0; + const int family = stmt->tproxy.family; + int nftnl_reg_port; + + nle = alloc_nft_expr("tproxy"); + + nftnl_expr_set_u32(nle, NFTNL_EXPR_TPROXY_FAMILY, family); + + nftnl_reg_port = NFTNL_EXPR_TPROXY_REG_PORT; + + if (stmt->tproxy.addr) { + addr_reg = get_register(ctx, NULL); + registers++; + netlink_gen_expr(ctx, stmt->tproxy.addr, addr_reg); + netlink_put_register(nle, NFTNL_EXPR_TPROXY_REG_ADDR, + addr_reg); + } + + if (stmt->tproxy.port) { + port_reg = get_register(ctx, NULL); + registers++; + netlink_gen_expr(ctx, stmt->tproxy.port, port_reg); + netlink_put_register(nle, nftnl_reg_port, port_reg); + } + + while (registers > 0) { + release_register(ctx, NULL); + registers--; + } + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_synproxy_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("synproxy"); + nftnl_expr_set_u16(nle, NFTNL_EXPR_SYNPROXY_MSS, stmt->synproxy.mss); + nftnl_expr_set_u8(nle, NFTNL_EXPR_SYNPROXY_WSCALE, + stmt->synproxy.wscale); + nftnl_expr_set_u32(nle, NFTNL_EXPR_SYNPROXY_FLAGS, + stmt->synproxy.flags); + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_dup_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers sreg1, sreg2; + + nle = alloc_nft_expr("dup"); + + if (stmt->dup.to != NULL) { + if (stmt->dup.to->dtype == &ifindex_type) { + sreg1 = get_register(ctx, stmt->dup.to); + netlink_gen_expr(ctx, stmt->dup.to, sreg1); + netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_DEV, sreg1); + } else { + sreg1 = get_register(ctx, stmt->dup.to); + netlink_gen_expr(ctx, stmt->dup.to, sreg1); + netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_ADDR, sreg1); + } + } + if (stmt->dup.dev != NULL) { + sreg2 = get_register(ctx, stmt->dup.dev); + netlink_gen_expr(ctx, stmt->dup.dev, sreg2); + netlink_put_register(nle, NFTNL_EXPR_DUP_SREG_DEV, sreg2); + release_register(ctx, stmt->dup.dev); + } + if (stmt->dup.to != NULL) + release_register(ctx, stmt->dup.to); + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_fwd_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + enum nft_registers sreg1, sreg2; + struct nftnl_expr *nle; + + nle = alloc_nft_expr("fwd"); + + sreg1 = get_register(ctx, stmt->fwd.dev); + netlink_gen_expr(ctx, stmt->fwd.dev, sreg1); + netlink_put_register(nle, NFTNL_EXPR_FWD_SREG_DEV, sreg1); + + if (stmt->fwd.addr != NULL) { + sreg2 = get_register(ctx, stmt->fwd.addr); + netlink_gen_expr(ctx, stmt->fwd.addr, sreg2); + netlink_put_register(nle, NFTNL_EXPR_FWD_SREG_ADDR, sreg2); + release_register(ctx, stmt->fwd.addr); + } + release_register(ctx, stmt->fwd.dev); + + if (stmt->fwd.family) + nftnl_expr_set_u32(nle, NFTNL_EXPR_FWD_NFPROTO, + stmt->fwd.family); + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_optstrip_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle = alloc_nft_expr("exthdr"); + struct expr *expr = stmt->optstrip.expr; + + nftnl_expr_set_u8(nle, NFTNL_EXPR_EXTHDR_TYPE, + expr->exthdr.raw_type); + nftnl_expr_set_u32(nle, NFTNL_EXPR_EXTHDR_OP, expr->exthdr.op); + nft_rule_add_expr(ctx, nle, &expr->location); +} + +static void netlink_gen_queue_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + enum nft_registers sreg = 0; + struct nftnl_expr *nle; + uint16_t total_queues; + struct expr *expr; + mpz_t low, high; + + mpz_init2(low, 16); + mpz_init2(high, 16); + + expr = stmt->queue.queue; + + if (expr) { + if (expr->etype == EXPR_RANGE || expr->etype == EXPR_VALUE) { + range_expr_value_low(low, stmt->queue.queue); + range_expr_value_high(high, stmt->queue.queue); + } else { + sreg = get_register(ctx, expr); + netlink_gen_expr(ctx, expr, sreg); + release_register(ctx, expr); + } + } + + total_queues = mpz_get_uint16(high) - mpz_get_uint16(low) + 1; + + nle = alloc_nft_expr("queue"); + + if (sreg) { + netlink_put_register(nle, NFTNL_EXPR_QUEUE_SREG_QNUM, sreg); + } else { + nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_NUM, mpz_get_uint16(low)); + nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_TOTAL, total_queues); + } + + if (stmt->queue.flags) + nftnl_expr_set_u16(nle, NFTNL_EXPR_QUEUE_FLAGS, + stmt->queue.flags); + + nft_rule_add_expr(ctx, nle, &stmt->location); + + mpz_clear(low); + mpz_clear(high); +} + +static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers sreg; + + sreg = get_register(ctx, stmt->ct.expr); + netlink_gen_expr(ctx, stmt->ct.expr, sreg); + release_register(ctx, stmt->ct.expr); + + nle = alloc_nft_expr("ct"); + netlink_put_register(nle, NFTNL_EXPR_CT_SREG, sreg); + nftnl_expr_set_u32(nle, NFTNL_EXPR_CT_KEY, stmt->ct.key); + if (stmt->ct.direction >= 0) + nftnl_expr_set_u8(nle, NFTNL_EXPR_CT_DIR, + stmt->ct.direction); + + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_notrack_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("notrack"); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_flow_offload_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + nle = alloc_nft_expr("flow_offload"); + nftnl_expr_set_str(nle, NFTNL_EXPR_FLOW_TABLE_NAME, + stmt->flow.table_name); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct set *set = stmt->meter.set->set; + enum nft_registers sreg_key; + struct nftnl_expr *nle; + int num_stmts = 0; + struct stmt *this; + + sreg_key = get_register(ctx, stmt->set.key->key); + netlink_gen_expr(ctx, stmt->set.key->key, sreg_key); + release_register(ctx, stmt->set.key->key); + + nle = alloc_nft_expr("dynset"); + netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key); + if (stmt->set.key->timeout > 0) + nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT, + stmt->set.key->timeout); + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->set.op); + nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); + nft_rule_add_expr(ctx, nle, &stmt->location); + + list_for_each_entry(this, &stmt->set.stmt_list, list) + num_stmts++; + + if (num_stmts == 1) { + list_for_each_entry(this, &stmt->set.stmt_list, list) { + nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, + netlink_gen_stmt_stateful(this), 0); + } + } else if (num_stmts > 1) { + list_for_each_entry(this, &stmt->set.stmt_list, list) { + nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS, + netlink_gen_stmt_stateful(this)); + } + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_FLAGS, + NFT_DYNSET_F_EXPR); + } +} + +static void netlink_gen_map_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct set *set = stmt->map.set->set; + enum nft_registers sreg_data; + enum nft_registers sreg_key; + struct nftnl_expr *nle; + int num_stmts = 0; + struct stmt *this; + + sreg_key = get_register(ctx, stmt->map.key->key); + netlink_gen_expr(ctx, stmt->map.key->key, sreg_key); + + sreg_data = get_register(ctx, stmt->map.data); + netlink_gen_expr(ctx, stmt->map.data, sreg_data); + + release_register(ctx, stmt->map.key->key); + release_register(ctx, stmt->map.data); + + nle = alloc_nft_expr("dynset"); + netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key); + netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_DATA, sreg_data); + + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, stmt->map.op); + nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); + nft_rule_add_expr(ctx, nle, &stmt->location); + + if (stmt->map.key->timeout > 0) + nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT, + stmt->map.key->timeout); + + list_for_each_entry(this, &stmt->map.stmt_list, list) + num_stmts++; + + if (num_stmts == 1) { + list_for_each_entry(this, &stmt->map.stmt_list, list) { + nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, + netlink_gen_stmt_stateful(this), 0); + } + } else if (num_stmts > 1) { + list_for_each_entry(this, &stmt->map.stmt_list, list) { + nftnl_expr_add_expr(nle, NFTNL_EXPR_DYNSET_EXPRESSIONS, + netlink_gen_stmt_stateful(this)); + } + } +} + +static void netlink_gen_meter_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + enum nft_registers sreg_key; + enum nft_dynset_ops op; + struct set *set; + + sreg_key = get_register(ctx, stmt->meter.key->key); + netlink_gen_expr(ctx, stmt->meter.key->key, sreg_key); + release_register(ctx, stmt->meter.key->key); + + set = stmt->meter.set->set; + if (stmt->meter.key->timeout) + op = NFT_DYNSET_OP_UPDATE; + else + op = NFT_DYNSET_OP_ADD; + + nle = alloc_nft_expr("dynset"); + netlink_put_register(nle, NFTNL_EXPR_DYNSET_SREG_KEY, sreg_key); + if (stmt->meter.key->timeout) + nftnl_expr_set_u64(nle, NFTNL_EXPR_DYNSET_TIMEOUT, + stmt->meter.key->timeout); + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_OP, op); + nftnl_expr_set_str(nle, NFTNL_EXPR_DYNSET_SET_NAME, set->handle.set.name); + nftnl_expr_set_u32(nle, NFTNL_EXPR_DYNSET_SET_ID, set->handle.set_id); + nftnl_expr_set(nle, NFTNL_EXPR_DYNSET_EXPR, + netlink_gen_stmt_stateful(stmt->meter.stmt), 0); + nft_rule_add_expr(ctx, nle, &stmt->location); +} + +static void netlink_gen_chain_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + return netlink_gen_expr(ctx, stmt->chain.expr, NFT_REG_VERDICT); +} + +static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nftnl_expr *nle; + + 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_METER: + return netlink_gen_meter_stmt(ctx, stmt); + case STMT_EXTHDR: + return netlink_gen_exthdr_stmt(ctx, stmt); + case STMT_PAYLOAD: + return netlink_gen_payload_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_REJECT: + return netlink_gen_reject_stmt(ctx, stmt); + case STMT_NAT: + return netlink_gen_nat_stmt(ctx, stmt); + case STMT_TPROXY: + return netlink_gen_tproxy_stmt(ctx, stmt); + case STMT_SYNPROXY: + return netlink_gen_synproxy_stmt(ctx, stmt); + case STMT_DUP: + return netlink_gen_dup_stmt(ctx, stmt); + case STMT_QUEUE: + return netlink_gen_queue_stmt(ctx, stmt); + case STMT_CT: + return netlink_gen_ct_stmt(ctx, stmt); + case STMT_SET: + return netlink_gen_set_stmt(ctx, stmt); + case STMT_FWD: + return netlink_gen_fwd_stmt(ctx, stmt); + case STMT_CONNLIMIT: + case STMT_COUNTER: + case STMT_LIMIT: + case STMT_QUOTA: + case STMT_LAST: + nle = netlink_gen_stmt_stateful(stmt); + nft_rule_add_expr(ctx, nle, &stmt->location); + break; + case STMT_NOTRACK: + return netlink_gen_notrack_stmt(ctx, stmt); + case STMT_FLOW_OFFLOAD: + return netlink_gen_flow_offload_stmt(ctx, stmt); + case STMT_OBJREF: + return netlink_gen_objref_stmt(ctx, stmt); + case STMT_MAP: + return netlink_gen_map_stmt(ctx, stmt); + case STMT_CHAIN: + return netlink_gen_chain_stmt(ctx, stmt); + case STMT_OPTSTRIP: + return netlink_gen_optstrip_stmt(ctx, stmt); + default: + BUG("unknown statement type %s\n", stmt->ops->name); + } +} + +void netlink_linearize_init(struct netlink_linearize_ctx *lctx, + struct nftnl_rule *nlr) +{ + int i; + + memset(lctx, 0, sizeof(*lctx)); + lctx->reg_low = NFT_REG_1; + lctx->nlr = nlr; + lctx->expr_loc_htable = + xmalloc(sizeof(struct list_head) * NFT_EXPR_LOC_HSIZE); + for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) + init_list_head(&lctx->expr_loc_htable[i]); +} + +void netlink_linearize_fini(struct netlink_linearize_ctx *lctx) +{ + struct nft_expr_loc *eloc, *next; + int i; + + for (i = 0; i < NFT_EXPR_LOC_HSIZE; i++) { + list_for_each_entry_safe(eloc, next, &lctx->expr_loc_htable[i], hlist) + xfree(eloc); + } + xfree(lctx->expr_loc_htable); +} + +void netlink_linearize_rule(struct netlink_ctx *ctx, + const struct rule *rule, + struct netlink_linearize_ctx *lctx) +{ + const struct stmt *stmt; + + list_for_each_entry(stmt, &rule->stmts, list) + netlink_gen_stmt(lctx, stmt); + + if (rule->comment) { + struct nftnl_udata_buf *udata; + + udata = nftnl_udata_buf_alloc(NFT_USERDATA_MAXLEN); + if (!udata) + memory_allocation_error(); + + if (!nftnl_udata_put_strz(udata, NFTNL_UDATA_RULE_COMMENT, + rule->comment)) + memory_allocation_error(); + nftnl_rule_set_data(lctx->nlr, NFTNL_RULE_USERDATA, + nftnl_udata_buf_data(udata), + nftnl_udata_buf_len(udata)); + + nftnl_udata_buf_free(udata); + } + + if (ctx->nft->debug_mask & NFT_DEBUG_NETLINK) { + nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_TABLE, + rule->handle.table.name); + if (rule->handle.chain.name) + nftnl_rule_set_str(lctx->nlr, NFTNL_RULE_CHAIN, + rule->handle.chain.name); + + netlink_dump_rule(lctx->nlr, ctx); + + nftnl_rule_unset(lctx->nlr, NFTNL_RULE_CHAIN); + nftnl_rule_unset(lctx->nlr, NFTNL_RULE_TABLE); + } +} diff --git a/src/nfnl_osf.c b/src/nfnl_osf.c new file mode 100644 index 0000000..20a1bfe --- /dev/null +++ b/src/nfnl_osf.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2005 Evgeniy Polyakov <johnpol@2ka.mxt.ru> + * + * 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. + * + * + * Based on iptables/utils/nfnl_osf.c. + */ + +#include <nft.h> + +#include <sys/time.h> + +#include <ctype.h> +#include <errno.h> +#include <time.h> + +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#include <linux/unistd.h> + +#include <libmnl/libmnl.h> + +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nfnetlink_osf.h> +#include <mnl.h> +#include <osf.h> + +#define OPTDEL ',' +#define OSFPDEL ':' +#define MAXOPTSTRLEN 128 + +static struct nf_osf_opt IANA_opts[] = { + { .kind = 0, .length = 1,}, + { .kind=1, .length=1,}, + { .kind=2, .length=4,}, + { .kind=3, .length=3,}, + { .kind=4, .length=2,}, + { .kind=5, .length=1,}, /* SACK length is not defined */ + { .kind=6, .length=6,}, + { .kind=7, .length=6,}, + { .kind=8, .length=10,}, + { .kind=9, .length=2,}, + { .kind=10, .length=3,}, + { .kind=11, .length=1,}, /* CC: Suppose 1 */ + { .kind=12, .length=1,}, /* the same */ + { .kind=13, .length=1,}, /* and here too */ + { .kind=14, .length=3,}, + { .kind=15, .length=1,}, /* TCP Alternate Checksum Data. Length is not defined */ + { .kind=16, .length=1,}, + { .kind=17, .length=1,}, + { .kind=18, .length=3,}, + { .kind=19, .length=18,}, + { .kind=20, .length=1,}, + { .kind=21, .length=1,}, + { .kind=22, .length=1,}, + { .kind=23, .length=1,}, + { .kind=24, .length=1,}, + { .kind=25, .length=1,}, + { .kind=26, .length=1,}, +}; + +static char *nf_osf_strchr(char *ptr, char c) +{ + char *tmp; + + tmp = strchr(ptr, c); + if (tmp) + *tmp = '\0'; + + while (tmp && isspace(*(tmp + 1))) + tmp++; + + return tmp; +} + +static void nf_osf_parse_opt(struct nf_osf_opt *opt, __u16 *optnum, char *obuf, + int olen) +{ + int i, op; + char *ptr, wc; + unsigned long val; + + ptr = &obuf[0]; + i = 0; + while (ptr != NULL && i < olen && *ptr != 0) { + val = 0; + wc = OSF_WSS_PLAIN; + switch (obuf[i]) { + case 'N': + op = OSFOPT_NOP; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + *ptr = '\0'; + ptr++; + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + case 'S': + op = OSFOPT_SACKP; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + *ptr = '\0'; + ptr++; + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + case 'T': + op = OSFOPT_TS; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + *ptr = '\0'; + ptr++; + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + case 'W': + op = OSFOPT_WSO; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + switch (obuf[i + 1]) { + case '%': + wc = OSF_WSS_MODULO; + break; + case 'S': + wc = OSF_WSS_MSS; + break; + case 'T': + wc = OSF_WSS_MTU; + break; + default: + wc = OSF_WSS_PLAIN; + break; + } + + *ptr = '\0'; + ptr++; + if (wc) + val = strtoul(&obuf[i + 2], NULL, 10); + else + val = strtoul(&obuf[i + 1], NULL, 10); + i += (int)(ptr - &obuf[i]); + + } else + i++; + break; + case 'M': + op = OSFOPT_MSS; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + if (obuf[i + 1] == '%') + wc = OSF_WSS_MODULO; + *ptr = '\0'; + ptr++; + if (wc) + val = strtoul(&obuf[i + 2], NULL, 10); + else + val = strtoul(&obuf[i + 1], NULL, 10); + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + case 'E': + op = OSFOPT_EOL; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + *ptr = '\0'; + ptr++; + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + default: + op = OSFOPT_EMPTY; + ptr = nf_osf_strchr(&obuf[i], OPTDEL); + if (ptr) { + ptr++; + i += (int)(ptr - &obuf[i]); + } else + i++; + break; + } + + if (op != OSFOPT_EMPTY) { + opt[*optnum].kind = IANA_opts[op].kind; + opt[*optnum].length = IANA_opts[op].length; + opt[*optnum].wc.wc = wc; + opt[*optnum].wc.val = val; + (*optnum)++; + } + } +} + +static int osf_load_line(char *buffer, int len, int del, + struct netlink_ctx *ctx) +{ + int i, cnt = 0; + char obuf[MAXOPTSTRLEN]; + struct nf_osf_user_finger f; + char *pbeg, *pend; + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + memset(&f, 0, sizeof(struct nf_osf_user_finger)); + + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) + nft_print(&ctx->nft->output, "Loading '%s'.\n", buffer); + + for (i = 0; i < len && buffer[i] != '\0'; ++i) { + if (buffer[i] == ':') + cnt++; + } + + if (cnt != 8) { + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) + nft_print(&ctx->nft->output, "Wrong input line '%s': cnt: %d, must be 8, i: %d, must be %d.\n", buffer, cnt, i, len); + return -EINVAL; + } + + memset(obuf, 0, sizeof(obuf)); + + pbeg = buffer; + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + if (pbeg[0] == 'S') { + f.wss.wc = OSF_WSS_MSS; + if (pbeg[1] == '%') + f.wss.val = strtoul(&pbeg[2], NULL, 10); + else if (pbeg[1] == '*') + f.wss.val = 0; + else + f.wss.val = strtoul(&pbeg[1], NULL, 10); + } else if (pbeg[0] == 'T') { + f.wss.wc = OSF_WSS_MTU; + if (pbeg[1] == '%') + f.wss.val = strtoul(&pbeg[2], NULL, 10); + else if (pbeg[1] == '*') + f.wss.val = 0; + else + f.wss.val = strtoul(&pbeg[1], NULL, 10); + } else if (pbeg[0] == '%') { + f.wss.wc = OSF_WSS_MODULO; + f.wss.val = strtoul(&pbeg[1], NULL, 10); + } else if (isdigit(pbeg[0])) { + f.wss.wc = OSF_WSS_PLAIN; + f.wss.val = strtoul(&pbeg[0], NULL, 10); + } + + pbeg = pend + 1; + } + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + f.ttl = strtoul(pbeg, NULL, 10); + pbeg = pend + 1; + } + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + f.df = strtoul(pbeg, NULL, 10); + pbeg = pend + 1; + } + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + f.ss = strtoul(pbeg, NULL, 10); + pbeg = pend + 1; + } + + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + i = sizeof(obuf); + snprintf(obuf, i, "%.*s,", i - 2, pbeg); + pbeg = pend + 1; + } + + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + i = sizeof(f.genre); + if (pbeg[0] == '@' || pbeg[0] == '*') + pbeg++; + snprintf(f.genre, i, "%.*s", i - 1, pbeg); + pbeg = pend + 1; + } + + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + i = sizeof(f.version); + snprintf(f.version, i, "%.*s", i - 1, pbeg); + pbeg = pend + 1; + } + + pend = nf_osf_strchr(pbeg, OSFPDEL); + if (pend) { + *pend = '\0'; + i = sizeof(f.subtype); + snprintf(f.subtype, i, "%.*s", i - 1, pbeg); + pbeg = pend + 1; + } + + nf_osf_parse_opt(f.opt, &f.opt_num, obuf, sizeof(obuf)); + + memset(buf, 0, sizeof(buf)); + + if (del) { + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_REMOVE; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = ctx->seqnum; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + } else { + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_OSF << 8) | OSF_MSG_ADD; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; + nlh->nlmsg_seq = ctx->seqnum; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_UNSPEC; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + + mnl_attr_put(nlh, OSF_ATTR_FINGER, sizeof(struct nf_osf_user_finger), &f); + } + + return nft_mnl_talk(ctx, nlh, nlh->nlmsg_len, 0, NULL); +} + +#define OS_SIGNATURES DEFAULT_INCLUDE_PATH "/nftables/osf/pf.os" + +int nfnl_osf_load_fingerprints(struct netlink_ctx *ctx, int del) +{ + FILE *inf; + int err = 0; + char buf[1024]; + + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) + nft_print(&ctx->nft->output, "Opening OS signature file '%s'\n", + OS_SIGNATURES); + + inf = fopen(OS_SIGNATURES, "r"); + if (!inf) { + if (ctx->nft->debug_mask & NFT_DEBUG_MNL) + nft_print(&ctx->nft->output, "Failed to open file '%s'\n", + OS_SIGNATURES); + + return -1; + } + + while (fgets(buf, sizeof(buf), inf)) { + int len; + + if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') + continue; + + len = strlen(buf) - 1; + + if (len <= 0) + continue; + + buf[len] = '\0'; + + err = osf_load_line(buf, len, del, ctx); + if (err) + break; + + memset(buf, 0, sizeof(buf)); + } + + fclose(inf); + return err; +} diff --git a/src/nftutils.c b/src/nftutils.c new file mode 100644 index 0000000..ca178aa --- /dev/null +++ b/src/nftutils.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <nft.h> + +#include "nftutils.h" + +#include <netdb.h> + +/* Buffer size used for getprotobynumber_r() and similar. The manual comments + * that a buffer of 1024 should be sufficient "for most applications"(??), so + * let's double it. It still fits reasonably on the stack, so no need to + * choose a smaller one. */ +#define NETDB_BUFSIZE 2048 + +bool nft_getprotobynumber(int proto, char *out_name, size_t name_len) +{ + const struct protoent *result; + +#if HAVE_DECL_GETPROTOBYNUMBER_R + struct protoent result_buf; + char buf[NETDB_BUFSIZE]; + int r; + + r = getprotobynumber_r(proto, + &result_buf, + buf, + sizeof(buf), + (struct protoent **) &result); + if (r != 0 || result != &result_buf) + result = NULL; +#else + result = getprotobynumber(proto); +#endif + + if (!result) + return false; + + if (strlen(result->p_name) >= name_len) + return false; + strcpy(out_name, result->p_name); + return true; +} + +int nft_getprotobyname(const char *name) +{ + const struct protoent *result; + +#if HAVE_DECL_GETPROTOBYNAME_R + struct protoent result_buf; + char buf[NETDB_BUFSIZE]; + int r; + + r = getprotobyname_r(name, + &result_buf, + buf, + sizeof(buf), + (struct protoent **) &result); + if (r != 0 || result != &result_buf) + result = NULL; +#else + result = getprotobyname(name); +#endif + + if (!result) + return -1; + + if (result->p_proto < 0 || result->p_proto > UINT8_MAX) + return -1; + return (uint8_t) result->p_proto; +} + +bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len) +{ + const struct servent *result; + +#if HAVE_DECL_GETSERVBYPORT_R + struct servent result_buf; + char buf[NETDB_BUFSIZE]; + int r; + + r = getservbyport_r(port, + proto, + &result_buf, + buf, + sizeof(buf), + (struct servent**) &result); + if (r != 0 || result != &result_buf) + result = NULL; +#else + result = getservbyport(port, proto); +#endif + + if (!result) + return false; + + if (strlen(result->s_name) >= name_len) + return false; + strcpy(out_name, result->s_name); + return true; +} diff --git a/src/nftutils.h b/src/nftutils.h new file mode 100644 index 0000000..7db56f4 --- /dev/null +++ b/src/nftutils.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef NFTUTILS_H +#define NFTUTILS_H + +#include <stddef.h> + +/* The maximum buffer size for (struct protoent).p_name. It is excessively large, + * while still reasonably fitting on the stack. Arbitrarily chosen. */ +#define NFT_PROTONAME_MAXSIZE 1024 + +bool nft_getprotobynumber(int number, char *out_name, size_t name_len); +int nft_getprotobyname(const char *name); + +/* The maximum buffer size for (struct servent).s_name. It is excessively large, + * while still reasonably fitting on the stack. Arbitrarily chosen. */ +#define NFT_SERVNAME_MAXSIZE 1024 + +bool nft_getservbyport(int port, const char *proto, char *out_name, size_t name_len); + +#endif /* NFTUTILS_H */ diff --git a/src/numgen.c b/src/numgen.c new file mode 100644 index 0000000..3029fa5 --- /dev/null +++ b/src/numgen.c @@ -0,0 +1,140 @@ +/* + * Number generator expression definitions. + * + * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <expression.h> +#include <datatype.h> +#include <gmputil.h> +#include <numgen.h> +#include <utils.h> + +static const char *numgen_type[NFT_NG_RANDOM + 1] = { + [NFT_NG_INCREMENTAL] = "inc", + [NFT_NG_RANDOM] = "random", +}; + +static const char *numgen_type_str(enum nft_ng_types type) +{ + if (type > NFT_NG_RANDOM) + return "[unknown numgen]"; + + return numgen_type[type]; +} + +static void numgen_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + nft_print(octx, "numgen %s mod %u", + numgen_type_str(expr->numgen.type), + expr->numgen.mod); + if (expr->numgen.offset) + nft_print(octx, " offset %u", expr->numgen.offset); +} + +static bool numgen_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->numgen.type == e2->numgen.type && + e1->numgen.mod == e2->numgen.mod && + e1->numgen.offset == e2->numgen.offset; +} + +static void numgen_expr_clone(struct expr *new, const struct expr *expr) +{ + new->numgen.type = expr->numgen.type; + new->numgen.mod = expr->numgen.mod; + new->numgen.offset = expr->numgen.offset; +} + +#define NFTNL_UDATA_NUMGEN_TYPE 0 +#define NFTNL_UDATA_NUMGEN_MOD 1 +#define NFTNL_UDATA_NUMGEN_OFFSET 2 +#define NFTNL_UDATA_NUMGEN_MAX 3 + +static int numgen_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_NUMGEN_TYPE, expr->numgen.type); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_NUMGEN_MOD, expr->numgen.mod); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_NUMGEN_OFFSET, expr->numgen.offset); + + return 0; +} + +static int numgen_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_NUMGEN_TYPE: + case NFTNL_UDATA_NUMGEN_MOD: + case NFTNL_UDATA_NUMGEN_OFFSET: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *numgen_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_NUMGEN_MAX + 1] = {}; + enum nft_ng_types type; + uint32_t mod, offset; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + numgen_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_NUMGEN_TYPE] || + !ud[NFTNL_UDATA_NUMGEN_MOD] || + !ud[NFTNL_UDATA_NUMGEN_OFFSET]) + return NULL; + + type = nftnl_udata_get_u32(ud[NFTNL_UDATA_NUMGEN_TYPE]); + mod = nftnl_udata_get_u32(ud[NFTNL_UDATA_NUMGEN_MOD]); + offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_NUMGEN_OFFSET]); + + return numgen_expr_alloc(&internal_location, type, mod, offset); +} + +const struct expr_ops numgen_expr_ops = { + .type = EXPR_NUMGEN, + .name = "numgen", + .print = numgen_expr_print, + .json = numgen_expr_json, + .cmp = numgen_expr_cmp, + .clone = numgen_expr_clone, + .parse_udata = numgen_expr_parse_udata, + .build_udata = numgen_expr_build_udata, +}; + +struct expr *numgen_expr_alloc(const struct location *loc, + enum nft_ng_types type, uint32_t mod, + uint32_t offset) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_NUMGEN, &integer_type, + BYTEORDER_HOST_ENDIAN, 4 * BITS_PER_BYTE); + expr->numgen.type = type; + expr->numgen.mod = mod; + expr->numgen.offset = offset; + + return expr; +} diff --git a/src/optimize.c b/src/optimize.c new file mode 100644 index 0000000..27e0ffe --- /dev/null +++ b/src/optimize.c @@ -0,0 +1,1406 @@ +/* + * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +/* Funded through the NGI0 PET Fund established by NLnet (https://nlnet.nl) + * with support from the European Commission's Next Generation Internet + * programme. + */ + +#include <nft.h> + +#include <errno.h> +#include <inttypes.h> +#include <nftables.h> +#include <parser.h> +#include <expression.h> +#include <statement.h> +#include <utils.h> +#include <erec.h> +#include <linux/netfilter.h> + +#define MAX_STMTS 32 + +struct optimize_ctx { + struct stmt *stmt[MAX_STMTS]; + uint32_t num_stmts; + + struct stmt ***stmt_matrix; + struct rule **rule; + uint32_t num_rules; +}; + +static bool __expr_cmp(const struct expr *expr_a, const struct expr *expr_b) +{ + if (expr_a->etype != expr_b->etype) + return false; + + switch (expr_a->etype) { + case EXPR_PAYLOAD: + if (expr_a->payload.base != expr_b->payload.base) + return false; + if (expr_a->payload.offset != expr_b->payload.offset) + return false; + if (expr_a->payload.desc != expr_b->payload.desc) + return false; + if (expr_a->payload.inner_desc != expr_b->payload.inner_desc) + return false; + if (expr_a->payload.tmpl != expr_b->payload.tmpl) + return false; + break; + case EXPR_EXTHDR: + if (expr_a->exthdr.desc != expr_b->exthdr.desc) + return false; + if (expr_a->exthdr.tmpl != expr_b->exthdr.tmpl) + return false; + break; + case EXPR_META: + if (expr_a->meta.key != expr_b->meta.key) + return false; + if (expr_a->meta.base != expr_b->meta.base) + return false; + break; + case EXPR_CT: + if (expr_a->ct.key != expr_b->ct.key) + return false; + if (expr_a->ct.base != expr_b->ct.base) + return false; + if (expr_a->ct.direction != expr_b->ct.direction) + return false; + if (expr_a->ct.nfproto != expr_b->ct.nfproto) + return false; + break; + case EXPR_RT: + if (expr_a->rt.key != expr_b->rt.key) + return false; + break; + case EXPR_SOCKET: + if (expr_a->socket.key != expr_b->socket.key) + return false; + if (expr_a->socket.level != expr_b->socket.level) + return false; + break; + case EXPR_OSF: + if (expr_a->osf.ttl != expr_b->osf.ttl) + return false; + if (expr_a->osf.flags != expr_b->osf.flags) + return false; + break; + case EXPR_XFRM: + if (expr_a->xfrm.key != expr_b->xfrm.key) + return false; + if (expr_a->xfrm.direction != expr_b->xfrm.direction) + return false; + break; + case EXPR_FIB: + if (expr_a->fib.flags != expr_b->fib.flags) + return false; + if (expr_a->fib.result != expr_b->fib.result) + return false; + break; + case EXPR_NUMGEN: + if (expr_a->numgen.type != expr_b->numgen.type) + return false; + if (expr_a->numgen.mod != expr_b->numgen.mod) + return false; + if (expr_a->numgen.offset != expr_b->numgen.offset) + return false; + break; + case EXPR_HASH: + if (expr_a->hash.mod != expr_b->hash.mod) + return false; + if (expr_a->hash.seed_set != expr_b->hash.seed_set) + return false; + if (expr_a->hash.seed != expr_b->hash.seed) + return false; + if (expr_a->hash.offset != expr_b->hash.offset) + return false; + if (expr_a->hash.type != expr_b->hash.type) + return false; + break; + case EXPR_BINOP: + return __expr_cmp(expr_a->left, expr_b->left); + default: + return false; + } + + return true; +} + +static bool stmt_expr_supported(const struct expr *expr) +{ + switch (expr->right->etype) { + case EXPR_SYMBOL: + case EXPR_RANGE: + case EXPR_PREFIX: + case EXPR_SET: + case EXPR_LIST: + case EXPR_VALUE: + return true; + default: + break; + } + + return false; +} + +static bool expr_symbol_set(const struct expr *expr) +{ + return expr->right->etype == EXPR_SYMBOL && + expr->right->symtype == SYMBOL_SET; +} + +static bool __stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b, + bool fully_compare) +{ + struct expr *expr_a, *expr_b; + + if (stmt_a->ops->type != stmt_b->ops->type) + return false; + + switch (stmt_a->ops->type) { + case STMT_EXPRESSION: + expr_a = stmt_a->expr; + expr_b = stmt_b->expr; + + if (expr_a->op != expr_b->op) + return false; + if (expr_a->op != OP_IMPLICIT && expr_a->op != OP_EQ) + return false; + + if (fully_compare) { + if (!stmt_expr_supported(expr_a) || + !stmt_expr_supported(expr_b)) + return false; + + if (expr_symbol_set(expr_a) || + expr_symbol_set(expr_b)) + return false; + } + + return __expr_cmp(expr_a->left, expr_b->left); + case STMT_COUNTER: + case STMT_NOTRACK: + break; + case STMT_VERDICT: + if (!fully_compare) + break; + + expr_a = stmt_a->expr; + expr_b = stmt_b->expr; + + if (expr_a->etype != expr_b->etype) + return false; + + if (expr_a->etype == EXPR_MAP && + expr_b->etype == EXPR_MAP) + return __expr_cmp(expr_a->map, expr_b->map); + break; + case STMT_LOG: + if (stmt_a->log.snaplen != stmt_b->log.snaplen || + stmt_a->log.group != stmt_b->log.group || + stmt_a->log.qthreshold != stmt_b->log.qthreshold || + stmt_a->log.level != stmt_b->log.level || + stmt_a->log.logflags != stmt_b->log.logflags || + stmt_a->log.flags != stmt_b->log.flags) + return false; + + if (!!stmt_a->log.prefix ^ !!stmt_b->log.prefix) + return false; + + if (!stmt_a->log.prefix) + return true; + + if (stmt_a->log.prefix->etype != EXPR_VALUE || + stmt_b->log.prefix->etype != EXPR_VALUE || + mpz_cmp(stmt_a->log.prefix->value, stmt_b->log.prefix->value)) + return false; + break; + case STMT_REJECT: + if (stmt_a->reject.family != stmt_b->reject.family || + stmt_a->reject.type != stmt_b->reject.type || + stmt_a->reject.icmp_code != stmt_b->reject.icmp_code) + return false; + + if (!!stmt_a->reject.expr ^ !!stmt_b->reject.expr) + return false; + + if (!stmt_a->reject.expr) + return true; + + if (__expr_cmp(stmt_a->reject.expr, stmt_b->reject.expr)) + return false; + break; + case STMT_NAT: + if (stmt_a->nat.type != stmt_b->nat.type || + stmt_a->nat.flags != stmt_b->nat.flags || + stmt_a->nat.family != stmt_b->nat.family || + stmt_a->nat.type_flags != stmt_b->nat.type_flags) + return false; + + switch (stmt_a->nat.type) { + case NFT_NAT_SNAT: + case NFT_NAT_DNAT: + if ((stmt_a->nat.addr && + stmt_a->nat.addr->etype != EXPR_SYMBOL && + stmt_a->nat.addr->etype != EXPR_RANGE) || + (stmt_b->nat.addr && + stmt_b->nat.addr->etype != EXPR_SYMBOL && + stmt_b->nat.addr->etype != EXPR_RANGE) || + (stmt_a->nat.proto && + stmt_a->nat.proto->etype != EXPR_SYMBOL && + stmt_a->nat.proto->etype != EXPR_RANGE) || + (stmt_b->nat.proto && + stmt_b->nat.proto->etype != EXPR_SYMBOL && + stmt_b->nat.proto->etype != EXPR_RANGE)) + return false; + break; + case NFT_NAT_MASQ: + break; + case NFT_NAT_REDIR: + if ((stmt_a->nat.proto && + stmt_a->nat.proto->etype != EXPR_SYMBOL && + stmt_a->nat.proto->etype != EXPR_RANGE) || + (stmt_b->nat.proto && + stmt_b->nat.proto->etype != EXPR_SYMBOL && + stmt_b->nat.proto->etype != EXPR_RANGE)) + return false; + + /* it should be possible to infer implicit redirections + * such as: + * + * tcp dport 1234 redirect + * tcp dport 3456 redirect to :7890 + * merge: + * redirect to tcp dport map { 1234 : 1234, 3456 : 7890 } + * + * currently not implemented. + */ + if (fully_compare && + stmt_a->nat.type == NFT_NAT_REDIR && + stmt_b->nat.type == NFT_NAT_REDIR && + (!!stmt_a->nat.proto ^ !!stmt_b->nat.proto)) + return false; + + break; + default: + assert(0); + } + + return true; + default: + /* ... Merging anything else is yet unsupported. */ + return false; + } + + return true; +} + +static bool expr_verdict_eq(const struct expr *expr_a, const struct expr *expr_b) +{ + if (expr_a->verdict != expr_b->verdict) + return false; + if (expr_a->chain && expr_b->chain) { + if (expr_a->chain->etype != expr_b->chain->etype) + return false; + if (expr_a->chain->etype == EXPR_VALUE && + strcmp(expr_a->chain->identifier, expr_b->chain->identifier)) + return false; + } else if (expr_a->chain || expr_b->chain) { + return false; + } + + return true; +} + +static bool stmt_verdict_eq(const struct stmt *stmt_a, const struct stmt *stmt_b) +{ + struct expr *expr_a, *expr_b; + + assert (stmt_a->ops->type == STMT_VERDICT); + + expr_a = stmt_a->expr; + expr_b = stmt_b->expr; + if (expr_a->etype == EXPR_VERDICT && + expr_b->etype == EXPR_VERDICT) + return expr_verdict_eq(expr_a, expr_b); + + if (expr_a->etype == EXPR_MAP && + expr_b->etype == EXPR_MAP) + return __expr_cmp(expr_a->map, expr_b->map); + + return false; +} + +static bool stmt_type_find(struct optimize_ctx *ctx, const struct stmt *stmt) +{ + bool unsupported_exists = false; + uint32_t i; + + for (i = 0; i < ctx->num_stmts; i++) { + if (ctx->stmt[i]->ops->type == STMT_INVALID) + unsupported_exists = true; + + if (__stmt_type_eq(stmt, ctx->stmt[i], false)) + return true; + } + + switch (stmt->ops->type) { + case STMT_EXPRESSION: + case STMT_VERDICT: + case STMT_COUNTER: + case STMT_NOTRACK: + case STMT_LOG: + case STMT_NAT: + case STMT_REJECT: + break; + default: + /* add unsupported statement only once to statement matrix. */ + if (unsupported_exists) + return true; + break; + } + + return false; +} + +static struct stmt_ops unsupported_stmt_ops = { + .type = STMT_INVALID, + .name = "unsupported", +}; + +static int rule_collect_stmts(struct optimize_ctx *ctx, struct rule *rule) +{ + struct stmt *stmt, *clone; + + list_for_each_entry(stmt, &rule->stmts, list) { + if (stmt_type_find(ctx, stmt)) + continue; + + /* No refcounter available in statement objects, clone it to + * to store in the array of selectors. + */ + clone = stmt_alloc(&internal_location, stmt->ops); + switch (stmt->ops->type) { + case STMT_EXPRESSION: + if (stmt->expr->op != OP_IMPLICIT && + stmt->expr->op != OP_EQ) { + clone->ops = &unsupported_stmt_ops; + break; + } + if (stmt->expr->left->etype == EXPR_CONCAT) { + clone->ops = &unsupported_stmt_ops; + break; + } + /* fall-through */ + case STMT_VERDICT: + clone->expr = expr_get(stmt->expr); + break; + case STMT_COUNTER: + case STMT_NOTRACK: + break; + case STMT_LOG: + memcpy(&clone->log, &stmt->log, sizeof(clone->log)); + if (stmt->log.prefix) + clone->log.prefix = expr_get(stmt->log.prefix); + break; + case STMT_NAT: + if ((stmt->nat.addr && + stmt->nat.addr->etype == EXPR_MAP) || + (stmt->nat.proto && + stmt->nat.proto->etype == EXPR_MAP)) { + clone->ops = &unsupported_stmt_ops; + break; + } + clone->nat.type = stmt->nat.type; + clone->nat.family = stmt->nat.family; + if (stmt->nat.addr) + clone->nat.addr = expr_clone(stmt->nat.addr); + if (stmt->nat.proto) + clone->nat.proto = expr_clone(stmt->nat.proto); + clone->nat.flags = stmt->nat.flags; + clone->nat.type_flags = stmt->nat.type_flags; + break; + case STMT_REJECT: + if (stmt->reject.expr) + clone->reject.expr = expr_get(stmt->reject.expr); + clone->reject.type = stmt->reject.type; + clone->reject.icmp_code = stmt->reject.icmp_code; + clone->reject.family = stmt->reject.family; + break; + default: + clone->ops = &unsupported_stmt_ops; + break; + } + + ctx->stmt[ctx->num_stmts++] = clone; + if (ctx->num_stmts >= MAX_STMTS) + return -1; + } + + return 0; +} + +static int unsupported_in_stmt_matrix(const struct optimize_ctx *ctx) +{ + uint32_t i; + + for (i = 0; i < ctx->num_stmts; i++) { + if (ctx->stmt[i]->ops->type == STMT_INVALID) + return i; + } + /* this should not happen. */ + return -1; +} + +static int cmd_stmt_find_in_stmt_matrix(struct optimize_ctx *ctx, struct stmt *stmt) +{ + uint32_t i; + + for (i = 0; i < ctx->num_stmts; i++) { + if (__stmt_type_eq(stmt, ctx->stmt[i], false)) + return i; + } + + return -1; +} + +static struct stmt unsupported_stmt = { + .ops = &unsupported_stmt_ops, +}; + +static void rule_build_stmt_matrix_stmts(struct optimize_ctx *ctx, + struct rule *rule, uint32_t *i) +{ + struct stmt *stmt; + int k; + + list_for_each_entry(stmt, &rule->stmts, list) { + k = cmd_stmt_find_in_stmt_matrix(ctx, stmt); + if (k < 0) { + k = unsupported_in_stmt_matrix(ctx); + assert(k >= 0); + ctx->stmt_matrix[*i][k] = &unsupported_stmt; + continue; + } + ctx->stmt_matrix[*i][k] = stmt; + } + ctx->rule[(*i)++] = rule; +} + +static int stmt_verdict_find(const struct optimize_ctx *ctx) +{ + uint32_t i; + + for (i = 0; i < ctx->num_stmts; i++) { + if (ctx->stmt[i]->ops->type != STMT_VERDICT) + continue; + + return i; + } + + return -1; +} + +struct merge { + /* interval of rules to be merged */ + uint32_t rule_from; + uint32_t num_rules; + /* statements to be merged (index relative to statement matrix) */ + uint32_t stmt[MAX_STMTS]; + uint32_t num_stmts; +}; + +static void merge_expr_stmts(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge, + struct stmt *stmt_a) +{ + struct expr *expr_a, *expr_b, *set, *elem; + struct stmt *stmt_b; + uint32_t i; + + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + expr_a = stmt_a->expr->right; + elem = set_elem_expr_alloc(&internal_location, expr_get(expr_a)); + compound_expr_add(set, elem); + + for (i = from + 1; i <= to; i++) { + stmt_b = ctx->stmt_matrix[i][merge->stmt[0]]; + expr_b = stmt_b->expr->right; + elem = set_elem_expr_alloc(&internal_location, expr_get(expr_b)); + compound_expr_add(set, elem); + } + + expr_free(stmt_a->expr->right); + stmt_a->expr->right = set; +} + +static void merge_vmap(const struct optimize_ctx *ctx, + struct stmt *stmt_a, const struct stmt *stmt_b) +{ + struct expr *mappings, *mapping, *expr; + + mappings = stmt_b->expr->mappings; + list_for_each_entry(expr, &mappings->expressions, list) { + mapping = expr_clone(expr); + compound_expr_add(stmt_a->expr->mappings, mapping); + } +} + +static void merge_verdict_stmts(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge, + struct stmt *stmt_a) +{ + struct stmt *stmt_b; + uint32_t i; + + for (i = from + 1; i <= to; i++) { + stmt_b = ctx->stmt_matrix[i][merge->stmt[0]]; + switch (stmt_b->ops->type) { + case STMT_VERDICT: + switch (stmt_b->expr->etype) { + case EXPR_MAP: + merge_vmap(ctx, stmt_a, stmt_b); + break; + default: + assert(0); + } + break; + default: + assert(0); + break; + } + } +} + +static void merge_stmts(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, const struct merge *merge) +{ + struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]]; + + switch (stmt_a->ops->type) { + case STMT_EXPRESSION: + merge_expr_stmts(ctx, from, to, merge, stmt_a); + break; + case STMT_VERDICT: + merge_verdict_stmts(ctx, from, to, merge, stmt_a); + break; + default: + assert(0); + } +} + +static void __merge_concat(const struct optimize_ctx *ctx, uint32_t i, + const struct merge *merge, struct list_head *concat_list) +{ + struct expr *concat, *next, *expr, *concat_clone, *clone; + LIST_HEAD(pending_list); + struct stmt *stmt_a; + uint32_t k; + + concat = concat_expr_alloc(&internal_location); + list_add(&concat->list, concat_list); + + for (k = 0; k < merge->num_stmts; k++) { + list_for_each_entry_safe(concat, next, concat_list, list) { + stmt_a = ctx->stmt_matrix[i][merge->stmt[k]]; + switch (stmt_a->expr->right->etype) { + case EXPR_SET: + list_for_each_entry(expr, &stmt_a->expr->right->expressions, list) { + concat_clone = expr_clone(concat); + clone = expr_clone(expr->key); + compound_expr_add(concat_clone, clone); + list_add_tail(&concat_clone->list, &pending_list); + } + list_del(&concat->list); + expr_free(concat); + break; + case EXPR_SYMBOL: + case EXPR_VALUE: + case EXPR_PREFIX: + case EXPR_RANGE: + clone = expr_clone(stmt_a->expr->right); + compound_expr_add(concat, clone); + break; + default: + assert(0); + break; + } + } + list_splice_init(&pending_list, concat_list); + } +} + +static void __merge_concat_stmts(const struct optimize_ctx *ctx, uint32_t i, + const struct merge *merge, struct expr *set) +{ + struct expr *concat, *next, *elem; + LIST_HEAD(concat_list); + + __merge_concat(ctx, i, merge, &concat_list); + + list_for_each_entry_safe(concat, next, &concat_list, list) { + list_del(&concat->list); + elem = set_elem_expr_alloc(&internal_location, concat); + compound_expr_add(set, elem); + } +} + +static void merge_concat_stmts(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct stmt *stmt, *stmt_a; + struct expr *concat, *set; + uint32_t i, k; + + stmt = ctx->stmt_matrix[from][merge->stmt[0]]; + /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */ + concat = concat_expr_alloc(&internal_location); + + for (k = 0; k < merge->num_stmts; k++) { + stmt_a = ctx->stmt_matrix[from][merge->stmt[k]]; + compound_expr_add(concat, expr_get(stmt_a->expr->left)); + } + expr_free(stmt->expr->left); + stmt->expr->left = concat; + + /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 } */ + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + for (i = from; i <= to; i++) + __merge_concat_stmts(ctx, i, merge, set); + + expr_free(stmt->expr->right); + stmt->expr->right = set; + + for (k = 1; k < merge->num_stmts; k++) { + stmt_a = ctx->stmt_matrix[from][merge->stmt[k]]; + list_del(&stmt_a->list); + stmt_free(stmt_a); + } +} + +static void build_verdict_map(struct expr *expr, struct stmt *verdict, + struct expr *set, struct stmt *counter) +{ + struct expr *item, *elem, *mapping; + + switch (expr->etype) { + case EXPR_LIST: + list_for_each_entry(item, &expr->expressions, list) { + elem = set_elem_expr_alloc(&internal_location, expr_get(item)); + if (counter) + list_add_tail(&counter->list, &elem->stmt_list); + + mapping = mapping_expr_alloc(&internal_location, elem, + expr_get(verdict->expr)); + compound_expr_add(set, mapping); + } + break; + case EXPR_SET: + list_for_each_entry(item, &expr->expressions, list) { + elem = set_elem_expr_alloc(&internal_location, expr_get(item->key)); + if (counter) + list_add_tail(&counter->list, &elem->stmt_list); + + mapping = mapping_expr_alloc(&internal_location, elem, + expr_get(verdict->expr)); + compound_expr_add(set, mapping); + } + break; + case EXPR_PREFIX: + case EXPR_RANGE: + case EXPR_VALUE: + case EXPR_SYMBOL: + case EXPR_CONCAT: + elem = set_elem_expr_alloc(&internal_location, expr_get(expr)); + if (counter) + list_add_tail(&counter->list, &elem->stmt_list); + + mapping = mapping_expr_alloc(&internal_location, elem, + expr_get(verdict->expr)); + compound_expr_add(set, mapping); + break; + default: + assert(0); + break; + } +} + +static void remove_counter(const struct optimize_ctx *ctx, uint32_t from) +{ + struct stmt *stmt; + uint32_t i; + + /* remove counter statement */ + for (i = 0; i < ctx->num_stmts; i++) { + stmt = ctx->stmt_matrix[from][i]; + if (!stmt) + continue; + + if (stmt->ops->type == STMT_COUNTER) { + list_del(&stmt->list); + stmt_free(stmt); + } + } +} + +static struct stmt *zap_counter(const struct optimize_ctx *ctx, uint32_t from) +{ + struct stmt *stmt; + uint32_t i; + + /* remove counter statement */ + for (i = 0; i < ctx->num_stmts; i++) { + stmt = ctx->stmt_matrix[from][i]; + if (!stmt) + continue; + + if (stmt->ops->type == STMT_COUNTER) { + list_del(&stmt->list); + return stmt; + } + } + + return NULL; +} + +static void merge_stmts_vmap(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct stmt *stmt_a = ctx->stmt_matrix[from][merge->stmt[0]]; + struct stmt *stmt_b, *verdict_a, *verdict_b, *stmt; + struct expr *expr_a, *expr_b, *expr, *left, *set; + struct stmt *counter; + uint32_t i; + int k; + + k = stmt_verdict_find(ctx); + assert(k >= 0); + + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + expr_a = stmt_a->expr->right; + verdict_a = ctx->stmt_matrix[from][k]; + counter = zap_counter(ctx, from); + build_verdict_map(expr_a, verdict_a, set, counter); + + for (i = from + 1; i <= to; i++) { + stmt_b = ctx->stmt_matrix[i][merge->stmt[0]]; + expr_b = stmt_b->expr->right; + verdict_b = ctx->stmt_matrix[i][k]; + counter = zap_counter(ctx, i); + build_verdict_map(expr_b, verdict_b, set, counter); + } + + left = expr_get(stmt_a->expr->left); + expr = map_expr_alloc(&internal_location, left, set); + stmt = verdict_stmt_alloc(&internal_location, expr); + + list_add(&stmt->list, &stmt_a->list); + list_del(&stmt_a->list); + stmt_free(stmt_a); + list_del(&verdict_a->list); + stmt_free(verdict_a); +} + +static void __merge_concat_stmts_vmap(const struct optimize_ctx *ctx, + uint32_t i, const struct merge *merge, + struct expr *set, struct stmt *verdict) +{ + struct expr *concat, *next, *elem, *mapping; + LIST_HEAD(concat_list); + struct stmt *counter; + + counter = zap_counter(ctx, i); + __merge_concat(ctx, i, merge, &concat_list); + + list_for_each_entry_safe(concat, next, &concat_list, list) { + list_del(&concat->list); + elem = set_elem_expr_alloc(&internal_location, concat); + if (counter) + list_add_tail(&counter->list, &elem->stmt_list); + + mapping = mapping_expr_alloc(&internal_location, elem, + expr_get(verdict->expr)); + compound_expr_add(set, mapping); + } +} + +static void merge_concat_stmts_vmap(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct stmt *orig_stmt = ctx->stmt_matrix[from][merge->stmt[0]]; + struct stmt *stmt, *stmt_a, *verdict; + struct expr *concat_a, *expr, *set; + uint32_t i; + int k; + + k = stmt_verdict_find(ctx); + assert(k >= 0); + + /* build concatenation of selectors, eg. ifname . ip daddr . tcp dport */ + concat_a = concat_expr_alloc(&internal_location); + for (i = 0; i < merge->num_stmts; i++) { + stmt_a = ctx->stmt_matrix[from][merge->stmt[i]]; + compound_expr_add(concat_a, expr_get(stmt_a->expr->left)); + } + + /* build set data contenation, eg. { eth0 . 1.1.1.1 . 22 : accept } */ + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + for (i = from; i <= to; i++) { + verdict = ctx->stmt_matrix[i][k]; + __merge_concat_stmts_vmap(ctx, i, merge, set, verdict); + } + + expr = map_expr_alloc(&internal_location, concat_a, set); + stmt = verdict_stmt_alloc(&internal_location, expr); + + list_add(&stmt->list, &orig_stmt->list); + list_del(&orig_stmt->list); + stmt_free(orig_stmt); + + for (i = 1; i < merge->num_stmts; i++) { + stmt_a = ctx->stmt_matrix[from][merge->stmt[i]]; + list_del(&stmt_a->list); + stmt_free(stmt_a); + } + + verdict = ctx->stmt_matrix[from][k]; + list_del(&verdict->list); + stmt_free(verdict); +} + +static bool stmt_verdict_cmp(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to) +{ + struct stmt *stmt_a, *stmt_b; + uint32_t i; + int k; + + k = stmt_verdict_find(ctx); + if (k < 0) + return true; + + for (i = from; i + 1 <= to; i++) { + stmt_a = ctx->stmt_matrix[i][k]; + stmt_b = ctx->stmt_matrix[i + 1][k]; + if (!stmt_a && !stmt_b) + continue; + if (!stmt_a || !stmt_b) + return false; + if (!stmt_verdict_eq(stmt_a, stmt_b)) + return false; + } + + return true; +} + +static int stmt_nat_type(const struct optimize_ctx *ctx, int from, + enum nft_nat_etypes *nat_type) +{ + uint32_t j; + + for (j = 0; j < ctx->num_stmts; j++) { + if (!ctx->stmt_matrix[from][j]) + continue; + + if (ctx->stmt_matrix[from][j]->ops->type == STMT_NAT) { + *nat_type = ctx->stmt_matrix[from][j]->nat.type; + return 0; + } + } + + return -1; +} + +static int stmt_nat_find(const struct optimize_ctx *ctx, int from) +{ + enum nft_nat_etypes nat_type; + uint32_t i; + + if (stmt_nat_type(ctx, from, &nat_type) < 0) + return -1; + + for (i = 0; i < ctx->num_stmts; i++) { + if (ctx->stmt[i]->ops->type != STMT_NAT || + ctx->stmt[i]->nat.type != nat_type) + continue; + + return i; + } + + return -1; +} + +static struct expr *stmt_nat_expr(struct stmt *nat_stmt) +{ + struct expr *nat_expr; + + assert(nat_stmt->ops->type == STMT_NAT); + + if (nat_stmt->nat.proto) { + if (nat_stmt->nat.addr) { + nat_expr = concat_expr_alloc(&internal_location); + compound_expr_add(nat_expr, expr_get(nat_stmt->nat.addr)); + compound_expr_add(nat_expr, expr_get(nat_stmt->nat.proto)); + } else { + nat_expr = expr_get(nat_stmt->nat.proto); + } + expr_free(nat_stmt->nat.proto); + nat_stmt->nat.proto = NULL; + } else { + nat_expr = expr_get(nat_stmt->nat.addr); + } + + assert(nat_expr); + + return nat_expr; +} + +static void merge_nat(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct expr *expr, *set, *elem, *nat_expr, *mapping, *left; + int k, family = NFPROTO_UNSPEC; + struct stmt *stmt, *nat_stmt; + uint32_t i; + + k = stmt_nat_find(ctx, from); + assert(k >= 0); + + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + for (i = from; i <= to; i++) { + stmt = ctx->stmt_matrix[i][merge->stmt[0]]; + expr = stmt->expr->right; + + nat_stmt = ctx->stmt_matrix[i][k]; + nat_expr = stmt_nat_expr(nat_stmt); + + elem = set_elem_expr_alloc(&internal_location, expr_get(expr)); + mapping = mapping_expr_alloc(&internal_location, elem, nat_expr); + compound_expr_add(set, mapping); + } + + stmt = ctx->stmt_matrix[from][merge->stmt[0]]; + left = expr_get(stmt->expr->left); + if (left->etype == EXPR_PAYLOAD) { + if (left->payload.desc == &proto_ip) + family = NFPROTO_IPV4; + else if (left->payload.desc == &proto_ip6) + family = NFPROTO_IPV6; + } + expr = map_expr_alloc(&internal_location, left, set); + + nat_stmt = ctx->stmt_matrix[from][k]; + if (nat_stmt->nat.family == NFPROTO_UNSPEC) + nat_stmt->nat.family = family; + + expr_free(nat_stmt->nat.addr); + if (nat_stmt->nat.type == NFT_NAT_REDIR) + nat_stmt->nat.proto = expr; + else + nat_stmt->nat.addr = expr; + + remove_counter(ctx, from); + list_del(&stmt->list); + stmt_free(stmt); +} + +static void merge_concat_nat(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge) +{ + struct expr *expr, *set, *elem, *nat_expr, *mapping, *left, *concat; + int k, family = NFPROTO_UNSPEC; + struct stmt *stmt, *nat_stmt; + uint32_t i, j; + + k = stmt_nat_find(ctx, from); + assert(k >= 0); + + set = set_expr_alloc(&internal_location, NULL); + set->set_flags |= NFT_SET_ANONYMOUS; + + for (i = from; i <= to; i++) { + + concat = concat_expr_alloc(&internal_location); + for (j = 0; j < merge->num_stmts; j++) { + stmt = ctx->stmt_matrix[i][merge->stmt[j]]; + expr = stmt->expr->right; + compound_expr_add(concat, expr_get(expr)); + } + + nat_stmt = ctx->stmt_matrix[i][k]; + nat_expr = stmt_nat_expr(nat_stmt); + + elem = set_elem_expr_alloc(&internal_location, concat); + mapping = mapping_expr_alloc(&internal_location, elem, nat_expr); + compound_expr_add(set, mapping); + } + + concat = concat_expr_alloc(&internal_location); + for (j = 0; j < merge->num_stmts; j++) { + stmt = ctx->stmt_matrix[from][merge->stmt[j]]; + left = stmt->expr->left; + if (left->etype == EXPR_PAYLOAD) { + if (left->payload.desc == &proto_ip) + family = NFPROTO_IPV4; + else if (left->payload.desc == &proto_ip6) + family = NFPROTO_IPV6; + } + compound_expr_add(concat, expr_get(left)); + } + expr = map_expr_alloc(&internal_location, concat, set); + + nat_stmt = ctx->stmt_matrix[from][k]; + if (nat_stmt->nat.family == NFPROTO_UNSPEC) + nat_stmt->nat.family = family; + + expr_free(nat_stmt->nat.addr); + nat_stmt->nat.addr = expr; + + remove_counter(ctx, from); + for (j = 0; j < merge->num_stmts; j++) { + stmt = ctx->stmt_matrix[from][merge->stmt[j]]; + list_del(&stmt->list); + stmt_free(stmt); + } +} + +static void rule_optimize_print(struct output_ctx *octx, + const struct rule *rule) +{ + const struct location *loc = &rule->location; + const struct input_descriptor *indesc = loc->indesc; + const char *line = ""; + char buf[1024]; + + switch (indesc->type) { + case INDESC_BUFFER: + case INDESC_CLI: + line = indesc->data; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_STDIN: + line = indesc->data; + line += loc->line_offset; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_FILE: + line = line_location(indesc, loc, buf, sizeof(buf)); + break; + case INDESC_INTERNAL: + case INDESC_NETLINK: + break; + default: + BUG("invalid input descriptor type %u\n", indesc->type); + } + + print_location(octx->error_fp, indesc, loc); + fprintf(octx->error_fp, "%s\n", line); +} + +enum { + MERGE_BY_VERDICT, + MERGE_BY_NAT_MAP, + MERGE_BY_NAT, +}; + +static uint32_t merge_stmt_type(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to) +{ + const struct stmt *stmt; + uint32_t i, j; + + for (i = from; i <= to; i++) { + for (j = 0; j < ctx->num_stmts; j++) { + stmt = ctx->stmt_matrix[i][j]; + if (!stmt) + continue; + if (stmt->ops->type == STMT_NAT) { + if ((stmt->nat.type == NFT_NAT_REDIR && + !stmt->nat.proto) || + stmt->nat.type == NFT_NAT_MASQ) + return MERGE_BY_NAT; + + return MERGE_BY_NAT_MAP; + } + } + } + + /* merge by verdict, even if no verdict is specified. */ + return MERGE_BY_VERDICT; +} + +static void merge_rules(const struct optimize_ctx *ctx, + uint32_t from, uint32_t to, + const struct merge *merge, + struct output_ctx *octx) +{ + uint32_t merge_type; + bool same_verdict; + uint32_t i; + + merge_type = merge_stmt_type(ctx, from, to); + + switch (merge_type) { + case MERGE_BY_VERDICT: + same_verdict = stmt_verdict_cmp(ctx, from, to); + if (merge->num_stmts > 1) { + if (same_verdict) + merge_concat_stmts(ctx, from, to, merge); + else + merge_concat_stmts_vmap(ctx, from, to, merge); + } else { + if (same_verdict) + merge_stmts(ctx, from, to, merge); + else + merge_stmts_vmap(ctx, from, to, merge); + } + break; + case MERGE_BY_NAT_MAP: + if (merge->num_stmts > 1) + merge_concat_nat(ctx, from, to, merge); + else + merge_nat(ctx, from, to, merge); + break; + case MERGE_BY_NAT: + if (merge->num_stmts > 1) + merge_concat_stmts(ctx, from, to, merge); + else + merge_stmts(ctx, from, to, merge); + break; + default: + assert(0); + } + + if (ctx->rule[from]->comment) { + xfree(ctx->rule[from]->comment); + ctx->rule[from]->comment = NULL; + } + + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + + fprintf(octx->error_fp, "Merging:\n"); + rule_optimize_print(octx, ctx->rule[from]); + + for (i = from + 1; i <= to; i++) { + rule_optimize_print(octx, ctx->rule[i]); + list_del(&ctx->rule[i]->list); + rule_free(ctx->rule[i]); + } + + fprintf(octx->error_fp, "into:\n\t"); + rule_print(ctx->rule[from], octx); + fprintf(octx->error_fp, "\n"); + + octx->flags &= ~NFT_CTX_OUTPUT_STATELESS; +} + +static bool stmt_type_eq(const struct stmt *stmt_a, const struct stmt *stmt_b) +{ + if (!stmt_a && !stmt_b) + return true; + else if (!stmt_a) + return false; + else if (!stmt_b) + return false; + + return __stmt_type_eq(stmt_a, stmt_b, true); +} + +static bool stmt_is_mergeable(const struct stmt *stmt) +{ + if (!stmt) + return false; + + switch (stmt->ops->type) { + case STMT_VERDICT: + if (stmt->expr->etype == EXPR_MAP) + return true; + break; + case STMT_EXPRESSION: + case STMT_NAT: + return true; + default: + break; + } + + return false; +} + +static bool rules_eq(const struct optimize_ctx *ctx, int i, int j) +{ + uint32_t k, mergeable = 0; + + for (k = 0; k < ctx->num_stmts; k++) { + if (stmt_is_mergeable(ctx->stmt_matrix[i][k])) + mergeable++; + + if (!stmt_type_eq(ctx->stmt_matrix[i][k], ctx->stmt_matrix[j][k])) + return false; + } + + if (mergeable == 0) + return false; + + return true; +} + +static int chain_optimize(struct nft_ctx *nft, struct list_head *rules) +{ + struct optimize_ctx *ctx; + uint32_t num_merges = 0; + struct merge *merge; + uint32_t i, j, m, k; + struct rule *rule; + int ret; + + ctx = xzalloc(sizeof(*ctx)); + + /* Step 1: collect statements in rules */ + list_for_each_entry(rule, rules, list) { + ret = rule_collect_stmts(ctx, rule); + if (ret < 0) + goto err; + + ctx->num_rules++; + } + + ctx->rule = xzalloc(sizeof(*ctx->rule) * ctx->num_rules); + ctx->stmt_matrix = xzalloc(sizeof(*ctx->stmt_matrix) * ctx->num_rules); + for (i = 0; i < ctx->num_rules; i++) + ctx->stmt_matrix[i] = xzalloc_array(MAX_STMTS, + sizeof(**ctx->stmt_matrix)); + + merge = xzalloc(sizeof(*merge) * ctx->num_rules); + + /* Step 2: Build matrix of statements */ + i = 0; + list_for_each_entry(rule, rules, list) + rule_build_stmt_matrix_stmts(ctx, rule, &i); + + /* Step 3: Look for common selectors for possible rule mergers */ + for (i = 0; i < ctx->num_rules; i++) { + for (j = i + 1; j < ctx->num_rules; j++) { + if (!rules_eq(ctx, i, j)) { + if (merge[num_merges].num_rules > 0) + num_merges++; + + i = j - 1; + break; + } + if (merge[num_merges].num_rules > 0) { + merge[num_merges].num_rules++; + } else { + merge[num_merges].rule_from = i; + merge[num_merges].num_rules = 2; + } + } + if (j == ctx->num_rules && merge[num_merges].num_rules > 0) { + num_merges++; + break; + } + } + + /* Step 4: Infer how to merge the candidate rules */ + for (k = 0; k < num_merges; k++) { + i = merge[k].rule_from; + + for (m = 0; m < ctx->num_stmts; m++) { + if (!ctx->stmt_matrix[i][m]) + continue; + switch (ctx->stmt_matrix[i][m]->ops->type) { + case STMT_EXPRESSION: + merge[k].stmt[merge[k].num_stmts++] = m; + break; + case STMT_VERDICT: + if (ctx->stmt_matrix[i][m]->expr->etype == EXPR_MAP) + merge[k].stmt[merge[k].num_stmts++] = m; + break; + default: + break; + } + } + + j = merge[k].num_rules - 1; + merge_rules(ctx, i, i + j, &merge[k], &nft->output); + } + ret = 0; + for (i = 0; i < ctx->num_rules; i++) + xfree(ctx->stmt_matrix[i]); + + xfree(ctx->stmt_matrix); + xfree(merge); +err: + for (i = 0; i < ctx->num_stmts; i++) + stmt_free(ctx->stmt[i]); + + xfree(ctx->rule); + xfree(ctx); + + return ret; +} + +static int cmd_optimize(struct nft_ctx *nft, struct cmd *cmd) +{ + struct table *table; + struct chain *chain; + int ret = 0; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + table = cmd->table; + if (!table) + break; + + list_for_each_entry(chain, &table->chains, list) { + if (chain->flags & CHAIN_F_HW_OFFLOAD) + continue; + + chain_optimize(nft, &chain->rules); + } + break; + default: + break; + } + + return ret; +} + +int nft_optimize(struct nft_ctx *nft, struct list_head *cmds) +{ + struct cmd *cmd; + int ret = 0; + + list_for_each_entry(cmd, cmds, list) { + switch (cmd->op) { + case CMD_ADD: + ret = cmd_optimize(nft, cmd); + break; + default: + break; + } + } + + return ret; +} diff --git a/src/osf.c b/src/osf.c new file mode 100644 index 0000000..a8f80b2 --- /dev/null +++ b/src/osf.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2018 Fernando Fernandez Mancera <ffmancera@riseup.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 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <expression.h> +#include <utils.h> +#include <osf.h> +#include <json.h> + +static const char *osf_ttl_int_to_str(const uint8_t ttl) +{ + if (ttl == 1) + return "ttl loose "; + else if (ttl == 2) + return "ttl skip "; + + return ""; +} + +static void osf_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const char *ttl_str = osf_ttl_int_to_str(expr->osf.ttl); + + if (expr->osf.flags & NFT_OSF_F_VERSION) + nft_print(octx, "osf %sversion", ttl_str); + else + nft_print(octx, "osf %sname", ttl_str); +} + +static void osf_expr_clone(struct expr *new, const struct expr *expr) +{ + new->osf.ttl = expr->osf.ttl; + new->osf.flags = expr->osf.flags; +} + +static bool osf_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return (e1->osf.ttl == e2->osf.ttl) && + (e1->osf.flags == e2->osf.flags); +} + +static int osf_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + return 0; +} + +static struct expr *osf_expr_parse_udata(const struct nftnl_udata *attr) +{ + return osf_expr_alloc(&internal_location, 0, 0); +} + +const struct expr_ops osf_expr_ops = { + .type = EXPR_OSF, + .name = "osf", + .print = osf_expr_print, + .clone = osf_expr_clone, + .cmp = osf_expr_cmp, + .json = osf_expr_json, + .parse_udata = osf_expr_parse_udata, + .build_udata = osf_expr_build_udata, +}; + +struct expr *osf_expr_alloc(const struct location *loc, const uint8_t ttl, + const uint32_t flags) +{ + unsigned int len = NFT_OSF_MAXGENRELEN * BITS_PER_BYTE; + const struct datatype *type = &string_type; + struct expr *expr; + + expr = expr_alloc(loc, EXPR_OSF, type, + BYTEORDER_HOST_ENDIAN, len); + expr->osf.ttl = ttl; + expr->osf.flags = flags; + + return expr; +} diff --git a/src/owner.c b/src/owner.c new file mode 100644 index 0000000..65eaad3 --- /dev/null +++ b/src/owner.c @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2021 Pablo Neira Ayuso <pablo@netfilter.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <stdio.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> +#include <inttypes.h> +#include <dirent.h> + +#include <netlink.h> +#include <owner.h> + +static char *pid2name(pid_t pid) +{ + char procname[256], *prog; + FILE *fp; + int ret; + + ret = snprintf(procname, sizeof(procname), "/proc/%lu/stat", (unsigned long)pid); + if (ret < 0 || ret > (int)sizeof(procname)) + return NULL; + + fp = fopen(procname, "r"); + if (!fp) + return NULL; + + ret = fscanf(fp, "%*u (%m[^)]", &prog); + + fclose(fp); + + if (ret == 1) + return prog; + + return NULL; +} + +static char *portid2name(pid_t pid, uint32_t portid, unsigned long inode) +{ + const struct dirent *ent; + char procname[256]; + DIR *dir; + int ret; + + ret = snprintf(procname, sizeof(procname), "/proc/%lu/fd/", (unsigned long)pid); + if (ret < 0 || ret >= (int)sizeof(procname)) + return NULL; + + dir = opendir(procname); + if (!dir) + return NULL; + + for (;;) { + unsigned long ino; + char tmp[128]; + ssize_t rl; + + ent = readdir(dir); + if (!ent) + break; + + if (ent->d_type != DT_LNK) + continue; + + ret = snprintf(procname, sizeof(procname), "/proc/%d/fd/%s", + pid, ent->d_name); + if (ret < 0 || ret >= (int)sizeof(procname)) + continue; + + rl = readlink(procname, tmp, sizeof(tmp)); + if (rl <= 0 || rl >= (ssize_t)sizeof(tmp)) + continue; + + tmp[rl] = 0; + + ret = sscanf(tmp, "socket:[%lu]", &ino); + if (ret == 1 && ino == inode) { + closedir(dir); + return pid2name(pid); + } + } + + closedir(dir); + return NULL; +} + +static char *name_by_portid(uint32_t portid, unsigned long inode) +{ + const struct dirent *ent; + char *prog; + DIR *dir; + + /* Many netlink users use their process ID to allocate the first port id. */ + prog = portid2name(portid, portid, inode); + if (prog) + return prog; + + /* no luck, search harder. */ + dir = opendir("/proc"); + if (!dir) + return NULL; + + for (;;) { + unsigned long pid; + char *end; + + ent = readdir(dir); + if (!ent) + break; + + if (ent->d_type != DT_DIR) + continue; + + pid = strtoul(ent->d_name, &end, 10); + if (pid <= 1 || *end) + continue; + + if (pid == portid) /* already tried */ + continue; + + prog = portid2name(pid, portid, inode); + if (prog) + break; + } + + closedir(dir); + return prog; +} + +char *get_progname(uint32_t portid) +{ + FILE *fp = fopen("/proc/net/netlink", "r"); + uint32_t portid_check; + unsigned long inode; + int ret, prot; + + if (!fp) + return NULL; + + for (;;) { + char line[256]; + + if (!fgets(line, sizeof(line), fp)) + break; + + ret = sscanf(line, "%*x %d %u %*x %*d %*d %*x %*d %*u %lu\n", + &prot, &portid_check, &inode); + + if (ret == EOF) + break; + + if (ret == 3 && portid_check == portid && prot == NETLINK_NETFILTER) { + static uint32_t last_portid; + static uint32_t last_inode; + static char *last_program; + char *prog; + + fclose(fp); + + if (last_portid == portid && last_inode == inode) + return last_program; + + prog = name_by_portid(portid, inode); + + free(last_program); + last_program = prog; + last_portid = portid; + last_inode = inode; + return prog; + } + } + + fclose(fp); + return NULL; +} diff --git a/src/parser_bison.c b/src/parser_bison.c new file mode 100644 index 0000000..379f322 --- /dev/null +++ b/src/parser_bison.c @@ -0,0 +1,17491 @@ +/* A Bison parser, made by GNU Bison 3.7.5. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* 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. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30705 + +/* Bison version string. */ +#define YYBISON_VERSION "3.7.5" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + +/* Substitute the variable and function names. */ +#define yyparse nft_parse +#define yylex nft_lex +#define yyerror nft_error +#define yydebug nft_debug +#define yynerrs nft_nerrs + +/* First part of user prologue. */ +#line 11 "parser_bison.y" + +#include <nft.h> + +#include <ctype.h> +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> +#include <syslog.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/if_ether.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_conntrack_tuple_common.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nfnetlink_osf.h> +#include <linux/netfilter/nf_synproxy.h> +#include <linux/xfrm.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <libnftnl/common.h> +#include <libnftnl/set.h> +#include <libnftnl/udata.h> + +#include <rule.h> +#include <statement.h> +#include <expression.h> +#include <headers.h> +#include <utils.h> +#include <parser.h> +#include <erec.h> +#include <sctp_chunk.h> + +#include "parser_bison.h" + +void parser_init(struct nft_ctx *nft, struct parser_state *state, + struct list_head *msgs, struct list_head *cmds, + struct scope *top_scope) +{ + memset(state, 0, sizeof(*state)); + state->msgs = msgs; + state->cmds = cmds; + state->scopes[0] = scope_init(top_scope, NULL); + init_list_head(&state->indesc_list); +} + +static void yyerror(struct location *loc, struct nft_ctx *nft, void *scanner, + struct parser_state *state, const char *s) +{ + erec_queue(error(loc, "%s", s), state->msgs); +} + +static struct scope *current_scope(const struct parser_state *state) +{ + return state->scopes[state->scope]; +} + +static int open_scope(struct parser_state *state, struct scope *scope) +{ + if (state->scope >= array_size(state->scopes) - 1) { + state->scope_err = true; + return -1; + } + + scope_init(scope, current_scope(state)); + state->scopes[++state->scope] = scope; + + return 0; +} + +static void close_scope(struct parser_state *state) +{ + if (state->scope_err || state->scope == 0) { + state->scope_err = false; + return; + } + + state->scope--; +} + +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; + } +} + +static struct expr *handle_concat_expr(const struct location *loc, + struct expr *expr, + struct expr *expr_l, struct expr *expr_r, + struct location loc_rhs[3]) +{ + if (expr->etype != EXPR_CONCAT) { + expr = concat_expr_alloc(loc); + compound_expr_add(expr, expr_l); + } else { + location_update(&expr_r->location, loc_rhs, 2); + + expr = expr_l; + expr->location = *loc; + } + + compound_expr_add(expr, expr_r); + return expr; +} + +static bool already_set(const void *attr, const struct location *loc, + struct parser_state *state) +{ + if (!attr) + return false; + + erec_queue(error(loc, "You can only specify this once. This statement is duplicated."), + state->msgs); + return true; +} + +static struct expr *ifname_expr_alloc(const struct location *location, + struct list_head *queue, + const char *name) +{ + unsigned int length = strlen(name); + struct expr *expr; + + if (length == 0) { + xfree(name); + erec_queue(error(location, "empty interface name"), queue); + return NULL; + } + + if (length > 16) { + xfree(name); + erec_queue(error(location, "interface name too long"), queue); + return NULL; + } + + expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN, + length * BITS_PER_BYTE, name); + + xfree(name); + + return expr; +} + +#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N) + +#define symbol_value(loc, str) \ + symbol_expr_alloc(loc, SYMBOL_VALUE, current_scope(state), str) + +/* Declare those here to avoid compiler warnings */ +void nft_set_debug(int, void *); +int nft_lex(void *, void *, void *); + +#line 250 "parser_bison.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast<Type> (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + +/* Use api.header.include to #include this header + instead of duplicating it here. */ +#ifndef YY_NFT_PARSER_BISON_H_INCLUDED +# define YY_NFT_PARSER_BISON_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int nft_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + TOKEN_EOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + JUNK = 258, /* "junk" */ + NEWLINE = 259, /* "newline" */ + COLON = 260, /* "colon" */ + SEMICOLON = 261, /* "semicolon" */ + COMMA = 262, /* "comma" */ + DOT = 263, /* "." */ + EQ = 264, /* "==" */ + NEQ = 265, /* "!=" */ + LT = 266, /* "<" */ + GT = 267, /* ">" */ + GTE = 268, /* ">=" */ + LTE = 269, /* "<=" */ + LSHIFT = 270, /* "<<" */ + RSHIFT = 271, /* ">>" */ + AMPERSAND = 272, /* "&" */ + CARET = 273, /* "^" */ + NOT = 274, /* "!" */ + SLASH = 275, /* "/" */ + ASTERISK = 276, /* "*" */ + DASH = 277, /* "-" */ + AT = 278, /* "@" */ + VMAP = 279, /* "vmap" */ + PLUS = 280, /* "+" */ + INCLUDE = 281, /* "include" */ + DEFINE = 282, /* "define" */ + REDEFINE = 283, /* "redefine" */ + UNDEFINE = 284, /* "undefine" */ + FIB = 285, /* "fib" */ + SOCKET = 286, /* "socket" */ + TRANSPARENT = 287, /* "transparent" */ + WILDCARD = 288, /* "wildcard" */ + CGROUPV2 = 289, /* "cgroupv2" */ + TPROXY = 290, /* "tproxy" */ + OSF = 291, /* "osf" */ + SYNPROXY = 292, /* "synproxy" */ + MSS = 293, /* "mss" */ + WSCALE = 294, /* "wscale" */ + TYPEOF = 295, /* "typeof" */ + HOOK = 296, /* "hook" */ + HOOKS = 297, /* "hooks" */ + DEVICE = 298, /* "device" */ + DEVICES = 299, /* "devices" */ + TABLE = 300, /* "table" */ + TABLES = 301, /* "tables" */ + CHAIN = 302, /* "chain" */ + CHAINS = 303, /* "chains" */ + RULE = 304, /* "rule" */ + RULES = 305, /* "rules" */ + SETS = 306, /* "sets" */ + SET = 307, /* "set" */ + ELEMENT = 308, /* "element" */ + MAP = 309, /* "map" */ + MAPS = 310, /* "maps" */ + FLOWTABLE = 311, /* "flowtable" */ + HANDLE = 312, /* "handle" */ + RULESET = 313, /* "ruleset" */ + TRACE = 314, /* "trace" */ + INET = 315, /* "inet" */ + NETDEV = 316, /* "netdev" */ + ADD = 317, /* "add" */ + UPDATE = 318, /* "update" */ + REPLACE = 319, /* "replace" */ + CREATE = 320, /* "create" */ + INSERT = 321, /* "insert" */ + DELETE = 322, /* "delete" */ + GET = 323, /* "get" */ + LIST = 324, /* "list" */ + RESET = 325, /* "reset" */ + FLUSH = 326, /* "flush" */ + RENAME = 327, /* "rename" */ + DESCRIBE = 328, /* "describe" */ + IMPORT = 329, /* "import" */ + EXPORT = 330, /* "export" */ + DESTROY = 331, /* "destroy" */ + MONITOR = 332, /* "monitor" */ + ALL = 333, /* "all" */ + ACCEPT = 334, /* "accept" */ + DROP = 335, /* "drop" */ + CONTINUE = 336, /* "continue" */ + JUMP = 337, /* "jump" */ + GOTO = 338, /* "goto" */ + RETURN = 339, /* "return" */ + TO = 340, /* "to" */ + CONSTANT = 341, /* "constant" */ + INTERVAL = 342, /* "interval" */ + DYNAMIC = 343, /* "dynamic" */ + AUTOMERGE = 344, /* "auto-merge" */ + TIMEOUT = 345, /* "timeout" */ + GC_INTERVAL = 346, /* "gc-interval" */ + ELEMENTS = 347, /* "elements" */ + EXPIRES = 348, /* "expires" */ + POLICY = 349, /* "policy" */ + MEMORY = 350, /* "memory" */ + PERFORMANCE = 351, /* "performance" */ + SIZE = 352, /* "size" */ + FLOW = 353, /* "flow" */ + OFFLOAD = 354, /* "offload" */ + METER = 355, /* "meter" */ + METERS = 356, /* "meters" */ + FLOWTABLES = 357, /* "flowtables" */ + NUM = 358, /* "number" */ + STRING = 359, /* "string" */ + QUOTED_STRING = 360, /* "quoted string" */ + ASTERISK_STRING = 361, /* "string with a trailing asterisk" */ + LL_HDR = 362, /* "ll" */ + NETWORK_HDR = 363, /* "nh" */ + TRANSPORT_HDR = 364, /* "th" */ + BRIDGE = 365, /* "bridge" */ + ETHER = 366, /* "ether" */ + SADDR = 367, /* "saddr" */ + DADDR = 368, /* "daddr" */ + TYPE = 369, /* "type" */ + VLAN = 370, /* "vlan" */ + ID = 371, /* "id" */ + CFI = 372, /* "cfi" */ + DEI = 373, /* "dei" */ + PCP = 374, /* "pcp" */ + ARP = 375, /* "arp" */ + HTYPE = 376, /* "htype" */ + PTYPE = 377, /* "ptype" */ + HLEN = 378, /* "hlen" */ + PLEN = 379, /* "plen" */ + OPERATION = 380, /* "operation" */ + IP = 381, /* "ip" */ + HDRVERSION = 382, /* "version" */ + HDRLENGTH = 383, /* "hdrlength" */ + DSCP = 384, /* "dscp" */ + ECN = 385, /* "ecn" */ + LENGTH = 386, /* "length" */ + FRAG_OFF = 387, /* "frag-off" */ + TTL = 388, /* "ttl" */ + PROTOCOL = 389, /* "protocol" */ + CHECKSUM = 390, /* "checksum" */ + PTR = 391, /* "ptr" */ + VALUE = 392, /* "value" */ + LSRR = 393, /* "lsrr" */ + RR = 394, /* "rr" */ + SSRR = 395, /* "ssrr" */ + RA = 396, /* "ra" */ + ICMP = 397, /* "icmp" */ + CODE = 398, /* "code" */ + SEQUENCE = 399, /* "seq" */ + GATEWAY = 400, /* "gateway" */ + MTU = 401, /* "mtu" */ + IGMP = 402, /* "igmp" */ + MRT = 403, /* "mrt" */ + OPTIONS = 404, /* "options" */ + IP6 = 405, /* "ip6" */ + PRIORITY = 406, /* "priority" */ + FLOWLABEL = 407, /* "flowlabel" */ + NEXTHDR = 408, /* "nexthdr" */ + HOPLIMIT = 409, /* "hoplimit" */ + ICMP6 = 410, /* "icmpv6" */ + PPTR = 411, /* "param-problem" */ + MAXDELAY = 412, /* "max-delay" */ + TADDR = 413, /* "taddr" */ + AH = 414, /* "ah" */ + RESERVED = 415, /* "reserved" */ + SPI = 416, /* "spi" */ + ESP = 417, /* "esp" */ + COMP = 418, /* "comp" */ + FLAGS = 419, /* "flags" */ + CPI = 420, /* "cpi" */ + PORT = 421, /* "port" */ + UDP = 422, /* "udp" */ + SPORT = 423, /* "sport" */ + DPORT = 424, /* "dport" */ + UDPLITE = 425, /* "udplite" */ + CSUMCOV = 426, /* "csumcov" */ + TCP = 427, /* "tcp" */ + ACKSEQ = 428, /* "ackseq" */ + DOFF = 429, /* "doff" */ + WINDOW = 430, /* "window" */ + URGPTR = 431, /* "urgptr" */ + OPTION = 432, /* "option" */ + ECHO = 433, /* "echo" */ + EOL = 434, /* "eol" */ + MPTCP = 435, /* "mptcp" */ + NOP = 436, /* "nop" */ + SACK = 437, /* "sack" */ + SACK0 = 438, /* "sack0" */ + SACK1 = 439, /* "sack1" */ + SACK2 = 440, /* "sack2" */ + SACK3 = 441, /* "sack3" */ + SACK_PERM = 442, /* "sack-permitted" */ + FASTOPEN = 443, /* "fastopen" */ + MD5SIG = 444, /* "md5sig" */ + TIMESTAMP = 445, /* "timestamp" */ + COUNT = 446, /* "count" */ + LEFT = 447, /* "left" */ + RIGHT = 448, /* "right" */ + TSVAL = 449, /* "tsval" */ + TSECR = 450, /* "tsecr" */ + SUBTYPE = 451, /* "subtype" */ + DCCP = 452, /* "dccp" */ + VXLAN = 453, /* "vxlan" */ + VNI = 454, /* "vni" */ + GRE = 455, /* "gre" */ + GRETAP = 456, /* "gretap" */ + GENEVE = 457, /* "geneve" */ + SCTP = 458, /* "sctp" */ + CHUNK = 459, /* "chunk" */ + DATA = 460, /* "data" */ + INIT = 461, /* "init" */ + INIT_ACK = 462, /* "init-ack" */ + HEARTBEAT = 463, /* "heartbeat" */ + HEARTBEAT_ACK = 464, /* "heartbeat-ack" */ + ABORT = 465, /* "abort" */ + SHUTDOWN = 466, /* "shutdown" */ + SHUTDOWN_ACK = 467, /* "shutdown-ack" */ + ERROR = 468, /* "error" */ + COOKIE_ECHO = 469, /* "cookie-echo" */ + COOKIE_ACK = 470, /* "cookie-ack" */ + ECNE = 471, /* "ecne" */ + CWR = 472, /* "cwr" */ + SHUTDOWN_COMPLETE = 473, /* "shutdown-complete" */ + ASCONF_ACK = 474, /* "asconf-ack" */ + FORWARD_TSN = 475, /* "forward-tsn" */ + ASCONF = 476, /* "asconf" */ + TSN = 477, /* "tsn" */ + STREAM = 478, /* "stream" */ + SSN = 479, /* "ssn" */ + PPID = 480, /* "ppid" */ + INIT_TAG = 481, /* "init-tag" */ + A_RWND = 482, /* "a-rwnd" */ + NUM_OSTREAMS = 483, /* "num-outbound-streams" */ + NUM_ISTREAMS = 484, /* "num-inbound-streams" */ + INIT_TSN = 485, /* "initial-tsn" */ + CUM_TSN_ACK = 486, /* "cum-tsn-ack" */ + NUM_GACK_BLOCKS = 487, /* "num-gap-ack-blocks" */ + NUM_DUP_TSNS = 488, /* "num-dup-tsns" */ + LOWEST_TSN = 489, /* "lowest-tsn" */ + SEQNO = 490, /* "seqno" */ + NEW_CUM_TSN = 491, /* "new-cum-tsn" */ + VTAG = 492, /* "vtag" */ + RT = 493, /* "rt" */ + RT0 = 494, /* "rt0" */ + RT2 = 495, /* "rt2" */ + RT4 = 496, /* "srh" */ + SEG_LEFT = 497, /* "seg-left" */ + ADDR = 498, /* "addr" */ + LAST_ENT = 499, /* "last-entry" */ + TAG = 500, /* "tag" */ + SID = 501, /* "sid" */ + HBH = 502, /* "hbh" */ + FRAG = 503, /* "frag" */ + RESERVED2 = 504, /* "reserved2" */ + MORE_FRAGMENTS = 505, /* "more-fragments" */ + DST = 506, /* "dst" */ + MH = 507, /* "mh" */ + META = 508, /* "meta" */ + MARK = 509, /* "mark" */ + IIF = 510, /* "iif" */ + IIFNAME = 511, /* "iifname" */ + IIFTYPE = 512, /* "iiftype" */ + OIF = 513, /* "oif" */ + OIFNAME = 514, /* "oifname" */ + OIFTYPE = 515, /* "oiftype" */ + SKUID = 516, /* "skuid" */ + SKGID = 517, /* "skgid" */ + NFTRACE = 518, /* "nftrace" */ + RTCLASSID = 519, /* "rtclassid" */ + IBRIPORT = 520, /* "ibriport" */ + OBRIPORT = 521, /* "obriport" */ + IBRIDGENAME = 522, /* "ibrname" */ + OBRIDGENAME = 523, /* "obrname" */ + PKTTYPE = 524, /* "pkttype" */ + CPU = 525, /* "cpu" */ + IIFGROUP = 526, /* "iifgroup" */ + OIFGROUP = 527, /* "oifgroup" */ + CGROUP = 528, /* "cgroup" */ + TIME = 529, /* "time" */ + CLASSID = 530, /* "classid" */ + NEXTHOP = 531, /* "nexthop" */ + CT = 532, /* "ct" */ + L3PROTOCOL = 533, /* "l3proto" */ + PROTO_SRC = 534, /* "proto-src" */ + PROTO_DST = 535, /* "proto-dst" */ + ZONE = 536, /* "zone" */ + DIRECTION = 537, /* "direction" */ + EVENT = 538, /* "event" */ + EXPECTATION = 539, /* "expectation" */ + EXPIRATION = 540, /* "expiration" */ + HELPER = 541, /* "helper" */ + LABEL = 542, /* "label" */ + STATE = 543, /* "state" */ + STATUS = 544, /* "status" */ + ORIGINAL = 545, /* "original" */ + REPLY = 546, /* "reply" */ + COUNTER = 547, /* "counter" */ + NAME = 548, /* "name" */ + PACKETS = 549, /* "packets" */ + BYTES = 550, /* "bytes" */ + AVGPKT = 551, /* "avgpkt" */ + LAST = 552, /* "last" */ + NEVER = 553, /* "never" */ + COUNTERS = 554, /* "counters" */ + QUOTAS = 555, /* "quotas" */ + LIMITS = 556, /* "limits" */ + SYNPROXYS = 557, /* "synproxys" */ + HELPERS = 558, /* "helpers" */ + LOG = 559, /* "log" */ + PREFIX = 560, /* "prefix" */ + GROUP = 561, /* "group" */ + SNAPLEN = 562, /* "snaplen" */ + QUEUE_THRESHOLD = 563, /* "queue-threshold" */ + LEVEL = 564, /* "level" */ + LIMIT = 565, /* "limit" */ + RATE = 566, /* "rate" */ + BURST = 567, /* "burst" */ + OVER = 568, /* "over" */ + UNTIL = 569, /* "until" */ + QUOTA = 570, /* "quota" */ + USED = 571, /* "used" */ + SECMARK = 572, /* "secmark" */ + SECMARKS = 573, /* "secmarks" */ + SECOND = 574, /* "second" */ + MINUTE = 575, /* "minute" */ + HOUR = 576, /* "hour" */ + DAY = 577, /* "day" */ + WEEK = 578, /* "week" */ + _REJECT = 579, /* "reject" */ + WITH = 580, /* "with" */ + ICMPX = 581, /* "icmpx" */ + SNAT = 582, /* "snat" */ + DNAT = 583, /* "dnat" */ + MASQUERADE = 584, /* "masquerade" */ + REDIRECT = 585, /* "redirect" */ + RANDOM = 586, /* "random" */ + FULLY_RANDOM = 587, /* "fully-random" */ + PERSISTENT = 588, /* "persistent" */ + QUEUE = 589, /* "queue" */ + QUEUENUM = 590, /* "num" */ + BYPASS = 591, /* "bypass" */ + FANOUT = 592, /* "fanout" */ + DUP = 593, /* "dup" */ + FWD = 594, /* "fwd" */ + NUMGEN = 595, /* "numgen" */ + INC = 596, /* "inc" */ + MOD = 597, /* "mod" */ + OFFSET = 598, /* "offset" */ + JHASH = 599, /* "jhash" */ + SYMHASH = 600, /* "symhash" */ + SEED = 601, /* "seed" */ + POSITION = 602, /* "position" */ + INDEX = 603, /* "index" */ + COMMENT = 604, /* "comment" */ + XML = 605, /* "xml" */ + JSON = 606, /* "json" */ + VM = 607, /* "vm" */ + NOTRACK = 608, /* "notrack" */ + EXISTS = 609, /* "exists" */ + MISSING = 610, /* "missing" */ + EXTHDR = 611, /* "exthdr" */ + IPSEC = 612, /* "ipsec" */ + REQID = 613, /* "reqid" */ + SPNUM = 614, /* "spnum" */ + IN = 615, /* "in" */ + OUT = 616, /* "out" */ + XT = 617 /* "xt" */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define TOKEN_EOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define JUNK 258 +#define NEWLINE 259 +#define COLON 260 +#define SEMICOLON 261 +#define COMMA 262 +#define DOT 263 +#define EQ 264 +#define NEQ 265 +#define LT 266 +#define GT 267 +#define GTE 268 +#define LTE 269 +#define LSHIFT 270 +#define RSHIFT 271 +#define AMPERSAND 272 +#define CARET 273 +#define NOT 274 +#define SLASH 275 +#define ASTERISK 276 +#define DASH 277 +#define AT 278 +#define VMAP 279 +#define PLUS 280 +#define INCLUDE 281 +#define DEFINE 282 +#define REDEFINE 283 +#define UNDEFINE 284 +#define FIB 285 +#define SOCKET 286 +#define TRANSPARENT 287 +#define WILDCARD 288 +#define CGROUPV2 289 +#define TPROXY 290 +#define OSF 291 +#define SYNPROXY 292 +#define MSS 293 +#define WSCALE 294 +#define TYPEOF 295 +#define HOOK 296 +#define HOOKS 297 +#define DEVICE 298 +#define DEVICES 299 +#define TABLE 300 +#define TABLES 301 +#define CHAIN 302 +#define CHAINS 303 +#define RULE 304 +#define RULES 305 +#define SETS 306 +#define SET 307 +#define ELEMENT 308 +#define MAP 309 +#define MAPS 310 +#define FLOWTABLE 311 +#define HANDLE 312 +#define RULESET 313 +#define TRACE 314 +#define INET 315 +#define NETDEV 316 +#define ADD 317 +#define UPDATE 318 +#define REPLACE 319 +#define CREATE 320 +#define INSERT 321 +#define DELETE 322 +#define GET 323 +#define LIST 324 +#define RESET 325 +#define FLUSH 326 +#define RENAME 327 +#define DESCRIBE 328 +#define IMPORT 329 +#define EXPORT 330 +#define DESTROY 331 +#define MONITOR 332 +#define ALL 333 +#define ACCEPT 334 +#define DROP 335 +#define CONTINUE 336 +#define JUMP 337 +#define GOTO 338 +#define RETURN 339 +#define TO 340 +#define CONSTANT 341 +#define INTERVAL 342 +#define DYNAMIC 343 +#define AUTOMERGE 344 +#define TIMEOUT 345 +#define GC_INTERVAL 346 +#define ELEMENTS 347 +#define EXPIRES 348 +#define POLICY 349 +#define MEMORY 350 +#define PERFORMANCE 351 +#define SIZE 352 +#define FLOW 353 +#define OFFLOAD 354 +#define METER 355 +#define METERS 356 +#define FLOWTABLES 357 +#define NUM 358 +#define STRING 359 +#define QUOTED_STRING 360 +#define ASTERISK_STRING 361 +#define LL_HDR 362 +#define NETWORK_HDR 363 +#define TRANSPORT_HDR 364 +#define BRIDGE 365 +#define ETHER 366 +#define SADDR 367 +#define DADDR 368 +#define TYPE 369 +#define VLAN 370 +#define ID 371 +#define CFI 372 +#define DEI 373 +#define PCP 374 +#define ARP 375 +#define HTYPE 376 +#define PTYPE 377 +#define HLEN 378 +#define PLEN 379 +#define OPERATION 380 +#define IP 381 +#define HDRVERSION 382 +#define HDRLENGTH 383 +#define DSCP 384 +#define ECN 385 +#define LENGTH 386 +#define FRAG_OFF 387 +#define TTL 388 +#define PROTOCOL 389 +#define CHECKSUM 390 +#define PTR 391 +#define VALUE 392 +#define LSRR 393 +#define RR 394 +#define SSRR 395 +#define RA 396 +#define ICMP 397 +#define CODE 398 +#define SEQUENCE 399 +#define GATEWAY 400 +#define MTU 401 +#define IGMP 402 +#define MRT 403 +#define OPTIONS 404 +#define IP6 405 +#define PRIORITY 406 +#define FLOWLABEL 407 +#define NEXTHDR 408 +#define HOPLIMIT 409 +#define ICMP6 410 +#define PPTR 411 +#define MAXDELAY 412 +#define TADDR 413 +#define AH 414 +#define RESERVED 415 +#define SPI 416 +#define ESP 417 +#define COMP 418 +#define FLAGS 419 +#define CPI 420 +#define PORT 421 +#define UDP 422 +#define SPORT 423 +#define DPORT 424 +#define UDPLITE 425 +#define CSUMCOV 426 +#define TCP 427 +#define ACKSEQ 428 +#define DOFF 429 +#define WINDOW 430 +#define URGPTR 431 +#define OPTION 432 +#define ECHO 433 +#define EOL 434 +#define MPTCP 435 +#define NOP 436 +#define SACK 437 +#define SACK0 438 +#define SACK1 439 +#define SACK2 440 +#define SACK3 441 +#define SACK_PERM 442 +#define FASTOPEN 443 +#define MD5SIG 444 +#define TIMESTAMP 445 +#define COUNT 446 +#define LEFT 447 +#define RIGHT 448 +#define TSVAL 449 +#define TSECR 450 +#define SUBTYPE 451 +#define DCCP 452 +#define VXLAN 453 +#define VNI 454 +#define GRE 455 +#define GRETAP 456 +#define GENEVE 457 +#define SCTP 458 +#define CHUNK 459 +#define DATA 460 +#define INIT 461 +#define INIT_ACK 462 +#define HEARTBEAT 463 +#define HEARTBEAT_ACK 464 +#define ABORT 465 +#define SHUTDOWN 466 +#define SHUTDOWN_ACK 467 +#define ERROR 468 +#define COOKIE_ECHO 469 +#define COOKIE_ACK 470 +#define ECNE 471 +#define CWR 472 +#define SHUTDOWN_COMPLETE 473 +#define ASCONF_ACK 474 +#define FORWARD_TSN 475 +#define ASCONF 476 +#define TSN 477 +#define STREAM 478 +#define SSN 479 +#define PPID 480 +#define INIT_TAG 481 +#define A_RWND 482 +#define NUM_OSTREAMS 483 +#define NUM_ISTREAMS 484 +#define INIT_TSN 485 +#define CUM_TSN_ACK 486 +#define NUM_GACK_BLOCKS 487 +#define NUM_DUP_TSNS 488 +#define LOWEST_TSN 489 +#define SEQNO 490 +#define NEW_CUM_TSN 491 +#define VTAG 492 +#define RT 493 +#define RT0 494 +#define RT2 495 +#define RT4 496 +#define SEG_LEFT 497 +#define ADDR 498 +#define LAST_ENT 499 +#define TAG 500 +#define SID 501 +#define HBH 502 +#define FRAG 503 +#define RESERVED2 504 +#define MORE_FRAGMENTS 505 +#define DST 506 +#define MH 507 +#define META 508 +#define MARK 509 +#define IIF 510 +#define IIFNAME 511 +#define IIFTYPE 512 +#define OIF 513 +#define OIFNAME 514 +#define OIFTYPE 515 +#define SKUID 516 +#define SKGID 517 +#define NFTRACE 518 +#define RTCLASSID 519 +#define IBRIPORT 520 +#define OBRIPORT 521 +#define IBRIDGENAME 522 +#define OBRIDGENAME 523 +#define PKTTYPE 524 +#define CPU 525 +#define IIFGROUP 526 +#define OIFGROUP 527 +#define CGROUP 528 +#define TIME 529 +#define CLASSID 530 +#define NEXTHOP 531 +#define CT 532 +#define L3PROTOCOL 533 +#define PROTO_SRC 534 +#define PROTO_DST 535 +#define ZONE 536 +#define DIRECTION 537 +#define EVENT 538 +#define EXPECTATION 539 +#define EXPIRATION 540 +#define HELPER 541 +#define LABEL 542 +#define STATE 543 +#define STATUS 544 +#define ORIGINAL 545 +#define REPLY 546 +#define COUNTER 547 +#define NAME 548 +#define PACKETS 549 +#define BYTES 550 +#define AVGPKT 551 +#define LAST 552 +#define NEVER 553 +#define COUNTERS 554 +#define QUOTAS 555 +#define LIMITS 556 +#define SYNPROXYS 557 +#define HELPERS 558 +#define LOG 559 +#define PREFIX 560 +#define GROUP 561 +#define SNAPLEN 562 +#define QUEUE_THRESHOLD 563 +#define LEVEL 564 +#define LIMIT 565 +#define RATE 566 +#define BURST 567 +#define OVER 568 +#define UNTIL 569 +#define QUOTA 570 +#define USED 571 +#define SECMARK 572 +#define SECMARKS 573 +#define SECOND 574 +#define MINUTE 575 +#define HOUR 576 +#define DAY 577 +#define WEEK 578 +#define _REJECT 579 +#define WITH 580 +#define ICMPX 581 +#define SNAT 582 +#define DNAT 583 +#define MASQUERADE 584 +#define REDIRECT 585 +#define RANDOM 586 +#define FULLY_RANDOM 587 +#define PERSISTENT 588 +#define QUEUE 589 +#define QUEUENUM 590 +#define BYPASS 591 +#define FANOUT 592 +#define DUP 593 +#define FWD 594 +#define NUMGEN 595 +#define INC 596 +#define MOD 597 +#define OFFSET 598 +#define JHASH 599 +#define SYMHASH 600 +#define SEED 601 +#define POSITION 602 +#define INDEX 603 +#define COMMENT 604 +#define XML 605 +#define JSON 606 +#define VM 607 +#define NOTRACK 608 +#define EXISTS 609 +#define MISSING 610 +#define EXTHDR 611 +#define IPSEC 612 +#define REQID 613 +#define SPNUM 614 +#define IN 615 +#define OUT 616 +#define XT 617 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 205 "parser_bison.y" + + uint64_t val; + uint32_t val32; + uint8_t val8; + 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; + struct set *set; + struct obj *obj; + struct flowtable *flowtable; + struct ct *ct; + const struct datatype *datatype; + struct handle_spec handle_spec; + struct position_spec position_spec; + struct prio_spec prio_spec; + struct limit_rate limit_rate; + struct tcp_kind_field { + uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */ + uint8_t field; + } tcp_kind_field; + +#line 1056 "parser_bison.c" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int nft_parse (struct nft_ctx *nft, void *scanner, struct parser_state *state); + +#endif /* !YY_NFT_PARSER_BISON_H_INCLUDED */ +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_JUNK = 3, /* "junk" */ + YYSYMBOL_NEWLINE = 4, /* "newline" */ + YYSYMBOL_COLON = 5, /* "colon" */ + YYSYMBOL_SEMICOLON = 6, /* "semicolon" */ + YYSYMBOL_COMMA = 7, /* "comma" */ + YYSYMBOL_DOT = 8, /* "." */ + YYSYMBOL_EQ = 9, /* "==" */ + YYSYMBOL_NEQ = 10, /* "!=" */ + YYSYMBOL_LT = 11, /* "<" */ + YYSYMBOL_GT = 12, /* ">" */ + YYSYMBOL_GTE = 13, /* ">=" */ + YYSYMBOL_LTE = 14, /* "<=" */ + YYSYMBOL_LSHIFT = 15, /* "<<" */ + YYSYMBOL_RSHIFT = 16, /* ">>" */ + YYSYMBOL_AMPERSAND = 17, /* "&" */ + YYSYMBOL_CARET = 18, /* "^" */ + YYSYMBOL_NOT = 19, /* "!" */ + YYSYMBOL_SLASH = 20, /* "/" */ + YYSYMBOL_ASTERISK = 21, /* "*" */ + YYSYMBOL_DASH = 22, /* "-" */ + YYSYMBOL_AT = 23, /* "@" */ + YYSYMBOL_VMAP = 24, /* "vmap" */ + YYSYMBOL_PLUS = 25, /* "+" */ + YYSYMBOL_INCLUDE = 26, /* "include" */ + YYSYMBOL_DEFINE = 27, /* "define" */ + YYSYMBOL_REDEFINE = 28, /* "redefine" */ + YYSYMBOL_UNDEFINE = 29, /* "undefine" */ + YYSYMBOL_FIB = 30, /* "fib" */ + YYSYMBOL_SOCKET = 31, /* "socket" */ + YYSYMBOL_TRANSPARENT = 32, /* "transparent" */ + YYSYMBOL_WILDCARD = 33, /* "wildcard" */ + YYSYMBOL_CGROUPV2 = 34, /* "cgroupv2" */ + YYSYMBOL_TPROXY = 35, /* "tproxy" */ + YYSYMBOL_OSF = 36, /* "osf" */ + YYSYMBOL_SYNPROXY = 37, /* "synproxy" */ + YYSYMBOL_MSS = 38, /* "mss" */ + YYSYMBOL_WSCALE = 39, /* "wscale" */ + YYSYMBOL_TYPEOF = 40, /* "typeof" */ + YYSYMBOL_HOOK = 41, /* "hook" */ + YYSYMBOL_HOOKS = 42, /* "hooks" */ + YYSYMBOL_DEVICE = 43, /* "device" */ + YYSYMBOL_DEVICES = 44, /* "devices" */ + YYSYMBOL_TABLE = 45, /* "table" */ + YYSYMBOL_TABLES = 46, /* "tables" */ + YYSYMBOL_CHAIN = 47, /* "chain" */ + YYSYMBOL_CHAINS = 48, /* "chains" */ + YYSYMBOL_RULE = 49, /* "rule" */ + YYSYMBOL_RULES = 50, /* "rules" */ + YYSYMBOL_SETS = 51, /* "sets" */ + YYSYMBOL_SET = 52, /* "set" */ + YYSYMBOL_ELEMENT = 53, /* "element" */ + YYSYMBOL_MAP = 54, /* "map" */ + YYSYMBOL_MAPS = 55, /* "maps" */ + YYSYMBOL_FLOWTABLE = 56, /* "flowtable" */ + YYSYMBOL_HANDLE = 57, /* "handle" */ + YYSYMBOL_RULESET = 58, /* "ruleset" */ + YYSYMBOL_TRACE = 59, /* "trace" */ + YYSYMBOL_INET = 60, /* "inet" */ + YYSYMBOL_NETDEV = 61, /* "netdev" */ + YYSYMBOL_ADD = 62, /* "add" */ + YYSYMBOL_UPDATE = 63, /* "update" */ + YYSYMBOL_REPLACE = 64, /* "replace" */ + YYSYMBOL_CREATE = 65, /* "create" */ + YYSYMBOL_INSERT = 66, /* "insert" */ + YYSYMBOL_DELETE = 67, /* "delete" */ + YYSYMBOL_GET = 68, /* "get" */ + YYSYMBOL_LIST = 69, /* "list" */ + YYSYMBOL_RESET = 70, /* "reset" */ + YYSYMBOL_FLUSH = 71, /* "flush" */ + YYSYMBOL_RENAME = 72, /* "rename" */ + YYSYMBOL_DESCRIBE = 73, /* "describe" */ + YYSYMBOL_IMPORT = 74, /* "import" */ + YYSYMBOL_EXPORT = 75, /* "export" */ + YYSYMBOL_DESTROY = 76, /* "destroy" */ + YYSYMBOL_MONITOR = 77, /* "monitor" */ + YYSYMBOL_ALL = 78, /* "all" */ + YYSYMBOL_ACCEPT = 79, /* "accept" */ + YYSYMBOL_DROP = 80, /* "drop" */ + YYSYMBOL_CONTINUE = 81, /* "continue" */ + YYSYMBOL_JUMP = 82, /* "jump" */ + YYSYMBOL_GOTO = 83, /* "goto" */ + YYSYMBOL_RETURN = 84, /* "return" */ + YYSYMBOL_TO = 85, /* "to" */ + YYSYMBOL_CONSTANT = 86, /* "constant" */ + YYSYMBOL_INTERVAL = 87, /* "interval" */ + YYSYMBOL_DYNAMIC = 88, /* "dynamic" */ + YYSYMBOL_AUTOMERGE = 89, /* "auto-merge" */ + YYSYMBOL_TIMEOUT = 90, /* "timeout" */ + YYSYMBOL_GC_INTERVAL = 91, /* "gc-interval" */ + YYSYMBOL_ELEMENTS = 92, /* "elements" */ + YYSYMBOL_EXPIRES = 93, /* "expires" */ + YYSYMBOL_POLICY = 94, /* "policy" */ + YYSYMBOL_MEMORY = 95, /* "memory" */ + YYSYMBOL_PERFORMANCE = 96, /* "performance" */ + YYSYMBOL_SIZE = 97, /* "size" */ + YYSYMBOL_FLOW = 98, /* "flow" */ + YYSYMBOL_OFFLOAD = 99, /* "offload" */ + YYSYMBOL_METER = 100, /* "meter" */ + YYSYMBOL_METERS = 101, /* "meters" */ + YYSYMBOL_FLOWTABLES = 102, /* "flowtables" */ + YYSYMBOL_NUM = 103, /* "number" */ + YYSYMBOL_STRING = 104, /* "string" */ + YYSYMBOL_QUOTED_STRING = 105, /* "quoted string" */ + YYSYMBOL_ASTERISK_STRING = 106, /* "string with a trailing asterisk" */ + YYSYMBOL_LL_HDR = 107, /* "ll" */ + YYSYMBOL_NETWORK_HDR = 108, /* "nh" */ + YYSYMBOL_TRANSPORT_HDR = 109, /* "th" */ + YYSYMBOL_BRIDGE = 110, /* "bridge" */ + YYSYMBOL_ETHER = 111, /* "ether" */ + YYSYMBOL_SADDR = 112, /* "saddr" */ + YYSYMBOL_DADDR = 113, /* "daddr" */ + YYSYMBOL_TYPE = 114, /* "type" */ + YYSYMBOL_VLAN = 115, /* "vlan" */ + YYSYMBOL_ID = 116, /* "id" */ + YYSYMBOL_CFI = 117, /* "cfi" */ + YYSYMBOL_DEI = 118, /* "dei" */ + YYSYMBOL_PCP = 119, /* "pcp" */ + YYSYMBOL_ARP = 120, /* "arp" */ + YYSYMBOL_HTYPE = 121, /* "htype" */ + YYSYMBOL_PTYPE = 122, /* "ptype" */ + YYSYMBOL_HLEN = 123, /* "hlen" */ + YYSYMBOL_PLEN = 124, /* "plen" */ + YYSYMBOL_OPERATION = 125, /* "operation" */ + YYSYMBOL_IP = 126, /* "ip" */ + YYSYMBOL_HDRVERSION = 127, /* "version" */ + YYSYMBOL_HDRLENGTH = 128, /* "hdrlength" */ + YYSYMBOL_DSCP = 129, /* "dscp" */ + YYSYMBOL_ECN = 130, /* "ecn" */ + YYSYMBOL_LENGTH = 131, /* "length" */ + YYSYMBOL_FRAG_OFF = 132, /* "frag-off" */ + YYSYMBOL_TTL = 133, /* "ttl" */ + YYSYMBOL_PROTOCOL = 134, /* "protocol" */ + YYSYMBOL_CHECKSUM = 135, /* "checksum" */ + YYSYMBOL_PTR = 136, /* "ptr" */ + YYSYMBOL_VALUE = 137, /* "value" */ + YYSYMBOL_LSRR = 138, /* "lsrr" */ + YYSYMBOL_RR = 139, /* "rr" */ + YYSYMBOL_SSRR = 140, /* "ssrr" */ + YYSYMBOL_RA = 141, /* "ra" */ + YYSYMBOL_ICMP = 142, /* "icmp" */ + YYSYMBOL_CODE = 143, /* "code" */ + YYSYMBOL_SEQUENCE = 144, /* "seq" */ + YYSYMBOL_GATEWAY = 145, /* "gateway" */ + YYSYMBOL_MTU = 146, /* "mtu" */ + YYSYMBOL_IGMP = 147, /* "igmp" */ + YYSYMBOL_MRT = 148, /* "mrt" */ + YYSYMBOL_OPTIONS = 149, /* "options" */ + YYSYMBOL_IP6 = 150, /* "ip6" */ + YYSYMBOL_PRIORITY = 151, /* "priority" */ + YYSYMBOL_FLOWLABEL = 152, /* "flowlabel" */ + YYSYMBOL_NEXTHDR = 153, /* "nexthdr" */ + YYSYMBOL_HOPLIMIT = 154, /* "hoplimit" */ + YYSYMBOL_ICMP6 = 155, /* "icmpv6" */ + YYSYMBOL_PPTR = 156, /* "param-problem" */ + YYSYMBOL_MAXDELAY = 157, /* "max-delay" */ + YYSYMBOL_TADDR = 158, /* "taddr" */ + YYSYMBOL_AH = 159, /* "ah" */ + YYSYMBOL_RESERVED = 160, /* "reserved" */ + YYSYMBOL_SPI = 161, /* "spi" */ + YYSYMBOL_ESP = 162, /* "esp" */ + YYSYMBOL_COMP = 163, /* "comp" */ + YYSYMBOL_FLAGS = 164, /* "flags" */ + YYSYMBOL_CPI = 165, /* "cpi" */ + YYSYMBOL_PORT = 166, /* "port" */ + YYSYMBOL_UDP = 167, /* "udp" */ + YYSYMBOL_SPORT = 168, /* "sport" */ + YYSYMBOL_DPORT = 169, /* "dport" */ + YYSYMBOL_UDPLITE = 170, /* "udplite" */ + YYSYMBOL_CSUMCOV = 171, /* "csumcov" */ + YYSYMBOL_TCP = 172, /* "tcp" */ + YYSYMBOL_ACKSEQ = 173, /* "ackseq" */ + YYSYMBOL_DOFF = 174, /* "doff" */ + YYSYMBOL_WINDOW = 175, /* "window" */ + YYSYMBOL_URGPTR = 176, /* "urgptr" */ + YYSYMBOL_OPTION = 177, /* "option" */ + YYSYMBOL_ECHO = 178, /* "echo" */ + YYSYMBOL_EOL = 179, /* "eol" */ + YYSYMBOL_MPTCP = 180, /* "mptcp" */ + YYSYMBOL_NOP = 181, /* "nop" */ + YYSYMBOL_SACK = 182, /* "sack" */ + YYSYMBOL_SACK0 = 183, /* "sack0" */ + YYSYMBOL_SACK1 = 184, /* "sack1" */ + YYSYMBOL_SACK2 = 185, /* "sack2" */ + YYSYMBOL_SACK3 = 186, /* "sack3" */ + YYSYMBOL_SACK_PERM = 187, /* "sack-permitted" */ + YYSYMBOL_FASTOPEN = 188, /* "fastopen" */ + YYSYMBOL_MD5SIG = 189, /* "md5sig" */ + YYSYMBOL_TIMESTAMP = 190, /* "timestamp" */ + YYSYMBOL_COUNT = 191, /* "count" */ + YYSYMBOL_LEFT = 192, /* "left" */ + YYSYMBOL_RIGHT = 193, /* "right" */ + YYSYMBOL_TSVAL = 194, /* "tsval" */ + YYSYMBOL_TSECR = 195, /* "tsecr" */ + YYSYMBOL_SUBTYPE = 196, /* "subtype" */ + YYSYMBOL_DCCP = 197, /* "dccp" */ + YYSYMBOL_VXLAN = 198, /* "vxlan" */ + YYSYMBOL_VNI = 199, /* "vni" */ + YYSYMBOL_GRE = 200, /* "gre" */ + YYSYMBOL_GRETAP = 201, /* "gretap" */ + YYSYMBOL_GENEVE = 202, /* "geneve" */ + YYSYMBOL_SCTP = 203, /* "sctp" */ + YYSYMBOL_CHUNK = 204, /* "chunk" */ + YYSYMBOL_DATA = 205, /* "data" */ + YYSYMBOL_INIT = 206, /* "init" */ + YYSYMBOL_INIT_ACK = 207, /* "init-ack" */ + YYSYMBOL_HEARTBEAT = 208, /* "heartbeat" */ + YYSYMBOL_HEARTBEAT_ACK = 209, /* "heartbeat-ack" */ + YYSYMBOL_ABORT = 210, /* "abort" */ + YYSYMBOL_SHUTDOWN = 211, /* "shutdown" */ + YYSYMBOL_SHUTDOWN_ACK = 212, /* "shutdown-ack" */ + YYSYMBOL_ERROR = 213, /* "error" */ + YYSYMBOL_COOKIE_ECHO = 214, /* "cookie-echo" */ + YYSYMBOL_COOKIE_ACK = 215, /* "cookie-ack" */ + YYSYMBOL_ECNE = 216, /* "ecne" */ + YYSYMBOL_CWR = 217, /* "cwr" */ + YYSYMBOL_SHUTDOWN_COMPLETE = 218, /* "shutdown-complete" */ + YYSYMBOL_ASCONF_ACK = 219, /* "asconf-ack" */ + YYSYMBOL_FORWARD_TSN = 220, /* "forward-tsn" */ + YYSYMBOL_ASCONF = 221, /* "asconf" */ + YYSYMBOL_TSN = 222, /* "tsn" */ + YYSYMBOL_STREAM = 223, /* "stream" */ + YYSYMBOL_SSN = 224, /* "ssn" */ + YYSYMBOL_PPID = 225, /* "ppid" */ + YYSYMBOL_INIT_TAG = 226, /* "init-tag" */ + YYSYMBOL_A_RWND = 227, /* "a-rwnd" */ + YYSYMBOL_NUM_OSTREAMS = 228, /* "num-outbound-streams" */ + YYSYMBOL_NUM_ISTREAMS = 229, /* "num-inbound-streams" */ + YYSYMBOL_INIT_TSN = 230, /* "initial-tsn" */ + YYSYMBOL_CUM_TSN_ACK = 231, /* "cum-tsn-ack" */ + YYSYMBOL_NUM_GACK_BLOCKS = 232, /* "num-gap-ack-blocks" */ + YYSYMBOL_NUM_DUP_TSNS = 233, /* "num-dup-tsns" */ + YYSYMBOL_LOWEST_TSN = 234, /* "lowest-tsn" */ + YYSYMBOL_SEQNO = 235, /* "seqno" */ + YYSYMBOL_NEW_CUM_TSN = 236, /* "new-cum-tsn" */ + YYSYMBOL_VTAG = 237, /* "vtag" */ + YYSYMBOL_RT = 238, /* "rt" */ + YYSYMBOL_RT0 = 239, /* "rt0" */ + YYSYMBOL_RT2 = 240, /* "rt2" */ + YYSYMBOL_RT4 = 241, /* "srh" */ + YYSYMBOL_SEG_LEFT = 242, /* "seg-left" */ + YYSYMBOL_ADDR = 243, /* "addr" */ + YYSYMBOL_LAST_ENT = 244, /* "last-entry" */ + YYSYMBOL_TAG = 245, /* "tag" */ + YYSYMBOL_SID = 246, /* "sid" */ + YYSYMBOL_HBH = 247, /* "hbh" */ + YYSYMBOL_FRAG = 248, /* "frag" */ + YYSYMBOL_RESERVED2 = 249, /* "reserved2" */ + YYSYMBOL_MORE_FRAGMENTS = 250, /* "more-fragments" */ + YYSYMBOL_DST = 251, /* "dst" */ + YYSYMBOL_MH = 252, /* "mh" */ + YYSYMBOL_META = 253, /* "meta" */ + YYSYMBOL_MARK = 254, /* "mark" */ + YYSYMBOL_IIF = 255, /* "iif" */ + YYSYMBOL_IIFNAME = 256, /* "iifname" */ + YYSYMBOL_IIFTYPE = 257, /* "iiftype" */ + YYSYMBOL_OIF = 258, /* "oif" */ + YYSYMBOL_OIFNAME = 259, /* "oifname" */ + YYSYMBOL_OIFTYPE = 260, /* "oiftype" */ + YYSYMBOL_SKUID = 261, /* "skuid" */ + YYSYMBOL_SKGID = 262, /* "skgid" */ + YYSYMBOL_NFTRACE = 263, /* "nftrace" */ + YYSYMBOL_RTCLASSID = 264, /* "rtclassid" */ + YYSYMBOL_IBRIPORT = 265, /* "ibriport" */ + YYSYMBOL_OBRIPORT = 266, /* "obriport" */ + YYSYMBOL_IBRIDGENAME = 267, /* "ibrname" */ + YYSYMBOL_OBRIDGENAME = 268, /* "obrname" */ + YYSYMBOL_PKTTYPE = 269, /* "pkttype" */ + YYSYMBOL_CPU = 270, /* "cpu" */ + YYSYMBOL_IIFGROUP = 271, /* "iifgroup" */ + YYSYMBOL_OIFGROUP = 272, /* "oifgroup" */ + YYSYMBOL_CGROUP = 273, /* "cgroup" */ + YYSYMBOL_TIME = 274, /* "time" */ + YYSYMBOL_CLASSID = 275, /* "classid" */ + YYSYMBOL_NEXTHOP = 276, /* "nexthop" */ + YYSYMBOL_CT = 277, /* "ct" */ + YYSYMBOL_L3PROTOCOL = 278, /* "l3proto" */ + YYSYMBOL_PROTO_SRC = 279, /* "proto-src" */ + YYSYMBOL_PROTO_DST = 280, /* "proto-dst" */ + YYSYMBOL_ZONE = 281, /* "zone" */ + YYSYMBOL_DIRECTION = 282, /* "direction" */ + YYSYMBOL_EVENT = 283, /* "event" */ + YYSYMBOL_EXPECTATION = 284, /* "expectation" */ + YYSYMBOL_EXPIRATION = 285, /* "expiration" */ + YYSYMBOL_HELPER = 286, /* "helper" */ + YYSYMBOL_LABEL = 287, /* "label" */ + YYSYMBOL_STATE = 288, /* "state" */ + YYSYMBOL_STATUS = 289, /* "status" */ + YYSYMBOL_ORIGINAL = 290, /* "original" */ + YYSYMBOL_REPLY = 291, /* "reply" */ + YYSYMBOL_COUNTER = 292, /* "counter" */ + YYSYMBOL_NAME = 293, /* "name" */ + YYSYMBOL_PACKETS = 294, /* "packets" */ + YYSYMBOL_BYTES = 295, /* "bytes" */ + YYSYMBOL_AVGPKT = 296, /* "avgpkt" */ + YYSYMBOL_LAST = 297, /* "last" */ + YYSYMBOL_NEVER = 298, /* "never" */ + YYSYMBOL_COUNTERS = 299, /* "counters" */ + YYSYMBOL_QUOTAS = 300, /* "quotas" */ + YYSYMBOL_LIMITS = 301, /* "limits" */ + YYSYMBOL_SYNPROXYS = 302, /* "synproxys" */ + YYSYMBOL_HELPERS = 303, /* "helpers" */ + YYSYMBOL_LOG = 304, /* "log" */ + YYSYMBOL_PREFIX = 305, /* "prefix" */ + YYSYMBOL_GROUP = 306, /* "group" */ + YYSYMBOL_SNAPLEN = 307, /* "snaplen" */ + YYSYMBOL_QUEUE_THRESHOLD = 308, /* "queue-threshold" */ + YYSYMBOL_LEVEL = 309, /* "level" */ + YYSYMBOL_LIMIT = 310, /* "limit" */ + YYSYMBOL_RATE = 311, /* "rate" */ + YYSYMBOL_BURST = 312, /* "burst" */ + YYSYMBOL_OVER = 313, /* "over" */ + YYSYMBOL_UNTIL = 314, /* "until" */ + YYSYMBOL_QUOTA = 315, /* "quota" */ + YYSYMBOL_USED = 316, /* "used" */ + YYSYMBOL_SECMARK = 317, /* "secmark" */ + YYSYMBOL_SECMARKS = 318, /* "secmarks" */ + YYSYMBOL_SECOND = 319, /* "second" */ + YYSYMBOL_MINUTE = 320, /* "minute" */ + YYSYMBOL_HOUR = 321, /* "hour" */ + YYSYMBOL_DAY = 322, /* "day" */ + YYSYMBOL_WEEK = 323, /* "week" */ + YYSYMBOL__REJECT = 324, /* "reject" */ + YYSYMBOL_WITH = 325, /* "with" */ + YYSYMBOL_ICMPX = 326, /* "icmpx" */ + YYSYMBOL_SNAT = 327, /* "snat" */ + YYSYMBOL_DNAT = 328, /* "dnat" */ + YYSYMBOL_MASQUERADE = 329, /* "masquerade" */ + YYSYMBOL_REDIRECT = 330, /* "redirect" */ + YYSYMBOL_RANDOM = 331, /* "random" */ + YYSYMBOL_FULLY_RANDOM = 332, /* "fully-random" */ + YYSYMBOL_PERSISTENT = 333, /* "persistent" */ + YYSYMBOL_QUEUE = 334, /* "queue" */ + YYSYMBOL_QUEUENUM = 335, /* "num" */ + YYSYMBOL_BYPASS = 336, /* "bypass" */ + YYSYMBOL_FANOUT = 337, /* "fanout" */ + YYSYMBOL_DUP = 338, /* "dup" */ + YYSYMBOL_FWD = 339, /* "fwd" */ + YYSYMBOL_NUMGEN = 340, /* "numgen" */ + YYSYMBOL_INC = 341, /* "inc" */ + YYSYMBOL_MOD = 342, /* "mod" */ + YYSYMBOL_OFFSET = 343, /* "offset" */ + YYSYMBOL_JHASH = 344, /* "jhash" */ + YYSYMBOL_SYMHASH = 345, /* "symhash" */ + YYSYMBOL_SEED = 346, /* "seed" */ + YYSYMBOL_POSITION = 347, /* "position" */ + YYSYMBOL_INDEX = 348, /* "index" */ + YYSYMBOL_COMMENT = 349, /* "comment" */ + YYSYMBOL_XML = 350, /* "xml" */ + YYSYMBOL_JSON = 351, /* "json" */ + YYSYMBOL_VM = 352, /* "vm" */ + YYSYMBOL_NOTRACK = 353, /* "notrack" */ + YYSYMBOL_EXISTS = 354, /* "exists" */ + YYSYMBOL_MISSING = 355, /* "missing" */ + YYSYMBOL_EXTHDR = 356, /* "exthdr" */ + YYSYMBOL_IPSEC = 357, /* "ipsec" */ + YYSYMBOL_REQID = 358, /* "reqid" */ + YYSYMBOL_SPNUM = 359, /* "spnum" */ + YYSYMBOL_IN = 360, /* "in" */ + YYSYMBOL_OUT = 361, /* "out" */ + YYSYMBOL_XT = 362, /* "xt" */ + YYSYMBOL_363_ = 363, /* '=' */ + YYSYMBOL_364_ = 364, /* '{' */ + YYSYMBOL_365_ = 365, /* '}' */ + YYSYMBOL_366_ = 366, /* '(' */ + YYSYMBOL_367_ = 367, /* ')' */ + YYSYMBOL_368_ = 368, /* '|' */ + YYSYMBOL_369_ = 369, /* '$' */ + YYSYMBOL_370_ = 370, /* '[' */ + YYSYMBOL_371_ = 371, /* ']' */ + YYSYMBOL_YYACCEPT = 372, /* $accept */ + YYSYMBOL_input = 373, /* input */ + YYSYMBOL_stmt_separator = 374, /* stmt_separator */ + YYSYMBOL_opt_newline = 375, /* opt_newline */ + YYSYMBOL_close_scope_ah = 376, /* close_scope_ah */ + YYSYMBOL_close_scope_arp = 377, /* close_scope_arp */ + YYSYMBOL_close_scope_at = 378, /* close_scope_at */ + YYSYMBOL_close_scope_comp = 379, /* close_scope_comp */ + YYSYMBOL_close_scope_ct = 380, /* close_scope_ct */ + YYSYMBOL_close_scope_counter = 381, /* close_scope_counter */ + YYSYMBOL_close_scope_last = 382, /* close_scope_last */ + YYSYMBOL_close_scope_dccp = 383, /* close_scope_dccp */ + YYSYMBOL_close_scope_destroy = 384, /* close_scope_destroy */ + YYSYMBOL_close_scope_dst = 385, /* close_scope_dst */ + YYSYMBOL_close_scope_dup = 386, /* close_scope_dup */ + YYSYMBOL_close_scope_esp = 387, /* close_scope_esp */ + YYSYMBOL_close_scope_eth = 388, /* close_scope_eth */ + YYSYMBOL_close_scope_export = 389, /* close_scope_export */ + YYSYMBOL_close_scope_fib = 390, /* close_scope_fib */ + YYSYMBOL_close_scope_frag = 391, /* close_scope_frag */ + YYSYMBOL_close_scope_fwd = 392, /* close_scope_fwd */ + YYSYMBOL_close_scope_gre = 393, /* close_scope_gre */ + YYSYMBOL_close_scope_hash = 394, /* close_scope_hash */ + YYSYMBOL_close_scope_hbh = 395, /* close_scope_hbh */ + YYSYMBOL_close_scope_ip = 396, /* close_scope_ip */ + YYSYMBOL_close_scope_ip6 = 397, /* close_scope_ip6 */ + YYSYMBOL_close_scope_vlan = 398, /* close_scope_vlan */ + YYSYMBOL_close_scope_icmp = 399, /* close_scope_icmp */ + YYSYMBOL_close_scope_igmp = 400, /* close_scope_igmp */ + YYSYMBOL_close_scope_import = 401, /* close_scope_import */ + YYSYMBOL_close_scope_ipsec = 402, /* close_scope_ipsec */ + YYSYMBOL_close_scope_list = 403, /* close_scope_list */ + YYSYMBOL_close_scope_limit = 404, /* close_scope_limit */ + YYSYMBOL_close_scope_meta = 405, /* close_scope_meta */ + YYSYMBOL_close_scope_mh = 406, /* close_scope_mh */ + YYSYMBOL_close_scope_monitor = 407, /* close_scope_monitor */ + YYSYMBOL_close_scope_nat = 408, /* close_scope_nat */ + YYSYMBOL_close_scope_numgen = 409, /* close_scope_numgen */ + YYSYMBOL_close_scope_osf = 410, /* close_scope_osf */ + YYSYMBOL_close_scope_policy = 411, /* close_scope_policy */ + YYSYMBOL_close_scope_quota = 412, /* close_scope_quota */ + YYSYMBOL_close_scope_queue = 413, /* close_scope_queue */ + YYSYMBOL_close_scope_reject = 414, /* close_scope_reject */ + YYSYMBOL_close_scope_reset = 415, /* close_scope_reset */ + YYSYMBOL_close_scope_rt = 416, /* close_scope_rt */ + YYSYMBOL_close_scope_sctp = 417, /* close_scope_sctp */ + YYSYMBOL_close_scope_sctp_chunk = 418, /* close_scope_sctp_chunk */ + YYSYMBOL_close_scope_secmark = 419, /* close_scope_secmark */ + YYSYMBOL_close_scope_socket = 420, /* close_scope_socket */ + YYSYMBOL_close_scope_tcp = 421, /* close_scope_tcp */ + YYSYMBOL_close_scope_tproxy = 422, /* close_scope_tproxy */ + YYSYMBOL_close_scope_type = 423, /* close_scope_type */ + YYSYMBOL_close_scope_th = 424, /* close_scope_th */ + YYSYMBOL_close_scope_udp = 425, /* close_scope_udp */ + YYSYMBOL_close_scope_udplite = 426, /* close_scope_udplite */ + YYSYMBOL_close_scope_log = 427, /* close_scope_log */ + YYSYMBOL_close_scope_synproxy = 428, /* close_scope_synproxy */ + YYSYMBOL_close_scope_xt = 429, /* close_scope_xt */ + YYSYMBOL_common_block = 430, /* common_block */ + YYSYMBOL_line = 431, /* line */ + YYSYMBOL_base_cmd = 432, /* base_cmd */ + YYSYMBOL_add_cmd = 433, /* add_cmd */ + YYSYMBOL_replace_cmd = 434, /* replace_cmd */ + YYSYMBOL_create_cmd = 435, /* create_cmd */ + YYSYMBOL_insert_cmd = 436, /* insert_cmd */ + YYSYMBOL_table_or_id_spec = 437, /* table_or_id_spec */ + YYSYMBOL_chain_or_id_spec = 438, /* chain_or_id_spec */ + YYSYMBOL_set_or_id_spec = 439, /* set_or_id_spec */ + YYSYMBOL_obj_or_id_spec = 440, /* obj_or_id_spec */ + YYSYMBOL_delete_cmd = 441, /* delete_cmd */ + YYSYMBOL_destroy_cmd = 442, /* destroy_cmd */ + YYSYMBOL_get_cmd = 443, /* get_cmd */ + YYSYMBOL_list_cmd = 444, /* list_cmd */ + YYSYMBOL_basehook_device_name = 445, /* basehook_device_name */ + YYSYMBOL_basehook_spec = 446, /* basehook_spec */ + YYSYMBOL_reset_cmd = 447, /* reset_cmd */ + YYSYMBOL_flush_cmd = 448, /* flush_cmd */ + YYSYMBOL_rename_cmd = 449, /* rename_cmd */ + YYSYMBOL_import_cmd = 450, /* import_cmd */ + YYSYMBOL_export_cmd = 451, /* export_cmd */ + YYSYMBOL_monitor_cmd = 452, /* monitor_cmd */ + YYSYMBOL_monitor_event = 453, /* monitor_event */ + YYSYMBOL_monitor_object = 454, /* monitor_object */ + YYSYMBOL_monitor_format = 455, /* monitor_format */ + YYSYMBOL_markup_format = 456, /* markup_format */ + YYSYMBOL_describe_cmd = 457, /* describe_cmd */ + YYSYMBOL_table_block_alloc = 458, /* table_block_alloc */ + YYSYMBOL_table_options = 459, /* table_options */ + YYSYMBOL_table_block = 460, /* table_block */ + YYSYMBOL_chain_block_alloc = 461, /* chain_block_alloc */ + YYSYMBOL_chain_block = 462, /* chain_block */ + YYSYMBOL_subchain_block = 463, /* subchain_block */ + YYSYMBOL_typeof_data_expr = 464, /* typeof_data_expr */ + YYSYMBOL_typeof_expr = 465, /* typeof_expr */ + YYSYMBOL_set_block_alloc = 466, /* set_block_alloc */ + YYSYMBOL_set_block = 467, /* set_block */ + YYSYMBOL_set_block_expr = 468, /* set_block_expr */ + YYSYMBOL_set_flag_list = 469, /* set_flag_list */ + YYSYMBOL_set_flag = 470, /* set_flag */ + YYSYMBOL_map_block_alloc = 471, /* map_block_alloc */ + YYSYMBOL_map_block_obj_type = 472, /* map_block_obj_type */ + YYSYMBOL_map_block_data_interval = 473, /* map_block_data_interval */ + YYSYMBOL_map_block = 474, /* map_block */ + YYSYMBOL_set_mechanism = 475, /* set_mechanism */ + YYSYMBOL_set_policy_spec = 476, /* set_policy_spec */ + YYSYMBOL_flowtable_block_alloc = 477, /* flowtable_block_alloc */ + YYSYMBOL_flowtable_block = 478, /* flowtable_block */ + YYSYMBOL_flowtable_expr = 479, /* flowtable_expr */ + YYSYMBOL_flowtable_list_expr = 480, /* flowtable_list_expr */ + YYSYMBOL_flowtable_expr_member = 481, /* flowtable_expr_member */ + YYSYMBOL_data_type_atom_expr = 482, /* data_type_atom_expr */ + YYSYMBOL_data_type_expr = 483, /* data_type_expr */ + YYSYMBOL_obj_block_alloc = 484, /* obj_block_alloc */ + YYSYMBOL_counter_block = 485, /* counter_block */ + YYSYMBOL_quota_block = 486, /* quota_block */ + YYSYMBOL_ct_helper_block = 487, /* ct_helper_block */ + YYSYMBOL_ct_timeout_block = 488, /* ct_timeout_block */ + YYSYMBOL_ct_expect_block = 489, /* ct_expect_block */ + YYSYMBOL_limit_block = 490, /* limit_block */ + YYSYMBOL_secmark_block = 491, /* secmark_block */ + YYSYMBOL_synproxy_block = 492, /* synproxy_block */ + YYSYMBOL_type_identifier = 493, /* type_identifier */ + YYSYMBOL_hook_spec = 494, /* hook_spec */ + YYSYMBOL_prio_spec = 495, /* prio_spec */ + YYSYMBOL_extended_prio_name = 496, /* extended_prio_name */ + YYSYMBOL_extended_prio_spec = 497, /* extended_prio_spec */ + YYSYMBOL_int_num = 498, /* int_num */ + YYSYMBOL_dev_spec = 499, /* dev_spec */ + YYSYMBOL_flags_spec = 500, /* flags_spec */ + YYSYMBOL_policy_spec = 501, /* policy_spec */ + YYSYMBOL_policy_expr = 502, /* policy_expr */ + YYSYMBOL_chain_policy = 503, /* chain_policy */ + YYSYMBOL_identifier = 504, /* identifier */ + YYSYMBOL_string = 505, /* string */ + YYSYMBOL_time_spec = 506, /* time_spec */ + YYSYMBOL_time_spec_or_num_s = 507, /* time_spec_or_num_s */ + YYSYMBOL_family_spec = 508, /* family_spec */ + YYSYMBOL_family_spec_explicit = 509, /* family_spec_explicit */ + YYSYMBOL_table_spec = 510, /* table_spec */ + YYSYMBOL_tableid_spec = 511, /* tableid_spec */ + YYSYMBOL_chain_spec = 512, /* chain_spec */ + YYSYMBOL_chainid_spec = 513, /* chainid_spec */ + YYSYMBOL_chain_identifier = 514, /* chain_identifier */ + YYSYMBOL_set_spec = 515, /* set_spec */ + YYSYMBOL_setid_spec = 516, /* setid_spec */ + YYSYMBOL_set_identifier = 517, /* set_identifier */ + YYSYMBOL_flowtable_spec = 518, /* flowtable_spec */ + YYSYMBOL_flowtableid_spec = 519, /* flowtableid_spec */ + YYSYMBOL_flowtable_identifier = 520, /* flowtable_identifier */ + YYSYMBOL_obj_spec = 521, /* obj_spec */ + YYSYMBOL_objid_spec = 522, /* objid_spec */ + YYSYMBOL_obj_identifier = 523, /* obj_identifier */ + YYSYMBOL_handle_spec = 524, /* handle_spec */ + YYSYMBOL_position_spec = 525, /* position_spec */ + YYSYMBOL_index_spec = 526, /* index_spec */ + YYSYMBOL_rule_position = 527, /* rule_position */ + YYSYMBOL_ruleid_spec = 528, /* ruleid_spec */ + YYSYMBOL_comment_spec = 529, /* comment_spec */ + YYSYMBOL_ruleset_spec = 530, /* ruleset_spec */ + YYSYMBOL_rule = 531, /* rule */ + YYSYMBOL_rule_alloc = 532, /* rule_alloc */ + YYSYMBOL_stmt_list = 533, /* stmt_list */ + YYSYMBOL_stateful_stmt_list = 534, /* stateful_stmt_list */ + YYSYMBOL_stateful_stmt = 535, /* stateful_stmt */ + YYSYMBOL_stmt = 536, /* stmt */ + YYSYMBOL_xt_stmt = 537, /* xt_stmt */ + YYSYMBOL_chain_stmt_type = 538, /* chain_stmt_type */ + YYSYMBOL_chain_stmt = 539, /* chain_stmt */ + YYSYMBOL_verdict_stmt = 540, /* verdict_stmt */ + YYSYMBOL_verdict_map_stmt = 541, /* verdict_map_stmt */ + YYSYMBOL_verdict_map_expr = 542, /* verdict_map_expr */ + YYSYMBOL_verdict_map_list_expr = 543, /* verdict_map_list_expr */ + YYSYMBOL_verdict_map_list_member_expr = 544, /* verdict_map_list_member_expr */ + YYSYMBOL_connlimit_stmt = 545, /* connlimit_stmt */ + YYSYMBOL_counter_stmt = 546, /* counter_stmt */ + YYSYMBOL_counter_stmt_alloc = 547, /* counter_stmt_alloc */ + YYSYMBOL_counter_args = 548, /* counter_args */ + YYSYMBOL_counter_arg = 549, /* counter_arg */ + YYSYMBOL_last_stmt = 550, /* last_stmt */ + YYSYMBOL_log_stmt = 551, /* log_stmt */ + YYSYMBOL_log_stmt_alloc = 552, /* log_stmt_alloc */ + YYSYMBOL_log_args = 553, /* log_args */ + YYSYMBOL_log_arg = 554, /* log_arg */ + YYSYMBOL_level_type = 555, /* level_type */ + YYSYMBOL_log_flags = 556, /* log_flags */ + YYSYMBOL_log_flags_tcp = 557, /* log_flags_tcp */ + YYSYMBOL_log_flag_tcp = 558, /* log_flag_tcp */ + YYSYMBOL_limit_stmt = 559, /* limit_stmt */ + YYSYMBOL_quota_mode = 560, /* quota_mode */ + YYSYMBOL_quota_unit = 561, /* quota_unit */ + YYSYMBOL_quota_used = 562, /* quota_used */ + YYSYMBOL_quota_stmt = 563, /* quota_stmt */ + YYSYMBOL_limit_mode = 564, /* limit_mode */ + YYSYMBOL_limit_burst_pkts = 565, /* limit_burst_pkts */ + YYSYMBOL_limit_rate_pkts = 566, /* limit_rate_pkts */ + YYSYMBOL_limit_burst_bytes = 567, /* limit_burst_bytes */ + YYSYMBOL_limit_rate_bytes = 568, /* limit_rate_bytes */ + YYSYMBOL_limit_bytes = 569, /* limit_bytes */ + YYSYMBOL_time_unit = 570, /* time_unit */ + YYSYMBOL_reject_stmt = 571, /* reject_stmt */ + YYSYMBOL_reject_stmt_alloc = 572, /* reject_stmt_alloc */ + YYSYMBOL_reject_with_expr = 573, /* reject_with_expr */ + YYSYMBOL_reject_opts = 574, /* reject_opts */ + YYSYMBOL_nat_stmt = 575, /* nat_stmt */ + YYSYMBOL_nat_stmt_alloc = 576, /* nat_stmt_alloc */ + YYSYMBOL_tproxy_stmt = 577, /* tproxy_stmt */ + YYSYMBOL_synproxy_stmt = 578, /* synproxy_stmt */ + YYSYMBOL_synproxy_stmt_alloc = 579, /* synproxy_stmt_alloc */ + YYSYMBOL_synproxy_args = 580, /* synproxy_args */ + YYSYMBOL_synproxy_arg = 581, /* synproxy_arg */ + YYSYMBOL_synproxy_config = 582, /* synproxy_config */ + YYSYMBOL_synproxy_obj = 583, /* synproxy_obj */ + YYSYMBOL_synproxy_ts = 584, /* synproxy_ts */ + YYSYMBOL_synproxy_sack = 585, /* synproxy_sack */ + YYSYMBOL_primary_stmt_expr = 586, /* primary_stmt_expr */ + YYSYMBOL_shift_stmt_expr = 587, /* shift_stmt_expr */ + YYSYMBOL_and_stmt_expr = 588, /* and_stmt_expr */ + YYSYMBOL_exclusive_or_stmt_expr = 589, /* exclusive_or_stmt_expr */ + YYSYMBOL_inclusive_or_stmt_expr = 590, /* inclusive_or_stmt_expr */ + YYSYMBOL_basic_stmt_expr = 591, /* basic_stmt_expr */ + YYSYMBOL_concat_stmt_expr = 592, /* concat_stmt_expr */ + YYSYMBOL_map_stmt_expr_set = 593, /* map_stmt_expr_set */ + YYSYMBOL_map_stmt_expr = 594, /* map_stmt_expr */ + YYSYMBOL_prefix_stmt_expr = 595, /* prefix_stmt_expr */ + YYSYMBOL_range_stmt_expr = 596, /* range_stmt_expr */ + YYSYMBOL_multiton_stmt_expr = 597, /* multiton_stmt_expr */ + YYSYMBOL_stmt_expr = 598, /* stmt_expr */ + YYSYMBOL_nat_stmt_args = 599, /* nat_stmt_args */ + YYSYMBOL_masq_stmt = 600, /* masq_stmt */ + YYSYMBOL_masq_stmt_alloc = 601, /* masq_stmt_alloc */ + YYSYMBOL_masq_stmt_args = 602, /* masq_stmt_args */ + YYSYMBOL_redir_stmt = 603, /* redir_stmt */ + YYSYMBOL_redir_stmt_alloc = 604, /* redir_stmt_alloc */ + YYSYMBOL_redir_stmt_arg = 605, /* redir_stmt_arg */ + YYSYMBOL_dup_stmt = 606, /* dup_stmt */ + YYSYMBOL_fwd_stmt = 607, /* fwd_stmt */ + YYSYMBOL_nf_nat_flags = 608, /* nf_nat_flags */ + YYSYMBOL_nf_nat_flag = 609, /* nf_nat_flag */ + YYSYMBOL_queue_stmt = 610, /* queue_stmt */ + YYSYMBOL_queue_stmt_compat = 611, /* queue_stmt_compat */ + YYSYMBOL_queue_stmt_alloc = 612, /* queue_stmt_alloc */ + YYSYMBOL_queue_stmt_args = 613, /* queue_stmt_args */ + YYSYMBOL_queue_stmt_arg = 614, /* queue_stmt_arg */ + YYSYMBOL_queue_expr = 615, /* queue_expr */ + YYSYMBOL_queue_stmt_expr_simple = 616, /* queue_stmt_expr_simple */ + YYSYMBOL_queue_stmt_expr = 617, /* queue_stmt_expr */ + YYSYMBOL_queue_stmt_flags = 618, /* queue_stmt_flags */ + YYSYMBOL_queue_stmt_flag = 619, /* queue_stmt_flag */ + YYSYMBOL_set_elem_expr_stmt = 620, /* set_elem_expr_stmt */ + YYSYMBOL_set_elem_expr_stmt_alloc = 621, /* set_elem_expr_stmt_alloc */ + YYSYMBOL_set_stmt = 622, /* set_stmt */ + YYSYMBOL_set_stmt_op = 623, /* set_stmt_op */ + YYSYMBOL_map_stmt = 624, /* map_stmt */ + YYSYMBOL_meter_stmt = 625, /* meter_stmt */ + YYSYMBOL_flow_stmt_legacy_alloc = 626, /* flow_stmt_legacy_alloc */ + YYSYMBOL_flow_stmt_opts = 627, /* flow_stmt_opts */ + YYSYMBOL_flow_stmt_opt = 628, /* flow_stmt_opt */ + YYSYMBOL_meter_stmt_alloc = 629, /* meter_stmt_alloc */ + YYSYMBOL_match_stmt = 630, /* match_stmt */ + YYSYMBOL_variable_expr = 631, /* variable_expr */ + YYSYMBOL_symbol_expr = 632, /* symbol_expr */ + YYSYMBOL_set_ref_expr = 633, /* set_ref_expr */ + YYSYMBOL_set_ref_symbol_expr = 634, /* set_ref_symbol_expr */ + YYSYMBOL_integer_expr = 635, /* integer_expr */ + YYSYMBOL_primary_expr = 636, /* primary_expr */ + YYSYMBOL_fib_expr = 637, /* fib_expr */ + YYSYMBOL_fib_result = 638, /* fib_result */ + YYSYMBOL_fib_flag = 639, /* fib_flag */ + YYSYMBOL_fib_tuple = 640, /* fib_tuple */ + YYSYMBOL_osf_expr = 641, /* osf_expr */ + YYSYMBOL_osf_ttl = 642, /* osf_ttl */ + YYSYMBOL_shift_expr = 643, /* shift_expr */ + YYSYMBOL_and_expr = 644, /* and_expr */ + YYSYMBOL_exclusive_or_expr = 645, /* exclusive_or_expr */ + YYSYMBOL_inclusive_or_expr = 646, /* inclusive_or_expr */ + YYSYMBOL_basic_expr = 647, /* basic_expr */ + YYSYMBOL_concat_expr = 648, /* concat_expr */ + YYSYMBOL_prefix_rhs_expr = 649, /* prefix_rhs_expr */ + YYSYMBOL_range_rhs_expr = 650, /* range_rhs_expr */ + YYSYMBOL_multiton_rhs_expr = 651, /* multiton_rhs_expr */ + YYSYMBOL_map_expr = 652, /* map_expr */ + YYSYMBOL_expr = 653, /* expr */ + YYSYMBOL_set_expr = 654, /* set_expr */ + YYSYMBOL_set_list_expr = 655, /* set_list_expr */ + YYSYMBOL_set_list_member_expr = 656, /* set_list_member_expr */ + YYSYMBOL_meter_key_expr = 657, /* meter_key_expr */ + YYSYMBOL_meter_key_expr_alloc = 658, /* meter_key_expr_alloc */ + YYSYMBOL_set_elem_expr = 659, /* set_elem_expr */ + YYSYMBOL_set_elem_key_expr = 660, /* set_elem_key_expr */ + YYSYMBOL_set_elem_expr_alloc = 661, /* set_elem_expr_alloc */ + YYSYMBOL_set_elem_options = 662, /* set_elem_options */ + YYSYMBOL_set_elem_option = 663, /* set_elem_option */ + YYSYMBOL_set_elem_expr_options = 664, /* set_elem_expr_options */ + YYSYMBOL_set_elem_stmt_list = 665, /* set_elem_stmt_list */ + YYSYMBOL_set_elem_stmt = 666, /* set_elem_stmt */ + YYSYMBOL_set_elem_expr_option = 667, /* set_elem_expr_option */ + YYSYMBOL_set_lhs_expr = 668, /* set_lhs_expr */ + YYSYMBOL_set_rhs_expr = 669, /* set_rhs_expr */ + YYSYMBOL_initializer_expr = 670, /* initializer_expr */ + YYSYMBOL_counter_config = 671, /* counter_config */ + YYSYMBOL_counter_obj = 672, /* counter_obj */ + YYSYMBOL_quota_config = 673, /* quota_config */ + YYSYMBOL_quota_obj = 674, /* quota_obj */ + YYSYMBOL_secmark_config = 675, /* secmark_config */ + YYSYMBOL_secmark_obj = 676, /* secmark_obj */ + YYSYMBOL_ct_obj_type = 677, /* ct_obj_type */ + YYSYMBOL_ct_cmd_type = 678, /* ct_cmd_type */ + YYSYMBOL_ct_l4protoname = 679, /* ct_l4protoname */ + YYSYMBOL_ct_helper_config = 680, /* ct_helper_config */ + YYSYMBOL_timeout_states = 681, /* timeout_states */ + YYSYMBOL_timeout_state = 682, /* timeout_state */ + YYSYMBOL_ct_timeout_config = 683, /* ct_timeout_config */ + YYSYMBOL_ct_expect_config = 684, /* ct_expect_config */ + YYSYMBOL_ct_obj_alloc = 685, /* ct_obj_alloc */ + YYSYMBOL_limit_config = 686, /* limit_config */ + YYSYMBOL_limit_obj = 687, /* limit_obj */ + YYSYMBOL_relational_expr = 688, /* relational_expr */ + YYSYMBOL_list_rhs_expr = 689, /* list_rhs_expr */ + YYSYMBOL_rhs_expr = 690, /* rhs_expr */ + YYSYMBOL_shift_rhs_expr = 691, /* shift_rhs_expr */ + YYSYMBOL_and_rhs_expr = 692, /* and_rhs_expr */ + YYSYMBOL_exclusive_or_rhs_expr = 693, /* exclusive_or_rhs_expr */ + YYSYMBOL_inclusive_or_rhs_expr = 694, /* inclusive_or_rhs_expr */ + YYSYMBOL_basic_rhs_expr = 695, /* basic_rhs_expr */ + YYSYMBOL_concat_rhs_expr = 696, /* concat_rhs_expr */ + YYSYMBOL_boolean_keys = 697, /* boolean_keys */ + YYSYMBOL_boolean_expr = 698, /* boolean_expr */ + YYSYMBOL_keyword_expr = 699, /* keyword_expr */ + YYSYMBOL_primary_rhs_expr = 700, /* primary_rhs_expr */ + YYSYMBOL_relational_op = 701, /* relational_op */ + YYSYMBOL_verdict_expr = 702, /* verdict_expr */ + YYSYMBOL_chain_expr = 703, /* chain_expr */ + YYSYMBOL_meta_expr = 704, /* meta_expr */ + YYSYMBOL_meta_key = 705, /* meta_key */ + YYSYMBOL_meta_key_qualified = 706, /* meta_key_qualified */ + YYSYMBOL_meta_key_unqualified = 707, /* meta_key_unqualified */ + YYSYMBOL_meta_stmt = 708, /* meta_stmt */ + YYSYMBOL_socket_expr = 709, /* socket_expr */ + YYSYMBOL_socket_key = 710, /* socket_key */ + YYSYMBOL_offset_opt = 711, /* offset_opt */ + YYSYMBOL_numgen_type = 712, /* numgen_type */ + YYSYMBOL_numgen_expr = 713, /* numgen_expr */ + YYSYMBOL_xfrm_spnum = 714, /* xfrm_spnum */ + YYSYMBOL_xfrm_dir = 715, /* xfrm_dir */ + YYSYMBOL_xfrm_state_key = 716, /* xfrm_state_key */ + YYSYMBOL_xfrm_state_proto_key = 717, /* xfrm_state_proto_key */ + YYSYMBOL_xfrm_expr = 718, /* xfrm_expr */ + YYSYMBOL_hash_expr = 719, /* hash_expr */ + YYSYMBOL_nf_key_proto = 720, /* nf_key_proto */ + YYSYMBOL_rt_expr = 721, /* rt_expr */ + YYSYMBOL_rt_key = 722, /* rt_key */ + YYSYMBOL_ct_expr = 723, /* ct_expr */ + YYSYMBOL_ct_dir = 724, /* ct_dir */ + YYSYMBOL_ct_key = 725, /* ct_key */ + YYSYMBOL_ct_key_dir = 726, /* ct_key_dir */ + YYSYMBOL_ct_key_proto_field = 727, /* ct_key_proto_field */ + YYSYMBOL_ct_key_dir_optional = 728, /* ct_key_dir_optional */ + YYSYMBOL_symbol_stmt_expr = 729, /* symbol_stmt_expr */ + YYSYMBOL_list_stmt_expr = 730, /* list_stmt_expr */ + YYSYMBOL_ct_stmt = 731, /* ct_stmt */ + YYSYMBOL_payload_stmt = 732, /* payload_stmt */ + YYSYMBOL_payload_expr = 733, /* payload_expr */ + YYSYMBOL_payload_raw_expr = 734, /* payload_raw_expr */ + YYSYMBOL_payload_base_spec = 735, /* payload_base_spec */ + YYSYMBOL_eth_hdr_expr = 736, /* eth_hdr_expr */ + YYSYMBOL_eth_hdr_field = 737, /* eth_hdr_field */ + YYSYMBOL_vlan_hdr_expr = 738, /* vlan_hdr_expr */ + YYSYMBOL_vlan_hdr_field = 739, /* vlan_hdr_field */ + YYSYMBOL_arp_hdr_expr = 740, /* arp_hdr_expr */ + YYSYMBOL_arp_hdr_field = 741, /* arp_hdr_field */ + YYSYMBOL_ip_hdr_expr = 742, /* ip_hdr_expr */ + YYSYMBOL_ip_hdr_field = 743, /* ip_hdr_field */ + YYSYMBOL_ip_option_type = 744, /* ip_option_type */ + YYSYMBOL_ip_option_field = 745, /* ip_option_field */ + YYSYMBOL_icmp_hdr_expr = 746, /* icmp_hdr_expr */ + YYSYMBOL_icmp_hdr_field = 747, /* icmp_hdr_field */ + YYSYMBOL_igmp_hdr_expr = 748, /* igmp_hdr_expr */ + YYSYMBOL_igmp_hdr_field = 749, /* igmp_hdr_field */ + YYSYMBOL_ip6_hdr_expr = 750, /* ip6_hdr_expr */ + YYSYMBOL_ip6_hdr_field = 751, /* ip6_hdr_field */ + YYSYMBOL_icmp6_hdr_expr = 752, /* icmp6_hdr_expr */ + YYSYMBOL_icmp6_hdr_field = 753, /* icmp6_hdr_field */ + YYSYMBOL_auth_hdr_expr = 754, /* auth_hdr_expr */ + YYSYMBOL_auth_hdr_field = 755, /* auth_hdr_field */ + YYSYMBOL_esp_hdr_expr = 756, /* esp_hdr_expr */ + YYSYMBOL_esp_hdr_field = 757, /* esp_hdr_field */ + YYSYMBOL_comp_hdr_expr = 758, /* comp_hdr_expr */ + YYSYMBOL_comp_hdr_field = 759, /* comp_hdr_field */ + YYSYMBOL_udp_hdr_expr = 760, /* udp_hdr_expr */ + YYSYMBOL_udp_hdr_field = 761, /* udp_hdr_field */ + YYSYMBOL_udplite_hdr_expr = 762, /* udplite_hdr_expr */ + YYSYMBOL_udplite_hdr_field = 763, /* udplite_hdr_field */ + YYSYMBOL_tcp_hdr_expr = 764, /* tcp_hdr_expr */ + YYSYMBOL_inner_inet_expr = 765, /* inner_inet_expr */ + YYSYMBOL_inner_eth_expr = 766, /* inner_eth_expr */ + YYSYMBOL_inner_expr = 767, /* inner_expr */ + YYSYMBOL_vxlan_hdr_expr = 768, /* vxlan_hdr_expr */ + YYSYMBOL_vxlan_hdr_field = 769, /* vxlan_hdr_field */ + YYSYMBOL_geneve_hdr_expr = 770, /* geneve_hdr_expr */ + YYSYMBOL_geneve_hdr_field = 771, /* geneve_hdr_field */ + YYSYMBOL_gre_hdr_expr = 772, /* gre_hdr_expr */ + YYSYMBOL_gre_hdr_field = 773, /* gre_hdr_field */ + YYSYMBOL_gretap_hdr_expr = 774, /* gretap_hdr_expr */ + YYSYMBOL_optstrip_stmt = 775, /* optstrip_stmt */ + YYSYMBOL_tcp_hdr_field = 776, /* tcp_hdr_field */ + YYSYMBOL_tcp_hdr_option_kind_and_field = 777, /* tcp_hdr_option_kind_and_field */ + YYSYMBOL_tcp_hdr_option_sack = 778, /* tcp_hdr_option_sack */ + YYSYMBOL_tcp_hdr_option_type = 779, /* tcp_hdr_option_type */ + YYSYMBOL_tcpopt_field_sack = 780, /* tcpopt_field_sack */ + YYSYMBOL_tcpopt_field_window = 781, /* tcpopt_field_window */ + YYSYMBOL_tcpopt_field_tsopt = 782, /* tcpopt_field_tsopt */ + YYSYMBOL_tcpopt_field_maxseg = 783, /* tcpopt_field_maxseg */ + YYSYMBOL_tcpopt_field_mptcp = 784, /* tcpopt_field_mptcp */ + YYSYMBOL_dccp_hdr_expr = 785, /* dccp_hdr_expr */ + YYSYMBOL_dccp_hdr_field = 786, /* dccp_hdr_field */ + YYSYMBOL_sctp_chunk_type = 787, /* sctp_chunk_type */ + YYSYMBOL_sctp_chunk_common_field = 788, /* sctp_chunk_common_field */ + YYSYMBOL_sctp_chunk_data_field = 789, /* sctp_chunk_data_field */ + YYSYMBOL_sctp_chunk_init_field = 790, /* sctp_chunk_init_field */ + YYSYMBOL_sctp_chunk_sack_field = 791, /* sctp_chunk_sack_field */ + YYSYMBOL_sctp_chunk_alloc = 792, /* sctp_chunk_alloc */ + YYSYMBOL_sctp_hdr_expr = 793, /* sctp_hdr_expr */ + YYSYMBOL_sctp_hdr_field = 794, /* sctp_hdr_field */ + YYSYMBOL_th_hdr_expr = 795, /* th_hdr_expr */ + YYSYMBOL_th_hdr_field = 796, /* th_hdr_field */ + YYSYMBOL_exthdr_expr = 797, /* exthdr_expr */ + YYSYMBOL_hbh_hdr_expr = 798, /* hbh_hdr_expr */ + YYSYMBOL_hbh_hdr_field = 799, /* hbh_hdr_field */ + YYSYMBOL_rt_hdr_expr = 800, /* rt_hdr_expr */ + YYSYMBOL_rt_hdr_field = 801, /* rt_hdr_field */ + YYSYMBOL_rt0_hdr_expr = 802, /* rt0_hdr_expr */ + YYSYMBOL_rt0_hdr_field = 803, /* rt0_hdr_field */ + YYSYMBOL_rt2_hdr_expr = 804, /* rt2_hdr_expr */ + YYSYMBOL_rt2_hdr_field = 805, /* rt2_hdr_field */ + YYSYMBOL_rt4_hdr_expr = 806, /* rt4_hdr_expr */ + YYSYMBOL_rt4_hdr_field = 807, /* rt4_hdr_field */ + YYSYMBOL_frag_hdr_expr = 808, /* frag_hdr_expr */ + YYSYMBOL_frag_hdr_field = 809, /* frag_hdr_field */ + YYSYMBOL_dst_hdr_expr = 810, /* dst_hdr_expr */ + YYSYMBOL_dst_hdr_field = 811, /* dst_hdr_field */ + YYSYMBOL_mh_hdr_expr = 812, /* mh_hdr_expr */ + YYSYMBOL_mh_hdr_field = 813, /* mh_hdr_field */ + YYSYMBOL_exthdr_exists_expr = 814, /* exthdr_exists_expr */ + YYSYMBOL_exthdr_key = 815 /* exthdr_key */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + <limits.h> and (if available) <stdint.h> are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include <limits.h> /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stdint.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>. */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include <stddef.h> /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int16 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include <libintl.h> /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +#if defined __GNUC__ && ! defined __ICC && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if 1 + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include <alloca.h> /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include <malloc.h> /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (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 EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* 1 */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (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 +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; + YYLTYPE yyls_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE) \ + + YYSIZEOF (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* 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_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 2 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 8857 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 372 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 444 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 1368 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 2353 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 617 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int16 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 369, 2, 2, 2, + 366, 367, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 363, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 370, 2, 371, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 364, 368, 365, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, + 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, + 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, + 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362 +}; + +#if YYDEBUG + /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_int16 yyrline[] = +{ + 0, 978, 978, 979, 988, 989, 992, 993, 996, 997, + 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, + 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, + 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, + 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, + 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1048, + 1049, 1050, 1052, 1060, 1075, 1082, 1094, 1102, 1103, 1104, + 1105, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, + 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1143, 1147, 1154, + 1158, 1166, 1170, 1174, 1181, 1188, 1192, 1199, 1208, 1212, + 1216, 1220, 1224, 1228, 1232, 1236, 1240, 1244, 1248, 1252, + 1256, 1262, 1268, 1272, 1279, 1283, 1291, 1298, 1305, 1309, + 1316, 1325, 1329, 1333, 1337, 1341, 1345, 1349, 1353, 1359, + 1365, 1366, 1369, 1370, 1373, 1374, 1377, 1378, 1381, 1385, + 1389, 1396, 1400, 1404, 1408, 1412, 1416, 1420, 1427, 1431, + 1435, 1441, 1445, 1449, 1455, 1459, 1463, 1467, 1471, 1475, + 1479, 1483, 1487, 1494, 1498, 1502, 1508, 1512, 1516, 1523, + 1529, 1533, 1537, 1541, 1545, 1549, 1553, 1557, 1561, 1565, + 1569, 1573, 1577, 1581, 1585, 1589, 1593, 1597, 1601, 1605, + 1609, 1613, 1617, 1621, 1625, 1629, 1633, 1637, 1641, 1645, + 1649, 1653, 1657, 1661, 1667, 1673, 1677, 1687, 1691, 1695, + 1700, 1704, 1708, 1712, 1717, 1721, 1725, 1729, 1734, 1738, + 1743, 1747, 1751, 1755, 1761, 1765, 1769, 1773, 1777, 1781, + 1785, 1791, 1798, 1804, 1812, 1818, 1826, 1835, 1836, 1839, + 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1849, 1850, 1853, + 1854, 1855, 1858, 1867, 1877, 1892, 1902, 1903, 1904, 1905, + 1906, 1917, 1927, 1938, 1948, 1959, 1970, 1979, 1988, 1997, + 2008, 2019, 2033, 2043, 2044, 2045, 2046, 2047, 2048, 2049, + 2054, 2063, 2073, 2074, 2075, 2082, 2103, 2114, 2125, 2138, + 2143, 2144, 2145, 2146, 2151, 2157, 2162, 2167, 2172, 2178, + 2183, 2188, 2189, 2200, 2201, 2204, 2208, 2211, 2212, 2213, + 2214, 2218, 2223, 2224, 2225, 2226, 2227, 2230, 2231, 2234, + 2235, 2236, 2237, 2242, 2247, 2258, 2269, 2281, 2290, 2295, + 2301, 2306, 2315, 2318, 2322, 2328, 2329, 2333, 2338, 2339, + 2340, 2341, 2355, 2359, 2363, 2369, 2374, 2381, 2386, 2391, + 2394, 2403, 2412, 2419, 2432, 2439, 2440, 2452, 2457, 2458, + 2459, 2460, 2464, 2474, 2475, 2476, 2477, 2481, 2491, 2492, + 2493, 2494, 2498, 2509, 2513, 2514, 2515, 2519, 2529, 2530, + 2531, 2532, 2536, 2546, 2547, 2548, 2549, 2553, 2563, 2564, + 2565, 2566, 2570, 2580, 2581, 2582, 2583, 2587, 2597, 2598, + 2599, 2600, 2601, 2604, 2635, 2642, 2646, 2649, 2659, 2666, + 2677, 2690, 2705, 2706, 2709, 2720, 2726, 2730, 2733, 2739, + 2752, 2757, 2766, 2767, 2770, 2771, 2774, 2775, 2776, 2779, + 2795, 2796, 2799, 2800, 2803, 2804, 2805, 2806, 2807, 2808, + 2811, 2820, 2829, 2837, 2845, 2853, 2861, 2869, 2877, 2885, + 2893, 2901, 2909, 2917, 2925, 2933, 2941, 2949, 2953, 2958, + 2966, 2973, 2980, 2994, 2998, 3005, 3009, 3015, 3027, 3033, + 3040, 3046, 3053, 3054, 3055, 3056, 3057, 3060, 3061, 3062, + 3063, 3064, 3065, 3066, 3067, 3068, 3069, 3070, 3071, 3072, + 3073, 3074, 3075, 3076, 3077, 3078, 3079, 3080, 3081, 3084, + 3095, 3096, 3099, 3108, 3112, 3118, 3124, 3129, 3132, 3137, + 3142, 3145, 3151, 3156, 3164, 3165, 3167, 3171, 3179, 3183, + 3186, 3190, 3196, 3200, 3204, 3212, 3213, 3216, 3222, 3226, + 3229, 3354, 3359, 3364, 3369, 3374, 3380, 3410, 3414, 3418, + 3422, 3426, 3432, 3436, 3439, 3443, 3449, 3463, 3472, 3480, + 3481, 3482, 3485, 3486, 3489, 3490, 3505, 3521, 3529, 3530, + 3531, 3534, 3535, 3538, 3545, 3546, 3549, 3563, 3570, 3571, + 3586, 3587, 3588, 3589, 3590, 3593, 3596, 3602, 3608, 3612, + 3616, 3623, 3630, 3637, 3644, 3650, 3656, 3662, 3665, 3666, + 3669, 3675, 3681, 3687, 3694, 3701, 3709, 3710, 3713, 3717, + 3725, 3729, 3732, 3737, 3742, 3746, 3752, 3768, 3787, 3793, + 3794, 3800, 3801, 3807, 3808, 3809, 3810, 3811, 3812, 3813, + 3814, 3815, 3816, 3817, 3818, 3819, 3822, 3823, 3827, 3833, + 3834, 3840, 3841, 3847, 3848, 3854, 3857, 3858, 3869, 3870, + 3873, 3877, 3880, 3886, 3892, 3893, 3896, 3897, 3898, 3901, + 3905, 3909, 3914, 3919, 3924, 3930, 3934, 3938, 3942, 3948, + 3953, 3957, 3965, 3974, 3975, 3978, 3981, 3985, 3990, 3996, + 3997, 4000, 4003, 4007, 4011, 4015, 4020, 4027, 4032, 4040, + 4045, 4054, 4055, 4061, 4062, 4063, 4066, 4067, 4071, 4075, + 4081, 4082, 4085, 4091, 4095, 4098, 4103, 4109, 4110, 4113, + 4114, 4115, 4121, 4122, 4123, 4124, 4127, 4128, 4134, 4135, + 4138, 4139, 4142, 4148, 4155, 4162, 4173, 4174, 4175, 4178, + 4186, 4198, 4205, 4208, 4214, 4218, 4221, 4227, 4236, 4247, + 4253, 4279, 4280, 4289, 4290, 4293, 4302, 4313, 4314, 4315, + 4316, 4317, 4318, 4319, 4320, 4321, 4322, 4323, 4324, 4325, + 4326, 4327, 4330, 4353, 4354, 4355, 4358, 4359, 4360, 4361, + 4362, 4365, 4369, 4372, 4376, 4383, 4386, 4402, 4403, 4407, + 4413, 4414, 4420, 4421, 4427, 4428, 4434, 4437, 4438, 4449, + 4455, 4461, 4462, 4465, 4471, 4472, 4473, 4476, 4483, 4488, + 4493, 4496, 4500, 4504, 4510, 4511, 4518, 4524, 4525, 4528, + 4529, 4532, 4538, 4544, 4548, 4551, 4555, 4559, 4569, 4573, + 4576, 4582, 4589, 4593, 4599, 4613, 4627, 4632, 4638, 4654, + 4658, 4666, 4670, 4674, 4684, 4687, 4688, 4691, 4692, 4693, + 4694, 4705, 4716, 4722, 4743, 4749, 4766, 4772, 4773, 4774, + 4777, 4778, 4779, 4782, 4783, 4786, 4802, 4808, 4814, 4821, + 4834, 4842, 4850, 4856, 4860, 4864, 4868, 4872, 4879, 4884, + 4895, 4909, 4915, 4919, 4923, 4927, 4931, 4935, 4939, 4943, + 4949, 4955, 4963, 4964, 4965, 4968, 4969, 4973, 4979, 4980, + 4986, 4987, 4993, 4994, 5000, 5003, 5004, 5005, 5014, 5025, + 5026, 5029, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5044, + 5045, 5046, 5047, 5048, 5049, 5050, 5053, 5054, 5055, 5056, + 5057, 5064, 5071, 5078, 5085, 5092, 5099, 5106, 5113, 5120, + 5127, 5134, 5141, 5148, 5151, 5152, 5153, 5154, 5155, 5156, + 5157, 5160, 5164, 5168, 5172, 5176, 5180, 5186, 5187, 5197, + 5201, 5205, 5221, 5222, 5225, 5226, 5227, 5228, 5229, 5232, + 5233, 5234, 5235, 5236, 5237, 5238, 5239, 5240, 5241, 5242, + 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, 5251, 5252, + 5253, 5254, 5255, 5258, 5278, 5282, 5296, 5300, 5304, 5310, + 5314, 5320, 5321, 5322, 5325, 5326, 5329, 5330, 5333, 5339, + 5340, 5343, 5344, 5347, 5348, 5351, 5352, 5355, 5363, 5390, + 5395, 5400, 5406, 5407, 5410, 5414, 5434, 5435, 5436, 5437, + 5440, 5444, 5448, 5454, 5455, 5458, 5459, 5460, 5461, 5462, + 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, 5471, 5472, + 5473, 5474, 5477, 5478, 5479, 5480, 5481, 5482, 5483, 5486, + 5487, 5488, 5489, 5492, 5493, 5494, 5495, 5498, 5499, 5502, + 5508, 5516, 5529, 5536, 5542, 5548, 5557, 5558, 5559, 5560, + 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5570, + 5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5581, 5590, + 5591, 5592, 5593, 5606, 5612, 5613, 5614, 5617, 5623, 5624, + 5625, 5626, 5627, 5630, 5636, 5637, 5638, 5639, 5640, 5641, + 5642, 5643, 5644, 5647, 5651, 5659, 5666, 5667, 5668, 5669, + 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5680, 5681, + 5682, 5683, 5686, 5687, 5688, 5689, 5690, 5693, 5699, 5700, + 5701, 5702, 5703, 5704, 5705, 5708, 5714, 5715, 5716, 5717, + 5720, 5726, 5727, 5728, 5729, 5730, 5731, 5732, 5733, 5734, + 5736, 5742, 5743, 5744, 5745, 5746, 5747, 5748, 5749, 5750, + 5751, 5754, 5760, 5761, 5762, 5763, 5764, 5767, 5773, 5774, + 5777, 5783, 5784, 5785, 5788, 5794, 5795, 5796, 5797, 5800, + 5806, 5807, 5808, 5809, 5812, 5816, 5821, 5825, 5832, 5833, + 5834, 5835, 5836, 5837, 5838, 5839, 5840, 5841, 5842, 5843, + 5844, 5845, 5848, 5849, 5850, 5853, 5854, 5857, 5865, 5873, + 5874, 5877, 5885, 5893, 5894, 5897, 5901, 5908, 5909, 5910, + 5913, 5920, 5927, 5928, 5929, 5930, 5931, 5932, 5933, 5934, + 5935, 5936, 5939, 5944, 5949, 5954, 5959, 5964, 5971, 5972, + 5973, 5974, 5975, 5978, 5979, 5980, 5981, 5982, 5983, 5984, + 5985, 5986, 5987, 5988, 5989, 5998, 5999, 6002, 6005, 6006, + 6009, 6012, 6015, 6019, 6030, 6031, 6032, 6035, 6036, 6037, + 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, + 6048, 6049, 6050, 6051, 6052, 6055, 6056, 6057, 6060, 6061, + 6062, 6063, 6066, 6067, 6068, 6069, 6070, 6073, 6074, 6075, + 6076, 6079, 6084, 6088, 6092, 6096, 6100, 6104, 6109, 6114, + 6119, 6124, 6129, 6136, 6140, 6146, 6147, 6148, 6149, 6152, + 6160, 6161, 6164, 6165, 6166, 6167, 6168, 6169, 6170, 6171, + 6174, 6180, 6181, 6184, 6190, 6191, 6192, 6193, 6196, 6202, + 6208, 6214, 6217, 6223, 6224, 6225, 6226, 6232, 6238, 6239, + 6240, 6241, 6242, 6243, 6246, 6252, 6253, 6256, 6262, 6263, + 6264, 6265, 6266, 6269, 6283, 6284, 6285, 6286, 6287 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if 1 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "\"junk\"", + "\"newline\"", "\"colon\"", "\"semicolon\"", "\"comma\"", "\".\"", + "\"==\"", "\"!=\"", "\"<\"", "\">\"", "\">=\"", "\"<=\"", "\"<<\"", + "\">>\"", "\"&\"", "\"^\"", "\"!\"", "\"/\"", "\"*\"", "\"-\"", "\"@\"", + "\"vmap\"", "\"+\"", "\"include\"", "\"define\"", "\"redefine\"", + "\"undefine\"", "\"fib\"", "\"socket\"", "\"transparent\"", + "\"wildcard\"", "\"cgroupv2\"", "\"tproxy\"", "\"osf\"", "\"synproxy\"", + "\"mss\"", "\"wscale\"", "\"typeof\"", "\"hook\"", "\"hooks\"", + "\"device\"", "\"devices\"", "\"table\"", "\"tables\"", "\"chain\"", + "\"chains\"", "\"rule\"", "\"rules\"", "\"sets\"", "\"set\"", + "\"element\"", "\"map\"", "\"maps\"", "\"flowtable\"", "\"handle\"", + "\"ruleset\"", "\"trace\"", "\"inet\"", "\"netdev\"", "\"add\"", + "\"update\"", "\"replace\"", "\"create\"", "\"insert\"", "\"delete\"", + "\"get\"", "\"list\"", "\"reset\"", "\"flush\"", "\"rename\"", + "\"describe\"", "\"import\"", "\"export\"", "\"destroy\"", "\"monitor\"", + "\"all\"", "\"accept\"", "\"drop\"", "\"continue\"", "\"jump\"", + "\"goto\"", "\"return\"", "\"to\"", "\"constant\"", "\"interval\"", + "\"dynamic\"", "\"auto-merge\"", "\"timeout\"", "\"gc-interval\"", + "\"elements\"", "\"expires\"", "\"policy\"", "\"memory\"", + "\"performance\"", "\"size\"", "\"flow\"", "\"offload\"", "\"meter\"", + "\"meters\"", "\"flowtables\"", "\"number\"", "\"string\"", + "\"quoted string\"", "\"string with a trailing asterisk\"", "\"ll\"", + "\"nh\"", "\"th\"", "\"bridge\"", "\"ether\"", "\"saddr\"", "\"daddr\"", + "\"type\"", "\"vlan\"", "\"id\"", "\"cfi\"", "\"dei\"", "\"pcp\"", + "\"arp\"", "\"htype\"", "\"ptype\"", "\"hlen\"", "\"plen\"", + "\"operation\"", "\"ip\"", "\"version\"", "\"hdrlength\"", "\"dscp\"", + "\"ecn\"", "\"length\"", "\"frag-off\"", "\"ttl\"", "\"protocol\"", + "\"checksum\"", "\"ptr\"", "\"value\"", "\"lsrr\"", "\"rr\"", "\"ssrr\"", + "\"ra\"", "\"icmp\"", "\"code\"", "\"seq\"", "\"gateway\"", "\"mtu\"", + "\"igmp\"", "\"mrt\"", "\"options\"", "\"ip6\"", "\"priority\"", + "\"flowlabel\"", "\"nexthdr\"", "\"hoplimit\"", "\"icmpv6\"", + "\"param-problem\"", "\"max-delay\"", "\"taddr\"", "\"ah\"", + "\"reserved\"", "\"spi\"", "\"esp\"", "\"comp\"", "\"flags\"", "\"cpi\"", + "\"port\"", "\"udp\"", "\"sport\"", "\"dport\"", "\"udplite\"", + "\"csumcov\"", "\"tcp\"", "\"ackseq\"", "\"doff\"", "\"window\"", + "\"urgptr\"", "\"option\"", "\"echo\"", "\"eol\"", "\"mptcp\"", + "\"nop\"", "\"sack\"", "\"sack0\"", "\"sack1\"", "\"sack2\"", + "\"sack3\"", "\"sack-permitted\"", "\"fastopen\"", "\"md5sig\"", + "\"timestamp\"", "\"count\"", "\"left\"", "\"right\"", "\"tsval\"", + "\"tsecr\"", "\"subtype\"", "\"dccp\"", "\"vxlan\"", "\"vni\"", + "\"gre\"", "\"gretap\"", "\"geneve\"", "\"sctp\"", "\"chunk\"", + "\"data\"", "\"init\"", "\"init-ack\"", "\"heartbeat\"", + "\"heartbeat-ack\"", "\"abort\"", "\"shutdown\"", "\"shutdown-ack\"", + "\"error\"", "\"cookie-echo\"", "\"cookie-ack\"", "\"ecne\"", "\"cwr\"", + "\"shutdown-complete\"", "\"asconf-ack\"", "\"forward-tsn\"", + "\"asconf\"", "\"tsn\"", "\"stream\"", "\"ssn\"", "\"ppid\"", + "\"init-tag\"", "\"a-rwnd\"", "\"num-outbound-streams\"", + "\"num-inbound-streams\"", "\"initial-tsn\"", "\"cum-tsn-ack\"", + "\"num-gap-ack-blocks\"", "\"num-dup-tsns\"", "\"lowest-tsn\"", + "\"seqno\"", "\"new-cum-tsn\"", "\"vtag\"", "\"rt\"", "\"rt0\"", + "\"rt2\"", "\"srh\"", "\"seg-left\"", "\"addr\"", "\"last-entry\"", + "\"tag\"", "\"sid\"", "\"hbh\"", "\"frag\"", "\"reserved2\"", + "\"more-fragments\"", "\"dst\"", "\"mh\"", "\"meta\"", "\"mark\"", + "\"iif\"", "\"iifname\"", "\"iiftype\"", "\"oif\"", "\"oifname\"", + "\"oiftype\"", "\"skuid\"", "\"skgid\"", "\"nftrace\"", "\"rtclassid\"", + "\"ibriport\"", "\"obriport\"", "\"ibrname\"", "\"obrname\"", + "\"pkttype\"", "\"cpu\"", "\"iifgroup\"", "\"oifgroup\"", "\"cgroup\"", + "\"time\"", "\"classid\"", "\"nexthop\"", "\"ct\"", "\"l3proto\"", + "\"proto-src\"", "\"proto-dst\"", "\"zone\"", "\"direction\"", + "\"event\"", "\"expectation\"", "\"expiration\"", "\"helper\"", + "\"label\"", "\"state\"", "\"status\"", "\"original\"", "\"reply\"", + "\"counter\"", "\"name\"", "\"packets\"", "\"bytes\"", "\"avgpkt\"", + "\"last\"", "\"never\"", "\"counters\"", "\"quotas\"", "\"limits\"", + "\"synproxys\"", "\"helpers\"", "\"log\"", "\"prefix\"", "\"group\"", + "\"snaplen\"", "\"queue-threshold\"", "\"level\"", "\"limit\"", + "\"rate\"", "\"burst\"", "\"over\"", "\"until\"", "\"quota\"", + "\"used\"", "\"secmark\"", "\"secmarks\"", "\"second\"", "\"minute\"", + "\"hour\"", "\"day\"", "\"week\"", "\"reject\"", "\"with\"", "\"icmpx\"", + "\"snat\"", "\"dnat\"", "\"masquerade\"", "\"redirect\"", "\"random\"", + "\"fully-random\"", "\"persistent\"", "\"queue\"", "\"num\"", + "\"bypass\"", "\"fanout\"", "\"dup\"", "\"fwd\"", "\"numgen\"", + "\"inc\"", "\"mod\"", "\"offset\"", "\"jhash\"", "\"symhash\"", + "\"seed\"", "\"position\"", "\"index\"", "\"comment\"", "\"xml\"", + "\"json\"", "\"vm\"", "\"notrack\"", "\"exists\"", "\"missing\"", + "\"exthdr\"", "\"ipsec\"", "\"reqid\"", "\"spnum\"", "\"in\"", "\"out\"", + "\"xt\"", "'='", "'{'", "'}'", "'('", "')'", "'|'", "'$'", "'['", "']'", + "$accept", "input", "stmt_separator", "opt_newline", "close_scope_ah", + "close_scope_arp", "close_scope_at", "close_scope_comp", + "close_scope_ct", "close_scope_counter", "close_scope_last", + "close_scope_dccp", "close_scope_destroy", "close_scope_dst", + "close_scope_dup", "close_scope_esp", "close_scope_eth", + "close_scope_export", "close_scope_fib", "close_scope_frag", + "close_scope_fwd", "close_scope_gre", "close_scope_hash", + "close_scope_hbh", "close_scope_ip", "close_scope_ip6", + "close_scope_vlan", "close_scope_icmp", "close_scope_igmp", + "close_scope_import", "close_scope_ipsec", "close_scope_list", + "close_scope_limit", "close_scope_meta", "close_scope_mh", + "close_scope_monitor", "close_scope_nat", "close_scope_numgen", + "close_scope_osf", "close_scope_policy", "close_scope_quota", + "close_scope_queue", "close_scope_reject", "close_scope_reset", + "close_scope_rt", "close_scope_sctp", "close_scope_sctp_chunk", + "close_scope_secmark", "close_scope_socket", "close_scope_tcp", + "close_scope_tproxy", "close_scope_type", "close_scope_th", + "close_scope_udp", "close_scope_udplite", "close_scope_log", + "close_scope_synproxy", "close_scope_xt", "common_block", "line", + "base_cmd", "add_cmd", "replace_cmd", "create_cmd", "insert_cmd", + "table_or_id_spec", "chain_or_id_spec", "set_or_id_spec", + "obj_or_id_spec", "delete_cmd", "destroy_cmd", "get_cmd", "list_cmd", + "basehook_device_name", "basehook_spec", "reset_cmd", "flush_cmd", + "rename_cmd", "import_cmd", "export_cmd", "monitor_cmd", "monitor_event", + "monitor_object", "monitor_format", "markup_format", "describe_cmd", + "table_block_alloc", "table_options", "table_block", "chain_block_alloc", + "chain_block", "subchain_block", "typeof_data_expr", "typeof_expr", + "set_block_alloc", "set_block", "set_block_expr", "set_flag_list", + "set_flag", "map_block_alloc", "map_block_obj_type", + "map_block_data_interval", "map_block", "set_mechanism", + "set_policy_spec", "flowtable_block_alloc", "flowtable_block", + "flowtable_expr", "flowtable_list_expr", "flowtable_expr_member", + "data_type_atom_expr", "data_type_expr", "obj_block_alloc", + "counter_block", "quota_block", "ct_helper_block", "ct_timeout_block", + "ct_expect_block", "limit_block", "secmark_block", "synproxy_block", + "type_identifier", "hook_spec", "prio_spec", "extended_prio_name", + "extended_prio_spec", "int_num", "dev_spec", "flags_spec", "policy_spec", + "policy_expr", "chain_policy", "identifier", "string", "time_spec", + "time_spec_or_num_s", "family_spec", "family_spec_explicit", + "table_spec", "tableid_spec", "chain_spec", "chainid_spec", + "chain_identifier", "set_spec", "setid_spec", "set_identifier", + "flowtable_spec", "flowtableid_spec", "flowtable_identifier", "obj_spec", + "objid_spec", "obj_identifier", "handle_spec", "position_spec", + "index_spec", "rule_position", "ruleid_spec", "comment_spec", + "ruleset_spec", "rule", "rule_alloc", "stmt_list", "stateful_stmt_list", + "stateful_stmt", "stmt", "xt_stmt", "chain_stmt_type", "chain_stmt", + "verdict_stmt", "verdict_map_stmt", "verdict_map_expr", + "verdict_map_list_expr", "verdict_map_list_member_expr", + "connlimit_stmt", "counter_stmt", "counter_stmt_alloc", "counter_args", + "counter_arg", "last_stmt", "log_stmt", "log_stmt_alloc", "log_args", + "log_arg", "level_type", "log_flags", "log_flags_tcp", "log_flag_tcp", + "limit_stmt", "quota_mode", "quota_unit", "quota_used", "quota_stmt", + "limit_mode", "limit_burst_pkts", "limit_rate_pkts", "limit_burst_bytes", + "limit_rate_bytes", "limit_bytes", "time_unit", "reject_stmt", + "reject_stmt_alloc", "reject_with_expr", "reject_opts", "nat_stmt", + "nat_stmt_alloc", "tproxy_stmt", "synproxy_stmt", "synproxy_stmt_alloc", + "synproxy_args", "synproxy_arg", "synproxy_config", "synproxy_obj", + "synproxy_ts", "synproxy_sack", "primary_stmt_expr", "shift_stmt_expr", + "and_stmt_expr", "exclusive_or_stmt_expr", "inclusive_or_stmt_expr", + "basic_stmt_expr", "concat_stmt_expr", "map_stmt_expr_set", + "map_stmt_expr", "prefix_stmt_expr", "range_stmt_expr", + "multiton_stmt_expr", "stmt_expr", "nat_stmt_args", "masq_stmt", + "masq_stmt_alloc", "masq_stmt_args", "redir_stmt", "redir_stmt_alloc", + "redir_stmt_arg", "dup_stmt", "fwd_stmt", "nf_nat_flags", "nf_nat_flag", + "queue_stmt", "queue_stmt_compat", "queue_stmt_alloc", "queue_stmt_args", + "queue_stmt_arg", "queue_expr", "queue_stmt_expr_simple", + "queue_stmt_expr", "queue_stmt_flags", "queue_stmt_flag", + "set_elem_expr_stmt", "set_elem_expr_stmt_alloc", "set_stmt", + "set_stmt_op", "map_stmt", "meter_stmt", "flow_stmt_legacy_alloc", + "flow_stmt_opts", "flow_stmt_opt", "meter_stmt_alloc", "match_stmt", + "variable_expr", "symbol_expr", "set_ref_expr", "set_ref_symbol_expr", + "integer_expr", "primary_expr", "fib_expr", "fib_result", "fib_flag", + "fib_tuple", "osf_expr", "osf_ttl", "shift_expr", "and_expr", + "exclusive_or_expr", "inclusive_or_expr", "basic_expr", "concat_expr", + "prefix_rhs_expr", "range_rhs_expr", "multiton_rhs_expr", "map_expr", + "expr", "set_expr", "set_list_expr", "set_list_member_expr", + "meter_key_expr", "meter_key_expr_alloc", "set_elem_expr", + "set_elem_key_expr", "set_elem_expr_alloc", "set_elem_options", + "set_elem_option", "set_elem_expr_options", "set_elem_stmt_list", + "set_elem_stmt", "set_elem_expr_option", "set_lhs_expr", "set_rhs_expr", + "initializer_expr", "counter_config", "counter_obj", "quota_config", + "quota_obj", "secmark_config", "secmark_obj", "ct_obj_type", + "ct_cmd_type", "ct_l4protoname", "ct_helper_config", "timeout_states", + "timeout_state", "ct_timeout_config", "ct_expect_config", "ct_obj_alloc", + "limit_config", "limit_obj", "relational_expr", "list_rhs_expr", + "rhs_expr", "shift_rhs_expr", "and_rhs_expr", "exclusive_or_rhs_expr", + "inclusive_or_rhs_expr", "basic_rhs_expr", "concat_rhs_expr", + "boolean_keys", "boolean_expr", "keyword_expr", "primary_rhs_expr", + "relational_op", "verdict_expr", "chain_expr", "meta_expr", "meta_key", + "meta_key_qualified", "meta_key_unqualified", "meta_stmt", "socket_expr", + "socket_key", "offset_opt", "numgen_type", "numgen_expr", "xfrm_spnum", + "xfrm_dir", "xfrm_state_key", "xfrm_state_proto_key", "xfrm_expr", + "hash_expr", "nf_key_proto", "rt_expr", "rt_key", "ct_expr", "ct_dir", + "ct_key", "ct_key_dir", "ct_key_proto_field", "ct_key_dir_optional", + "symbol_stmt_expr", "list_stmt_expr", "ct_stmt", "payload_stmt", + "payload_expr", "payload_raw_expr", "payload_base_spec", "eth_hdr_expr", + "eth_hdr_field", "vlan_hdr_expr", "vlan_hdr_field", "arp_hdr_expr", + "arp_hdr_field", "ip_hdr_expr", "ip_hdr_field", "ip_option_type", + "ip_option_field", "icmp_hdr_expr", "icmp_hdr_field", "igmp_hdr_expr", + "igmp_hdr_field", "ip6_hdr_expr", "ip6_hdr_field", "icmp6_hdr_expr", + "icmp6_hdr_field", "auth_hdr_expr", "auth_hdr_field", "esp_hdr_expr", + "esp_hdr_field", "comp_hdr_expr", "comp_hdr_field", "udp_hdr_expr", + "udp_hdr_field", "udplite_hdr_expr", "udplite_hdr_field", "tcp_hdr_expr", + "inner_inet_expr", "inner_eth_expr", "inner_expr", "vxlan_hdr_expr", + "vxlan_hdr_field", "geneve_hdr_expr", "geneve_hdr_field", "gre_hdr_expr", + "gre_hdr_field", "gretap_hdr_expr", "optstrip_stmt", "tcp_hdr_field", + "tcp_hdr_option_kind_and_field", "tcp_hdr_option_sack", + "tcp_hdr_option_type", "tcpopt_field_sack", "tcpopt_field_window", + "tcpopt_field_tsopt", "tcpopt_field_maxseg", "tcpopt_field_mptcp", + "dccp_hdr_expr", "dccp_hdr_field", "sctp_chunk_type", + "sctp_chunk_common_field", "sctp_chunk_data_field", + "sctp_chunk_init_field", "sctp_chunk_sack_field", "sctp_chunk_alloc", + "sctp_hdr_expr", "sctp_hdr_field", "th_hdr_expr", "th_hdr_field", + "exthdr_expr", "hbh_hdr_expr", "hbh_hdr_field", "rt_hdr_expr", + "rt_hdr_field", "rt0_hdr_expr", "rt0_hdr_field", "rt2_hdr_expr", + "rt2_hdr_field", "rt4_hdr_expr", "rt4_hdr_field", "frag_hdr_expr", + "frag_hdr_field", "dst_hdr_expr", "dst_hdr_field", "mh_hdr_expr", + "mh_hdr_field", "exthdr_exists_expr", "exthdr_key", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#ifdef YYPRINT +/* YYTOKNUM[NUM] -- (External) token number corresponding to the + (internal) symbol number NUM (which must be that of a token). */ +static const yytype_int16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, + 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, + 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, + 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, + 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, + 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, + 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, + 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, + 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, + 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, + 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, + 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, + 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, + 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, + 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, + 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, + 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, + 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, + 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, + 615, 616, 617, 61, 123, 125, 40, 41, 124, 36, + 91, 93 +}; +#endif + +#define YYPACT_NINF (-1837) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-1049) + +#define yytable_value_is_error(Yyn) \ + 0 + + /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int16 yypact[] = +{ + -1837, 8167, -1837, 370, -1837, -1837, 120, 257, 257, 257, + 1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315, -1837, -1837, + 2577, 296, 2378, 319, 1563, 227, 2797, 881, 1594, 348, + 7783, 199, 208, 2365, 325, -1837, -1837, -1837, -1837, 577, + 1315, 1315, 1315, 1315, -1837, -1837, -1837, 1024, -1837, 257, + -1837, 257, 134, 6943, -1837, 370, -1837, -1837, 275, 281, + 370, 257, -1837, 122, 250, 6943, 257, -1837, 323, -1837, + 257, -1837, -1837, 1315, -1837, 1315, 1315, 1315, 1315, 1315, + 1315, 1315, 578, 1315, 1315, 1315, 1315, -1837, 1315, -1837, + 1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315, 612, 1315, + 1315, 1315, 1315, -1837, 1315, -1837, 1315, 1315, 1315, 1315, + 1315, 1315, 1372, 1315, 1315, 1315, 1315, 1315, 532, 1315, + 1315, 1315, 113, 1315, 2325, 2443, 2452, 2534, 1315, 1315, + 1315, 2661, -1837, 1315, 728, 1315, 1315, 1315, 1315, 1228, + 1237, 1315, -1837, 1315, 1315, 1315, 1315, 1315, 493, 1315, + -1837, 1315, -1837, 1559, 632, 785, 528, -1837, -1837, -1837, + -1837, 788, 1034, 1590, 2270, 2686, 1131, 657, 2079, 2810, + 1668, 123, 924, 939, 1151, 3220, 913, 3634, 773, -1837, + 5759, 818, 977, 432, 438, 800, 235, 1149, 295, 1722, + 5013, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 5435, -1837, -1837, 5, 7419, 357, 1272, 606, + 7783, 257, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 1088, -1837, -1837, 367, -1837, -1837, 1088, -1837, -1837, + 1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315, 612, 1315, + 1315, 1315, 1315, -1837, -1837, -1837, 1611, -1837, -1837, -1837, + 1315, 1315, 1315, -44, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 636, 665, 679, -1837, -1837, -1837, 821, 466, 1005, + -1837, -1837, -1837, 655, -1837, -1837, -1837, 127, 127, -1837, + 355, 257, 8446, 3291, 547, 550, -1837, 96, 749, -1837, + -1837, -1837, -1837, -1837, 174, 770, 897, -1837, 768, 893, + -1837, 570, 6943, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 757, -1837, -1837, 919, -1837, -1837, -1837, 596, + -1837, 5327, -1837, -1837, 853, -1837, 241, -1837, 424, -1837, + -1837, -1837, -1837, 1391, -1837, 146, -1837, -1837, 858, -1837, + -1837, -1837, 1095, 911, 923, 582, -1837, 752, -1837, 6457, + -1837, -1837, -1837, 936, -1837, -1837, -1837, 940, -1837, -1837, + 6797, 6797, -1837, -1837, 156, 677, 707, -1837, -1837, 711, + -1837, -1837, -1837, 716, -1837, 721, 963, 6943, -1837, 122, + 250, -1837, 323, -1837, -1837, 1315, 1315, 1315, 682, -1837, + -1837, -1837, 6943, -1837, 274, -1837, -1837, -1837, 383, -1837, + -1837, -1837, 440, 250, -1837, -1837, -1837, 500, -1837, -1837, + 323, -1837, 506, 733, -1837, -1837, -1837, -1837, 1315, -1837, + -1837, -1837, -1837, 323, -1837, -1837, -1837, 1063, -1837, -1837, + -1837, -1837, 1315, -1837, -1837, -1837, -1837, -1837, -1837, 1315, + 1315, -1837, -1837, -1837, 1086, 1090, -1837, 1315, 1093, -1837, + 1315, -1837, 1315, -1837, 1315, -1837, 1315, -1837, -1837, -1837, + -1837, 1315, -1837, -1837, -1837, 1315, 1315, 328, 257, -1837, + -1837, -1837, 323, -1837, -1837, 1315, -1837, -1837, 1315, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1315, -1837, + 257, -1837, -1837, -1837, -1837, 1145, -1837, -1837, -1837, -1837, + -1837, 1171, 656, -1837, -1837, 875, -1837, -1837, 1087, 49, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 673, 698, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1312, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 2809, -1837, -1837, -1837, -1837, 1092, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 3029, -1837, 5968, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 3975, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 554, -1837, + -1837, 840, -1837, -1837, -1837, -1837, -1837, -1837, 850, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 2175, -1837, -1837, -1837, -1837, 908, 262, 912, 1157, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 921, + 929, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, 323, -1837, 733, -1837, 1315, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 1088, -1837, -1837, -1837, -1837, 39, 13, 690, + 111, -1837, -1837, -1837, 5536, 1216, 7210, 7783, 1126, -1837, + -1837, -1837, -1837, 1283, 1285, 82, 1259, 1263, 1269, 126, + 1271, 2175, 1276, 7210, 114, 7210, 842, 7210, -1837, -1837, + 1227, 7783, 866, 7210, 7210, 1248, 1644, -1837, 6475, 163, + -1837, 1644, -1837, -1837, -1837, 972, -1837, 1242, 1249, 757, + -1837, -1837, -1837, 884, 1644, 1253, 1267, 1303, 1644, 919, + -1837, -1837, 130, -1837, -1837, 7210, -1837, -1837, 5745, 1277, + 1034, 1590, 2270, 2686, -1837, 2079, 664, -1837, -1837, -1837, + -1837, 1281, -1837, -1837, -1837, -1837, 7210, -1837, 1255, 1392, + 1398, 1062, 441, 327, -1837, -1837, -1837, -1837, 1429, 1484, + 1438, -1837, -1837, -1837, -1837, 1449, -1837, -1837, -1837, -1837, + 622, -1837, -1837, 1456, 1460, -1837, -1837, -1837, 1370, 1381, + -1837, -1837, 853, -1837, -1837, 1474, -1837, -1837, -1837, -1837, + 1480, -1837, -1837, 5954, -1837, 1480, -1837, -1837, -1837, 102, + -1837, -1837, 1391, -1837, 1486, -1837, 257, -1837, 1125, -1837, + 257, 157, -1837, 8299, 8299, 8299, 8299, 8299, 7783, 121, + 7988, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, 8299, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, 703, -1837, 1357, 1489, 1495, 1147, + 902, 1513, -1837, -1837, -1837, 7988, 7210, 7210, 1452, 159, + 370, 1525, -1837, 1168, 370, 1464, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 1503, 1195, 1206, 1209, -1837, + 1225, 1239, -1837, -1837, -1837, -1837, 1298, 1290, 1099, 1644, + -1837, -1837, 1508, 1510, 1515, 1256, 1530, -1837, 1532, 1278, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1536, -1837, -1837, + -1837, -1837, -1837, 1315, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1541, 632, -1837, -1837, -1837, -1837, 1546, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1176, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1548, -1837, 1459, -1837, -1837, 1462, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1306, -1837, + 1311, 1524, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1163, + 1689, 1777, 1777, -1837, -1837, -1837, 1425, -1837, -1837, -1837, + -1837, 1430, 1439, -1837, 1440, 1436, 1443, 572, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1588, -1837, -1837, + 1593, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1396, -1837, 1405, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1595, 1608, 1350, -1837, -1837, -1837, -1837, + -1837, 1612, 272, -1837, -1837, -1837, 1353, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, 1355, 1356, 1367, 1632, -1837, -1837, + 842, -1837, -1837, -1837, 1635, -1837, -1837, -1837, -1837, 7210, + 2686, 2079, 1735, 6163, -1837, 146, 260, 1734, 3115, 1644, + 1644, 1653, 7783, 7210, 7210, 7210, -1837, 1662, 7210, 1715, + 7210, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1665, -1837, + 154, 1749, -1837, -1837, 247, 297, 262, -1837, 410, 476, + 183, 1730, -1837, 7210, -1837, -1837, 893, 1542, 1134, 277, + -1837, 829, 1513, 893, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 1628, 591, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1073, 1164, -1837, 1265, -1837, -1837, -1837, + 7210, 1774, 7210, -1837, -1837, -1837, 667, 726, -1837, 7210, + -1837, -1837, 1417, -1837, -1837, 7210, 7210, 7210, 7210, 7210, + 1686, 7210, 7210, 177, 7210, 1480, 7210, 1707, 1787, 1713, + 2495, 2495, -1837, -1837, -1837, 7210, 1484, 7210, 1484, -1837, + 1778, 1784, -1837, 866, -1837, 7783, -1837, 7783, -1837, -1837, + -1837, 1357, 1489, 1495, -1837, 893, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 1441, 8299, 8299, 8299, 8299, 8299, 8299, + 8299, 8299, 8488, 8299, 8299, 795, -1837, 1178, -1837, -1837, + -1837, -1837, -1837, 1708, -1837, 304, 409, -1837, 2905, 3652, + 3251, 4477, 841, -1837, -1837, -1837, -1837, -1837, -1837, 1446, + 1468, 1471, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1806, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 3115, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1453, + 1470, -1837, -1837, -1837, -1837, -1837, -1837, 1350, 668, 1742, + -1837, -1837, -1837, -1837, -1837, 1437, -1837, -1837, -1837, -1837, + -1837, 1554, 1203, -1837, 1665, 1673, -1837, 539, 154, -1837, + 990, -1837, -1837, 7210, 7210, 1841, -1837, 1747, 1747, -1837, + 260, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + 1500, 1734, 6943, 260, -1837, -1837, -1837, -1837, -1837, -1837, + 7210, -1837, -1837, 253, 1555, 1560, 1856, -1837, -1837, -1837, + 1567, 102, -1837, 7783, 102, 7210, 1836, -1837, 8218, -1837, + 1696, 1597, 1573, 1581, 1099, 1134, -1837, 1747, 1747, -1837, + 277, -1837, 6475, -1837, 5051, -1837, -1837, -1837, -1837, 1888, + -1837, -1837, 1461, -1837, -1837, 1461, -1837, 1826, 1461, -1837, + -1837, 7210, -1837, -1837, -1837, -1837, -1837, 1255, 1392, 1398, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1897, 7210, 1737, + 7210, -1837, -1837, -1837, -1837, 1484, -1837, 1484, 1480, -1837, + -1837, 237, 6943, 6579, 166, -1837, -1837, -1837, 1525, 1900, + -1837, -1837, 1357, 1489, 1495, -1837, 231, 1525, -1837, -1837, + 829, 8299, 8488, -1837, 1805, 1878, -1837, -1837, -1837, -1837, + -1837, 257, 257, 257, 257, 257, 1815, 658, 257, 257, + 257, 257, -1837, -1837, -1837, 370, -1837, 1562, 108, -1837, + 1821, -1837, -1837, -1837, 370, 370, 370, 370, 370, 7783, + -1837, 1747, 1747, 1566, 1482, 1820, 983, 1766, 1741, -1837, + -1837, -1837, 370, 370, 531, -1837, 7783, 1747, 1747, 1570, + 983, 1766, -1837, -1837, -1837, 370, 370, 531, 1830, 1577, + 1837, -1837, -1837, -1837, -1837, -1837, 5203, 4006, 3513, 4831, + 1207, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 4360, 1501, + -1837, -1837, 1838, -1837, -1837, -1837, 1937, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, 1843, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, 2014, -1837, 737, 472, 1072, 1844, + -1837, -1837, -1837, -1837, -1837, 1555, 1560, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1567, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 7210, -1837, -1837, -1837, -1837, + -1837, -1837, 7783, 1585, 260, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 1731, 1934, -1837, 1857, -1837, 1858, -1837, 1731, + 1861, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 7210, 127, + 127, 893, 1513, -1837, 251, 1866, -1837, 185, 842, 1867, + -1837, -1837, -1837, -1837, -1837, -1837, 370, -1837, 591, -1837, + -1837, -1837, -1837, -1837, -1837, 7210, -1837, 1874, -1837, 1480, + 1480, 7783, -1837, 436, 1606, 1967, 893, -1837, 1525, 1525, + 1785, 1871, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, 257, 257, 257, -1837, -1837, -1837, -1837, + -1837, 416, -1837, -1837, -1837, -1837, -1837, 1873, -1837, -1837, + -1837, -1837, -1837, -1837, 1279, -1837, 370, 370, 323, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + 1380, -1837, -1837, -1837, -1837, -1837, 1336, -1837, -1837, -1837, + -1837, -1837, 1027, 370, 370, 323, 1110, 1336, -1837, -1837, + -1837, 1831, 416, 370, -1837, -1837, -1837, -1837, -1837, -1837, + 1603, 1032, 1754, -1837, -1837, -1837, 1880, -1837, 1350, -1837, + -1837, -1837, 1618, 812, 1315, -1837, -1837, -1837, -1837, -1837, + 1747, 1883, 812, 1884, 1315, -1837, -1837, -1837, -1837, -1837, + 1889, 1315, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, 6943, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, 1694, -1837, 288, -1837, -1837, + -1837, 154, -1837, -1837, -1837, -1837, -1837, -1837, 1886, 1702, + -1837, -1837, 1665, 154, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 7210, 1633, 7783, -1837, -1837, 2106, 6579, -1837, -1837, + 1813, 370, 1649, 1650, 1652, 1655, 1657, -1837, -1837, -1837, + 1659, 1660, 1661, 1667, 139, 370, -1837, -1837, 1988, 7783, + -1837, -1837, -1837, -1837, -1837, 983, -1837, 1766, -1837, 7607, + -1837, -1837, -1837, 603, -1837, 301, 370, 370, -1837, -1837, + -1837, -1837, -1837, 2027, -1837, 1671, -1837, -1837, 370, 370, + -1837, 370, 370, 370, 370, 370, -1837, 1902, 370, -1837, + 1674, -1837, -1837, -1837, -1837, -1837, 1942, -1837, -1837, 1555, + 1560, 1567, -1837, -1837, -1837, -1837, 1681, 893, -1837, -1837, + 1785, -1837, -1837, -1837, -1837, -1837, 1684, 1693, 1697, -1837, + -1837, -1837, -1837, -1837, -1837, 168, -1837, -1837, -1837, 1956, + -1837, -1837, -1837, -1837, 7783, 370, 2054, 2058, -1837, -1837, + -1837, -1837, -1837, -1837, 370, 983, 1964, -1837, -1837, -1837, + 1103, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1966, + -1837, 1968, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 812, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 1813, 1376, 4714, 4123, 6351, 2117, -1837, -1837, -1837, + 1442, 2345, 1557, 1188, 135, -1837, 1551, 1279, -1837, 7783, + -1837, -1837, -1837, -1837, -1837, -1837, 1380, -1837, 1971, 1972, + -1837, 2065, 171, -1837, 370, -1837, -1837, -1837, -1837, -1837, + 370, 370, 370, 370, 370, 1833, 1115, 2111, 370, 370, + 370, 370, -1837, -1837, 224, 1720, 1831, -1837, 2063, -1837, + -1837, -1837, -1837, 1478, 1968, 370, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 416, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, 370, 370, 370, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837 +}; + + /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int16 yydefact[] = +{ + 2, 0, 1, 0, 4, 5, 0, 0, 0, 0, + 432, 432, 432, 432, 432, 432, 432, 432, 436, 439, + 432, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 237, 438, 9, 28, 29, 0, + 432, 432, 432, 432, 68, 67, 3, 0, 71, 0, + 433, 0, 457, 0, 66, 0, 424, 425, 0, 0, + 0, 0, 608, 87, 89, 0, 0, 289, 0, 311, + 0, 337, 72, 432, 73, 432, 432, 432, 432, 432, + 432, 432, 0, 432, 432, 432, 432, 74, 432, 75, + 432, 432, 432, 432, 432, 432, 432, 432, 0, 432, + 432, 432, 432, 76, 432, 77, 432, 463, 432, 463, + 432, 463, 463, 432, 432, 463, 432, 463, 0, 432, + 463, 463, 0, 432, 463, 463, 463, 463, 432, 432, + 432, 463, 35, 432, 463, 432, 432, 432, 432, 463, + 463, 432, 47, 432, 432, 432, 432, 463, 0, 432, + 80, 432, 81, 0, 0, 0, 765, 736, 426, 427, + 428, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 25, 25, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 949, 950, 951, 952, 953, 954, 955, 956, 957, + 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, + 968, 970, 0, 972, 971, 0, 0, 0, 0, 34, + 0, 0, 85, 732, 731, 737, 738, 252, 748, 749, + 742, 940, 743, 746, 750, 747, 744, 745, 739, 1056, + 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, + 1067, 1068, 1069, 53, 1074, 1075, 1076, 1077, 1071, 1072, + 1073, 740, 1322, 1323, 1324, 1325, 1326, 1327, 1328, 1329, + 741, 0, 249, 250, 0, 33, 233, 0, 21, 235, + 432, 432, 432, 432, 432, 432, 432, 432, 0, 432, + 432, 432, 432, 16, 238, 39, 239, 437, 434, 435, + 432, 432, 432, 13, 861, 834, 836, 70, 69, 440, + 442, 0, 0, 0, 459, 458, 460, 0, 598, 0, + 716, 717, 718, 0, 931, 932, 933, 500, 501, 936, + 723, 0, 0, 0, 516, 522, 527, 0, 551, 576, + 588, 589, 665, 671, 692, 0, 0, 976, 0, 7, + 92, 465, 467, 481, 468, 61, 272, 496, 477, 504, + 475, 13, 514, 14, 59, 525, 473, 474, 46, 579, + 40, 0, 54, 60, 596, 40, 664, 40, 670, 18, + 24, 487, 45, 690, 493, 0, 494, 479, 0, 722, + 478, 767, 770, 772, 774, 776, 777, 784, 786, 0, + 785, 729, 503, 940, 482, 488, 480, 739, 497, 62, + 0, 0, 65, 451, 0, 0, 0, 91, 445, 0, + 95, 304, 303, 0, 448, 0, 0, 0, 608, 112, + 114, 289, 0, 311, 337, 432, 432, 432, 13, 861, + 834, 836, 0, 60, 0, 136, 137, 138, 0, 130, + 131, 139, 0, 132, 133, 141, 142, 0, 134, 135, + 0, 143, 0, 145, 146, 838, 839, 837, 432, 13, + 36, 44, 51, 0, 60, 203, 464, 205, 170, 171, + 172, 173, 432, 174, 176, 200, 199, 198, 192, 432, + 463, 196, 195, 197, 838, 839, 840, 432, 0, 13, + 432, 177, 432, 180, 432, 183, 432, 189, 36, 44, + 51, 432, 186, 78, 220, 432, 432, 464, 216, 218, + 215, 222, 0, 223, 13, 432, 208, 207, 432, 213, + 211, 44, 79, 224, 225, 226, 227, 230, 432, 229, + 0, 1082, 1079, 1080, 56, 0, 756, 757, 758, 759, + 760, 762, 0, 981, 983, 0, 982, 52, 0, 0, + 1320, 1321, 56, 1084, 1085, 55, 20, 55, 1088, 1089, + 1090, 1091, 30, 0, 0, 1094, 1095, 1096, 1097, 1098, + 9, 1116, 1117, 1111, 1106, 1107, 1108, 1109, 1110, 1112, + 1113, 1114, 1115, 0, 28, 55, 1131, 1130, 1129, 1132, + 1133, 1134, 31, 55, 1137, 1138, 1139, 32, 1148, 1149, + 1141, 1142, 1143, 1145, 1144, 1146, 1147, 29, 1160, 55, + 1156, 1153, 1152, 1157, 1155, 1154, 1158, 1159, 31, 1163, + 1166, 1162, 1164, 1165, 8, 1169, 1168, 19, 1171, 1172, + 1173, 11, 1177, 1178, 1175, 1176, 57, 1183, 1180, 1181, + 1182, 58, 1230, 1224, 1227, 1228, 1222, 1223, 1225, 1226, + 1229, 1231, 0, 1184, 55, 1264, 1265, 0, 15, 1210, + 1209, 1202, 1203, 1204, 1188, 1189, 1190, 1191, 1192, 1193, + 1194, 1195, 1196, 1197, 53, 1206, 1205, 1208, 1207, 1199, + 1200, 1201, 1217, 1219, 1218, 0, 25, 0, 1214, 1213, + 1212, 1211, 1318, 1315, 1316, 0, 1317, 49, 55, 28, + 1335, 1008, 29, 1334, 1337, 1006, 1007, 34, 0, 48, + 48, 0, 48, 1341, 48, 1344, 1343, 1345, 0, 48, + 1332, 1331, 27, 1353, 1350, 1348, 1349, 1351, 1352, 23, + 1356, 1355, 17, 55, 1359, 1362, 1358, 1361, 38, 37, + 944, 945, 946, 51, 947, 34, 37, 942, 943, 1023, + 1024, 1030, 1016, 1017, 1015, 1025, 1026, 1046, 1019, 1028, + 1021, 1022, 1027, 1018, 1020, 1013, 1014, 1044, 1043, 1045, + 51, 0, 12, 1031, 987, 986, 0, 784, 0, 0, + 48, 27, 23, 17, 38, 1363, 991, 992, 969, 990, + 0, 730, 1070, 232, 251, 82, 234, 83, 60, 154, + 155, 132, 156, 157, 0, 158, 160, 161, 432, 13, + 36, 44, 51, 86, 84, 240, 241, 243, 242, 245, + 246, 244, 247, 858, 858, 858, 97, 0, 0, 551, + 0, 454, 455, 456, 0, 0, 0, 0, 0, 938, + 937, 934, 935, 0, 0, 0, 37, 37, 0, 0, + 0, 0, 12, 0, 0, 0, 560, 0, 549, 550, + 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, + 788, 0, 466, 469, 498, 0, 472, 0, 0, 515, + 518, 476, 483, 0, 0, 0, 0, 0, 0, 526, + 528, 484, 0, 575, 485, 0, 47, 16, 0, 0, + 20, 30, 9, 28, 899, 29, 0, 904, 902, 903, + 14, 0, 40, 40, 889, 890, 0, 626, 629, 631, + 633, 635, 636, 641, 646, 644, 645, 647, 649, 587, + 613, 614, 624, 891, 615, 622, 616, 623, 619, 620, + 0, 617, 618, 0, 648, 621, 486, 495, 0, 0, + 605, 604, 597, 600, 489, 0, 683, 684, 685, 663, + 668, 681, 490, 0, 669, 674, 491, 492, 686, 0, + 708, 709, 691, 693, 696, 706, 0, 734, 0, 733, + 0, 0, 724, 0, 0, 0, 0, 0, 0, 0, + 0, 924, 925, 926, 927, 928, 929, 930, 20, 30, + 9, 28, 31, 916, 29, 31, 8, 19, 11, 57, + 58, 53, 15, 25, 49, 40, 0, 906, 874, 907, + 781, 782, 886, 873, 863, 862, 878, 880, 882, 884, + 885, 872, 908, 909, 875, 0, 0, 0, 0, 7, + 0, 828, 827, 885, 0, 0, 393, 60, 256, 273, + 290, 319, 338, 461, 111, 0, 0, 0, 0, 118, + 0, 0, 858, 858, 858, 120, 0, 0, 551, 0, + 129, 153, 0, 0, 0, 0, 0, 144, 0, 0, + 858, 148, 151, 149, 152, 169, 191, 0, 206, 175, + 194, 193, 12, 432, 179, 178, 181, 184, 190, 185, + 182, 188, 187, 217, 219, 221, 210, 209, 212, 214, + 228, 231, 1081, 0, 0, 55, 753, 754, 22, 0, + 979, 766, 42, 42, 1319, 1086, 1083, 1092, 1087, 20, + 28, 20, 28, 1093, 1118, 1119, 1120, 1121, 28, 1103, + 1128, 1127, 1136, 1135, 1140, 1151, 1150, 1161, 1167, 1170, + 1174, 1179, 10, 1248, 1254, 1252, 1243, 1244, 1247, 1249, + 1238, 1239, 1240, 1241, 1242, 1250, 1245, 1246, 1251, 1186, + 1253, 1185, 1266, 15, 1262, 1198, 1216, 1215, 1220, 1270, + 1267, 1268, 1269, 1271, 1272, 1273, 1274, 1275, 1276, 1277, + 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1301, 50, 1313, + 1336, 1002, 1003, 1009, 48, 1004, 1333, 0, 1338, 1340, + 0, 1342, 1330, 1347, 1354, 1360, 1357, 941, 948, 939, + 1029, 1032, 1033, 0, 1035, 0, 1034, 1036, 1037, 12, + 12, 1038, 1010, 0, 0, 984, 1365, 1364, 1366, 1367, + 1368, 0, 0, 751, 168, 159, 0, 858, 163, 166, + 164, 167, 236, 248, 0, 0, 0, 0, 358, 13, + 560, 383, 36, 363, 0, 44, 388, 835, 51, 0, + 28, 29, 590, 0, 599, 0, 710, 712, 0, 0, + 0, 0, 0, 0, 0, 0, 12, 0, 0, 1038, + 0, 517, 429, 523, 524, 36, 558, 559, 0, 44, + 0, 0, 705, 45, 700, 699, 0, 704, 702, 703, + 0, 677, 679, 0, 499, 800, 7, 7, 802, 797, + 799, 885, 824, 7, 787, 462, 282, 520, 521, 519, + 541, 20, 0, 0, 539, 535, 530, 531, 532, 533, + 536, 534, 529, 0, 0, 53, 0, 655, 900, 901, + 0, 650, 0, 892, 895, 896, 893, 894, 905, 0, + 898, 897, 0, 613, 622, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 657, 0, 0, 0, 0, + 0, 0, 602, 603, 601, 0, 0, 0, 672, 695, + 700, 699, 694, 0, 10, 0, 726, 0, 725, 768, + 769, 771, 773, 775, 778, 7, 505, 507, 783, 893, + 915, 894, 917, 914, 913, 919, 911, 912, 910, 920, + 918, 921, 922, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 869, 868, 885, 974, 1055, + 830, 829, 63, 0, 64, 0, 0, 109, 0, 0, + 0, 0, 0, 60, 256, 273, 290, 319, 338, 0, + 0, 0, 13, 36, 44, 51, 452, 441, 443, 273, + 446, 449, 338, 12, 204, 201, 12, 0, 761, 755, + 752, 52, 763, 764, 1099, 1101, 1100, 1102, 55, 1123, + 1125, 1124, 1126, 1105, 28, 0, 1260, 1232, 1257, 1234, + 1261, 1237, 1258, 1259, 1235, 1255, 1256, 1233, 1236, 1263, + 1298, 1297, 1299, 1300, 1306, 1288, 1289, 1290, 1291, 1303, + 1292, 1293, 1294, 1295, 1296, 1304, 1305, 1307, 1308, 1309, + 1310, 1311, 1312, 55, 1287, 1286, 1302, 49, 1005, 0, + 0, 28, 28, 29, 29, 1011, 1012, 984, 984, 0, + 26, 989, 993, 994, 34, 0, 338, 12, 373, 378, + 368, 0, 0, 98, 0, 0, 105, 0, 0, 100, + 0, 107, 592, 0, 0, 591, 713, 0, 0, 807, + 711, 803, 1248, 1252, 1247, 1251, 1253, 53, 10, 10, + 0, 796, 0, 794, 37, 37, 12, 512, 12, 12, + 0, 12, 548, 0, 561, 564, 0, 557, 553, 552, + 554, 0, 687, 0, 0, 0, 0, 791, 0, 792, + 0, 13, 0, 0, 551, 801, 810, 0, 0, 823, + 798, 808, 790, 789, 0, 540, 28, 544, 545, 53, + 543, 577, 0, 581, 578, 0, 583, 0, 0, 585, + 656, 0, 660, 662, 625, 627, 628, 630, 632, 634, + 642, 643, 637, 640, 639, 638, 652, 651, 0, 0, + 0, 1047, 1048, 1049, 1050, 666, 682, 673, 675, 707, + 735, 0, 0, 0, 0, 508, 923, 871, 865, 0, + 876, 877, 879, 881, 883, 870, 779, 864, 780, 887, + 888, 0, 0, 779, 0, 0, 60, 395, 394, 397, + 396, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 88, 258, 257, 0, 255, 0, 0, 55, + 0, 90, 275, 274, 0, 0, 0, 0, 0, 0, + 300, 0, 0, 0, 0, 0, 0, 0, 0, 93, + 292, 291, 0, 0, 0, 470, 0, 0, 0, 0, + 0, 0, 94, 321, 320, 0, 0, 0, 0, 0, + 0, 13, 96, 340, 339, 128, 0, 0, 0, 0, + 0, 373, 378, 368, 121, 126, 122, 127, 0, 0, + 150, 202, 0, 980, 1122, 1104, 0, 1285, 1314, 1339, + 1346, 1039, 1040, 1041, 1042, 41, 0, 26, 985, 1001, + 997, 996, 995, 34, 0, 165, 0, 0, 0, 0, + 13, 360, 359, 362, 361, 561, 564, 36, 385, 384, + 387, 386, 44, 365, 364, 367, 366, 554, 51, 390, + 389, 392, 391, 593, 595, 0, 805, 806, 804, 1221, + 978, 977, 0, 0, 795, 975, 973, 1052, 513, 1053, + 12, 1051, 0, 566, 568, 0, 36, 0, 36, 0, + 0, 44, 701, 697, 698, 45, 45, 678, 0, 0, + 0, 7, 825, 826, 0, 0, 812, 0, 560, 0, + 811, 821, 822, 809, 502, 283, 0, 538, 0, 537, + 55, 55, 47, 55, 653, 0, 659, 0, 661, 667, + 676, 0, 714, 0, 0, 0, 7, 506, 867, 866, + 609, 0, 110, 453, 357, 444, 272, 447, 289, 311, + 450, 337, 254, 0, 0, 0, 357, 357, 357, 357, + 259, 0, 422, 423, 43, 421, 420, 0, 418, 276, + 278, 277, 281, 279, 0, 287, 0, 0, 0, 336, + 335, 43, 334, 398, 400, 401, 399, 354, 402, 355, + 0, 353, 307, 308, 310, 309, 0, 306, 301, 302, + 298, 471, 0, 0, 0, 0, 0, 0, 332, 331, + 329, 0, 0, 0, 343, 113, 115, 116, 117, 119, + 0, 0, 0, 140, 147, 10, 0, 988, 984, 1000, + 998, 162, 0, 0, 0, 12, 375, 374, 377, 376, + 0, 0, 0, 0, 0, 12, 380, 379, 382, 381, + 0, 0, 12, 370, 369, 372, 371, 831, 99, 859, + 860, 106, 101, 833, 108, 594, 0, 727, 1054, 570, + 571, 572, 573, 574, 563, 0, 546, 0, 565, 547, + 567, 0, 556, 688, 689, 680, 793, 12, 0, 0, + 14, 14, 0, 0, 284, 542, 31, 31, 586, 584, + 654, 0, 0, 0, 715, 721, 0, 510, 509, 610, + 611, 0, 0, 0, 0, 0, 0, 357, 357, 357, + 0, 0, 0, 0, 0, 0, 346, 419, 0, 0, + 294, 296, 297, 299, 333, 0, 55, 0, 295, 0, + 322, 323, 330, 318, 328, 0, 0, 0, 344, 12, + 12, 12, 1078, 0, 26, 0, 57, 53, 0, 0, + 103, 0, 0, 0, 0, 0, 104, 0, 0, 102, + 0, 562, 569, 555, 816, 12, 0, 819, 820, 561, + 564, 554, 580, 582, 658, 719, 0, 7, 612, 606, + 609, 393, 273, 290, 319, 338, 0, 0, 0, 358, + 383, 363, 388, 351, 350, 0, 347, 352, 280, 0, + 288, 356, 293, 305, 0, 0, 0, 285, 60, 317, + 13, 36, 44, 51, 0, 0, 0, 412, 406, 405, + 409, 404, 407, 408, 341, 342, 124, 125, 123, 0, + 999, 0, 844, 843, 850, 852, 855, 856, 853, 854, + 857, 0, 846, 728, 817, 13, 36, 36, 44, 720, + 511, 611, 0, 0, 0, 0, 0, 373, 378, 368, + 0, 0, 0, 0, 7, 345, 417, 0, 325, 0, + 316, 312, 314, 313, 315, 55, 0, 413, 0, 0, + 1187, 0, 0, 847, 0, 813, 814, 815, 818, 607, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 349, 348, 0, 0, 0, 326, 286, 327, + 55, 411, 410, 0, 0, 0, 55, 60, 260, 261, + 262, 263, 12, 12, 12, 13, 36, 44, 51, 414, + 415, 0, 403, 324, 430, 431, 849, 848, 43, 845, + 271, 0, 0, 0, 264, 269, 265, 270, 416, 851, + 267, 268, 266 +}; + + /* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -1837, -1837, -1, -1275, 1070, 54, -1321, 1067, 1068, -352, + -898, -718, 1182, 1297, -1837, 1074, -519, -1837, -1837, 1301, + -1837, -140, -1625, 1304, -8, -14, 1526, -601, -1837, -1837, + -687, -1837, -493, -724, 1305, -1837, -305, -1837, 964, -1828, + -489, -1233, -1837, -875, -492, -919, -1837, -497, 610, -659, + -1837, -546, 1544, -982, 1089, -1837, -399, -1837, 16, -1837, + -1837, 2084, -1837, -1837, -1837, 1832, 1834, 595, 1069, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, 11, -1837, 1691, -1837, 660, -330, + -1384, -1837, -1837, -1624, -424, -1406, -420, 354, 1, -423, + -1837, -1837, -1417, -1416, -1837, -429, -1413, -1836, -1837, -138, + 2, -1639, -685, -59, -60, -1671, -1667, -1664, -57, -58, + -40, -1837, -1837, -164, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 175, -773, -1477, -1837, 340, -33, 3359, -1837, + 309, -1837, -1837, 780, -1837, 425, 792, 1868, -1837, 211, + -1837, -722, 1733, -1837, -1837, 268, 700, 758, 834, -42, + -1837, -1837, -1420, -1398, -346, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, 230, -1837, -1837, -1837, -1837, 1273, -1837, -1837, + -1837, -1837, 1261, -1837, -1837, -1837, 255, -1837, -320, -1428, + -1649, -1837, -1193, -1690, -1475, -1681, -1470, 287, 286, -1837, + -1837, -1047, -1837, -1837, -1837, -1837, -1837, -1837, -1837, 1204, + -353, 1740, -9, -79, 25, 797, 798, 796, -1837, -806, + -1837, -1837, -1837, -1837, -1837, -1837, 1744, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -350, 783, -1837, -1837, + -1837, -1837, 1198, 560, -913, 573, 1321, 799, -1340, -1837, + -1837, 1864, -1837, -1837, -1837, -1837, 1208, -1837, -1837, -67, + 246, -851, -349, 1084, -27, -1837, -1837, -1837, 1071, 3, + -1837, -1837, -1837, -1837, -1837, -182, -208, -1837, -1837, 759, + -823, 1985, -52, -1837, 872, -1277, -1837, -1555, -1837, -1837, + 609, -1395, -1837, -1837, 585, 583, -1837, -1837, 1811, -604, + 1786, -552, 1789, -540, 1793, 170, -1837, -1753, -1837, -1837, + -88, -1837, -1837, -622, -516, 1788, -1837, -385, -328, -854, + -844, -840, -1837, -284, -859, -1837, 1322, 1538, -759, -1837, + -1509, -324, 128, 1898, -1837, 32, -1837, 155, -1837, -1422, + -1837, 307, -1837, -1837, -1837, -1837, -1837, 571, -282, 947, + 1517, 1039, 1903, 1905, -1837, -1837, -478, 207, -1837, -1837, + -1837, 1109, -1837, -1837, -68, -1837, -48, -1837, -43, -1837, + -64, -1837, -1837, -1837, -31, -1837, -26, -1837, -21, -1837, + -13, -1837, -12, -1837, -6, -1837, 0, -1837, 9, -1837, + 19, -1837, 24, 1545, -1837, -84, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, 1579, -1061, -1837, -1837, + -1837, -1837, -1837, 29, -1837, -1837, -1837, -1837, 1051, -1837, + -1837, 30, -1837, 34, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, -1837, + -1837, -1837, -1837, -1837 +}; + + /* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + 0, 1, 1783, 878, 1157, 1365, 1505, 1159, 1242, 836, + 891, 1184, 823, 1224, 976, 1158, 1363, 807, 1490, 1223, + 977, 695, 1819, 1222, 1419, 1421, 1364, 1151, 1153, 805, + 798, 513, 1092, 1227, 1226, 824, 904, 2017, 1492, 2117, + 1093, 978, 901, 532, 1215, 1209, 1547, 1094, 1130, 802, + 956, 1135, 1122, 1160, 1161, 892, 957, 884, 1784, 46, + 47, 48, 74, 87, 89, 447, 451, 456, 443, 103, + 293, 105, 132, 1098, 475, 142, 150, 152, 275, 278, + 295, 296, 832, 1262, 276, 222, 415, 1735, 1458, 416, + 1459, 1644, 2205, 1964, 419, 1460, 420, 1986, 1987, 423, + 2214, 2215, 1461, 1762, 1971, 425, 1462, 2115, 2195, 2196, + 1979, 1980, 2102, 1572, 1577, 1828, 1826, 1827, 1575, 1580, + 1456, 1981, 1744, 2136, 2220, 2221, 2222, 2306, 1745, 1746, + 1954, 1955, 1933, 223, 1304, 2336, 49, 50, 61, 450, + 52, 454, 1936, 458, 459, 1938, 71, 464, 1941, 445, + 446, 1934, 314, 315, 316, 53, 427, 1589, 477, 1748, + 351, 352, 1764, 353, 354, 355, 356, 357, 358, 359, + 1416, 1694, 1695, 360, 361, 362, 889, 890, 363, 364, + 365, 899, 900, 1351, 1345, 1649, 1650, 366, 1274, 1620, + 1881, 367, 1308, 1876, 1614, 1878, 1615, 1616, 2064, 368, + 369, 1653, 903, 370, 371, 372, 373, 374, 962, 963, + 1720, 414, 2100, 2179, 927, 928, 929, 930, 931, 932, + 933, 1673, 934, 935, 936, 937, 938, 939, 375, 376, + 969, 377, 378, 974, 379, 380, 970, 971, 381, 382, + 383, 982, 983, 1311, 1312, 1313, 984, 985, 1285, 1286, + 384, 385, 386, 387, 388, 991, 992, 389, 390, 224, + 940, 988, 1028, 941, 391, 228, 1128, 551, 552, 942, + 559, 392, 393, 394, 395, 396, 397, 1030, 1031, 1032, + 398, 399, 400, 879, 880, 1602, 1603, 1327, 1328, 1329, + 1590, 1591, 1640, 1635, 1636, 1641, 1330, 1891, 1050, 1834, + 837, 1846, 839, 1852, 840, 468, 498, 2148, 2046, 2282, + 2283, 2029, 2039, 1264, 1841, 838, 401, 1051, 1052, 1036, + 1037, 1038, 1039, 1331, 1041, 943, 944, 945, 1044, 1045, + 402, 851, 946, 756, 757, 231, 404, 947, 557, 1560, + 786, 948, 1252, 799, 1564, 1823, 234, 949, 718, 951, + 719, 952, 781, 782, 1239, 1240, 783, 953, 954, 405, + 406, 955, 239, 545, 240, 566, 241, 572, 242, 580, + 243, 594, 1148, 1504, 244, 602, 245, 607, 246, 617, + 247, 628, 248, 634, 249, 637, 250, 641, 251, 646, + 252, 651, 253, 685, 686, 687, 254, 688, 255, 701, + 256, 696, 257, 408, 663, 1179, 1596, 1181, 1517, 1509, + 1514, 1507, 1511, 258, 668, 1207, 1546, 1529, 1535, 1524, + 1208, 259, 707, 260, 562, 261, 262, 732, 263, 720, + 264, 722, 265, 724, 266, 729, 267, 739, 268, 742, + 269, 748, 270, 795 +}; + + /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int16 yytable[] = +{ + 44, 421, 54, 227, 852, 1071, 883, 1068, 787, 886, + 1070, 350, 1069, 1111, 1034, 1109, 422, 45, 870, 1332, + 1110, 1137, 1368, 417, 299, 1185, 885, 1156, 975, 298, + 1213, 1358, 1229, 229, 1089, 845, 989, 1426, 800, 697, + 1087, 1777, 1119, 279, 1081, 1775, 308, 1136, 1317, 1150, + 1789, 1627, 1629, 1095, 409, 1790, 229, 1152, 1642, 412, + 1788, 1057, 1765, 1765, 875, 1691, 1399, 1277, 229, 1799, + 964, 1035, 972, 1155, 476, 1096, 476, 1574, 476, 476, + 1622, 1787, 476, 1690, 476, 403, 1075, 476, 476, 950, + 297, 476, 476, 476, 476, 1798, 700, 403, 476, 1835, + 1067, 517, 1115, 1324, 1836, 1431, 517, 517, 1335, 671, + 1856, 1857, 671, 674, 476, 1040, 674, 1091, 1182, 1893, + 1372, 1346, 2012, 1085, 2010, 1350, 1053, 1053, 2011, 672, + 1692, 1996, 672, 1229, 673, 1815, 1817, 673, 1925, 877, + 1693, 1411, 1992, 2124, 986, 2049, 675, 1104, 1417, 675, + 1847, 676, 1412, 1824, 676, 2050, 677, 1413, 230, 677, + 1901, 1902, 1210, 877, 678, 679, 2137, 678, 679, 986, + 1333, 680, 1116, 1926, 680, 2264, 1132, 681, 2314, 1291, + 681, 230, 58, 59, 60, 232, 682, 1952, 1953, 682, + 1403, 311, 2019, 230, 1055, 1858, 683, 1225, 2053, 683, + 986, 684, 990, 494, 684, 157, 689, 690, 232, 689, + 690, 691, 1265, 1266, 691, 158, 159, 160, 1302, 229, + 232, 62, 758, 229, 309, 55, 310, 1597, 1216, 1296, + 1218, 56, 1219, 1269, 1409, 1410, 413, 1221, -736, 2193, + 2194, 418, 1921, 2193, 2194, 424, -736, -736, -736, -736, + -832, 303, 304, 305, 306, -731, 1228, 271, 1618, 871, + 850, 850, -731, -731, -731, -731, 277, 635, 1623, -697, + 998, 1923, 1353, 1872, 1966, 1967, 225, 1860, 1861, 2153, + 104, 65, 803, 1230, 636, 1354, 428, 1275, 806, 1302, + 1993, 1994, 497, 1765, 438, 439, 440, 441, 1246, 225, + 1278, -731, 1355, 1241, 1429, -738, 1277, 1656, 4, 1659, + 5, 225, -738, -738, -738, -738, 1000, 474, 987, -698, + -832, 64, 1272, 2216, 1270, 1261, 965, 1259, 158, 159, + 160, 1082, 1260, 1267, 499, 1382, 784, 233, 872, 508, + 509, 510, 1133, 1714, 230, 73, 785, 1033, 230, 524, + 1587, -738, 531, 1588, 2077, 229, 442, 1873, 1033, 1033, + 233, 56, 1428, 730, 758, 421, 1991, 1637, 88, 1777, + 1638, 232, 233, 1775, 4, 232, 5, 1271, 56, 1991, + 422, 1383, 426, 1299, 403, 1064, 430, 1256, 731, 865, + 1765, 1765, 2162, 421, 1255, 151, 801, 495, 709, 467, + 1080, 453, 426, 1268, 2217, 2218, 421, 866, 422, 1254, + 3, 1420, 1303, 4, 1422, 5, 496, 853, -746, 480, + 2081, 422, 712, 740, 57, -746, -746, -746, -746, 294, + 229, 448, -433, 1562, 1586, 6, 7, 8, 9, 1297, + 1083, 2093, 426, 519, 1806, 229, 1292, 1055, 741, 1619, + 1469, 1470, 1471, 534, 854, 421, 1356, 476, 818, 403, + 540, 1380, 225, 1381, -746, 1519, 225, 1258, 1483, 1858, + 422, 221, 1472, 3, 403, 1276, 4, 221, 5, 2246, + 230, 312, 313, 2080, -747, 1415, -253, 56, 2284, 2247, + 221, -747, -747, -747, -747, 2348, 221, 1084, 6, 7, + 8, 9, 849, 849, 221, 2206, 855, 232, 221, 973, + 2349, 833, 834, 835, 1758, 221, 1598, 1599, 1624, 2230, + 1056, 1407, 2248, 233, 1451, 1991, 1474, 233, 1334, 334, + -747, 1927, 1674, 2265, 335, 4, 2315, 5, 538, 1475, + 3, 349, 1925, 4, 56, 5, 221, 337, 1874, 272, + 273, 274, 338, 2151, 57, 230, 1187, 1086, 272, 273, + 274, 1473, 2030, 1088, 2078, 6, 7, 8, 9, 2031, + 230, 57, 966, 967, 968, 1671, 2276, 489, 490, 1489, + 2267, 2092, 232, 1874, 1702, 2056, 1149, 2177, 2297, 1385, + 2295, 811, 426, 221, 2296, 1703, 2144, 232, 225, -736, + 1704, 235, 1922, 1154, 56, 1910, 2032, 2169, 1911, 881, + 56, 1913, 2170, 1188, -272, -731, 2076, 1370, 1371, 413, + 1494, 448, 1496, 309, 235, -433, 881, 310, 1808, 671, + 1563, 674, 418, 674, 1143, 1567, 235, 424, 410, 1287, + 2208, 2033, -551, 2163, 411, 1027, 1072, 1073, 1074, 672, + 989, 2097, 2073, 2074, 673, 2171, 1027, 1027, 1457, 233, + 1445, 558, 2219, 1316, 675, -738, 675, 300, 435, 676, + 221, 676, 1418, 225, 677, 721, 677, 1700, 1701, 1090, + 57, 723, 678, 679, 678, 679, 1543, 349, 225, 680, + 2209, 680, 221, 310, 2142, 681, 1657, 681, 1212, 789, + 711, 1211, 465, 1544, 682, 2082, 682, 1386, 1102, 1387, + 1434, 1886, 1463, 1758, 683, 1121, 683, 1446, 804, 684, + 1432, 684, 1548, 1435, 689, 690, 689, 690, 334, 691, + 521, 691, 523, 335, 233, 1647, 1545, 57, 3, 841, + 1648, 4, 1433, 5, 546, 547, 337, 421, 1943, 233, + 2034, 338, -1002, 2176, -1002, 966, 967, 968, 881, 846, + 998, 1447, 422, 6, 7, 8, 9, 2255, 842, 1892, + 1125, 603, 2256, 515, 1716, 516, 999, 2254, -746, 1576, + 2114, 1581, 843, 1332, 1139, 221, 1579, 235, 18, 19, + 709, 235, 604, 455, 67, 68, 69, 57, 2253, 1140, + 1317, 2094, 1434, 57, 1314, 605, 1000, 1277, 1758, 1141, + 711, -1003, 1612, -1003, 712, 1711, 1414, 553, 554, 555, + 1617, 881, 1645, 334, 1142, 1114, 1326, 848, 335, 715, + 716, 2022, -432, 514, 1332, 1777, 2335, 2035, 35, 1775, + 863, 337, 3, 1263, -747, 4, 338, 5, 36, 1453, + 229, 1443, 868, 869, 37, 873, 1765, 1765, 431, 432, + 433, 301, 436, 302, 437, 1388, 864, 6, 7, 8, + 9, 2023, 876, 434, 229, 460, 461, 1820, 38, 813, + 1865, 1866, 1778, 1430, 473, 1779, 548, 549, 881, 463, + 550, 958, 959, 484, 485, 2210, 466, 877, 467, 491, + 692, 1367, 2250, 990, 1842, 1366, 844, 693, 487, 1441, + -1002, 717, 1400, 2211, 1126, 1127, 522, 1573, 2212, 881, + 2213, 902, 1442, 235, 1443, 535, 536, 1389, 995, 539, + 133, 134, 987, 135, 136, 137, 989, 694, 1859, 715, + 716, 996, 1944, 479, 1945, 481, 483, 709, 1033, 486, + 997, 488, 1804, 702, 492, 493, 560, 561, 501, 503, + 505, 507, 1340, 606, 725, 512, 796, 797, 520, -1003, + 1565, 712, -1002, 527, 530, 230, -832, 236, 1797, 2146, + 1795, 537, 874, 812, 2147, 1796, 703, 704, 1046, 2302, + 1909, 3, 1047, 1033, 4, 1341, 5, 1807, 235, 230, + 236, 229, 232, 868, 869, 1780, 1946, 1947, 1948, 1949, + 1342, 1559, 236, 235, 1816, 2024, 6, 7, 8, 9, + 311, 717, 705, 709, 307, -432, 232, 664, 4, 1257, + 5, -1003, 2129, 3, 989, 2119, 4, 2088, 5, 556, + 960, 1058, 867, 961, 726, 727, 728, 712, 1688, 1452, + 1698, 887, 888, 1454, 1273, 706, 1343, 1707, 6, 7, + 8, 9, 868, 869, 1785, 814, 815, 320, 321, 237, + 642, 1059, 322, 3, 643, 1060, 4, 638, 5, 816, + 1061, 665, 666, 893, 1601, 1062, 881, 1973, 639, 640, + 667, 708, 237, 225, 158, 159, 160, -337, 6, 7, + 8, 9, 2025, 709, 237, 710, 1097, 644, 645, 882, + 993, 994, 1974, 1975, 226, 2133, 3, 225, 2125, 4, + 1794, 5, 2030, 711, 1027, 2278, 230, 712, 2279, 2031, + 713, -841, 1495, 1781, 1497, -842, 2020, 226, 1103, 238, + 1503, 6, 7, 8, 9, 1344, 563, 564, 565, 226, + 1697, 1699, 1123, 232, 233, 1306, 1307, 1705, 1699, 1708, + 1710, 1404, 407, 236, 2232, 1406, 2032, 236, 469, 470, + 471, 472, 1373, 138, 407, 1441, 157, 1651, 1318, 1124, + 139, 140, 2167, 2168, 1129, 1441, 2040, 1652, 1453, 3, + 1443, 1131, 4, 1957, 5, 1183, 141, 1287, 1712, 1601, + 1443, 2033, 980, 981, 3, 2030, 1782, 4, 3, 5, + 1217, 4, 2031, 5, 6, 7, 8, 9, 987, 714, + 1220, 2107, 2108, 2109, 894, 895, 896, 897, 898, 6, + 7, 8, 9, 6, 7, 8, 9, 1976, 1332, 1027, + 1027, 1027, 1027, 1027, 225, 595, 1027, 596, 1778, 2032, + 1243, 1779, 715, 716, 1244, 237, 1863, 1977, 1978, 237, + 1245, 2110, 2111, 2112, 2113, 733, 597, 157, 1651, 1100, + 1375, 1376, 1027, 525, 598, 599, 600, 601, 1655, 1896, + 1251, 734, 528, 4, 2033, 5, 647, 2119, 18, 19, + 1498, 1027, 158, 159, 160, 229, 1253, 18, 19, 236, + 226, 1283, 735, 1288, 226, 233, 1289, 1499, 1290, 736, + 2034, 1293, 1500, 1501, 1899, 1294, 987, 1932, 1120, 648, + 649, 1295, 650, 1298, 1101, 238, 1928, 1929, 1300, 238, + 1310, 1675, -432, 1323, 717, 1919, 1336, 1920, 35, 881, + 4, -432, 5, 2127, 2051, 1337, 1924, 35, 36, 808, + 2041, 2054, 1338, 2052, 37, 1848, 1347, 36, 819, 820, + 821, 822, 1362, 37, 2086, 2087, 1369, 2089, 157, 1651, + 1348, 1780, 1436, 1437, 236, 18, 19, 3, 38, 1658, + 4, 881, 5, 2066, 4, 2069, 5, 38, 2125, 236, + 1520, 237, 2072, 2034, 1521, 1522, 1523, 2140, 737, 738, + 1665, 1666, 6, 7, 8, 9, 1349, 1672, 229, 1377, + 229, 1630, 868, 869, 1055, 1316, 1378, 482, 235, 1502, + 230, 881, 2186, 2187, 2188, 35, 1631, 1699, 1699, 2004, + 1379, 1632, 18, 19, 1384, 36, 226, 2042, 272, 273, + 274, 37, 1319, 3, 1633, -1047, 4, 232, 5, 1634, + 1144, 1145, 1146, 1147, 1715, 1717, -1048, 1733, 1742, 1760, + 1773, 407, 229, 1390, 881, 38, 237, 1391, 6, 7, + 8, 9, 1718, 1392, 1734, 1743, 1761, 1774, 2048, 1395, + 2323, 237, 35, 1029, 1393, 2172, 2173, 1396, 2233, 1405, + 1277, 403, 36, 1403, 1029, 1029, 1805, 1267, 37, 1781, + 1512, 1513, 3, 1515, 1516, 4, 1438, 5, 1551, 1552, + 790, 226, 2106, 1439, 2104, 1440, 2105, 1553, 1554, 791, + 792, 1444, 38, 793, 794, -432, 226, 6, 7, 8, + 9, 2329, 1434, 230, -432, 230, 407, 881, 225, 1813, + 1814, 1055, 1778, 1811, 1812, 1779, 877, 1628, 2123, 1821, + 1822, 407, 881, 2301, 1883, 1450, 1314, 1400, 3, 1464, + 232, 4, 232, 5, 157, 1651, 852, 1455, 1830, 235, + 1465, 1831, 2009, 1466, 1838, 2132, 1843, 1969, 1970, 1849, + 2202, 2334, 1302, 6, 7, 8, 9, 230, 1832, 1467, + 1326, 1839, 1267, 1844, 2304, 2305, 1850, 1683, 1684, 233, + 90, 1270, 1906, 1468, 3, 229, 2103, 4, 91, 5, + 92, 1476, 93, 1477, 232, 94, 95, 96, 1478, 97, + 1479, 1373, 1373, 1373, 1373, 1373, 229, 1373, 1373, 6, + 7, 8, 9, 1480, 403, 1481, 1681, 1681, 1907, 143, + 1484, 144, 1482, 1905, 1487, 1506, 145, 229, 146, 1491, + 1508, 225, 147, 225, 1601, 1518, 1537, 825, 1510, 826, + -551, 827, 828, 541, 1538, 1780, 542, 543, 544, 829, + 830, 1956, 1541, 1539, 3, 1540, 403, 4, 1542, 5, + 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027, + 1027, 1549, 148, 1559, 149, 229, 1550, 2022, 1557, 6, + 7, 8, 9, 831, 567, 225, 568, 569, 570, 571, + 2160, 1558, 233, 1287, 233, 1561, 2274, 1566, 2272, 1568, + 1569, 1042, 1965, 2273, 403, 881, 979, 980, 981, 2309, + 230, 1570, 1042, 1042, 1950, 1571, 1267, 2023, 1578, 1965, + 1583, 2290, 998, 1959, 1960, 1961, 1962, 1963, 158, 159, + 160, 230, 229, 2286, 2287, 3, 1600, 232, 4, 2288, + 5, 1988, 1989, 1990, 2333, 1608, 233, 1610, 1613, 229, + 2339, 1621, 230, 1625, 1998, 1999, 2000, 1646, 232, 1661, + 6, 7, 8, 9, 1664, 1733, 1742, 1760, 1773, 1670, + 229, 881, 1678, 1781, 236, 1679, 629, 1742, 1680, 232, + -697, 229, 1734, 1743, 1761, 1774, -698, 2298, 1696, 2270, + 1791, 1713, 630, 1802, 1743, 966, 967, 968, 236, 403, + 230, 631, 850, 850, 1809, 2026, 2036, 2043, 632, 633, + 403, 2347, 1792, 2345, 3, 1793, 743, 4, 2346, 5, + 98, 1810, 2027, 2037, 2044, 1818, 1855, 232, 225, 1829, + 744, 1302, 1982, 1983, 1984, 99, 1985, 745, 2271, 6, + 7, 8, 9, 235, 1862, 229, 2014, 1875, 2040, 225, + 868, 869, 1877, 100, 1027, 746, 1879, 230, 101, 1888, + 102, 2024, 747, 1880, 2116, 1287, 237, 1894, 1027, 1897, + 225, 1895, 1898, 2285, 230, 1908, 1912, 1935, 1937, 1937, + 1940, 421, 1915, 1917, 232, 2084, 881, 1441, 1930, 233, + 237, 1525, 1526, 1527, 1528, 230, 422, 1931, 2340, 1942, + 1958, 232, 2300, 1972, 229, 1951, 230, 2022, 421, 1968, + 1318, 226, 859, 1995, 2001, 2116, 2003, 1043, 225, 1027, + 2002, 2015, 232, 422, 2016, 236, 2018, 2047, 1043, 1043, + 2057, 233, 881, 232, -569, 1315, 238, 1027, 1027, 2091, + 2065, 2067, 1029, 2120, 2071, 2121, 2122, 2023, 2139, 2079, + 2083, 2095, 2096, 2344, 2101, 2099, 235, 2118, 235, 2126, + 238, 2145, 2135, 2143, 1270, 2128, 2152, 2154, 2161, 2165, + 230, 2149, 2130, 2131, 2157, 225, 2134, 2166, 2175, 233, + 2178, 2155, 2138, 1530, 1531, 1532, 1533, 1534, 2158, 2026, + 2036, 2043, 225, 2181, 2182, 3, 2183, 232, 4, 2184, + 5, 2185, 881, 2189, 2190, 2191, 2027, 2037, 2044, 2199, + 235, 2192, 2041, 225, 2229, 2231, 2241, 237, 1837, 2243, + 6, 7, 8, 9, 225, 2245, 2249, 2197, 2257, 230, + 2059, 2060, 2061, 2062, 2063, 1778, 233, 2258, 1779, 229, + 2266, 2259, 2269, 1401, 849, 849, -287, 2277, 2223, 2280, + 2313, -288, 2281, 233, 2311, 2312, 232, 1029, 1029, 1029, + 1029, 1029, 226, 2331, 1029, 1425, 1423, 1639, 403, 1359, + 1249, 1424, 2200, 1248, 233, 1247, 229, 1493, 1138, 1250, + 2180, 1803, 2207, 881, 72, 233, 1134, 238, 225, 1427, + 1029, 2024, 3, 809, 2198, 4, 810, 5, 3, 2141, + 1066, 4, 229, 5, 1786, 1997, 2303, 2201, 2203, 1029, + 2260, 2262, 229, 2261, 2263, 2224, 2225, 6, 7, 8, + 9, 2252, 2332, 6, 7, 8, 9, 2234, 2235, 1939, + 2236, 2237, 2238, 2239, 2240, 817, 2098, 2242, 1778, 1063, + 1352, 1779, 1339, 2085, 2068, 2070, 1394, 225, 1065, 233, + 1485, 2251, 2289, 235, 1667, 1669, 1668, 1965, 1780, 1686, + 1402, 1882, 881, 847, 230, 324, 325, 326, 1889, 1890, + 329, 608, 609, 1320, 1319, 1488, 1885, 2197, 2322, 1408, + 1042, 788, 1689, 1709, 2268, 1643, 610, 229, 611, 612, + 613, 232, 1864, 2275, 1719, 235, 1736, 1747, 1763, 1776, + 1900, 230, 1054, 1903, 1076, 2040, 2337, 1077, 233, 1078, + 857, 614, 615, 616, 1079, 1214, 861, 2330, 862, 236, + 1186, 1180, 2308, 1536, 0, 0, 0, 230, 232, 0, + 0, 1717, 1742, 1760, 1773, 0, 229, 230, 0, 1831, + 1838, 1843, 1849, 235, 2116, 0, 2307, 0, 1718, 1743, + 1761, 1774, 229, 0, 232, 2310, 1832, 1839, 1844, 1850, + 0, 1780, 0, 2316, 232, 403, 0, 1231, 1232, 2317, + 2318, 2319, 2320, 2321, 2026, 2036, 2043, 2325, 2326, 2327, + 2328, 1233, 225, 0, 0, 0, 1781, 1555, 1556, 1234, + 0, 2027, 2037, 2044, 2338, 1042, 1042, 1042, 1042, 1042, + 235, 0, 1042, 0, 0, 1235, 0, 0, 0, 0, + 1833, 237, 230, 1840, 0, 1845, 0, 235, 1851, 225, + 2350, 2351, 2352, 1027, 0, 0, 3, 0, 1042, 4, + 0, 5, 236, 0, 236, 0, 0, 0, 235, 232, + 0, 0, 0, 233, 1607, 225, 0, 1042, 0, 235, + 500, 6, 7, 8, 9, 225, 226, 0, 0, 2021, + 0, 230, 573, 574, 0, 18, 19, 0, 0, 2041, + 0, 575, 576, 577, 578, 579, 0, 230, 1639, 0, + 233, 238, 280, 0, 0, 0, 236, 0, 232, 1781, + 281, 0, 282, 0, 283, 75, 1043, 284, 285, 286, + 0, 287, 0, 76, 232, 77, 233, 0, 0, 0, + 78, 79, 80, 235, 81, 35, 233, 1654, 1654, 0, + 1654, 0, 0, 0, 237, 36, 237, 0, 0, 0, + 225, 37, 0, 1236, 1237, 1238, 767, 0, 0, 0, + 881, 0, 0, 0, 1374, 0, 0, 0, 0, 777, + 778, 779, 0, 0, 0, 38, 2324, 0, 0, 0, + 0, 0, 2294, 0, 0, 0, 0, 0, 502, 226, + 0, 226, 235, 0, 0, 0, 0, 504, 237, 225, + 0, 0, 0, 18, 19, 0, 0, 0, 0, 0, + 0, 233, 18, 19, 238, 225, 238, 0, 1029, 1029, + 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 0, + 0, 1043, 1043, 1043, 1043, 1043, 0, 0, 1043, 0, + 0, 0, 0, 226, 1736, 1747, 1763, 1776, 0, 236, + 0, 1800, 0, 35, 1801, 0, 1747, 0, 0, 0, + 233, 0, 35, 36, 1043, 906, 0, 0, 407, 37, + 236, 907, 36, 0, 0, 0, 233, 0, 37, 506, + 0, 0, 0, 1043, 2028, 2038, 2045, 0, 1282, 0, + 1284, 236, 0, 38, 18, 19, 0, 0, 0, 158, + 159, 160, 38, 0, 0, 0, 1008, 1301, 0, 1305, + 1009, 1309, 0, 0, 10, 1010, 0, 1321, 1322, 0, + 0, 1011, 11, 0, 12, 914, 13, 235, 0, 14, + 15, 16, 0, 17, 0, 1825, 0, 18, 19, 236, + 0, 237, 288, 0, 35, 1014, 0, 0, 0, 1357, + 0, 0, 1361, 0, 36, 82, 1270, 289, 0, 0, + 37, 0, 237, 0, 235, 0, 0, 0, 0, 0, + 83, 0, 0, 0, 1867, 290, 1868, 1869, 0, 1871, + 291, 0, 292, 237, 38, 0, 226, 35, 84, 0, + 235, 0, 0, 85, 881, 86, 236, 36, 0, 0, + 235, 0, 0, 37, 0, 1884, 511, 1315, 1401, 0, + 2299, 407, 1029, 236, 0, 0, 0, 1398, 0, 0, + 0, 18, 19, 0, 0, 0, 1029, 38, 226, 0, + 0, 237, 238, 0, 236, 0, 1654, 0, 0, 1654, + 0, 0, 1654, 0, 0, 236, 0, 0, 0, 0, + 0, 0, 0, 407, 0, 0, 1042, 1042, 1042, 1042, + 1042, 1042, 1042, 1042, 1042, 1042, 1042, 0, 2028, 2038, + 2045, 35, 0, 0, 0, 235, 226, 1029, 0, 0, + 0, 36, 917, 0, 0, 918, 919, 37, 237, 0, + 1448, 1449, 920, 0, 0, 1029, 1029, 0, 581, 582, + 0, 407, 583, 0, 0, 237, 0, 0, 0, 236, + 0, 38, 0, 584, 585, 586, 587, 588, 589, 590, + 591, 592, 922, 923, 235, 0, 237, 0, 0, 0, + 0, 0, 1162, 226, 106, 0, 0, 237, 0, 107, + 235, 0, 108, 109, 110, 111, 0, 1163, 112, 113, + 226, 114, 115, 116, 39, 117, 0, 0, 238, 0, + 0, 0, 0, 593, 221, 0, 0, 0, 236, 40, + 0, 226, 0, 0, 0, 238, 0, 0, 0, 0, + 0, 0, 226, 0, 0, 0, 0, 41, 0, 0, + 0, 0, 42, 0, 43, 118, 407, 119, 120, 121, + 0, 237, 0, 0, 0, 0, 3, 407, 0, 4, + 0, 5, 1164, 1374, 1374, 1374, 1374, 1374, 0, 1374, + 1374, 0, 0, 618, 619, 0, 620, 0, 1682, 1682, + 0, 6, 7, 8, 9, 0, 0, 0, 2058, 0, + 0, 0, 1721, 0, 0, 621, 226, 0, 0, 0, + 1042, 0, 1722, 622, 623, 0, 624, 1723, 0, 1724, + 237, 1725, 0, 0, 1042, 0, 625, 626, 627, 0, + 0, 238, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, + 1043, 1043, 1043, 0, 1165, 0, 0, 1166, 1167, 1168, + 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176, 1177, 1178, + 0, 0, 0, 236, 0, 226, 0, 0, 0, 0, + 1719, 1747, 1763, 1776, 0, 1042, 0, 0, 1833, 1840, + 1845, 1851, 0, 1582, 0, 0, 0, 1585, 0, 0, + 238, 0, 0, 1042, 1042, 0, 0, 1604, 1605, 1606, + 236, 0, 1609, 0, 1611, 0, 0, 0, 0, 0, + 0, 0, 0, 2028, 2038, 2045, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 236, 1626, 0, 1726, + 0, 0, 0, 0, 122, 0, 236, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 123, + 0, 0, 0, 2150, 0, 237, 124, 125, 126, 127, + 0, 0, 0, 2156, 1660, 0, 1662, 128, 0, 0, + 2159, 0, 129, 1663, 130, 131, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1676, 0, + 1677, 0, 237, 0, 0, 0, 0, 0, 161, 1685, + 226, 1687, 0, 0, 0, 2164, 0, 0, 0, 0, + 0, 236, 0, 1592, 0, 165, 0, 0, 237, 0, + 0, 0, 0, 0, 0, 407, 1043, 0, 237, 0, + 0, 166, 0, 0, 0, 0, 167, 226, 0, 168, + 1043, 1029, 1727, 0, 169, 0, 0, 0, 170, 0, + 0, 171, 172, 0, 0, 0, 173, 1728, 0, 174, + 236, 175, 238, 226, 0, 0, 0, 2226, 2227, 2228, + 0, 0, 0, 226, 0, 1729, 236, 0, 1164, 0, + 1730, 0, 1731, 0, 0, 0, 176, 0, 238, 0, + 0, 1043, 181, 2244, 0, 0, 0, 0, 238, 0, + 0, 0, 0, 237, 0, 0, 0, 0, 0, 1043, + 1043, 0, 3, 0, 881, 4, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1732, 0, 0, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 0, 0, 0, 0, 226, 0, + 1593, 1749, 237, 1166, 1167, 1594, 1169, 1170, 1171, 1172, + 1173, 1174, 1175, 1176, 1177, 1595, 0, 0, 237, 0, + 0, 0, 0, 238, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1853, 1854, 0, + 0, 0, 0, 0, 0, 0, 0, 226, 0, 0, + 1750, 1751, 1752, 1753, 0, 1754, 0, 0, 1755, 0, + 0, 0, 0, 226, 1870, 652, 0, 0, 0, 0, + 51, 0, 407, 0, 653, 1756, 0, 0, 0, 1887, + 63, 51, 51, 66, 66, 66, 70, 0, 238, 51, + 654, 858, 0, 0, 655, 0, 0, 0, 656, 657, + 2341, 2342, 2343, 658, 659, 660, 661, 662, 0, 0, + 0, 0, 0, 759, 760, 1914, 0, 761, 0, 0, + 0, 0, 0, 0, 0, 1757, 0, 0, 0, 1042, + 0, 0, 1916, 0, 1918, 762, 0, 0, 0, 0, + 0, 0, 51, 0, 0, 429, 51, 66, 66, 66, + 70, 0, 0, 0, 0, 0, 0, 51, 0, 444, + 449, 452, 51, 457, 66, 66, 462, 0, 444, 444, + 444, 444, 0, 66, 0, 0, 0, 478, 0, 51, + 0, 0, 66, 66, 0, 70, 0, 0, 66, 0, + 0, 0, 859, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 51, 518, 457, 66, 457, 0, 526, 529, + 0, 0, 533, 51, 66, 66, 0, 0, 66, 0, + 51, 0, 0, 0, 3, 0, 0, 4, 0, 5, + 0, 0, 0, 0, 0, 0, 0, 0, 1758, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, + 7, 8, 9, 334, 0, 763, 0, 0, 335, 0, + 0, 0, 0, 1749, 0, 0, 0, 0, 0, 0, + 0, 337, 0, 0, 0, 0, 338, 0, 0, 764, + 765, 766, 767, 768, 769, 860, 770, 771, 772, 773, + 774, 775, 776, 0, 0, 777, 778, 779, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2055, + 881, 0, 1750, 1751, 1752, 1753, 0, 1754, 780, 0, + 1755, 0, 0, 0, 0, 0, 1759, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1756, 0, 0, + 0, 0, 2075, 0, 0, 1043, 0, 0, 0, 444, + 449, 452, 51, 457, 66, 66, 462, 0, 444, 444, + 444, 444, 0, 3, 0, 0, 4, 0, 5, 2090, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 153, 0, 1757, 6, 7, + 8, 9, 154, 155, 0, 0, 0, 317, 156, 318, + 0, 0, 0, 0, 0, 0, 1737, 0, 0, 0, + 0, 0, 0, 0, 319, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 320, 321, 0, 0, 0, 322, + 0, 0, 323, 0, 0, 0, 0, 0, 0, 0, + 0, 324, 325, 326, 327, 328, 329, 0, 0, 0, + 0, 0, 0, 161, 0, 162, 1738, 0, 0, 163, + 330, 0, 331, 0, 164, 157, 158, 159, 160, 0, + 165, 161, 0, 162, 0, 0, 1739, 163, 0, 0, + 0, 0, 164, 0, 0, 0, 166, 0, 165, 0, + 0, 167, 0, 0, 168, 0, 0, 0, 0, 169, + 1758, 0, 0, 170, 166, 0, 171, 172, 669, 167, + 0, 173, 168, 0, 174, 334, 175, 169, 0, 0, + 335, 170, 0, 0, 171, 172, 1740, 0, 0, 173, + 0, 0, 174, 337, 175, 0, 0, 0, 338, 0, + 0, 176, 0, 670, 0, 2174, 0, 181, 0, 0, + 0, 1099, 0, 0, 0, 0, 0, 0, 66, 176, + 177, 0, 178, 179, 180, 181, 0, 0, 0, 1105, + 0, 1106, 881, 1107, 0, 1108, 0, 0, 0, 0, + 1112, 0, 0, 0, 1113, 51, 0, 0, 2007, 0, + 0, 0, 0, 0, 1117, 0, 0, 1118, 0, 0, + 182, 183, 184, 185, 0, 0, 0, 66, 0, 186, + 187, 0, 0, 188, 189, 332, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, + 205, 206, 207, 208, 209, 210, 211, 0, 0, 333, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 334, 0, 0, 0, 0, 335, + 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, + 0, 0, 337, 0, 0, 0, 0, 338, 0, 0, + 0, 0, 0, 213, 214, 0, 339, 0, 0, 340, + 341, 342, 343, 0, 0, 0, 344, 0, 0, 0, + 345, 346, 215, 0, 0, 0, 216, 217, 0, 0, + 0, 881, 0, 0, 0, 347, 0, 3, 218, 219, + 4, 0, 5, 0, 348, 0, 349, 1741, 220, 0, + 0, 221, 0, 0, 0, 0, 0, 0, 0, 153, + 0, 0, 6, 7, 8, 9, 154, 155, 0, 0, + 0, 317, 156, 318, 0, 0, 0, 0, 0, 0, + 1737, 0, 0, 0, 0, 0, 0, 0, 319, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 320, 321, + 0, 0, 0, 322, 0, 0, 323, 0, 0, 0, + 0, 0, 0, 0, 0, 324, 325, 326, 327, 328, + 329, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1738, 0, 0, 0, 330, 0, 331, 0, 0, 157, + 158, 159, 160, 0, 0, 161, 0, 162, 0, 0, + 1739, 163, 0, 0, 3, 0, 164, 4, 0, 5, + 0, 0, 165, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 166, 6, + 7, 8, 9, 167, 0, 0, 168, 1189, 0, 0, + 0, 169, 0, 1749, 0, 170, 0, 0, 171, 172, + 1740, 0, 0, 173, 0, 0, 174, 0, 175, 0, + 1190, 1191, 1192, 1193, 1194, 1195, 1196, 1197, 1198, 1199, + 1200, 1201, 1202, 1203, 1204, 1205, 1206, 0, 0, 0, + 0, 0, 0, 176, 177, 0, 178, 179, 180, 181, + 0, 0, 1750, 1751, 1752, 1753, 0, 1754, 0, 0, + 1755, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1756, 0, 0, + 0, 0, 0, 0, 182, 183, 184, 185, 0, 0, + 0, 0, 0, 186, 187, 0, 0, 188, 189, 332, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 0, 0, 333, 0, 0, 0, 1757, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 334, 0, + 0, 0, 0, 335, 0, 0, 0, 0, 0, 0, + 336, 0, 0, 0, 0, 0, 337, 0, 0, 0, + 0, 338, 0, 0, 0, 0, 0, 213, 214, 0, + 339, 0, 0, 340, 341, 342, 343, 0, 0, 0, + 344, 0, 0, 0, 345, 346, 215, 0, 0, 0, + 216, 217, 0, 0, 0, 881, 0, 0, 0, 347, + 0, 3, 218, 219, 4, 0, 5, 0, 348, 0, + 349, 2006, 220, 0, 0, 221, 0, 0, 0, 0, + 0, 0, 0, 153, 0, 0, 6, 7, 8, 9, + 154, 155, 0, 0, 0, 317, 156, 318, 0, 0, + 1758, 0, 0, 0, 1737, 0, 0, 0, 0, 0, + 0, 0, 319, 0, 0, 334, 0, 0, 0, 0, + 335, 0, 320, 321, 0, 0, 0, 322, 0, 0, + 323, 0, 0, 337, 0, 0, 0, 0, 338, 324, + 325, 326, 327, 328, 329, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1738, 0, 0, 0, 330, 0, + 331, 0, 1486, 157, 158, 159, 160, 0, 0, 161, + 0, 162, 881, 0, 1739, 163, 0, 0, 3, 0, + 164, 4, 0, 5, 0, 0, 165, 0, 2292, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 166, 6, 7, 8, 9, 167, 0, 0, + 168, 0, 0, 0, 0, 169, 0, 1766, 0, 170, + 0, 0, 171, 172, 1740, 0, 0, 173, 0, 0, + 174, 0, 175, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 176, 177, 0, + 178, 179, 180, 181, 0, 0, 0, 1767, 1768, 1769, + 0, 1754, 0, 0, 1755, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1770, 0, 0, 0, 0, 0, 0, 182, 183, + 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, + 0, 188, 189, 332, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 0, 0, 333, 0, 0, + 0, 1771, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 334, 0, 0, 0, 0, 335, 0, 0, + 0, 0, 0, 0, 336, 0, 0, 0, 0, 0, + 337, 0, 0, 0, 0, 338, 0, 0, 0, 0, + 0, 213, 214, 0, 339, 0, 0, 340, 341, 342, + 343, 0, 0, 0, 344, 0, 0, 0, 345, 346, + 215, 0, 0, 0, 216, 217, 0, 0, 0, 881, + 0, 0, 0, 347, 0, 3, 218, 219, 4, 0, + 5, 0, 348, 0, 349, 2013, 220, 0, 0, 221, + 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, + 6, 7, 8, 9, 154, 155, 0, 0, 0, 317, + 156, 318, 0, 0, 1758, 0, 0, 0, 1737, 0, + 0, 0, 0, 0, 0, 0, 319, 0, 0, 334, + 0, 0, 0, 0, 335, 0, 320, 321, 0, 0, + 0, 322, 0, 0, 323, 0, 0, 337, 0, 0, + 0, 0, 338, 324, 325, 326, 327, 328, 329, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1738, 0, + 0, 0, 330, 0, 331, 0, 0, 157, 158, 159, + 160, 0, 0, 161, 0, 162, 881, 0, 1739, 163, + 0, 0, 3, 0, 164, 4, 0, 5, 0, 0, + 165, 0, 1772, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 166, 6, 7, 8, + 9, 167, 0, 0, 168, 0, 0, 0, 0, 169, + 0, 1766, 0, 170, 0, 0, 171, 172, 1740, 0, + 0, 173, 0, 0, 174, 0, 175, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 179, 180, 181, 0, 0, + 0, 1767, 1768, 1769, 0, 1754, 0, 0, 1755, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1770, 0, 0, 0, 0, + 0, 0, 182, 183, 184, 185, 0, 0, 0, 0, + 0, 186, 187, 0, 0, 188, 189, 332, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 0, + 0, 333, 0, 0, 0, 1771, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 334, 0, 0, 0, + 0, 335, 0, 0, 0, 0, 0, 0, 336, 0, + 0, 0, 0, 0, 337, 0, 0, 0, 0, 338, + 0, 0, 0, 0, 0, 213, 214, 0, 339, 0, + 0, 340, 341, 342, 343, 0, 0, 0, 344, 0, + 0, 0, 345, 346, 215, 4, 0, 5, 216, 217, + 0, 0, 0, 881, 0, 0, 0, 347, 0, 0, + 218, 219, 0, 0, 153, 0, 348, 0, 349, 2291, + 220, 154, 155, 221, 0, 0, 317, 156, 318, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 319, 0, 0, 0, 0, 1758, 0, + 0, 0, 0, 320, 321, 0, 0, 749, 322, 0, + 0, 323, 0, 334, 0, 0, 0, 0, 335, 0, + 324, 325, 326, 327, 328, 329, 0, 0, 0, 0, + 0, 337, 0, 0, 750, 0, 338, 751, 0, 330, + 0, 331, 0, 0, 157, 158, 159, 160, 0, 0, + 161, 0, 162, 0, 752, 0, 163, 0, 0, 0, + 0, 164, 0, 0, 0, 0, 0, 165, 0, 0, + 881, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 166, 0, 0, 2008, 0, 167, 0, + 0, 168, 0, 0, 3, 0, 169, 4, 0, 5, + 170, 0, 0, 171, 172, 0, 0, 0, 173, 0, + 0, 174, 0, 175, 0, 0, 0, 0, 0, 6, + 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, + 1721, 0, 0, 0, 0, 0, 0, 0, 176, 177, + 1722, 178, 179, 180, 181, 1723, 0, 1724, 0, 1725, + 0, 0, 0, 0, 0, 0, 0, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 0, 182, + 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, + 0, 0, 188, 189, 332, 191, 192, 193, 194, 195, + 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, + 206, 207, 208, 209, 210, 211, 0, 0, 333, 0, + 753, 0, 905, 0, 213, 214, 0, 0, 0, 0, + 0, 0, 0, 334, 754, 0, 0, 0, 335, 0, + 153, 0, 0, 0, 0, 336, 0, 0, 155, 0, + 0, 337, 0, 156, 0, 0, 338, 1726, 0, 0, + 755, 0, 213, 214, 0, 339, 0, 0, 340, 341, + 342, 343, 0, 0, 0, 344, 0, 0, 0, 345, + 346, 215, 0, 0, 0, 216, 217, 906, 0, 0, + 0, 0, 0, 907, 347, 0, 0, 218, 219, 0, + 0, 0, 908, 348, 909, 349, 1904, 220, 0, 0, + 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 0, 0, 161, 0, 910, 0, + 0, 0, 911, 0, 0, 0, 0, 912, 0, 0, + 0, 0, 0, 913, 0, 0, 0, 914, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 166, + 0, 0, 0, 0, 167, 0, 0, 915, 0, 0, + 1727, 0, 169, 0, 0, 0, 170, 0, 0, 171, + 172, 0, 0, 0, 173, 1728, 0, 174, 0, 175, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1729, 0, 0, 0, 0, 1730, 0, + 1731, 0, 0, 0, 176, 177, 0, 178, 179, 180, + 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1279, 0, 0, 0, 0, 0, 759, 760, 0, + 0, 761, 881, 0, 0, 0, 0, 0, 0, 153, + 0, 0, 0, 0, 0, 916, 0, 155, 2005, 762, + 0, 0, 156, 0, 0, 0, 0, 0, 0, 0, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 0, 0, 212, 0, 906, 0, 0, 0, + 0, 0, 907, 0, 917, 0, 0, 918, 919, 0, + 0, 0, 0, 0, 920, 0, 0, 0, 0, 0, + 0, 0, 921, 0, 0, 0, 0, 0, 0, 157, + 158, 159, 160, 0, 0, 161, 0, 910, 213, 214, + 0, 911, 0, 0, 922, 923, 912, 0, 0, 0, + 0, 0, 1280, 0, 0, 0, 914, 215, 0, 0, + 0, 216, 217, 0, 0, 0, 0, 0, 166, 0, + 0, 924, 925, 167, 755, 0, 1281, 0, 0, 763, + 0, 169, 0, 926, 0, 170, 221, 0, 171, 172, + 0, 0, 0, 173, 0, 0, 174, 0, 175, 0, + 0, 0, 0, 764, 765, 766, 767, 768, 769, 0, + 770, 771, 772, 773, 774, 775, 776, 0, 0, 777, + 778, 779, 0, 176, 177, 0, 178, 179, 180, 181, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1360, 0, 780, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 153, 0, + 0, 0, 0, 0, 916, 0, 155, 0, 0, 0, + 0, 156, 0, 0, 0, 0, 0, 0, 0, 190, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 0, 0, 212, 0, 906, 0, 0, 0, 0, + 0, 907, 0, 917, 0, 0, 918, 919, 0, 0, + 0, 0, 0, 920, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 157, 158, + 159, 160, 0, 0, 161, 0, 910, 213, 214, 0, + 911, 0, 0, 922, 923, 912, 0, 0, 161, 0, + 162, 1280, 0, 698, 163, 914, 215, 0, 0, 164, + 216, 217, 0, 0, 0, 165, 0, 166, 0, 0, + 924, 925, 167, 755, 0, 1281, 0, 0, 0, 0, + 169, 166, 926, 0, 170, 221, 167, 171, 172, 168, + 0, 0, 173, 0, 169, 174, 0, 175, 170, 0, + 0, 171, 172, 0, 0, 0, 173, 0, 0, 174, + 0, 175, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 176, 177, 0, 178, 179, 180, 181, 0, + 0, 0, 0, 0, 0, 0, 176, 0, 699, 1397, + 0, 0, 181, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 153, 0, 0, + 0, 0, 0, 916, 0, 155, 0, 0, 0, 0, + 156, 0, 0, 0, 0, 0, 0, 0, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 0, 0, 212, 0, 906, 0, 0, 0, 0, 0, + 907, 0, 917, 0, 0, 918, 919, 0, 0, 0, + 0, 0, 920, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 157, 158, 159, + 160, 0, 0, 161, 0, 910, 213, 214, 0, 911, + 0, 0, 922, 923, 912, 0, 0, 161, 0, 162, + 1280, 0, 0, 163, 914, 215, 0, 0, 164, 216, + 217, 0, 0, 0, 165, 0, 166, 0, 0, 924, + 925, 167, 755, 0, 1281, 0, 0, 0, 0, 169, + 166, 926, 0, 170, 221, 167, 171, 172, 168, 0, + 0, 173, 0, 169, 174, 0, 175, 170, 0, 0, + 171, 172, 0, 0, 0, 173, 0, 0, 174, 0, + 175, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 176, 177, 0, 178, 179, 180, 181, 0, 0, + 0, 0, 0, 0, 0, 176, 0, 0, 1584, 0, + 0, 181, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 153, 0, 0, 0, + 0, 0, 916, 0, 155, 0, 0, 0, 0, 156, + 0, 0, 0, 0, 0, 0, 0, 190, 191, 192, + 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, + 203, 204, 205, 206, 207, 208, 209, 210, 211, 0, + 0, 212, 0, 906, 0, 0, 0, 0, 0, 907, + 0, 917, 0, 0, 918, 919, 0, 0, 0, 0, + 0, 920, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 157, 158, 159, 160, + 0, 0, 161, 0, 910, 213, 214, 0, 911, 0, + 0, 922, 923, 912, 0, 0, 0, 0, 0, 1280, + 0, 0, 0, 914, 215, 0, 0, 0, 216, 217, + 0, 0, 0, 0, 0, 166, 0, 0, 924, 925, + 167, 755, 0, 1281, 0, 0, 0, 0, 169, 0, + 926, 0, 170, 221, 0, 171, 172, 0, 0, 0, + 173, 0, 0, 174, 0, 175, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 4, 0, 5, 0, 0, + 176, 177, 0, 178, 179, 180, 181, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 7, 8, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1766, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 916, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 0, 0, + 212, 1767, 1768, 1769, 0, 1754, 0, 0, 1755, 0, + 917, 0, 0, 918, 919, 0, 0, 0, 0, 0, + 920, 0, 0, 0, 0, 1770, 1001, 1002, 1003, 1004, + 1005, 1006, 0, 0, 0, 0, 1007, 0, 0, 0, + 986, 0, 0, 0, 213, 214, 0, 0, 0, 0, + 922, 923, 0, 0, 0, 0, 1325, 0, 0, 0, + 0, 0, 0, 215, 0, 0, 0, 216, 217, 0, + 0, 0, 0, 0, 0, 1771, 0, 924, 925, 0, + 755, 0, 0, 0, 0, 0, 0, 906, 0, 926, + 0, 0, 221, 907, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 906, 0, 0, 0, 0, + 0, 907, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 0, 0, 0, 0, 1008, 0, + 0, 0, 1009, 0, 0, 0, 0, 1010, 157, 158, + 159, 160, 0, 1011, 0, 0, 1008, 914, 0, 0, + 1009, 0, 0, 0, 0, 1010, 0, 0, 0, 1012, + 1325, 1011, 0, 0, 1013, 914, 0, 1014, 0, 0, + 0, 0, 1015, 0, 0, 0, 1016, 1012, 0, 1017, + 1018, 0, 1013, 0, 1019, 1014, 0, 1020, 1758, 1021, + 1015, 0, 0, 0, 1016, 0, 0, 1017, 1018, 0, + 0, 0, 1019, 334, 0, 1020, 0, 1021, 335, 906, + 0, 0, 0, 0, 1022, 907, 0, 1023, 0, 0, + 1024, 337, 0, 0, 0, 0, 338, 0, 0, 0, + 0, 0, 1022, 0, 0, 1023, 0, 0, 1024, 0, + 0, 0, 157, 158, 159, 160, 0, 0, 0, 0, + 1008, 0, 0, 0, 1009, 0, 0, 0, 0, 1010, + 881, 0, 0, 0, 0, 1011, 0, 0, 0, 914, + 0, 0, 0, 0, 0, 0, 2293, 0, 0, 0, + 0, 1012, 0, 0, 0, 0, 1013, 0, 0, 1014, + 0, 0, 0, 0, 1015, 0, 0, 0, 1016, 0, + 0, 1017, 1018, 0, 917, 0, 1019, 918, 919, 1020, + 0, 1021, 0, 0, 920, 0, 0, 0, 0, 0, + 0, 0, 917, 0, 0, 918, 919, 0, 0, 0, + 0, 0, 920, 0, 0, 0, 1022, 0, 0, 1023, + 0, 0, 1024, 0, 922, 923, 0, 1025, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 922, 923, 0, 1025, 0, 0, 0, 0, + 0, 924, 925, 0, 0, 0, 0, 0, 0, 1048, + 986, 349, 0, 1026, 0, 0, 221, 0, 0, 924, + 925, 0, 0, 0, 0, 0, 0, 0, 0, 349, + 0, 1026, 0, 0, 221, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 917, 906, 0, 918, + 919, 0, 0, 907, 0, 0, 920, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 0, 0, 922, 923, 1008, 1025, + 0, 0, 1009, 0, 0, 0, 0, 1010, 0, 0, + 0, 0, 0, 1011, 0, 0, 0, 914, 0, 0, + 0, 0, 0, 924, 925, 0, 0, 0, 0, 1012, + 0, 0, 0, 0, 1013, 1026, 0, 1014, 221, 0, + 0, 0, 1015, 0, 0, 0, 1016, 0, 0, 1017, + 1018, 0, 0, 0, 1019, 0, 153, 1020, 0, 1021, + 0, 0, 0, 154, 155, 0, 0, 0, 317, 156, + 318, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1022, 319, 0, 1023, 0, 0, + 1024, 0, 0, 0, 0, 320, 321, 0, 0, 0, + 322, 0, 0, 323, 0, 0, 0, 0, 0, 0, + 0, 0, 324, 325, 326, 327, 328, 329, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 330, 0, 331, 0, 0, 157, 158, 159, 160, + 0, 0, 161, 0, 162, 0, 0, 0, 163, 0, + 0, 0, 0, 164, 0, 0, 0, 0, 0, 165, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 917, 166, 0, 918, 919, 0, + 167, 0, 0, 168, 920, 0, 0, 0, 169, 0, + 0, 0, 170, 0, 0, 171, 172, 0, 0, 0, + 173, 0, 0, 174, 0, 175, 0, 0, 0, 0, + 0, 0, 0, 0, 922, 923, 0, 1025, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 176, 177, 0, 178, 179, 180, 181, 0, 0, 0, + 0, 924, 925, 0, 0, 0, 0, 0, 0, 0, + 0, 1049, 0, 1026, 0, 0, 221, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 182, 183, 184, 185, 0, 0, 0, 0, 0, + 186, 187, 0, 0, 188, 189, 332, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 0, 0, + 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 153, 0, 334, 0, 0, 0, 0, + 335, 155, 0, 0, 0, 0, 156, 336, 0, 0, + 0, 0, 0, 337, 0, 0, 0, 0, 338, 0, + 0, 0, 0, 0, 213, 214, 0, 339, 0, 0, + 340, 341, 342, 343, 0, 0, 0, 344, 0, 0, + 906, 345, 346, 215, 0, 0, 907, 216, 217, 0, + 0, 0, 0, 0, 0, 0, 347, 0, 0, 218, + 219, 0, 0, 0, 0, 348, 0, 349, 0, 220, + 0, 0, 221, 157, 158, 159, 160, 0, 0, 161, + 0, 910, 0, 0, 0, 911, 0, 0, 0, 0, + 912, 0, 0, 0, 0, 0, 1280, 0, 0, 0, + 914, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 166, 0, 0, 0, 0, 167, 0, 0, + 1281, 0, 0, 0, 0, 169, 0, 0, 0, 170, + 0, 0, 171, 172, 0, 0, 0, 173, 0, 0, + 174, 0, 175, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 176, 177, 0, + 178, 179, 180, 181, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 153, 0, 0, 0, 0, 0, 916, 154, + 155, 0, 0, 0, 0, 156, 0, 0, 0, 0, + 0, 0, 0, 190, 191, 192, 193, 194, 195, 196, + 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 0, 0, 212, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 917, 0, 0, + 918, 919, 0, 0, 0, 0, 0, 920, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 157, 158, 159, 160, 0, 0, 161, 0, + 162, 213, 214, 0, 163, 0, 0, 922, 923, 164, + 0, 0, 0, 0, 0, 165, 0, 0, 0, 0, + 215, 0, 0, 0, 216, 217, 0, 0, 0, 0, + 0, 166, 0, 0, 924, 925, 167, 755, 0, 168, + 0, 0, 0, 0, 169, 0, 926, 0, 170, 221, + 0, 171, 172, 0, 0, 0, 173, 0, 0, 174, + 0, 175, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 176, 177, 0, 178, + 179, 180, 181, 0, 0, 0, 0, 0, 0, 0, + 153, 0, 0, 0, 0, 0, 0, 154, 155, 0, + 0, 0, 0, 156, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 182, 183, 184, + 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, + 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, + 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 2204, 0, 212, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 157, 158, 159, 160, 0, 0, 161, 0, 162, 0, + 0, 0, 163, 0, 0, 0, 0, 164, 0, 0, + 0, 0, 0, 165, 0, 0, 0, 0, 0, 0, + 213, 214, 0, 0, 0, 0, 0, 0, 0, 166, + 0, 0, 0, 0, 167, 0, 0, 168, 0, 215, + 0, 0, 169, 216, 217, 0, 170, 0, 0, 171, + 172, 0, 0, 0, 173, 218, 219, 174, 0, 175, + 0, 0, 0, 349, 0, 220, 0, 0, 221, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 176, 177, 153, 178, 179, 180, + 181, 0, 0, 154, 155, 0, 0, 0, 0, 156, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 182, 183, 184, 185, 0, + 0, 0, 0, 0, 186, 187, 0, 0, 188, 189, + 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 0, 0, 212, 0, 157, 158, 159, 160, + 0, 0, 161, 0, 162, 0, 0, 0, 163, 0, + 0, 0, 0, 164, 0, 0, 0, 0, 0, 165, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 166, 0, 0, 213, 214, + 167, 0, 0, 168, 0, 0, 0, 0, 169, 0, + 0, 0, 170, 0, 0, 171, 172, 215, 0, 0, + 173, 216, 217, 174, 0, 175, 0, 0, 0, 0, + 0, 0, 0, 218, 219, 0, 0, 0, 0, 0, + 0, 0, 0, 220, 0, 0, 221, 0, 0, 0, + 176, 177, 0, 178, 179, 180, 181, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 986, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 182, 183, 184, 185, 0, 0, 0, 0, 0, + 186, 187, 0, 0, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 906, 0, + 212, 0, 0, 0, 907, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 157, 158, 159, 160, 0, 0, 0, 0, 1008, + 0, 0, 0, 1009, 213, 214, 0, 0, 1010, 0, + 0, 0, 0, 0, 1011, 0, 0, 0, 914, 0, + 0, 0, 0, 215, 0, 0, 0, 216, 217, 0, + 1012, 0, 0, 0, 0, 1013, 0, 0, 1014, 218, + 219, 0, 0, 1015, 0, 0, 0, 1016, 0, 220, + 1017, 1018, 221, 0, 0, 1019, 0, 0, 1020, 0, + 1021, 0, 0, 0, 0, 0, 0, 2, 3, 0, + 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1022, 0, 0, 1023, 0, + 0, 1024, 0, 6, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 11, 0, 12, 0, 13, 0, 0, 14, + 15, 16, 0, 17, 0, 0, 0, 18, 19, 20, + 0, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -432, 0, 0, 0, 917, 0, 35, 918, 919, + 0, 0, 0, 0, 0, 920, 0, 36, 906, 0, + 0, 0, 0, 37, 907, 0, 0, 324, 325, 326, + 1889, 1890, 329, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 922, 923, 38, 1025, 0, + 0, 157, 158, 159, 160, 0, 0, 0, 0, 1008, + 0, 0, 0, 1009, 0, 0, 0, 0, 1010, 0, + 0, 0, 924, 925, 1011, 0, 0, 0, 914, 0, + 0, 0, 349, 0, 1026, 0, 0, 221, 0, 0, + 1012, 0, 0, 0, 0, 1013, 0, 0, 1014, 906, + 0, 0, 0, 1015, 0, 907, 0, 1016, 0, 0, + 1017, 1018, 0, 0, 0, 1019, 0, 0, 1020, 0, + 1021, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 157, 158, 159, 160, 0, 0, 0, 0, + 1008, 0, 0, 0, 1009, 1022, 0, 0, 1023, 1010, + 0, 1024, 0, 0, 0, 1011, 0, 0, 0, 914, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1012, 0, 0, 39, 0, 1013, 0, 0, 1014, + 0, 0, 0, 0, 1015, 0, 0, 0, 1016, 40, + 0, 1017, 1018, 0, -432, 0, 1019, 0, 0, 1020, + 0, 1021, 0, 0, 0, 0, 0, 41, 0, 0, + 0, 0, 42, 0, 43, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1022, 0, 0, 1023, + 0, 0, 1024, 0, 0, 917, 0, 0, 918, 919, + 0, 0, 0, 0, 0, 920, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 922, 923, 0, 1025, 0, + 856, 0, 0, 0, 0, 0, 0, 0, 906, 0, + 0, 0, 0, 0, 907, 0, 0, 0, 0, 0, + 0, 0, 924, 925, 0, 0, 0, 750, 0, 0, + 751, 0, 0, 0, 1026, 0, 917, 221, 0, 918, + 919, 1706, 158, 159, 160, 0, 920, 752, 0, 1008, + 0, 0, 0, 1009, 0, 0, 0, 0, 1010, 0, + 0, 0, 0, 0, 1011, 0, 0, 0, 914, 0, + 0, 0, 0, 0, 0, 0, 922, 923, 0, 1025, + 1012, 0, 0, 0, 0, 1013, 0, 0, 1014, 0, + 0, 0, 0, 1015, 0, 0, 0, 1016, 0, 0, + 1017, 1018, 0, 924, 925, 1019, 0, 0, 1020, 0, + 1021, 0, 0, 0, 0, 1026, 0, 0, 221, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1022, 0, 0, 1023, 0, + 0, 1024, 0, 0, 0, 0, 0, 0, 0, 0, + 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 753, 0, 0, 0, 213, 214, 0, + 0, 0, 0, 0, 0, 917, 0, 754, 918, 919, + 0, 0, 0, 0, 0, 920, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 755, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 922, 923, 0, 1025, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 924, 925, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1026, 0, 0, 221 +}; + +static const yytype_int16 yycheck[] = +{ + 1, 68, 3, 30, 328, 434, 352, 431, 216, 361, + 433, 53, 432, 510, 399, 508, 68, 1, 338, 878, + 509, 567, 920, 65, 38, 684, 356, 628, 378, 37, + 717, 906, 756, 30, 463, 317, 385, 1019, 220, 179, + 460, 1461, 531, 32, 443, 1461, 47, 566, 871, 595, + 1467, 1326, 1327, 473, 55, 1468, 53, 603, 1333, 60, + 1466, 414, 1460, 1461, 346, 1405, 979, 840, 65, 1482, + 375, 399, 377, 619, 107, 474, 109, 1270, 111, 112, + 1313, 1465, 115, 1404, 117, 53, 438, 120, 121, 371, + 36, 124, 125, 126, 127, 1479, 180, 65, 131, 1574, + 430, 134, 522, 876, 1574, 1024, 139, 140, 881, 177, + 1587, 1588, 180, 177, 147, 399, 180, 469, 664, 1628, + 926, 894, 1793, 453, 1791, 898, 410, 411, 1792, 177, + 1407, 1770, 180, 857, 177, 1557, 1558, 180, 1693, 4, + 1415, 995, 1766, 1971, 23, 1835, 177, 499, 999, 180, + 1578, 177, 996, 1566, 180, 1836, 177, 997, 30, 180, + 1637, 1638, 708, 4, 177, 177, 2002, 180, 180, 23, + 7, 177, 524, 7, 180, 7, 127, 177, 7, 97, + 180, 53, 7, 8, 9, 30, 177, 79, 80, 180, + 7, 57, 1817, 65, 38, 1590, 177, 743, 1847, 180, + 23, 177, 45, 90, 180, 103, 177, 177, 53, 180, + 180, 177, 834, 835, 180, 104, 105, 106, 104, 216, + 65, 10, 190, 220, 49, 105, 51, 1288, 720, 103, + 722, 104, 724, 837, 993, 994, 61, 729, 7, 104, + 105, 66, 5, 104, 105, 70, 15, 16, 17, 18, + 294, 40, 41, 42, 43, 8, 753, 58, 104, 85, + 327, 328, 15, 16, 17, 18, 58, 144, 85, 22, + 8, 1691, 142, 20, 1751, 1752, 30, 1598, 1599, 2032, + 53, 13, 271, 780, 161, 155, 75, 839, 277, 104, + 1767, 1768, 122, 1691, 83, 84, 85, 86, 790, 53, + 840, 54, 172, 781, 1022, 8, 1079, 1354, 4, 1356, + 6, 65, 15, 16, 17, 18, 54, 106, 385, 22, + 364, 12, 838, 22, 311, 822, 85, 820, 104, 105, + 106, 57, 821, 294, 123, 8, 331, 30, 164, 128, + 129, 130, 293, 39, 216, 49, 341, 399, 220, 138, + 90, 54, 141, 93, 103, 352, 88, 104, 410, 411, + 53, 104, 1021, 128, 332, 432, 1764, 90, 49, 1789, + 93, 216, 65, 1789, 4, 220, 6, 364, 104, 1777, + 432, 54, 73, 861, 352, 427, 77, 816, 153, 293, + 1788, 1789, 104, 460, 814, 47, 221, 284, 126, 286, + 442, 92, 93, 364, 103, 104, 473, 311, 460, 808, + 1, 1012, 298, 4, 1015, 6, 303, 62, 8, 110, + 1897, 473, 150, 128, 297, 15, 16, 17, 18, 104, + 427, 91, 104, 161, 1285, 26, 27, 28, 29, 313, + 57, 5, 133, 134, 1505, 442, 364, 38, 153, 295, + 1072, 1073, 1074, 144, 99, 522, 326, 490, 288, 427, + 151, 20, 216, 22, 54, 1183, 220, 819, 1090, 1864, + 522, 369, 1076, 1, 442, 364, 4, 369, 6, 2169, + 352, 347, 348, 298, 8, 364, 364, 104, 2241, 2170, + 369, 15, 16, 17, 18, 2331, 369, 57, 26, 27, + 28, 29, 327, 328, 369, 2129, 331, 352, 369, 85, + 2338, 300, 301, 302, 277, 369, 1289, 1290, 335, 2144, + 364, 364, 2171, 216, 365, 1923, 1078, 220, 365, 292, + 54, 365, 1383, 365, 297, 4, 365, 6, 45, 1079, + 1, 364, 2097, 4, 104, 6, 369, 310, 295, 350, + 351, 352, 315, 2030, 297, 427, 696, 57, 350, 351, + 352, 1077, 90, 57, 313, 26, 27, 28, 29, 97, + 442, 297, 331, 332, 333, 1381, 2215, 45, 46, 1125, + 2204, 1921, 427, 295, 1438, 1862, 594, 2096, 2259, 939, + 2257, 282, 283, 369, 2258, 1439, 2018, 442, 352, 368, + 1440, 30, 365, 617, 104, 1652, 134, 2082, 1655, 349, + 104, 1658, 2082, 697, 364, 368, 1891, 922, 923, 444, + 1139, 281, 1141, 448, 53, 297, 349, 452, 1547, 697, + 358, 695, 457, 697, 580, 1257, 65, 462, 363, 847, + 37, 169, 103, 2071, 363, 399, 435, 436, 437, 697, + 999, 1926, 1885, 1886, 697, 2083, 410, 411, 1057, 352, + 1045, 133, 361, 871, 695, 368, 697, 90, 90, 695, + 369, 697, 1000, 427, 695, 243, 697, 1436, 1437, 468, + 297, 243, 695, 695, 697, 697, 114, 364, 442, 695, + 87, 697, 369, 518, 2015, 695, 1355, 697, 712, 342, + 146, 709, 90, 131, 695, 1898, 697, 85, 497, 87, + 7, 1624, 1065, 277, 695, 540, 697, 1045, 351, 695, + 1025, 697, 1214, 20, 695, 695, 697, 697, 292, 695, + 135, 697, 137, 297, 427, 144, 164, 297, 1, 103, + 149, 4, 1026, 6, 112, 113, 310, 814, 90, 442, + 278, 315, 85, 2093, 87, 331, 332, 333, 349, 293, + 8, 1045, 814, 26, 27, 28, 29, 2184, 103, 1628, + 114, 114, 2185, 45, 365, 47, 24, 2183, 368, 1272, + 364, 1278, 103, 1642, 111, 369, 1275, 216, 60, 61, + 126, 220, 135, 93, 14, 15, 16, 297, 2182, 126, + 1623, 365, 7, 297, 871, 148, 54, 1580, 277, 111, + 146, 85, 1305, 87, 150, 20, 998, 32, 33, 34, + 1309, 349, 1341, 292, 126, 516, 878, 172, 297, 275, + 276, 94, 104, 133, 1693, 2255, 2313, 365, 110, 2255, + 293, 310, 1, 832, 368, 4, 315, 6, 120, 20, + 847, 22, 313, 314, 126, 85, 2254, 2255, 78, 79, + 80, 284, 284, 286, 286, 243, 316, 26, 27, 28, + 29, 134, 104, 81, 871, 95, 96, 1564, 150, 284, + 1604, 1605, 41, 1023, 104, 44, 254, 255, 349, 97, + 258, 38, 39, 113, 114, 292, 284, 4, 286, 119, + 127, 915, 2177, 45, 365, 913, 85, 134, 116, 7, + 243, 357, 979, 310, 258, 259, 136, 1269, 315, 349, + 317, 325, 20, 352, 22, 145, 146, 305, 17, 149, + 49, 50, 999, 52, 53, 54, 1285, 164, 1597, 275, + 276, 18, 284, 109, 286, 111, 112, 126, 1000, 115, + 368, 117, 1498, 135, 120, 121, 168, 169, 124, 125, + 126, 127, 78, 306, 164, 131, 360, 361, 134, 243, + 1252, 150, 305, 139, 140, 847, 294, 30, 1475, 167, + 1473, 147, 85, 283, 172, 1474, 168, 169, 52, 2264, + 1649, 1, 52, 1045, 4, 111, 6, 1543, 427, 871, + 53, 998, 847, 313, 314, 164, 1728, 1729, 1730, 1731, + 126, 343, 65, 442, 346, 278, 26, 27, 28, 29, + 57, 357, 204, 126, 0, 297, 871, 114, 4, 818, + 6, 305, 5, 1, 1383, 8, 4, 1912, 6, 254, + 187, 364, 293, 190, 244, 245, 246, 150, 1398, 1050, + 1435, 294, 295, 1054, 364, 237, 172, 1442, 26, 27, + 28, 29, 313, 314, 1463, 285, 286, 62, 63, 30, + 131, 364, 67, 1, 135, 364, 4, 153, 6, 287, + 364, 168, 169, 164, 1292, 364, 349, 104, 164, 165, + 177, 114, 53, 847, 104, 105, 106, 364, 26, 27, + 28, 29, 365, 126, 65, 128, 43, 168, 169, 351, + 15, 16, 129, 130, 30, 5, 1, 871, 8, 4, + 1472, 6, 90, 146, 878, 22, 998, 150, 25, 97, + 153, 45, 1140, 292, 1142, 45, 1823, 53, 45, 30, + 1148, 26, 27, 28, 29, 261, 112, 113, 114, 65, + 1434, 1435, 7, 998, 847, 313, 314, 1441, 1442, 1443, + 1444, 986, 53, 216, 2146, 990, 134, 220, 99, 100, + 101, 102, 926, 292, 65, 7, 103, 104, 871, 8, + 299, 300, 2080, 2081, 309, 7, 114, 114, 20, 1, + 22, 104, 4, 1739, 6, 103, 315, 1405, 20, 1407, + 22, 169, 336, 337, 1, 90, 365, 4, 1, 6, + 370, 4, 97, 6, 26, 27, 28, 29, 1285, 242, + 370, 1943, 1944, 1945, 305, 306, 307, 308, 309, 26, + 27, 28, 29, 26, 27, 28, 29, 254, 2097, 993, + 994, 995, 996, 997, 998, 114, 1000, 116, 41, 134, + 342, 44, 275, 276, 342, 216, 1602, 274, 275, 220, + 103, 1946, 1947, 1948, 1949, 116, 135, 103, 104, 489, + 15, 16, 1026, 45, 143, 144, 145, 146, 114, 1631, + 359, 132, 45, 4, 169, 6, 135, 8, 60, 61, + 114, 1045, 104, 105, 106, 1292, 367, 60, 61, 352, + 216, 85, 153, 177, 220, 998, 23, 131, 23, 160, + 278, 52, 136, 137, 1634, 52, 1383, 1716, 538, 168, + 169, 52, 171, 52, 490, 216, 1711, 1712, 52, 220, + 103, 1383, 104, 85, 357, 1685, 364, 1687, 110, 349, + 4, 104, 6, 7, 1837, 103, 1692, 110, 120, 280, + 278, 1848, 103, 1842, 126, 365, 103, 120, 289, 290, + 291, 292, 85, 126, 1910, 1911, 85, 1913, 103, 104, + 103, 164, 15, 16, 427, 60, 61, 1, 150, 114, + 4, 349, 6, 1876, 4, 1878, 6, 150, 8, 442, + 227, 352, 1881, 278, 231, 232, 233, 365, 249, 250, + 1375, 1376, 26, 27, 28, 29, 103, 1382, 1405, 17, + 1407, 277, 313, 314, 38, 1623, 18, 45, 847, 243, + 1292, 349, 2107, 2108, 2109, 110, 292, 1711, 1712, 1781, + 368, 297, 60, 61, 5, 120, 352, 365, 350, 351, + 352, 126, 871, 1, 310, 7, 4, 1292, 6, 315, + 138, 139, 140, 141, 1455, 1456, 7, 1458, 1459, 1460, + 1461, 352, 1459, 7, 349, 150, 427, 7, 26, 27, + 28, 29, 1456, 103, 1458, 1459, 1460, 1461, 1830, 5, + 365, 442, 110, 399, 103, 2086, 2087, 7, 2147, 364, + 2263, 1459, 120, 7, 410, 411, 1504, 294, 126, 292, + 194, 195, 1, 192, 193, 4, 17, 6, 112, 113, + 238, 427, 1941, 18, 1938, 368, 1939, 112, 113, 247, + 248, 8, 150, 251, 252, 297, 442, 26, 27, 28, + 29, 2304, 7, 1405, 297, 1407, 427, 349, 1292, 1553, + 1554, 38, 41, 1551, 1552, 44, 4, 5, 1968, 112, + 113, 442, 349, 365, 1621, 103, 1623, 1624, 1, 364, + 1405, 4, 1407, 6, 103, 104, 1890, 103, 365, 998, + 364, 1572, 365, 364, 1575, 1995, 1577, 95, 96, 1580, + 2126, 103, 104, 26, 27, 28, 29, 1459, 1572, 364, + 1642, 1575, 294, 1577, 43, 44, 1580, 1390, 1391, 1292, + 37, 311, 1644, 364, 1, 1602, 1936, 4, 45, 6, + 47, 103, 49, 103, 1459, 52, 53, 54, 103, 56, + 364, 1375, 1376, 1377, 1378, 1379, 1623, 1381, 1382, 26, + 27, 28, 29, 103, 1602, 103, 1390, 1391, 1646, 45, + 104, 47, 364, 1644, 103, 97, 52, 1644, 54, 103, + 191, 1405, 58, 1407, 1862, 131, 231, 46, 196, 48, + 103, 50, 51, 104, 234, 164, 107, 108, 109, 58, + 59, 1738, 236, 234, 1, 235, 1644, 4, 235, 6, + 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, + 1444, 103, 98, 343, 100, 1692, 103, 94, 103, 26, + 27, 28, 29, 92, 114, 1459, 116, 117, 118, 119, + 2056, 103, 1405, 1921, 1407, 103, 2213, 364, 2211, 364, + 364, 399, 1749, 2212, 1692, 349, 335, 336, 337, 2275, + 1602, 364, 410, 411, 1735, 103, 294, 134, 103, 1766, + 5, 365, 8, 1744, 1745, 1746, 1747, 1748, 104, 105, + 106, 1623, 1749, 2246, 2247, 1, 103, 1602, 4, 2248, + 6, 1762, 1763, 1764, 2310, 103, 1459, 52, 103, 1766, + 2316, 22, 1644, 43, 1775, 1776, 1777, 149, 1623, 5, + 26, 27, 28, 29, 367, 1786, 1787, 1788, 1789, 103, + 1787, 349, 85, 292, 847, 8, 128, 1798, 85, 1644, + 22, 1798, 1786, 1787, 1788, 1789, 22, 365, 367, 2208, + 364, 103, 144, 7, 1798, 331, 332, 333, 871, 1787, + 1692, 153, 1889, 1890, 371, 1826, 1827, 1828, 160, 161, + 1798, 2328, 364, 2326, 1, 364, 114, 4, 2327, 6, + 277, 371, 1826, 1827, 1828, 103, 5, 1692, 1602, 295, + 128, 104, 86, 87, 88, 292, 90, 135, 2210, 26, + 27, 28, 29, 1292, 364, 1862, 365, 312, 114, 1623, + 313, 314, 312, 310, 1628, 153, 20, 1749, 315, 43, + 317, 278, 160, 316, 1951, 2093, 847, 191, 1642, 316, + 1644, 294, 311, 2245, 1766, 7, 70, 1722, 1723, 1724, + 1725, 1968, 5, 166, 1749, 1906, 349, 7, 103, 1602, + 871, 222, 223, 224, 225, 1787, 1968, 39, 2317, 104, + 99, 1766, 365, 103, 1921, 363, 1798, 94, 1995, 363, + 1623, 847, 191, 363, 104, 2002, 99, 399, 1692, 1693, + 363, 103, 1787, 1995, 7, 998, 103, 103, 410, 411, + 365, 1644, 349, 1798, 20, 871, 847, 1711, 1712, 85, + 103, 103, 878, 1964, 103, 1966, 1967, 134, 365, 103, + 103, 365, 5, 2325, 103, 190, 1405, 104, 1407, 1980, + 871, 363, 151, 103, 311, 1986, 103, 103, 294, 103, + 1862, 2024, 1993, 1994, 105, 1749, 1997, 295, 365, 1692, + 187, 2034, 2003, 226, 227, 228, 229, 230, 2041, 2010, + 2011, 2012, 1766, 364, 364, 1, 364, 1862, 4, 364, + 6, 364, 349, 364, 364, 364, 2010, 2011, 2012, 41, + 1459, 364, 278, 1787, 7, 364, 134, 998, 365, 365, + 26, 27, 28, 29, 1798, 103, 365, 2114, 364, 1921, + 319, 320, 321, 322, 323, 41, 1749, 364, 44, 2056, + 104, 364, 8, 979, 1889, 1890, 8, 103, 2135, 103, + 5, 8, 104, 1766, 103, 103, 1921, 993, 994, 995, + 996, 997, 998, 363, 1000, 1018, 1016, 1329, 2056, 907, + 793, 1017, 2119, 792, 1787, 791, 2093, 1133, 572, 794, + 2101, 1491, 2129, 349, 20, 1798, 562, 998, 1862, 1020, + 1026, 278, 1, 281, 2115, 4, 282, 6, 1, 365, + 429, 4, 2119, 6, 1464, 1771, 2264, 2125, 2127, 1045, + 2189, 2191, 2129, 2190, 2192, 2136, 2137, 26, 27, 28, + 29, 2181, 2306, 26, 27, 28, 29, 2148, 2149, 1724, + 2151, 2152, 2153, 2154, 2155, 287, 1926, 2158, 41, 426, + 899, 44, 889, 1908, 1877, 1879, 962, 1921, 428, 1862, + 1102, 2180, 2251, 1602, 1377, 1379, 1378, 2204, 164, 1396, + 982, 1621, 349, 319, 2056, 79, 80, 81, 82, 83, + 84, 112, 113, 872, 1623, 1124, 1623, 2264, 365, 991, + 878, 216, 1403, 1444, 2205, 1333, 127, 2204, 129, 130, + 131, 2056, 1603, 2214, 1456, 1644, 1458, 1459, 1460, 1461, + 1635, 2093, 411, 1640, 438, 114, 2314, 439, 1921, 440, + 332, 152, 153, 154, 441, 718, 333, 2304, 333, 1292, + 695, 662, 2269, 1192, -1, -1, -1, 2119, 2093, -1, + -1, 2252, 2253, 2254, 2255, -1, 2253, 2129, -1, 2260, + 2261, 2262, 2263, 1692, 2331, -1, 2267, -1, 2252, 2253, + 2254, 2255, 2269, -1, 2119, 2276, 2260, 2261, 2262, 2263, + -1, 164, -1, 2284, 2129, 2253, -1, 112, 113, 2290, + 2291, 2292, 2293, 2294, 2295, 2296, 2297, 2298, 2299, 2300, + 2301, 126, 2056, -1, -1, -1, 292, 1239, 1240, 134, + -1, 2295, 2296, 2297, 2315, 993, 994, 995, 996, 997, + 1749, -1, 1000, -1, -1, 150, -1, -1, -1, -1, + 1572, 1292, 2204, 1575, -1, 1577, -1, 1766, 1580, 2093, + 2341, 2342, 2343, 2097, -1, -1, 1, -1, 1026, 4, + -1, 6, 1405, -1, 1407, -1, -1, -1, 1787, 2204, + -1, -1, -1, 2056, 1296, 2119, -1, 1045, -1, 1798, + 45, 26, 27, 28, 29, 2129, 1292, -1, -1, 365, + -1, 2253, 112, 113, -1, 60, 61, -1, -1, 278, + -1, 121, 122, 123, 124, 125, -1, 2269, 1640, -1, + 2093, 1292, 37, -1, -1, -1, 1459, -1, 2253, 292, + 45, -1, 47, -1, 49, 37, 878, 52, 53, 54, + -1, 56, -1, 45, 2269, 47, 2119, -1, -1, -1, + 52, 53, 54, 1862, 56, 110, 2129, 1353, 1354, -1, + 1356, -1, -1, -1, 1405, 120, 1407, -1, -1, -1, + 2204, 126, -1, 278, 279, 280, 281, -1, -1, -1, + 349, -1, -1, -1, 926, -1, -1, -1, -1, 294, + 295, 296, -1, -1, -1, 150, 365, -1, -1, -1, + -1, -1, 365, -1, -1, -1, -1, -1, 45, 1405, + -1, 1407, 1921, -1, -1, -1, -1, 45, 1459, 2253, + -1, -1, -1, 60, 61, -1, -1, -1, -1, -1, + -1, 2204, 60, 61, 1405, 2269, 1407, -1, 1434, 1435, + 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444, -1, + -1, 993, 994, 995, 996, 997, -1, -1, 1000, -1, + -1, -1, -1, 1459, 1786, 1787, 1788, 1789, -1, 1602, + -1, 1483, -1, 110, 1486, -1, 1798, -1, -1, -1, + 2253, -1, 110, 120, 1026, 70, -1, -1, 1459, 126, + 1623, 76, 120, -1, -1, -1, 2269, -1, 126, 45, + -1, -1, -1, 1045, 1826, 1827, 1828, -1, 844, -1, + 846, 1644, -1, 150, 60, 61, -1, -1, -1, 104, + 105, 106, 150, -1, -1, -1, 111, 863, -1, 865, + 115, 867, -1, -1, 37, 120, -1, 873, 874, -1, + -1, 126, 45, -1, 47, 130, 49, 2056, -1, 52, + 53, 54, -1, 56, -1, 1567, -1, 60, 61, 1692, + -1, 1602, 277, -1, 110, 150, -1, -1, -1, 905, + -1, -1, 908, -1, 120, 277, 311, 292, -1, -1, + 126, -1, 1623, -1, 2093, -1, -1, -1, -1, -1, + 292, -1, -1, -1, 1606, 310, 1608, 1609, -1, 1611, + 315, -1, 317, 1644, 150, -1, 1602, 110, 310, -1, + 2119, -1, -1, 315, 349, 317, 1749, 120, -1, -1, + 2129, -1, -1, 126, -1, 1621, 45, 1623, 1624, -1, + 365, 1602, 1628, 1766, -1, -1, -1, 973, -1, -1, + -1, 60, 61, -1, -1, -1, 1642, 150, 1644, -1, + -1, 1692, 1623, -1, 1787, -1, 1652, -1, -1, 1655, + -1, -1, 1658, -1, -1, 1798, -1, -1, -1, -1, + -1, -1, -1, 1644, -1, -1, 1434, 1435, 1436, 1437, + 1438, 1439, 1440, 1441, 1442, 1443, 1444, -1, 2010, 2011, + 2012, 110, -1, -1, -1, 2204, 1692, 1693, -1, -1, + -1, 120, 287, -1, -1, 290, 291, 126, 1749, -1, + 1046, 1047, 297, -1, -1, 1711, 1712, -1, 112, 113, + -1, 1692, 116, -1, -1, 1766, -1, -1, -1, 1862, + -1, 150, -1, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 327, 328, 2253, -1, 1787, -1, -1, -1, + -1, -1, 23, 1749, 37, -1, -1, 1798, -1, 42, + 2269, -1, 45, 46, 47, 48, -1, 38, 51, 52, + 1766, 54, 55, 56, 277, 58, -1, -1, 1749, -1, + -1, -1, -1, 177, 369, -1, -1, -1, 1921, 292, + -1, 1787, -1, -1, -1, 1766, -1, -1, -1, -1, + -1, -1, 1798, -1, -1, -1, -1, 310, -1, -1, + -1, -1, 315, -1, 317, 98, 1787, 100, 101, 102, + -1, 1862, -1, -1, -1, -1, 1, 1798, -1, 4, + -1, 6, 103, 1375, 1376, 1377, 1378, 1379, -1, 1381, + 1382, -1, -1, 113, 114, -1, 116, -1, 1390, 1391, + -1, 26, 27, 28, 29, -1, -1, -1, 1870, -1, + -1, -1, 37, -1, -1, 135, 1862, -1, -1, -1, + 1628, -1, 47, 143, 144, -1, 146, 52, -1, 54, + 1921, 56, -1, -1, 1642, -1, 156, 157, 158, -1, + -1, 1862, 1434, 1435, 1436, 1437, 1438, 1439, 1440, 1441, + 1442, 1443, 1444, -1, 175, -1, -1, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, + -1, -1, -1, 2056, -1, 1921, -1, -1, -1, -1, + 2252, 2253, 2254, 2255, -1, 1693, -1, -1, 2260, 2261, + 2262, 2263, -1, 1279, -1, -1, -1, 1283, -1, -1, + 1921, -1, -1, 1711, 1712, -1, -1, 1293, 1294, 1295, + 2093, -1, 1298, -1, 1300, -1, -1, -1, -1, -1, + -1, -1, -1, 2295, 2296, 2297, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 2119, 1323, -1, 164, + -1, -1, -1, -1, 277, -1, 2129, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 292, + -1, -1, -1, 2025, -1, 2056, 299, 300, 301, 302, + -1, -1, -1, 2035, 1360, -1, 1362, 310, -1, -1, + 2042, -1, 315, 1369, 317, 318, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 1384, -1, + 1386, -1, 2093, -1, -1, -1, -1, -1, 109, 1395, + 2056, 1397, -1, -1, -1, 2077, -1, -1, -1, -1, + -1, 2204, -1, 38, -1, 126, -1, -1, 2119, -1, + -1, -1, -1, -1, -1, 2056, 1628, -1, 2129, -1, + -1, 142, -1, -1, -1, -1, 147, 2093, -1, 150, + 1642, 2097, 277, -1, 155, -1, -1, -1, 159, -1, + -1, 162, 163, -1, -1, -1, 167, 292, -1, 170, + 2253, 172, 2093, 2119, -1, -1, -1, 2139, 2140, 2141, + -1, -1, -1, 2129, -1, 310, 2269, -1, 103, -1, + 315, -1, 317, -1, -1, -1, 197, -1, 2119, -1, + -1, 1693, 203, 2165, -1, -1, -1, -1, 2129, -1, + -1, -1, -1, 2204, -1, -1, -1, -1, -1, 1711, + 1712, -1, 1, -1, 349, 4, -1, 6, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 365, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, -1, -1, -1, -1, -1, -1, -1, 2204, -1, + 175, 40, 2253, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, -1, -1, 2269, -1, + -1, -1, -1, 2204, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 1583, 1584, -1, + -1, -1, -1, -1, -1, -1, -1, 2253, -1, -1, + 89, 90, 91, 92, -1, 94, -1, -1, 97, -1, + -1, -1, -1, 2269, 1610, 135, -1, -1, -1, -1, + 1, -1, 2253, -1, 144, 114, -1, -1, -1, 1625, + 11, 12, 13, 14, 15, 16, 17, -1, 2269, 20, + 160, 90, -1, -1, 164, -1, -1, -1, 168, 169, + 2322, 2323, 2324, 173, 174, 175, 176, 177, -1, -1, + -1, -1, -1, 112, 113, 1661, -1, 116, -1, -1, + -1, -1, -1, -1, -1, 164, -1, -1, -1, 2097, + -1, -1, 1678, -1, 1680, 134, -1, -1, -1, -1, + -1, -1, 73, -1, -1, 76, 77, 78, 79, 80, + 81, -1, -1, -1, -1, -1, -1, 88, -1, 90, + 91, 92, 93, 94, 95, 96, 97, -1, 99, 100, + 101, 102, -1, 104, -1, -1, -1, 108, -1, 110, + -1, -1, 113, 114, -1, 116, -1, -1, 119, -1, + -1, -1, 191, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 133, 134, 135, 136, 137, -1, 139, 140, + -1, -1, 143, 144, 145, 146, -1, -1, 149, -1, + 151, -1, -1, -1, 1, -1, -1, 4, -1, 6, + -1, -1, -1, -1, -1, -1, -1, -1, 277, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, + 27, 28, 29, 292, -1, 254, -1, -1, 297, -1, + -1, -1, -1, 40, -1, -1, -1, -1, -1, -1, + -1, 310, -1, -1, -1, -1, 315, -1, -1, 278, + 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, + 289, 290, 291, -1, -1, 294, 295, 296, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 1855, + 349, -1, 89, 90, 91, 92, -1, 94, 317, -1, + 97, -1, -1, -1, -1, -1, 365, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 114, -1, -1, + -1, -1, 1888, -1, -1, 2097, -1, -1, -1, 280, + 281, 282, 283, 284, 285, 286, 287, -1, 289, 290, + 291, 292, -1, 1, -1, -1, 4, -1, 6, 1915, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 23, -1, 164, 26, 27, + 28, 29, 30, 31, -1, -1, -1, 35, 36, 37, + -1, -1, -1, -1, -1, -1, 44, -1, -1, -1, + -1, -1, -1, -1, 52, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 62, 63, -1, -1, -1, 67, + -1, -1, 70, -1, -1, -1, -1, -1, -1, -1, + -1, 79, 80, 81, 82, 83, 84, -1, -1, -1, + -1, -1, -1, 109, -1, 111, 94, -1, -1, 115, + 98, -1, 100, -1, 120, 103, 104, 105, 106, -1, + 126, 109, -1, 111, -1, -1, 114, 115, -1, -1, + -1, -1, 120, -1, -1, -1, 142, -1, 126, -1, + -1, 147, -1, -1, 150, -1, -1, -1, -1, 155, + 277, -1, -1, 159, 142, -1, 162, 163, 164, 147, + -1, 167, 150, -1, 170, 292, 172, 155, -1, -1, + 297, 159, -1, -1, 162, 163, 164, -1, -1, 167, + -1, -1, 170, 310, 172, -1, -1, -1, 315, -1, + -1, 197, -1, 199, -1, 2091, -1, 203, -1, -1, + -1, 482, -1, -1, -1, -1, -1, -1, 489, 197, + 198, -1, 200, 201, 202, 203, -1, -1, -1, 500, + -1, 502, 349, 504, -1, 506, -1, -1, -1, -1, + 511, -1, -1, -1, 515, 516, -1, -1, 365, -1, + -1, -1, -1, -1, 525, -1, -1, 528, -1, -1, + 238, 239, 240, 241, -1, -1, -1, 538, -1, 247, + 248, -1, -1, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, + 268, 269, 270, 271, 272, 273, 274, -1, -1, 277, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 292, -1, -1, -1, -1, 297, + -1, -1, -1, -1, -1, -1, 304, -1, -1, -1, + -1, -1, 310, -1, -1, -1, -1, 315, -1, -1, + -1, -1, -1, 321, 322, -1, 324, -1, -1, 327, + 328, 329, 330, -1, -1, -1, 334, -1, -1, -1, + 338, 339, 340, -1, -1, -1, 344, 345, -1, -1, + -1, 349, -1, -1, -1, 353, -1, 1, 356, 357, + 4, -1, 6, -1, 362, -1, 364, 365, 366, -1, + -1, 369, -1, -1, -1, -1, -1, -1, -1, 23, + -1, -1, 26, 27, 28, 29, 30, 31, -1, -1, + -1, 35, 36, 37, -1, -1, -1, -1, -1, -1, + 44, -1, -1, -1, -1, -1, -1, -1, 52, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 62, 63, + -1, -1, -1, 67, -1, -1, 70, -1, -1, -1, + -1, -1, -1, -1, -1, 79, 80, 81, 82, 83, + 84, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 94, -1, -1, -1, 98, -1, 100, -1, -1, 103, + 104, 105, 106, -1, -1, 109, -1, 111, -1, -1, + 114, 115, -1, -1, 1, -1, 120, 4, -1, 6, + -1, -1, 126, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 142, 26, + 27, 28, 29, 147, -1, -1, 150, 182, -1, -1, + -1, 155, -1, 40, -1, 159, -1, -1, 162, 163, + 164, -1, -1, 167, -1, -1, 170, -1, 172, -1, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, -1, -1, -1, + -1, -1, -1, 197, 198, -1, 200, 201, 202, 203, + -1, -1, 89, 90, 91, 92, -1, 94, -1, -1, + 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 114, -1, -1, + -1, -1, -1, -1, 238, 239, 240, 241, -1, -1, + -1, -1, -1, 247, 248, -1, -1, 251, 252, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, -1, -1, 277, -1, -1, -1, 164, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 292, -1, + -1, -1, -1, 297, -1, -1, -1, -1, -1, -1, + 304, -1, -1, -1, -1, -1, 310, -1, -1, -1, + -1, 315, -1, -1, -1, -1, -1, 321, 322, -1, + 324, -1, -1, 327, 328, 329, 330, -1, -1, -1, + 334, -1, -1, -1, 338, 339, 340, -1, -1, -1, + 344, 345, -1, -1, -1, 349, -1, -1, -1, 353, + -1, 1, 356, 357, 4, -1, 6, -1, 362, -1, + 364, 365, 366, -1, -1, 369, -1, -1, -1, -1, + -1, -1, -1, 23, -1, -1, 26, 27, 28, 29, + 30, 31, -1, -1, -1, 35, 36, 37, -1, -1, + 277, -1, -1, -1, 44, -1, -1, -1, -1, -1, + -1, -1, 52, -1, -1, 292, -1, -1, -1, -1, + 297, -1, 62, 63, -1, -1, -1, 67, -1, -1, + 70, -1, -1, 310, -1, -1, -1, -1, 315, 79, + 80, 81, 82, 83, 84, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 94, -1, -1, -1, 98, -1, + 100, -1, 1103, 103, 104, 105, 106, -1, -1, 109, + -1, 111, 349, -1, 114, 115, -1, -1, 1, -1, + 120, 4, -1, 6, -1, -1, 126, -1, 365, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 142, 26, 27, 28, 29, 147, -1, -1, + 150, -1, -1, -1, -1, 155, -1, 40, -1, 159, + -1, -1, 162, 163, 164, -1, -1, 167, -1, -1, + 170, -1, 172, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 197, 198, -1, + 200, 201, 202, 203, -1, -1, -1, 90, 91, 92, + -1, 94, -1, -1, 97, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 114, -1, -1, -1, -1, -1, -1, 238, 239, + 240, 241, -1, -1, -1, -1, -1, 247, 248, -1, + -1, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, -1, -1, 277, -1, -1, + -1, 164, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 292, -1, -1, -1, -1, 297, -1, -1, + -1, -1, -1, -1, 304, -1, -1, -1, -1, -1, + 310, -1, -1, -1, -1, 315, -1, -1, -1, -1, + -1, 321, 322, -1, 324, -1, -1, 327, 328, 329, + 330, -1, -1, -1, 334, -1, -1, -1, 338, 339, + 340, -1, -1, -1, 344, 345, -1, -1, -1, 349, + -1, -1, -1, 353, -1, 1, 356, 357, 4, -1, + 6, -1, 362, -1, 364, 365, 366, -1, -1, 369, + -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, + 26, 27, 28, 29, 30, 31, -1, -1, -1, 35, + 36, 37, -1, -1, 277, -1, -1, -1, 44, -1, + -1, -1, -1, -1, -1, -1, 52, -1, -1, 292, + -1, -1, -1, -1, 297, -1, 62, 63, -1, -1, + -1, 67, -1, -1, 70, -1, -1, 310, -1, -1, + -1, -1, 315, 79, 80, 81, 82, 83, 84, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 94, -1, + -1, -1, 98, -1, 100, -1, -1, 103, 104, 105, + 106, -1, -1, 109, -1, 111, 349, -1, 114, 115, + -1, -1, 1, -1, 120, 4, -1, 6, -1, -1, + 126, -1, 365, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 142, 26, 27, 28, + 29, 147, -1, -1, 150, -1, -1, -1, -1, 155, + -1, 40, -1, 159, -1, -1, 162, 163, 164, -1, + -1, 167, -1, -1, 170, -1, 172, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 197, 198, -1, 200, 201, 202, 203, -1, -1, + -1, 90, 91, 92, -1, 94, -1, -1, 97, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 114, -1, -1, -1, -1, + -1, -1, 238, 239, 240, 241, -1, -1, -1, -1, + -1, 247, 248, -1, -1, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, -1, + -1, 277, -1, -1, -1, 164, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 292, -1, -1, -1, + -1, 297, -1, -1, -1, -1, -1, -1, 304, -1, + -1, -1, -1, -1, 310, -1, -1, -1, -1, 315, + -1, -1, -1, -1, -1, 321, 322, -1, 324, -1, + -1, 327, 328, 329, 330, -1, -1, -1, 334, -1, + -1, -1, 338, 339, 340, 4, -1, 6, 344, 345, + -1, -1, -1, 349, -1, -1, -1, 353, -1, -1, + 356, 357, -1, -1, 23, -1, 362, -1, 364, 365, + 366, 30, 31, 369, -1, -1, 35, 36, 37, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 52, -1, -1, -1, -1, 277, -1, + -1, -1, -1, 62, 63, -1, -1, 104, 67, -1, + -1, 70, -1, 292, -1, -1, -1, -1, 297, -1, + 79, 80, 81, 82, 83, 84, -1, -1, -1, -1, + -1, 310, -1, -1, 131, -1, 315, 134, -1, 98, + -1, 100, -1, -1, 103, 104, 105, 106, -1, -1, + 109, -1, 111, -1, 151, -1, 115, -1, -1, -1, + -1, 120, -1, -1, -1, -1, -1, 126, -1, -1, + 349, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 142, -1, -1, 365, -1, 147, -1, + -1, 150, -1, -1, 1, -1, 155, 4, -1, 6, + 159, -1, -1, 162, 163, -1, -1, -1, 167, -1, + -1, 170, -1, 172, -1, -1, -1, -1, -1, 26, + 27, 28, 29, -1, -1, -1, -1, -1, -1, -1, + 37, -1, -1, -1, -1, -1, -1, -1, 197, 198, + 47, 200, 201, 202, 203, 52, -1, 54, -1, 56, + -1, -1, -1, -1, -1, -1, -1, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, -1, 238, + 239, 240, 241, -1, -1, -1, -1, -1, 247, 248, + -1, -1, 251, 252, 253, 254, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, + 269, 270, 271, 272, 273, 274, -1, -1, 277, -1, + 317, -1, 5, -1, 321, 322, -1, -1, -1, -1, + -1, -1, -1, 292, 331, -1, -1, -1, 297, -1, + 23, -1, -1, -1, -1, 304, -1, -1, 31, -1, + -1, 310, -1, 36, -1, -1, 315, 164, -1, -1, + 357, -1, 321, 322, -1, 324, -1, -1, 327, 328, + 329, 330, -1, -1, -1, 334, -1, -1, -1, 338, + 339, 340, -1, -1, -1, 344, 345, 70, -1, -1, + -1, -1, -1, 76, 353, -1, -1, 356, 357, -1, + -1, -1, 85, 362, 87, 364, 365, 366, -1, -1, + 369, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 103, 104, 105, 106, -1, -1, 109, -1, 111, -1, + -1, -1, 115, -1, -1, -1, -1, 120, -1, -1, + -1, -1, -1, 126, -1, -1, -1, 130, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 142, + -1, -1, -1, -1, 147, -1, -1, 150, -1, -1, + 277, -1, 155, -1, -1, -1, 159, -1, -1, 162, + 163, -1, -1, -1, 167, 292, -1, 170, -1, 172, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 310, -1, -1, -1, -1, 315, -1, + 317, -1, -1, -1, 197, 198, -1, 200, 201, 202, + 203, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 5, -1, -1, -1, -1, -1, 112, 113, -1, + -1, 116, 349, -1, -1, -1, -1, -1, -1, 23, + -1, -1, -1, -1, -1, 238, -1, 31, 365, 134, + -1, -1, 36, -1, -1, -1, -1, -1, -1, -1, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, -1, -1, 277, -1, 70, -1, -1, -1, + -1, -1, 76, -1, 287, -1, -1, 290, 291, -1, + -1, -1, -1, -1, 297, -1, -1, -1, -1, -1, + -1, -1, 305, -1, -1, -1, -1, -1, -1, 103, + 104, 105, 106, -1, -1, 109, -1, 111, 321, 322, + -1, 115, -1, -1, 327, 328, 120, -1, -1, -1, + -1, -1, 126, -1, -1, -1, 130, 340, -1, -1, + -1, 344, 345, -1, -1, -1, -1, -1, 142, -1, + -1, 354, 355, 147, 357, -1, 150, -1, -1, 254, + -1, 155, -1, 366, -1, 159, 369, -1, 162, 163, + -1, -1, -1, 167, -1, -1, 170, -1, 172, -1, + -1, -1, -1, 278, 279, 280, 281, 282, 283, -1, + 285, 286, 287, 288, 289, 290, 291, -1, -1, 294, + 295, 296, -1, 197, 198, -1, 200, 201, 202, 203, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 5, -1, 317, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 23, -1, + -1, -1, -1, -1, 238, -1, 31, -1, -1, -1, + -1, 36, -1, -1, -1, -1, -1, -1, -1, 253, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, -1, -1, 277, -1, 70, -1, -1, -1, -1, + -1, 76, -1, 287, -1, -1, 290, 291, -1, -1, + -1, -1, -1, 297, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 103, 104, + 105, 106, -1, -1, 109, -1, 111, 321, 322, -1, + 115, -1, -1, 327, 328, 120, -1, -1, 109, -1, + 111, 126, -1, 114, 115, 130, 340, -1, -1, 120, + 344, 345, -1, -1, -1, 126, -1, 142, -1, -1, + 354, 355, 147, 357, -1, 150, -1, -1, -1, -1, + 155, 142, 366, -1, 159, 369, 147, 162, 163, 150, + -1, -1, 167, -1, 155, 170, -1, 172, 159, -1, + -1, 162, 163, -1, -1, -1, 167, -1, -1, 170, + -1, 172, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 197, 198, -1, 200, 201, 202, 203, -1, + -1, -1, -1, -1, -1, -1, 197, -1, 199, 5, + -1, -1, 203, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 23, -1, -1, + -1, -1, -1, 238, -1, 31, -1, -1, -1, -1, + 36, -1, -1, -1, -1, -1, -1, -1, 253, 254, + 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + -1, -1, 277, -1, 70, -1, -1, -1, -1, -1, + 76, -1, 287, -1, -1, 290, 291, -1, -1, -1, + -1, -1, 297, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 103, 104, 105, + 106, -1, -1, 109, -1, 111, 321, 322, -1, 115, + -1, -1, 327, 328, 120, -1, -1, 109, -1, 111, + 126, -1, -1, 115, 130, 340, -1, -1, 120, 344, + 345, -1, -1, -1, 126, -1, 142, -1, -1, 354, + 355, 147, 357, -1, 150, -1, -1, -1, -1, 155, + 142, 366, -1, 159, 369, 147, 162, 163, 150, -1, + -1, 167, -1, 155, 170, -1, 172, 159, -1, -1, + 162, 163, -1, -1, -1, 167, -1, -1, 170, -1, + 172, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 197, 198, -1, 200, 201, 202, 203, -1, -1, + -1, -1, -1, -1, -1, 197, -1, -1, 5, -1, + -1, 203, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 23, -1, -1, -1, + -1, -1, 238, -1, 31, -1, -1, -1, -1, 36, + -1, -1, -1, -1, -1, -1, -1, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, 274, -1, + -1, 277, -1, 70, -1, -1, -1, -1, -1, 76, + -1, 287, -1, -1, 290, 291, -1, -1, -1, -1, + -1, 297, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 103, 104, 105, 106, + -1, -1, 109, -1, 111, 321, 322, -1, 115, -1, + -1, 327, 328, 120, -1, -1, -1, -1, -1, 126, + -1, -1, -1, 130, 340, -1, -1, -1, 344, 345, + -1, -1, -1, -1, -1, 142, -1, -1, 354, 355, + 147, 357, -1, 150, -1, -1, -1, -1, 155, -1, + 366, -1, 159, 369, -1, 162, 163, -1, -1, -1, + 167, -1, -1, 170, -1, 172, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 1, -1, -1, 4, -1, 6, -1, -1, + 197, 198, -1, 200, 201, 202, 203, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 40, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 238, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, -1, -1, + 277, 90, 91, 92, -1, 94, -1, -1, 97, -1, + 287, -1, -1, 290, 291, -1, -1, -1, -1, -1, + 297, -1, -1, -1, -1, 114, 9, 10, 11, 12, + 13, 14, -1, -1, -1, -1, 19, -1, -1, -1, + 23, -1, -1, -1, 321, 322, -1, -1, -1, -1, + 327, 328, -1, -1, -1, -1, 21, -1, -1, -1, + -1, -1, -1, 340, -1, -1, -1, 344, 345, -1, + -1, -1, -1, -1, -1, 164, -1, 354, 355, -1, + 357, -1, -1, -1, -1, -1, -1, 70, -1, 366, + -1, -1, 369, 76, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 70, -1, -1, -1, -1, + -1, 76, -1, -1, -1, -1, -1, -1, -1, -1, + 103, 104, 105, 106, -1, -1, -1, -1, 111, -1, + -1, -1, 115, -1, -1, -1, -1, 120, 103, 104, + 105, 106, -1, 126, -1, -1, 111, 130, -1, -1, + 115, -1, -1, -1, -1, 120, -1, -1, -1, 142, + 21, 126, -1, -1, 147, 130, -1, 150, -1, -1, + -1, -1, 155, -1, -1, -1, 159, 142, -1, 162, + 163, -1, 147, -1, 167, 150, -1, 170, 277, 172, + 155, -1, -1, -1, 159, -1, -1, 162, 163, -1, + -1, -1, 167, 292, -1, 170, -1, 172, 297, 70, + -1, -1, -1, -1, 197, 76, -1, 200, -1, -1, + 203, 310, -1, -1, -1, -1, 315, -1, -1, -1, + -1, -1, 197, -1, -1, 200, -1, -1, 203, -1, + -1, -1, 103, 104, 105, 106, -1, -1, -1, -1, + 111, -1, -1, -1, 115, -1, -1, -1, -1, 120, + 349, -1, -1, -1, -1, 126, -1, -1, -1, 130, + -1, -1, -1, -1, -1, -1, 365, -1, -1, -1, + -1, 142, -1, -1, -1, -1, 147, -1, -1, 150, + -1, -1, -1, -1, 155, -1, -1, -1, 159, -1, + -1, 162, 163, -1, 287, -1, 167, 290, 291, 170, + -1, 172, -1, -1, 297, -1, -1, -1, -1, -1, + -1, -1, 287, -1, -1, 290, 291, -1, -1, -1, + -1, -1, 297, -1, -1, -1, 197, -1, -1, 200, + -1, -1, 203, -1, 327, 328, -1, 330, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 327, 328, -1, 330, -1, -1, -1, -1, + -1, 354, 355, -1, -1, -1, -1, -1, -1, 22, + 23, 364, -1, 366, -1, -1, 369, -1, -1, 354, + 355, -1, -1, -1, -1, -1, -1, -1, -1, 364, + -1, 366, -1, -1, 369, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 287, 70, -1, 290, + 291, -1, -1, 76, -1, -1, 297, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 103, 104, 105, 106, -1, -1, 327, 328, 111, 330, + -1, -1, 115, -1, -1, -1, -1, 120, -1, -1, + -1, -1, -1, 126, -1, -1, -1, 130, -1, -1, + -1, -1, -1, 354, 355, -1, -1, -1, -1, 142, + -1, -1, -1, -1, 147, 366, -1, 150, 369, -1, + -1, -1, 155, -1, -1, -1, 159, -1, -1, 162, + 163, -1, -1, -1, 167, -1, 23, 170, -1, 172, + -1, -1, -1, 30, 31, -1, -1, -1, 35, 36, + 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 197, 52, -1, 200, -1, -1, + 203, -1, -1, -1, -1, 62, 63, -1, -1, -1, + 67, -1, -1, 70, -1, -1, -1, -1, -1, -1, + -1, -1, 79, 80, 81, 82, 83, 84, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 98, -1, 100, -1, -1, 103, 104, 105, 106, + -1, -1, 109, -1, 111, -1, -1, -1, 115, -1, + -1, -1, -1, 120, -1, -1, -1, -1, -1, 126, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 287, 142, -1, 290, 291, -1, + 147, -1, -1, 150, 297, -1, -1, -1, 155, -1, + -1, -1, 159, -1, -1, 162, 163, -1, -1, -1, + 167, -1, -1, 170, -1, 172, -1, -1, -1, -1, + -1, -1, -1, -1, 327, 328, -1, 330, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 197, 198, -1, 200, 201, 202, 203, -1, -1, -1, + -1, 354, 355, -1, -1, -1, -1, -1, -1, -1, + -1, 364, -1, 366, -1, -1, 369, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 238, 239, 240, 241, -1, -1, -1, -1, -1, + 247, 248, -1, -1, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, -1, -1, + 277, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 23, -1, 292, -1, -1, -1, -1, + 297, 31, -1, -1, -1, -1, 36, 304, -1, -1, + -1, -1, -1, 310, -1, -1, -1, -1, 315, -1, + -1, -1, -1, -1, 321, 322, -1, 324, -1, -1, + 327, 328, 329, 330, -1, -1, -1, 334, -1, -1, + 70, 338, 339, 340, -1, -1, 76, 344, 345, -1, + -1, -1, -1, -1, -1, -1, 353, -1, -1, 356, + 357, -1, -1, -1, -1, 362, -1, 364, -1, 366, + -1, -1, 369, 103, 104, 105, 106, -1, -1, 109, + -1, 111, -1, -1, -1, 115, -1, -1, -1, -1, + 120, -1, -1, -1, -1, -1, 126, -1, -1, -1, + 130, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 142, -1, -1, -1, -1, 147, -1, -1, + 150, -1, -1, -1, -1, 155, -1, -1, -1, 159, + -1, -1, 162, 163, -1, -1, -1, 167, -1, -1, + 170, -1, 172, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 197, 198, -1, + 200, 201, 202, 203, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 23, -1, -1, -1, -1, -1, 238, 30, + 31, -1, -1, -1, -1, 36, -1, -1, -1, -1, + -1, -1, -1, 253, 254, 255, 256, 257, 258, 259, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, -1, -1, 277, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 287, -1, -1, + 290, 291, -1, -1, -1, -1, -1, 297, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 103, 104, 105, 106, -1, -1, 109, -1, + 111, 321, 322, -1, 115, -1, -1, 327, 328, 120, + -1, -1, -1, -1, -1, 126, -1, -1, -1, -1, + 340, -1, -1, -1, 344, 345, -1, -1, -1, -1, + -1, 142, -1, -1, 354, 355, 147, 357, -1, 150, + -1, -1, -1, -1, 155, -1, 366, -1, 159, 369, + -1, 162, 163, -1, -1, -1, 167, -1, -1, 170, + -1, 172, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 197, 198, -1, 200, + 201, 202, 203, -1, -1, -1, -1, -1, -1, -1, + 23, -1, -1, -1, -1, -1, -1, 30, 31, -1, + -1, -1, -1, 36, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 238, 239, 240, + 241, -1, -1, -1, -1, -1, 247, 248, -1, -1, + 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, + 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, + 271, 272, 273, 274, 87, -1, 277, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 103, 104, 105, 106, -1, -1, 109, -1, 111, -1, + -1, -1, 115, -1, -1, -1, -1, 120, -1, -1, + -1, -1, -1, 126, -1, -1, -1, -1, -1, -1, + 321, 322, -1, -1, -1, -1, -1, -1, -1, 142, + -1, -1, -1, -1, 147, -1, -1, 150, -1, 340, + -1, -1, 155, 344, 345, -1, 159, -1, -1, 162, + 163, -1, -1, -1, 167, 356, 357, 170, -1, 172, + -1, -1, -1, 364, -1, 366, -1, -1, 369, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 197, 198, 23, 200, 201, 202, + 203, -1, -1, 30, 31, -1, -1, -1, -1, 36, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 238, 239, 240, 241, -1, + -1, -1, -1, -1, 247, 248, -1, -1, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, -1, -1, 277, -1, 103, 104, 105, 106, + -1, -1, 109, -1, 111, -1, -1, -1, 115, -1, + -1, -1, -1, 120, -1, -1, -1, -1, -1, 126, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 142, -1, -1, 321, 322, + 147, -1, -1, 150, -1, -1, -1, -1, 155, -1, + -1, -1, 159, -1, -1, 162, 163, 340, -1, -1, + 167, 344, 345, 170, -1, 172, -1, -1, -1, -1, + -1, -1, -1, 356, 357, -1, -1, -1, -1, -1, + -1, -1, -1, 366, -1, -1, 369, -1, -1, -1, + 197, 198, -1, 200, 201, 202, 203, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 23, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 238, 239, 240, 241, -1, -1, -1, -1, -1, + 247, 248, -1, -1, 251, 252, 253, 254, 255, 256, + 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 270, 271, 272, 273, 274, 70, -1, + 277, -1, -1, -1, 76, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 103, 104, 105, 106, -1, -1, -1, -1, 111, + -1, -1, -1, 115, 321, 322, -1, -1, 120, -1, + -1, -1, -1, -1, 126, -1, -1, -1, 130, -1, + -1, -1, -1, 340, -1, -1, -1, 344, 345, -1, + 142, -1, -1, -1, -1, 147, -1, -1, 150, 356, + 357, -1, -1, 155, -1, -1, -1, 159, -1, 366, + 162, 163, 369, -1, -1, 167, -1, -1, 170, -1, + 172, -1, -1, -1, -1, -1, -1, 0, 1, -1, + -1, 4, -1, 6, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 197, -1, -1, 200, -1, + -1, 203, -1, 26, 27, 28, 29, -1, -1, -1, + -1, -1, -1, -1, 37, -1, -1, -1, -1, -1, + -1, -1, 45, -1, 47, -1, 49, -1, -1, 52, + 53, 54, -1, 56, -1, -1, -1, 60, 61, 62, + -1, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 104, -1, -1, -1, 287, -1, 110, 290, 291, + -1, -1, -1, -1, -1, 297, -1, 120, 70, -1, + -1, -1, -1, 126, 76, -1, -1, 79, 80, 81, + 82, 83, 84, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 327, 328, 150, 330, -1, + -1, 103, 104, 105, 106, -1, -1, -1, -1, 111, + -1, -1, -1, 115, -1, -1, -1, -1, 120, -1, + -1, -1, 354, 355, 126, -1, -1, -1, 130, -1, + -1, -1, 364, -1, 366, -1, -1, 369, -1, -1, + 142, -1, -1, -1, -1, 147, -1, -1, 150, 70, + -1, -1, -1, 155, -1, 76, -1, 159, -1, -1, + 162, 163, -1, -1, -1, 167, -1, -1, 170, -1, + 172, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 103, 104, 105, 106, -1, -1, -1, -1, + 111, -1, -1, -1, 115, 197, -1, -1, 200, 120, + -1, 203, -1, -1, -1, 126, -1, -1, -1, 130, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 142, -1, -1, 277, -1, 147, -1, -1, 150, + -1, -1, -1, -1, 155, -1, -1, -1, 159, 292, + -1, 162, 163, -1, 297, -1, 167, -1, -1, 170, + -1, 172, -1, -1, -1, -1, -1, 310, -1, -1, + -1, -1, 315, -1, 317, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 197, -1, -1, 200, + -1, -1, 203, -1, -1, 287, -1, -1, 290, 291, + -1, -1, -1, -1, -1, 297, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 327, 328, -1, 330, -1, + 104, -1, -1, -1, -1, -1, -1, -1, 70, -1, + -1, -1, -1, -1, 76, -1, -1, -1, -1, -1, + -1, -1, 354, 355, -1, -1, -1, 131, -1, -1, + 134, -1, -1, -1, 366, -1, 287, 369, -1, 290, + 291, 103, 104, 105, 106, -1, 297, 151, -1, 111, + -1, -1, -1, 115, -1, -1, -1, -1, 120, -1, + -1, -1, -1, -1, 126, -1, -1, -1, 130, -1, + -1, -1, -1, -1, -1, -1, 327, 328, -1, 330, + 142, -1, -1, -1, -1, 147, -1, -1, 150, -1, + -1, -1, -1, 155, -1, -1, -1, 159, -1, -1, + 162, 163, -1, 354, 355, 167, -1, -1, 170, -1, + 172, -1, -1, -1, -1, 366, -1, -1, 369, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 197, -1, -1, 200, -1, + -1, 203, -1, -1, -1, -1, -1, -1, -1, -1, + 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, + 274, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 317, -1, -1, -1, 321, 322, -1, + -1, -1, -1, -1, -1, 287, -1, 331, 290, 291, + -1, -1, -1, -1, -1, 297, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 357, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 327, 328, -1, 330, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 354, 355, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 366, -1, -1, 369 +}; + + /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_int16 yystos[] = +{ + 0, 373, 0, 1, 4, 6, 26, 27, 28, 29, + 37, 45, 47, 49, 52, 53, 54, 56, 60, 61, + 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 110, 120, 126, 150, 277, + 292, 310, 315, 317, 374, 430, 431, 432, 433, 508, + 509, 510, 512, 527, 374, 105, 104, 297, 504, 504, + 504, 510, 521, 510, 512, 527, 510, 515, 515, 515, + 510, 518, 433, 49, 434, 37, 45, 47, 52, 53, + 54, 56, 277, 292, 310, 315, 317, 435, 49, 436, + 37, 45, 47, 49, 52, 53, 54, 56, 277, 292, + 310, 315, 317, 441, 53, 443, 37, 42, 45, 46, + 47, 48, 51, 52, 54, 55, 56, 58, 98, 100, + 101, 102, 277, 292, 299, 300, 301, 302, 310, 315, + 317, 318, 444, 49, 50, 52, 53, 54, 292, 299, + 300, 315, 447, 45, 47, 52, 54, 58, 98, 100, + 448, 47, 449, 23, 30, 31, 36, 103, 104, 105, + 106, 109, 111, 115, 120, 126, 142, 147, 150, 155, + 159, 162, 163, 167, 170, 172, 197, 198, 200, 201, + 202, 203, 238, 239, 240, 241, 247, 248, 251, 252, + 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, + 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, + 273, 274, 277, 321, 322, 340, 344, 345, 356, 357, + 366, 369, 457, 505, 631, 632, 635, 636, 637, 641, + 704, 707, 709, 713, 718, 719, 721, 723, 733, 734, + 736, 738, 740, 742, 746, 748, 750, 752, 754, 756, + 758, 760, 762, 764, 768, 770, 772, 774, 785, 793, + 795, 797, 798, 800, 802, 804, 806, 808, 810, 812, + 814, 58, 350, 351, 352, 450, 456, 58, 451, 456, + 37, 45, 47, 49, 52, 53, 54, 56, 277, 292, + 310, 315, 317, 442, 104, 452, 453, 377, 396, 397, + 90, 284, 286, 521, 521, 521, 521, 0, 374, 504, + 504, 57, 347, 348, 524, 525, 526, 35, 37, 52, + 62, 63, 67, 70, 79, 80, 81, 82, 83, 84, + 98, 100, 253, 277, 292, 297, 304, 310, 315, 324, + 327, 328, 329, 330, 334, 338, 339, 353, 362, 364, + 531, 532, 533, 535, 536, 537, 538, 539, 540, 541, + 545, 546, 547, 550, 551, 552, 559, 563, 571, 572, + 575, 576, 577, 578, 579, 600, 601, 603, 604, 606, + 607, 610, 611, 612, 622, 623, 624, 625, 626, 629, + 630, 636, 643, 644, 645, 646, 647, 648, 652, 653, + 654, 688, 702, 707, 708, 731, 732, 733, 775, 374, + 363, 363, 374, 504, 583, 458, 461, 531, 504, 466, + 468, 631, 654, 471, 504, 477, 512, 528, 521, 510, + 512, 515, 515, 515, 518, 90, 284, 286, 521, 521, + 521, 521, 527, 440, 510, 521, 522, 437, 508, 510, + 511, 438, 510, 512, 513, 528, 439, 510, 515, 516, + 515, 515, 510, 518, 519, 90, 284, 286, 677, 440, + 440, 440, 440, 515, 521, 446, 509, 530, 510, 530, + 512, 530, 45, 530, 515, 515, 530, 518, 530, 45, + 46, 515, 530, 530, 90, 284, 303, 677, 678, 521, + 45, 530, 45, 530, 45, 530, 45, 530, 521, 521, + 521, 45, 530, 403, 528, 45, 47, 509, 510, 512, + 530, 439, 515, 439, 521, 45, 510, 530, 45, 510, + 530, 521, 415, 510, 512, 515, 515, 530, 45, 515, + 512, 104, 107, 108, 109, 735, 112, 113, 254, 255, + 258, 639, 640, 32, 33, 34, 254, 710, 133, 642, + 168, 169, 796, 112, 113, 114, 737, 114, 116, 117, + 118, 119, 739, 112, 113, 121, 122, 123, 124, 125, + 741, 112, 113, 116, 127, 128, 129, 130, 131, 132, + 133, 134, 135, 177, 743, 114, 116, 135, 143, 144, + 145, 146, 747, 114, 135, 148, 306, 749, 112, 113, + 127, 129, 130, 131, 152, 153, 154, 751, 113, 114, + 116, 135, 143, 144, 146, 156, 157, 158, 753, 128, + 144, 153, 160, 161, 755, 144, 161, 757, 153, 164, + 165, 759, 131, 135, 168, 169, 761, 135, 168, 169, + 171, 763, 135, 144, 160, 164, 168, 169, 173, 174, + 175, 176, 177, 776, 114, 168, 169, 177, 786, 164, + 199, 736, 738, 740, 742, 746, 748, 750, 752, 754, + 756, 758, 760, 762, 764, 765, 766, 767, 769, 785, + 793, 795, 127, 134, 164, 393, 773, 393, 114, 199, + 767, 771, 135, 168, 169, 204, 237, 794, 114, 126, + 128, 146, 150, 153, 242, 275, 276, 357, 720, 722, + 801, 243, 803, 243, 805, 164, 244, 245, 246, 807, + 128, 153, 799, 116, 132, 153, 160, 249, 250, 809, + 128, 153, 811, 114, 128, 135, 153, 160, 813, 104, + 131, 134, 151, 317, 331, 357, 705, 706, 707, 112, + 113, 116, 134, 254, 278, 279, 280, 281, 282, 283, + 285, 286, 287, 288, 289, 290, 291, 294, 295, 296, + 317, 724, 725, 728, 331, 341, 712, 648, 653, 342, + 238, 247, 248, 251, 252, 815, 360, 361, 402, 715, + 647, 504, 421, 456, 351, 401, 456, 389, 440, 437, + 438, 512, 528, 439, 515, 515, 518, 519, 677, 440, + 440, 440, 440, 384, 407, 46, 48, 50, 51, 58, + 59, 92, 454, 521, 521, 521, 381, 672, 687, 674, + 676, 103, 103, 103, 85, 720, 293, 623, 172, 504, + 631, 703, 703, 62, 99, 504, 104, 705, 90, 191, + 284, 724, 725, 293, 316, 293, 311, 293, 313, 314, + 560, 85, 164, 85, 85, 720, 104, 4, 375, 655, + 656, 349, 529, 536, 429, 461, 381, 294, 295, 548, + 549, 382, 427, 164, 305, 306, 307, 308, 309, 553, + 554, 414, 325, 574, 408, 5, 70, 76, 85, 87, + 111, 115, 120, 126, 130, 150, 238, 287, 290, 291, + 297, 305, 327, 328, 354, 355, 366, 586, 587, 588, + 589, 590, 591, 592, 594, 595, 596, 597, 598, 599, + 632, 635, 641, 697, 698, 699, 704, 709, 713, 719, + 720, 721, 723, 729, 730, 733, 422, 428, 38, 39, + 187, 190, 580, 581, 408, 85, 331, 332, 333, 602, + 608, 609, 408, 85, 605, 608, 386, 392, 413, 335, + 336, 337, 613, 614, 618, 619, 23, 631, 633, 634, + 45, 627, 628, 15, 16, 17, 18, 368, 8, 24, + 54, 9, 10, 11, 12, 13, 14, 19, 111, 115, + 120, 126, 142, 147, 150, 155, 159, 162, 163, 167, + 170, 172, 197, 200, 203, 330, 366, 632, 634, 635, + 649, 650, 651, 654, 689, 690, 691, 692, 693, 694, + 695, 696, 698, 699, 700, 701, 52, 52, 22, 364, + 670, 689, 690, 695, 670, 38, 364, 582, 364, 364, + 364, 364, 364, 524, 531, 583, 458, 461, 466, 468, + 471, 477, 521, 521, 521, 381, 672, 687, 674, 676, + 531, 428, 57, 57, 57, 461, 57, 468, 57, 477, + 521, 381, 404, 412, 419, 468, 428, 43, 445, 510, + 515, 530, 521, 45, 381, 510, 510, 510, 510, 404, + 412, 419, 510, 510, 512, 468, 381, 510, 510, 412, + 515, 504, 424, 7, 8, 114, 258, 259, 638, 309, + 420, 104, 127, 293, 424, 423, 388, 423, 398, 111, + 126, 111, 126, 377, 138, 139, 140, 141, 744, 396, + 423, 399, 423, 400, 397, 423, 399, 376, 387, 379, + 425, 426, 23, 38, 103, 175, 178, 179, 180, 181, + 182, 183, 184, 185, 186, 187, 188, 189, 190, 777, + 778, 779, 423, 103, 383, 421, 765, 393, 767, 182, + 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 787, 792, 417, + 423, 396, 397, 402, 722, 416, 416, 370, 416, 416, + 370, 416, 395, 391, 385, 423, 406, 405, 419, 405, + 419, 112, 113, 126, 134, 150, 278, 279, 280, 726, + 727, 728, 380, 342, 342, 103, 416, 395, 391, 385, + 406, 359, 714, 367, 428, 468, 477, 521, 381, 404, + 412, 419, 455, 456, 685, 685, 685, 294, 364, 671, + 311, 364, 686, 364, 560, 673, 364, 505, 675, 5, + 126, 150, 598, 85, 598, 620, 621, 648, 177, 23, + 23, 97, 364, 52, 52, 52, 103, 313, 52, 728, + 52, 598, 104, 298, 506, 598, 313, 314, 564, 598, + 103, 615, 616, 617, 631, 635, 648, 652, 713, 719, + 618, 598, 598, 85, 505, 21, 654, 659, 660, 661, + 668, 695, 696, 7, 365, 505, 364, 103, 103, 549, + 78, 111, 126, 172, 261, 556, 505, 103, 103, 103, + 505, 555, 554, 142, 155, 172, 326, 598, 415, 384, + 5, 598, 85, 388, 398, 377, 396, 397, 382, 85, + 408, 408, 591, 632, 699, 15, 16, 17, 18, 368, + 20, 22, 8, 54, 5, 608, 85, 87, 243, 305, + 7, 7, 103, 103, 581, 5, 7, 5, 598, 616, + 631, 635, 614, 7, 504, 364, 504, 364, 628, 700, + 700, 691, 692, 693, 647, 364, 542, 633, 690, 396, + 399, 397, 399, 376, 387, 379, 425, 426, 421, 383, + 393, 417, 408, 695, 7, 20, 15, 16, 17, 18, + 368, 7, 20, 22, 8, 689, 690, 695, 598, 598, + 103, 365, 374, 20, 374, 103, 492, 428, 460, 462, + 467, 474, 478, 582, 364, 364, 364, 364, 364, 685, + 685, 685, 671, 686, 673, 675, 103, 103, 103, 364, + 103, 103, 364, 685, 104, 380, 510, 103, 640, 423, + 390, 103, 410, 410, 388, 396, 388, 396, 114, 131, + 136, 137, 243, 396, 745, 378, 97, 783, 191, 781, + 196, 784, 194, 195, 782, 192, 193, 780, 131, 383, + 227, 231, 232, 233, 791, 222, 223, 224, 225, 789, + 226, 227, 228, 229, 230, 790, 790, 231, 234, 234, + 235, 236, 235, 114, 131, 164, 788, 418, 416, 103, + 103, 112, 113, 112, 113, 380, 380, 103, 103, 343, + 711, 103, 161, 358, 716, 720, 364, 685, 364, 364, + 364, 103, 485, 381, 564, 490, 404, 486, 103, 412, + 491, 419, 598, 5, 5, 598, 633, 90, 93, 529, + 662, 663, 38, 175, 180, 190, 778, 779, 505, 505, + 103, 648, 657, 658, 598, 598, 598, 380, 103, 598, + 52, 598, 404, 103, 566, 568, 569, 412, 104, 295, + 561, 22, 413, 85, 335, 43, 598, 375, 5, 375, + 277, 292, 297, 310, 315, 665, 666, 90, 93, 529, + 664, 667, 375, 656, 463, 388, 149, 144, 149, 557, + 558, 104, 114, 573, 635, 114, 573, 421, 114, 573, + 598, 5, 598, 598, 367, 586, 586, 587, 588, 589, + 103, 591, 586, 593, 633, 654, 598, 598, 85, 8, + 85, 632, 699, 729, 729, 598, 609, 598, 608, 619, + 378, 620, 657, 375, 543, 544, 367, 695, 689, 695, + 700, 700, 691, 692, 693, 695, 103, 689, 695, 651, + 695, 20, 20, 103, 39, 374, 365, 374, 430, 529, + 582, 37, 47, 52, 54, 56, 164, 277, 292, 310, + 315, 317, 365, 374, 430, 459, 529, 44, 94, 114, + 164, 365, 374, 430, 494, 500, 501, 529, 531, 40, + 89, 90, 91, 92, 94, 97, 114, 164, 277, 365, + 374, 430, 475, 529, 534, 535, 40, 90, 91, 92, + 114, 164, 365, 374, 430, 475, 529, 534, 41, 44, + 164, 292, 365, 374, 430, 428, 460, 462, 467, 474, + 478, 364, 364, 364, 381, 404, 412, 419, 462, 478, + 380, 380, 7, 420, 423, 396, 779, 423, 417, 371, + 371, 396, 396, 397, 397, 711, 346, 711, 103, 394, + 402, 112, 113, 717, 478, 380, 488, 489, 487, 295, + 365, 374, 430, 529, 671, 566, 568, 365, 374, 430, + 529, 686, 365, 374, 430, 529, 673, 561, 365, 374, + 430, 529, 675, 598, 598, 5, 506, 506, 663, 421, + 378, 378, 364, 536, 662, 405, 405, 380, 380, 380, + 598, 380, 20, 104, 295, 312, 565, 312, 567, 20, + 316, 562, 615, 631, 635, 617, 616, 598, 43, 82, + 83, 669, 696, 702, 191, 294, 381, 316, 311, 560, + 666, 506, 506, 667, 365, 374, 531, 396, 7, 421, + 573, 573, 70, 573, 598, 5, 598, 166, 598, 608, + 608, 5, 365, 534, 536, 659, 7, 365, 689, 689, + 103, 39, 428, 504, 523, 504, 514, 504, 517, 517, + 504, 520, 104, 90, 284, 286, 523, 523, 523, 523, + 374, 363, 79, 80, 502, 503, 631, 423, 99, 374, + 374, 374, 374, 374, 465, 636, 506, 506, 363, 95, + 96, 476, 103, 104, 129, 130, 254, 274, 275, 482, + 483, 493, 86, 87, 88, 90, 469, 470, 374, 374, + 374, 535, 465, 506, 506, 363, 483, 469, 374, 374, + 374, 104, 363, 99, 381, 365, 365, 365, 365, 365, + 488, 489, 487, 365, 365, 103, 7, 409, 103, 394, + 402, 365, 94, 134, 278, 365, 374, 430, 529, 683, + 90, 97, 134, 169, 278, 365, 374, 430, 529, 684, + 114, 278, 365, 374, 430, 529, 680, 103, 381, 565, + 567, 404, 412, 562, 419, 598, 657, 365, 380, 319, + 320, 321, 322, 323, 570, 103, 404, 103, 569, 404, + 570, 103, 412, 413, 413, 598, 375, 103, 313, 103, + 298, 506, 564, 103, 374, 558, 423, 423, 415, 423, + 598, 85, 620, 5, 365, 365, 5, 375, 544, 190, + 584, 103, 484, 461, 466, 471, 477, 523, 523, 523, + 484, 484, 484, 484, 364, 479, 631, 411, 104, 8, + 374, 374, 374, 468, 411, 8, 374, 7, 374, 5, + 374, 374, 468, 5, 374, 151, 495, 479, 374, 365, + 365, 365, 378, 103, 711, 363, 167, 172, 679, 509, + 380, 506, 103, 679, 103, 509, 380, 105, 509, 380, + 536, 294, 104, 561, 380, 103, 295, 382, 382, 566, + 568, 561, 399, 399, 598, 365, 620, 702, 187, 585, + 374, 364, 364, 364, 364, 364, 484, 484, 484, 364, + 364, 364, 364, 104, 105, 480, 481, 631, 374, 41, + 636, 482, 423, 470, 87, 464, 465, 636, 37, 87, + 292, 310, 315, 317, 472, 473, 22, 103, 104, 361, + 496, 497, 498, 631, 374, 374, 380, 380, 380, 7, + 394, 364, 425, 421, 374, 374, 374, 374, 374, 374, + 374, 134, 374, 365, 380, 103, 565, 567, 562, 365, + 375, 584, 492, 462, 467, 474, 478, 364, 364, 364, + 485, 490, 486, 491, 7, 365, 104, 465, 374, 8, + 428, 381, 404, 412, 419, 374, 483, 103, 22, 25, + 103, 104, 681, 682, 679, 381, 404, 404, 412, 585, + 365, 365, 365, 365, 365, 488, 489, 487, 365, 365, + 365, 365, 375, 481, 43, 44, 499, 374, 636, 423, + 374, 103, 103, 5, 7, 365, 374, 374, 374, 374, + 374, 374, 365, 365, 365, 374, 374, 374, 374, 505, + 631, 363, 495, 423, 103, 506, 507, 682, 374, 423, + 428, 380, 380, 380, 381, 404, 412, 419, 479, 411, + 374, 374, 374 +}; + + /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_int16 yyr1[] = +{ + 0, 372, 373, 373, 374, 374, 375, 375, 376, 377, + 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, + 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, + 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, + 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, + 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, + 428, 429, 430, 430, 430, 430, 430, 431, 431, 431, + 431, 432, 432, 432, 432, 432, 432, 432, 432, 432, + 432, 432, 432, 432, 432, 432, 432, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, + 433, 434, 435, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 435, 435, 435, 435, 435, 435, 436, + 437, 437, 438, 438, 439, 439, 440, 440, 441, 441, + 441, 441, 441, 441, 441, 441, 441, 441, 441, 441, + 441, 441, 441, 441, 442, 442, 442, 442, 442, 442, + 442, 442, 442, 442, 442, 442, 442, 442, 442, 443, + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 444, 444, 444, 444, 444, 444, 444, 444, 444, + 444, 444, 444, 444, 445, 446, 446, 447, 447, 447, + 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, + 447, 447, 447, 447, 448, 448, 448, 448, 448, 448, + 448, 449, 450, 450, 451, 451, 452, 453, 453, 454, + 454, 454, 454, 454, 454, 454, 454, 455, 455, 456, + 456, 456, 457, 458, 459, 459, 460, 460, 460, 460, + 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, + 460, 460, 461, 462, 462, 462, 462, 462, 462, 462, + 462, 462, 463, 463, 463, 464, 464, 465, 465, 466, + 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, + 467, 467, 467, 468, 468, 469, 469, 470, 470, 470, + 470, 471, 472, 472, 472, 472, 472, 473, 473, 474, + 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, + 474, 474, 474, 475, 475, 476, 476, 477, 478, 478, + 478, 478, 478, 478, 478, 479, 479, 480, 480, 480, + 481, 481, 481, 482, 482, 483, 483, 484, 485, 485, + 485, 485, 485, 486, 486, 486, 486, 486, 487, 487, + 487, 487, 487, 488, 488, 488, 488, 488, 489, 489, + 489, 489, 489, 490, 490, 490, 490, 490, 491, 491, + 491, 491, 491, 492, 492, 492, 492, 492, 493, 493, + 493, 493, 493, 494, 495, 496, 496, 497, 497, 497, + 497, 497, 498, 498, 499, 499, 499, 499, 500, 501, + 502, 502, 503, 503, 504, 504, 505, 505, 505, 506, + 507, 507, 508, 508, 509, 509, 509, 509, 509, 509, + 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, + 520, 521, 522, 523, 524, 525, 526, 527, 527, 527, + 527, 528, 529, 530, 530, 531, 531, 532, 533, 533, + 534, 534, 535, 535, 535, 535, 535, 536, 536, 536, + 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, + 536, 536, 536, 536, 536, 536, 536, 536, 536, 537, + 538, 538, 539, 540, 540, 541, 542, 542, 543, 543, + 543, 544, 545, 545, 546, 546, 547, 547, 548, 548, + 549, 549, 550, 550, 550, 551, 551, 552, 553, 553, + 554, 554, 554, 554, 554, 554, 555, 556, 556, 556, + 556, 556, 557, 557, 558, 558, 559, 559, 559, 560, + 560, 560, 561, 561, 562, 562, 563, 563, 564, 564, + 564, 565, 565, 566, 567, 567, 568, 568, 569, 569, + 570, 570, 570, 570, 570, 571, 572, 573, 573, 574, + 574, 574, 574, 574, 574, 574, 574, 575, 576, 576, + 577, 577, 577, 577, 577, 577, 578, 578, 579, 579, + 580, 580, 581, 581, 581, 581, 582, 582, 583, 584, + 584, 585, 585, 586, 586, 586, 586, 586, 586, 586, + 586, 586, 586, 586, 586, 586, 587, 587, 587, 588, + 588, 589, 589, 590, 590, 591, 592, 592, 593, 593, + 594, 594, 595, 596, 597, 597, 598, 598, 598, 599, + 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, + 599, 599, 599, 600, 600, 601, 602, 602, 602, 603, + 603, 604, 605, 605, 605, 605, 605, 606, 606, 607, + 607, 608, 608, 609, 609, 609, 610, 610, 610, 610, + 611, 611, 612, 613, 613, 614, 614, 615, 615, 616, + 616, 616, 617, 617, 617, 617, 618, 618, 619, 619, + 620, 620, 621, 622, 622, 622, 623, 623, 623, 624, + 624, 625, 625, 626, 627, 627, 628, 629, 629, 630, + 631, 632, 632, 633, 633, 634, 635, 636, 636, 636, + 636, 636, 636, 636, 636, 636, 636, 636, 636, 636, + 636, 636, 637, 638, 638, 638, 639, 639, 639, 639, + 639, 640, 640, 641, 641, 642, 642, 643, 643, 643, + 644, 644, 645, 645, 646, 646, 647, 648, 648, 649, + 650, 651, 651, 652, 653, 653, 653, 654, 655, 655, + 655, 656, 656, 656, 657, 657, 658, 659, 659, 660, + 660, 661, 661, 662, 662, 663, 663, 663, 664, 664, + 665, 665, 666, 666, 666, 666, 666, 666, 666, 666, + 666, 667, 667, 667, 668, 669, 669, 670, 670, 670, + 670, 671, 672, 673, 674, 675, 676, 677, 677, 677, + 678, 678, 678, 679, 679, 680, 680, 681, 681, 682, + 683, 683, 683, 684, 684, 684, 684, 684, 685, 686, + 686, 687, 688, 688, 688, 688, 688, 688, 688, 688, + 689, 689, 690, 690, 690, 691, 691, 691, 692, 692, + 693, 693, 694, 694, 695, 696, 696, 696, 696, 697, + 697, 698, 699, 699, 699, 699, 699, 699, 699, 699, + 699, 699, 699, 699, 699, 699, 700, 700, 700, 700, + 700, 700, 700, 700, 700, 700, 700, 700, 700, 700, + 700, 700, 700, 700, 701, 701, 701, 701, 701, 701, + 701, 702, 702, 702, 702, 702, 702, 703, 703, 704, + 704, 704, 705, 705, 706, 706, 706, 706, 706, 707, + 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, + 707, 707, 707, 707, 707, 707, 707, 707, 707, 707, + 707, 707, 707, 708, 708, 708, 708, 708, 708, 709, + 709, 710, 710, 710, 711, 711, 712, 712, 713, 714, + 714, 715, 715, 716, 716, 717, 717, 718, 718, 719, + 719, 719, 720, 720, 721, 721, 722, 722, 722, 722, + 723, 723, 723, 724, 724, 725, 725, 725, 725, 725, + 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, + 725, 725, 726, 726, 726, 726, 726, 726, 726, 727, + 727, 727, 727, 728, 728, 728, 728, 729, 729, 730, + 730, 731, 731, 731, 731, 732, 733, 733, 733, 733, + 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, + 733, 733, 733, 733, 733, 733, 733, 733, 734, 735, + 735, 735, 735, 736, 737, 737, 737, 738, 739, 739, + 739, 739, 739, 740, 741, 741, 741, 741, 741, 741, + 741, 741, 741, 742, 742, 742, 743, 743, 743, 743, + 743, 743, 743, 743, 743, 743, 743, 743, 744, 744, + 744, 744, 745, 745, 745, 745, 745, 746, 747, 747, + 747, 747, 747, 747, 747, 748, 749, 749, 749, 749, + 750, 751, 751, 751, 751, 751, 751, 751, 751, 751, + 752, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 754, 755, 755, 755, 755, 755, 756, 757, 757, + 758, 759, 759, 759, 760, 761, 761, 761, 761, 762, + 763, 763, 763, 763, 764, 764, 764, 764, 765, 765, + 765, 765, 765, 765, 765, 765, 765, 765, 765, 765, + 765, 765, 766, 766, 766, 767, 767, 768, 768, 769, + 769, 770, 770, 771, 771, 772, 772, 773, 773, 773, + 774, 775, 776, 776, 776, 776, 776, 776, 776, 776, + 776, 776, 777, 777, 777, 777, 777, 777, 778, 778, + 778, 778, 778, 779, 779, 779, 779, 779, 779, 779, + 779, 779, 779, 779, 779, 780, 780, 781, 782, 782, + 783, 784, 785, 785, 786, 786, 786, 787, 787, 787, + 787, 787, 787, 787, 787, 787, 787, 787, 787, 787, + 787, 787, 787, 787, 787, 788, 788, 788, 789, 789, + 789, 789, 790, 790, 790, 790, 790, 791, 791, 791, + 791, 792, 792, 792, 792, 792, 792, 792, 792, 792, + 792, 792, 792, 793, 793, 794, 794, 794, 794, 795, + 796, 796, 797, 797, 797, 797, 797, 797, 797, 797, + 798, 799, 799, 800, 801, 801, 801, 801, 802, 803, + 804, 805, 806, 807, 807, 807, 807, 808, 809, 809, + 809, 809, 809, 809, 810, 811, 811, 812, 813, 813, + 813, 813, 813, 814, 815, 815, 815, 815, 815 +}; + + /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 0, 2, 1, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 5, 5, 3, 2, 1, 1, 2, + 2, 1, 2, 2, 2, 2, 2, 2, 3, 3, + 2, 2, 3, 3, 3, 2, 3, 2, 6, 2, + 6, 3, 2, 6, 6, 3, 6, 3, 5, 7, + 5, 7, 8, 8, 8, 5, 7, 5, 7, 5, + 7, 3, 2, 6, 2, 6, 6, 6, 3, 6, + 3, 5, 5, 8, 8, 8, 5, 5, 5, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, + 6, 2, 2, 2, 3, 2, 2, 6, 3, 3, + 5, 3, 3, 3, 2, 2, 2, 2, 2, 3, + 2, 2, 6, 3, 3, 5, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 3, 2, 2, 3, 3, + 2, 3, 3, 2, 3, 3, 2, 3, 3, 2, + 3, 3, 2, 3, 3, 2, 2, 2, 2, 2, + 2, 4, 5, 2, 2, 1, 2, 2, 2, 3, + 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, + 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, + 2, 3, 2, 1, 2, 1, 3, 0, 1, 0, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, + 1, 2, 1, 0, 2, 1, 0, 2, 2, 3, + 8, 8, 8, 8, 9, 9, 10, 10, 10, 9, + 9, 9, 0, 0, 2, 2, 3, 3, 3, 3, + 5, 3, 0, 2, 3, 1, 3, 1, 3, 0, + 0, 2, 2, 5, 4, 4, 4, 4, 3, 4, + 2, 3, 3, 1, 1, 3, 1, 1, 1, 1, + 1, 0, 2, 2, 2, 2, 2, 1, 0, 0, + 2, 2, 4, 4, 8, 6, 7, 7, 4, 3, + 4, 3, 3, 3, 2, 1, 1, 0, 0, 2, + 2, 5, 5, 3, 4, 3, 1, 1, 3, 3, + 1, 1, 1, 1, 1, 1, 3, 0, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 2, 0, 2, + 2, 2, 2, 0, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 7, 2, 1, 1, 1, 1, 1, + 3, 3, 1, 2, 2, 2, 3, 0, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 2, 2, 1, 2, 1, 1, + 2, 3, 2, 3, 1, 2, 3, 1, 2, 3, + 1, 2, 3, 1, 2, 2, 2, 1, 2, 2, + 2, 2, 2, 0, 1, 1, 2, 1, 1, 2, + 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, + 2, 2, 2, 1, 1, 2, 1, 1, 2, 3, + 1, 1, 5, 1, 1, 3, 3, 1, 1, 3, + 3, 5, 4, 5, 1, 2, 1, 3, 1, 2, + 2, 2, 1, 3, 3, 1, 2, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 1, 3, 3, 1, + 2, 1, 3, 1, 1, 1, 6, 6, 4, 1, + 1, 0, 1, 1, 0, 3, 6, 4, 1, 1, + 0, 0, 3, 3, 0, 2, 2, 3, 2, 2, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, + 6, 3, 6, 3, 5, 3, 5, 2, 1, 1, + 3, 4, 4, 5, 6, 5, 1, 2, 1, 3, + 1, 2, 2, 2, 1, 1, 6, 8, 0, 0, + 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 1, 3, 3, 1, + 3, 1, 3, 1, 3, 1, 1, 3, 1, 1, + 3, 1, 3, 3, 1, 1, 1, 1, 1, 1, + 2, 3, 3, 4, 5, 2, 3, 2, 6, 4, + 3, 4, 3, 2, 1, 1, 3, 4, 1, 2, + 1, 1, 2, 3, 1, 3, 4, 3, 5, 3, + 6, 1, 3, 1, 1, 1, 2, 4, 6, 6, + 1, 2, 1, 1, 2, 2, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, + 1, 2, 1, 4, 5, 6, 1, 1, 1, 7, + 8, 6, 1, 1, 1, 2, 2, 6, 8, 1, + 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 4, 1, 1, 2, 1, 1, 1, 1, + 1, 3, 1, 4, 4, 0, 2, 1, 3, 3, + 1, 3, 1, 3, 1, 3, 1, 1, 3, 3, + 3, 1, 1, 3, 1, 1, 1, 3, 1, 3, + 3, 3, 3, 5, 1, 2, 1, 1, 2, 1, + 1, 2, 1, 1, 2, 2, 2, 1, 1, 2, + 1, 2, 2, 6, 6, 6, 4, 5, 6, 4, + 4, 2, 2, 1, 1, 1, 1, 1, 1, 2, + 2, 4, 0, 4, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 2, 2, 6, 3, 1, 3, 3, + 3, 7, 3, 3, 3, 3, 3, 3, 0, 4, + 4, 0, 2, 2, 4, 4, 5, 5, 3, 3, + 3, 3, 1, 1, 1, 1, 3, 3, 1, 3, + 1, 3, 1, 3, 1, 1, 1, 3, 3, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 1, + 2, 2, 1, 1, 1, 2, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, + 2, 2, 2, 3, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 2, 1, 1, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 5, 3, 5, 1, 5, 5, 3, + 5, 1, 1, 1, 0, 2, 1, 1, 6, 2, + 0, 1, 1, 1, 1, 1, 1, 5, 6, 8, + 6, 5, 2, 2, 3, 4, 1, 1, 1, 2, + 3, 4, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, + 3, 3, 3, 1, 1, 1, 1, 1, 1, 3, + 3, 5, 5, 5, 6, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 1, 1, 1, 1, 1, 1, 7, 1, + 1, 2, 1, 3, 1, 1, 2, 3, 1, 1, + 1, 1, 2, 3, 1, 1, 1, 1, 1, 3, + 3, 3, 3, 3, 5, 4, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 1, 1, 1, 1, 3, 2, 1, + 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, + 3, 1, 1, 1, 3, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 2, 3, 3, 9, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, + 1, 2, 2, 1, 1, 3, 3, 1, 1, 1, + 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 4, 1, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 5, 1, 1, 1, 1, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 3, 1, 1, 3, 1, 1, 2, 1, 3, 4, + 3, 1, 3, 1, 1, 1, 4, 3, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 3, 1, 1, + 2, 1, 1, 2, 2, 2, 2, 2, 2 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (&yylloc, nft, scanner, state, YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + +/* 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). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (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 (0) +#endif + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include <stdio.h> /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + +/* 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 defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + +/* Print *YYLOCP on YYO. Private, do not rely on its existence. */ + +YY_ATTRIBUTE_UNUSED +static int +yy_location_print_ (FILE *yyo, YYLTYPE const * const yylocp) +{ + int res = 0; + int end_col = 0 != yylocp->last_column ? yylocp->last_column - 1 : 0; + if (0 <= yylocp->first_line) + { + res += YYFPRINTF (yyo, "%d", yylocp->first_line); + if (0 <= yylocp->first_column) + res += YYFPRINTF (yyo, ".%d", yylocp->first_column); + } + if (0 <= yylocp->last_line) + { + if (yylocp->first_line < yylocp->last_line) + { + res += YYFPRINTF (yyo, "-%d", yylocp->last_line); + if (0 <= end_col) + res += YYFPRINTF (yyo, ".%d", end_col); + } + else if (0 <= end_col && yylocp->first_column < end_col) + res += YYFPRINTF (yyo, "-%d", end_col); + } + return res; + } + +# define YY_LOCATION_PRINT(File, Loc) \ + yy_location_print_ (File, &(Loc)) + +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +# endif /* !defined YY_LOCATION_PRINT */ + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value, Location, nft, scanner, state); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct nft_ctx *nft, void *scanner, struct parser_state *state) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + YY_USE (yylocationp); + YY_USE (nft); + YY_USE (scanner); + YY_USE (state); + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yykind < YYNTOKENS) + YYPRINT (yyo, yytoknum[yykind], *yyvaluep); +# endif + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep, YYLTYPE const * const yylocationp, struct nft_ctx *nft, void *scanner, struct parser_state *state) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + YY_LOCATION_PRINT (yyo, *yylocationp); + YYFPRINTF (yyo, ": "); + yy_symbol_value_print (yyo, yykind, yyvaluep, yylocationp, nft, scanner, state); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, YYLTYPE *yylsp, + int yyrule, struct nft_ctx *nft, void *scanner, struct parser_state *state) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)], + &(yylsp[(yyi + 1) - (yynrhs)]), nft, scanner, state); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, yylsp, Rule, nft, scanner, state); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, 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 200 +#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 10000 +#endif + + +/* Context of a parse error. */ +typedef struct +{ + yy_state_t *yyssp; + yysymbol_kind_t yytoken; + YYLTYPE *yylloc; +} yypcontext_t; + +/* Put in YYARG at most YYARGN of the expected tokens given the + current YYCTX, and return the number of tokens stored in YYARG. If + YYARG is null, return the number of expected tokens (guaranteed to + be less than YYNTOKENS). Return YYENOMEM on memory exhaustion. + Return 0 if there are more than YYARGN expected tokens, yet fill + YYARG up to YYARGN. */ +static int +yypcontext_expected_tokens (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + int yyn = yypact[+*yyctx->yyssp]; + if (!yypact_value_is_default (yyn)) + { + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. In other words, skip the first -YYN actions for + this state because they are default actions. */ + 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 yyx; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYSYMBOL_YYerror + && !yytable_value_is_error (yytable[yyx + yyn])) + { + if (!yyarg) + ++yycount; + else if (yycount == yyargn) + return 0; + else + yyarg[yycount++] = YY_CAST (yysymbol_kind_t, yyx); + } + } + if (yyarg && yycount == 0 && 0 < yyargn) + yyarg[0] = YYSYMBOL_YYEMPTY; + return yycount; +} + + + + +#ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen(S) (YY_CAST (YYPTRDIFF_T, strlen (S))) +# else +/* Return the length of YYSTR. */ +static YYPTRDIFF_T +yystrlen (const char *yystr) +{ + YYPTRDIFF_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. */ +static char * +yystpcpy (char *yydest, const char *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 YYPTRDIFF_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYPTRDIFF_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; + else + goto append; + + append: + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (yyres) + return yystpcpy (yyres, yystr) - yyres; + else + return yystrlen (yystr); +} +#endif + + +static int +yy_syntax_error_arguments (const yypcontext_t *yyctx, + yysymbol_kind_t yyarg[], int yyargn) +{ + /* Actual size of YYARG. */ + int yycount = 0; + /* There are many possibilities here to consider: + - If this state is a consistent state with a default action, then + the only way this function was invoked is if the default action + is an error action. In that case, don't check for expected + tokens because there are none. + - The only way there can be no lookahead present (in yychar) is if + this state is a consistent state with a default action. Thus, + detecting the absence of a lookahead is sufficient to determine + that there is no unexpected or expected token to report. In that + case, just report a simple "syntax error". + - Don't assume there isn't a lookahead just because this state is a + consistent state with a default action. There might have been a + previous inconsistent state, consistent state with a non-default + action, or user semantic action that manipulated yychar. + - Of course, the expected token list depends on states to have + correct lookahead information, and it depends on the parser not + to perform extra reductions after fetching a lookahead from the + scanner and before detecting a syntax error. Thus, state merging + (from LALR or IELR) and default reductions corrupt the expected + token list. However, the list is correct for canonical LR with + one exception: it will still contain any token that will not be + accepted due to an error action in a later state. + */ + if (yyctx->yytoken != YYSYMBOL_YYEMPTY) + { + int yyn; + if (yyarg) + yyarg[yycount] = yyctx->yytoken; + ++yycount; + yyn = yypcontext_expected_tokens (yyctx, + yyarg ? yyarg + 1 : yyarg, yyargn - 1); + if (yyn == YYENOMEM) + return YYENOMEM; + else + yycount += yyn; + } + return yycount; +} + +/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message + about the unexpected token YYTOKEN for the state stack whose top is + YYSSP. + + Return 0 if *YYMSG was successfully written. Return -1 if *YYMSG is + not large enough to hold the message. In that case, also set + *YYMSG_ALLOC to the required number of bytes. Return YYENOMEM if the + required number of bytes is too large to store. */ +static int +yysyntax_error (YYPTRDIFF_T *yymsg_alloc, char **yymsg, + const yypcontext_t *yyctx) +{ + enum { YYARGS_MAX = 5 }; + /* Internationalized format string. */ + const char *yyformat = YY_NULLPTR; + /* Arguments of yyformat: reported tokens (one for the "unexpected", + one per "expected"). */ + yysymbol_kind_t yyarg[YYARGS_MAX]; + /* Cumulated lengths of YYARG. */ + YYPTRDIFF_T yysize = 0; + + /* Actual size of YYARG. */ + int yycount = yy_syntax_error_arguments (yyctx, yyarg, YYARGS_MAX); + if (yycount == YYENOMEM) + return YYENOMEM; + + switch (yycount) + { +#define YYCASE_(N, S) \ + case N: \ + yyformat = S; \ + break + default: /* Avoid compiler warnings. */ + YYCASE_(0, YY_("syntax error")); + YYCASE_(1, YY_("syntax error, unexpected %s")); + YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); + YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); + YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); + YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); +#undef YYCASE_ + } + + /* Compute error message size. Don't count the "%s"s, but reserve + room for the terminator. */ + yysize = yystrlen (yyformat) - 2 * yycount + 1; + { + int yyi; + for (yyi = 0; yyi < yycount; ++yyi) + { + YYPTRDIFF_T yysize1 + = yysize + yytnamerr (YY_NULLPTR, yytname[yyarg[yyi]]); + if (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM) + yysize = yysize1; + else + return YYENOMEM; + } + } + + if (*yymsg_alloc < yysize) + { + *yymsg_alloc = 2 * yysize; + if (! (yysize <= *yymsg_alloc + && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) + *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; + return -1; + } + + /* 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 = *yymsg; + int yyi = 0; + while ((*yyp = *yyformat) != '\0') + if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yytname[yyarg[yyi++]]); + yyformat += 2; + } + else + { + ++yyp; + ++yyformat; + } + } + return 0; +} + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, struct nft_ctx *nft, void *scanner, struct parser_state *state) +{ + YY_USE (yyvaluep); + YY_USE (yylocationp); + YY_USE (nft); + YY_USE (scanner); + YY_USE (state); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + switch (yykind) + { + case YYSYMBOL_STRING: /* "string" */ +#line 360 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6179 "parser_bison.c" + break; + + case YYSYMBOL_QUOTED_STRING: /* "quoted string" */ +#line 360 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6185 "parser_bison.c" + break; + + case YYSYMBOL_ASTERISK_STRING: /* "string with a trailing asterisk" */ +#line 360 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6191 "parser_bison.c" + break; + + case YYSYMBOL_line: /* line */ +#line 684 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6197 "parser_bison.c" + break; + + case YYSYMBOL_base_cmd: /* base_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6203 "parser_bison.c" + break; + + case YYSYMBOL_add_cmd: /* add_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6209 "parser_bison.c" + break; + + case YYSYMBOL_replace_cmd: /* replace_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6215 "parser_bison.c" + break; + + case YYSYMBOL_create_cmd: /* create_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6221 "parser_bison.c" + break; + + case YYSYMBOL_insert_cmd: /* insert_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6227 "parser_bison.c" + break; + + case YYSYMBOL_table_or_id_spec: /* table_or_id_spec */ +#line 690 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6233 "parser_bison.c" + break; + + case YYSYMBOL_chain_or_id_spec: /* chain_or_id_spec */ +#line 692 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6239 "parser_bison.c" + break; + + case YYSYMBOL_set_or_id_spec: /* set_or_id_spec */ +#line 697 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6245 "parser_bison.c" + break; + + case YYSYMBOL_obj_or_id_spec: /* obj_or_id_spec */ +#line 699 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6251 "parser_bison.c" + break; + + case YYSYMBOL_delete_cmd: /* delete_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6257 "parser_bison.c" + break; + + case YYSYMBOL_destroy_cmd: /* destroy_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6263 "parser_bison.c" + break; + + case YYSYMBOL_get_cmd: /* get_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6269 "parser_bison.c" + break; + + case YYSYMBOL_list_cmd: /* list_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6275 "parser_bison.c" + break; + + case YYSYMBOL_basehook_device_name: /* basehook_device_name */ +#line 711 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6281 "parser_bison.c" + break; + + case YYSYMBOL_basehook_spec: /* basehook_spec */ +#line 705 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6287 "parser_bison.c" + break; + + case YYSYMBOL_reset_cmd: /* reset_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6293 "parser_bison.c" + break; + + case YYSYMBOL_flush_cmd: /* flush_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6299 "parser_bison.c" + break; + + case YYSYMBOL_rename_cmd: /* rename_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6305 "parser_bison.c" + break; + + case YYSYMBOL_import_cmd: /* import_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6311 "parser_bison.c" + break; + + case YYSYMBOL_export_cmd: /* export_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6317 "parser_bison.c" + break; + + case YYSYMBOL_monitor_cmd: /* monitor_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6323 "parser_bison.c" + break; + + case YYSYMBOL_monitor_event: /* monitor_event */ +#line 930 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6329 "parser_bison.c" + break; + + case YYSYMBOL_describe_cmd: /* describe_cmd */ +#line 687 "parser_bison.y" + { cmd_free(((*yyvaluep).cmd)); } +#line 6335 "parser_bison.c" + break; + + case YYSYMBOL_table_block_alloc: /* table_block_alloc */ +#line 717 "parser_bison.y" + { close_scope(state); table_free(((*yyvaluep).table)); } +#line 6341 "parser_bison.c" + break; + + case YYSYMBOL_chain_block_alloc: /* chain_block_alloc */ +#line 719 "parser_bison.y" + { close_scope(state); chain_free(((*yyvaluep).chain)); } +#line 6347 "parser_bison.c" + break; + + case YYSYMBOL_typeof_data_expr: /* typeof_data_expr */ +#line 791 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6353 "parser_bison.c" + break; + + case YYSYMBOL_typeof_expr: /* typeof_expr */ +#line 791 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6359 "parser_bison.c" + break; + + case YYSYMBOL_set_block_alloc: /* set_block_alloc */ +#line 728 "parser_bison.y" + { set_free(((*yyvaluep).set)); } +#line 6365 "parser_bison.c" + break; + + case YYSYMBOL_set_block_expr: /* set_block_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6371 "parser_bison.c" + break; + + case YYSYMBOL_map_block_alloc: /* map_block_alloc */ +#line 731 "parser_bison.y" + { set_free(((*yyvaluep).set)); } +#line 6377 "parser_bison.c" + break; + + case YYSYMBOL_flowtable_block_alloc: /* flowtable_block_alloc */ +#line 735 "parser_bison.y" + { flowtable_free(((*yyvaluep).flowtable)); } +#line 6383 "parser_bison.c" + break; + + case YYSYMBOL_flowtable_expr: /* flowtable_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6389 "parser_bison.c" + break; + + case YYSYMBOL_flowtable_list_expr: /* flowtable_list_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6395 "parser_bison.c" + break; + + case YYSYMBOL_flowtable_expr_member: /* flowtable_expr_member */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6401 "parser_bison.c" + break; + + case YYSYMBOL_data_type_atom_expr: /* data_type_atom_expr */ +#line 681 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6407 "parser_bison.c" + break; + + case YYSYMBOL_data_type_expr: /* data_type_expr */ +#line 681 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6413 "parser_bison.c" + break; + + case YYSYMBOL_obj_block_alloc: /* obj_block_alloc */ +#line 738 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 6419 "parser_bison.c" + break; + + case YYSYMBOL_type_identifier: /* type_identifier */ +#line 676 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6425 "parser_bison.c" + break; + + case YYSYMBOL_extended_prio_name: /* extended_prio_name */ +#line 711 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6431 "parser_bison.c" + break; + + case YYSYMBOL_dev_spec: /* dev_spec */ +#line 714 "parser_bison.y" + { xfree(((*yyvaluep).expr)); } +#line 6437 "parser_bison.c" + break; + + case YYSYMBOL_policy_expr: /* policy_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6443 "parser_bison.c" + break; + + case YYSYMBOL_identifier: /* identifier */ +#line 676 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6449 "parser_bison.c" + break; + + case YYSYMBOL_string: /* string */ +#line 676 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6455 "parser_bison.c" + break; + + case YYSYMBOL_table_spec: /* table_spec */ +#line 690 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6461 "parser_bison.c" + break; + + case YYSYMBOL_tableid_spec: /* tableid_spec */ +#line 690 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6467 "parser_bison.c" + break; + + case YYSYMBOL_chain_spec: /* chain_spec */ +#line 692 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6473 "parser_bison.c" + break; + + case YYSYMBOL_chainid_spec: /* chainid_spec */ +#line 692 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6479 "parser_bison.c" + break; + + case YYSYMBOL_chain_identifier: /* chain_identifier */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6485 "parser_bison.c" + break; + + case YYSYMBOL_set_spec: /* set_spec */ +#line 697 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6491 "parser_bison.c" + break; + + case YYSYMBOL_setid_spec: /* setid_spec */ +#line 697 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6497 "parser_bison.c" + break; + + case YYSYMBOL_set_identifier: /* set_identifier */ +#line 702 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6503 "parser_bison.c" + break; + + case YYSYMBOL_flowtable_spec: /* flowtable_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6509 "parser_bison.c" + break; + + case YYSYMBOL_flowtableid_spec: /* flowtableid_spec */ +#line 702 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6515 "parser_bison.c" + break; + + case YYSYMBOL_obj_spec: /* obj_spec */ +#line 699 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6521 "parser_bison.c" + break; + + case YYSYMBOL_objid_spec: /* objid_spec */ +#line 699 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6527 "parser_bison.c" + break; + + case YYSYMBOL_obj_identifier: /* obj_identifier */ +#line 702 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6533 "parser_bison.c" + break; + + case YYSYMBOL_handle_spec: /* handle_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6539 "parser_bison.c" + break; + + case YYSYMBOL_position_spec: /* position_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6545 "parser_bison.c" + break; + + case YYSYMBOL_index_spec: /* index_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6551 "parser_bison.c" + break; + + case YYSYMBOL_rule_position: /* rule_position */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6557 "parser_bison.c" + break; + + case YYSYMBOL_ruleid_spec: /* ruleid_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6563 "parser_bison.c" + break; + + case YYSYMBOL_comment_spec: /* comment_spec */ +#line 676 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6569 "parser_bison.c" + break; + + case YYSYMBOL_ruleset_spec: /* ruleset_spec */ +#line 695 "parser_bison.y" + { handle_free(&((*yyvaluep).handle)); } +#line 6575 "parser_bison.c" + break; + + case YYSYMBOL_rule: /* rule */ +#line 721 "parser_bison.y" + { rule_free(((*yyvaluep).rule)); } +#line 6581 "parser_bison.c" + break; + + case YYSYMBOL_stmt_list: /* stmt_list */ +#line 741 "parser_bison.y" + { stmt_list_free(((*yyvaluep).list)); xfree(((*yyvaluep).list)); } +#line 6587 "parser_bison.c" + break; + + case YYSYMBOL_stateful_stmt_list: /* stateful_stmt_list */ +#line 741 "parser_bison.y" + { stmt_list_free(((*yyvaluep).list)); xfree(((*yyvaluep).list)); } +#line 6593 "parser_bison.c" + break; + + case YYSYMBOL_stateful_stmt: /* stateful_stmt */ +#line 745 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6599 "parser_bison.c" + break; + + case YYSYMBOL_stmt: /* stmt */ +#line 743 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6605 "parser_bison.c" + break; + + case YYSYMBOL_xt_stmt: /* xt_stmt */ +#line 954 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6611 "parser_bison.c" + break; + + case YYSYMBOL_chain_stmt: /* chain_stmt */ +#line 768 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6617 "parser_bison.c" + break; + + case YYSYMBOL_verdict_stmt: /* verdict_stmt */ +#line 743 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6623 "parser_bison.c" + break; + + case YYSYMBOL_verdict_map_stmt: /* verdict_map_stmt */ +#line 826 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6629 "parser_bison.c" + break; + + case YYSYMBOL_verdict_map_expr: /* verdict_map_expr */ +#line 829 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6635 "parser_bison.c" + break; + + case YYSYMBOL_verdict_map_list_expr: /* verdict_map_list_expr */ +#line 829 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6641 "parser_bison.c" + break; + + case YYSYMBOL_verdict_map_list_member_expr: /* verdict_map_list_member_expr */ +#line 829 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6647 "parser_bison.c" + break; + + case YYSYMBOL_connlimit_stmt: /* connlimit_stmt */ +#line 756 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6653 "parser_bison.c" + break; + + case YYSYMBOL_counter_stmt: /* counter_stmt */ +#line 745 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6659 "parser_bison.c" + break; + + case YYSYMBOL_counter_stmt_alloc: /* counter_stmt_alloc */ +#line 745 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6665 "parser_bison.c" + break; + + case YYSYMBOL_last_stmt: /* last_stmt */ +#line 745 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6671 "parser_bison.c" + break; + + case YYSYMBOL_log_stmt: /* log_stmt */ +#line 753 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6677 "parser_bison.c" + break; + + case YYSYMBOL_log_stmt_alloc: /* log_stmt_alloc */ +#line 753 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6683 "parser_bison.c" + break; + + case YYSYMBOL_limit_stmt: /* limit_stmt */ +#line 756 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6689 "parser_bison.c" + break; + + case YYSYMBOL_quota_unit: /* quota_unit */ +#line 711 "parser_bison.y" + { xfree(((*yyvaluep).string)); } +#line 6695 "parser_bison.c" + break; + + case YYSYMBOL_quota_stmt: /* quota_stmt */ +#line 756 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6701 "parser_bison.c" + break; + + case YYSYMBOL_reject_stmt: /* reject_stmt */ +#line 759 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6707 "parser_bison.c" + break; + + case YYSYMBOL_reject_stmt_alloc: /* reject_stmt_alloc */ +#line 759 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6713 "parser_bison.c" + break; + + case YYSYMBOL_reject_with_expr: /* reject_with_expr */ +#line 774 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6719 "parser_bison.c" + break; + + case YYSYMBOL_nat_stmt: /* nat_stmt */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6725 "parser_bison.c" + break; + + case YYSYMBOL_nat_stmt_alloc: /* nat_stmt_alloc */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6731 "parser_bison.c" + break; + + case YYSYMBOL_tproxy_stmt: /* tproxy_stmt */ +#line 764 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6737 "parser_bison.c" + break; + + case YYSYMBOL_synproxy_stmt: /* synproxy_stmt */ +#line 766 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6743 "parser_bison.c" + break; + + case YYSYMBOL_synproxy_stmt_alloc: /* synproxy_stmt_alloc */ +#line 766 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6749 "parser_bison.c" + break; + + case YYSYMBOL_synproxy_obj: /* synproxy_obj */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 6755 "parser_bison.c" + break; + + case YYSYMBOL_primary_stmt_expr: /* primary_stmt_expr */ +#line 813 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6761 "parser_bison.c" + break; + + case YYSYMBOL_shift_stmt_expr: /* shift_stmt_expr */ +#line 815 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6767 "parser_bison.c" + break; + + case YYSYMBOL_and_stmt_expr: /* and_stmt_expr */ +#line 817 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6773 "parser_bison.c" + break; + + case YYSYMBOL_exclusive_or_stmt_expr: /* exclusive_or_stmt_expr */ +#line 817 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6779 "parser_bison.c" + break; + + case YYSYMBOL_inclusive_or_stmt_expr: /* inclusive_or_stmt_expr */ +#line 817 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6785 "parser_bison.c" + break; + + case YYSYMBOL_basic_stmt_expr: /* basic_stmt_expr */ +#line 813 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6791 "parser_bison.c" + break; + + case YYSYMBOL_concat_stmt_expr: /* concat_stmt_expr */ +#line 805 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6797 "parser_bison.c" + break; + + case YYSYMBOL_map_stmt_expr_set: /* map_stmt_expr_set */ +#line 805 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6803 "parser_bison.c" + break; + + case YYSYMBOL_map_stmt_expr: /* map_stmt_expr */ +#line 805 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6809 "parser_bison.c" + break; + + case YYSYMBOL_prefix_stmt_expr: /* prefix_stmt_expr */ +#line 810 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6815 "parser_bison.c" + break; + + case YYSYMBOL_range_stmt_expr: /* range_stmt_expr */ +#line 810 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6821 "parser_bison.c" + break; + + case YYSYMBOL_multiton_stmt_expr: /* multiton_stmt_expr */ +#line 808 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6827 "parser_bison.c" + break; + + case YYSYMBOL_stmt_expr: /* stmt_expr */ +#line 805 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6833 "parser_bison.c" + break; + + case YYSYMBOL_masq_stmt: /* masq_stmt */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6839 "parser_bison.c" + break; + + case YYSYMBOL_masq_stmt_alloc: /* masq_stmt_alloc */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6845 "parser_bison.c" + break; + + case YYSYMBOL_redir_stmt: /* redir_stmt */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6851 "parser_bison.c" + break; + + case YYSYMBOL_redir_stmt_alloc: /* redir_stmt_alloc */ +#line 761 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6857 "parser_bison.c" + break; + + case YYSYMBOL_dup_stmt: /* dup_stmt */ +#line 777 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6863 "parser_bison.c" + break; + + case YYSYMBOL_fwd_stmt: /* fwd_stmt */ +#line 779 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6869 "parser_bison.c" + break; + + case YYSYMBOL_queue_stmt: /* queue_stmt */ +#line 772 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6875 "parser_bison.c" + break; + + case YYSYMBOL_queue_stmt_compat: /* queue_stmt_compat */ +#line 772 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6881 "parser_bison.c" + break; + + case YYSYMBOL_queue_stmt_alloc: /* queue_stmt_alloc */ +#line 772 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6887 "parser_bison.c" + break; + + case YYSYMBOL_queue_expr: /* queue_expr */ +#line 774 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6893 "parser_bison.c" + break; + + case YYSYMBOL_queue_stmt_expr_simple: /* queue_stmt_expr_simple */ +#line 774 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6899 "parser_bison.c" + break; + + case YYSYMBOL_queue_stmt_expr: /* queue_stmt_expr */ +#line 774 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6905 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_expr_stmt: /* set_elem_expr_stmt */ +#line 836 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6911 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_expr_stmt_alloc: /* set_elem_expr_stmt_alloc */ +#line 836 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6917 "parser_bison.c" + break; + + case YYSYMBOL_set_stmt: /* set_stmt */ +#line 781 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6923 "parser_bison.c" + break; + + case YYSYMBOL_map_stmt: /* map_stmt */ +#line 784 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6929 "parser_bison.c" + break; + + case YYSYMBOL_meter_stmt: /* meter_stmt */ +#line 786 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6935 "parser_bison.c" + break; + + case YYSYMBOL_flow_stmt_legacy_alloc: /* flow_stmt_legacy_alloc */ +#line 786 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6941 "parser_bison.c" + break; + + case YYSYMBOL_meter_stmt_alloc: /* meter_stmt_alloc */ +#line 786 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6947 "parser_bison.c" + break; + + case YYSYMBOL_match_stmt: /* match_stmt */ +#line 743 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 6953 "parser_bison.c" + break; + + case YYSYMBOL_variable_expr: /* variable_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6959 "parser_bison.c" + break; + + case YYSYMBOL_symbol_expr: /* symbol_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6965 "parser_bison.c" + break; + + case YYSYMBOL_set_ref_expr: /* set_ref_expr */ +#line 797 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6971 "parser_bison.c" + break; + + case YYSYMBOL_set_ref_symbol_expr: /* set_ref_symbol_expr */ +#line 797 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6977 "parser_bison.c" + break; + + case YYSYMBOL_integer_expr: /* integer_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6983 "parser_bison.c" + break; + + case YYSYMBOL_primary_expr: /* primary_expr */ +#line 791 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6989 "parser_bison.c" + break; + + case YYSYMBOL_fib_expr: /* fib_expr */ +#line 921 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 6995 "parser_bison.c" + break; + + case YYSYMBOL_osf_expr: /* osf_expr */ +#line 926 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7001 "parser_bison.c" + break; + + case YYSYMBOL_shift_expr: /* shift_expr */ +#line 791 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7007 "parser_bison.c" + break; + + case YYSYMBOL_and_expr: /* and_expr */ +#line 791 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7013 "parser_bison.c" + break; + + case YYSYMBOL_exclusive_or_expr: /* exclusive_or_expr */ +#line 793 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7019 "parser_bison.c" + break; + + case YYSYMBOL_inclusive_or_expr: /* inclusive_or_expr */ +#line 793 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7025 "parser_bison.c" + break; + + case YYSYMBOL_basic_expr: /* basic_expr */ +#line 795 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7031 "parser_bison.c" + break; + + case YYSYMBOL_concat_expr: /* concat_expr */ +#line 820 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7037 "parser_bison.c" + break; + + case YYSYMBOL_prefix_rhs_expr: /* prefix_rhs_expr */ +#line 802 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7043 "parser_bison.c" + break; + + case YYSYMBOL_range_rhs_expr: /* range_rhs_expr */ +#line 802 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7049 "parser_bison.c" + break; + + case YYSYMBOL_multiton_rhs_expr: /* multiton_rhs_expr */ +#line 800 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7055 "parser_bison.c" + break; + + case YYSYMBOL_map_expr: /* map_expr */ +#line 823 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7061 "parser_bison.c" + break; + + case YYSYMBOL_expr: /* expr */ +#line 842 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7067 "parser_bison.c" + break; + + case YYSYMBOL_set_expr: /* set_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7073 "parser_bison.c" + break; + + case YYSYMBOL_set_list_expr: /* set_list_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7079 "parser_bison.c" + break; + + case YYSYMBOL_set_list_member_expr: /* set_list_member_expr */ +#line 832 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7085 "parser_bison.c" + break; + + case YYSYMBOL_meter_key_expr: /* meter_key_expr */ +#line 839 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7091 "parser_bison.c" + break; + + case YYSYMBOL_meter_key_expr_alloc: /* meter_key_expr_alloc */ +#line 839 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7097 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_expr: /* set_elem_expr */ +#line 834 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7103 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_key_expr: /* set_elem_key_expr */ +#line 974 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7109 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_expr_alloc: /* set_elem_expr_alloc */ +#line 834 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7115 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_stmt_list: /* set_elem_stmt_list */ +#line 741 "parser_bison.y" + { stmt_list_free(((*yyvaluep).list)); xfree(((*yyvaluep).list)); } +#line 7121 "parser_bison.c" + break; + + case YYSYMBOL_set_elem_stmt: /* set_elem_stmt */ +#line 743 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 7127 "parser_bison.c" + break; + + case YYSYMBOL_set_lhs_expr: /* set_lhs_expr */ +#line 834 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7133 "parser_bison.c" + break; + + case YYSYMBOL_set_rhs_expr: /* set_rhs_expr */ +#line 834 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7139 "parser_bison.c" + break; + + case YYSYMBOL_initializer_expr: /* initializer_expr */ +#line 842 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7145 "parser_bison.c" + break; + + case YYSYMBOL_counter_obj: /* counter_obj */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 7151 "parser_bison.c" + break; + + case YYSYMBOL_quota_obj: /* quota_obj */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 7157 "parser_bison.c" + break; + + case YYSYMBOL_secmark_obj: /* secmark_obj */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 7163 "parser_bison.c" + break; + + case YYSYMBOL_timeout_states: /* timeout_states */ +#line 967 "parser_bison.y" + { xfree(((*yyvaluep).list)); } +#line 7169 "parser_bison.c" + break; + + case YYSYMBOL_timeout_state: /* timeout_state */ +#line 967 "parser_bison.y" + { xfree(((*yyvaluep).list)); } +#line 7175 "parser_bison.c" + break; + + case YYSYMBOL_ct_obj_alloc: /* ct_obj_alloc */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 7181 "parser_bison.c" + break; + + case YYSYMBOL_limit_obj: /* limit_obj */ +#line 852 "parser_bison.y" + { obj_free(((*yyvaluep).obj)); } +#line 7187 "parser_bison.c" + break; + + case YYSYMBOL_relational_expr: /* relational_expr */ +#line 855 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7193 "parser_bison.c" + break; + + case YYSYMBOL_list_rhs_expr: /* list_rhs_expr */ +#line 847 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7199 "parser_bison.c" + break; + + case YYSYMBOL_rhs_expr: /* rhs_expr */ +#line 845 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7205 "parser_bison.c" + break; + + case YYSYMBOL_shift_rhs_expr: /* shift_rhs_expr */ +#line 847 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7211 "parser_bison.c" + break; + + case YYSYMBOL_and_rhs_expr: /* and_rhs_expr */ +#line 849 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7217 "parser_bison.c" + break; + + case YYSYMBOL_exclusive_or_rhs_expr: /* exclusive_or_rhs_expr */ +#line 849 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7223 "parser_bison.c" + break; + + case YYSYMBOL_inclusive_or_rhs_expr: /* inclusive_or_rhs_expr */ +#line 849 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7229 "parser_bison.c" + break; + + case YYSYMBOL_basic_rhs_expr: /* basic_rhs_expr */ +#line 845 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7235 "parser_bison.c" + break; + + case YYSYMBOL_concat_rhs_expr: /* concat_rhs_expr */ +#line 845 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7241 "parser_bison.c" + break; + + case YYSYMBOL_boolean_expr: /* boolean_expr */ +#line 957 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7247 "parser_bison.c" + break; + + case YYSYMBOL_keyword_expr: /* keyword_expr */ +#line 842 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7253 "parser_bison.c" + break; + + case YYSYMBOL_primary_rhs_expr: /* primary_rhs_expr */ +#line 847 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7259 "parser_bison.c" + break; + + case YYSYMBOL_verdict_expr: /* verdict_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7265 "parser_bison.c" + break; + + case YYSYMBOL_chain_expr: /* chain_expr */ +#line 789 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7271 "parser_bison.c" + break; + + case YYSYMBOL_meta_expr: /* meta_expr */ +#line 903 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7277 "parser_bison.c" + break; + + case YYSYMBOL_meta_stmt: /* meta_stmt */ +#line 751 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 7283 "parser_bison.c" + break; + + case YYSYMBOL_socket_expr: /* socket_expr */ +#line 907 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7289 "parser_bison.c" + break; + + case YYSYMBOL_numgen_expr: /* numgen_expr */ +#line 868 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7295 "parser_bison.c" + break; + + case YYSYMBOL_xfrm_expr: /* xfrm_expr */ +#line 971 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7301 "parser_bison.c" + break; + + case YYSYMBOL_hash_expr: /* hash_expr */ +#line 868 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7307 "parser_bison.c" + break; + + case YYSYMBOL_rt_expr: /* rt_expr */ +#line 913 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7313 "parser_bison.c" + break; + + case YYSYMBOL_ct_expr: /* ct_expr */ +#line 917 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7319 "parser_bison.c" + break; + + case YYSYMBOL_symbol_stmt_expr: /* symbol_stmt_expr */ +#line 847 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7325 "parser_bison.c" + break; + + case YYSYMBOL_list_stmt_expr: /* list_stmt_expr */ +#line 815 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7331 "parser_bison.c" + break; + + case YYSYMBOL_ct_stmt: /* ct_stmt */ +#line 749 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 7337 "parser_bison.c" + break; + + case YYSYMBOL_payload_stmt: /* payload_stmt */ +#line 747 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 7343 "parser_bison.c" + break; + + case YYSYMBOL_payload_expr: /* payload_expr */ +#line 859 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7349 "parser_bison.c" + break; + + case YYSYMBOL_payload_raw_expr: /* payload_raw_expr */ +#line 859 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7355 "parser_bison.c" + break; + + case YYSYMBOL_eth_hdr_expr: /* eth_hdr_expr */ +#line 862 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7361 "parser_bison.c" + break; + + case YYSYMBOL_vlan_hdr_expr: /* vlan_hdr_expr */ +#line 862 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7367 "parser_bison.c" + break; + + case YYSYMBOL_arp_hdr_expr: /* arp_hdr_expr */ +#line 865 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7373 "parser_bison.c" + break; + + case YYSYMBOL_ip_hdr_expr: /* ip_hdr_expr */ +#line 868 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7379 "parser_bison.c" + break; + + case YYSYMBOL_icmp_hdr_expr: /* icmp_hdr_expr */ +#line 868 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7385 "parser_bison.c" + break; + + case YYSYMBOL_igmp_hdr_expr: /* igmp_hdr_expr */ +#line 868 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7391 "parser_bison.c" + break; + + case YYSYMBOL_ip6_hdr_expr: /* ip6_hdr_expr */ +#line 872 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7397 "parser_bison.c" + break; + + case YYSYMBOL_icmp6_hdr_expr: /* icmp6_hdr_expr */ +#line 872 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7403 "parser_bison.c" + break; + + case YYSYMBOL_auth_hdr_expr: /* auth_hdr_expr */ +#line 875 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7409 "parser_bison.c" + break; + + case YYSYMBOL_esp_hdr_expr: /* esp_hdr_expr */ +#line 875 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7415 "parser_bison.c" + break; + + case YYSYMBOL_comp_hdr_expr: /* comp_hdr_expr */ +#line 875 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7421 "parser_bison.c" + break; + + case YYSYMBOL_udp_hdr_expr: /* udp_hdr_expr */ +#line 878 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7427 "parser_bison.c" + break; + + case YYSYMBOL_udplite_hdr_expr: /* udplite_hdr_expr */ +#line 878 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7433 "parser_bison.c" + break; + + case YYSYMBOL_tcp_hdr_expr: /* tcp_hdr_expr */ +#line 936 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7439 "parser_bison.c" + break; + + case YYSYMBOL_inner_inet_expr: /* inner_inet_expr */ +#line 944 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7445 "parser_bison.c" + break; + + case YYSYMBOL_inner_eth_expr: /* inner_eth_expr */ +#line 944 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7451 "parser_bison.c" + break; + + case YYSYMBOL_inner_expr: /* inner_expr */ +#line 944 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7457 "parser_bison.c" + break; + + case YYSYMBOL_vxlan_hdr_expr: /* vxlan_hdr_expr */ +#line 947 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7463 "parser_bison.c" + break; + + case YYSYMBOL_geneve_hdr_expr: /* geneve_hdr_expr */ +#line 947 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7469 "parser_bison.c" + break; + + case YYSYMBOL_gre_hdr_expr: /* gre_hdr_expr */ +#line 947 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7475 "parser_bison.c" + break; + + case YYSYMBOL_gretap_hdr_expr: /* gretap_hdr_expr */ +#line 947 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7481 "parser_bison.c" + break; + + case YYSYMBOL_optstrip_stmt: /* optstrip_stmt */ +#line 951 "parser_bison.y" + { stmt_free(((*yyvaluep).stmt)); } +#line 7487 "parser_bison.c" + break; + + case YYSYMBOL_dccp_hdr_expr: /* dccp_hdr_expr */ +#line 881 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7493 "parser_bison.c" + break; + + case YYSYMBOL_sctp_chunk_alloc: /* sctp_chunk_alloc */ +#line 881 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7499 "parser_bison.c" + break; + + case YYSYMBOL_sctp_hdr_expr: /* sctp_hdr_expr */ +#line 881 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7505 "parser_bison.c" + break; + + case YYSYMBOL_th_hdr_expr: /* th_hdr_expr */ +#line 887 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7511 "parser_bison.c" + break; + + case YYSYMBOL_exthdr_expr: /* exthdr_expr */ +#line 891 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7517 "parser_bison.c" + break; + + case YYSYMBOL_hbh_hdr_expr: /* hbh_hdr_expr */ +#line 893 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7523 "parser_bison.c" + break; + + case YYSYMBOL_rt_hdr_expr: /* rt_hdr_expr */ +#line 896 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7529 "parser_bison.c" + break; + + case YYSYMBOL_rt0_hdr_expr: /* rt0_hdr_expr */ +#line 896 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7535 "parser_bison.c" + break; + + case YYSYMBOL_rt2_hdr_expr: /* rt2_hdr_expr */ +#line 896 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7541 "parser_bison.c" + break; + + case YYSYMBOL_rt4_hdr_expr: /* rt4_hdr_expr */ +#line 896 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7547 "parser_bison.c" + break; + + case YYSYMBOL_frag_hdr_expr: /* frag_hdr_expr */ +#line 893 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7553 "parser_bison.c" + break; + + case YYSYMBOL_dst_hdr_expr: /* dst_hdr_expr */ +#line 893 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7559 "parser_bison.c" + break; + + case YYSYMBOL_mh_hdr_expr: /* mh_hdr_expr */ +#line 899 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7565 "parser_bison.c" + break; + + case YYSYMBOL_exthdr_exists_expr: /* exthdr_exists_expr */ +#line 961 "parser_bison.y" + { expr_free(((*yyvaluep).expr)); } +#line 7571 "parser_bison.c" + break; + + default: + break; + } + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (struct nft_ctx *nft, void *scanner, struct parser_state *state) +{ +/* Lookahead token kind. */ +int yychar; + + +/* The semantic value of the lookahead symbol. */ +/* Default value used for initialization, for pacifying older GCCs + or non-GCC compilers. */ +YY_INITIAL_VALUE (static YYSTYPE yyval_default;) +YYSTYPE yylval YY_INITIAL_VALUE (= yyval_default); + +/* Location data for the lookahead symbol. */ +static YYLTYPE yyloc_default +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL + = { 1, 1, 1, 1 } +# endif +; +YYLTYPE yylloc = yyloc_default; + + /* Number of syntax errors so far. */ + int yynerrs = 0; + + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + /* The location stack: array, bottom, top. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp = yyls; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + YYLTYPE yyloc; + + /* The locations where the error started and ended. */ + YYLTYPE yyerror_range[3]; + + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYPTRDIFF_T yymsg_alloc = sizeof yymsgbuf; + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N), yylsp -= (N)) + + /* 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")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + +/* User initialization code. */ +#line 197 "parser_bison.y" +{ + location_init(scanner, state, &yylloc); + if (nft->debug_mask & NFT_DEBUG_SCANNER) + nft_set_debug(1, scanner); + if (nft->debug_mask & NFT_DEBUG_PARSER) + yydebug = 1; +} + +#line 7676 "parser_bison.c" + + yylsp[0] = yylloc; + 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 -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + goto yyexhaustedlab; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined 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. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + 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 * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yyls1, yysize * YYSIZEOF (*yylsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + yyls = yyls1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyls_alloc, yyls); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + yylsp = yyls + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (&yylval, &yylloc, scanner); + } + + if (yychar <= TOKEN_EOF) + { + yychar = TOKEN_EOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + yyerror_range[1] = yylloc; + goto yyerrlab1; + } + 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 (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + *++yylsp = yylloc; + + /* Discard the shifted token. */ + yychar = YYEMPTY; + 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]; + + /* Default location. */ + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); + yyerror_range[1] = yyloc; + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 3: /* input: input line */ +#line 980 "parser_bison.y" + { + if ((yyvsp[0].cmd) != NULL) { + (yyvsp[0].cmd)->location = (yylsp[0]); + list_add_tail(&(yyvsp[0].cmd)->list, state->cmds); + } + } +#line 7893 "parser_bison.c" + break; + + case 8: /* close_scope_ah: %empty */ +#line 996 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); } +#line 7899 "parser_bison.c" + break; + + case 9: /* close_scope_arp: %empty */ +#line 997 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); } +#line 7905 "parser_bison.c" + break; + + case 10: /* close_scope_at: %empty */ +#line 998 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); } +#line 7911 "parser_bison.c" + break; + + case 11: /* close_scope_comp: %empty */ +#line 999 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); } +#line 7917 "parser_bison.c" + break; + + case 12: /* close_scope_ct: %empty */ +#line 1000 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); } +#line 7923 "parser_bison.c" + break; + + case 13: /* close_scope_counter: %empty */ +#line 1001 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); } +#line 7929 "parser_bison.c" + break; + + case 14: /* close_scope_last: %empty */ +#line 1002 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); } +#line 7935 "parser_bison.c" + break; + + case 15: /* close_scope_dccp: %empty */ +#line 1003 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); } +#line 7941 "parser_bison.c" + break; + + case 16: /* close_scope_destroy: %empty */ +#line 1004 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); } +#line 7947 "parser_bison.c" + break; + + case 17: /* close_scope_dst: %empty */ +#line 1005 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); } +#line 7953 "parser_bison.c" + break; + + case 18: /* close_scope_dup: %empty */ +#line 1006 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); } +#line 7959 "parser_bison.c" + break; + + case 19: /* close_scope_esp: %empty */ +#line 1007 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); } +#line 7965 "parser_bison.c" + break; + + case 20: /* close_scope_eth: %empty */ +#line 1008 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); } +#line 7971 "parser_bison.c" + break; + + case 21: /* close_scope_export: %empty */ +#line 1009 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); } +#line 7977 "parser_bison.c" + break; + + case 22: /* close_scope_fib: %empty */ +#line 1010 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); } +#line 7983 "parser_bison.c" + break; + + case 23: /* close_scope_frag: %empty */ +#line 1011 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); } +#line 7989 "parser_bison.c" + break; + + case 24: /* close_scope_fwd: %empty */ +#line 1012 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); } +#line 7995 "parser_bison.c" + break; + + case 25: /* close_scope_gre: %empty */ +#line 1013 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); } +#line 8001 "parser_bison.c" + break; + + case 26: /* close_scope_hash: %empty */ +#line 1014 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); } +#line 8007 "parser_bison.c" + break; + + case 27: /* close_scope_hbh: %empty */ +#line 1015 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); } +#line 8013 "parser_bison.c" + break; + + case 28: /* close_scope_ip: %empty */ +#line 1016 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); } +#line 8019 "parser_bison.c" + break; + + case 29: /* close_scope_ip6: %empty */ +#line 1017 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); } +#line 8025 "parser_bison.c" + break; + + case 30: /* close_scope_vlan: %empty */ +#line 1018 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); } +#line 8031 "parser_bison.c" + break; + + case 31: /* close_scope_icmp: %empty */ +#line 1019 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); } +#line 8037 "parser_bison.c" + break; + + case 32: /* close_scope_igmp: %empty */ +#line 1020 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); } +#line 8043 "parser_bison.c" + break; + + case 33: /* close_scope_import: %empty */ +#line 1021 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); } +#line 8049 "parser_bison.c" + break; + + case 34: /* close_scope_ipsec: %empty */ +#line 1022 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); } +#line 8055 "parser_bison.c" + break; + + case 35: /* close_scope_list: %empty */ +#line 1023 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); } +#line 8061 "parser_bison.c" + break; + + case 36: /* close_scope_limit: %empty */ +#line 1024 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); } +#line 8067 "parser_bison.c" + break; + + case 37: /* close_scope_meta: %empty */ +#line 1025 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); } +#line 8073 "parser_bison.c" + break; + + case 38: /* close_scope_mh: %empty */ +#line 1026 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); } +#line 8079 "parser_bison.c" + break; + + case 39: /* close_scope_monitor: %empty */ +#line 1027 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); } +#line 8085 "parser_bison.c" + break; + + case 40: /* close_scope_nat: %empty */ +#line 1028 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); } +#line 8091 "parser_bison.c" + break; + + case 41: /* close_scope_numgen: %empty */ +#line 1029 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); } +#line 8097 "parser_bison.c" + break; + + case 42: /* close_scope_osf: %empty */ +#line 1030 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); } +#line 8103 "parser_bison.c" + break; + + case 43: /* close_scope_policy: %empty */ +#line 1031 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); } +#line 8109 "parser_bison.c" + break; + + case 44: /* close_scope_quota: %empty */ +#line 1032 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); } +#line 8115 "parser_bison.c" + break; + + case 45: /* close_scope_queue: %empty */ +#line 1033 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); } +#line 8121 "parser_bison.c" + break; + + case 46: /* close_scope_reject: %empty */ +#line 1034 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); } +#line 8127 "parser_bison.c" + break; + + case 47: /* close_scope_reset: %empty */ +#line 1035 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); } +#line 8133 "parser_bison.c" + break; + + case 48: /* close_scope_rt: %empty */ +#line 1036 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); } +#line 8139 "parser_bison.c" + break; + + case 49: /* close_scope_sctp: %empty */ +#line 1037 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); } +#line 8145 "parser_bison.c" + break; + + case 50: /* close_scope_sctp_chunk: %empty */ +#line 1038 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); } +#line 8151 "parser_bison.c" + break; + + case 51: /* close_scope_secmark: %empty */ +#line 1039 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); } +#line 8157 "parser_bison.c" + break; + + case 52: /* close_scope_socket: %empty */ +#line 1040 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); } +#line 8163 "parser_bison.c" + break; + + case 53: /* close_scope_tcp: %empty */ +#line 1041 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); } +#line 8169 "parser_bison.c" + break; + + case 54: /* close_scope_tproxy: %empty */ +#line 1042 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); } +#line 8175 "parser_bison.c" + break; + + case 55: /* close_scope_type: %empty */ +#line 1043 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); } +#line 8181 "parser_bison.c" + break; + + case 56: /* close_scope_th: %empty */ +#line 1044 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); } +#line 8187 "parser_bison.c" + break; + + case 57: /* close_scope_udp: %empty */ +#line 1045 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); } +#line 8193 "parser_bison.c" + break; + + case 58: /* close_scope_udplite: %empty */ +#line 1046 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); } +#line 8199 "parser_bison.c" + break; + + case 59: /* close_scope_log: %empty */ +#line 1048 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); } +#line 8205 "parser_bison.c" + break; + + case 60: /* close_scope_synproxy: %empty */ +#line 1049 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); } +#line 8211 "parser_bison.c" + break; + + case 61: /* close_scope_xt: %empty */ +#line 1050 "parser_bison.y" + { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); } +#line 8217 "parser_bison.c" + break; + + case 62: /* common_block: "include" "quoted string" stmt_separator */ +#line 1053 "parser_bison.y" + { + if (scanner_include_file(nft, scanner, (yyvsp[-1].string), &(yyloc)) < 0) { + xfree((yyvsp[-1].string)); + YYERROR; + } + xfree((yyvsp[-1].string)); + } +#line 8229 "parser_bison.c" + break; + + case 63: /* common_block: "define" identifier '=' initializer_expr stmt_separator */ +#line 1061 "parser_bison.y" + { + struct scope *scope = current_scope(state); + + if (symbol_lookup(scope, (yyvsp[-3].string)) != NULL) { + erec_queue(error(&(yylsp[-3]), "redefinition of symbol '%s'", (yyvsp[-3].string)), + state->msgs); + expr_free((yyvsp[-1].expr)); + xfree((yyvsp[-3].string)); + YYERROR; + } + + symbol_bind(scope, (yyvsp[-3].string), (yyvsp[-1].expr)); + xfree((yyvsp[-3].string)); + } +#line 8248 "parser_bison.c" + break; + + case 64: /* common_block: "redefine" identifier '=' initializer_expr stmt_separator */ +#line 1076 "parser_bison.y" + { + struct scope *scope = current_scope(state); + + symbol_bind(scope, (yyvsp[-3].string), (yyvsp[-1].expr)); + xfree((yyvsp[-3].string)); + } +#line 8259 "parser_bison.c" + break; + + case 65: /* common_block: "undefine" identifier stmt_separator */ +#line 1083 "parser_bison.y" + { + struct scope *scope = current_scope(state); + + if (symbol_unbind(scope, (yyvsp[-1].string)) < 0) { + erec_queue(error(&(yylsp[-1]), "undefined symbol '%s'", (yyvsp[-1].string)), + state->msgs); + xfree((yyvsp[-1].string)); + YYERROR; + } + xfree((yyvsp[-1].string)); + } +#line 8275 "parser_bison.c" + break; + + case 66: /* common_block: error stmt_separator */ +#line 1095 "parser_bison.y" + { + if (++state->nerrs == nft->parser_max_errors) + YYABORT; + yyerrok; + } +#line 8285 "parser_bison.c" + break; + + case 67: /* line: common_block */ +#line 1102 "parser_bison.y" + { (yyval.cmd) = NULL; } +#line 8291 "parser_bison.c" + break; + + case 68: /* line: stmt_separator */ +#line 1103 "parser_bison.y" + { (yyval.cmd) = NULL; } +#line 8297 "parser_bison.c" + break; + + case 69: /* line: base_cmd stmt_separator */ +#line 1104 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8303 "parser_bison.c" + break; + + case 70: /* line: base_cmd "end of file" */ +#line 1106 "parser_bison.y" + { + /* + * Very hackish workaround for bison >= 2.4: previous versions + * terminated parsing after EOF, 2.4+ tries to get further input + * in 'input' and calls the scanner again, causing a crash when + * the final input buffer has been popped. Terminate manually to + * avoid this. The correct fix should be to adjust the grammar + * to accept EOF in input, but for unknown reasons it does not + * work. + */ + if ((yyvsp[-1].cmd) != NULL) { + (yyvsp[-1].cmd)->location = (yylsp[-1]); + list_add_tail(&(yyvsp[-1].cmd)->list, state->cmds); + } + (yyval.cmd) = NULL; + YYACCEPT; + } +#line 8325 "parser_bison.c" + break; + + case 71: /* base_cmd: add_cmd */ +#line 1125 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8331 "parser_bison.c" + break; + + case 72: /* base_cmd: "add" add_cmd */ +#line 1126 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8337 "parser_bison.c" + break; + + case 73: /* base_cmd: "replace" replace_cmd */ +#line 1127 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8343 "parser_bison.c" + break; + + case 74: /* base_cmd: "create" create_cmd */ +#line 1128 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8349 "parser_bison.c" + break; + + case 75: /* base_cmd: "insert" insert_cmd */ +#line 1129 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8355 "parser_bison.c" + break; + + case 76: /* base_cmd: "delete" delete_cmd */ +#line 1130 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8361 "parser_bison.c" + break; + + case 77: /* base_cmd: "get" get_cmd */ +#line 1131 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8367 "parser_bison.c" + break; + + case 78: /* base_cmd: "list" list_cmd close_scope_list */ +#line 1132 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8373 "parser_bison.c" + break; + + case 79: /* base_cmd: "reset" reset_cmd close_scope_reset */ +#line 1133 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8379 "parser_bison.c" + break; + + case 80: /* base_cmd: "flush" flush_cmd */ +#line 1134 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8385 "parser_bison.c" + break; + + case 81: /* base_cmd: "rename" rename_cmd */ +#line 1135 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8391 "parser_bison.c" + break; + + case 82: /* base_cmd: "import" import_cmd close_scope_import */ +#line 1136 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8397 "parser_bison.c" + break; + + case 83: /* base_cmd: "export" export_cmd close_scope_export */ +#line 1137 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8403 "parser_bison.c" + break; + + case 84: /* base_cmd: "monitor" monitor_cmd close_scope_monitor */ +#line 1138 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8409 "parser_bison.c" + break; + + case 85: /* base_cmd: "describe" describe_cmd */ +#line 1139 "parser_bison.y" + { (yyval.cmd) = (yyvsp[0].cmd); } +#line 8415 "parser_bison.c" + break; + + case 86: /* base_cmd: "destroy" destroy_cmd close_scope_destroy */ +#line 1140 "parser_bison.y" + { (yyval.cmd) = (yyvsp[-1].cmd); } +#line 8421 "parser_bison.c" + break; + + case 87: /* add_cmd: "table" table_spec */ +#line 1144 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8429 "parser_bison.c" + break; + + case 88: /* add_cmd: "table" table_spec table_block_alloc '{' table_block '}' */ +#line 1149 "parser_bison.y" + { + handle_merge(&(yyvsp[-3].table)->handle, &(yyvsp[-4].handle)); + close_scope(state); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_TABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].table)); + } +#line 8439 "parser_bison.c" + break; + + case 89: /* add_cmd: "chain" chain_spec */ +#line 1155 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8447 "parser_bison.c" + break; + + case 90: /* add_cmd: "chain" chain_spec chain_block_alloc '{' chain_block '}' */ +#line 1160 "parser_bison.y" + { + (yyvsp[-1].chain)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].chain)->handle, &(yyvsp[-4].handle)); + close_scope(state); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].chain)); + } +#line 8458 "parser_bison.c" + break; + + case 91: /* add_cmd: "rule" rule_position rule */ +#line 1167 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].rule)); + } +#line 8466 "parser_bison.c" + break; + + case 92: /* add_cmd: rule_position rule */ +#line 1171 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].rule)); + } +#line 8474 "parser_bison.c" + break; + + case 93: /* add_cmd: "set" set_spec set_block_alloc '{' set_block '}' */ +#line 1176 "parser_bison.y" + { + (yyvsp[-1].set)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].set)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].set)); + } +#line 8484 "parser_bison.c" + break; + + case 94: /* add_cmd: "map" set_spec map_block_alloc '{' map_block '}' */ +#line 1183 "parser_bison.y" + { + (yyvsp[-1].set)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].set)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].set)); + } +#line 8494 "parser_bison.c" + break; + + case 95: /* add_cmd: "element" set_spec set_block_expr */ +#line 1189 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 8502 "parser_bison.c" + break; + + case 96: /* add_cmd: "flowtable" flowtable_spec flowtable_block_alloc '{' flowtable_block '}' */ +#line 1194 "parser_bison.y" + { + (yyvsp[-1].flowtable)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].flowtable)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].flowtable)); + } +#line 8512 "parser_bison.c" + break; + + case 97: /* add_cmd: "counter" obj_spec close_scope_counter */ +#line 1200 "parser_bison.y" + { + struct obj *obj; + + obj = obj_alloc(&(yyloc)); + obj->type = NFT_OBJECT_COUNTER; + handle_merge(&obj->handle, &(yyvsp[-1].handle)); + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &(yyvsp[-1].handle), &(yyloc), obj); + } +#line 8525 "parser_bison.c" + break; + + case 98: /* add_cmd: "counter" obj_spec counter_obj counter_config close_scope_counter */ +#line 1209 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8533 "parser_bison.c" + break; + + case 99: /* add_cmd: "counter" obj_spec counter_obj '{' counter_block '}' close_scope_counter */ +#line 1213 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8541 "parser_bison.c" + break; + + case 100: /* add_cmd: "quota" obj_spec quota_obj quota_config close_scope_quota */ +#line 1217 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8549 "parser_bison.c" + break; + + case 101: /* add_cmd: "quota" obj_spec quota_obj '{' quota_block '}' close_scope_quota */ +#line 1221 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8557 "parser_bison.c" + break; + + case 102: /* add_cmd: "ct" "helper" obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct */ +#line 1225 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8565 "parser_bison.c" + break; + + case 103: /* add_cmd: "ct" "timeout" obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct */ +#line 1229 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_TIMEOUT, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8573 "parser_bison.c" + break; + + case 104: /* add_cmd: "ct" "expectation" obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct */ +#line 1233 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_EXPECT, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8581 "parser_bison.c" + break; + + case 105: /* add_cmd: "limit" obj_spec limit_obj limit_config close_scope_limit */ +#line 1237 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8589 "parser_bison.c" + break; + + case 106: /* add_cmd: "limit" obj_spec limit_obj '{' limit_block '}' close_scope_limit */ +#line 1241 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8597 "parser_bison.c" + break; + + case 107: /* add_cmd: "secmark" obj_spec secmark_obj secmark_config close_scope_secmark */ +#line 1245 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8605 "parser_bison.c" + break; + + case 108: /* add_cmd: "secmark" obj_spec secmark_obj '{' secmark_block '}' close_scope_secmark */ +#line 1249 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8613 "parser_bison.c" + break; + + case 109: /* add_cmd: "synproxy" obj_spec synproxy_obj synproxy_config close_scope_synproxy */ +#line 1253 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8621 "parser_bison.c" + break; + + case 110: /* add_cmd: "synproxy" obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy */ +#line 1257 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8629 "parser_bison.c" + break; + + case 111: /* replace_cmd: "rule" ruleid_spec rule */ +#line 1263 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_REPLACE, CMD_OBJ_RULE, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].rule)); + } +#line 8637 "parser_bison.c" + break; + + case 112: /* create_cmd: "table" table_spec */ +#line 1269 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8645 "parser_bison.c" + break; + + case 113: /* create_cmd: "table" table_spec table_block_alloc '{' table_block '}' */ +#line 1274 "parser_bison.y" + { + handle_merge(&(yyvsp[-3].table)->handle, &(yyvsp[-4].handle)); + close_scope(state); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].table)); + } +#line 8655 "parser_bison.c" + break; + + case 114: /* create_cmd: "chain" chain_spec */ +#line 1280 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8663 "parser_bison.c" + break; + + case 115: /* create_cmd: "chain" chain_spec chain_block_alloc '{' chain_block '}' */ +#line 1285 "parser_bison.y" + { + (yyvsp[-1].chain)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].chain)->handle, &(yyvsp[-4].handle)); + close_scope(state); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].chain)); + } +#line 8674 "parser_bison.c" + break; + + case 116: /* create_cmd: "set" set_spec set_block_alloc '{' set_block '}' */ +#line 1293 "parser_bison.y" + { + (yyvsp[-1].set)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].set)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].set)); + } +#line 8684 "parser_bison.c" + break; + + case 117: /* create_cmd: "map" set_spec map_block_alloc '{' map_block '}' */ +#line 1300 "parser_bison.y" + { + (yyvsp[-1].set)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].set)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].set)); + } +#line 8694 "parser_bison.c" + break; + + case 118: /* create_cmd: "element" set_spec set_block_expr */ +#line 1306 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 8702 "parser_bison.c" + break; + + case 119: /* create_cmd: "flowtable" flowtable_spec flowtable_block_alloc '{' flowtable_block '}' */ +#line 1311 "parser_bison.y" + { + (yyvsp[-1].flowtable)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].flowtable)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].flowtable)); + } +#line 8712 "parser_bison.c" + break; + + case 120: /* create_cmd: "counter" obj_spec close_scope_counter */ +#line 1317 "parser_bison.y" + { + struct obj *obj; + + obj = obj_alloc(&(yyloc)); + obj->type = NFT_OBJECT_COUNTER; + handle_merge(&obj->handle, &(yyvsp[-1].handle)); + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &(yyvsp[-1].handle), &(yyloc), obj); + } +#line 8725 "parser_bison.c" + break; + + case 121: /* create_cmd: "counter" obj_spec counter_obj counter_config close_scope_counter */ +#line 1326 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8733 "parser_bison.c" + break; + + case 122: /* create_cmd: "quota" obj_spec quota_obj quota_config close_scope_quota */ +#line 1330 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_QUOTA, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8741 "parser_bison.c" + break; + + case 123: /* create_cmd: "ct" "helper" obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct */ +#line 1334 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_HELPER, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8749 "parser_bison.c" + break; + + case 124: /* create_cmd: "ct" "timeout" obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct */ +#line 1338 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_TIMEOUT, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8757 "parser_bison.c" + break; + + case 125: /* create_cmd: "ct" "expectation" obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct */ +#line 1342 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_EXPECT, &(yyvsp[-5].handle), &(yyloc), (yyvsp[-4].obj)); + } +#line 8765 "parser_bison.c" + break; + + case 126: /* create_cmd: "limit" obj_spec limit_obj limit_config close_scope_limit */ +#line 1346 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8773 "parser_bison.c" + break; + + case 127: /* create_cmd: "secmark" obj_spec secmark_obj secmark_config close_scope_secmark */ +#line 1350 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8781 "parser_bison.c" + break; + + case 128: /* create_cmd: "synproxy" obj_spec synproxy_obj synproxy_config close_scope_synproxy */ +#line 1354 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &(yyvsp[-3].handle), &(yyloc), (yyvsp[-2].obj)); + } +#line 8789 "parser_bison.c" + break; + + case 129: /* insert_cmd: "rule" rule_position rule */ +#line 1360 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_INSERT, CMD_OBJ_RULE, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].rule)); + } +#line 8797 "parser_bison.c" + break; + + case 138: /* delete_cmd: "table" table_or_id_spec */ +#line 1382 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8805 "parser_bison.c" + break; + + case 139: /* delete_cmd: "chain" chain_or_id_spec */ +#line 1386 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8813 "parser_bison.c" + break; + + case 140: /* delete_cmd: "chain" chain_spec chain_block_alloc '{' chain_block '}' */ +#line 1391 "parser_bison.y" + { + (yyvsp[-1].chain)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].chain)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].chain)); + } +#line 8823 "parser_bison.c" + break; + + case 141: /* delete_cmd: "rule" ruleid_spec */ +#line 1397 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8831 "parser_bison.c" + break; + + case 142: /* delete_cmd: "set" set_or_id_spec */ +#line 1401 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8839 "parser_bison.c" + break; + + case 143: /* delete_cmd: "map" set_spec */ +#line 1405 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8847 "parser_bison.c" + break; + + case 144: /* delete_cmd: "element" set_spec set_block_expr */ +#line 1409 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 8855 "parser_bison.c" + break; + + case 145: /* delete_cmd: "flowtable" flowtable_spec */ +#line 1413 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8863 "parser_bison.c" + break; + + case 146: /* delete_cmd: "flowtable" flowtableid_spec */ +#line 1417 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8871 "parser_bison.c" + break; + + case 147: /* delete_cmd: "flowtable" flowtable_spec flowtable_block_alloc '{' flowtable_block '}' */ +#line 1422 "parser_bison.y" + { + (yyvsp[-1].flowtable)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].flowtable)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].flowtable)); + } +#line 8881 "parser_bison.c" + break; + + case 148: /* delete_cmd: "counter" obj_or_id_spec close_scope_counter */ +#line 1428 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 8889 "parser_bison.c" + break; + + case 149: /* delete_cmd: "quota" obj_or_id_spec close_scope_quota */ +#line 1432 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 8897 "parser_bison.c" + break; + + case 150: /* delete_cmd: "ct" ct_obj_type obj_spec ct_obj_alloc close_scope_ct */ +#line 1436 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_DELETE, (yyvsp[-3].val), &(yyvsp[-2].handle), &(yyloc), (yyvsp[-1].obj)); + if ((yyvsp[-3].val) == NFT_OBJECT_CT_TIMEOUT) + init_list_head(&(yyvsp[-1].obj)->ct_timeout.timeout_list); + } +#line 8907 "parser_bison.c" + break; + + case 151: /* delete_cmd: "limit" obj_or_id_spec close_scope_limit */ +#line 1442 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 8915 "parser_bison.c" + break; + + case 152: /* delete_cmd: "secmark" obj_or_id_spec close_scope_secmark */ +#line 1446 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 8923 "parser_bison.c" + break; + + case 153: /* delete_cmd: "synproxy" obj_or_id_spec close_scope_synproxy */ +#line 1450 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 8931 "parser_bison.c" + break; + + case 154: /* destroy_cmd: "table" table_or_id_spec */ +#line 1456 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8939 "parser_bison.c" + break; + + case 155: /* destroy_cmd: "chain" chain_or_id_spec */ +#line 1460 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8947 "parser_bison.c" + break; + + case 156: /* destroy_cmd: "rule" ruleid_spec */ +#line 1464 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8955 "parser_bison.c" + break; + + case 157: /* destroy_cmd: "set" set_or_id_spec */ +#line 1468 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8963 "parser_bison.c" + break; + + case 158: /* destroy_cmd: "map" set_spec */ +#line 1472 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8971 "parser_bison.c" + break; + + case 159: /* destroy_cmd: "element" set_spec set_block_expr */ +#line 1476 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 8979 "parser_bison.c" + break; + + case 160: /* destroy_cmd: "flowtable" flowtable_spec */ +#line 1480 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8987 "parser_bison.c" + break; + + case 161: /* destroy_cmd: "flowtable" flowtableid_spec */ +#line 1484 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 8995 "parser_bison.c" + break; + + case 162: /* destroy_cmd: "flowtable" flowtable_spec flowtable_block_alloc '{' flowtable_block '}' */ +#line 1489 "parser_bison.y" + { + (yyvsp[-1].flowtable)->location = (yylsp[-1]); + handle_merge(&(yyvsp[-3].flowtable)->handle, &(yyvsp[-4].handle)); + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &(yyvsp[-4].handle), &(yyloc), (yyvsp[-1].flowtable)); + } +#line 9005 "parser_bison.c" + break; + + case 163: /* destroy_cmd: "counter" obj_or_id_spec close_scope_counter */ +#line 1495 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9013 "parser_bison.c" + break; + + case 164: /* destroy_cmd: "quota" obj_or_id_spec close_scope_quota */ +#line 1499 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9021 "parser_bison.c" + break; + + case 165: /* destroy_cmd: "ct" ct_obj_type obj_spec ct_obj_alloc close_scope_ct */ +#line 1503 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_DESTROY, (yyvsp[-3].val), &(yyvsp[-2].handle), &(yyloc), (yyvsp[-1].obj)); + if ((yyvsp[-3].val) == NFT_OBJECT_CT_TIMEOUT) + init_list_head(&(yyvsp[-1].obj)->ct_timeout.timeout_list); + } +#line 9031 "parser_bison.c" + break; + + case 166: /* destroy_cmd: "limit" obj_or_id_spec close_scope_limit */ +#line 1509 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9039 "parser_bison.c" + break; + + case 167: /* destroy_cmd: "secmark" obj_or_id_spec close_scope_secmark */ +#line 1513 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9047 "parser_bison.c" + break; + + case 168: /* destroy_cmd: "synproxy" obj_or_id_spec close_scope_synproxy */ +#line 1517 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9055 "parser_bison.c" + break; + + case 169: /* get_cmd: "element" set_spec set_block_expr */ +#line 1524 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 9063 "parser_bison.c" + break; + + case 170: /* list_cmd: "table" table_spec */ +#line 1530 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9071 "parser_bison.c" + break; + + case 171: /* list_cmd: "tables" ruleset_spec */ +#line 1534 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9079 "parser_bison.c" + break; + + case 172: /* list_cmd: "chain" chain_spec */ +#line 1538 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9087 "parser_bison.c" + break; + + case 173: /* list_cmd: "chains" ruleset_spec */ +#line 1542 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_CHAINS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9095 "parser_bison.c" + break; + + case 174: /* list_cmd: "sets" ruleset_spec */ +#line 1546 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9103 "parser_bison.c" + break; + + case 175: /* list_cmd: "sets" "table" table_spec */ +#line 1550 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9111 "parser_bison.c" + break; + + case 176: /* list_cmd: "set" set_spec */ +#line 1554 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9119 "parser_bison.c" + break; + + case 177: /* list_cmd: "counters" ruleset_spec */ +#line 1558 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9127 "parser_bison.c" + break; + + case 178: /* list_cmd: "counters" "table" table_spec */ +#line 1562 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9135 "parser_bison.c" + break; + + case 179: /* list_cmd: "counter" obj_spec close_scope_counter */ +#line 1566 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9143 "parser_bison.c" + break; + + case 180: /* list_cmd: "quotas" ruleset_spec */ +#line 1570 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9151 "parser_bison.c" + break; + + case 181: /* list_cmd: "quotas" "table" table_spec */ +#line 1574 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9159 "parser_bison.c" + break; + + case 182: /* list_cmd: "quota" obj_spec close_scope_quota */ +#line 1578 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9167 "parser_bison.c" + break; + + case 183: /* list_cmd: "limits" ruleset_spec */ +#line 1582 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9175 "parser_bison.c" + break; + + case 184: /* list_cmd: "limits" "table" table_spec */ +#line 1586 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9183 "parser_bison.c" + break; + + case 185: /* list_cmd: "limit" obj_spec close_scope_limit */ +#line 1590 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9191 "parser_bison.c" + break; + + case 186: /* list_cmd: "secmarks" ruleset_spec */ +#line 1594 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9199 "parser_bison.c" + break; + + case 187: /* list_cmd: "secmarks" "table" table_spec */ +#line 1598 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9207 "parser_bison.c" + break; + + case 188: /* list_cmd: "secmark" obj_spec close_scope_secmark */ +#line 1602 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9215 "parser_bison.c" + break; + + case 189: /* list_cmd: "synproxys" ruleset_spec */ +#line 1606 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9223 "parser_bison.c" + break; + + case 190: /* list_cmd: "synproxys" "table" table_spec */ +#line 1610 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9231 "parser_bison.c" + break; + + case 191: /* list_cmd: "synproxy" obj_spec close_scope_synproxy */ +#line 1614 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9239 "parser_bison.c" + break; + + case 192: /* list_cmd: "ruleset" ruleset_spec */ +#line 1618 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9247 "parser_bison.c" + break; + + case 193: /* list_cmd: "flow" "tables" ruleset_spec */ +#line 1622 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9255 "parser_bison.c" + break; + + case 194: /* list_cmd: "flow" "table" set_spec */ +#line 1626 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9263 "parser_bison.c" + break; + + case 195: /* list_cmd: "meters" ruleset_spec */ +#line 1630 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9271 "parser_bison.c" + break; + + case 196: /* list_cmd: "meter" set_spec */ +#line 1634 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9279 "parser_bison.c" + break; + + case 197: /* list_cmd: "flowtables" ruleset_spec */ +#line 1638 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9287 "parser_bison.c" + break; + + case 198: /* list_cmd: "flowtable" flowtable_spec */ +#line 1642 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9295 "parser_bison.c" + break; + + case 199: /* list_cmd: "maps" ruleset_spec */ +#line 1646 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9303 "parser_bison.c" + break; + + case 200: /* list_cmd: "map" set_spec */ +#line 1650 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_MAP, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9311 "parser_bison.c" + break; + + case 201: /* list_cmd: "ct" ct_obj_type obj_spec close_scope_ct */ +#line 1654 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc_obj_ct(CMD_LIST, (yyvsp[-2].val), &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9319 "parser_bison.c" + break; + + case 202: /* list_cmd: "ct" ct_cmd_type "table" table_spec close_scope_ct */ +#line 1658 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, (yyvsp[-3].val), &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9327 "parser_bison.c" + break; + + case 203: /* list_cmd: "hooks" basehook_spec */ +#line 1662 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_LIST, CMD_OBJ_HOOKS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9335 "parser_bison.c" + break; + + case 204: /* basehook_device_name: "device" "string" */ +#line 1668 "parser_bison.y" + { + (yyval.string) = (yyvsp[0].string); + } +#line 9343 "parser_bison.c" + break; + + case 205: /* basehook_spec: ruleset_spec */ +#line 1674 "parser_bison.y" + { + (yyval.handle) = (yyvsp[0].handle); + } +#line 9351 "parser_bison.c" + break; + + case 206: /* basehook_spec: ruleset_spec basehook_device_name */ +#line 1678 "parser_bison.y" + { + if ((yyvsp[0].string)) { + (yyvsp[-1].handle).obj.name = (yyvsp[0].string); + (yyvsp[-1].handle).obj.location = (yylsp[0]); + } + (yyval.handle) = (yyvsp[-1].handle); + } +#line 9363 "parser_bison.c" + break; + + case 207: /* reset_cmd: "counters" ruleset_spec */ +#line 1688 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9371 "parser_bison.c" + break; + + case 208: /* reset_cmd: "counters" table_spec */ +#line 1692 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9379 "parser_bison.c" + break; + + case 209: /* reset_cmd: "counters" "table" table_spec */ +#line 1696 "parser_bison.y" + { + /* alias of previous rule. */ + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9388 "parser_bison.c" + break; + + case 210: /* reset_cmd: "counter" obj_spec close_scope_counter */ +#line 1701 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTER, &(yyvsp[-1].handle),&(yyloc), NULL); + } +#line 9396 "parser_bison.c" + break; + + case 211: /* reset_cmd: "quotas" ruleset_spec */ +#line 1705 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9404 "parser_bison.c" + break; + + case 212: /* reset_cmd: "quotas" "table" table_spec */ +#line 1709 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9412 "parser_bison.c" + break; + + case 213: /* reset_cmd: "quotas" table_spec */ +#line 1713 "parser_bison.y" + { + /* alias of previous rule. */ + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9421 "parser_bison.c" + break; + + case 214: /* reset_cmd: "quota" obj_spec close_scope_quota */ +#line 1718 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &(yyvsp[-1].handle), &(yyloc), NULL); + } +#line 9429 "parser_bison.c" + break; + + case 215: /* reset_cmd: "rules" ruleset_spec */ +#line 1722 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9437 "parser_bison.c" + break; + + case 216: /* reset_cmd: "rules" table_spec */ +#line 1726 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9445 "parser_bison.c" + break; + + case 217: /* reset_cmd: "rules" "table" table_spec */ +#line 1730 "parser_bison.y" + { + /* alias of previous rule. */ + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9454 "parser_bison.c" + break; + + case 218: /* reset_cmd: "rules" chain_spec */ +#line 1735 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9462 "parser_bison.c" + break; + + case 219: /* reset_cmd: "rules" "chain" chain_spec */ +#line 1739 "parser_bison.y" + { + /* alias of previous rule. */ + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9471 "parser_bison.c" + break; + + case 220: /* reset_cmd: "rule" ruleid_spec */ +#line 1744 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9479 "parser_bison.c" + break; + + case 221: /* reset_cmd: "element" set_spec set_block_expr */ +#line 1748 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &(yyvsp[-1].handle), &(yyloc), (yyvsp[0].expr)); + } +#line 9487 "parser_bison.c" + break; + + case 222: /* reset_cmd: "set" set_or_id_spec */ +#line 1752 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9495 "parser_bison.c" + break; + + case 223: /* reset_cmd: "map" set_or_id_spec */ +#line 1756 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9503 "parser_bison.c" + break; + + case 224: /* flush_cmd: "table" table_spec */ +#line 1762 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_TABLE, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9511 "parser_bison.c" + break; + + case 225: /* flush_cmd: "chain" chain_spec */ +#line 1766 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_CHAIN, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9519 "parser_bison.c" + break; + + case 226: /* flush_cmd: "set" set_spec */ +#line 1770 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9527 "parser_bison.c" + break; + + case 227: /* flush_cmd: "map" set_spec */ +#line 1774 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_MAP, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9535 "parser_bison.c" + break; + + case 228: /* flush_cmd: "flow" "table" set_spec */ +#line 1778 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9543 "parser_bison.c" + break; + + case 229: /* flush_cmd: "meter" set_spec */ +#line 1782 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9551 "parser_bison.c" + break; + + case 230: /* flush_cmd: "ruleset" ruleset_spec */ +#line 1786 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &(yyvsp[0].handle), &(yyloc), NULL); + } +#line 9559 "parser_bison.c" + break; + + case 231: /* rename_cmd: "chain" chain_spec identifier */ +#line 1792 "parser_bison.y" + { + (yyval.cmd) = cmd_alloc(CMD_RENAME, CMD_OBJ_CHAIN, &(yyvsp[-1].handle), &(yyloc), NULL); + (yyval.cmd)->arg = (yyvsp[0].string); + } +#line 9568 "parser_bison.c" + break; + + case 232: /* import_cmd: "ruleset" markup_format */ +#line 1799 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc((yyvsp[0].val)); + (yyval.cmd) = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &(yyloc), markup); + } +#line 9578 "parser_bison.c" + break; + + case 233: /* import_cmd: markup_format */ +#line 1805 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc((yyvsp[0].val)); + (yyval.cmd) = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &(yyloc), markup); + } +#line 9588 "parser_bison.c" + break; + + case 234: /* export_cmd: "ruleset" markup_format */ +#line 1813 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc((yyvsp[0].val)); + (yyval.cmd) = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &(yyloc), markup); + } +#line 9598 "parser_bison.c" + break; + + case 235: /* export_cmd: markup_format */ +#line 1819 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc((yyvsp[0].val)); + (yyval.cmd) = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &(yyloc), markup); + } +#line 9608 "parser_bison.c" + break; + + case 236: /* monitor_cmd: monitor_event monitor_object monitor_format */ +#line 1827 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct monitor *m = monitor_alloc((yyvsp[0].val), (yyvsp[-1].val), (yyvsp[-2].string)); + m->location = (yylsp[-2]); + (yyval.cmd) = cmd_alloc(CMD_MONITOR, CMD_OBJ_MONITOR, &h, &(yyloc), m); + } +#line 9619 "parser_bison.c" + break; + + case 237: /* monitor_event: %empty */ +#line 1835 "parser_bison.y" + { (yyval.string) = NULL; } +#line 9625 "parser_bison.c" + break; + + case 238: /* monitor_event: "string" */ +#line 1836 "parser_bison.y" + { (yyval.string) = (yyvsp[0].string); } +#line 9631 "parser_bison.c" + break; + + case 239: /* monitor_object: %empty */ +#line 1839 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_ANY; } +#line 9637 "parser_bison.c" + break; + + case 240: /* monitor_object: "tables" */ +#line 1840 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_TABLES; } +#line 9643 "parser_bison.c" + break; + + case 241: /* monitor_object: "chains" */ +#line 1841 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_CHAINS; } +#line 9649 "parser_bison.c" + break; + + case 242: /* monitor_object: "sets" */ +#line 1842 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_SETS; } +#line 9655 "parser_bison.c" + break; + + case 243: /* monitor_object: "rules" */ +#line 1843 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_RULES; } +#line 9661 "parser_bison.c" + break; + + case 244: /* monitor_object: "elements" */ +#line 1844 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_ELEMS; } +#line 9667 "parser_bison.c" + break; + + case 245: /* monitor_object: "ruleset" */ +#line 1845 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_RULESET; } +#line 9673 "parser_bison.c" + break; + + case 246: /* monitor_object: "trace" */ +#line 1846 "parser_bison.y" + { (yyval.val) = CMD_MONITOR_OBJ_TRACE; } +#line 9679 "parser_bison.c" + break; + + case 247: /* monitor_format: %empty */ +#line 1849 "parser_bison.y" + { (yyval.val) = NFTNL_OUTPUT_DEFAULT; } +#line 9685 "parser_bison.c" + break; + + case 249: /* markup_format: "xml" */ +#line 1853 "parser_bison.y" + { (yyval.val) = __NFT_OUTPUT_NOTSUPP; } +#line 9691 "parser_bison.c" + break; + + case 250: /* markup_format: "json" */ +#line 1854 "parser_bison.y" + { (yyval.val) = NFTNL_OUTPUT_JSON; } +#line 9697 "parser_bison.c" + break; + + case 251: /* markup_format: "vm" "json" */ +#line 1855 "parser_bison.y" + { (yyval.val) = NFTNL_OUTPUT_JSON; } +#line 9703 "parser_bison.c" + break; + + case 252: /* describe_cmd: primary_expr */ +#line 1859 "parser_bison.y" + { + struct handle h = { .family = NFPROTO_UNSPEC }; + (yyval.cmd) = cmd_alloc(CMD_DESCRIBE, CMD_OBJ_EXPR, &h, &(yyloc), NULL); + (yyval.cmd)->expr = (yyvsp[0].expr); + } +#line 9713 "parser_bison.c" + break; + + case 253: /* table_block_alloc: %empty */ +#line 1867 "parser_bison.y" + { + (yyval.table) = table_alloc(); + if (open_scope(state, &(yyval.table)->scope) < 0) { + erec_queue(error(&(yyloc), "too many levels of nesting"), + state->msgs); + state->nerrs++; + } + } +#line 9726 "parser_bison.c" + break; + + case 254: /* table_options: "flags" "string" */ +#line 1878 "parser_bison.y" + { + if (strcmp((yyvsp[0].string), "dormant") == 0) { + (yyvsp[-2].table)->flags |= TABLE_F_DORMANT; + xfree((yyvsp[0].string)); + } else if (strcmp((yyvsp[0].string), "owner") == 0) { + (yyvsp[-2].table)->flags |= TABLE_F_OWNER; + xfree((yyvsp[0].string)); + } else { + erec_queue(error(&(yylsp[0]), "unknown table option %s", (yyvsp[0].string)), + state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + } +#line 9745 "parser_bison.c" + break; + + case 255: /* table_options: comment_spec */ +#line 1893 "parser_bison.y" + { + if (already_set((yyvsp[-1].table)->comment, &(yyloc), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].table)->comment = (yyvsp[0].string); + } +#line 9757 "parser_bison.c" + break; + + case 256: /* table_block: %empty */ +#line 1902 "parser_bison.y" + { (yyval.table) = (yyvsp[(-1) - (0)].table); } +#line 9763 "parser_bison.c" + break; + + case 260: /* table_block: table_block "chain" chain_identifier chain_block_alloc '{' chain_block '}' stmt_separator */ +#line 1909 "parser_bison.y" + { + (yyvsp[-4].chain)->location = (yylsp[-5]); + handle_merge(&(yyvsp[-4].chain)->handle, &(yyvsp[-5].handle)); + handle_free(&(yyvsp[-5].handle)); + close_scope(state); + list_add_tail(&(yyvsp[-4].chain)->list, &(yyvsp[-7].table)->chains); + (yyval.table) = (yyvsp[-7].table); + } +#line 9776 "parser_bison.c" + break; + + case 261: /* table_block: table_block "set" set_identifier set_block_alloc '{' set_block '}' stmt_separator */ +#line 1920 "parser_bison.y" + { + (yyvsp[-4].set)->location = (yylsp[-5]); + handle_merge(&(yyvsp[-4].set)->handle, &(yyvsp[-5].handle)); + handle_free(&(yyvsp[-5].handle)); + list_add_tail(&(yyvsp[-4].set)->list, &(yyvsp[-7].table)->sets); + (yyval.table) = (yyvsp[-7].table); + } +#line 9788 "parser_bison.c" + break; + + case 262: /* table_block: table_block "map" set_identifier map_block_alloc '{' map_block '}' stmt_separator */ +#line 1930 "parser_bison.y" + { + (yyvsp[-4].set)->location = (yylsp[-5]); + handle_merge(&(yyvsp[-4].set)->handle, &(yyvsp[-5].handle)); + handle_free(&(yyvsp[-5].handle)); + list_add_tail(&(yyvsp[-4].set)->list, &(yyvsp[-7].table)->sets); + (yyval.table) = (yyvsp[-7].table); + } +#line 9800 "parser_bison.c" + break; + + case 263: /* table_block: table_block "flowtable" flowtable_identifier flowtable_block_alloc '{' flowtable_block '}' stmt_separator */ +#line 1941 "parser_bison.y" + { + (yyvsp[-4].flowtable)->location = (yylsp[-5]); + handle_merge(&(yyvsp[-4].flowtable)->handle, &(yyvsp[-5].handle)); + handle_free(&(yyvsp[-5].handle)); + list_add_tail(&(yyvsp[-4].flowtable)->list, &(yyvsp[-7].table)->flowtables); + (yyval.table) = (yyvsp[-7].table); + } +#line 9812 "parser_bison.c" + break; + + case 264: /* table_block: table_block "counter" obj_identifier obj_block_alloc '{' counter_block '}' stmt_separator close_scope_counter */ +#line 1951 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_COUNTER; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-8].table)->objs); + (yyval.table) = (yyvsp[-8].table); + } +#line 9825 "parser_bison.c" + break; + + case 265: /* table_block: table_block "quota" obj_identifier obj_block_alloc '{' quota_block '}' stmt_separator close_scope_quota */ +#line 1962 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_QUOTA; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-8].table)->objs); + (yyval.table) = (yyvsp[-8].table); + } +#line 9838 "parser_bison.c" + break; + + case 266: /* table_block: table_block "ct" "helper" obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator */ +#line 1971 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_CT_HELPER; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-9].table)->objs); + (yyval.table) = (yyvsp[-9].table); + } +#line 9851 "parser_bison.c" + break; + + case 267: /* table_block: table_block "ct" "timeout" obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator */ +#line 1980 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_CT_TIMEOUT; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-9].table)->objs); + (yyval.table) = (yyvsp[-9].table); + } +#line 9864 "parser_bison.c" + break; + + case 268: /* table_block: table_block "ct" "expectation" obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator */ +#line 1989 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_CT_EXPECT; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-9].table)->objs); + (yyval.table) = (yyvsp[-9].table); + } +#line 9877 "parser_bison.c" + break; + + case 269: /* table_block: table_block "limit" obj_identifier obj_block_alloc '{' limit_block '}' stmt_separator close_scope_limit */ +#line 2000 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_LIMIT; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-8].table)->objs); + (yyval.table) = (yyvsp[-8].table); + } +#line 9890 "parser_bison.c" + break; + + case 270: /* table_block: table_block "secmark" obj_identifier obj_block_alloc '{' secmark_block '}' stmt_separator close_scope_secmark */ +#line 2011 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_SECMARK; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-8].table)->objs); + (yyval.table) = (yyvsp[-8].table); + } +#line 9903 "parser_bison.c" + break; + + case 271: /* table_block: table_block "synproxy" obj_identifier obj_block_alloc '{' synproxy_block '}' stmt_separator close_scope_synproxy */ +#line 2022 "parser_bison.y" + { + (yyvsp[-5].obj)->location = (yylsp[-6]); + (yyvsp[-5].obj)->type = NFT_OBJECT_SYNPROXY; + handle_merge(&(yyvsp[-5].obj)->handle, &(yyvsp[-6].handle)); + handle_free(&(yyvsp[-6].handle)); + list_add_tail(&(yyvsp[-5].obj)->list, &(yyvsp[-8].table)->objs); + (yyval.table) = (yyvsp[-8].table); + } +#line 9916 "parser_bison.c" + break; + + case 272: /* chain_block_alloc: %empty */ +#line 2033 "parser_bison.y" + { + (yyval.chain) = chain_alloc(); + if (open_scope(state, &(yyval.chain)->scope) < 0) { + erec_queue(error(&(yyloc), "too many levels of nesting"), + state->msgs); + state->nerrs++; + } + } +#line 9929 "parser_bison.c" + break; + + case 273: /* chain_block: %empty */ +#line 2043 "parser_bison.y" + { (yyval.chain) = (yyvsp[(-1) - (0)].chain); } +#line 9935 "parser_bison.c" + break; + + case 279: /* chain_block: chain_block rule stmt_separator */ +#line 2050 "parser_bison.y" + { + list_add_tail(&(yyvsp[-1].rule)->list, &(yyvsp[-2].chain)->rules); + (yyval.chain) = (yyvsp[-2].chain); + } +#line 9944 "parser_bison.c" + break; + + case 280: /* chain_block: chain_block "devices" '=' flowtable_expr stmt_separator */ +#line 2055 "parser_bison.y" + { + if ((yyval.chain)->dev_expr) { + list_splice_init(&(yyvsp[-1].expr)->expressions, &(yyval.chain)->dev_expr->expressions); + expr_free((yyvsp[-1].expr)); + break; + } + (yyval.chain)->dev_expr = (yyvsp[-1].expr); + } +#line 9957 "parser_bison.c" + break; + + case 281: /* chain_block: chain_block comment_spec stmt_separator */ +#line 2064 "parser_bison.y" + { + if (already_set((yyvsp[-2].chain)->comment, &(yylsp[-1]), state)) { + xfree((yyvsp[-1].string)); + YYERROR; + } + (yyvsp[-2].chain)->comment = (yyvsp[-1].string); + } +#line 9969 "parser_bison.c" + break; + + case 282: /* subchain_block: %empty */ +#line 2073 "parser_bison.y" + { (yyval.chain) = (yyvsp[(-1) - (0)].chain); } +#line 9975 "parser_bison.c" + break; + + case 284: /* subchain_block: subchain_block rule stmt_separator */ +#line 2076 "parser_bison.y" + { + list_add_tail(&(yyvsp[-1].rule)->list, &(yyvsp[-2].chain)->rules); + (yyval.chain) = (yyvsp[-2].chain); + } +#line 9984 "parser_bison.c" + break; + + case 285: /* typeof_data_expr: primary_expr */ +#line 2083 "parser_bison.y" + { + struct expr *e = (yyvsp[0].expr); + + if (e->etype == EXPR_SYMBOL && + strcmp("verdict", e->identifier) == 0) { + struct expr *v = verdict_expr_alloc(&(yylsp[0]), NF_ACCEPT, NULL); + + expr_free(e); + v->flags &= ~EXPR_F_CONSTANT; + e = v; + } + + if (expr_ops(e)->build_udata == NULL) { + erec_queue(error(&(yylsp[0]), "map data type '%s' lacks typeof serialization", expr_ops(e)->name), + state->msgs); + expr_free(e); + YYERROR; + } + (yyval.expr) = e; + } +#line 10009 "parser_bison.c" + break; + + case 286: /* typeof_data_expr: typeof_expr "." primary_expr */ +#line 2104 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 10022 "parser_bison.c" + break; + + case 287: /* typeof_expr: primary_expr */ +#line 2115 "parser_bison.y" + { + if (expr_ops((yyvsp[0].expr))->build_udata == NULL) { + erec_queue(error(&(yylsp[0]), "primary expression type '%s' lacks typeof serialization", expr_ops((yyvsp[0].expr))->name), + state->msgs); + expr_free((yyvsp[0].expr)); + YYERROR; + } + + (yyval.expr) = (yyvsp[0].expr); + } +#line 10037 "parser_bison.c" + break; + + case 288: /* typeof_expr: typeof_expr "." primary_expr */ +#line 2126 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 10050 "parser_bison.c" + break; + + case 289: /* set_block_alloc: %empty */ +#line 2138 "parser_bison.y" + { + (yyval.set) = set_alloc(&internal_location); + } +#line 10058 "parser_bison.c" + break; + + case 290: /* set_block: %empty */ +#line 2143 "parser_bison.y" + { (yyval.set) = (yyvsp[(-1) - (0)].set); } +#line 10064 "parser_bison.c" + break; + + case 293: /* set_block: set_block "type" data_type_expr stmt_separator close_scope_type */ +#line 2147 "parser_bison.y" + { + (yyvsp[-4].set)->key = (yyvsp[-2].expr); + (yyval.set) = (yyvsp[-4].set); + } +#line 10073 "parser_bison.c" + break; + + case 294: /* set_block: set_block "typeof" typeof_expr stmt_separator */ +#line 2152 "parser_bison.y" + { + (yyvsp[-3].set)->key = (yyvsp[-1].expr); + datatype_set((yyvsp[-3].set)->key, (yyvsp[-1].expr)->dtype); + (yyval.set) = (yyvsp[-3].set); + } +#line 10083 "parser_bison.c" + break; + + case 295: /* set_block: set_block "flags" set_flag_list stmt_separator */ +#line 2158 "parser_bison.y" + { + (yyvsp[-3].set)->flags = (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10092 "parser_bison.c" + break; + + case 296: /* set_block: set_block "timeout" time_spec stmt_separator */ +#line 2163 "parser_bison.y" + { + (yyvsp[-3].set)->timeout = (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10101 "parser_bison.c" + break; + + case 297: /* set_block: set_block "gc-interval" time_spec stmt_separator */ +#line 2168 "parser_bison.y" + { + (yyvsp[-3].set)->gc_int = (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10110 "parser_bison.c" + break; + + case 298: /* set_block: set_block stateful_stmt_list stmt_separator */ +#line 2173 "parser_bison.y" + { + list_splice_tail((yyvsp[-1].list), &(yyvsp[-2].set)->stmt_list); + (yyval.set) = (yyvsp[-2].set); + free((yyvsp[-1].list)); + } +#line 10120 "parser_bison.c" + break; + + case 299: /* set_block: set_block "elements" '=' set_block_expr */ +#line 2179 "parser_bison.y" + { + (yyvsp[-3].set)->init = (yyvsp[0].expr); + (yyval.set) = (yyvsp[-3].set); + } +#line 10129 "parser_bison.c" + break; + + case 300: /* set_block: set_block "auto-merge" */ +#line 2184 "parser_bison.y" + { + (yyvsp[-1].set)->automerge = true; + (yyval.set) = (yyvsp[-1].set); + } +#line 10138 "parser_bison.c" + break; + + case 302: /* set_block: set_block comment_spec stmt_separator */ +#line 2190 "parser_bison.y" + { + if (already_set((yyvsp[-2].set)->comment, &(yylsp[-1]), state)) { + xfree((yyvsp[-1].string)); + YYERROR; + } + (yyvsp[-2].set)->comment = (yyvsp[-1].string); + (yyval.set) = (yyvsp[-2].set); + } +#line 10151 "parser_bison.c" + break; + + case 305: /* set_flag_list: set_flag_list "comma" set_flag */ +#line 2205 "parser_bison.y" + { + (yyval.val) = (yyvsp[-2].val) | (yyvsp[0].val); + } +#line 10159 "parser_bison.c" + break; + + case 307: /* set_flag: "constant" */ +#line 2211 "parser_bison.y" + { (yyval.val) = NFT_SET_CONSTANT; } +#line 10165 "parser_bison.c" + break; + + case 308: /* set_flag: "interval" */ +#line 2212 "parser_bison.y" + { (yyval.val) = NFT_SET_INTERVAL; } +#line 10171 "parser_bison.c" + break; + + case 309: /* set_flag: "timeout" */ +#line 2213 "parser_bison.y" + { (yyval.val) = NFT_SET_TIMEOUT; } +#line 10177 "parser_bison.c" + break; + + case 310: /* set_flag: "dynamic" */ +#line 2214 "parser_bison.y" + { (yyval.val) = NFT_SET_EVAL; } +#line 10183 "parser_bison.c" + break; + + case 311: /* map_block_alloc: %empty */ +#line 2218 "parser_bison.y" + { + (yyval.set) = set_alloc(&internal_location); + } +#line 10191 "parser_bison.c" + break; + + case 312: /* map_block_obj_type: "counter" close_scope_counter */ +#line 2223 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_COUNTER; } +#line 10197 "parser_bison.c" + break; + + case 313: /* map_block_obj_type: "quota" close_scope_quota */ +#line 2224 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_QUOTA; } +#line 10203 "parser_bison.c" + break; + + case 314: /* map_block_obj_type: "limit" close_scope_limit */ +#line 2225 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_LIMIT; } +#line 10209 "parser_bison.c" + break; + + case 315: /* map_block_obj_type: "secmark" close_scope_secmark */ +#line 2226 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_SECMARK; } +#line 10215 "parser_bison.c" + break; + + case 316: /* map_block_obj_type: "synproxy" close_scope_synproxy */ +#line 2227 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_SYNPROXY; } +#line 10221 "parser_bison.c" + break; + + case 317: /* map_block_data_interval: "interval" */ +#line 2230 "parser_bison.y" + { (yyval.val) = EXPR_F_INTERVAL; } +#line 10227 "parser_bison.c" + break; + + case 318: /* map_block_data_interval: %empty */ +#line 2231 "parser_bison.y" + { (yyval.val) = 0; } +#line 10233 "parser_bison.c" + break; + + case 319: /* map_block: %empty */ +#line 2234 "parser_bison.y" + { (yyval.set) = (yyvsp[(-1) - (0)].set); } +#line 10239 "parser_bison.c" + break; + + case 322: /* map_block: map_block "timeout" time_spec stmt_separator */ +#line 2238 "parser_bison.y" + { + (yyvsp[-3].set)->timeout = (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10248 "parser_bison.c" + break; + + case 323: /* map_block: map_block "gc-interval" time_spec stmt_separator */ +#line 2243 "parser_bison.y" + { + (yyvsp[-3].set)->gc_int = (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10257 "parser_bison.c" + break; + + case 324: /* map_block: map_block "type" data_type_expr "colon" map_block_data_interval data_type_expr stmt_separator close_scope_type */ +#line 2250 "parser_bison.y" + { + (yyvsp[-7].set)->key = (yyvsp[-5].expr); + (yyvsp[-7].set)->data = (yyvsp[-2].expr); + (yyvsp[-7].set)->data->flags |= (yyvsp[-3].val); + + (yyvsp[-7].set)->flags |= NFT_SET_MAP; + (yyval.set) = (yyvsp[-7].set); + } +#line 10270 "parser_bison.c" + break; + + case 325: /* map_block: map_block "typeof" typeof_expr "colon" typeof_data_expr stmt_separator */ +#line 2261 "parser_bison.y" + { + (yyvsp[-5].set)->key = (yyvsp[-3].expr); + datatype_set((yyvsp[-5].set)->key, (yyvsp[-3].expr)->dtype); + (yyvsp[-5].set)->data = (yyvsp[-1].expr); + + (yyvsp[-5].set)->flags |= NFT_SET_MAP; + (yyval.set) = (yyvsp[-5].set); + } +#line 10283 "parser_bison.c" + break; + + case 326: /* map_block: map_block "typeof" typeof_expr "colon" "interval" typeof_expr stmt_separator */ +#line 2272 "parser_bison.y" + { + (yyvsp[-6].set)->key = (yyvsp[-4].expr); + datatype_set((yyvsp[-6].set)->key, (yyvsp[-4].expr)->dtype); + (yyvsp[-6].set)->data = (yyvsp[-1].expr); + (yyvsp[-6].set)->data->flags |= EXPR_F_INTERVAL; + + (yyvsp[-6].set)->flags |= NFT_SET_MAP; + (yyval.set) = (yyvsp[-6].set); + } +#line 10297 "parser_bison.c" + break; + + case 327: /* map_block: map_block "type" data_type_expr "colon" map_block_obj_type stmt_separator close_scope_type */ +#line 2284 "parser_bison.y" + { + (yyvsp[-6].set)->key = (yyvsp[-4].expr); + (yyvsp[-6].set)->objtype = (yyvsp[-2].val); + (yyvsp[-6].set)->flags |= NFT_SET_OBJECT; + (yyval.set) = (yyvsp[-6].set); + } +#line 10308 "parser_bison.c" + break; + + case 328: /* map_block: map_block "flags" set_flag_list stmt_separator */ +#line 2291 "parser_bison.y" + { + (yyvsp[-3].set)->flags |= (yyvsp[-1].val); + (yyval.set) = (yyvsp[-3].set); + } +#line 10317 "parser_bison.c" + break; + + case 329: /* map_block: map_block stateful_stmt_list stmt_separator */ +#line 2296 "parser_bison.y" + { + list_splice_tail((yyvsp[-1].list), &(yyvsp[-2].set)->stmt_list); + (yyval.set) = (yyvsp[-2].set); + free((yyvsp[-1].list)); + } +#line 10327 "parser_bison.c" + break; + + case 330: /* map_block: map_block "elements" '=' set_block_expr */ +#line 2302 "parser_bison.y" + { + (yyvsp[-3].set)->init = (yyvsp[0].expr); + (yyval.set) = (yyvsp[-3].set); + } +#line 10336 "parser_bison.c" + break; + + case 331: /* map_block: map_block comment_spec stmt_separator */ +#line 2307 "parser_bison.y" + { + if (already_set((yyvsp[-2].set)->comment, &(yylsp[-1]), state)) { + xfree((yyvsp[-1].string)); + YYERROR; + } + (yyvsp[-2].set)->comment = (yyvsp[-1].string); + (yyval.set) = (yyvsp[-2].set); + } +#line 10349 "parser_bison.c" + break; + + case 333: /* set_mechanism: "policy" set_policy_spec close_scope_policy */ +#line 2319 "parser_bison.y" + { + (yyvsp[-3].set)->policy = (yyvsp[-1].val); + } +#line 10357 "parser_bison.c" + break; + + case 334: /* set_mechanism: "size" "number" */ +#line 2323 "parser_bison.y" + { + (yyvsp[-2].set)->desc.size = (yyvsp[0].val); + } +#line 10365 "parser_bison.c" + break; + + case 335: /* set_policy_spec: "performance" */ +#line 2328 "parser_bison.y" + { (yyval.val) = NFT_SET_POL_PERFORMANCE; } +#line 10371 "parser_bison.c" + break; + + case 336: /* set_policy_spec: "memory" */ +#line 2329 "parser_bison.y" + { (yyval.val) = NFT_SET_POL_MEMORY; } +#line 10377 "parser_bison.c" + break; + + case 337: /* flowtable_block_alloc: %empty */ +#line 2333 "parser_bison.y" + { + (yyval.flowtable) = flowtable_alloc(&internal_location); + } +#line 10385 "parser_bison.c" + break; + + case 338: /* flowtable_block: %empty */ +#line 2338 "parser_bison.y" + { (yyval.flowtable) = (yyvsp[(-1) - (0)].flowtable); } +#line 10391 "parser_bison.c" + break; + + case 341: /* flowtable_block: flowtable_block "hook" "string" prio_spec stmt_separator */ +#line 2342 "parser_bison.y" + { + (yyval.flowtable)->hook.loc = (yylsp[-2]); + (yyval.flowtable)->hook.name = chain_hookname_lookup((yyvsp[-2].string)); + if ((yyval.flowtable)->hook.name == NULL) { + erec_queue(error(&(yylsp[-2]), "unknown chain hook"), + state->msgs); + xfree((yyvsp[-2].string)); + YYERROR; + } + xfree((yyvsp[-2].string)); + + (yyval.flowtable)->priority = (yyvsp[-1].prio_spec); + } +#line 10409 "parser_bison.c" + break; + + case 342: /* flowtable_block: flowtable_block "devices" '=' flowtable_expr stmt_separator */ +#line 2356 "parser_bison.y" + { + (yyval.flowtable)->dev_expr = (yyvsp[-1].expr); + } +#line 10417 "parser_bison.c" + break; + + case 343: /* flowtable_block: flowtable_block "counter" close_scope_counter */ +#line 2360 "parser_bison.y" + { + (yyval.flowtable)->flags |= NFT_FLOWTABLE_COUNTER; + } +#line 10425 "parser_bison.c" + break; + + case 344: /* flowtable_block: flowtable_block "flags" "offload" stmt_separator */ +#line 2364 "parser_bison.y" + { + (yyval.flowtable)->flags |= FLOWTABLE_F_HW_OFFLOAD; + } +#line 10433 "parser_bison.c" + break; + + case 345: /* flowtable_expr: '{' flowtable_list_expr '}' */ +#line 2370 "parser_bison.y" + { + (yyvsp[-1].expr)->location = (yyloc); + (yyval.expr) = (yyvsp[-1].expr); + } +#line 10442 "parser_bison.c" + break; + + case 346: /* flowtable_expr: variable_expr */ +#line 2375 "parser_bison.y" + { + (yyvsp[0].expr)->location = (yyloc); + (yyval.expr) = (yyvsp[0].expr); + } +#line 10451 "parser_bison.c" + break; + + case 347: /* flowtable_list_expr: flowtable_expr_member */ +#line 2382 "parser_bison.y" + { + (yyval.expr) = compound_expr_alloc(&(yyloc), EXPR_LIST); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 10460 "parser_bison.c" + break; + + case 348: /* flowtable_list_expr: flowtable_list_expr "comma" flowtable_expr_member */ +#line 2387 "parser_bison.y" + { + compound_expr_add((yyvsp[-2].expr), (yyvsp[0].expr)); + (yyval.expr) = (yyvsp[-2].expr); + } +#line 10469 "parser_bison.c" + break; + + case 350: /* flowtable_expr_member: "quoted string" */ +#line 2395 "parser_bison.y" + { + struct expr *expr = ifname_expr_alloc(&(yyloc), state->msgs, (yyvsp[0].string)); + + if (!expr) + YYERROR; + + (yyval.expr) = expr; + } +#line 10482 "parser_bison.c" + break; + + case 351: /* flowtable_expr_member: "string" */ +#line 2404 "parser_bison.y" + { + struct expr *expr = ifname_expr_alloc(&(yyloc), state->msgs, (yyvsp[0].string)); + + if (!expr) + YYERROR; + + (yyval.expr) = expr; + } +#line 10495 "parser_bison.c" + break; + + case 352: /* flowtable_expr_member: variable_expr */ +#line 2413 "parser_bison.y" + { + datatype_set((yyvsp[0].expr)->sym->expr, &ifname_type); + (yyval.expr) = (yyvsp[0].expr); + } +#line 10504 "parser_bison.c" + break; + + case 353: /* data_type_atom_expr: type_identifier */ +#line 2420 "parser_bison.y" + { + const struct datatype *dtype = datatype_lookup_byname((yyvsp[0].string)); + if (dtype == NULL) { + erec_queue(error(&(yylsp[0]), "unknown datatype %s", (yyvsp[0].string)), + state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + (yyval.expr) = constant_expr_alloc(&(yylsp[0]), dtype, dtype->byteorder, + dtype->size, NULL); + xfree((yyvsp[0].string)); + } +#line 10521 "parser_bison.c" + break; + + case 354: /* data_type_atom_expr: "time" */ +#line 2433 "parser_bison.y" + { + (yyval.expr) = constant_expr_alloc(&(yylsp[0]), &time_type, time_type.byteorder, + time_type.size, NULL); + } +#line 10530 "parser_bison.c" + break; + + case 356: /* data_type_expr: data_type_expr "." data_type_atom_expr */ +#line 2441 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 10543 "parser_bison.c" + break; + + case 357: /* obj_block_alloc: %empty */ +#line 2452 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&internal_location); + } +#line 10551 "parser_bison.c" + break; + + case 358: /* counter_block: %empty */ +#line 2457 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10557 "parser_bison.c" + break; + + case 361: /* counter_block: counter_block counter_config */ +#line 2461 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10565 "parser_bison.c" + break; + + case 362: /* counter_block: counter_block comment_spec */ +#line 2465 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10577 "parser_bison.c" + break; + + case 363: /* quota_block: %empty */ +#line 2474 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10583 "parser_bison.c" + break; + + case 366: /* quota_block: quota_block quota_config */ +#line 2478 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10591 "parser_bison.c" + break; + + case 367: /* quota_block: quota_block comment_spec */ +#line 2482 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10603 "parser_bison.c" + break; + + case 368: /* ct_helper_block: %empty */ +#line 2491 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10609 "parser_bison.c" + break; + + case 371: /* ct_helper_block: ct_helper_block ct_helper_config */ +#line 2495 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10617 "parser_bison.c" + break; + + case 372: /* ct_helper_block: ct_helper_block comment_spec */ +#line 2499 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10629 "parser_bison.c" + break; + + case 373: /* ct_timeout_block: %empty */ +#line 2509 "parser_bison.y" + { + (yyval.obj) = (yyvsp[(-1) - (0)].obj); + init_list_head(&(yyval.obj)->ct_timeout.timeout_list); + } +#line 10638 "parser_bison.c" + break; + + case 376: /* ct_timeout_block: ct_timeout_block ct_timeout_config */ +#line 2516 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10646 "parser_bison.c" + break; + + case 377: /* ct_timeout_block: ct_timeout_block comment_spec */ +#line 2520 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10658 "parser_bison.c" + break; + + case 378: /* ct_expect_block: %empty */ +#line 2529 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10664 "parser_bison.c" + break; + + case 381: /* ct_expect_block: ct_expect_block ct_expect_config */ +#line 2533 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10672 "parser_bison.c" + break; + + case 382: /* ct_expect_block: ct_expect_block comment_spec */ +#line 2537 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10684 "parser_bison.c" + break; + + case 383: /* limit_block: %empty */ +#line 2546 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10690 "parser_bison.c" + break; + + case 386: /* limit_block: limit_block limit_config */ +#line 2550 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10698 "parser_bison.c" + break; + + case 387: /* limit_block: limit_block comment_spec */ +#line 2554 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10710 "parser_bison.c" + break; + + case 388: /* secmark_block: %empty */ +#line 2563 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10716 "parser_bison.c" + break; + + case 391: /* secmark_block: secmark_block secmark_config */ +#line 2567 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10724 "parser_bison.c" + break; + + case 392: /* secmark_block: secmark_block comment_spec */ +#line 2571 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10736 "parser_bison.c" + break; + + case 393: /* synproxy_block: %empty */ +#line 2580 "parser_bison.y" + { (yyval.obj) = (yyvsp[(-1) - (0)].obj); } +#line 10742 "parser_bison.c" + break; + + case 396: /* synproxy_block: synproxy_block synproxy_config */ +#line 2584 "parser_bison.y" + { + (yyval.obj) = (yyvsp[-1].obj); + } +#line 10750 "parser_bison.c" + break; + + case 397: /* synproxy_block: synproxy_block comment_spec */ +#line 2588 "parser_bison.y" + { + if (already_set((yyvsp[-1].obj)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].obj)->comment = (yyvsp[0].string); + } +#line 10762 "parser_bison.c" + break; + + case 398: /* type_identifier: "string" */ +#line 2597 "parser_bison.y" + { (yyval.string) = (yyvsp[0].string); } +#line 10768 "parser_bison.c" + break; + + case 399: /* type_identifier: "mark" */ +#line 2598 "parser_bison.y" + { (yyval.string) = xstrdup("mark"); } +#line 10774 "parser_bison.c" + break; + + case 400: /* type_identifier: "dscp" */ +#line 2599 "parser_bison.y" + { (yyval.string) = xstrdup("dscp"); } +#line 10780 "parser_bison.c" + break; + + case 401: /* type_identifier: "ecn" */ +#line 2600 "parser_bison.y" + { (yyval.string) = xstrdup("ecn"); } +#line 10786 "parser_bison.c" + break; + + case 402: /* type_identifier: "classid" */ +#line 2601 "parser_bison.y" + { (yyval.string) = xstrdup("classid"); } +#line 10792 "parser_bison.c" + break; + + case 403: /* hook_spec: "type" close_scope_type "string" "hook" "string" dev_spec prio_spec */ +#line 2605 "parser_bison.y" + { + const char *chain_type = chain_type_name_lookup((yyvsp[-4].string)); + + if (chain_type == NULL) { + erec_queue(error(&(yylsp[-4]), "unknown chain type"), + state->msgs); + xfree((yyvsp[-4].string)); + YYERROR; + } + (yyvsp[-7].chain)->type.loc = (yylsp[-4]); + (yyvsp[-7].chain)->type.str = xstrdup(chain_type); + xfree((yyvsp[-4].string)); + + (yyvsp[-7].chain)->loc = (yyloc); + (yyvsp[-7].chain)->hook.loc = (yylsp[-2]); + (yyvsp[-7].chain)->hook.name = chain_hookname_lookup((yyvsp[-2].string)); + if ((yyvsp[-7].chain)->hook.name == NULL) { + erec_queue(error(&(yylsp[-2]), "unknown chain hook"), + state->msgs); + xfree((yyvsp[-2].string)); + YYERROR; + } + xfree((yyvsp[-2].string)); + + (yyvsp[-7].chain)->dev_expr = (yyvsp[-1].expr); + (yyvsp[-7].chain)->priority = (yyvsp[0].prio_spec); + (yyvsp[-7].chain)->flags |= CHAIN_F_BASECHAIN; + } +#line 10825 "parser_bison.c" + break; + + case 404: /* prio_spec: "priority" extended_prio_spec */ +#line 2636 "parser_bison.y" + { + (yyval.prio_spec) = (yyvsp[0].prio_spec); + (yyval.prio_spec).loc = (yyloc); + } +#line 10834 "parser_bison.c" + break; + + case 405: /* extended_prio_name: "out" */ +#line 2643 "parser_bison.y" + { + (yyval.string) = strdup("out"); + } +#line 10842 "parser_bison.c" + break; + + case 407: /* extended_prio_spec: int_num */ +#line 2650 "parser_bison.y" + { + struct prio_spec spec = {0}; + + spec.expr = constant_expr_alloc(&(yyloc), &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, &(yyvsp[0].val32)); + (yyval.prio_spec) = spec; + } +#line 10856 "parser_bison.c" + break; + + case 408: /* extended_prio_spec: variable_expr */ +#line 2660 "parser_bison.y" + { + struct prio_spec spec = {0}; + + spec.expr = (yyvsp[0].expr); + (yyval.prio_spec) = spec; + } +#line 10867 "parser_bison.c" + break; + + case 409: /* extended_prio_spec: extended_prio_name */ +#line 2667 "parser_bison.y" + { + struct prio_spec spec = {0}; + + spec.expr = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + strlen((yyvsp[0].string)) * BITS_PER_BYTE, + (yyvsp[0].string)); + xfree((yyvsp[0].string)); + (yyval.prio_spec) = spec; + } +#line 10882 "parser_bison.c" + break; + + case 410: /* extended_prio_spec: extended_prio_name "+" "number" */ +#line 2678 "parser_bison.y" + { + struct prio_spec spec = {0}; + + char str[NFT_NAME_MAXLEN]; + snprintf(str, sizeof(str), "%s + %" PRIu64, (yyvsp[-2].string), (yyvsp[0].val)); + spec.expr = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(str) * BITS_PER_BYTE, + str); + xfree((yyvsp[-2].string)); + (yyval.prio_spec) = spec; + } +#line 10899 "parser_bison.c" + break; + + case 411: /* extended_prio_spec: extended_prio_name "-" "number" */ +#line 2691 "parser_bison.y" + { + struct prio_spec spec = {0}; + char str[NFT_NAME_MAXLEN]; + + snprintf(str, sizeof(str), "%s - %" PRIu64, (yyvsp[-2].string), (yyvsp[0].val)); + spec.expr = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(str) * BITS_PER_BYTE, + str); + xfree((yyvsp[-2].string)); + (yyval.prio_spec) = spec; + } +#line 10916 "parser_bison.c" + break; + + case 412: /* int_num: "number" */ +#line 2705 "parser_bison.y" + { (yyval.val32) = (yyvsp[0].val); } +#line 10922 "parser_bison.c" + break; + + case 413: /* int_num: "-" "number" */ +#line 2706 "parser_bison.y" + { (yyval.val32) = -(yyvsp[0].val); } +#line 10928 "parser_bison.c" + break; + + case 414: /* dev_spec: "device" string */ +#line 2710 "parser_bison.y" + { + struct expr *expr = ifname_expr_alloc(&(yyloc), state->msgs, (yyvsp[0].string)); + + if (!expr) + YYERROR; + + (yyval.expr) = compound_expr_alloc(&(yyloc), EXPR_LIST); + compound_expr_add((yyval.expr), expr); + + } +#line 10943 "parser_bison.c" + break; + + case 415: /* dev_spec: "device" variable_expr */ +#line 2721 "parser_bison.y" + { + datatype_set((yyvsp[0].expr)->sym->expr, &ifname_type); + (yyval.expr) = compound_expr_alloc(&(yyloc), EXPR_LIST); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 10953 "parser_bison.c" + break; + + case 416: /* dev_spec: "devices" '=' flowtable_expr */ +#line 2727 "parser_bison.y" + { + (yyval.expr) = (yyvsp[0].expr); + } +#line 10961 "parser_bison.c" + break; + + case 417: /* dev_spec: %empty */ +#line 2730 "parser_bison.y" + { (yyval.expr) = NULL; } +#line 10967 "parser_bison.c" + break; + + case 418: /* flags_spec: "flags" "offload" */ +#line 2734 "parser_bison.y" + { + (yyvsp[-2].chain)->flags |= CHAIN_F_HW_OFFLOAD; + } +#line 10975 "parser_bison.c" + break; + + case 419: /* policy_spec: "policy" policy_expr close_scope_policy */ +#line 2740 "parser_bison.y" + { + if ((yyvsp[-3].chain)->policy) { + erec_queue(error(&(yyloc), "you cannot set chain policy twice"), + state->msgs); + expr_free((yyvsp[-1].expr)); + YYERROR; + } + (yyvsp[-3].chain)->policy = (yyvsp[-1].expr); + (yyvsp[-3].chain)->policy->location = (yyloc); + } +#line 10990 "parser_bison.c" + break; + + case 420: /* policy_expr: variable_expr */ +#line 2753 "parser_bison.y" + { + datatype_set((yyvsp[0].expr)->sym->expr, &policy_type); + (yyval.expr) = (yyvsp[0].expr); + } +#line 10999 "parser_bison.c" + break; + + case 421: /* policy_expr: chain_policy */ +#line 2758 "parser_bison.y" + { + (yyval.expr) = constant_expr_alloc(&(yyloc), &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, &(yyvsp[0].val32)); + } +#line 11010 "parser_bison.c" + break; + + case 422: /* chain_policy: "accept" */ +#line 2766 "parser_bison.y" + { (yyval.val32) = NF_ACCEPT; } +#line 11016 "parser_bison.c" + break; + + case 423: /* chain_policy: "drop" */ +#line 2767 "parser_bison.y" + { (yyval.val32) = NF_DROP; } +#line 11022 "parser_bison.c" + break; + + case 425: /* identifier: "last" */ +#line 2771 "parser_bison.y" + { (yyval.string) = xstrdup("last"); } +#line 11028 "parser_bison.c" + break; + + case 429: /* time_spec: "string" */ +#line 2780 "parser_bison.y" + { + struct error_record *erec; + uint64_t res; + + erec = time_parse(&(yylsp[0]), (yyvsp[0].string), &res); + xfree((yyvsp[0].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.val) = res; + } +#line 11045 "parser_bison.c" + break; + + case 431: /* time_spec_or_num_s: time_spec */ +#line 2796 "parser_bison.y" + { (yyval.val) = (yyvsp[0].val) / 1000u; } +#line 11051 "parser_bison.c" + break; + + case 432: /* family_spec: %empty */ +#line 2799 "parser_bison.y" + { (yyval.val) = NFPROTO_IPV4; } +#line 11057 "parser_bison.c" + break; + + case 434: /* family_spec_explicit: "ip" close_scope_ip */ +#line 2803 "parser_bison.y" + { (yyval.val) = NFPROTO_IPV4; } +#line 11063 "parser_bison.c" + break; + + case 435: /* family_spec_explicit: "ip6" close_scope_ip6 */ +#line 2804 "parser_bison.y" + { (yyval.val) = NFPROTO_IPV6; } +#line 11069 "parser_bison.c" + break; + + case 436: /* family_spec_explicit: "inet" */ +#line 2805 "parser_bison.y" + { (yyval.val) = NFPROTO_INET; } +#line 11075 "parser_bison.c" + break; + + case 437: /* family_spec_explicit: "arp" close_scope_arp */ +#line 2806 "parser_bison.y" + { (yyval.val) = NFPROTO_ARP; } +#line 11081 "parser_bison.c" + break; + + case 438: /* family_spec_explicit: "bridge" */ +#line 2807 "parser_bison.y" + { (yyval.val) = NFPROTO_BRIDGE; } +#line 11087 "parser_bison.c" + break; + + case 439: /* family_spec_explicit: "netdev" */ +#line 2808 "parser_bison.y" + { (yyval.val) = NFPROTO_NETDEV; } +#line 11093 "parser_bison.c" + break; + + case 440: /* table_spec: family_spec identifier */ +#line 2812 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).family = (yyvsp[-1].val); + (yyval.handle).table.location = (yylsp[0]); + (yyval.handle).table.name = (yyvsp[0].string); + } +#line 11104 "parser_bison.c" + break; + + case 441: /* tableid_spec: family_spec "handle" "number" */ +#line 2821 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).family = (yyvsp[-2].val); + (yyval.handle).handle.id = (yyvsp[0].val); + (yyval.handle).handle.location = (yylsp[0]); + } +#line 11115 "parser_bison.c" + break; + + case 442: /* chain_spec: table_spec identifier */ +#line 2830 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-1].handle); + (yyval.handle).chain.name = (yyvsp[0].string); + (yyval.handle).chain.location = (yylsp[0]); + } +#line 11125 "parser_bison.c" + break; + + case 443: /* chainid_spec: table_spec "handle" "number" */ +#line 2838 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-2].handle); + (yyval.handle).handle.location = (yylsp[0]); + (yyval.handle).handle.id = (yyvsp[0].val); + } +#line 11135 "parser_bison.c" + break; + + case 444: /* chain_identifier: identifier */ +#line 2846 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).chain.name = (yyvsp[0].string); + (yyval.handle).chain.location = (yylsp[0]); + } +#line 11145 "parser_bison.c" + break; + + case 445: /* set_spec: table_spec identifier */ +#line 2854 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-1].handle); + (yyval.handle).set.name = (yyvsp[0].string); + (yyval.handle).set.location = (yylsp[0]); + } +#line 11155 "parser_bison.c" + break; + + case 446: /* setid_spec: table_spec "handle" "number" */ +#line 2862 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-2].handle); + (yyval.handle).handle.location = (yylsp[0]); + (yyval.handle).handle.id = (yyvsp[0].val); + } +#line 11165 "parser_bison.c" + break; + + case 447: /* set_identifier: identifier */ +#line 2870 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).set.name = (yyvsp[0].string); + (yyval.handle).set.location = (yylsp[0]); + } +#line 11175 "parser_bison.c" + break; + + case 448: /* flowtable_spec: table_spec identifier */ +#line 2878 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-1].handle); + (yyval.handle).flowtable.name = (yyvsp[0].string); + (yyval.handle).flowtable.location = (yylsp[0]); + } +#line 11185 "parser_bison.c" + break; + + case 449: /* flowtableid_spec: table_spec "handle" "number" */ +#line 2886 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-2].handle); + (yyval.handle).handle.location = (yylsp[0]); + (yyval.handle).handle.id = (yyvsp[0].val); + } +#line 11195 "parser_bison.c" + break; + + case 450: /* flowtable_identifier: identifier */ +#line 2894 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).flowtable.name = (yyvsp[0].string); + (yyval.handle).flowtable.location = (yylsp[0]); + } +#line 11205 "parser_bison.c" + break; + + case 451: /* obj_spec: table_spec identifier */ +#line 2902 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-1].handle); + (yyval.handle).obj.name = (yyvsp[0].string); + (yyval.handle).obj.location = (yylsp[0]); + } +#line 11215 "parser_bison.c" + break; + + case 452: /* objid_spec: table_spec "handle" "number" */ +#line 2910 "parser_bison.y" + { + (yyval.handle) = (yyvsp[-2].handle); + (yyval.handle).handle.location = (yylsp[0]); + (yyval.handle).handle.id = (yyvsp[0].val); + } +#line 11225 "parser_bison.c" + break; + + case 453: /* obj_identifier: identifier */ +#line 2918 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).obj.name = (yyvsp[0].string); + (yyval.handle).obj.location = (yylsp[0]); + } +#line 11235 "parser_bison.c" + break; + + case 454: /* handle_spec: "handle" "number" */ +#line 2926 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).handle.location = (yylsp[0]); + (yyval.handle).handle.id = (yyvsp[0].val); + } +#line 11245 "parser_bison.c" + break; + + case 455: /* position_spec: "position" "number" */ +#line 2934 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).position.location = (yyloc); + (yyval.handle).position.id = (yyvsp[0].val); + } +#line 11255 "parser_bison.c" + break; + + case 456: /* index_spec: "index" "number" */ +#line 2942 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).index.location = (yyloc); + (yyval.handle).index.id = (yyvsp[0].val) + 1; + } +#line 11265 "parser_bison.c" + break; + + case 457: /* rule_position: chain_spec */ +#line 2950 "parser_bison.y" + { + (yyval.handle) = (yyvsp[0].handle); + } +#line 11273 "parser_bison.c" + break; + + case 458: /* rule_position: chain_spec position_spec */ +#line 2954 "parser_bison.y" + { + handle_merge(&(yyvsp[-1].handle), &(yyvsp[0].handle)); + (yyval.handle) = (yyvsp[-1].handle); + } +#line 11282 "parser_bison.c" + break; + + case 459: /* rule_position: chain_spec handle_spec */ +#line 2959 "parser_bison.y" + { + (yyvsp[0].handle).position.location = (yyvsp[0].handle).handle.location; + (yyvsp[0].handle).position.id = (yyvsp[0].handle).handle.id; + (yyvsp[0].handle).handle.id = 0; + handle_merge(&(yyvsp[-1].handle), &(yyvsp[0].handle)); + (yyval.handle) = (yyvsp[-1].handle); + } +#line 11294 "parser_bison.c" + break; + + case 460: /* rule_position: chain_spec index_spec */ +#line 2967 "parser_bison.y" + { + handle_merge(&(yyvsp[-1].handle), &(yyvsp[0].handle)); + (yyval.handle) = (yyvsp[-1].handle); + } +#line 11303 "parser_bison.c" + break; + + case 461: /* ruleid_spec: chain_spec handle_spec */ +#line 2974 "parser_bison.y" + { + handle_merge(&(yyvsp[-1].handle), &(yyvsp[0].handle)); + (yyval.handle) = (yyvsp[-1].handle); + } +#line 11312 "parser_bison.c" + break; + + case 462: /* comment_spec: "comment" string */ +#line 2981 "parser_bison.y" + { + if (strlen((yyvsp[0].string)) > NFTNL_UDATA_COMMENT_MAXLEN) { + erec_queue(error(&(yylsp[0]), "comment too long, %d characters maximum allowed", + NFTNL_UDATA_COMMENT_MAXLEN), + state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + (yyval.string) = (yyvsp[0].string); + } +#line 11327 "parser_bison.c" + break; + + case 463: /* ruleset_spec: %empty */ +#line 2994 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).family = NFPROTO_UNSPEC; + } +#line 11336 "parser_bison.c" + break; + + case 464: /* ruleset_spec: family_spec_explicit */ +#line 2999 "parser_bison.y" + { + memset(&(yyval.handle), 0, sizeof((yyval.handle))); + (yyval.handle).family = (yyvsp[0].val); + } +#line 11345 "parser_bison.c" + break; + + case 465: /* rule: rule_alloc */ +#line 3006 "parser_bison.y" + { + (yyval.rule)->comment = NULL; + } +#line 11353 "parser_bison.c" + break; + + case 466: /* rule: rule_alloc comment_spec */ +#line 3010 "parser_bison.y" + { + (yyval.rule)->comment = (yyvsp[0].string); + } +#line 11361 "parser_bison.c" + break; + + case 467: /* rule_alloc: stmt_list */ +#line 3016 "parser_bison.y" + { + struct stmt *i; + + (yyval.rule) = rule_alloc(&(yyloc), NULL); + list_for_each_entry(i, (yyvsp[0].list), list) + (yyval.rule)->num_stmts++; + list_splice_tail((yyvsp[0].list), &(yyval.rule)->stmts); + xfree((yyvsp[0].list)); + } +#line 11375 "parser_bison.c" + break; + + case 468: /* stmt_list: stmt */ +#line 3028 "parser_bison.y" + { + (yyval.list) = xmalloc(sizeof(*(yyval.list))); + init_list_head((yyval.list)); + list_add_tail(&(yyvsp[0].stmt)->list, (yyval.list)); + } +#line 11385 "parser_bison.c" + break; + + case 469: /* stmt_list: stmt_list stmt */ +#line 3034 "parser_bison.y" + { + (yyval.list) = (yyvsp[-1].list); + list_add_tail(&(yyvsp[0].stmt)->list, (yyvsp[-1].list)); + } +#line 11394 "parser_bison.c" + break; + + case 470: /* stateful_stmt_list: stateful_stmt */ +#line 3041 "parser_bison.y" + { + (yyval.list) = xmalloc(sizeof(*(yyval.list))); + init_list_head((yyval.list)); + list_add_tail(&(yyvsp[0].stmt)->list, (yyval.list)); + } +#line 11404 "parser_bison.c" + break; + + case 471: /* stateful_stmt_list: stateful_stmt_list stateful_stmt */ +#line 3047 "parser_bison.y" + { + (yyval.list) = (yyvsp[-1].list); + list_add_tail(&(yyvsp[0].stmt)->list, (yyvsp[-1].list)); + } +#line 11413 "parser_bison.c" + break; + + case 499: /* xt_stmt: "xt" "string" string */ +#line 3085 "parser_bison.y" + { + (yyval.stmt) = NULL; + xfree((yyvsp[-1].string)); + xfree((yyvsp[0].string)); + erec_queue(error(&(yyloc), "unsupported xtables compat expression, use iptables-nft with this ruleset"), + state->msgs); + YYERROR; + } +#line 11426 "parser_bison.c" + break; + + case 500: /* chain_stmt_type: "jump" */ +#line 3095 "parser_bison.y" + { (yyval.val) = NFT_JUMP; } +#line 11432 "parser_bison.c" + break; + + case 501: /* chain_stmt_type: "goto" */ +#line 3096 "parser_bison.y" + { (yyval.val) = NFT_GOTO; } +#line 11438 "parser_bison.c" + break; + + case 502: /* chain_stmt: chain_stmt_type chain_block_alloc '{' subchain_block '}' */ +#line 3100 "parser_bison.y" + { + (yyvsp[-3].chain)->location = (yylsp[-3]); + close_scope(state); + (yyvsp[-1].chain)->location = (yylsp[-1]); + (yyval.stmt) = chain_stmt_alloc(&(yyloc), (yyvsp[-1].chain), (yyvsp[-4].val)); + } +#line 11449 "parser_bison.c" + break; + + case 503: /* verdict_stmt: verdict_expr */ +#line 3109 "parser_bison.y" + { + (yyval.stmt) = verdict_stmt_alloc(&(yyloc), (yyvsp[0].expr)); + } +#line 11457 "parser_bison.c" + break; + + case 504: /* verdict_stmt: verdict_map_stmt */ +#line 3113 "parser_bison.y" + { + (yyval.stmt) = verdict_stmt_alloc(&(yyloc), (yyvsp[0].expr)); + } +#line 11465 "parser_bison.c" + break; + + case 505: /* verdict_map_stmt: concat_expr "vmap" verdict_map_expr */ +#line 3119 "parser_bison.y" + { + (yyval.expr) = map_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 11473 "parser_bison.c" + break; + + case 506: /* verdict_map_expr: '{' verdict_map_list_expr '}' */ +#line 3125 "parser_bison.y" + { + (yyvsp[-1].expr)->location = (yyloc); + (yyval.expr) = (yyvsp[-1].expr); + } +#line 11482 "parser_bison.c" + break; + + case 508: /* verdict_map_list_expr: verdict_map_list_member_expr */ +#line 3133 "parser_bison.y" + { + (yyval.expr) = set_expr_alloc(&(yyloc), NULL); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 11491 "parser_bison.c" + break; + + case 509: /* verdict_map_list_expr: verdict_map_list_expr "comma" verdict_map_list_member_expr */ +#line 3138 "parser_bison.y" + { + compound_expr_add((yyvsp[-2].expr), (yyvsp[0].expr)); + (yyval.expr) = (yyvsp[-2].expr); + } +#line 11500 "parser_bison.c" + break; + + case 511: /* verdict_map_list_member_expr: opt_newline set_elem_expr "colon" verdict_expr opt_newline */ +#line 3146 "parser_bison.y" + { + (yyval.expr) = mapping_expr_alloc(&(yylsp[-3]), (yyvsp[-3].expr), (yyvsp[-1].expr)); + } +#line 11508 "parser_bison.c" + break; + + case 512: /* connlimit_stmt: "ct" "count" "number" close_scope_ct */ +#line 3152 "parser_bison.y" + { + (yyval.stmt) = connlimit_stmt_alloc(&(yyloc)); + (yyval.stmt)->connlimit.count = (yyvsp[-1].val); + } +#line 11517 "parser_bison.c" + break; + + case 513: /* connlimit_stmt: "ct" "count" "over" "number" close_scope_ct */ +#line 3157 "parser_bison.y" + { + (yyval.stmt) = connlimit_stmt_alloc(&(yyloc)); + (yyval.stmt)->connlimit.count = (yyvsp[-1].val); + (yyval.stmt)->connlimit.flags = NFT_CONNLIMIT_F_INV; + } +#line 11527 "parser_bison.c" + break; + + case 516: /* counter_stmt_alloc: "counter" */ +#line 3168 "parser_bison.y" + { + (yyval.stmt) = counter_stmt_alloc(&(yyloc)); + } +#line 11535 "parser_bison.c" + break; + + case 517: /* counter_stmt_alloc: "counter" "name" stmt_expr */ +#line 3172 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_COUNTER; + (yyval.stmt)->objref.expr = (yyvsp[0].expr); + } +#line 11545 "parser_bison.c" + break; + + case 518: /* counter_args: counter_arg */ +#line 3180 "parser_bison.y" + { + (yyval.stmt) = (yyvsp[-1].stmt); + } +#line 11553 "parser_bison.c" + break; + + case 520: /* counter_arg: "packets" "number" */ +#line 3187 "parser_bison.y" + { + (yyvsp[-2].stmt)->counter.packets = (yyvsp[0].val); + } +#line 11561 "parser_bison.c" + break; + + case 521: /* counter_arg: "bytes" "number" */ +#line 3191 "parser_bison.y" + { + (yyvsp[-2].stmt)->counter.bytes = (yyvsp[0].val); + } +#line 11569 "parser_bison.c" + break; + + case 522: /* last_stmt: "last" */ +#line 3197 "parser_bison.y" + { + (yyval.stmt) = last_stmt_alloc(&(yyloc)); + } +#line 11577 "parser_bison.c" + break; + + case 523: /* last_stmt: "last" "used" "never" */ +#line 3201 "parser_bison.y" + { + (yyval.stmt) = last_stmt_alloc(&(yyloc)); + } +#line 11585 "parser_bison.c" + break; + + case 524: /* last_stmt: "last" "used" time_spec */ +#line 3205 "parser_bison.y" + { + (yyval.stmt) = last_stmt_alloc(&(yyloc)); + (yyval.stmt)->last.used = (yyvsp[0].val); + (yyval.stmt)->last.set = true; + } +#line 11595 "parser_bison.c" + break; + + case 527: /* log_stmt_alloc: "log" */ +#line 3217 "parser_bison.y" + { + (yyval.stmt) = log_stmt_alloc(&(yyloc)); + } +#line 11603 "parser_bison.c" + break; + + case 528: /* log_args: log_arg */ +#line 3223 "parser_bison.y" + { + (yyval.stmt) = (yyvsp[-1].stmt); + } +#line 11611 "parser_bison.c" + break; + + case 530: /* log_arg: "prefix" string */ +#line 3230 "parser_bison.y" + { + struct scope *scope = current_scope(state); + bool done = false, another_var = false; + char *start, *end, scratch = '\0'; + struct expr *expr, *item; + struct symbol *sym; + enum { + PARSE_TEXT, + PARSE_VAR, + } prefix_state; + + /* No variables in log prefix, skip. */ + if (!strchr((yyvsp[0].string), '$')) { + expr = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen((yyvsp[0].string)) + 1) * BITS_PER_BYTE, (yyvsp[0].string)); + xfree((yyvsp[0].string)); + (yyvsp[-2].stmt)->log.prefix = expr; + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_PREFIX; + break; + } + + /* Parse variables in log prefix string using a + * state machine parser with two states. This + * parser creates list of expressions composed + * of constant and variable expressions. + */ + expr = compound_expr_alloc(&(yyloc), EXPR_LIST); + + start = (char *)(yyvsp[0].string); + + if (*start != '$') { + prefix_state = PARSE_TEXT; + } else { + prefix_state = PARSE_VAR; + start++; + } + end = start; + + /* Not nice, but works. */ + while (!done) { + switch (prefix_state) { + case PARSE_TEXT: + while (*end != '\0' && *end != '$') + end++; + + if (*end == '\0') + done = true; + + *end = '\0'; + item = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen(start) + 1) * BITS_PER_BYTE, + start); + compound_expr_add(expr, item); + + if (done) + break; + + start = end + 1; + end = start; + + /* fall through */ + case PARSE_VAR: + while (isalnum(*end) || *end == '_') + end++; + + if (*end == '\0') + done = true; + else if (*end == '$') + another_var = true; + else + scratch = *end; + + *end = '\0'; + + sym = symbol_get(scope, start); + if (!sym) { + sym = symbol_lookup_fuzzy(scope, start); + if (sym) { + erec_queue(error(&(yylsp[0]), "unknown identifier '%s'; " + "did you mean identifier ‘%s’?", + start, sym->identifier), + state->msgs); + } else { + erec_queue(error(&(yylsp[0]), "unknown identifier '%s'", + start), + state->msgs); + } + expr_free(expr); + xfree((yyvsp[0].string)); + YYERROR; + } + item = variable_expr_alloc(&(yyloc), scope, sym); + compound_expr_add(expr, item); + + if (done) + break; + + /* Restore original byte after + * symbol lookup. + */ + if (scratch) { + *end = scratch; + scratch = '\0'; + } + + start = end; + if (another_var) { + another_var = false; + start++; + prefix_state = PARSE_VAR; + } else { + prefix_state = PARSE_TEXT; + } + end = start; + break; + } + } + + xfree((yyvsp[0].string)); + (yyvsp[-2].stmt)->log.prefix = expr; + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_PREFIX; + } +#line 11740 "parser_bison.c" + break; + + case 531: /* log_arg: "group" "number" */ +#line 3355 "parser_bison.y" + { + (yyvsp[-2].stmt)->log.group = (yyvsp[0].val); + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_GROUP; + } +#line 11749 "parser_bison.c" + break; + + case 532: /* log_arg: "snaplen" "number" */ +#line 3360 "parser_bison.y" + { + (yyvsp[-2].stmt)->log.snaplen = (yyvsp[0].val); + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_SNAPLEN; + } +#line 11758 "parser_bison.c" + break; + + case 533: /* log_arg: "queue-threshold" "number" */ +#line 3365 "parser_bison.y" + { + (yyvsp[-2].stmt)->log.qthreshold = (yyvsp[0].val); + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_QTHRESHOLD; + } +#line 11767 "parser_bison.c" + break; + + case 534: /* log_arg: "level" level_type */ +#line 3370 "parser_bison.y" + { + (yyvsp[-2].stmt)->log.level = (yyvsp[0].val); + (yyvsp[-2].stmt)->log.flags |= STMT_LOG_LEVEL; + } +#line 11776 "parser_bison.c" + break; + + case 535: /* log_arg: "flags" log_flags */ +#line 3375 "parser_bison.y" + { + (yyvsp[-2].stmt)->log.logflags |= (yyvsp[0].val); + } +#line 11784 "parser_bison.c" + break; + + case 536: /* level_type: string */ +#line 3381 "parser_bison.y" + { + if (!strcmp("emerg", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_EMERG; + else if (!strcmp("alert", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_ALERT; + else if (!strcmp("crit", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_CRIT; + else if (!strcmp("err", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_ERR; + else if (!strcmp("warn", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_WARNING; + else if (!strcmp("notice", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_NOTICE; + else if (!strcmp("info", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_INFO; + else if (!strcmp("debug", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_DEBUG; + else if (!strcmp("audit", (yyvsp[0].string))) + (yyval.val) = NFT_LOGLEVEL_AUDIT; + else { + erec_queue(error(&(yylsp[0]), "invalid log level"), + state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + xfree((yyvsp[0].string)); + } +#line 11816 "parser_bison.c" + break; + + case 537: /* log_flags: "tcp" log_flags_tcp close_scope_tcp */ +#line 3411 "parser_bison.y" + { + (yyval.val) = (yyvsp[-1].val); + } +#line 11824 "parser_bison.c" + break; + + case 538: /* log_flags: "ip" "options" close_scope_ip */ +#line 3415 "parser_bison.y" + { + (yyval.val) = NF_LOG_IPOPT; + } +#line 11832 "parser_bison.c" + break; + + case 539: /* log_flags: "skuid" */ +#line 3419 "parser_bison.y" + { + (yyval.val) = NF_LOG_UID; + } +#line 11840 "parser_bison.c" + break; + + case 540: /* log_flags: "ether" close_scope_eth */ +#line 3423 "parser_bison.y" + { + (yyval.val) = NF_LOG_MACDECODE; + } +#line 11848 "parser_bison.c" + break; + + case 541: /* log_flags: "all" */ +#line 3427 "parser_bison.y" + { + (yyval.val) = NF_LOG_MASK; + } +#line 11856 "parser_bison.c" + break; + + case 542: /* log_flags_tcp: log_flags_tcp "comma" log_flag_tcp */ +#line 3433 "parser_bison.y" + { + (yyval.val) = (yyvsp[-2].val) | (yyvsp[0].val); + } +#line 11864 "parser_bison.c" + break; + + case 544: /* log_flag_tcp: "seq" */ +#line 3440 "parser_bison.y" + { + (yyval.val) = NF_LOG_TCPSEQ; + } +#line 11872 "parser_bison.c" + break; + + case 545: /* log_flag_tcp: "options" */ +#line 3444 "parser_bison.y" + { + (yyval.val) = NF_LOG_TCPOPT; + } +#line 11880 "parser_bison.c" + break; + + case 546: /* limit_stmt: "limit" "rate" limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit */ +#line 3450 "parser_bison.y" + { + if ((yyvsp[-1].val) == 0) { + erec_queue(error(&(yylsp[-1]), "packet limit burst must be > 0"), + state->msgs); + YYERROR; + } + (yyval.stmt) = limit_stmt_alloc(&(yyloc)); + (yyval.stmt)->limit.rate = (yyvsp[-2].limit_rate).rate; + (yyval.stmt)->limit.unit = (yyvsp[-2].limit_rate).unit; + (yyval.stmt)->limit.burst = (yyvsp[-1].val); + (yyval.stmt)->limit.type = NFT_LIMIT_PKTS; + (yyval.stmt)->limit.flags = (yyvsp[-3].val); + } +#line 11898 "parser_bison.c" + break; + + case 547: /* limit_stmt: "limit" "rate" limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit */ +#line 3464 "parser_bison.y" + { + (yyval.stmt) = limit_stmt_alloc(&(yyloc)); + (yyval.stmt)->limit.rate = (yyvsp[-2].limit_rate).rate; + (yyval.stmt)->limit.unit = (yyvsp[-2].limit_rate).unit; + (yyval.stmt)->limit.burst = (yyvsp[-1].val); + (yyval.stmt)->limit.type = NFT_LIMIT_PKT_BYTES; + (yyval.stmt)->limit.flags = (yyvsp[-3].val); + } +#line 11911 "parser_bison.c" + break; + + case 548: /* limit_stmt: "limit" "name" stmt_expr close_scope_limit */ +#line 3473 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_LIMIT; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + } +#line 11921 "parser_bison.c" + break; + + case 549: /* quota_mode: "over" */ +#line 3480 "parser_bison.y" + { (yyval.val) = NFT_QUOTA_F_INV; } +#line 11927 "parser_bison.c" + break; + + case 550: /* quota_mode: "until" */ +#line 3481 "parser_bison.y" + { (yyval.val) = 0; } +#line 11933 "parser_bison.c" + break; + + case 551: /* quota_mode: %empty */ +#line 3482 "parser_bison.y" + { (yyval.val) = 0; } +#line 11939 "parser_bison.c" + break; + + case 552: /* quota_unit: "bytes" */ +#line 3485 "parser_bison.y" + { (yyval.string) = xstrdup("bytes"); } +#line 11945 "parser_bison.c" + break; + + case 553: /* quota_unit: "string" */ +#line 3486 "parser_bison.y" + { (yyval.string) = (yyvsp[0].string); } +#line 11951 "parser_bison.c" + break; + + case 554: /* quota_used: %empty */ +#line 3489 "parser_bison.y" + { (yyval.val) = 0; } +#line 11957 "parser_bison.c" + break; + + case 555: /* quota_used: "used" "number" quota_unit */ +#line 3491 "parser_bison.y" + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&(yyloc), (yyvsp[0].string), &rate); + xfree((yyvsp[0].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.val) = (yyvsp[-1].val) * rate; + } +#line 11974 "parser_bison.c" + break; + + case 556: /* quota_stmt: "quota" quota_mode "number" quota_unit quota_used close_scope_quota */ +#line 3506 "parser_bison.y" + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&(yyloc), (yyvsp[-2].string), &rate); + xfree((yyvsp[-2].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.stmt) = quota_stmt_alloc(&(yyloc)); + (yyval.stmt)->quota.bytes = (yyvsp[-3].val) * rate; + (yyval.stmt)->quota.used = (yyvsp[-1].val); + (yyval.stmt)->quota.flags = (yyvsp[-4].val); + } +#line 11994 "parser_bison.c" + break; + + case 557: /* quota_stmt: "quota" "name" stmt_expr close_scope_quota */ +#line 3522 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_QUOTA; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + } +#line 12004 "parser_bison.c" + break; + + case 558: /* limit_mode: "over" */ +#line 3529 "parser_bison.y" + { (yyval.val) = NFT_LIMIT_F_INV; } +#line 12010 "parser_bison.c" + break; + + case 559: /* limit_mode: "until" */ +#line 3530 "parser_bison.y" + { (yyval.val) = 0; } +#line 12016 "parser_bison.c" + break; + + case 560: /* limit_mode: %empty */ +#line 3531 "parser_bison.y" + { (yyval.val) = 0; } +#line 12022 "parser_bison.c" + break; + + case 561: /* limit_burst_pkts: %empty */ +#line 3534 "parser_bison.y" + { (yyval.val) = 5; } +#line 12028 "parser_bison.c" + break; + + case 562: /* limit_burst_pkts: "burst" "number" "packets" */ +#line 3535 "parser_bison.y" + { (yyval.val) = (yyvsp[-1].val); } +#line 12034 "parser_bison.c" + break; + + case 563: /* limit_rate_pkts: "number" "/" time_unit */ +#line 3539 "parser_bison.y" + { + (yyval.limit_rate).rate = (yyvsp[-2].val); + (yyval.limit_rate).unit = (yyvsp[0].val); + } +#line 12043 "parser_bison.c" + break; + + case 564: /* limit_burst_bytes: %empty */ +#line 3545 "parser_bison.y" + { (yyval.val) = 0; } +#line 12049 "parser_bison.c" + break; + + case 565: /* limit_burst_bytes: "burst" limit_bytes */ +#line 3546 "parser_bison.y" + { (yyval.val) = (yyvsp[0].val); } +#line 12055 "parser_bison.c" + break; + + case 566: /* limit_rate_bytes: "number" "string" */ +#line 3550 "parser_bison.y" + { + struct error_record *erec; + uint64_t rate, unit; + + erec = rate_parse(&(yyloc), (yyvsp[0].string), &rate, &unit); + xfree((yyvsp[0].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.limit_rate).rate = rate * (yyvsp[-1].val); + (yyval.limit_rate).unit = unit; + } +#line 12073 "parser_bison.c" + break; + + case 567: /* limit_rate_bytes: limit_bytes "/" time_unit */ +#line 3564 "parser_bison.y" + { + (yyval.limit_rate).rate = (yyvsp[-2].val); + (yyval.limit_rate).unit = (yyvsp[0].val); + } +#line 12082 "parser_bison.c" + break; + + case 568: /* limit_bytes: "number" "bytes" */ +#line 3570 "parser_bison.y" + { (yyval.val) = (yyvsp[-1].val); } +#line 12088 "parser_bison.c" + break; + + case 569: /* limit_bytes: "number" "string" */ +#line 3572 "parser_bison.y" + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&(yyloc), (yyvsp[0].string), &rate); + xfree((yyvsp[0].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.val) = (yyvsp[-1].val) * rate; + } +#line 12105 "parser_bison.c" + break; + + case 570: /* time_unit: "second" */ +#line 3586 "parser_bison.y" + { (yyval.val) = 1ULL; } +#line 12111 "parser_bison.c" + break; + + case 571: /* time_unit: "minute" */ +#line 3587 "parser_bison.y" + { (yyval.val) = 1ULL * 60; } +#line 12117 "parser_bison.c" + break; + + case 572: /* time_unit: "hour" */ +#line 3588 "parser_bison.y" + { (yyval.val) = 1ULL * 60 * 60; } +#line 12123 "parser_bison.c" + break; + + case 573: /* time_unit: "day" */ +#line 3589 "parser_bison.y" + { (yyval.val) = 1ULL * 60 * 60 * 24; } +#line 12129 "parser_bison.c" + break; + + case 574: /* time_unit: "week" */ +#line 3590 "parser_bison.y" + { (yyval.val) = 1ULL * 60 * 60 * 24 * 7; } +#line 12135 "parser_bison.c" + break; + + case 576: /* reject_stmt_alloc: "reject" */ +#line 3597 "parser_bison.y" + { + (yyval.stmt) = reject_stmt_alloc(&(yyloc)); + } +#line 12143 "parser_bison.c" + break; + + case 577: /* reject_with_expr: "string" */ +#line 3603 "parser_bison.y" + { + (yyval.expr) = symbol_expr_alloc(&(yyloc), SYMBOL_VALUE, + current_scope(state), (yyvsp[0].string)); + xfree((yyvsp[0].string)); + } +#line 12153 "parser_bison.c" + break; + + case 578: /* reject_with_expr: integer_expr */ +#line 3608 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12159 "parser_bison.c" + break; + + case 579: /* reject_opts: %empty */ +#line 3612 "parser_bison.y" + { + (yyvsp[0].stmt)->reject.type = -1; + (yyvsp[0].stmt)->reject.icmp_code = -1; + } +#line 12168 "parser_bison.c" + break; + + case 580: /* reject_opts: "with" "icmp" "type" reject_with_expr close_scope_type close_scope_icmp */ +#line 3617 "parser_bison.y" + { + (yyvsp[-6].stmt)->reject.family = NFPROTO_IPV4; + (yyvsp[-6].stmt)->reject.type = NFT_REJECT_ICMP_UNREACH; + (yyvsp[-6].stmt)->reject.expr = (yyvsp[-2].expr); + datatype_set((yyvsp[-6].stmt)->reject.expr, &icmp_code_type); + } +#line 12179 "parser_bison.c" + break; + + case 581: /* reject_opts: "with" "icmp" reject_with_expr */ +#line 3624 "parser_bison.y" + { + (yyvsp[-3].stmt)->reject.family = NFPROTO_IPV4; + (yyvsp[-3].stmt)->reject.type = NFT_REJECT_ICMP_UNREACH; + (yyvsp[-3].stmt)->reject.expr = (yyvsp[0].expr); + datatype_set((yyvsp[-3].stmt)->reject.expr, &icmp_code_type); + } +#line 12190 "parser_bison.c" + break; + + case 582: /* reject_opts: "with" "icmpv6" "type" reject_with_expr close_scope_type close_scope_icmp */ +#line 3631 "parser_bison.y" + { + (yyvsp[-6].stmt)->reject.family = NFPROTO_IPV6; + (yyvsp[-6].stmt)->reject.type = NFT_REJECT_ICMP_UNREACH; + (yyvsp[-6].stmt)->reject.expr = (yyvsp[-2].expr); + datatype_set((yyvsp[-6].stmt)->reject.expr, &icmpv6_code_type); + } +#line 12201 "parser_bison.c" + break; + + case 583: /* reject_opts: "with" "icmpv6" reject_with_expr */ +#line 3638 "parser_bison.y" + { + (yyvsp[-3].stmt)->reject.family = NFPROTO_IPV6; + (yyvsp[-3].stmt)->reject.type = NFT_REJECT_ICMP_UNREACH; + (yyvsp[-3].stmt)->reject.expr = (yyvsp[0].expr); + datatype_set((yyvsp[-3].stmt)->reject.expr, &icmpv6_code_type); + } +#line 12212 "parser_bison.c" + break; + + case 584: /* reject_opts: "with" "icmpx" "type" reject_with_expr close_scope_type */ +#line 3645 "parser_bison.y" + { + (yyvsp[-5].stmt)->reject.type = NFT_REJECT_ICMPX_UNREACH; + (yyvsp[-5].stmt)->reject.expr = (yyvsp[-1].expr); + datatype_set((yyvsp[-5].stmt)->reject.expr, &icmpx_code_type); + } +#line 12222 "parser_bison.c" + break; + + case 585: /* reject_opts: "with" "icmpx" reject_with_expr */ +#line 3651 "parser_bison.y" + { + (yyvsp[-3].stmt)->reject.type = NFT_REJECT_ICMPX_UNREACH; + (yyvsp[-3].stmt)->reject.expr = (yyvsp[0].expr); + datatype_set((yyvsp[-3].stmt)->reject.expr, &icmpx_code_type); + } +#line 12232 "parser_bison.c" + break; + + case 586: /* reject_opts: "with" "tcp" close_scope_tcp "reset" close_scope_reset */ +#line 3657 "parser_bison.y" + { + (yyvsp[-5].stmt)->reject.type = NFT_REJECT_TCP_RST; + } +#line 12240 "parser_bison.c" + break; + + case 588: /* nat_stmt_alloc: "snat" */ +#line 3665 "parser_bison.y" + { (yyval.stmt) = nat_stmt_alloc(&(yyloc), __NFT_NAT_SNAT); } +#line 12246 "parser_bison.c" + break; + + case 589: /* nat_stmt_alloc: "dnat" */ +#line 3666 "parser_bison.y" + { (yyval.stmt) = nat_stmt_alloc(&(yyloc), __NFT_NAT_DNAT); } +#line 12252 "parser_bison.c" + break; + + case 590: /* tproxy_stmt: "tproxy" "to" stmt_expr */ +#line 3670 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = NFPROTO_UNSPEC; + (yyval.stmt)->tproxy.addr = (yyvsp[0].expr); + } +#line 12262 "parser_bison.c" + break; + + case 591: /* tproxy_stmt: "tproxy" nf_key_proto "to" stmt_expr */ +#line 3676 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = (yyvsp[-2].val); + (yyval.stmt)->tproxy.addr = (yyvsp[0].expr); + } +#line 12272 "parser_bison.c" + break; + + case 592: /* tproxy_stmt: "tproxy" "to" "colon" stmt_expr */ +#line 3682 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = NFPROTO_UNSPEC; + (yyval.stmt)->tproxy.port = (yyvsp[0].expr); + } +#line 12282 "parser_bison.c" + break; + + case 593: /* tproxy_stmt: "tproxy" "to" stmt_expr "colon" stmt_expr */ +#line 3688 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = NFPROTO_UNSPEC; + (yyval.stmt)->tproxy.addr = (yyvsp[-2].expr); + (yyval.stmt)->tproxy.port = (yyvsp[0].expr); + } +#line 12293 "parser_bison.c" + break; + + case 594: /* tproxy_stmt: "tproxy" nf_key_proto "to" stmt_expr "colon" stmt_expr */ +#line 3695 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = (yyvsp[-4].val); + (yyval.stmt)->tproxy.addr = (yyvsp[-2].expr); + (yyval.stmt)->tproxy.port = (yyvsp[0].expr); + } +#line 12304 "parser_bison.c" + break; + + case 595: /* tproxy_stmt: "tproxy" nf_key_proto "to" "colon" stmt_expr */ +#line 3702 "parser_bison.y" + { + (yyval.stmt) = tproxy_stmt_alloc(&(yyloc)); + (yyval.stmt)->tproxy.family = (yyvsp[-3].val); + (yyval.stmt)->tproxy.port = (yyvsp[0].expr); + } +#line 12314 "parser_bison.c" + break; + + case 598: /* synproxy_stmt_alloc: "synproxy" */ +#line 3714 "parser_bison.y" + { + (yyval.stmt) = synproxy_stmt_alloc(&(yyloc)); + } +#line 12322 "parser_bison.c" + break; + + case 599: /* synproxy_stmt_alloc: "synproxy" "name" stmt_expr */ +#line 3718 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_SYNPROXY; + (yyval.stmt)->objref.expr = (yyvsp[0].expr); + } +#line 12332 "parser_bison.c" + break; + + case 600: /* synproxy_args: synproxy_arg */ +#line 3726 "parser_bison.y" + { + (yyval.stmt) = (yyvsp[-1].stmt); + } +#line 12340 "parser_bison.c" + break; + + case 602: /* synproxy_arg: "mss" "number" */ +#line 3733 "parser_bison.y" + { + (yyvsp[-2].stmt)->synproxy.mss = (yyvsp[0].val); + (yyvsp[-2].stmt)->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + } +#line 12349 "parser_bison.c" + break; + + case 603: /* synproxy_arg: "wscale" "number" */ +#line 3738 "parser_bison.y" + { + (yyvsp[-2].stmt)->synproxy.wscale = (yyvsp[0].val); + (yyvsp[-2].stmt)->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } +#line 12358 "parser_bison.c" + break; + + case 604: /* synproxy_arg: "timestamp" */ +#line 3743 "parser_bison.y" + { + (yyvsp[-1].stmt)->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP; + } +#line 12366 "parser_bison.c" + break; + + case 605: /* synproxy_arg: "sack-permitted" */ +#line 3747 "parser_bison.y" + { + (yyvsp[-1].stmt)->synproxy.flags |= NF_SYNPROXY_OPT_SACK_PERM; + } +#line 12374 "parser_bison.c" + break; + + case 606: /* synproxy_config: "mss" "number" "wscale" "number" synproxy_ts synproxy_sack */ +#line 3753 "parser_bison.y" + { + struct synproxy *synproxy; + uint32_t flags = 0; + + synproxy = &(yyvsp[-6].obj)->synproxy; + synproxy->mss = (yyvsp[-4].val); + flags |= NF_SYNPROXY_OPT_MSS; + synproxy->wscale = (yyvsp[-2].val); + flags |= NF_SYNPROXY_OPT_WSCALE; + if ((yyvsp[-1].val)) + flags |= (yyvsp[-1].val); + if ((yyvsp[0].val)) + flags |= (yyvsp[0].val); + synproxy->flags = flags; + } +#line 12394 "parser_bison.c" + break; + + case 607: /* synproxy_config: "mss" "number" stmt_separator "wscale" "number" stmt_separator synproxy_ts synproxy_sack */ +#line 3769 "parser_bison.y" + { + struct synproxy *synproxy; + uint32_t flags = 0; + + synproxy = &(yyvsp[-8].obj)->synproxy; + synproxy->mss = (yyvsp[-6].val); + flags |= NF_SYNPROXY_OPT_MSS; + synproxy->wscale = (yyvsp[-3].val); + flags |= NF_SYNPROXY_OPT_WSCALE; + if ((yyvsp[-1].val)) + flags |= (yyvsp[-1].val); + if ((yyvsp[0].val)) + flags |= (yyvsp[0].val); + synproxy->flags = flags; + } +#line 12414 "parser_bison.c" + break; + + case 608: /* synproxy_obj: %empty */ +#line 3787 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + (yyval.obj)->type = NFT_OBJECT_SYNPROXY; + } +#line 12423 "parser_bison.c" + break; + + case 609: /* synproxy_ts: %empty */ +#line 3793 "parser_bison.y" + { (yyval.val) = 0; } +#line 12429 "parser_bison.c" + break; + + case 610: /* synproxy_ts: "timestamp" */ +#line 3795 "parser_bison.y" + { + (yyval.val) = NF_SYNPROXY_OPT_TIMESTAMP; + } +#line 12437 "parser_bison.c" + break; + + case 611: /* synproxy_sack: %empty */ +#line 3800 "parser_bison.y" + { (yyval.val) = 0; } +#line 12443 "parser_bison.c" + break; + + case 612: /* synproxy_sack: "sack-permitted" */ +#line 3802 "parser_bison.y" + { + (yyval.val) = NF_SYNPROXY_OPT_SACK_PERM; + } +#line 12451 "parser_bison.c" + break; + + case 613: /* primary_stmt_expr: symbol_expr */ +#line 3807 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12457 "parser_bison.c" + break; + + case 614: /* primary_stmt_expr: integer_expr */ +#line 3808 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12463 "parser_bison.c" + break; + + case 615: /* primary_stmt_expr: boolean_expr */ +#line 3809 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12469 "parser_bison.c" + break; + + case 616: /* primary_stmt_expr: meta_expr */ +#line 3810 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12475 "parser_bison.c" + break; + + case 617: /* primary_stmt_expr: rt_expr */ +#line 3811 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12481 "parser_bison.c" + break; + + case 618: /* primary_stmt_expr: ct_expr */ +#line 3812 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12487 "parser_bison.c" + break; + + case 619: /* primary_stmt_expr: numgen_expr */ +#line 3813 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12493 "parser_bison.c" + break; + + case 620: /* primary_stmt_expr: hash_expr */ +#line 3814 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12499 "parser_bison.c" + break; + + case 621: /* primary_stmt_expr: payload_expr */ +#line 3815 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12505 "parser_bison.c" + break; + + case 622: /* primary_stmt_expr: keyword_expr */ +#line 3816 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12511 "parser_bison.c" + break; + + case 623: /* primary_stmt_expr: socket_expr */ +#line 3817 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12517 "parser_bison.c" + break; + + case 624: /* primary_stmt_expr: osf_expr */ +#line 3818 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12523 "parser_bison.c" + break; + + case 625: /* primary_stmt_expr: '(' basic_stmt_expr ')' */ +#line 3819 "parser_bison.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 12529 "parser_bison.c" + break; + + case 627: /* shift_stmt_expr: shift_stmt_expr "<<" primary_stmt_expr */ +#line 3824 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_LSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12537 "parser_bison.c" + break; + + case 628: /* shift_stmt_expr: shift_stmt_expr ">>" primary_stmt_expr */ +#line 3828 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_RSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12545 "parser_bison.c" + break; + + case 630: /* and_stmt_expr: and_stmt_expr "&" shift_stmt_expr */ +#line 3835 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12553 "parser_bison.c" + break; + + case 632: /* exclusive_or_stmt_expr: exclusive_or_stmt_expr "^" and_stmt_expr */ +#line 3842 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_XOR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12561 "parser_bison.c" + break; + + case 634: /* inclusive_or_stmt_expr: inclusive_or_stmt_expr '|' exclusive_or_stmt_expr */ +#line 3849 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12569 "parser_bison.c" + break; + + case 637: /* concat_stmt_expr: concat_stmt_expr "." primary_stmt_expr */ +#line 3859 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 12582 "parser_bison.c" + break; + + case 640: /* map_stmt_expr: concat_stmt_expr "map" map_stmt_expr_set */ +#line 3874 "parser_bison.y" + { + (yyval.expr) = map_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12590 "parser_bison.c" + break; + + case 641: /* map_stmt_expr: concat_stmt_expr */ +#line 3877 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 12596 "parser_bison.c" + break; + + case 642: /* prefix_stmt_expr: basic_stmt_expr "/" "number" */ +#line 3881 "parser_bison.y" + { + (yyval.expr) = prefix_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].val)); + } +#line 12604 "parser_bison.c" + break; + + case 643: /* range_stmt_expr: basic_stmt_expr "-" basic_stmt_expr */ +#line 3887 "parser_bison.y" + { + (yyval.expr) = range_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12612 "parser_bison.c" + break; + + case 649: /* nat_stmt_args: stmt_expr */ +#line 3902 "parser_bison.y" + { + (yyvsp[-1].stmt)->nat.addr = (yyvsp[0].expr); + } +#line 12620 "parser_bison.c" + break; + + case 650: /* nat_stmt_args: "to" stmt_expr */ +#line 3906 "parser_bison.y" + { + (yyvsp[-2].stmt)->nat.addr = (yyvsp[0].expr); + } +#line 12628 "parser_bison.c" + break; + + case 651: /* nat_stmt_args: nf_key_proto "to" stmt_expr */ +#line 3910 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.family = (yyvsp[-2].val); + (yyvsp[-3].stmt)->nat.addr = (yyvsp[0].expr); + } +#line 12637 "parser_bison.c" + break; + + case 652: /* nat_stmt_args: stmt_expr "colon" stmt_expr */ +#line 3915 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.addr = (yyvsp[-2].expr); + (yyvsp[-3].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12646 "parser_bison.c" + break; + + case 653: /* nat_stmt_args: "to" stmt_expr "colon" stmt_expr */ +#line 3920 "parser_bison.y" + { + (yyvsp[-4].stmt)->nat.addr = (yyvsp[-2].expr); + (yyvsp[-4].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12655 "parser_bison.c" + break; + + case 654: /* nat_stmt_args: nf_key_proto "to" stmt_expr "colon" stmt_expr */ +#line 3925 "parser_bison.y" + { + (yyvsp[-5].stmt)->nat.family = (yyvsp[-4].val); + (yyvsp[-5].stmt)->nat.addr = (yyvsp[-2].expr); + (yyvsp[-5].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12665 "parser_bison.c" + break; + + case 655: /* nat_stmt_args: "colon" stmt_expr */ +#line 3931 "parser_bison.y" + { + (yyvsp[-2].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12673 "parser_bison.c" + break; + + case 656: /* nat_stmt_args: "to" "colon" stmt_expr */ +#line 3935 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12681 "parser_bison.c" + break; + + case 657: /* nat_stmt_args: nat_stmt_args nf_nat_flags */ +#line 3939 "parser_bison.y" + { + (yyvsp[-2].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12689 "parser_bison.c" + break; + + case 658: /* nat_stmt_args: nf_key_proto "addr" "." "port" "to" stmt_expr */ +#line 3943 "parser_bison.y" + { + (yyvsp[-6].stmt)->nat.family = (yyvsp[-5].val); + (yyvsp[-6].stmt)->nat.addr = (yyvsp[0].expr); + (yyvsp[-6].stmt)->nat.type_flags = STMT_NAT_F_CONCAT; + } +#line 12699 "parser_bison.c" + break; + + case 659: /* nat_stmt_args: nf_key_proto "interval" "to" stmt_expr */ +#line 3949 "parser_bison.y" + { + (yyvsp[-4].stmt)->nat.family = (yyvsp[-3].val); + (yyvsp[-4].stmt)->nat.addr = (yyvsp[0].expr); + } +#line 12708 "parser_bison.c" + break; + + case 660: /* nat_stmt_args: "interval" "to" stmt_expr */ +#line 3954 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.addr = (yyvsp[0].expr); + } +#line 12716 "parser_bison.c" + break; + + case 661: /* nat_stmt_args: nf_key_proto "prefix" "to" stmt_expr */ +#line 3958 "parser_bison.y" + { + (yyvsp[-4].stmt)->nat.family = (yyvsp[-3].val); + (yyvsp[-4].stmt)->nat.addr = (yyvsp[0].expr); + (yyvsp[-4].stmt)->nat.type_flags = + STMT_NAT_F_PREFIX; + (yyvsp[-4].stmt)->nat.flags |= NF_NAT_RANGE_NETMAP; + } +#line 12728 "parser_bison.c" + break; + + case 662: /* nat_stmt_args: "prefix" "to" stmt_expr */ +#line 3966 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.addr = (yyvsp[0].expr); + (yyvsp[-3].stmt)->nat.type_flags = + STMT_NAT_F_PREFIX; + (yyvsp[-3].stmt)->nat.flags |= NF_NAT_RANGE_NETMAP; + } +#line 12739 "parser_bison.c" + break; + + case 665: /* masq_stmt_alloc: "masquerade" */ +#line 3978 "parser_bison.y" + { (yyval.stmt) = nat_stmt_alloc(&(yyloc), NFT_NAT_MASQ); } +#line 12745 "parser_bison.c" + break; + + case 666: /* masq_stmt_args: "to" "colon" stmt_expr */ +#line 3982 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12753 "parser_bison.c" + break; + + case 667: /* masq_stmt_args: "to" "colon" stmt_expr nf_nat_flags */ +#line 3986 "parser_bison.y" + { + (yyvsp[-4].stmt)->nat.proto = (yyvsp[-1].expr); + (yyvsp[-4].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12762 "parser_bison.c" + break; + + case 668: /* masq_stmt_args: nf_nat_flags */ +#line 3991 "parser_bison.y" + { + (yyvsp[-1].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12770 "parser_bison.c" + break; + + case 671: /* redir_stmt_alloc: "redirect" */ +#line 4000 "parser_bison.y" + { (yyval.stmt) = nat_stmt_alloc(&(yyloc), NFT_NAT_REDIR); } +#line 12776 "parser_bison.c" + break; + + case 672: /* redir_stmt_arg: "to" stmt_expr */ +#line 4004 "parser_bison.y" + { + (yyvsp[-2].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12784 "parser_bison.c" + break; + + case 673: /* redir_stmt_arg: "to" "colon" stmt_expr */ +#line 4008 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.proto = (yyvsp[0].expr); + } +#line 12792 "parser_bison.c" + break; + + case 674: /* redir_stmt_arg: nf_nat_flags */ +#line 4012 "parser_bison.y" + { + (yyvsp[-1].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12800 "parser_bison.c" + break; + + case 675: /* redir_stmt_arg: "to" stmt_expr nf_nat_flags */ +#line 4016 "parser_bison.y" + { + (yyvsp[-3].stmt)->nat.proto = (yyvsp[-1].expr); + (yyvsp[-3].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12809 "parser_bison.c" + break; + + case 676: /* redir_stmt_arg: "to" "colon" stmt_expr nf_nat_flags */ +#line 4021 "parser_bison.y" + { + (yyvsp[-4].stmt)->nat.proto = (yyvsp[-1].expr); + (yyvsp[-4].stmt)->nat.flags = (yyvsp[0].val); + } +#line 12818 "parser_bison.c" + break; + + case 677: /* dup_stmt: "dup" "to" stmt_expr */ +#line 4028 "parser_bison.y" + { + (yyval.stmt) = dup_stmt_alloc(&(yyloc)); + (yyval.stmt)->dup.to = (yyvsp[0].expr); + } +#line 12827 "parser_bison.c" + break; + + case 678: /* dup_stmt: "dup" "to" stmt_expr "device" stmt_expr */ +#line 4033 "parser_bison.y" + { + (yyval.stmt) = dup_stmt_alloc(&(yyloc)); + (yyval.stmt)->dup.to = (yyvsp[-2].expr); + (yyval.stmt)->dup.dev = (yyvsp[0].expr); + } +#line 12837 "parser_bison.c" + break; + + case 679: /* fwd_stmt: "fwd" "to" stmt_expr */ +#line 4041 "parser_bison.y" + { + (yyval.stmt) = fwd_stmt_alloc(&(yyloc)); + (yyval.stmt)->fwd.dev = (yyvsp[0].expr); + } +#line 12846 "parser_bison.c" + break; + + case 680: /* fwd_stmt: "fwd" nf_key_proto "to" stmt_expr "device" stmt_expr */ +#line 4046 "parser_bison.y" + { + (yyval.stmt) = fwd_stmt_alloc(&(yyloc)); + (yyval.stmt)->fwd.family = (yyvsp[-4].val); + (yyval.stmt)->fwd.addr = (yyvsp[-2].expr); + (yyval.stmt)->fwd.dev = (yyvsp[0].expr); + } +#line 12857 "parser_bison.c" + break; + + case 682: /* nf_nat_flags: nf_nat_flags "comma" nf_nat_flag */ +#line 4056 "parser_bison.y" + { + (yyval.val) = (yyvsp[-2].val) | (yyvsp[0].val); + } +#line 12865 "parser_bison.c" + break; + + case 683: /* nf_nat_flag: "random" */ +#line 4061 "parser_bison.y" + { (yyval.val) = NF_NAT_RANGE_PROTO_RANDOM; } +#line 12871 "parser_bison.c" + break; + + case 684: /* nf_nat_flag: "fully-random" */ +#line 4062 "parser_bison.y" + { (yyval.val) = NF_NAT_RANGE_PROTO_RANDOM_FULLY; } +#line 12877 "parser_bison.c" + break; + + case 685: /* nf_nat_flag: "persistent" */ +#line 4063 "parser_bison.y" + { (yyval.val) = NF_NAT_RANGE_PERSISTENT; } +#line 12883 "parser_bison.c" + break; + + case 687: /* queue_stmt: "queue" "to" queue_stmt_expr close_scope_queue */ +#line 4068 "parser_bison.y" + { + (yyval.stmt) = queue_stmt_alloc(&(yyloc), (yyvsp[-1].expr), 0); + } +#line 12891 "parser_bison.c" + break; + + case 688: /* queue_stmt: "queue" "flags" queue_stmt_flags "to" queue_stmt_expr close_scope_queue */ +#line 4072 "parser_bison.y" + { + (yyval.stmt) = queue_stmt_alloc(&(yyloc), (yyvsp[-1].expr), (yyvsp[-3].val)); + } +#line 12899 "parser_bison.c" + break; + + case 689: /* queue_stmt: "queue" "flags" queue_stmt_flags "num" queue_stmt_expr_simple close_scope_queue */ +#line 4076 "parser_bison.y" + { + (yyval.stmt) = queue_stmt_alloc(&(yyloc), (yyvsp[-1].expr), (yyvsp[-3].val)); + } +#line 12907 "parser_bison.c" + break; + + case 692: /* queue_stmt_alloc: "queue" */ +#line 4086 "parser_bison.y" + { + (yyval.stmt) = queue_stmt_alloc(&(yyloc), NULL, 0); + } +#line 12915 "parser_bison.c" + break; + + case 693: /* queue_stmt_args: queue_stmt_arg */ +#line 4092 "parser_bison.y" + { + (yyval.stmt) = (yyvsp[-1].stmt); + } +#line 12923 "parser_bison.c" + break; + + case 695: /* queue_stmt_arg: "num" queue_stmt_expr_simple */ +#line 4099 "parser_bison.y" + { + (yyvsp[-2].stmt)->queue.queue = (yyvsp[0].expr); + (yyvsp[-2].stmt)->queue.queue->location = (yyloc); + } +#line 12932 "parser_bison.c" + break; + + case 696: /* queue_stmt_arg: queue_stmt_flags */ +#line 4104 "parser_bison.y" + { + (yyvsp[-1].stmt)->queue.flags |= (yyvsp[0].val); + } +#line 12940 "parser_bison.c" + break; + + case 701: /* queue_stmt_expr_simple: queue_expr "-" queue_expr */ +#line 4116 "parser_bison.y" + { + (yyval.expr) = range_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 12948 "parser_bison.c" + break; + + case 707: /* queue_stmt_flags: queue_stmt_flags "comma" queue_stmt_flag */ +#line 4129 "parser_bison.y" + { + (yyval.val) = (yyvsp[-2].val) | (yyvsp[0].val); + } +#line 12956 "parser_bison.c" + break; + + case 708: /* queue_stmt_flag: "bypass" */ +#line 4134 "parser_bison.y" + { (yyval.val) = NFT_QUEUE_FLAG_BYPASS; } +#line 12962 "parser_bison.c" + break; + + case 709: /* queue_stmt_flag: "fanout" */ +#line 4135 "parser_bison.y" + { (yyval.val) = NFT_QUEUE_FLAG_CPU_FANOUT; } +#line 12968 "parser_bison.c" + break; + + case 712: /* set_elem_expr_stmt_alloc: concat_expr */ +#line 4143 "parser_bison.y" + { + (yyval.expr) = set_elem_expr_alloc(&(yylsp[0]), (yyvsp[0].expr)); + } +#line 12976 "parser_bison.c" + break; + + case 713: /* set_stmt: "set" set_stmt_op set_elem_expr_stmt set_ref_expr */ +#line 4149 "parser_bison.y" + { + (yyval.stmt) = set_stmt_alloc(&(yyloc)); + (yyval.stmt)->set.op = (yyvsp[-2].val); + (yyval.stmt)->set.key = (yyvsp[-1].expr); + (yyval.stmt)->set.set = (yyvsp[0].expr); + } +#line 12987 "parser_bison.c" + break; + + case 714: /* set_stmt: set_stmt_op set_ref_expr '{' set_elem_expr_stmt '}' */ +#line 4156 "parser_bison.y" + { + (yyval.stmt) = set_stmt_alloc(&(yyloc)); + (yyval.stmt)->set.op = (yyvsp[-4].val); + (yyval.stmt)->set.key = (yyvsp[-1].expr); + (yyval.stmt)->set.set = (yyvsp[-3].expr); + } +#line 12998 "parser_bison.c" + break; + + case 715: /* set_stmt: set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list '}' */ +#line 4163 "parser_bison.y" + { + (yyval.stmt) = set_stmt_alloc(&(yyloc)); + (yyval.stmt)->set.op = (yyvsp[-5].val); + (yyval.stmt)->set.key = (yyvsp[-2].expr); + (yyval.stmt)->set.set = (yyvsp[-4].expr); + list_splice_tail((yyvsp[-1].list), &(yyval.stmt)->set.stmt_list); + free((yyvsp[-1].list)); + } +#line 13011 "parser_bison.c" + break; + + case 716: /* set_stmt_op: "add" */ +#line 4173 "parser_bison.y" + { (yyval.val) = NFT_DYNSET_OP_ADD; } +#line 13017 "parser_bison.c" + break; + + case 717: /* set_stmt_op: "update" */ +#line 4174 "parser_bison.y" + { (yyval.val) = NFT_DYNSET_OP_UPDATE; } +#line 13023 "parser_bison.c" + break; + + case 718: /* set_stmt_op: "delete" */ +#line 4175 "parser_bison.y" + { (yyval.val) = NFT_DYNSET_OP_DELETE; } +#line 13029 "parser_bison.c" + break; + + case 719: /* map_stmt: set_stmt_op set_ref_expr '{' set_elem_expr_stmt "colon" set_elem_expr_stmt '}' */ +#line 4179 "parser_bison.y" + { + (yyval.stmt) = map_stmt_alloc(&(yyloc)); + (yyval.stmt)->map.op = (yyvsp[-6].val); + (yyval.stmt)->map.key = (yyvsp[-3].expr); + (yyval.stmt)->map.data = (yyvsp[-1].expr); + (yyval.stmt)->map.set = (yyvsp[-5].expr); + } +#line 13041 "parser_bison.c" + break; + + case 720: /* map_stmt: set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list "colon" set_elem_expr_stmt '}' */ +#line 4187 "parser_bison.y" + { + (yyval.stmt) = map_stmt_alloc(&(yyloc)); + (yyval.stmt)->map.op = (yyvsp[-7].val); + (yyval.stmt)->map.key = (yyvsp[-4].expr); + (yyval.stmt)->map.data = (yyvsp[-1].expr); + (yyval.stmt)->map.set = (yyvsp[-6].expr); + list_splice_tail((yyvsp[-3].list), &(yyval.stmt)->map.stmt_list); + free((yyvsp[-3].list)); + } +#line 13055 "parser_bison.c" + break; + + case 721: /* meter_stmt: flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}' */ +#line 4199 "parser_bison.y" + { + (yyvsp[-5].stmt)->meter.key = (yyvsp[-2].expr); + (yyvsp[-5].stmt)->meter.stmt = (yyvsp[-1].stmt); + (yyval.stmt)->location = (yyloc); + (yyval.stmt) = (yyvsp[-5].stmt); + } +#line 13066 "parser_bison.c" + break; + + case 722: /* meter_stmt: meter_stmt_alloc */ +#line 4205 "parser_bison.y" + { (yyval.stmt) = (yyvsp[0].stmt); } +#line 13072 "parser_bison.c" + break; + + case 723: /* flow_stmt_legacy_alloc: "flow" */ +#line 4209 "parser_bison.y" + { + (yyval.stmt) = meter_stmt_alloc(&(yyloc)); + } +#line 13080 "parser_bison.c" + break; + + case 724: /* flow_stmt_opts: flow_stmt_opt */ +#line 4215 "parser_bison.y" + { + (yyval.stmt) = (yyvsp[-1].stmt); + } +#line 13088 "parser_bison.c" + break; + + case 726: /* flow_stmt_opt: "table" identifier */ +#line 4222 "parser_bison.y" + { + (yyvsp[-2].stmt)->meter.name = (yyvsp[0].string); + } +#line 13096 "parser_bison.c" + break; + + case 727: /* meter_stmt_alloc: "meter" identifier '{' meter_key_expr stmt '}' */ +#line 4228 "parser_bison.y" + { + (yyval.stmt) = meter_stmt_alloc(&(yyloc)); + (yyval.stmt)->meter.name = (yyvsp[-4].string); + (yyval.stmt)->meter.size = 0; + (yyval.stmt)->meter.key = (yyvsp[-2].expr); + (yyval.stmt)->meter.stmt = (yyvsp[-1].stmt); + (yyval.stmt)->location = (yyloc); + } +#line 13109 "parser_bison.c" + break; + + case 728: /* meter_stmt_alloc: "meter" identifier "size" "number" '{' meter_key_expr stmt '}' */ +#line 4237 "parser_bison.y" + { + (yyval.stmt) = meter_stmt_alloc(&(yyloc)); + (yyval.stmt)->meter.name = (yyvsp[-6].string); + (yyval.stmt)->meter.size = (yyvsp[-4].val); + (yyval.stmt)->meter.key = (yyvsp[-2].expr); + (yyval.stmt)->meter.stmt = (yyvsp[-1].stmt); + (yyval.stmt)->location = (yyloc); + } +#line 13122 "parser_bison.c" + break; + + case 729: /* match_stmt: relational_expr */ +#line 4248 "parser_bison.y" + { + (yyval.stmt) = expr_stmt_alloc(&(yyloc), (yyvsp[0].expr)); + } +#line 13130 "parser_bison.c" + break; + + case 730: /* variable_expr: '$' identifier */ +#line 4254 "parser_bison.y" + { + struct scope *scope = current_scope(state); + struct symbol *sym; + + sym = symbol_get(scope, (yyvsp[0].string)); + if (!sym) { + sym = symbol_lookup_fuzzy(scope, (yyvsp[0].string)); + if (sym) { + erec_queue(error(&(yylsp[0]), "unknown identifier '%s'; " + "did you mean identifier ‘%s’?", + (yyvsp[0].string), sym->identifier), + state->msgs); + } else { + erec_queue(error(&(yylsp[0]), "unknown identifier '%s'", (yyvsp[0].string)), + state->msgs); + } + xfree((yyvsp[0].string)); + YYERROR; + } + + (yyval.expr) = variable_expr_alloc(&(yyloc), scope, sym); + xfree((yyvsp[0].string)); + } +#line 13158 "parser_bison.c" + break; + + case 732: /* symbol_expr: string */ +#line 4281 "parser_bison.y" + { + (yyval.expr) = symbol_expr_alloc(&(yyloc), SYMBOL_VALUE, + current_scope(state), + (yyvsp[0].string)); + xfree((yyvsp[0].string)); + } +#line 13169 "parser_bison.c" + break; + + case 735: /* set_ref_symbol_expr: "@" identifier close_scope_at */ +#line 4294 "parser_bison.y" + { + (yyval.expr) = symbol_expr_alloc(&(yyloc), SYMBOL_SET, + current_scope(state), + (yyvsp[-1].string)); + xfree((yyvsp[-1].string)); + } +#line 13180 "parser_bison.c" + break; + + case 736: /* integer_expr: "number" */ +#line 4303 "parser_bison.y" + { + char str[64]; + + snprintf(str, sizeof(str), "%" PRIu64, (yyvsp[0].val)); + (yyval.expr) = symbol_expr_alloc(&(yyloc), SYMBOL_VALUE, + current_scope(state), + str); + } +#line 13193 "parser_bison.c" + break; + + case 737: /* primary_expr: symbol_expr */ +#line 4313 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13199 "parser_bison.c" + break; + + case 738: /* primary_expr: integer_expr */ +#line 4314 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13205 "parser_bison.c" + break; + + case 739: /* primary_expr: payload_expr */ +#line 4315 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13211 "parser_bison.c" + break; + + case 740: /* primary_expr: exthdr_expr */ +#line 4316 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13217 "parser_bison.c" + break; + + case 741: /* primary_expr: exthdr_exists_expr */ +#line 4317 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13223 "parser_bison.c" + break; + + case 742: /* primary_expr: meta_expr */ +#line 4318 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13229 "parser_bison.c" + break; + + case 743: /* primary_expr: socket_expr */ +#line 4319 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13235 "parser_bison.c" + break; + + case 744: /* primary_expr: rt_expr */ +#line 4320 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13241 "parser_bison.c" + break; + + case 745: /* primary_expr: ct_expr */ +#line 4321 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13247 "parser_bison.c" + break; + + case 746: /* primary_expr: numgen_expr */ +#line 4322 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13253 "parser_bison.c" + break; + + case 747: /* primary_expr: hash_expr */ +#line 4323 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13259 "parser_bison.c" + break; + + case 748: /* primary_expr: fib_expr */ +#line 4324 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13265 "parser_bison.c" + break; + + case 749: /* primary_expr: osf_expr */ +#line 4325 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13271 "parser_bison.c" + break; + + case 750: /* primary_expr: xfrm_expr */ +#line 4326 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13277 "parser_bison.c" + break; + + case 751: /* primary_expr: '(' basic_expr ')' */ +#line 4327 "parser_bison.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 13283 "parser_bison.c" + break; + + case 752: /* fib_expr: "fib" fib_tuple fib_result close_scope_fib */ +#line 4331 "parser_bison.y" + { + if (((yyvsp[-2].val) & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { + erec_queue(error(&(yylsp[-2]), "fib: need either saddr or daddr"), state->msgs); + YYERROR; + } + + if (((yyvsp[-2].val) & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == + (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + erec_queue(error(&(yylsp[-2]), "fib: saddr and daddr are mutually exclusive"), state->msgs); + YYERROR; + } + + if (((yyvsp[-2].val) & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == + (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + erec_queue(error(&(yylsp[-2]), "fib: iif and oif are mutually exclusive"), state->msgs); + YYERROR; + } + + (yyval.expr) = fib_expr_alloc(&(yyloc), (yyvsp[-2].val), (yyvsp[-1].val)); + } +#line 13308 "parser_bison.c" + break; + + case 753: /* fib_result: "oif" */ +#line 4353 "parser_bison.y" + { (yyval.val) =NFT_FIB_RESULT_OIF; } +#line 13314 "parser_bison.c" + break; + + case 754: /* fib_result: "oifname" */ +#line 4354 "parser_bison.y" + { (yyval.val) =NFT_FIB_RESULT_OIFNAME; } +#line 13320 "parser_bison.c" + break; + + case 755: /* fib_result: "type" close_scope_type */ +#line 4355 "parser_bison.y" + { (yyval.val) =NFT_FIB_RESULT_ADDRTYPE; } +#line 13326 "parser_bison.c" + break; + + case 756: /* fib_flag: "saddr" */ +#line 4358 "parser_bison.y" + { (yyval.val) = NFTA_FIB_F_SADDR; } +#line 13332 "parser_bison.c" + break; + + case 757: /* fib_flag: "daddr" */ +#line 4359 "parser_bison.y" + { (yyval.val) = NFTA_FIB_F_DADDR; } +#line 13338 "parser_bison.c" + break; + + case 758: /* fib_flag: "mark" */ +#line 4360 "parser_bison.y" + { (yyval.val) = NFTA_FIB_F_MARK; } +#line 13344 "parser_bison.c" + break; + + case 759: /* fib_flag: "iif" */ +#line 4361 "parser_bison.y" + { (yyval.val) = NFTA_FIB_F_IIF; } +#line 13350 "parser_bison.c" + break; + + case 760: /* fib_flag: "oif" */ +#line 4362 "parser_bison.y" + { (yyval.val) = NFTA_FIB_F_OIF; } +#line 13356 "parser_bison.c" + break; + + case 761: /* fib_tuple: fib_flag "." fib_tuple */ +#line 4366 "parser_bison.y" + { + (yyval.val) = (yyvsp[-2].val) | (yyvsp[0].val); + } +#line 13364 "parser_bison.c" + break; + + case 763: /* osf_expr: "osf" osf_ttl "version" close_scope_osf */ +#line 4373 "parser_bison.y" + { + (yyval.expr) = osf_expr_alloc(&(yyloc), (yyvsp[-2].val), NFT_OSF_F_VERSION); + } +#line 13372 "parser_bison.c" + break; + + case 764: /* osf_expr: "osf" osf_ttl "name" close_scope_osf */ +#line 4377 "parser_bison.y" + { + (yyval.expr) = osf_expr_alloc(&(yyloc), (yyvsp[-2].val), 0); + } +#line 13380 "parser_bison.c" + break; + + case 765: /* osf_ttl: %empty */ +#line 4383 "parser_bison.y" + { + (yyval.val) = NF_OSF_TTL_TRUE; + } +#line 13388 "parser_bison.c" + break; + + case 766: /* osf_ttl: "ttl" "string" */ +#line 4387 "parser_bison.y" + { + if (!strcmp((yyvsp[0].string), "loose")) + (yyval.val) = NF_OSF_TTL_LESS; + else if (!strcmp((yyvsp[0].string), "skip")) + (yyval.val) = NF_OSF_TTL_NOCHECK; + else { + erec_queue(error(&(yylsp[0]), "invalid ttl option"), + state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + xfree((yyvsp[0].string)); + } +#line 13406 "parser_bison.c" + break; + + case 768: /* shift_expr: shift_expr "<<" primary_rhs_expr */ +#line 4404 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_LSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13414 "parser_bison.c" + break; + + case 769: /* shift_expr: shift_expr ">>" primary_rhs_expr */ +#line 4408 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_RSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13422 "parser_bison.c" + break; + + case 771: /* and_expr: and_expr "&" shift_rhs_expr */ +#line 4415 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13430 "parser_bison.c" + break; + + case 773: /* exclusive_or_expr: exclusive_or_expr "^" and_rhs_expr */ +#line 4422 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_XOR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13438 "parser_bison.c" + break; + + case 775: /* inclusive_or_expr: inclusive_or_expr '|' exclusive_or_rhs_expr */ +#line 4429 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13446 "parser_bison.c" + break; + + case 778: /* concat_expr: concat_expr "." basic_expr */ +#line 4439 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 13459 "parser_bison.c" + break; + + case 779: /* prefix_rhs_expr: basic_rhs_expr "/" "number" */ +#line 4450 "parser_bison.y" + { + (yyval.expr) = prefix_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].val)); + } +#line 13467 "parser_bison.c" + break; + + case 780: /* range_rhs_expr: basic_rhs_expr "-" basic_rhs_expr */ +#line 4456 "parser_bison.y" + { + (yyval.expr) = range_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13475 "parser_bison.c" + break; + + case 783: /* map_expr: concat_expr "map" rhs_expr */ +#line 4466 "parser_bison.y" + { + (yyval.expr) = map_expr_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 13483 "parser_bison.c" + break; + + case 787: /* set_expr: '{' set_list_expr '}' */ +#line 4477 "parser_bison.y" + { + (yyvsp[-1].expr)->location = (yyloc); + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13492 "parser_bison.c" + break; + + case 788: /* set_list_expr: set_list_member_expr */ +#line 4484 "parser_bison.y" + { + (yyval.expr) = set_expr_alloc(&(yyloc), NULL); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 13501 "parser_bison.c" + break; + + case 789: /* set_list_expr: set_list_expr "comma" set_list_member_expr */ +#line 4489 "parser_bison.y" + { + compound_expr_add((yyvsp[-2].expr), (yyvsp[0].expr)); + (yyval.expr) = (yyvsp[-2].expr); + } +#line 13510 "parser_bison.c" + break; + + case 791: /* set_list_member_expr: opt_newline set_expr opt_newline */ +#line 4497 "parser_bison.y" + { + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13518 "parser_bison.c" + break; + + case 792: /* set_list_member_expr: opt_newline set_elem_expr opt_newline */ +#line 4501 "parser_bison.y" + { + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13526 "parser_bison.c" + break; + + case 793: /* set_list_member_expr: opt_newline set_elem_expr "colon" set_rhs_expr opt_newline */ +#line 4505 "parser_bison.y" + { + (yyval.expr) = mapping_expr_alloc(&(yylsp[-3]), (yyvsp[-3].expr), (yyvsp[-1].expr)); + } +#line 13534 "parser_bison.c" + break; + + case 795: /* meter_key_expr: meter_key_expr_alloc set_elem_options */ +#line 4512 "parser_bison.y" + { + (yyval.expr)->location = (yyloc); + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13543 "parser_bison.c" + break; + + case 796: /* meter_key_expr_alloc: concat_expr */ +#line 4519 "parser_bison.y" + { + (yyval.expr) = set_elem_expr_alloc(&(yylsp[0]), (yyvsp[0].expr)); + } +#line 13551 "parser_bison.c" + break; + + case 799: /* set_elem_key_expr: set_lhs_expr */ +#line 4528 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 13557 "parser_bison.c" + break; + + case 800: /* set_elem_key_expr: "*" */ +#line 4529 "parser_bison.y" + { (yyval.expr) = set_elem_catchall_expr_alloc(&(yylsp[0])); } +#line 13563 "parser_bison.c" + break; + + case 801: /* set_elem_expr_alloc: set_elem_key_expr set_elem_stmt_list */ +#line 4533 "parser_bison.y" + { + (yyval.expr) = set_elem_expr_alloc(&(yylsp[-1]), (yyvsp[-1].expr)); + list_splice_tail((yyvsp[0].list), &(yyval.expr)->stmt_list); + xfree((yyvsp[0].list)); + } +#line 13573 "parser_bison.c" + break; + + case 802: /* set_elem_expr_alloc: set_elem_key_expr */ +#line 4539 "parser_bison.y" + { + (yyval.expr) = set_elem_expr_alloc(&(yylsp[0]), (yyvsp[0].expr)); + } +#line 13581 "parser_bison.c" + break; + + case 803: /* set_elem_options: set_elem_option */ +#line 4545 "parser_bison.y" + { + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13589 "parser_bison.c" + break; + + case 805: /* set_elem_option: "timeout" time_spec */ +#line 4552 "parser_bison.y" + { + (yyvsp[-2].expr)->timeout = (yyvsp[0].val); + } +#line 13597 "parser_bison.c" + break; + + case 806: /* set_elem_option: "expires" time_spec */ +#line 4556 "parser_bison.y" + { + (yyvsp[-2].expr)->expiration = (yyvsp[0].val); + } +#line 13605 "parser_bison.c" + break; + + case 807: /* set_elem_option: comment_spec */ +#line 4560 "parser_bison.y" + { + if (already_set((yyvsp[-1].expr)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].expr)->comment = (yyvsp[0].string); + } +#line 13617 "parser_bison.c" + break; + + case 808: /* set_elem_expr_options: set_elem_expr_option */ +#line 4570 "parser_bison.y" + { + (yyval.expr) = (yyvsp[-1].expr); + } +#line 13625 "parser_bison.c" + break; + + case 810: /* set_elem_stmt_list: set_elem_stmt */ +#line 4577 "parser_bison.y" + { + (yyval.list) = xmalloc(sizeof(*(yyval.list))); + init_list_head((yyval.list)); + list_add_tail(&(yyvsp[0].stmt)->list, (yyval.list)); + } +#line 13635 "parser_bison.c" + break; + + case 811: /* set_elem_stmt_list: set_elem_stmt_list set_elem_stmt */ +#line 4583 "parser_bison.y" + { + (yyval.list) = (yyvsp[-1].list); + list_add_tail(&(yyvsp[0].stmt)->list, (yyvsp[-1].list)); + } +#line 13644 "parser_bison.c" + break; + + case 812: /* set_elem_stmt: "counter" close_scope_counter */ +#line 4590 "parser_bison.y" + { + (yyval.stmt) = counter_stmt_alloc(&(yyloc)); + } +#line 13652 "parser_bison.c" + break; + + case 813: /* set_elem_stmt: "counter" "packets" "number" "bytes" "number" close_scope_counter */ +#line 4594 "parser_bison.y" + { + (yyval.stmt) = counter_stmt_alloc(&(yyloc)); + (yyval.stmt)->counter.packets = (yyvsp[-3].val); + (yyval.stmt)->counter.bytes = (yyvsp[-1].val); + } +#line 13662 "parser_bison.c" + break; + + case 814: /* set_elem_stmt: "limit" "rate" limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit */ +#line 4600 "parser_bison.y" + { + if ((yyvsp[-1].val) == 0) { + erec_queue(error(&(yylsp[-1]), "limit burst must be > 0"), + state->msgs); + YYERROR; + } + (yyval.stmt) = limit_stmt_alloc(&(yyloc)); + (yyval.stmt)->limit.rate = (yyvsp[-2].limit_rate).rate; + (yyval.stmt)->limit.unit = (yyvsp[-2].limit_rate).unit; + (yyval.stmt)->limit.burst = (yyvsp[-1].val); + (yyval.stmt)->limit.type = NFT_LIMIT_PKTS; + (yyval.stmt)->limit.flags = (yyvsp[-3].val); + } +#line 13680 "parser_bison.c" + break; + + case 815: /* set_elem_stmt: "limit" "rate" limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit */ +#line 4614 "parser_bison.y" + { + if ((yyvsp[-1].val) == 0) { + erec_queue(error(&(yylsp[0]), "limit burst must be > 0"), + state->msgs); + YYERROR; + } + (yyval.stmt) = limit_stmt_alloc(&(yyloc)); + (yyval.stmt)->limit.rate = (yyvsp[-2].limit_rate).rate; + (yyval.stmt)->limit.unit = (yyvsp[-2].limit_rate).unit; + (yyval.stmt)->limit.burst = (yyvsp[-1].val); + (yyval.stmt)->limit.type = NFT_LIMIT_PKT_BYTES; + (yyval.stmt)->limit.flags = (yyvsp[-3].val); + } +#line 13698 "parser_bison.c" + break; + + case 816: /* set_elem_stmt: "ct" "count" "number" close_scope_ct */ +#line 4628 "parser_bison.y" + { + (yyval.stmt) = connlimit_stmt_alloc(&(yyloc)); + (yyval.stmt)->connlimit.count = (yyvsp[-1].val); + } +#line 13707 "parser_bison.c" + break; + + case 817: /* set_elem_stmt: "ct" "count" "over" "number" close_scope_ct */ +#line 4633 "parser_bison.y" + { + (yyval.stmt) = connlimit_stmt_alloc(&(yyloc)); + (yyval.stmt)->connlimit.count = (yyvsp[-1].val); + (yyval.stmt)->connlimit.flags = NFT_CONNLIMIT_F_INV; + } +#line 13717 "parser_bison.c" + break; + + case 818: /* set_elem_stmt: "quota" quota_mode "number" quota_unit quota_used close_scope_quota */ +#line 4639 "parser_bison.y" + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&(yyloc), (yyvsp[-2].string), &rate); + xfree((yyvsp[-2].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + (yyval.stmt) = quota_stmt_alloc(&(yyloc)); + (yyval.stmt)->quota.bytes = (yyvsp[-3].val) * rate; + (yyval.stmt)->quota.used = (yyvsp[-1].val); + (yyval.stmt)->quota.flags = (yyvsp[-4].val); + } +#line 13737 "parser_bison.c" + break; + + case 819: /* set_elem_stmt: "last" "used" "never" close_scope_last */ +#line 4655 "parser_bison.y" + { + (yyval.stmt) = last_stmt_alloc(&(yyloc)); + } +#line 13745 "parser_bison.c" + break; + + case 820: /* set_elem_stmt: "last" "used" time_spec close_scope_last */ +#line 4659 "parser_bison.y" + { + (yyval.stmt) = last_stmt_alloc(&(yyloc)); + (yyval.stmt)->last.used = (yyvsp[-1].val); + (yyval.stmt)->last.set = true; + } +#line 13755 "parser_bison.c" + break; + + case 821: /* set_elem_expr_option: "timeout" time_spec */ +#line 4667 "parser_bison.y" + { + (yyvsp[-2].expr)->timeout = (yyvsp[0].val); + } +#line 13763 "parser_bison.c" + break; + + case 822: /* set_elem_expr_option: "expires" time_spec */ +#line 4671 "parser_bison.y" + { + (yyvsp[-2].expr)->expiration = (yyvsp[0].val); + } +#line 13771 "parser_bison.c" + break; + + case 823: /* set_elem_expr_option: comment_spec */ +#line 4675 "parser_bison.y" + { + if (already_set((yyvsp[-1].expr)->comment, &(yylsp[0]), state)) { + xfree((yyvsp[0].string)); + YYERROR; + } + (yyvsp[-1].expr)->comment = (yyvsp[0].string); + } +#line 13783 "parser_bison.c" + break; + + case 829: /* initializer_expr: '{' '}' */ +#line 4693 "parser_bison.y" + { (yyval.expr) = compound_expr_alloc(&(yyloc), EXPR_SET); } +#line 13789 "parser_bison.c" + break; + + case 830: /* initializer_expr: "-" "number" */ +#line 4695 "parser_bison.y" + { + int32_t num = -(yyvsp[0].val); + + (yyval.expr) = constant_expr_alloc(&(yyloc), &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(num) * BITS_PER_BYTE, + &num); + } +#line 13802 "parser_bison.c" + break; + + case 831: /* counter_config: "packets" "number" "bytes" "number" */ +#line 4706 "parser_bison.y" + { + struct counter *counter; + + counter = &(yyvsp[-4].obj)->counter; + counter->packets = (yyvsp[-2].val); + counter->bytes = (yyvsp[0].val); + } +#line 13814 "parser_bison.c" + break; + + case 832: /* counter_obj: %empty */ +#line 4716 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + (yyval.obj)->type = NFT_OBJECT_COUNTER; + } +#line 13823 "parser_bison.c" + break; + + case 833: /* quota_config: quota_mode "number" quota_unit quota_used */ +#line 4723 "parser_bison.y" + { + struct error_record *erec; + struct quota *quota; + uint64_t rate; + + erec = data_unit_parse(&(yyloc), (yyvsp[-1].string), &rate); + xfree((yyvsp[-1].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + quota = &(yyvsp[-4].obj)->quota; + quota->bytes = (yyvsp[-2].val) * rate; + quota->used = (yyvsp[0].val); + quota->flags = (yyvsp[-3].val); + } +#line 13845 "parser_bison.c" + break; + + case 834: /* quota_obj: %empty */ +#line 4743 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + (yyval.obj)->type = NFT_OBJECT_QUOTA; + } +#line 13854 "parser_bison.c" + break; + + case 835: /* secmark_config: string */ +#line 4750 "parser_bison.y" + { + int ret; + struct secmark *secmark; + + secmark = &(yyvsp[-1].obj)->secmark; + ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", (yyvsp[0].string)); + if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) { + erec_queue(error(&(yylsp[0]), "invalid context '%s', max length is %u\n", (yyvsp[0].string), (int)sizeof(secmark->ctx)), state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + xfree((yyvsp[0].string)); + } +#line 13872 "parser_bison.c" + break; + + case 836: /* secmark_obj: %empty */ +#line 4766 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + (yyval.obj)->type = NFT_OBJECT_SECMARK; + } +#line 13881 "parser_bison.c" + break; + + case 837: /* ct_obj_type: "helper" */ +#line 4772 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_CT_HELPER; } +#line 13887 "parser_bison.c" + break; + + case 838: /* ct_obj_type: "timeout" */ +#line 4773 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_CT_TIMEOUT; } +#line 13893 "parser_bison.c" + break; + + case 839: /* ct_obj_type: "expectation" */ +#line 4774 "parser_bison.y" + { (yyval.val) = NFT_OBJECT_CT_EXPECT; } +#line 13899 "parser_bison.c" + break; + + case 840: /* ct_cmd_type: "helpers" */ +#line 4777 "parser_bison.y" + { (yyval.val) = CMD_OBJ_CT_HELPERS; } +#line 13905 "parser_bison.c" + break; + + case 841: /* ct_cmd_type: "timeout" */ +#line 4778 "parser_bison.y" + { (yyval.val) = CMD_OBJ_CT_TIMEOUTS; } +#line 13911 "parser_bison.c" + break; + + case 842: /* ct_cmd_type: "expectation" */ +#line 4779 "parser_bison.y" + { (yyval.val) = CMD_OBJ_CT_EXPECTATIONS; } +#line 13917 "parser_bison.c" + break; + + case 843: /* ct_l4protoname: "tcp" close_scope_tcp */ +#line 4782 "parser_bison.y" + { (yyval.val) = IPPROTO_TCP; } +#line 13923 "parser_bison.c" + break; + + case 844: /* ct_l4protoname: "udp" close_scope_udp */ +#line 4783 "parser_bison.y" + { (yyval.val) = IPPROTO_UDP; } +#line 13929 "parser_bison.c" + break; + + case 845: /* ct_helper_config: "type" "quoted string" "protocol" ct_l4protoname stmt_separator close_scope_type */ +#line 4787 "parser_bison.y" + { + struct ct_helper *ct; + int ret; + + ct = &(yyvsp[-6].obj)->ct_helper; + + ret = snprintf(ct->name, sizeof(ct->name), "%s", (yyvsp[-4].string)); + if (ret <= 0 || ret >= (int)sizeof(ct->name)) { + erec_queue(error(&(yylsp[-4]), "invalid name '%s', max length is %u\n", (yyvsp[-4].string), (int)sizeof(ct->name)), state->msgs); + YYERROR; + } + xfree((yyvsp[-4].string)); + + ct->l4proto = (yyvsp[-2].val); + } +#line 13949 "parser_bison.c" + break; + + case 846: /* ct_helper_config: "l3proto" family_spec_explicit stmt_separator */ +#line 4803 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_helper.l3proto = (yyvsp[-1].val); + } +#line 13957 "parser_bison.c" + break; + + case 847: /* timeout_states: timeout_state */ +#line 4809 "parser_bison.y" + { + (yyval.list) = xmalloc(sizeof(*(yyval.list))); + init_list_head((yyval.list)); + list_add_tail((yyvsp[0].list), (yyval.list)); + } +#line 13967 "parser_bison.c" + break; + + case 848: /* timeout_states: timeout_states "comma" timeout_state */ +#line 4815 "parser_bison.y" + { + list_add_tail((yyvsp[0].list), (yyvsp[-2].list)); + (yyval.list) = (yyvsp[-2].list); + } +#line 13976 "parser_bison.c" + break; + + case 849: /* timeout_state: "string" "colon" time_spec_or_num_s */ +#line 4822 "parser_bison.y" + { + struct timeout_state *ts; + + ts = xzalloc(sizeof(*ts)); + ts->timeout_str = (yyvsp[-2].string); + ts->timeout_value = (yyvsp[0].val); + ts->location = (yylsp[-2]); + init_list_head(&ts->head); + (yyval.list) = &ts->head; + } +#line 13991 "parser_bison.c" + break; + + case 850: /* ct_timeout_config: "protocol" ct_l4protoname stmt_separator */ +#line 4835 "parser_bison.y" + { + struct ct_timeout *ct; + int l4proto = (yyvsp[-1].val); + + ct = &(yyvsp[-3].obj)->ct_timeout; + ct->l4proto = l4proto; + } +#line 14003 "parser_bison.c" + break; + + case 851: /* ct_timeout_config: "policy" '=' '{' timeout_states '}' stmt_separator close_scope_policy */ +#line 4843 "parser_bison.y" + { + struct ct_timeout *ct; + + ct = &(yyvsp[-7].obj)->ct_timeout; + list_splice_tail((yyvsp[-3].list), &ct->timeout_list); + xfree((yyvsp[-3].list)); + } +#line 14015 "parser_bison.c" + break; + + case 852: /* ct_timeout_config: "l3proto" family_spec_explicit stmt_separator */ +#line 4851 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_timeout.l3proto = (yyvsp[-1].val); + } +#line 14023 "parser_bison.c" + break; + + case 853: /* ct_expect_config: "protocol" ct_l4protoname stmt_separator */ +#line 4857 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_expect.l4proto = (yyvsp[-1].val); + } +#line 14031 "parser_bison.c" + break; + + case 854: /* ct_expect_config: "dport" "number" stmt_separator */ +#line 4861 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_expect.dport = (yyvsp[-1].val); + } +#line 14039 "parser_bison.c" + break; + + case 855: /* ct_expect_config: "timeout" time_spec stmt_separator */ +#line 4865 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_expect.timeout = (yyvsp[-1].val); + } +#line 14047 "parser_bison.c" + break; + + case 856: /* ct_expect_config: "size" "number" stmt_separator */ +#line 4869 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_expect.size = (yyvsp[-1].val); + } +#line 14055 "parser_bison.c" + break; + + case 857: /* ct_expect_config: "l3proto" family_spec_explicit stmt_separator */ +#line 4873 "parser_bison.y" + { + (yyvsp[-3].obj)->ct_expect.l3proto = (yyvsp[-1].val); + } +#line 14063 "parser_bison.c" + break; + + case 858: /* ct_obj_alloc: %empty */ +#line 4879 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + } +#line 14071 "parser_bison.c" + break; + + case 859: /* limit_config: "rate" limit_mode limit_rate_pkts limit_burst_pkts */ +#line 4885 "parser_bison.y" + { + struct limit *limit; + + limit = &(yyvsp[-4].obj)->limit; + limit->rate = (yyvsp[-1].limit_rate).rate; + limit->unit = (yyvsp[-1].limit_rate).unit; + limit->burst = (yyvsp[0].val); + limit->type = NFT_LIMIT_PKTS; + limit->flags = (yyvsp[-2].val); + } +#line 14086 "parser_bison.c" + break; + + case 860: /* limit_config: "rate" limit_mode limit_rate_bytes limit_burst_bytes */ +#line 4896 "parser_bison.y" + { + struct limit *limit; + + limit = &(yyvsp[-4].obj)->limit; + limit->rate = (yyvsp[-1].limit_rate).rate; + limit->unit = (yyvsp[-1].limit_rate).unit; + limit->burst = (yyvsp[0].val); + limit->type = NFT_LIMIT_PKT_BYTES; + limit->flags = (yyvsp[-2].val); + } +#line 14101 "parser_bison.c" + break; + + case 861: /* limit_obj: %empty */ +#line 4909 "parser_bison.y" + { + (yyval.obj) = obj_alloc(&(yyloc)); + (yyval.obj)->type = NFT_OBJECT_LIMIT; + } +#line 14110 "parser_bison.c" + break; + + case 862: /* relational_expr: expr rhs_expr */ +#line 4916 "parser_bison.y" + { + (yyval.expr) = relational_expr_alloc(&(yyloc), OP_IMPLICIT, (yyvsp[-1].expr), (yyvsp[0].expr)); + } +#line 14118 "parser_bison.c" + break; + + case 863: /* relational_expr: expr list_rhs_expr */ +#line 4920 "parser_bison.y" + { + (yyval.expr) = relational_expr_alloc(&(yyloc), OP_IMPLICIT, (yyvsp[-1].expr), (yyvsp[0].expr)); + } +#line 14126 "parser_bison.c" + break; + + case 864: /* relational_expr: expr basic_rhs_expr "/" list_rhs_expr */ +#line 4924 "parser_bison.y" + { + (yyval.expr) = flagcmp_expr_alloc(&(yyloc), OP_EQ, (yyvsp[-3].expr), (yyvsp[0].expr), (yyvsp[-2].expr)); + } +#line 14134 "parser_bison.c" + break; + + case 865: /* relational_expr: expr list_rhs_expr "/" list_rhs_expr */ +#line 4928 "parser_bison.y" + { + (yyval.expr) = flagcmp_expr_alloc(&(yyloc), OP_EQ, (yyvsp[-3].expr), (yyvsp[0].expr), (yyvsp[-2].expr)); + } +#line 14142 "parser_bison.c" + break; + + case 866: /* relational_expr: expr relational_op basic_rhs_expr "/" list_rhs_expr */ +#line 4932 "parser_bison.y" + { + (yyval.expr) = flagcmp_expr_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-4].expr), (yyvsp[0].expr), (yyvsp[-2].expr)); + } +#line 14150 "parser_bison.c" + break; + + case 867: /* relational_expr: expr relational_op list_rhs_expr "/" list_rhs_expr */ +#line 4936 "parser_bison.y" + { + (yyval.expr) = flagcmp_expr_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-4].expr), (yyvsp[0].expr), (yyvsp[-2].expr)); + } +#line 14158 "parser_bison.c" + break; + + case 868: /* relational_expr: expr relational_op rhs_expr */ +#line 4940 "parser_bison.y" + { + (yyval.expr) = relational_expr_alloc(&(yylsp[-1]), (yyvsp[-1].val), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14166 "parser_bison.c" + break; + + case 869: /* relational_expr: expr relational_op list_rhs_expr */ +#line 4944 "parser_bison.y" + { + (yyval.expr) = relational_expr_alloc(&(yylsp[-1]), (yyvsp[-1].val), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14174 "parser_bison.c" + break; + + case 870: /* list_rhs_expr: basic_rhs_expr "comma" basic_rhs_expr */ +#line 4950 "parser_bison.y" + { + (yyval.expr) = list_expr_alloc(&(yyloc)); + compound_expr_add((yyval.expr), (yyvsp[-2].expr)); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 14184 "parser_bison.c" + break; + + case 871: /* list_rhs_expr: list_rhs_expr "comma" basic_rhs_expr */ +#line 4956 "parser_bison.y" + { + (yyvsp[-2].expr)->location = (yyloc); + compound_expr_add((yyvsp[-2].expr), (yyvsp[0].expr)); + (yyval.expr) = (yyvsp[-2].expr); + } +#line 14194 "parser_bison.c" + break; + + case 872: /* rhs_expr: concat_rhs_expr */ +#line 4963 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14200 "parser_bison.c" + break; + + case 873: /* rhs_expr: set_expr */ +#line 4964 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14206 "parser_bison.c" + break; + + case 874: /* rhs_expr: set_ref_symbol_expr */ +#line 4965 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14212 "parser_bison.c" + break; + + case 876: /* shift_rhs_expr: shift_rhs_expr "<<" primary_rhs_expr */ +#line 4970 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_LSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14220 "parser_bison.c" + break; + + case 877: /* shift_rhs_expr: shift_rhs_expr ">>" primary_rhs_expr */ +#line 4974 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_RSHIFT, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14228 "parser_bison.c" + break; + + case 879: /* and_rhs_expr: and_rhs_expr "&" shift_rhs_expr */ +#line 4981 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14236 "parser_bison.c" + break; + + case 881: /* exclusive_or_rhs_expr: exclusive_or_rhs_expr "^" and_rhs_expr */ +#line 4988 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_XOR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14244 "parser_bison.c" + break; + + case 883: /* inclusive_or_rhs_expr: inclusive_or_rhs_expr '|' exclusive_or_rhs_expr */ +#line 4995 "parser_bison.y" + { + (yyval.expr) = binop_expr_alloc(&(yyloc), OP_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 14252 "parser_bison.c" + break; + + case 887: /* concat_rhs_expr: concat_rhs_expr "." multiton_rhs_expr */ +#line 5006 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 14265 "parser_bison.c" + break; + + case 888: /* concat_rhs_expr: concat_rhs_expr "." basic_rhs_expr */ +#line 5015 "parser_bison.y" + { + struct location rhs[] = { + [1] = (yylsp[-1]), + [2] = (yylsp[0]), + }; + + (yyval.expr) = handle_concat_expr(&(yyloc), (yyval.expr), (yyvsp[-2].expr), (yyvsp[0].expr), rhs); + } +#line 14278 "parser_bison.c" + break; + + case 889: /* boolean_keys: "exists" */ +#line 5025 "parser_bison.y" + { (yyval.val8) = true; } +#line 14284 "parser_bison.c" + break; + + case 890: /* boolean_keys: "missing" */ +#line 5026 "parser_bison.y" + { (yyval.val8) = false; } +#line 14290 "parser_bison.c" + break; + + case 891: /* boolean_expr: boolean_keys */ +#line 5030 "parser_bison.y" + { + (yyval.expr) = constant_expr_alloc(&(yyloc), &boolean_type, + BYTEORDER_HOST_ENDIAN, + sizeof((yyvsp[0].val8)) * BITS_PER_BYTE, &(yyvsp[0].val8)); + } +#line 14300 "parser_bison.c" + break; + + case 892: /* keyword_expr: "ether" close_scope_eth */ +#line 5037 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "ether"); } +#line 14306 "parser_bison.c" + break; + + case 893: /* keyword_expr: "ip" close_scope_ip */ +#line 5038 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "ip"); } +#line 14312 "parser_bison.c" + break; + + case 894: /* keyword_expr: "ip6" close_scope_ip6 */ +#line 5039 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "ip6"); } +#line 14318 "parser_bison.c" + break; + + case 895: /* keyword_expr: "vlan" close_scope_vlan */ +#line 5040 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "vlan"); } +#line 14324 "parser_bison.c" + break; + + case 896: /* keyword_expr: "arp" close_scope_arp */ +#line 5041 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "arp"); } +#line 14330 "parser_bison.c" + break; + + case 897: /* keyword_expr: "dnat" close_scope_nat */ +#line 5042 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "dnat"); } +#line 14336 "parser_bison.c" + break; + + case 898: /* keyword_expr: "snat" close_scope_nat */ +#line 5043 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "snat"); } +#line 14342 "parser_bison.c" + break; + + case 899: /* keyword_expr: "ecn" */ +#line 5044 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "ecn"); } +#line 14348 "parser_bison.c" + break; + + case 900: /* keyword_expr: "reset" close_scope_reset */ +#line 5045 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "reset"); } +#line 14354 "parser_bison.c" + break; + + case 901: /* keyword_expr: "destroy" close_scope_destroy */ +#line 5046 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "destroy"); } +#line 14360 "parser_bison.c" + break; + + case 902: /* keyword_expr: "original" */ +#line 5047 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "original"); } +#line 14366 "parser_bison.c" + break; + + case 903: /* keyword_expr: "reply" */ +#line 5048 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "reply"); } +#line 14372 "parser_bison.c" + break; + + case 904: /* keyword_expr: "label" */ +#line 5049 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "label"); } +#line 14378 "parser_bison.c" + break; + + case 905: /* keyword_expr: "last" close_scope_last */ +#line 5050 "parser_bison.y" + { (yyval.expr) = symbol_value(&(yyloc), "last"); } +#line 14384 "parser_bison.c" + break; + + case 906: /* primary_rhs_expr: symbol_expr */ +#line 5053 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14390 "parser_bison.c" + break; + + case 907: /* primary_rhs_expr: integer_expr */ +#line 5054 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14396 "parser_bison.c" + break; + + case 908: /* primary_rhs_expr: boolean_expr */ +#line 5055 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14402 "parser_bison.c" + break; + + case 909: /* primary_rhs_expr: keyword_expr */ +#line 5056 "parser_bison.y" + { (yyval.expr) = (yyvsp[0].expr); } +#line 14408 "parser_bison.c" + break; + + case 910: /* primary_rhs_expr: "tcp" close_scope_tcp */ +#line 5058 "parser_bison.y" + { + uint8_t data = IPPROTO_TCP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14419 "parser_bison.c" + break; + + case 911: /* primary_rhs_expr: "udp" close_scope_udp */ +#line 5065 "parser_bison.y" + { + uint8_t data = IPPROTO_UDP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14430 "parser_bison.c" + break; + + case 912: /* primary_rhs_expr: "udplite" close_scope_udplite */ +#line 5072 "parser_bison.y" + { + uint8_t data = IPPROTO_UDPLITE; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14441 "parser_bison.c" + break; + + case 913: /* primary_rhs_expr: "esp" close_scope_esp */ +#line 5079 "parser_bison.y" + { + uint8_t data = IPPROTO_ESP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14452 "parser_bison.c" + break; + + case 914: /* primary_rhs_expr: "ah" close_scope_ah */ +#line 5086 "parser_bison.y" + { + uint8_t data = IPPROTO_AH; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14463 "parser_bison.c" + break; + + case 915: /* primary_rhs_expr: "icmp" close_scope_icmp */ +#line 5093 "parser_bison.y" + { + uint8_t data = IPPROTO_ICMP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14474 "parser_bison.c" + break; + + case 916: /* primary_rhs_expr: "igmp" */ +#line 5100 "parser_bison.y" + { + uint8_t data = IPPROTO_IGMP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14485 "parser_bison.c" + break; + + case 917: /* primary_rhs_expr: "icmpv6" close_scope_icmp */ +#line 5107 "parser_bison.y" + { + uint8_t data = IPPROTO_ICMPV6; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14496 "parser_bison.c" + break; + + case 918: /* primary_rhs_expr: "gre" close_scope_gre */ +#line 5114 "parser_bison.y" + { + uint8_t data = IPPROTO_GRE; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14507 "parser_bison.c" + break; + + case 919: /* primary_rhs_expr: "comp" close_scope_comp */ +#line 5121 "parser_bison.y" + { + uint8_t data = IPPROTO_COMP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14518 "parser_bison.c" + break; + + case 920: /* primary_rhs_expr: "dccp" close_scope_dccp */ +#line 5128 "parser_bison.y" + { + uint8_t data = IPPROTO_DCCP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14529 "parser_bison.c" + break; + + case 921: /* primary_rhs_expr: "sctp" close_scope_sctp */ +#line 5135 "parser_bison.y" + { + uint8_t data = IPPROTO_SCTP; + (yyval.expr) = constant_expr_alloc(&(yyloc), &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14540 "parser_bison.c" + break; + + case 922: /* primary_rhs_expr: "redirect" close_scope_nat */ +#line 5142 "parser_bison.y" + { + uint8_t data = ICMP_REDIRECT; + (yyval.expr) = constant_expr_alloc(&(yyloc), &icmp_type_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } +#line 14551 "parser_bison.c" + break; + + case 923: /* primary_rhs_expr: '(' basic_rhs_expr ')' */ +#line 5148 "parser_bison.y" + { (yyval.expr) = (yyvsp[-1].expr); } +#line 14557 "parser_bison.c" + break; + + case 924: /* relational_op: "==" */ +#line 5151 "parser_bison.y" + { (yyval.val) = OP_EQ; } +#line 14563 "parser_bison.c" + break; + + case 925: /* relational_op: "!=" */ +#line 5152 "parser_bison.y" + { (yyval.val) = OP_NEQ; } +#line 14569 "parser_bison.c" + break; + + case 926: /* relational_op: "<" */ +#line 5153 "parser_bison.y" + { (yyval.val) = OP_LT; } +#line 14575 "parser_bison.c" + break; + + case 927: /* relational_op: ">" */ +#line 5154 "parser_bison.y" + { (yyval.val) = OP_GT; } +#line 14581 "parser_bison.c" + break; + + case 928: /* relational_op: ">=" */ +#line 5155 "parser_bison.y" + { (yyval.val) = OP_GTE; } +#line 14587 "parser_bison.c" + break; + + case 929: /* relational_op: "<=" */ +#line 5156 "parser_bison.y" + { (yyval.val) = OP_LTE; } +#line 14593 "parser_bison.c" + break; + + case 930: /* relational_op: "!" */ +#line 5157 "parser_bison.y" + { (yyval.val) = OP_NEG; } +#line 14599 "parser_bison.c" + break; + + case 931: /* verdict_expr: "accept" */ +#line 5161 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NF_ACCEPT, NULL); + } +#line 14607 "parser_bison.c" + break; + + case 932: /* verdict_expr: "drop" */ +#line 5165 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NF_DROP, NULL); + } +#line 14615 "parser_bison.c" + break; + + case 933: /* verdict_expr: "continue" */ +#line 5169 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NFT_CONTINUE, NULL); + } +#line 14623 "parser_bison.c" + break; + + case 934: /* verdict_expr: "jump" chain_expr */ +#line 5173 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NFT_JUMP, (yyvsp[0].expr)); + } +#line 14631 "parser_bison.c" + break; + + case 935: /* verdict_expr: "goto" chain_expr */ +#line 5177 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NFT_GOTO, (yyvsp[0].expr)); + } +#line 14639 "parser_bison.c" + break; + + case 936: /* verdict_expr: "return" */ +#line 5181 "parser_bison.y" + { + (yyval.expr) = verdict_expr_alloc(&(yyloc), NFT_RETURN, NULL); + } +#line 14647 "parser_bison.c" + break; + + case 938: /* chain_expr: identifier */ +#line 5188 "parser_bison.y" + { + (yyval.expr) = constant_expr_alloc(&(yyloc), &string_type, + BYTEORDER_HOST_ENDIAN, + strlen((yyvsp[0].string)) * BITS_PER_BYTE, + (yyvsp[0].string)); + xfree((yyvsp[0].string)); + } +#line 14659 "parser_bison.c" + break; + + case 939: /* meta_expr: "meta" meta_key close_scope_meta */ +#line 5198 "parser_bison.y" + { + (yyval.expr) = meta_expr_alloc(&(yyloc), (yyvsp[-1].val)); + } +#line 14667 "parser_bison.c" + break; + + case 940: /* meta_expr: meta_key_unqualified */ +#line 5202 "parser_bison.y" + { + (yyval.expr) = meta_expr_alloc(&(yyloc), (yyvsp[0].val)); + } +#line 14675 "parser_bison.c" + break; + + case 941: /* meta_expr: "meta" "string" close_scope_meta */ +#line 5206 "parser_bison.y" + { + struct error_record *erec; + unsigned int key; + + erec = meta_key_parse(&(yyloc), (yyvsp[-1].string), &key); + xfree((yyvsp[-1].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + (yyval.expr) = meta_expr_alloc(&(yyloc), key); + } +#line 14693 "parser_bison.c" + break; + + case 944: /* meta_key_qualified: "length" */ +#line 5225 "parser_bison.y" + { (yyval.val) = NFT_META_LEN; } +#line 14699 "parser_bison.c" + break; + + case 945: /* meta_key_qualified: "protocol" */ +#line 5226 "parser_bison.y" + { (yyval.val) = NFT_META_PROTOCOL; } +#line 14705 "parser_bison.c" + break; + + case 946: /* meta_key_qualified: "priority" */ +#line 5227 "parser_bison.y" + { (yyval.val) = NFT_META_PRIORITY; } +#line 14711 "parser_bison.c" + break; + + case 947: /* meta_key_qualified: "random" */ +#line 5228 "parser_bison.y" + { (yyval.val) = NFT_META_PRANDOM; } +#line 14717 "parser_bison.c" + break; + + case 948: /* meta_key_qualified: "secmark" close_scope_secmark */ +#line 5229 "parser_bison.y" + { (yyval.val) = NFT_META_SECMARK; } +#line 14723 "parser_bison.c" + break; + + case 949: /* meta_key_unqualified: "mark" */ +#line 5232 "parser_bison.y" + { (yyval.val) = NFT_META_MARK; } +#line 14729 "parser_bison.c" + break; + + case 950: /* meta_key_unqualified: "iif" */ +#line 5233 "parser_bison.y" + { (yyval.val) = NFT_META_IIF; } +#line 14735 "parser_bison.c" + break; + + case 951: /* meta_key_unqualified: "iifname" */ +#line 5234 "parser_bison.y" + { (yyval.val) = NFT_META_IIFNAME; } +#line 14741 "parser_bison.c" + break; + + case 952: /* meta_key_unqualified: "iiftype" */ +#line 5235 "parser_bison.y" + { (yyval.val) = NFT_META_IIFTYPE; } +#line 14747 "parser_bison.c" + break; + + case 953: /* meta_key_unqualified: "oif" */ +#line 5236 "parser_bison.y" + { (yyval.val) = NFT_META_OIF; } +#line 14753 "parser_bison.c" + break; + + case 954: /* meta_key_unqualified: "oifname" */ +#line 5237 "parser_bison.y" + { (yyval.val) = NFT_META_OIFNAME; } +#line 14759 "parser_bison.c" + break; + + case 955: /* meta_key_unqualified: "oiftype" */ +#line 5238 "parser_bison.y" + { (yyval.val) = NFT_META_OIFTYPE; } +#line 14765 "parser_bison.c" + break; + + case 956: /* meta_key_unqualified: "skuid" */ +#line 5239 "parser_bison.y" + { (yyval.val) = NFT_META_SKUID; } +#line 14771 "parser_bison.c" + break; + + case 957: /* meta_key_unqualified: "skgid" */ +#line 5240 "parser_bison.y" + { (yyval.val) = NFT_META_SKGID; } +#line 14777 "parser_bison.c" + break; + + case 958: /* meta_key_unqualified: "nftrace" */ +#line 5241 "parser_bison.y" + { (yyval.val) = NFT_META_NFTRACE; } +#line 14783 "parser_bison.c" + break; + + case 959: /* meta_key_unqualified: "rtclassid" */ +#line 5242 "parser_bison.y" + { (yyval.val) = NFT_META_RTCLASSID; } +#line 14789 "parser_bison.c" + break; + + case 960: /* meta_key_unqualified: "ibriport" */ +#line 5243 "parser_bison.y" + { (yyval.val) = NFT_META_BRI_IIFNAME; } +#line 14795 "parser_bison.c" + break; + + case 961: /* meta_key_unqualified: "obriport" */ +#line 5244 "parser_bison.y" + { (yyval.val) = NFT_META_BRI_OIFNAME; } +#line 14801 "parser_bison.c" + break; + + case 962: /* meta_key_unqualified: "ibrname" */ +#line 5245 "parser_bison.y" + { (yyval.val) = NFT_META_BRI_IIFNAME; } +#line 14807 "parser_bison.c" + break; + + case 963: /* meta_key_unqualified: "obrname" */ +#line 5246 "parser_bison.y" + { (yyval.val) = NFT_META_BRI_OIFNAME; } +#line 14813 "parser_bison.c" + break; + + case 964: /* meta_key_unqualified: "pkttype" */ +#line 5247 "parser_bison.y" + { (yyval.val) = NFT_META_PKTTYPE; } +#line 14819 "parser_bison.c" + break; + + case 965: /* meta_key_unqualified: "cpu" */ +#line 5248 "parser_bison.y" + { (yyval.val) = NFT_META_CPU; } +#line 14825 "parser_bison.c" + break; + + case 966: /* meta_key_unqualified: "iifgroup" */ +#line 5249 "parser_bison.y" + { (yyval.val) = NFT_META_IIFGROUP; } +#line 14831 "parser_bison.c" + break; + + case 967: /* meta_key_unqualified: "oifgroup" */ +#line 5250 "parser_bison.y" + { (yyval.val) = NFT_META_OIFGROUP; } +#line 14837 "parser_bison.c" + break; + + case 968: /* meta_key_unqualified: "cgroup" */ +#line 5251 "parser_bison.y" + { (yyval.val) = NFT_META_CGROUP; } +#line 14843 "parser_bison.c" + break; + + case 969: /* meta_key_unqualified: "ipsec" close_scope_ipsec */ +#line 5252 "parser_bison.y" + { (yyval.val) = NFT_META_SECPATH; } +#line 14849 "parser_bison.c" + break; + + case 970: /* meta_key_unqualified: "time" */ +#line 5253 "parser_bison.y" + { (yyval.val) = NFT_META_TIME_NS; } +#line 14855 "parser_bison.c" + break; + + case 971: /* meta_key_unqualified: "day" */ +#line 5254 "parser_bison.y" + { (yyval.val) = NFT_META_TIME_DAY; } +#line 14861 "parser_bison.c" + break; + + case 972: /* meta_key_unqualified: "hour" */ +#line 5255 "parser_bison.y" + { (yyval.val) = NFT_META_TIME_HOUR; } +#line 14867 "parser_bison.c" + break; + + case 973: /* meta_stmt: "meta" meta_key "set" stmt_expr close_scope_meta */ +#line 5259 "parser_bison.y" + { + switch ((yyvsp[-3].val)) { + case NFT_META_SECMARK: + switch ((yyvsp[-1].expr)->etype) { + case EXPR_CT: + (yyval.stmt) = meta_stmt_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-1].expr)); + break; + default: + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_SECMARK; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + break; + } + break; + default: + (yyval.stmt) = meta_stmt_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-1].expr)); + break; + } + } +#line 14891 "parser_bison.c" + break; + + case 974: /* meta_stmt: meta_key_unqualified "set" stmt_expr */ +#line 5279 "parser_bison.y" + { + (yyval.stmt) = meta_stmt_alloc(&(yyloc), (yyvsp[-2].val), (yyvsp[0].expr)); + } +#line 14899 "parser_bison.c" + break; + + case 975: /* meta_stmt: "meta" "string" "set" stmt_expr close_scope_meta */ +#line 5283 "parser_bison.y" + { + struct error_record *erec; + unsigned int key; + + erec = meta_key_parse(&(yyloc), (yyvsp[-3].string), &key); + xfree((yyvsp[-3].string)); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + (yyval.stmt) = meta_stmt_alloc(&(yyloc), key, (yyvsp[-1].expr)); + } +#line 14917 "parser_bison.c" + break; + + case 976: /* meta_stmt: "notrack" */ +#line 5297 "parser_bison.y" + { + (yyval.stmt) = notrack_stmt_alloc(&(yyloc)); + } +#line 14925 "parser_bison.c" + break; + + case 977: /* meta_stmt: "flow" "offload" "@" string close_scope_at */ +#line 5301 "parser_bison.y" + { + (yyval.stmt) = flow_offload_stmt_alloc(&(yyloc), (yyvsp[-1].string)); + } +#line 14933 "parser_bison.c" + break; + + case 978: /* meta_stmt: "flow" "add" "@" string close_scope_at */ +#line 5305 "parser_bison.y" + { + (yyval.stmt) = flow_offload_stmt_alloc(&(yyloc), (yyvsp[-1].string)); + } +#line 14941 "parser_bison.c" + break; + + case 979: /* socket_expr: "socket" socket_key close_scope_socket */ +#line 5311 "parser_bison.y" + { + (yyval.expr) = socket_expr_alloc(&(yyloc), (yyvsp[-1].val), 0); + } +#line 14949 "parser_bison.c" + break; + + case 980: /* socket_expr: "socket" "cgroupv2" "level" "number" close_scope_socket */ +#line 5315 "parser_bison.y" + { + (yyval.expr) = socket_expr_alloc(&(yyloc), NFT_SOCKET_CGROUPV2, (yyvsp[-1].val)); + } +#line 14957 "parser_bison.c" + break; + + case 981: /* socket_key: "transparent" */ +#line 5320 "parser_bison.y" + { (yyval.val) = NFT_SOCKET_TRANSPARENT; } +#line 14963 "parser_bison.c" + break; + + case 982: /* socket_key: "mark" */ +#line 5321 "parser_bison.y" + { (yyval.val) = NFT_SOCKET_MARK; } +#line 14969 "parser_bison.c" + break; + + case 983: /* socket_key: "wildcard" */ +#line 5322 "parser_bison.y" + { (yyval.val) = NFT_SOCKET_WILDCARD; } +#line 14975 "parser_bison.c" + break; + + case 984: /* offset_opt: %empty */ +#line 5325 "parser_bison.y" + { (yyval.val) = 0; } +#line 14981 "parser_bison.c" + break; + + case 985: /* offset_opt: "offset" "number" */ +#line 5326 "parser_bison.y" + { (yyval.val) = (yyvsp[0].val); } +#line 14987 "parser_bison.c" + break; + + case 986: /* numgen_type: "inc" */ +#line 5329 "parser_bison.y" + { (yyval.val) = NFT_NG_INCREMENTAL; } +#line 14993 "parser_bison.c" + break; + + case 987: /* numgen_type: "random" */ +#line 5330 "parser_bison.y" + { (yyval.val) = NFT_NG_RANDOM; } +#line 14999 "parser_bison.c" + break; + + case 988: /* numgen_expr: "numgen" numgen_type "mod" "number" offset_opt close_scope_numgen */ +#line 5334 "parser_bison.y" + { + (yyval.expr) = numgen_expr_alloc(&(yyloc), (yyvsp[-4].val), (yyvsp[-2].val), (yyvsp[-1].val)); + } +#line 15007 "parser_bison.c" + break; + + case 989: /* xfrm_spnum: "spnum" "number" */ +#line 5339 "parser_bison.y" + { (yyval.val) = (yyvsp[0].val); } +#line 15013 "parser_bison.c" + break; + + case 990: /* xfrm_spnum: %empty */ +#line 5340 "parser_bison.y" + { (yyval.val) = 0; } +#line 15019 "parser_bison.c" + break; + + case 991: /* xfrm_dir: "in" */ +#line 5343 "parser_bison.y" + { (yyval.val) = XFRM_POLICY_IN; } +#line 15025 "parser_bison.c" + break; + + case 992: /* xfrm_dir: "out" */ +#line 5344 "parser_bison.y" + { (yyval.val) = XFRM_POLICY_OUT; } +#line 15031 "parser_bison.c" + break; + + case 993: /* xfrm_state_key: "spi" */ +#line 5347 "parser_bison.y" + { (yyval.val) = NFT_XFRM_KEY_SPI; } +#line 15037 "parser_bison.c" + break; + + case 994: /* xfrm_state_key: "reqid" */ +#line 5348 "parser_bison.y" + { (yyval.val) = NFT_XFRM_KEY_REQID; } +#line 15043 "parser_bison.c" + break; + + case 995: /* xfrm_state_proto_key: "daddr" */ +#line 5351 "parser_bison.y" + { (yyval.val) = NFT_XFRM_KEY_DADDR_IP4; } +#line 15049 "parser_bison.c" + break; + + case 996: /* xfrm_state_proto_key: "saddr" */ +#line 5352 "parser_bison.y" + { (yyval.val) = NFT_XFRM_KEY_SADDR_IP4; } +#line 15055 "parser_bison.c" + break; + + case 997: /* xfrm_expr: "ipsec" xfrm_dir xfrm_spnum xfrm_state_key close_scope_ipsec */ +#line 5356 "parser_bison.y" + { + if ((yyvsp[-2].val) > 255) { + erec_queue(error(&(yylsp[-2]), "value too large"), state->msgs); + YYERROR; + } + (yyval.expr) = xfrm_expr_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-2].val), (yyvsp[-1].val)); + } +#line 15067 "parser_bison.c" + break; + + case 998: /* xfrm_expr: "ipsec" xfrm_dir xfrm_spnum nf_key_proto xfrm_state_proto_key close_scope_ipsec */ +#line 5364 "parser_bison.y" + { + enum nft_xfrm_keys xfrmk = (yyvsp[-1].val); + + switch ((yyvsp[-2].val)) { + case NFPROTO_IPV4: + break; + case NFPROTO_IPV6: + if ((yyvsp[-1].val) == NFT_XFRM_KEY_SADDR_IP4) + xfrmk = NFT_XFRM_KEY_SADDR_IP6; + else if ((yyvsp[-1].val) == NFT_XFRM_KEY_DADDR_IP4) + xfrmk = NFT_XFRM_KEY_DADDR_IP6; + break; + default: + YYERROR; + break; + } + + if ((yyvsp[-3].val) > 255) { + erec_queue(error(&(yylsp[-3]), "value too large"), state->msgs); + YYERROR; + } + + (yyval.expr) = xfrm_expr_alloc(&(yyloc), (yyvsp[-4].val), (yyvsp[-3].val), xfrmk); + } +#line 15096 "parser_bison.c" + break; + + case 999: /* hash_expr: "jhash" expr "mod" "number" "seed" "number" offset_opt close_scope_hash */ +#line 5391 "parser_bison.y" + { + (yyval.expr) = hash_expr_alloc(&(yyloc), (yyvsp[-4].val), true, (yyvsp[-2].val), (yyvsp[-1].val), NFT_HASH_JENKINS); + (yyval.expr)->hash.expr = (yyvsp[-6].expr); + } +#line 15105 "parser_bison.c" + break; + + case 1000: /* hash_expr: "jhash" expr "mod" "number" offset_opt close_scope_hash */ +#line 5396 "parser_bison.y" + { + (yyval.expr) = hash_expr_alloc(&(yyloc), (yyvsp[-2].val), false, 0, (yyvsp[-1].val), NFT_HASH_JENKINS); + (yyval.expr)->hash.expr = (yyvsp[-4].expr); + } +#line 15114 "parser_bison.c" + break; + + case 1001: /* hash_expr: "symhash" "mod" "number" offset_opt close_scope_hash */ +#line 5401 "parser_bison.y" + { + (yyval.expr) = hash_expr_alloc(&(yyloc), (yyvsp[-2].val), false, 0, (yyvsp[-1].val), NFT_HASH_SYM); + } +#line 15122 "parser_bison.c" + break; + + case 1002: /* nf_key_proto: "ip" close_scope_ip */ +#line 5406 "parser_bison.y" + { (yyval.val) = NFPROTO_IPV4; } +#line 15128 "parser_bison.c" + break; + + case 1003: /* nf_key_proto: "ip6" close_scope_ip6 */ +#line 5407 "parser_bison.y" + { (yyval.val) = NFPROTO_IPV6; } +#line 15134 "parser_bison.c" + break; + + case 1004: /* rt_expr: "rt" rt_key close_scope_rt */ +#line 5411 "parser_bison.y" + { + (yyval.expr) = rt_expr_alloc(&(yyloc), (yyvsp[-1].val), true); + } +#line 15142 "parser_bison.c" + break; + + case 1005: /* rt_expr: "rt" nf_key_proto rt_key close_scope_rt */ +#line 5415 "parser_bison.y" + { + enum nft_rt_keys rtk = (yyvsp[-1].val); + + switch ((yyvsp[-2].val)) { + case NFPROTO_IPV4: + break; + case NFPROTO_IPV6: + if ((yyvsp[-1].val) == NFT_RT_NEXTHOP4) + rtk = NFT_RT_NEXTHOP6; + break; + default: + YYERROR; + break; + } + + (yyval.expr) = rt_expr_alloc(&(yyloc), rtk, false); + } +#line 15164 "parser_bison.c" + break; + + case 1006: /* rt_key: "classid" */ +#line 5434 "parser_bison.y" + { (yyval.val) = NFT_RT_CLASSID; } +#line 15170 "parser_bison.c" + break; + + case 1007: /* rt_key: "nexthop" */ +#line 5435 "parser_bison.y" + { (yyval.val) = NFT_RT_NEXTHOP4; } +#line 15176 "parser_bison.c" + break; + + case 1008: /* rt_key: "mtu" */ +#line 5436 "parser_bison.y" + { (yyval.val) = NFT_RT_TCPMSS; } +#line 15182 "parser_bison.c" + break; + + case 1009: /* rt_key: "ipsec" close_scope_ipsec */ +#line 5437 "parser_bison.y" + { (yyval.val) = NFT_RT_XFRM; } +#line 15188 "parser_bison.c" + break; + + case 1010: /* ct_expr: "ct" ct_key close_scope_ct */ +#line 5441 "parser_bison.y" + { + (yyval.expr) = ct_expr_alloc(&(yyloc), (yyvsp[-1].val), -1); + } +#line 15196 "parser_bison.c" + break; + + case 1011: /* ct_expr: "ct" ct_dir ct_key_dir close_scope_ct */ +#line 5445 "parser_bison.y" + { + (yyval.expr) = ct_expr_alloc(&(yyloc), (yyvsp[-1].val), (yyvsp[-2].val)); + } +#line 15204 "parser_bison.c" + break; + + case 1012: /* ct_expr: "ct" ct_dir ct_key_proto_field close_scope_ct */ +#line 5449 "parser_bison.y" + { + (yyval.expr) = ct_expr_alloc(&(yyloc), (yyvsp[-1].val), (yyvsp[-2].val)); + } +#line 15212 "parser_bison.c" + break; + + case 1013: /* ct_dir: "original" */ +#line 5454 "parser_bison.y" + { (yyval.val) = IP_CT_DIR_ORIGINAL; } +#line 15218 "parser_bison.c" + break; + + case 1014: /* ct_dir: "reply" */ +#line 5455 "parser_bison.y" + { (yyval.val) = IP_CT_DIR_REPLY; } +#line 15224 "parser_bison.c" + break; + + case 1015: /* ct_key: "l3proto" */ +#line 5458 "parser_bison.y" + { (yyval.val) = NFT_CT_L3PROTOCOL; } +#line 15230 "parser_bison.c" + break; + + case 1016: /* ct_key: "protocol" */ +#line 5459 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTOCOL; } +#line 15236 "parser_bison.c" + break; + + case 1017: /* ct_key: "mark" */ +#line 5460 "parser_bison.y" + { (yyval.val) = NFT_CT_MARK; } +#line 15242 "parser_bison.c" + break; + + case 1018: /* ct_key: "state" */ +#line 5461 "parser_bison.y" + { (yyval.val) = NFT_CT_STATE; } +#line 15248 "parser_bison.c" + break; + + case 1019: /* ct_key: "direction" */ +#line 5462 "parser_bison.y" + { (yyval.val) = NFT_CT_DIRECTION; } +#line 15254 "parser_bison.c" + break; + + case 1020: /* ct_key: "status" */ +#line 5463 "parser_bison.y" + { (yyval.val) = NFT_CT_STATUS; } +#line 15260 "parser_bison.c" + break; + + case 1021: /* ct_key: "expiration" */ +#line 5464 "parser_bison.y" + { (yyval.val) = NFT_CT_EXPIRATION; } +#line 15266 "parser_bison.c" + break; + + case 1022: /* ct_key: "helper" */ +#line 5465 "parser_bison.y" + { (yyval.val) = NFT_CT_HELPER; } +#line 15272 "parser_bison.c" + break; + + case 1023: /* ct_key: "saddr" */ +#line 5466 "parser_bison.y" + { (yyval.val) = NFT_CT_SRC; } +#line 15278 "parser_bison.c" + break; + + case 1024: /* ct_key: "daddr" */ +#line 5467 "parser_bison.y" + { (yyval.val) = NFT_CT_DST; } +#line 15284 "parser_bison.c" + break; + + case 1025: /* ct_key: "proto-src" */ +#line 5468 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTO_SRC; } +#line 15290 "parser_bison.c" + break; + + case 1026: /* ct_key: "proto-dst" */ +#line 5469 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTO_DST; } +#line 15296 "parser_bison.c" + break; + + case 1027: /* ct_key: "label" */ +#line 5470 "parser_bison.y" + { (yyval.val) = NFT_CT_LABELS; } +#line 15302 "parser_bison.c" + break; + + case 1028: /* ct_key: "event" */ +#line 5471 "parser_bison.y" + { (yyval.val) = NFT_CT_EVENTMASK; } +#line 15308 "parser_bison.c" + break; + + case 1029: /* ct_key: "secmark" close_scope_secmark */ +#line 5472 "parser_bison.y" + { (yyval.val) = NFT_CT_SECMARK; } +#line 15314 "parser_bison.c" + break; + + case 1030: /* ct_key: "id" */ +#line 5473 "parser_bison.y" + { (yyval.val) = NFT_CT_ID; } +#line 15320 "parser_bison.c" + break; + + case 1032: /* ct_key_dir: "saddr" */ +#line 5477 "parser_bison.y" + { (yyval.val) = NFT_CT_SRC; } +#line 15326 "parser_bison.c" + break; + + case 1033: /* ct_key_dir: "daddr" */ +#line 5478 "parser_bison.y" + { (yyval.val) = NFT_CT_DST; } +#line 15332 "parser_bison.c" + break; + + case 1034: /* ct_key_dir: "l3proto" */ +#line 5479 "parser_bison.y" + { (yyval.val) = NFT_CT_L3PROTOCOL; } +#line 15338 "parser_bison.c" + break; + + case 1035: /* ct_key_dir: "protocol" */ +#line 5480 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTOCOL; } +#line 15344 "parser_bison.c" + break; + + case 1036: /* ct_key_dir: "proto-src" */ +#line 5481 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTO_SRC; } +#line 15350 "parser_bison.c" + break; + + case 1037: /* ct_key_dir: "proto-dst" */ +#line 5482 "parser_bison.y" + { (yyval.val) = NFT_CT_PROTO_DST; } +#line 15356 "parser_bison.c" + break; + + case 1039: /* ct_key_proto_field: "ip" "saddr" close_scope_ip */ +#line 5486 "parser_bison.y" + { (yyval.val) = NFT_CT_SRC_IP; } +#line 15362 "parser_bison.c" + break; + + case 1040: /* ct_key_proto_field: "ip" "daddr" close_scope_ip */ +#line 5487 "parser_bison.y" + { (yyval.val) = NFT_CT_DST_IP; } +#line 15368 "parser_bison.c" + break; + + case 1041: /* ct_key_proto_field: "ip6" "saddr" close_scope_ip6 */ +#line 5488 "parser_bison.y" + { (yyval.val) = NFT_CT_SRC_IP6; } +#line 15374 "parser_bison.c" + break; + + case 1042: /* ct_key_proto_field: "ip6" "daddr" close_scope_ip6 */ +#line 5489 "parser_bison.y" + { (yyval.val) = NFT_CT_DST_IP6; } +#line 15380 "parser_bison.c" + break; + + case 1043: /* ct_key_dir_optional: "bytes" */ +#line 5492 "parser_bison.y" + { (yyval.val) = NFT_CT_BYTES; } +#line 15386 "parser_bison.c" + break; + + case 1044: /* ct_key_dir_optional: "packets" */ +#line 5493 "parser_bison.y" + { (yyval.val) = NFT_CT_PKTS; } +#line 15392 "parser_bison.c" + break; + + case 1045: /* ct_key_dir_optional: "avgpkt" */ +#line 5494 "parser_bison.y" + { (yyval.val) = NFT_CT_AVGPKT; } +#line 15398 "parser_bison.c" + break; + + case 1046: /* ct_key_dir_optional: "zone" */ +#line 5495 "parser_bison.y" + { (yyval.val) = NFT_CT_ZONE; } +#line 15404 "parser_bison.c" + break; + + case 1049: /* list_stmt_expr: symbol_stmt_expr "comma" symbol_stmt_expr */ +#line 5503 "parser_bison.y" + { + (yyval.expr) = list_expr_alloc(&(yyloc)); + compound_expr_add((yyval.expr), (yyvsp[-2].expr)); + compound_expr_add((yyval.expr), (yyvsp[0].expr)); + } +#line 15414 "parser_bison.c" + break; + + case 1050: /* list_stmt_expr: list_stmt_expr "comma" symbol_stmt_expr */ +#line 5509 "parser_bison.y" + { + (yyvsp[-2].expr)->location = (yyloc); + compound_expr_add((yyvsp[-2].expr), (yyvsp[0].expr)); + (yyval.expr) = (yyvsp[-2].expr); + } +#line 15424 "parser_bison.c" + break; + + case 1051: /* ct_stmt: "ct" ct_key "set" stmt_expr close_scope_ct */ +#line 5517 "parser_bison.y" + { + switch ((yyvsp[-3].val)) { + case NFT_CT_HELPER: + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_CT_HELPER; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + break; + default: + (yyval.stmt) = ct_stmt_alloc(&(yyloc), (yyvsp[-3].val), -1, (yyvsp[-1].expr)); + break; + } + } +#line 15441 "parser_bison.c" + break; + + case 1052: /* ct_stmt: "ct" "timeout" "set" stmt_expr close_scope_ct */ +#line 5530 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_CT_TIMEOUT; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + + } +#line 15452 "parser_bison.c" + break; + + case 1053: /* ct_stmt: "ct" "expectation" "set" stmt_expr close_scope_ct */ +#line 5537 "parser_bison.y" + { + (yyval.stmt) = objref_stmt_alloc(&(yyloc)); + (yyval.stmt)->objref.type = NFT_OBJECT_CT_EXPECT; + (yyval.stmt)->objref.expr = (yyvsp[-1].expr); + } +#line 15462 "parser_bison.c" + break; + + case 1054: /* ct_stmt: "ct" ct_dir ct_key_dir_optional "set" stmt_expr close_scope_ct */ +#line 5543 "parser_bison.y" + { + (yyval.stmt) = ct_stmt_alloc(&(yyloc), (yyvsp[-3].val), (yyvsp[-4].val), (yyvsp[-1].expr)); + } +#line 15470 "parser_bison.c" + break; + + case 1055: /* payload_stmt: payload_expr "set" stmt_expr */ +#line 5549 "parser_bison.y" + { + if ((yyvsp[-2].expr)->etype == EXPR_EXTHDR) + (yyval.stmt) = exthdr_stmt_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + else + (yyval.stmt) = payload_stmt_alloc(&(yyloc), (yyvsp[-2].expr), (yyvsp[0].expr)); + } +#line 15481 "parser_bison.c" + break; + + case 1078: /* payload_raw_expr: "@" payload_base_spec "comma" "number" "comma" "number" close_scope_at */ +#line 5582 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), NULL, 0); + payload_init_raw((yyval.expr), (yyvsp[-5].val), (yyvsp[-3].val), (yyvsp[-1].val)); + (yyval.expr)->byteorder = BYTEORDER_BIG_ENDIAN; + (yyval.expr)->payload.is_raw = true; + } +#line 15492 "parser_bison.c" + break; + + case 1079: /* payload_base_spec: "ll" */ +#line 5590 "parser_bison.y" + { (yyval.val) = PROTO_BASE_LL_HDR; } +#line 15498 "parser_bison.c" + break; + + case 1080: /* payload_base_spec: "nh" */ +#line 5591 "parser_bison.y" + { (yyval.val) = PROTO_BASE_NETWORK_HDR; } +#line 15504 "parser_bison.c" + break; + + case 1081: /* payload_base_spec: "th" close_scope_th */ +#line 5592 "parser_bison.y" + { (yyval.val) = PROTO_BASE_TRANSPORT_HDR; } +#line 15510 "parser_bison.c" + break; + + case 1082: /* payload_base_spec: "string" */ +#line 5594 "parser_bison.y" + { + if (!strcmp((yyvsp[0].string), "ih")) { + (yyval.val) = PROTO_BASE_INNER_HDR; + } else { + erec_queue(error(&(yylsp[0]), "unknown raw payload base"), state->msgs); + xfree((yyvsp[0].string)); + YYERROR; + } + xfree((yyvsp[0].string)); + } +#line 15525 "parser_bison.c" + break; + + case 1083: /* eth_hdr_expr: "ether" eth_hdr_field close_scope_eth */ +#line 5607 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_eth, (yyvsp[-1].val)); + } +#line 15533 "parser_bison.c" + break; + + case 1084: /* eth_hdr_field: "saddr" */ +#line 5612 "parser_bison.y" + { (yyval.val) = ETHHDR_SADDR; } +#line 15539 "parser_bison.c" + break; + + case 1085: /* eth_hdr_field: "daddr" */ +#line 5613 "parser_bison.y" + { (yyval.val) = ETHHDR_DADDR; } +#line 15545 "parser_bison.c" + break; + + case 1086: /* eth_hdr_field: "type" close_scope_type */ +#line 5614 "parser_bison.y" + { (yyval.val) = ETHHDR_TYPE; } +#line 15551 "parser_bison.c" + break; + + case 1087: /* vlan_hdr_expr: "vlan" vlan_hdr_field close_scope_vlan */ +#line 5618 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_vlan, (yyvsp[-1].val)); + } +#line 15559 "parser_bison.c" + break; + + case 1088: /* vlan_hdr_field: "id" */ +#line 5623 "parser_bison.y" + { (yyval.val) = VLANHDR_VID; } +#line 15565 "parser_bison.c" + break; + + case 1089: /* vlan_hdr_field: "cfi" */ +#line 5624 "parser_bison.y" + { (yyval.val) = VLANHDR_CFI; } +#line 15571 "parser_bison.c" + break; + + case 1090: /* vlan_hdr_field: "dei" */ +#line 5625 "parser_bison.y" + { (yyval.val) = VLANHDR_DEI; } +#line 15577 "parser_bison.c" + break; + + case 1091: /* vlan_hdr_field: "pcp" */ +#line 5626 "parser_bison.y" + { (yyval.val) = VLANHDR_PCP; } +#line 15583 "parser_bison.c" + break; + + case 1092: /* vlan_hdr_field: "type" close_scope_type */ +#line 5627 "parser_bison.y" + { (yyval.val) = VLANHDR_TYPE; } +#line 15589 "parser_bison.c" + break; + + case 1093: /* arp_hdr_expr: "arp" arp_hdr_field close_scope_arp */ +#line 5631 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_arp, (yyvsp[-1].val)); + } +#line 15597 "parser_bison.c" + break; + + case 1094: /* arp_hdr_field: "htype" */ +#line 5636 "parser_bison.y" + { (yyval.val) = ARPHDR_HRD; } +#line 15603 "parser_bison.c" + break; + + case 1095: /* arp_hdr_field: "ptype" */ +#line 5637 "parser_bison.y" + { (yyval.val) = ARPHDR_PRO; } +#line 15609 "parser_bison.c" + break; + + case 1096: /* arp_hdr_field: "hlen" */ +#line 5638 "parser_bison.y" + { (yyval.val) = ARPHDR_HLN; } +#line 15615 "parser_bison.c" + break; + + case 1097: /* arp_hdr_field: "plen" */ +#line 5639 "parser_bison.y" + { (yyval.val) = ARPHDR_PLN; } +#line 15621 "parser_bison.c" + break; + + case 1098: /* arp_hdr_field: "operation" */ +#line 5640 "parser_bison.y" + { (yyval.val) = ARPHDR_OP; } +#line 15627 "parser_bison.c" + break; + + case 1099: /* arp_hdr_field: "saddr" "ether" close_scope_eth */ +#line 5641 "parser_bison.y" + { (yyval.val) = ARPHDR_SADDR_ETHER; } +#line 15633 "parser_bison.c" + break; + + case 1100: /* arp_hdr_field: "daddr" "ether" close_scope_eth */ +#line 5642 "parser_bison.y" + { (yyval.val) = ARPHDR_DADDR_ETHER; } +#line 15639 "parser_bison.c" + break; + + case 1101: /* arp_hdr_field: "saddr" "ip" close_scope_ip */ +#line 5643 "parser_bison.y" + { (yyval.val) = ARPHDR_SADDR_IP; } +#line 15645 "parser_bison.c" + break; + + case 1102: /* arp_hdr_field: "daddr" "ip" close_scope_ip */ +#line 5644 "parser_bison.y" + { (yyval.val) = ARPHDR_DADDR_IP; } +#line 15651 "parser_bison.c" + break; + + case 1103: /* ip_hdr_expr: "ip" ip_hdr_field close_scope_ip */ +#line 5648 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_ip, (yyvsp[-1].val)); + } +#line 15659 "parser_bison.c" + break; + + case 1104: /* ip_hdr_expr: "ip" "option" ip_option_type ip_option_field close_scope_ip */ +#line 5652 "parser_bison.y" + { + (yyval.expr) = ipopt_expr_alloc(&(yyloc), (yyvsp[-2].val), (yyvsp[-1].val)); + if (!(yyval.expr)) { + erec_queue(error(&(yylsp[-4]), "unknown ip option type/field"), state->msgs); + YYERROR; + } + } +#line 15671 "parser_bison.c" + break; + + case 1105: /* ip_hdr_expr: "ip" "option" ip_option_type close_scope_ip */ +#line 5660 "parser_bison.y" + { + (yyval.expr) = ipopt_expr_alloc(&(yyloc), (yyvsp[-1].val), IPOPT_FIELD_TYPE); + (yyval.expr)->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } +#line 15680 "parser_bison.c" + break; + + case 1106: /* ip_hdr_field: "version" */ +#line 5666 "parser_bison.y" + { (yyval.val) = IPHDR_VERSION; } +#line 15686 "parser_bison.c" + break; + + case 1107: /* ip_hdr_field: "hdrlength" */ +#line 5667 "parser_bison.y" + { (yyval.val) = IPHDR_HDRLENGTH; } +#line 15692 "parser_bison.c" + break; + + case 1108: /* ip_hdr_field: "dscp" */ +#line 5668 "parser_bison.y" + { (yyval.val) = IPHDR_DSCP; } +#line 15698 "parser_bison.c" + break; + + case 1109: /* ip_hdr_field: "ecn" */ +#line 5669 "parser_bison.y" + { (yyval.val) = IPHDR_ECN; } +#line 15704 "parser_bison.c" + break; + + case 1110: /* ip_hdr_field: "length" */ +#line 5670 "parser_bison.y" + { (yyval.val) = IPHDR_LENGTH; } +#line 15710 "parser_bison.c" + break; + + case 1111: /* ip_hdr_field: "id" */ +#line 5671 "parser_bison.y" + { (yyval.val) = IPHDR_ID; } +#line 15716 "parser_bison.c" + break; + + case 1112: /* ip_hdr_field: "frag-off" */ +#line 5672 "parser_bison.y" + { (yyval.val) = IPHDR_FRAG_OFF; } +#line 15722 "parser_bison.c" + break; + + case 1113: /* ip_hdr_field: "ttl" */ +#line 5673 "parser_bison.y" + { (yyval.val) = IPHDR_TTL; } +#line 15728 "parser_bison.c" + break; + + case 1114: /* ip_hdr_field: "protocol" */ +#line 5674 "parser_bison.y" + { (yyval.val) = IPHDR_PROTOCOL; } +#line 15734 "parser_bison.c" + break; + + case 1115: /* ip_hdr_field: "checksum" */ +#line 5675 "parser_bison.y" + { (yyval.val) = IPHDR_CHECKSUM; } +#line 15740 "parser_bison.c" + break; + + case 1116: /* ip_hdr_field: "saddr" */ +#line 5676 "parser_bison.y" + { (yyval.val) = IPHDR_SADDR; } +#line 15746 "parser_bison.c" + break; + + case 1117: /* ip_hdr_field: "daddr" */ +#line 5677 "parser_bison.y" + { (yyval.val) = IPHDR_DADDR; } +#line 15752 "parser_bison.c" + break; + + case 1118: /* ip_option_type: "lsrr" */ +#line 5680 "parser_bison.y" + { (yyval.val) = IPOPT_LSRR; } +#line 15758 "parser_bison.c" + break; + + case 1119: /* ip_option_type: "rr" */ +#line 5681 "parser_bison.y" + { (yyval.val) = IPOPT_RR; } +#line 15764 "parser_bison.c" + break; + + case 1120: /* ip_option_type: "ssrr" */ +#line 5682 "parser_bison.y" + { (yyval.val) = IPOPT_SSRR; } +#line 15770 "parser_bison.c" + break; + + case 1121: /* ip_option_type: "ra" */ +#line 5683 "parser_bison.y" + { (yyval.val) = IPOPT_RA; } +#line 15776 "parser_bison.c" + break; + + case 1122: /* ip_option_field: "type" close_scope_type */ +#line 5686 "parser_bison.y" + { (yyval.val) = IPOPT_FIELD_TYPE; } +#line 15782 "parser_bison.c" + break; + + case 1123: /* ip_option_field: "length" */ +#line 5687 "parser_bison.y" + { (yyval.val) = IPOPT_FIELD_LENGTH; } +#line 15788 "parser_bison.c" + break; + + case 1124: /* ip_option_field: "value" */ +#line 5688 "parser_bison.y" + { (yyval.val) = IPOPT_FIELD_VALUE; } +#line 15794 "parser_bison.c" + break; + + case 1125: /* ip_option_field: "ptr" */ +#line 5689 "parser_bison.y" + { (yyval.val) = IPOPT_FIELD_PTR; } +#line 15800 "parser_bison.c" + break; + + case 1126: /* ip_option_field: "addr" */ +#line 5690 "parser_bison.y" + { (yyval.val) = IPOPT_FIELD_ADDR_0; } +#line 15806 "parser_bison.c" + break; + + case 1127: /* icmp_hdr_expr: "icmp" icmp_hdr_field close_scope_icmp */ +#line 5694 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_icmp, (yyvsp[-1].val)); + } +#line 15814 "parser_bison.c" + break; + + case 1128: /* icmp_hdr_field: "type" close_scope_type */ +#line 5699 "parser_bison.y" + { (yyval.val) = ICMPHDR_TYPE; } +#line 15820 "parser_bison.c" + break; + + case 1129: /* icmp_hdr_field: "code" */ +#line 5700 "parser_bison.y" + { (yyval.val) = ICMPHDR_CODE; } +#line 15826 "parser_bison.c" + break; + + case 1130: /* icmp_hdr_field: "checksum" */ +#line 5701 "parser_bison.y" + { (yyval.val) = ICMPHDR_CHECKSUM; } +#line 15832 "parser_bison.c" + break; + + case 1131: /* icmp_hdr_field: "id" */ +#line 5702 "parser_bison.y" + { (yyval.val) = ICMPHDR_ID; } +#line 15838 "parser_bison.c" + break; + + case 1132: /* icmp_hdr_field: "seq" */ +#line 5703 "parser_bison.y" + { (yyval.val) = ICMPHDR_SEQ; } +#line 15844 "parser_bison.c" + break; + + case 1133: /* icmp_hdr_field: "gateway" */ +#line 5704 "parser_bison.y" + { (yyval.val) = ICMPHDR_GATEWAY; } +#line 15850 "parser_bison.c" + break; + + case 1134: /* icmp_hdr_field: "mtu" */ +#line 5705 "parser_bison.y" + { (yyval.val) = ICMPHDR_MTU; } +#line 15856 "parser_bison.c" + break; + + case 1135: /* igmp_hdr_expr: "igmp" igmp_hdr_field close_scope_igmp */ +#line 5709 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_igmp, (yyvsp[-1].val)); + } +#line 15864 "parser_bison.c" + break; + + case 1136: /* igmp_hdr_field: "type" close_scope_type */ +#line 5714 "parser_bison.y" + { (yyval.val) = IGMPHDR_TYPE; } +#line 15870 "parser_bison.c" + break; + + case 1137: /* igmp_hdr_field: "checksum" */ +#line 5715 "parser_bison.y" + { (yyval.val) = IGMPHDR_CHECKSUM; } +#line 15876 "parser_bison.c" + break; + + case 1138: /* igmp_hdr_field: "mrt" */ +#line 5716 "parser_bison.y" + { (yyval.val) = IGMPHDR_MRT; } +#line 15882 "parser_bison.c" + break; + + case 1139: /* igmp_hdr_field: "group" */ +#line 5717 "parser_bison.y" + { (yyval.val) = IGMPHDR_GROUP; } +#line 15888 "parser_bison.c" + break; + + case 1140: /* ip6_hdr_expr: "ip6" ip6_hdr_field close_scope_ip6 */ +#line 5721 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_ip6, (yyvsp[-1].val)); + } +#line 15896 "parser_bison.c" + break; + + case 1141: /* ip6_hdr_field: "version" */ +#line 5726 "parser_bison.y" + { (yyval.val) = IP6HDR_VERSION; } +#line 15902 "parser_bison.c" + break; + + case 1142: /* ip6_hdr_field: "dscp" */ +#line 5727 "parser_bison.y" + { (yyval.val) = IP6HDR_DSCP; } +#line 15908 "parser_bison.c" + break; + + case 1143: /* ip6_hdr_field: "ecn" */ +#line 5728 "parser_bison.y" + { (yyval.val) = IP6HDR_ECN; } +#line 15914 "parser_bison.c" + break; + + case 1144: /* ip6_hdr_field: "flowlabel" */ +#line 5729 "parser_bison.y" + { (yyval.val) = IP6HDR_FLOWLABEL; } +#line 15920 "parser_bison.c" + break; + + case 1145: /* ip6_hdr_field: "length" */ +#line 5730 "parser_bison.y" + { (yyval.val) = IP6HDR_LENGTH; } +#line 15926 "parser_bison.c" + break; + + case 1146: /* ip6_hdr_field: "nexthdr" */ +#line 5731 "parser_bison.y" + { (yyval.val) = IP6HDR_NEXTHDR; } +#line 15932 "parser_bison.c" + break; + + case 1147: /* ip6_hdr_field: "hoplimit" */ +#line 5732 "parser_bison.y" + { (yyval.val) = IP6HDR_HOPLIMIT; } +#line 15938 "parser_bison.c" + break; + + case 1148: /* ip6_hdr_field: "saddr" */ +#line 5733 "parser_bison.y" + { (yyval.val) = IP6HDR_SADDR; } +#line 15944 "parser_bison.c" + break; + + case 1149: /* ip6_hdr_field: "daddr" */ +#line 5734 "parser_bison.y" + { (yyval.val) = IP6HDR_DADDR; } +#line 15950 "parser_bison.c" + break; + + case 1150: /* icmp6_hdr_expr: "icmpv6" icmp6_hdr_field close_scope_icmp */ +#line 5737 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_icmp6, (yyvsp[-1].val)); + } +#line 15958 "parser_bison.c" + break; + + case 1151: /* icmp6_hdr_field: "type" close_scope_type */ +#line 5742 "parser_bison.y" + { (yyval.val) = ICMP6HDR_TYPE; } +#line 15964 "parser_bison.c" + break; + + case 1152: /* icmp6_hdr_field: "code" */ +#line 5743 "parser_bison.y" + { (yyval.val) = ICMP6HDR_CODE; } +#line 15970 "parser_bison.c" + break; + + case 1153: /* icmp6_hdr_field: "checksum" */ +#line 5744 "parser_bison.y" + { (yyval.val) = ICMP6HDR_CHECKSUM; } +#line 15976 "parser_bison.c" + break; + + case 1154: /* icmp6_hdr_field: "param-problem" */ +#line 5745 "parser_bison.y" + { (yyval.val) = ICMP6HDR_PPTR; } +#line 15982 "parser_bison.c" + break; + + case 1155: /* icmp6_hdr_field: "mtu" */ +#line 5746 "parser_bison.y" + { (yyval.val) = ICMP6HDR_MTU; } +#line 15988 "parser_bison.c" + break; + + case 1156: /* icmp6_hdr_field: "id" */ +#line 5747 "parser_bison.y" + { (yyval.val) = ICMP6HDR_ID; } +#line 15994 "parser_bison.c" + break; + + case 1157: /* icmp6_hdr_field: "seq" */ +#line 5748 "parser_bison.y" + { (yyval.val) = ICMP6HDR_SEQ; } +#line 16000 "parser_bison.c" + break; + + case 1158: /* icmp6_hdr_field: "max-delay" */ +#line 5749 "parser_bison.y" + { (yyval.val) = ICMP6HDR_MAXDELAY; } +#line 16006 "parser_bison.c" + break; + + case 1159: /* icmp6_hdr_field: "taddr" */ +#line 5750 "parser_bison.y" + { (yyval.val) = ICMP6HDR_TADDR; } +#line 16012 "parser_bison.c" + break; + + case 1160: /* icmp6_hdr_field: "daddr" */ +#line 5751 "parser_bison.y" + { (yyval.val) = ICMP6HDR_DADDR; } +#line 16018 "parser_bison.c" + break; + + case 1161: /* auth_hdr_expr: "ah" auth_hdr_field close_scope_ah */ +#line 5755 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_ah, (yyvsp[-1].val)); + } +#line 16026 "parser_bison.c" + break; + + case 1162: /* auth_hdr_field: "nexthdr" */ +#line 5760 "parser_bison.y" + { (yyval.val) = AHHDR_NEXTHDR; } +#line 16032 "parser_bison.c" + break; + + case 1163: /* auth_hdr_field: "hdrlength" */ +#line 5761 "parser_bison.y" + { (yyval.val) = AHHDR_HDRLENGTH; } +#line 16038 "parser_bison.c" + break; + + case 1164: /* auth_hdr_field: "reserved" */ +#line 5762 "parser_bison.y" + { (yyval.val) = AHHDR_RESERVED; } +#line 16044 "parser_bison.c" + break; + + case 1165: /* auth_hdr_field: "spi" */ +#line 5763 "parser_bison.y" + { (yyval.val) = AHHDR_SPI; } +#line 16050 "parser_bison.c" + break; + + case 1166: /* auth_hdr_field: "seq" */ +#line 5764 "parser_bison.y" + { (yyval.val) = AHHDR_SEQUENCE; } +#line 16056 "parser_bison.c" + break; + + case 1167: /* esp_hdr_expr: "esp" esp_hdr_field close_scope_esp */ +#line 5768 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_esp, (yyvsp[-1].val)); + } +#line 16064 "parser_bison.c" + break; + + case 1168: /* esp_hdr_field: "spi" */ +#line 5773 "parser_bison.y" + { (yyval.val) = ESPHDR_SPI; } +#line 16070 "parser_bison.c" + break; + + case 1169: /* esp_hdr_field: "seq" */ +#line 5774 "parser_bison.y" + { (yyval.val) = ESPHDR_SEQUENCE; } +#line 16076 "parser_bison.c" + break; + + case 1170: /* comp_hdr_expr: "comp" comp_hdr_field close_scope_comp */ +#line 5778 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_comp, (yyvsp[-1].val)); + } +#line 16084 "parser_bison.c" + break; + + case 1171: /* comp_hdr_field: "nexthdr" */ +#line 5783 "parser_bison.y" + { (yyval.val) = COMPHDR_NEXTHDR; } +#line 16090 "parser_bison.c" + break; + + case 1172: /* comp_hdr_field: "flags" */ +#line 5784 "parser_bison.y" + { (yyval.val) = COMPHDR_FLAGS; } +#line 16096 "parser_bison.c" + break; + + case 1173: /* comp_hdr_field: "cpi" */ +#line 5785 "parser_bison.y" + { (yyval.val) = COMPHDR_CPI; } +#line 16102 "parser_bison.c" + break; + + case 1174: /* udp_hdr_expr: "udp" udp_hdr_field close_scope_udp */ +#line 5789 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_udp, (yyvsp[-1].val)); + } +#line 16110 "parser_bison.c" + break; + + case 1175: /* udp_hdr_field: "sport" */ +#line 5794 "parser_bison.y" + { (yyval.val) = UDPHDR_SPORT; } +#line 16116 "parser_bison.c" + break; + + case 1176: /* udp_hdr_field: "dport" */ +#line 5795 "parser_bison.y" + { (yyval.val) = UDPHDR_DPORT; } +#line 16122 "parser_bison.c" + break; + + case 1177: /* udp_hdr_field: "length" */ +#line 5796 "parser_bison.y" + { (yyval.val) = UDPHDR_LENGTH; } +#line 16128 "parser_bison.c" + break; + + case 1178: /* udp_hdr_field: "checksum" */ +#line 5797 "parser_bison.y" + { (yyval.val) = UDPHDR_CHECKSUM; } +#line 16134 "parser_bison.c" + break; + + case 1179: /* udplite_hdr_expr: "udplite" udplite_hdr_field close_scope_udplite */ +#line 5801 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_udplite, (yyvsp[-1].val)); + } +#line 16142 "parser_bison.c" + break; + + case 1180: /* udplite_hdr_field: "sport" */ +#line 5806 "parser_bison.y" + { (yyval.val) = UDPHDR_SPORT; } +#line 16148 "parser_bison.c" + break; + + case 1181: /* udplite_hdr_field: "dport" */ +#line 5807 "parser_bison.y" + { (yyval.val) = UDPHDR_DPORT; } +#line 16154 "parser_bison.c" + break; + + case 1182: /* udplite_hdr_field: "csumcov" */ +#line 5808 "parser_bison.y" + { (yyval.val) = UDPHDR_LENGTH; } +#line 16160 "parser_bison.c" + break; + + case 1183: /* udplite_hdr_field: "checksum" */ +#line 5809 "parser_bison.y" + { (yyval.val) = UDPHDR_CHECKSUM; } +#line 16166 "parser_bison.c" + break; + + case 1184: /* tcp_hdr_expr: "tcp" tcp_hdr_field */ +#line 5813 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_tcp, (yyvsp[0].val)); + } +#line 16174 "parser_bison.c" + break; + + case 1185: /* tcp_hdr_expr: "tcp" "option" tcp_hdr_option_type */ +#line 5817 "parser_bison.y" + { + (yyval.expr) = tcpopt_expr_alloc(&(yyloc), (yyvsp[0].val), TCPOPT_COMMON_KIND); + (yyval.expr)->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } +#line 16183 "parser_bison.c" + break; + + case 1186: /* tcp_hdr_expr: "tcp" "option" tcp_hdr_option_kind_and_field */ +#line 5822 "parser_bison.y" + { + (yyval.expr) = tcpopt_expr_alloc(&(yyloc), (yyvsp[0].tcp_kind_field).kind, (yyvsp[0].tcp_kind_field).field); + } +#line 16191 "parser_bison.c" + break; + + case 1187: /* tcp_hdr_expr: "tcp" "option" "@" close_scope_at tcp_hdr_option_type "comma" "number" "comma" "number" */ +#line 5826 "parser_bison.y" + { + (yyval.expr) = tcpopt_expr_alloc(&(yyloc), (yyvsp[-4].val), 0); + tcpopt_init_raw((yyval.expr), (yyvsp[-4].val), (yyvsp[-2].val), (yyvsp[0].val), 0); + } +#line 16200 "parser_bison.c" + break; + + case 1207: /* vxlan_hdr_expr: "vxlan" vxlan_hdr_field */ +#line 5858 "parser_bison.y" + { + struct expr *expr; + + expr = payload_expr_alloc(&(yyloc), &proto_vxlan, (yyvsp[0].val)); + expr->payload.inner_desc = &proto_vxlan; + (yyval.expr) = expr; + } +#line 16212 "parser_bison.c" + break; + + case 1208: /* vxlan_hdr_expr: "vxlan" inner_expr */ +#line 5866 "parser_bison.y" + { + (yyval.expr) = (yyvsp[0].expr); + (yyval.expr)->location = (yyloc); + (yyval.expr)->payload.inner_desc = &proto_vxlan; + } +#line 16222 "parser_bison.c" + break; + + case 1209: /* vxlan_hdr_field: "vni" */ +#line 5873 "parser_bison.y" + { (yyval.val) = VXLANHDR_VNI; } +#line 16228 "parser_bison.c" + break; + + case 1210: /* vxlan_hdr_field: "flags" */ +#line 5874 "parser_bison.y" + { (yyval.val) = VXLANHDR_FLAGS; } +#line 16234 "parser_bison.c" + break; + + case 1211: /* geneve_hdr_expr: "geneve" geneve_hdr_field */ +#line 5878 "parser_bison.y" + { + struct expr *expr; + + expr = payload_expr_alloc(&(yyloc), &proto_geneve, (yyvsp[0].val)); + expr->payload.inner_desc = &proto_geneve; + (yyval.expr) = expr; + } +#line 16246 "parser_bison.c" + break; + + case 1212: /* geneve_hdr_expr: "geneve" inner_expr */ +#line 5886 "parser_bison.y" + { + (yyval.expr) = (yyvsp[0].expr); + (yyval.expr)->location = (yyloc); + (yyval.expr)->payload.inner_desc = &proto_geneve; + } +#line 16256 "parser_bison.c" + break; + + case 1213: /* geneve_hdr_field: "vni" */ +#line 5893 "parser_bison.y" + { (yyval.val) = GNVHDR_VNI; } +#line 16262 "parser_bison.c" + break; + + case 1214: /* geneve_hdr_field: "type" */ +#line 5894 "parser_bison.y" + { (yyval.val) = GNVHDR_TYPE; } +#line 16268 "parser_bison.c" + break; + + case 1215: /* gre_hdr_expr: "gre" gre_hdr_field close_scope_gre */ +#line 5898 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_gre, (yyvsp[-1].val)); + } +#line 16276 "parser_bison.c" + break; + + case 1216: /* gre_hdr_expr: "gre" close_scope_gre inner_inet_expr */ +#line 5902 "parser_bison.y" + { + (yyval.expr) = (yyvsp[0].expr); + (yyval.expr)->payload.inner_desc = &proto_gre; + } +#line 16285 "parser_bison.c" + break; + + case 1217: /* gre_hdr_field: "version" */ +#line 5908 "parser_bison.y" + { (yyval.val) = GREHDR_VERSION; } +#line 16291 "parser_bison.c" + break; + + case 1218: /* gre_hdr_field: "flags" */ +#line 5909 "parser_bison.y" + { (yyval.val) = GREHDR_FLAGS; } +#line 16297 "parser_bison.c" + break; + + case 1219: /* gre_hdr_field: "protocol" */ +#line 5910 "parser_bison.y" + { (yyval.val) = GREHDR_PROTOCOL; } +#line 16303 "parser_bison.c" + break; + + case 1220: /* gretap_hdr_expr: "gretap" close_scope_gre inner_expr */ +#line 5914 "parser_bison.y" + { + (yyval.expr) = (yyvsp[0].expr); + (yyval.expr)->payload.inner_desc = &proto_gretap; + } +#line 16312 "parser_bison.c" + break; + + case 1221: /* optstrip_stmt: "reset" "tcp" "option" tcp_hdr_option_type close_scope_tcp */ +#line 5921 "parser_bison.y" + { + (yyval.stmt) = optstrip_stmt_alloc(&(yyloc), tcpopt_expr_alloc(&(yyloc), + (yyvsp[-1].val), TCPOPT_COMMON_KIND)); + } +#line 16321 "parser_bison.c" + break; + + case 1222: /* tcp_hdr_field: "sport" */ +#line 5927 "parser_bison.y" + { (yyval.val) = TCPHDR_SPORT; } +#line 16327 "parser_bison.c" + break; + + case 1223: /* tcp_hdr_field: "dport" */ +#line 5928 "parser_bison.y" + { (yyval.val) = TCPHDR_DPORT; } +#line 16333 "parser_bison.c" + break; + + case 1224: /* tcp_hdr_field: "seq" */ +#line 5929 "parser_bison.y" + { (yyval.val) = TCPHDR_SEQ; } +#line 16339 "parser_bison.c" + break; + + case 1225: /* tcp_hdr_field: "ackseq" */ +#line 5930 "parser_bison.y" + { (yyval.val) = TCPHDR_ACKSEQ; } +#line 16345 "parser_bison.c" + break; + + case 1226: /* tcp_hdr_field: "doff" */ +#line 5931 "parser_bison.y" + { (yyval.val) = TCPHDR_DOFF; } +#line 16351 "parser_bison.c" + break; + + case 1227: /* tcp_hdr_field: "reserved" */ +#line 5932 "parser_bison.y" + { (yyval.val) = TCPHDR_RESERVED; } +#line 16357 "parser_bison.c" + break; + + case 1228: /* tcp_hdr_field: "flags" */ +#line 5933 "parser_bison.y" + { (yyval.val) = TCPHDR_FLAGS; } +#line 16363 "parser_bison.c" + break; + + case 1229: /* tcp_hdr_field: "window" */ +#line 5934 "parser_bison.y" + { (yyval.val) = TCPHDR_WINDOW; } +#line 16369 "parser_bison.c" + break; + + case 1230: /* tcp_hdr_field: "checksum" */ +#line 5935 "parser_bison.y" + { (yyval.val) = TCPHDR_CHECKSUM; } +#line 16375 "parser_bison.c" + break; + + case 1231: /* tcp_hdr_field: "urgptr" */ +#line 5936 "parser_bison.y" + { (yyval.val) = TCPHDR_URGPTR; } +#line 16381 "parser_bison.c" + break; + + case 1232: /* tcp_hdr_option_kind_and_field: "mss" tcpopt_field_maxseg */ +#line 5940 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = (yyvsp[0].val) }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16390 "parser_bison.c" + break; + + case 1233: /* tcp_hdr_option_kind_and_field: tcp_hdr_option_sack tcpopt_field_sack */ +#line 5945 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = (yyvsp[-1].val), .field = (yyvsp[0].val) }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16399 "parser_bison.c" + break; + + case 1234: /* tcp_hdr_option_kind_and_field: "window" tcpopt_field_window */ +#line 5950 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = (yyvsp[0].val) }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16408 "parser_bison.c" + break; + + case 1235: /* tcp_hdr_option_kind_and_field: "timestamp" tcpopt_field_tsopt */ +#line 5955 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = (yyvsp[0].val) }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16417 "parser_bison.c" + break; + + case 1236: /* tcp_hdr_option_kind_and_field: tcp_hdr_option_type "length" */ +#line 5960 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = (yyvsp[-1].val), .field = TCPOPT_COMMON_LENGTH }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16426 "parser_bison.c" + break; + + case 1237: /* tcp_hdr_option_kind_and_field: "mptcp" tcpopt_field_mptcp */ +#line 5965 "parser_bison.y" + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = (yyvsp[0].val) }; + (yyval.tcp_kind_field) = kind_field; + } +#line 16435 "parser_bison.c" + break; + + case 1238: /* tcp_hdr_option_sack: "sack" */ +#line 5971 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK; } +#line 16441 "parser_bison.c" + break; + + case 1239: /* tcp_hdr_option_sack: "sack0" */ +#line 5972 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK; } +#line 16447 "parser_bison.c" + break; + + case 1240: /* tcp_hdr_option_sack: "sack1" */ +#line 5973 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK1; } +#line 16453 "parser_bison.c" + break; + + case 1241: /* tcp_hdr_option_sack: "sack2" */ +#line 5974 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK2; } +#line 16459 "parser_bison.c" + break; + + case 1242: /* tcp_hdr_option_sack: "sack3" */ +#line 5975 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK3; } +#line 16465 "parser_bison.c" + break; + + case 1243: /* tcp_hdr_option_type: "echo" */ +#line 5978 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_ECHO; } +#line 16471 "parser_bison.c" + break; + + case 1244: /* tcp_hdr_option_type: "eol" */ +#line 5979 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_EOL; } +#line 16477 "parser_bison.c" + break; + + case 1245: /* tcp_hdr_option_type: "fastopen" */ +#line 5980 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_FASTOPEN; } +#line 16483 "parser_bison.c" + break; + + case 1246: /* tcp_hdr_option_type: "md5sig" */ +#line 5981 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_MD5SIG; } +#line 16489 "parser_bison.c" + break; + + case 1247: /* tcp_hdr_option_type: "mptcp" */ +#line 5982 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_MPTCP; } +#line 16495 "parser_bison.c" + break; + + case 1248: /* tcp_hdr_option_type: "mss" */ +#line 5983 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_MAXSEG; } +#line 16501 "parser_bison.c" + break; + + case 1249: /* tcp_hdr_option_type: "nop" */ +#line 5984 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_NOP; } +#line 16507 "parser_bison.c" + break; + + case 1250: /* tcp_hdr_option_type: "sack-permitted" */ +#line 5985 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_SACK_PERMITTED; } +#line 16513 "parser_bison.c" + break; + + case 1251: /* tcp_hdr_option_type: "timestamp" */ +#line 5986 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_TIMESTAMP; } +#line 16519 "parser_bison.c" + break; + + case 1252: /* tcp_hdr_option_type: "window" */ +#line 5987 "parser_bison.y" + { (yyval.val) = TCPOPT_KIND_WINDOW; } +#line 16525 "parser_bison.c" + break; + + case 1253: /* tcp_hdr_option_type: tcp_hdr_option_sack */ +#line 5988 "parser_bison.y" + { (yyval.val) = (yyvsp[0].val); } +#line 16531 "parser_bison.c" + break; + + case 1254: /* tcp_hdr_option_type: "number" */ +#line 5989 "parser_bison.y" + { + if ((yyvsp[0].val) > 255) { + erec_queue(error(&(yylsp[0]), "value too large"), state->msgs); + YYERROR; + } + (yyval.val) = (yyvsp[0].val); + } +#line 16543 "parser_bison.c" + break; + + case 1255: /* tcpopt_field_sack: "left" */ +#line 5998 "parser_bison.y" + { (yyval.val) = TCPOPT_SACK_LEFT; } +#line 16549 "parser_bison.c" + break; + + case 1256: /* tcpopt_field_sack: "right" */ +#line 5999 "parser_bison.y" + { (yyval.val) = TCPOPT_SACK_RIGHT; } +#line 16555 "parser_bison.c" + break; + + case 1257: /* tcpopt_field_window: "count" */ +#line 6002 "parser_bison.y" + { (yyval.val) = TCPOPT_WINDOW_COUNT; } +#line 16561 "parser_bison.c" + break; + + case 1258: /* tcpopt_field_tsopt: "tsval" */ +#line 6005 "parser_bison.y" + { (yyval.val) = TCPOPT_TS_TSVAL; } +#line 16567 "parser_bison.c" + break; + + case 1259: /* tcpopt_field_tsopt: "tsecr" */ +#line 6006 "parser_bison.y" + { (yyval.val) = TCPOPT_TS_TSECR; } +#line 16573 "parser_bison.c" + break; + + case 1260: /* tcpopt_field_maxseg: "size" */ +#line 6009 "parser_bison.y" + { (yyval.val) = TCPOPT_MAXSEG_SIZE; } +#line 16579 "parser_bison.c" + break; + + case 1261: /* tcpopt_field_mptcp: "subtype" */ +#line 6012 "parser_bison.y" + { (yyval.val) = TCPOPT_MPTCP_SUBTYPE; } +#line 16585 "parser_bison.c" + break; + + case 1262: /* dccp_hdr_expr: "dccp" dccp_hdr_field close_scope_dccp */ +#line 6016 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_dccp, (yyvsp[-1].val)); + } +#line 16593 "parser_bison.c" + break; + + case 1263: /* dccp_hdr_expr: "dccp" "option" "number" close_scope_dccp */ +#line 6020 "parser_bison.y" + { + if ((yyvsp[-1].val) > DCCPOPT_TYPE_MAX) { + erec_queue(error(&(yylsp[-3]), "value too large"), + state->msgs); + YYERROR; + } + (yyval.expr) = dccpopt_expr_alloc(&(yyloc), (yyvsp[-1].val)); + } +#line 16606 "parser_bison.c" + break; + + case 1264: /* dccp_hdr_field: "sport" */ +#line 6030 "parser_bison.y" + { (yyval.val) = DCCPHDR_SPORT; } +#line 16612 "parser_bison.c" + break; + + case 1265: /* dccp_hdr_field: "dport" */ +#line 6031 "parser_bison.y" + { (yyval.val) = DCCPHDR_DPORT; } +#line 16618 "parser_bison.c" + break; + + case 1266: /* dccp_hdr_field: "type" close_scope_type */ +#line 6032 "parser_bison.y" + { (yyval.val) = DCCPHDR_TYPE; } +#line 16624 "parser_bison.c" + break; + + case 1267: /* sctp_chunk_type: "data" */ +#line 6035 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_DATA; } +#line 16630 "parser_bison.c" + break; + + case 1268: /* sctp_chunk_type: "init" */ +#line 6036 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_INIT; } +#line 16636 "parser_bison.c" + break; + + case 1269: /* sctp_chunk_type: "init-ack" */ +#line 6037 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_INIT_ACK; } +#line 16642 "parser_bison.c" + break; + + case 1270: /* sctp_chunk_type: "sack" */ +#line 6038 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_SACK; } +#line 16648 "parser_bison.c" + break; + + case 1271: /* sctp_chunk_type: "heartbeat" */ +#line 6039 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_HEARTBEAT; } +#line 16654 "parser_bison.c" + break; + + case 1272: /* sctp_chunk_type: "heartbeat-ack" */ +#line 6040 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_HEARTBEAT_ACK; } +#line 16660 "parser_bison.c" + break; + + case 1273: /* sctp_chunk_type: "abort" */ +#line 6041 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_ABORT; } +#line 16666 "parser_bison.c" + break; + + case 1274: /* sctp_chunk_type: "shutdown" */ +#line 6042 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_SHUTDOWN; } +#line 16672 "parser_bison.c" + break; + + case 1275: /* sctp_chunk_type: "shutdown-ack" */ +#line 6043 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_SHUTDOWN_ACK; } +#line 16678 "parser_bison.c" + break; + + case 1276: /* sctp_chunk_type: "error" */ +#line 6044 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_ERROR; } +#line 16684 "parser_bison.c" + break; + + case 1277: /* sctp_chunk_type: "cookie-echo" */ +#line 6045 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_COOKIE_ECHO; } +#line 16690 "parser_bison.c" + break; + + case 1278: /* sctp_chunk_type: "cookie-ack" */ +#line 6046 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_COOKIE_ACK; } +#line 16696 "parser_bison.c" + break; + + case 1279: /* sctp_chunk_type: "ecne" */ +#line 6047 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_ECNE; } +#line 16702 "parser_bison.c" + break; + + case 1280: /* sctp_chunk_type: "cwr" */ +#line 6048 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_CWR; } +#line 16708 "parser_bison.c" + break; + + case 1281: /* sctp_chunk_type: "shutdown-complete" */ +#line 6049 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE; } +#line 16714 "parser_bison.c" + break; + + case 1282: /* sctp_chunk_type: "asconf-ack" */ +#line 6050 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_ASCONF_ACK; } +#line 16720 "parser_bison.c" + break; + + case 1283: /* sctp_chunk_type: "forward-tsn" */ +#line 6051 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_FORWARD_TSN; } +#line 16726 "parser_bison.c" + break; + + case 1284: /* sctp_chunk_type: "asconf" */ +#line 6052 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_TYPE_ASCONF; } +#line 16732 "parser_bison.c" + break; + + case 1285: /* sctp_chunk_common_field: "type" close_scope_type */ +#line 6055 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_COMMON_TYPE; } +#line 16738 "parser_bison.c" + break; + + case 1286: /* sctp_chunk_common_field: "flags" */ +#line 6056 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_COMMON_FLAGS; } +#line 16744 "parser_bison.c" + break; + + case 1287: /* sctp_chunk_common_field: "length" */ +#line 6057 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_COMMON_LENGTH; } +#line 16750 "parser_bison.c" + break; + + case 1288: /* sctp_chunk_data_field: "tsn" */ +#line 6060 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_DATA_TSN; } +#line 16756 "parser_bison.c" + break; + + case 1289: /* sctp_chunk_data_field: "stream" */ +#line 6061 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_DATA_STREAM; } +#line 16762 "parser_bison.c" + break; + + case 1290: /* sctp_chunk_data_field: "ssn" */ +#line 6062 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_DATA_SSN; } +#line 16768 "parser_bison.c" + break; + + case 1291: /* sctp_chunk_data_field: "ppid" */ +#line 6063 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_DATA_PPID; } +#line 16774 "parser_bison.c" + break; + + case 1292: /* sctp_chunk_init_field: "init-tag" */ +#line 6066 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_INIT_TAG; } +#line 16780 "parser_bison.c" + break; + + case 1293: /* sctp_chunk_init_field: "a-rwnd" */ +#line 6067 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_INIT_RWND; } +#line 16786 "parser_bison.c" + break; + + case 1294: /* sctp_chunk_init_field: "num-outbound-streams" */ +#line 6068 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_INIT_OSTREAMS; } +#line 16792 "parser_bison.c" + break; + + case 1295: /* sctp_chunk_init_field: "num-inbound-streams" */ +#line 6069 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_INIT_ISTREAMS; } +#line 16798 "parser_bison.c" + break; + + case 1296: /* sctp_chunk_init_field: "initial-tsn" */ +#line 6070 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_INIT_TSN; } +#line 16804 "parser_bison.c" + break; + + case 1297: /* sctp_chunk_sack_field: "cum-tsn-ack" */ +#line 6073 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_SACK_CTSN_ACK; } +#line 16810 "parser_bison.c" + break; + + case 1298: /* sctp_chunk_sack_field: "a-rwnd" */ +#line 6074 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_SACK_RWND; } +#line 16816 "parser_bison.c" + break; + + case 1299: /* sctp_chunk_sack_field: "num-gap-ack-blocks" */ +#line 6075 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_SACK_GACK_BLOCKS; } +#line 16822 "parser_bison.c" + break; + + case 1300: /* sctp_chunk_sack_field: "num-dup-tsns" */ +#line 6076 "parser_bison.y" + { (yyval.val) = SCTP_CHUNK_SACK_DUP_TSNS; } +#line 16828 "parser_bison.c" + break; + + case 1301: /* sctp_chunk_alloc: sctp_chunk_type */ +#line 6080 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), (yyvsp[0].val), SCTP_CHUNK_COMMON_TYPE); + (yyval.expr)->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } +#line 16837 "parser_bison.c" + break; + + case 1302: /* sctp_chunk_alloc: sctp_chunk_type sctp_chunk_common_field */ +#line 6085 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), (yyvsp[-1].val), (yyvsp[0].val)); + } +#line 16845 "parser_bison.c" + break; + + case 1303: /* sctp_chunk_alloc: "data" sctp_chunk_data_field */ +#line 6089 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_DATA, (yyvsp[0].val)); + } +#line 16853 "parser_bison.c" + break; + + case 1304: /* sctp_chunk_alloc: "init" sctp_chunk_init_field */ +#line 6093 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_INIT, (yyvsp[0].val)); + } +#line 16861 "parser_bison.c" + break; + + case 1305: /* sctp_chunk_alloc: "init-ack" sctp_chunk_init_field */ +#line 6097 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_INIT_ACK, (yyvsp[0].val)); + } +#line 16869 "parser_bison.c" + break; + + case 1306: /* sctp_chunk_alloc: "sack" sctp_chunk_sack_field */ +#line 6101 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_SACK, (yyvsp[0].val)); + } +#line 16877 "parser_bison.c" + break; + + case 1307: /* sctp_chunk_alloc: "shutdown" "cum-tsn-ack" */ +#line 6105 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_SHUTDOWN, + SCTP_CHUNK_SHUTDOWN_CTSN_ACK); + } +#line 16886 "parser_bison.c" + break; + + case 1308: /* sctp_chunk_alloc: "ecne" "lowest-tsn" */ +#line 6110 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_ECNE, + SCTP_CHUNK_ECNE_CWR_MIN_TSN); + } +#line 16895 "parser_bison.c" + break; + + case 1309: /* sctp_chunk_alloc: "cwr" "lowest-tsn" */ +#line 6115 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_CWR, + SCTP_CHUNK_ECNE_CWR_MIN_TSN); + } +#line 16904 "parser_bison.c" + break; + + case 1310: /* sctp_chunk_alloc: "asconf-ack" "seqno" */ +#line 6120 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_ASCONF_ACK, + SCTP_CHUNK_ASCONF_SEQNO); + } +#line 16913 "parser_bison.c" + break; + + case 1311: /* sctp_chunk_alloc: "forward-tsn" "new-cum-tsn" */ +#line 6125 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_FORWARD_TSN, + SCTP_CHUNK_FORWARD_TSN_NCTSN); + } +#line 16922 "parser_bison.c" + break; + + case 1312: /* sctp_chunk_alloc: "asconf" "seqno" */ +#line 6130 "parser_bison.y" + { + (yyval.expr) = sctp_chunk_expr_alloc(&(yyloc), SCTP_CHUNK_TYPE_ASCONF, + SCTP_CHUNK_ASCONF_SEQNO); + } +#line 16931 "parser_bison.c" + break; + + case 1313: /* sctp_hdr_expr: "sctp" sctp_hdr_field close_scope_sctp */ +#line 6137 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_sctp, (yyvsp[-1].val)); + } +#line 16939 "parser_bison.c" + break; + + case 1314: /* sctp_hdr_expr: "sctp" "chunk" sctp_chunk_alloc close_scope_sctp_chunk close_scope_sctp */ +#line 6141 "parser_bison.y" + { + (yyval.expr) = (yyvsp[-2].expr); + } +#line 16947 "parser_bison.c" + break; + + case 1315: /* sctp_hdr_field: "sport" */ +#line 6146 "parser_bison.y" + { (yyval.val) = SCTPHDR_SPORT; } +#line 16953 "parser_bison.c" + break; + + case 1316: /* sctp_hdr_field: "dport" */ +#line 6147 "parser_bison.y" + { (yyval.val) = SCTPHDR_DPORT; } +#line 16959 "parser_bison.c" + break; + + case 1317: /* sctp_hdr_field: "vtag" */ +#line 6148 "parser_bison.y" + { (yyval.val) = SCTPHDR_VTAG; } +#line 16965 "parser_bison.c" + break; + + case 1318: /* sctp_hdr_field: "checksum" */ +#line 6149 "parser_bison.y" + { (yyval.val) = SCTPHDR_CHECKSUM; } +#line 16971 "parser_bison.c" + break; + + case 1319: /* th_hdr_expr: "th" th_hdr_field close_scope_th */ +#line 6153 "parser_bison.y" + { + (yyval.expr) = payload_expr_alloc(&(yyloc), &proto_th, (yyvsp[-1].val)); + if ((yyval.expr)) + (yyval.expr)->payload.is_raw = true; + } +#line 16981 "parser_bison.c" + break; + + case 1320: /* th_hdr_field: "sport" */ +#line 6160 "parser_bison.y" + { (yyval.val) = THDR_SPORT; } +#line 16987 "parser_bison.c" + break; + + case 1321: /* th_hdr_field: "dport" */ +#line 6161 "parser_bison.y" + { (yyval.val) = THDR_DPORT; } +#line 16993 "parser_bison.c" + break; + + case 1330: /* hbh_hdr_expr: "hbh" hbh_hdr_field close_scope_hbh */ +#line 6175 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_hbh, (yyvsp[-1].val)); + } +#line 17001 "parser_bison.c" + break; + + case 1331: /* hbh_hdr_field: "nexthdr" */ +#line 6180 "parser_bison.y" + { (yyval.val) = HBHHDR_NEXTHDR; } +#line 17007 "parser_bison.c" + break; + + case 1332: /* hbh_hdr_field: "hdrlength" */ +#line 6181 "parser_bison.y" + { (yyval.val) = HBHHDR_HDRLENGTH; } +#line 17013 "parser_bison.c" + break; + + case 1333: /* rt_hdr_expr: "rt" rt_hdr_field close_scope_rt */ +#line 6185 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_rt, (yyvsp[-1].val)); + } +#line 17021 "parser_bison.c" + break; + + case 1334: /* rt_hdr_field: "nexthdr" */ +#line 6190 "parser_bison.y" + { (yyval.val) = RTHDR_NEXTHDR; } +#line 17027 "parser_bison.c" + break; + + case 1335: /* rt_hdr_field: "hdrlength" */ +#line 6191 "parser_bison.y" + { (yyval.val) = RTHDR_HDRLENGTH; } +#line 17033 "parser_bison.c" + break; + + case 1336: /* rt_hdr_field: "type" close_scope_type */ +#line 6192 "parser_bison.y" + { (yyval.val) = RTHDR_TYPE; } +#line 17039 "parser_bison.c" + break; + + case 1337: /* rt_hdr_field: "seg-left" */ +#line 6193 "parser_bison.y" + { (yyval.val) = RTHDR_SEG_LEFT; } +#line 17045 "parser_bison.c" + break; + + case 1338: /* rt0_hdr_expr: "rt0" rt0_hdr_field close_scope_rt */ +#line 6197 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_rt0, (yyvsp[-1].val)); + } +#line 17053 "parser_bison.c" + break; + + case 1339: /* rt0_hdr_field: "addr" '[' "number" ']' */ +#line 6203 "parser_bison.y" + { + (yyval.val) = RT0HDR_ADDR_1 + (yyvsp[-1].val) - 1; + } +#line 17061 "parser_bison.c" + break; + + case 1340: /* rt2_hdr_expr: "rt2" rt2_hdr_field close_scope_rt */ +#line 6209 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_rt2, (yyvsp[-1].val)); + } +#line 17069 "parser_bison.c" + break; + + case 1341: /* rt2_hdr_field: "addr" */ +#line 6214 "parser_bison.y" + { (yyval.val) = RT2HDR_ADDR; } +#line 17075 "parser_bison.c" + break; + + case 1342: /* rt4_hdr_expr: "srh" rt4_hdr_field close_scope_rt */ +#line 6218 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_rt4, (yyvsp[-1].val)); + } +#line 17083 "parser_bison.c" + break; + + case 1343: /* rt4_hdr_field: "last-entry" */ +#line 6223 "parser_bison.y" + { (yyval.val) = RT4HDR_LASTENT; } +#line 17089 "parser_bison.c" + break; + + case 1344: /* rt4_hdr_field: "flags" */ +#line 6224 "parser_bison.y" + { (yyval.val) = RT4HDR_FLAGS; } +#line 17095 "parser_bison.c" + break; + + case 1345: /* rt4_hdr_field: "tag" */ +#line 6225 "parser_bison.y" + { (yyval.val) = RT4HDR_TAG; } +#line 17101 "parser_bison.c" + break; + + case 1346: /* rt4_hdr_field: "sid" '[' "number" ']' */ +#line 6227 "parser_bison.y" + { + (yyval.val) = RT4HDR_SID_1 + (yyvsp[-1].val) - 1; + } +#line 17109 "parser_bison.c" + break; + + case 1347: /* frag_hdr_expr: "frag" frag_hdr_field close_scope_frag */ +#line 6233 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_frag, (yyvsp[-1].val)); + } +#line 17117 "parser_bison.c" + break; + + case 1348: /* frag_hdr_field: "nexthdr" */ +#line 6238 "parser_bison.y" + { (yyval.val) = FRAGHDR_NEXTHDR; } +#line 17123 "parser_bison.c" + break; + + case 1349: /* frag_hdr_field: "reserved" */ +#line 6239 "parser_bison.y" + { (yyval.val) = FRAGHDR_RESERVED; } +#line 17129 "parser_bison.c" + break; + + case 1350: /* frag_hdr_field: "frag-off" */ +#line 6240 "parser_bison.y" + { (yyval.val) = FRAGHDR_FRAG_OFF; } +#line 17135 "parser_bison.c" + break; + + case 1351: /* frag_hdr_field: "reserved2" */ +#line 6241 "parser_bison.y" + { (yyval.val) = FRAGHDR_RESERVED2; } +#line 17141 "parser_bison.c" + break; + + case 1352: /* frag_hdr_field: "more-fragments" */ +#line 6242 "parser_bison.y" + { (yyval.val) = FRAGHDR_MFRAGS; } +#line 17147 "parser_bison.c" + break; + + case 1353: /* frag_hdr_field: "id" */ +#line 6243 "parser_bison.y" + { (yyval.val) = FRAGHDR_ID; } +#line 17153 "parser_bison.c" + break; + + case 1354: /* dst_hdr_expr: "dst" dst_hdr_field close_scope_dst */ +#line 6247 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_dst, (yyvsp[-1].val)); + } +#line 17161 "parser_bison.c" + break; + + case 1355: /* dst_hdr_field: "nexthdr" */ +#line 6252 "parser_bison.y" + { (yyval.val) = DSTHDR_NEXTHDR; } +#line 17167 "parser_bison.c" + break; + + case 1356: /* dst_hdr_field: "hdrlength" */ +#line 6253 "parser_bison.y" + { (yyval.val) = DSTHDR_HDRLENGTH; } +#line 17173 "parser_bison.c" + break; + + case 1357: /* mh_hdr_expr: "mh" mh_hdr_field close_scope_mh */ +#line 6257 "parser_bison.y" + { + (yyval.expr) = exthdr_expr_alloc(&(yyloc), &exthdr_mh, (yyvsp[-1].val)); + } +#line 17181 "parser_bison.c" + break; + + case 1358: /* mh_hdr_field: "nexthdr" */ +#line 6262 "parser_bison.y" + { (yyval.val) = MHHDR_NEXTHDR; } +#line 17187 "parser_bison.c" + break; + + case 1359: /* mh_hdr_field: "hdrlength" */ +#line 6263 "parser_bison.y" + { (yyval.val) = MHHDR_HDRLENGTH; } +#line 17193 "parser_bison.c" + break; + + case 1360: /* mh_hdr_field: "type" close_scope_type */ +#line 6264 "parser_bison.y" + { (yyval.val) = MHHDR_TYPE; } +#line 17199 "parser_bison.c" + break; + + case 1361: /* mh_hdr_field: "reserved" */ +#line 6265 "parser_bison.y" + { (yyval.val) = MHHDR_RESERVED; } +#line 17205 "parser_bison.c" + break; + + case 1362: /* mh_hdr_field: "checksum" */ +#line 6266 "parser_bison.y" + { (yyval.val) = MHHDR_CHECKSUM; } +#line 17211 "parser_bison.c" + break; + + case 1363: /* exthdr_exists_expr: "exthdr" exthdr_key */ +#line 6270 "parser_bison.y" + { + const struct exthdr_desc *desc; + + desc = exthdr_find_proto((yyvsp[0].val)); + + /* Assume that NEXTHDR template is always + * the first one in list of templates. + */ + (yyval.expr) = exthdr_expr_alloc(&(yyloc), desc, 1); + (yyval.expr)->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } +#line 17227 "parser_bison.c" + break; + + case 1364: /* exthdr_key: "hbh" close_scope_hbh */ +#line 6283 "parser_bison.y" + { (yyval.val) = IPPROTO_HOPOPTS; } +#line 17233 "parser_bison.c" + break; + + case 1365: /* exthdr_key: "rt" close_scope_rt */ +#line 6284 "parser_bison.y" + { (yyval.val) = IPPROTO_ROUTING; } +#line 17239 "parser_bison.c" + break; + + case 1366: /* exthdr_key: "frag" close_scope_frag */ +#line 6285 "parser_bison.y" + { (yyval.val) = IPPROTO_FRAGMENT; } +#line 17245 "parser_bison.c" + break; + + case 1367: /* exthdr_key: "dst" close_scope_dst */ +#line 6286 "parser_bison.y" + { (yyval.val) = IPPROTO_DSTOPTS; } +#line 17251 "parser_bison.c" + break; + + case 1368: /* exthdr_key: "mh" close_scope_mh */ +#line 6287 "parser_bison.y" + { (yyval.val) = IPPROTO_MH; } +#line 17257 "parser_bison.c" + break; + + +#line 17261 "parser_bison.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + *++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. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + { + yypcontext_t yyctx + = {yyssp, yytoken, &yylloc}; + char const *yymsgp = YY_("syntax error"); + int yysyntax_error_status; + yysyntax_error_status = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + if (yysyntax_error_status == 0) + yymsgp = yymsg; + else if (yysyntax_error_status == -1) + { + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = YY_CAST (char *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, yymsg_alloc))); + if (yymsg) + { + yysyntax_error_status + = yysyntax_error (&yymsg_alloc, &yymsg, &yyctx); + yymsgp = yymsg; + } + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + yysyntax_error_status = YYENOMEM; + } + } + yyerror (&yylloc, nft, scanner, state, yymsgp); + if (yysyntax_error_status == YYENOMEM) + goto yyexhaustedlab; + } + } + + yyerror_range[1] = yylloc; + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= TOKEN_EOF) + { + /* Return failure if at end of input. */ + if (yychar == TOKEN_EOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval, &yylloc, nft, scanner, state); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + + /* Do not reclaim the symbols of the rule whose 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. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + yyerror_range[1] = *yylsp; + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp, yylsp, nft, scanner, state); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + yyerror_range[2] = yylloc; + ++yylsp; + YYLLOC_DEFAULT (*yylsp, yyerror_range, 2); + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (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; + + +#if 1 +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (&yylloc, nft, scanner, state, YY_("memory exhausted")); + yyresult = 2; + goto yyreturn; +#endif + + +/*-------------------------------------------------------. +| yyreturn -- parsing is finished, clean up and return. | +`-------------------------------------------------------*/ +yyreturn: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval, &yylloc, nft, scanner, state); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp, yylsp, nft, scanner, state); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + return yyresult; +} + +#line 6290 "parser_bison.y" + diff --git a/src/parser_bison.h b/src/parser_bison.h new file mode 100644 index 0000000..9e754a2 --- /dev/null +++ b/src/parser_bison.h @@ -0,0 +1,845 @@ +/* A Bison parser, made by GNU Bison 3.7.5. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 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 3 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, see <http://www.gnu.org/licenses/>. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_NFT_PARSER_BISON_H_INCLUDED +# define YY_NFT_PARSER_BISON_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 1 +#endif +#if YYDEBUG +extern int nft_debug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + TOKEN_EOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + JUNK = 258, /* "junk" */ + NEWLINE = 259, /* "newline" */ + COLON = 260, /* "colon" */ + SEMICOLON = 261, /* "semicolon" */ + COMMA = 262, /* "comma" */ + DOT = 263, /* "." */ + EQ = 264, /* "==" */ + NEQ = 265, /* "!=" */ + LT = 266, /* "<" */ + GT = 267, /* ">" */ + GTE = 268, /* ">=" */ + LTE = 269, /* "<=" */ + LSHIFT = 270, /* "<<" */ + RSHIFT = 271, /* ">>" */ + AMPERSAND = 272, /* "&" */ + CARET = 273, /* "^" */ + NOT = 274, /* "!" */ + SLASH = 275, /* "/" */ + ASTERISK = 276, /* "*" */ + DASH = 277, /* "-" */ + AT = 278, /* "@" */ + VMAP = 279, /* "vmap" */ + PLUS = 280, /* "+" */ + INCLUDE = 281, /* "include" */ + DEFINE = 282, /* "define" */ + REDEFINE = 283, /* "redefine" */ + UNDEFINE = 284, /* "undefine" */ + FIB = 285, /* "fib" */ + SOCKET = 286, /* "socket" */ + TRANSPARENT = 287, /* "transparent" */ + WILDCARD = 288, /* "wildcard" */ + CGROUPV2 = 289, /* "cgroupv2" */ + TPROXY = 290, /* "tproxy" */ + OSF = 291, /* "osf" */ + SYNPROXY = 292, /* "synproxy" */ + MSS = 293, /* "mss" */ + WSCALE = 294, /* "wscale" */ + TYPEOF = 295, /* "typeof" */ + HOOK = 296, /* "hook" */ + HOOKS = 297, /* "hooks" */ + DEVICE = 298, /* "device" */ + DEVICES = 299, /* "devices" */ + TABLE = 300, /* "table" */ + TABLES = 301, /* "tables" */ + CHAIN = 302, /* "chain" */ + CHAINS = 303, /* "chains" */ + RULE = 304, /* "rule" */ + RULES = 305, /* "rules" */ + SETS = 306, /* "sets" */ + SET = 307, /* "set" */ + ELEMENT = 308, /* "element" */ + MAP = 309, /* "map" */ + MAPS = 310, /* "maps" */ + FLOWTABLE = 311, /* "flowtable" */ + HANDLE = 312, /* "handle" */ + RULESET = 313, /* "ruleset" */ + TRACE = 314, /* "trace" */ + INET = 315, /* "inet" */ + NETDEV = 316, /* "netdev" */ + ADD = 317, /* "add" */ + UPDATE = 318, /* "update" */ + REPLACE = 319, /* "replace" */ + CREATE = 320, /* "create" */ + INSERT = 321, /* "insert" */ + DELETE = 322, /* "delete" */ + GET = 323, /* "get" */ + LIST = 324, /* "list" */ + RESET = 325, /* "reset" */ + FLUSH = 326, /* "flush" */ + RENAME = 327, /* "rename" */ + DESCRIBE = 328, /* "describe" */ + IMPORT = 329, /* "import" */ + EXPORT = 330, /* "export" */ + DESTROY = 331, /* "destroy" */ + MONITOR = 332, /* "monitor" */ + ALL = 333, /* "all" */ + ACCEPT = 334, /* "accept" */ + DROP = 335, /* "drop" */ + CONTINUE = 336, /* "continue" */ + JUMP = 337, /* "jump" */ + GOTO = 338, /* "goto" */ + RETURN = 339, /* "return" */ + TO = 340, /* "to" */ + CONSTANT = 341, /* "constant" */ + INTERVAL = 342, /* "interval" */ + DYNAMIC = 343, /* "dynamic" */ + AUTOMERGE = 344, /* "auto-merge" */ + TIMEOUT = 345, /* "timeout" */ + GC_INTERVAL = 346, /* "gc-interval" */ + ELEMENTS = 347, /* "elements" */ + EXPIRES = 348, /* "expires" */ + POLICY = 349, /* "policy" */ + MEMORY = 350, /* "memory" */ + PERFORMANCE = 351, /* "performance" */ + SIZE = 352, /* "size" */ + FLOW = 353, /* "flow" */ + OFFLOAD = 354, /* "offload" */ + METER = 355, /* "meter" */ + METERS = 356, /* "meters" */ + FLOWTABLES = 357, /* "flowtables" */ + NUM = 358, /* "number" */ + STRING = 359, /* "string" */ + QUOTED_STRING = 360, /* "quoted string" */ + ASTERISK_STRING = 361, /* "string with a trailing asterisk" */ + LL_HDR = 362, /* "ll" */ + NETWORK_HDR = 363, /* "nh" */ + TRANSPORT_HDR = 364, /* "th" */ + BRIDGE = 365, /* "bridge" */ + ETHER = 366, /* "ether" */ + SADDR = 367, /* "saddr" */ + DADDR = 368, /* "daddr" */ + TYPE = 369, /* "type" */ + VLAN = 370, /* "vlan" */ + ID = 371, /* "id" */ + CFI = 372, /* "cfi" */ + DEI = 373, /* "dei" */ + PCP = 374, /* "pcp" */ + ARP = 375, /* "arp" */ + HTYPE = 376, /* "htype" */ + PTYPE = 377, /* "ptype" */ + HLEN = 378, /* "hlen" */ + PLEN = 379, /* "plen" */ + OPERATION = 380, /* "operation" */ + IP = 381, /* "ip" */ + HDRVERSION = 382, /* "version" */ + HDRLENGTH = 383, /* "hdrlength" */ + DSCP = 384, /* "dscp" */ + ECN = 385, /* "ecn" */ + LENGTH = 386, /* "length" */ + FRAG_OFF = 387, /* "frag-off" */ + TTL = 388, /* "ttl" */ + PROTOCOL = 389, /* "protocol" */ + CHECKSUM = 390, /* "checksum" */ + PTR = 391, /* "ptr" */ + VALUE = 392, /* "value" */ + LSRR = 393, /* "lsrr" */ + RR = 394, /* "rr" */ + SSRR = 395, /* "ssrr" */ + RA = 396, /* "ra" */ + ICMP = 397, /* "icmp" */ + CODE = 398, /* "code" */ + SEQUENCE = 399, /* "seq" */ + GATEWAY = 400, /* "gateway" */ + MTU = 401, /* "mtu" */ + IGMP = 402, /* "igmp" */ + MRT = 403, /* "mrt" */ + OPTIONS = 404, /* "options" */ + IP6 = 405, /* "ip6" */ + PRIORITY = 406, /* "priority" */ + FLOWLABEL = 407, /* "flowlabel" */ + NEXTHDR = 408, /* "nexthdr" */ + HOPLIMIT = 409, /* "hoplimit" */ + ICMP6 = 410, /* "icmpv6" */ + PPTR = 411, /* "param-problem" */ + MAXDELAY = 412, /* "max-delay" */ + TADDR = 413, /* "taddr" */ + AH = 414, /* "ah" */ + RESERVED = 415, /* "reserved" */ + SPI = 416, /* "spi" */ + ESP = 417, /* "esp" */ + COMP = 418, /* "comp" */ + FLAGS = 419, /* "flags" */ + CPI = 420, /* "cpi" */ + PORT = 421, /* "port" */ + UDP = 422, /* "udp" */ + SPORT = 423, /* "sport" */ + DPORT = 424, /* "dport" */ + UDPLITE = 425, /* "udplite" */ + CSUMCOV = 426, /* "csumcov" */ + TCP = 427, /* "tcp" */ + ACKSEQ = 428, /* "ackseq" */ + DOFF = 429, /* "doff" */ + WINDOW = 430, /* "window" */ + URGPTR = 431, /* "urgptr" */ + OPTION = 432, /* "option" */ + ECHO = 433, /* "echo" */ + EOL = 434, /* "eol" */ + MPTCP = 435, /* "mptcp" */ + NOP = 436, /* "nop" */ + SACK = 437, /* "sack" */ + SACK0 = 438, /* "sack0" */ + SACK1 = 439, /* "sack1" */ + SACK2 = 440, /* "sack2" */ + SACK3 = 441, /* "sack3" */ + SACK_PERM = 442, /* "sack-permitted" */ + FASTOPEN = 443, /* "fastopen" */ + MD5SIG = 444, /* "md5sig" */ + TIMESTAMP = 445, /* "timestamp" */ + COUNT = 446, /* "count" */ + LEFT = 447, /* "left" */ + RIGHT = 448, /* "right" */ + TSVAL = 449, /* "tsval" */ + TSECR = 450, /* "tsecr" */ + SUBTYPE = 451, /* "subtype" */ + DCCP = 452, /* "dccp" */ + VXLAN = 453, /* "vxlan" */ + VNI = 454, /* "vni" */ + GRE = 455, /* "gre" */ + GRETAP = 456, /* "gretap" */ + GENEVE = 457, /* "geneve" */ + SCTP = 458, /* "sctp" */ + CHUNK = 459, /* "chunk" */ + DATA = 460, /* "data" */ + INIT = 461, /* "init" */ + INIT_ACK = 462, /* "init-ack" */ + HEARTBEAT = 463, /* "heartbeat" */ + HEARTBEAT_ACK = 464, /* "heartbeat-ack" */ + ABORT = 465, /* "abort" */ + SHUTDOWN = 466, /* "shutdown" */ + SHUTDOWN_ACK = 467, /* "shutdown-ack" */ + ERROR = 468, /* "error" */ + COOKIE_ECHO = 469, /* "cookie-echo" */ + COOKIE_ACK = 470, /* "cookie-ack" */ + ECNE = 471, /* "ecne" */ + CWR = 472, /* "cwr" */ + SHUTDOWN_COMPLETE = 473, /* "shutdown-complete" */ + ASCONF_ACK = 474, /* "asconf-ack" */ + FORWARD_TSN = 475, /* "forward-tsn" */ + ASCONF = 476, /* "asconf" */ + TSN = 477, /* "tsn" */ + STREAM = 478, /* "stream" */ + SSN = 479, /* "ssn" */ + PPID = 480, /* "ppid" */ + INIT_TAG = 481, /* "init-tag" */ + A_RWND = 482, /* "a-rwnd" */ + NUM_OSTREAMS = 483, /* "num-outbound-streams" */ + NUM_ISTREAMS = 484, /* "num-inbound-streams" */ + INIT_TSN = 485, /* "initial-tsn" */ + CUM_TSN_ACK = 486, /* "cum-tsn-ack" */ + NUM_GACK_BLOCKS = 487, /* "num-gap-ack-blocks" */ + NUM_DUP_TSNS = 488, /* "num-dup-tsns" */ + LOWEST_TSN = 489, /* "lowest-tsn" */ + SEQNO = 490, /* "seqno" */ + NEW_CUM_TSN = 491, /* "new-cum-tsn" */ + VTAG = 492, /* "vtag" */ + RT = 493, /* "rt" */ + RT0 = 494, /* "rt0" */ + RT2 = 495, /* "rt2" */ + RT4 = 496, /* "srh" */ + SEG_LEFT = 497, /* "seg-left" */ + ADDR = 498, /* "addr" */ + LAST_ENT = 499, /* "last-entry" */ + TAG = 500, /* "tag" */ + SID = 501, /* "sid" */ + HBH = 502, /* "hbh" */ + FRAG = 503, /* "frag" */ + RESERVED2 = 504, /* "reserved2" */ + MORE_FRAGMENTS = 505, /* "more-fragments" */ + DST = 506, /* "dst" */ + MH = 507, /* "mh" */ + META = 508, /* "meta" */ + MARK = 509, /* "mark" */ + IIF = 510, /* "iif" */ + IIFNAME = 511, /* "iifname" */ + IIFTYPE = 512, /* "iiftype" */ + OIF = 513, /* "oif" */ + OIFNAME = 514, /* "oifname" */ + OIFTYPE = 515, /* "oiftype" */ + SKUID = 516, /* "skuid" */ + SKGID = 517, /* "skgid" */ + NFTRACE = 518, /* "nftrace" */ + RTCLASSID = 519, /* "rtclassid" */ + IBRIPORT = 520, /* "ibriport" */ + OBRIPORT = 521, /* "obriport" */ + IBRIDGENAME = 522, /* "ibrname" */ + OBRIDGENAME = 523, /* "obrname" */ + PKTTYPE = 524, /* "pkttype" */ + CPU = 525, /* "cpu" */ + IIFGROUP = 526, /* "iifgroup" */ + OIFGROUP = 527, /* "oifgroup" */ + CGROUP = 528, /* "cgroup" */ + TIME = 529, /* "time" */ + CLASSID = 530, /* "classid" */ + NEXTHOP = 531, /* "nexthop" */ + CT = 532, /* "ct" */ + L3PROTOCOL = 533, /* "l3proto" */ + PROTO_SRC = 534, /* "proto-src" */ + PROTO_DST = 535, /* "proto-dst" */ + ZONE = 536, /* "zone" */ + DIRECTION = 537, /* "direction" */ + EVENT = 538, /* "event" */ + EXPECTATION = 539, /* "expectation" */ + EXPIRATION = 540, /* "expiration" */ + HELPER = 541, /* "helper" */ + LABEL = 542, /* "label" */ + STATE = 543, /* "state" */ + STATUS = 544, /* "status" */ + ORIGINAL = 545, /* "original" */ + REPLY = 546, /* "reply" */ + COUNTER = 547, /* "counter" */ + NAME = 548, /* "name" */ + PACKETS = 549, /* "packets" */ + BYTES = 550, /* "bytes" */ + AVGPKT = 551, /* "avgpkt" */ + LAST = 552, /* "last" */ + NEVER = 553, /* "never" */ + COUNTERS = 554, /* "counters" */ + QUOTAS = 555, /* "quotas" */ + LIMITS = 556, /* "limits" */ + SYNPROXYS = 557, /* "synproxys" */ + HELPERS = 558, /* "helpers" */ + LOG = 559, /* "log" */ + PREFIX = 560, /* "prefix" */ + GROUP = 561, /* "group" */ + SNAPLEN = 562, /* "snaplen" */ + QUEUE_THRESHOLD = 563, /* "queue-threshold" */ + LEVEL = 564, /* "level" */ + LIMIT = 565, /* "limit" */ + RATE = 566, /* "rate" */ + BURST = 567, /* "burst" */ + OVER = 568, /* "over" */ + UNTIL = 569, /* "until" */ + QUOTA = 570, /* "quota" */ + USED = 571, /* "used" */ + SECMARK = 572, /* "secmark" */ + SECMARKS = 573, /* "secmarks" */ + SECOND = 574, /* "second" */ + MINUTE = 575, /* "minute" */ + HOUR = 576, /* "hour" */ + DAY = 577, /* "day" */ + WEEK = 578, /* "week" */ + _REJECT = 579, /* "reject" */ + WITH = 580, /* "with" */ + ICMPX = 581, /* "icmpx" */ + SNAT = 582, /* "snat" */ + DNAT = 583, /* "dnat" */ + MASQUERADE = 584, /* "masquerade" */ + REDIRECT = 585, /* "redirect" */ + RANDOM = 586, /* "random" */ + FULLY_RANDOM = 587, /* "fully-random" */ + PERSISTENT = 588, /* "persistent" */ + QUEUE = 589, /* "queue" */ + QUEUENUM = 590, /* "num" */ + BYPASS = 591, /* "bypass" */ + FANOUT = 592, /* "fanout" */ + DUP = 593, /* "dup" */ + FWD = 594, /* "fwd" */ + NUMGEN = 595, /* "numgen" */ + INC = 596, /* "inc" */ + MOD = 597, /* "mod" */ + OFFSET = 598, /* "offset" */ + JHASH = 599, /* "jhash" */ + SYMHASH = 600, /* "symhash" */ + SEED = 601, /* "seed" */ + POSITION = 602, /* "position" */ + INDEX = 603, /* "index" */ + COMMENT = 604, /* "comment" */ + XML = 605, /* "xml" */ + JSON = 606, /* "json" */ + VM = 607, /* "vm" */ + NOTRACK = 608, /* "notrack" */ + EXISTS = 609, /* "exists" */ + MISSING = 610, /* "missing" */ + EXTHDR = 611, /* "exthdr" */ + IPSEC = 612, /* "ipsec" */ + REQID = 613, /* "reqid" */ + SPNUM = 614, /* "spnum" */ + IN = 615, /* "in" */ + OUT = 616, /* "out" */ + XT = 617 /* "xt" */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define TOKEN_EOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define JUNK 258 +#define NEWLINE 259 +#define COLON 260 +#define SEMICOLON 261 +#define COMMA 262 +#define DOT 263 +#define EQ 264 +#define NEQ 265 +#define LT 266 +#define GT 267 +#define GTE 268 +#define LTE 269 +#define LSHIFT 270 +#define RSHIFT 271 +#define AMPERSAND 272 +#define CARET 273 +#define NOT 274 +#define SLASH 275 +#define ASTERISK 276 +#define DASH 277 +#define AT 278 +#define VMAP 279 +#define PLUS 280 +#define INCLUDE 281 +#define DEFINE 282 +#define REDEFINE 283 +#define UNDEFINE 284 +#define FIB 285 +#define SOCKET 286 +#define TRANSPARENT 287 +#define WILDCARD 288 +#define CGROUPV2 289 +#define TPROXY 290 +#define OSF 291 +#define SYNPROXY 292 +#define MSS 293 +#define WSCALE 294 +#define TYPEOF 295 +#define HOOK 296 +#define HOOKS 297 +#define DEVICE 298 +#define DEVICES 299 +#define TABLE 300 +#define TABLES 301 +#define CHAIN 302 +#define CHAINS 303 +#define RULE 304 +#define RULES 305 +#define SETS 306 +#define SET 307 +#define ELEMENT 308 +#define MAP 309 +#define MAPS 310 +#define FLOWTABLE 311 +#define HANDLE 312 +#define RULESET 313 +#define TRACE 314 +#define INET 315 +#define NETDEV 316 +#define ADD 317 +#define UPDATE 318 +#define REPLACE 319 +#define CREATE 320 +#define INSERT 321 +#define DELETE 322 +#define GET 323 +#define LIST 324 +#define RESET 325 +#define FLUSH 326 +#define RENAME 327 +#define DESCRIBE 328 +#define IMPORT 329 +#define EXPORT 330 +#define DESTROY 331 +#define MONITOR 332 +#define ALL 333 +#define ACCEPT 334 +#define DROP 335 +#define CONTINUE 336 +#define JUMP 337 +#define GOTO 338 +#define RETURN 339 +#define TO 340 +#define CONSTANT 341 +#define INTERVAL 342 +#define DYNAMIC 343 +#define AUTOMERGE 344 +#define TIMEOUT 345 +#define GC_INTERVAL 346 +#define ELEMENTS 347 +#define EXPIRES 348 +#define POLICY 349 +#define MEMORY 350 +#define PERFORMANCE 351 +#define SIZE 352 +#define FLOW 353 +#define OFFLOAD 354 +#define METER 355 +#define METERS 356 +#define FLOWTABLES 357 +#define NUM 358 +#define STRING 359 +#define QUOTED_STRING 360 +#define ASTERISK_STRING 361 +#define LL_HDR 362 +#define NETWORK_HDR 363 +#define TRANSPORT_HDR 364 +#define BRIDGE 365 +#define ETHER 366 +#define SADDR 367 +#define DADDR 368 +#define TYPE 369 +#define VLAN 370 +#define ID 371 +#define CFI 372 +#define DEI 373 +#define PCP 374 +#define ARP 375 +#define HTYPE 376 +#define PTYPE 377 +#define HLEN 378 +#define PLEN 379 +#define OPERATION 380 +#define IP 381 +#define HDRVERSION 382 +#define HDRLENGTH 383 +#define DSCP 384 +#define ECN 385 +#define LENGTH 386 +#define FRAG_OFF 387 +#define TTL 388 +#define PROTOCOL 389 +#define CHECKSUM 390 +#define PTR 391 +#define VALUE 392 +#define LSRR 393 +#define RR 394 +#define SSRR 395 +#define RA 396 +#define ICMP 397 +#define CODE 398 +#define SEQUENCE 399 +#define GATEWAY 400 +#define MTU 401 +#define IGMP 402 +#define MRT 403 +#define OPTIONS 404 +#define IP6 405 +#define PRIORITY 406 +#define FLOWLABEL 407 +#define NEXTHDR 408 +#define HOPLIMIT 409 +#define ICMP6 410 +#define PPTR 411 +#define MAXDELAY 412 +#define TADDR 413 +#define AH 414 +#define RESERVED 415 +#define SPI 416 +#define ESP 417 +#define COMP 418 +#define FLAGS 419 +#define CPI 420 +#define PORT 421 +#define UDP 422 +#define SPORT 423 +#define DPORT 424 +#define UDPLITE 425 +#define CSUMCOV 426 +#define TCP 427 +#define ACKSEQ 428 +#define DOFF 429 +#define WINDOW 430 +#define URGPTR 431 +#define OPTION 432 +#define ECHO 433 +#define EOL 434 +#define MPTCP 435 +#define NOP 436 +#define SACK 437 +#define SACK0 438 +#define SACK1 439 +#define SACK2 440 +#define SACK3 441 +#define SACK_PERM 442 +#define FASTOPEN 443 +#define MD5SIG 444 +#define TIMESTAMP 445 +#define COUNT 446 +#define LEFT 447 +#define RIGHT 448 +#define TSVAL 449 +#define TSECR 450 +#define SUBTYPE 451 +#define DCCP 452 +#define VXLAN 453 +#define VNI 454 +#define GRE 455 +#define GRETAP 456 +#define GENEVE 457 +#define SCTP 458 +#define CHUNK 459 +#define DATA 460 +#define INIT 461 +#define INIT_ACK 462 +#define HEARTBEAT 463 +#define HEARTBEAT_ACK 464 +#define ABORT 465 +#define SHUTDOWN 466 +#define SHUTDOWN_ACK 467 +#define ERROR 468 +#define COOKIE_ECHO 469 +#define COOKIE_ACK 470 +#define ECNE 471 +#define CWR 472 +#define SHUTDOWN_COMPLETE 473 +#define ASCONF_ACK 474 +#define FORWARD_TSN 475 +#define ASCONF 476 +#define TSN 477 +#define STREAM 478 +#define SSN 479 +#define PPID 480 +#define INIT_TAG 481 +#define A_RWND 482 +#define NUM_OSTREAMS 483 +#define NUM_ISTREAMS 484 +#define INIT_TSN 485 +#define CUM_TSN_ACK 486 +#define NUM_GACK_BLOCKS 487 +#define NUM_DUP_TSNS 488 +#define LOWEST_TSN 489 +#define SEQNO 490 +#define NEW_CUM_TSN 491 +#define VTAG 492 +#define RT 493 +#define RT0 494 +#define RT2 495 +#define RT4 496 +#define SEG_LEFT 497 +#define ADDR 498 +#define LAST_ENT 499 +#define TAG 500 +#define SID 501 +#define HBH 502 +#define FRAG 503 +#define RESERVED2 504 +#define MORE_FRAGMENTS 505 +#define DST 506 +#define MH 507 +#define META 508 +#define MARK 509 +#define IIF 510 +#define IIFNAME 511 +#define IIFTYPE 512 +#define OIF 513 +#define OIFNAME 514 +#define OIFTYPE 515 +#define SKUID 516 +#define SKGID 517 +#define NFTRACE 518 +#define RTCLASSID 519 +#define IBRIPORT 520 +#define OBRIPORT 521 +#define IBRIDGENAME 522 +#define OBRIDGENAME 523 +#define PKTTYPE 524 +#define CPU 525 +#define IIFGROUP 526 +#define OIFGROUP 527 +#define CGROUP 528 +#define TIME 529 +#define CLASSID 530 +#define NEXTHOP 531 +#define CT 532 +#define L3PROTOCOL 533 +#define PROTO_SRC 534 +#define PROTO_DST 535 +#define ZONE 536 +#define DIRECTION 537 +#define EVENT 538 +#define EXPECTATION 539 +#define EXPIRATION 540 +#define HELPER 541 +#define LABEL 542 +#define STATE 543 +#define STATUS 544 +#define ORIGINAL 545 +#define REPLY 546 +#define COUNTER 547 +#define NAME 548 +#define PACKETS 549 +#define BYTES 550 +#define AVGPKT 551 +#define LAST 552 +#define NEVER 553 +#define COUNTERS 554 +#define QUOTAS 555 +#define LIMITS 556 +#define SYNPROXYS 557 +#define HELPERS 558 +#define LOG 559 +#define PREFIX 560 +#define GROUP 561 +#define SNAPLEN 562 +#define QUEUE_THRESHOLD 563 +#define LEVEL 564 +#define LIMIT 565 +#define RATE 566 +#define BURST 567 +#define OVER 568 +#define UNTIL 569 +#define QUOTA 570 +#define USED 571 +#define SECMARK 572 +#define SECMARKS 573 +#define SECOND 574 +#define MINUTE 575 +#define HOUR 576 +#define DAY 577 +#define WEEK 578 +#define _REJECT 579 +#define WITH 580 +#define ICMPX 581 +#define SNAT 582 +#define DNAT 583 +#define MASQUERADE 584 +#define REDIRECT 585 +#define RANDOM 586 +#define FULLY_RANDOM 587 +#define PERSISTENT 588 +#define QUEUE 589 +#define QUEUENUM 590 +#define BYPASS 591 +#define FANOUT 592 +#define DUP 593 +#define FWD 594 +#define NUMGEN 595 +#define INC 596 +#define MOD 597 +#define OFFSET 598 +#define JHASH 599 +#define SYMHASH 600 +#define SEED 601 +#define POSITION 602 +#define INDEX 603 +#define COMMENT 604 +#define XML 605 +#define JSON 606 +#define VM 607 +#define NOTRACK 608 +#define EXISTS 609 +#define MISSING 610 +#define EXTHDR 611 +#define IPSEC 612 +#define REQID 613 +#define SPNUM 614 +#define IN 615 +#define OUT 616 +#define XT 617 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +union YYSTYPE +{ +#line 205 "parser_bison.y" + + uint64_t val; + uint32_t val32; + uint8_t val8; + 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; + struct set *set; + struct obj *obj; + struct flowtable *flowtable; + struct ct *ct; + const struct datatype *datatype; + struct handle_spec handle_spec; + struct position_spec position_spec; + struct prio_spec prio_spec; + struct limit_rate limit_rate; + struct tcp_kind_field { + uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */ + uint8_t field; + } tcp_kind_field; + +#line 820 "parser_bison.h" + +}; +typedef union YYSTYPE YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + +/* Location type. */ +#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED +typedef struct YYLTYPE YYLTYPE; +struct YYLTYPE +{ + int first_line; + int first_column; + int last_line; + int last_column; +}; +# define YYLTYPE_IS_DECLARED 1 +# define YYLTYPE_IS_TRIVIAL 1 +#endif + + + +int nft_parse (struct nft_ctx *nft, void *scanner, struct parser_state *state); + +#endif /* !YY_NFT_PARSER_BISON_H_INCLUDED */ diff --git a/src/parser_bison.y b/src/parser_bison.y new file mode 100644 index 0000000..c517dc3 --- /dev/null +++ b/src/parser_bison.y @@ -0,0 +1,6290 @@ +/* + * Copyright (c) 2007-2012 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +%{ +#include <nft.h> + +#include <ctype.h> +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> +#include <syslog.h> +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/if_ether.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_conntrack_tuple_common.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nfnetlink_osf.h> +#include <linux/netfilter/nf_synproxy.h> +#include <linux/xfrm.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <libnftnl/common.h> +#include <libnftnl/set.h> +#include <libnftnl/udata.h> + +#include <rule.h> +#include <statement.h> +#include <expression.h> +#include <headers.h> +#include <utils.h> +#include <parser.h> +#include <erec.h> +#include <sctp_chunk.h> + +#include "parser_bison.h" + +void parser_init(struct nft_ctx *nft, struct parser_state *state, + struct list_head *msgs, struct list_head *cmds, + struct scope *top_scope) +{ + memset(state, 0, sizeof(*state)); + state->msgs = msgs; + state->cmds = cmds; + state->scopes[0] = scope_init(top_scope, NULL); + init_list_head(&state->indesc_list); +} + +static void yyerror(struct location *loc, struct nft_ctx *nft, void *scanner, + struct parser_state *state, const char *s) +{ + erec_queue(error(loc, "%s", s), state->msgs); +} + +static struct scope *current_scope(const struct parser_state *state) +{ + return state->scopes[state->scope]; +} + +static int open_scope(struct parser_state *state, struct scope *scope) +{ + if (state->scope >= array_size(state->scopes) - 1) { + state->scope_err = true; + return -1; + } + + scope_init(scope, current_scope(state)); + state->scopes[++state->scope] = scope; + + return 0; +} + +static void close_scope(struct parser_state *state) +{ + if (state->scope_err || state->scope == 0) { + state->scope_err = false; + return; + } + + state->scope--; +} + +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; + } +} + +static struct expr *handle_concat_expr(const struct location *loc, + struct expr *expr, + struct expr *expr_l, struct expr *expr_r, + struct location loc_rhs[3]) +{ + if (expr->etype != EXPR_CONCAT) { + expr = concat_expr_alloc(loc); + compound_expr_add(expr, expr_l); + } else { + location_update(&expr_r->location, loc_rhs, 2); + + expr = expr_l; + expr->location = *loc; + } + + compound_expr_add(expr, expr_r); + return expr; +} + +static bool already_set(const void *attr, const struct location *loc, + struct parser_state *state) +{ + if (!attr) + return false; + + erec_queue(error(loc, "You can only specify this once. This statement is duplicated."), + state->msgs); + return true; +} + +static struct expr *ifname_expr_alloc(const struct location *location, + struct list_head *queue, + const char *name) +{ + unsigned int length = strlen(name); + struct expr *expr; + + if (length == 0) { + xfree(name); + erec_queue(error(location, "empty interface name"), queue); + return NULL; + } + + if (length > 16) { + xfree(name); + erec_queue(error(location, "interface name too long"), queue); + return NULL; + } + + expr = constant_expr_alloc(location, &ifname_type, BYTEORDER_HOST_ENDIAN, + length * BITS_PER_BYTE, name); + + xfree(name); + + return expr; +} + +#define YYLLOC_DEFAULT(Current, Rhs, N) location_update(&Current, Rhs, N) + +#define symbol_value(loc, str) \ + symbol_expr_alloc(loc, SYMBOL_VALUE, current_scope(state), str) + +/* Declare those here to avoid compiler warnings */ +void nft_set_debug(int, void *); +int nft_lex(void *, void *, void *); +%} + +/* Declaration section */ + +%name-prefix "nft_" +%debug +%define api.pure +%parse-param { struct nft_ctx *nft } +%parse-param { void *scanner } +%parse-param { struct parser_state *state } +%lex-param { scanner } +%define parse.error verbose +%locations + +%initial-action { + location_init(scanner, state, &yylloc); + if (nft->debug_mask & NFT_DEBUG_SCANNER) + nft_set_debug(1, scanner); + if (nft->debug_mask & NFT_DEBUG_PARSER) + yydebug = 1; +} + +%union { + uint64_t val; + uint32_t val32; + uint8_t val8; + 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; + struct set *set; + struct obj *obj; + struct flowtable *flowtable; + struct ct *ct; + const struct datatype *datatype; + struct handle_spec handle_spec; + struct position_spec position_spec; + struct prio_spec prio_spec; + struct limit_rate limit_rate; + struct tcp_kind_field { + uint16_t kind; /* must allow > 255 for SACK1, 2.. hack */ + uint8_t field; + } tcp_kind_field; +} + +%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 VMAP "vmap" + +%token PLUS "+" + +%token INCLUDE "include" +%token DEFINE "define" +%token REDEFINE "redefine" +%token UNDEFINE "undefine" + +%token FIB "fib" + +%token SOCKET "socket" +%token TRANSPARENT "transparent" +%token WILDCARD "wildcard" +%token CGROUPV2 "cgroupv2" + +%token TPROXY "tproxy" + +%token OSF "osf" + +%token SYNPROXY "synproxy" +%token MSS "mss" +%token WSCALE "wscale" + +%token TYPEOF "typeof" + +%token HOOK "hook" +%token HOOKS "hooks" +%token DEVICE "device" +%token DEVICES "devices" +%token TABLE "table" +%token TABLES "tables" +%token CHAIN "chain" +%token CHAINS "chains" +%token RULE "rule" +%token RULES "rules" +%token SETS "sets" +%token SET "set" +%token ELEMENT "element" +%token MAP "map" +%token MAPS "maps" +%token FLOWTABLE "flowtable" +%token HANDLE "handle" +%token RULESET "ruleset" +%token TRACE "trace" + +%token INET "inet" +%token NETDEV "netdev" + +%token ADD "add" +%token UPDATE "update" +%token REPLACE "replace" +%token CREATE "create" +%token INSERT "insert" +%token DELETE "delete" +%token GET "get" +%token LIST "list" +%token RESET "reset" +%token FLUSH "flush" +%token RENAME "rename" +%token DESCRIBE "describe" +%token IMPORT "import" +%token EXPORT "export" +%token DESTROY "destroy" + +%token MONITOR "monitor" + +%token ALL "all" + +%token ACCEPT "accept" +%token DROP "drop" +%token CONTINUE "continue" +%token JUMP "jump" +%token GOTO "goto" +%token RETURN "return" +%token TO "to" + +%token CONSTANT "constant" +%token INTERVAL "interval" +%token DYNAMIC "dynamic" +%token AUTOMERGE "auto-merge" +%token TIMEOUT "timeout" +%token GC_INTERVAL "gc-interval" +%token ELEMENTS "elements" +%token EXPIRES "expires" + +%token POLICY "policy" +%token MEMORY "memory" +%token PERFORMANCE "performance" +%token SIZE "size" + +%token FLOW "flow" +%token OFFLOAD "offload" +%token METER "meter" +%token METERS "meters" + +%token FLOWTABLES "flowtables" + +%token <val> NUM "number" +%token <string> STRING "string" +%token <string> QUOTED_STRING "quoted string" +%token <string> ASTERISK_STRING "string with a trailing asterisk" +%destructor { xfree($$); } STRING QUOTED_STRING ASTERISK_STRING + +%token LL_HDR "ll" +%token NETWORK_HDR "nh" +%token TRANSPORT_HDR "th" + +%token BRIDGE "bridge" + +%token ETHER "ether" +%token SADDR "saddr" +%token DADDR "daddr" +%token TYPE "type" + +%token VLAN "vlan" +%token ID "id" +%token CFI "cfi" +%token DEI "dei" +%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 HDRVERSION "version" +%token HDRLENGTH "hdrlength" +%token DSCP "dscp" +%token ECN "ecn" +%token LENGTH "length" +%token FRAG_OFF "frag-off" +%token TTL "ttl" +%token PROTOCOL "protocol" +%token CHECKSUM "checksum" + +%token PTR "ptr" +%token VALUE "value" + +%token LSRR "lsrr" +%token RR "rr" +%token SSRR "ssrr" +%token RA "ra" + +%token ICMP "icmp" +%token CODE "code" +%token SEQUENCE "seq" +%token GATEWAY "gateway" +%token MTU "mtu" + +%token IGMP "igmp" +%token MRT "mrt" + +%token OPTIONS "options" + +%token IP6 "ip6" +%token PRIORITY "priority" +%token FLOWLABEL "flowlabel" +%token NEXTHDR "nexthdr" +%token HOPLIMIT "hoplimit" + +%token ICMP6 "icmpv6" +%token PPTR "param-problem" +%token MAXDELAY "max-delay" +%token TADDR "taddr" + +%token AH "ah" +%token RESERVED "reserved" +%token SPI "spi" + +%token ESP "esp" + +%token COMP "comp" +%token FLAGS "flags" +%token CPI "cpi" + +%token PORT "port" +%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 OPTION "option" +%token ECHO "echo" +%token EOL "eol" +%token MPTCP "mptcp" +%token NOP "nop" +%token SACK "sack" +%token SACK0 "sack0" +%token SACK1 "sack1" +%token SACK2 "sack2" +%token SACK3 "sack3" +%token SACK_PERM "sack-permitted" +%token FASTOPEN "fastopen" +%token MD5SIG "md5sig" +%token TIMESTAMP "timestamp" +%token COUNT "count" +%token LEFT "left" +%token RIGHT "right" +%token TSVAL "tsval" +%token TSECR "tsecr" +%token SUBTYPE "subtype" + +%token DCCP "dccp" + +%token VXLAN "vxlan" +%token VNI "vni" + +%token GRE "gre" +%token GRETAP "gretap" + +%token GENEVE "geneve" + +%token SCTP "sctp" +%token CHUNK "chunk" +%token DATA "data" +%token INIT "init" +%token INIT_ACK "init-ack" +%token HEARTBEAT "heartbeat" +%token HEARTBEAT_ACK "heartbeat-ack" +%token ABORT "abort" +%token SHUTDOWN "shutdown" +%token SHUTDOWN_ACK "shutdown-ack" +%token ERROR "error" +%token COOKIE_ECHO "cookie-echo" +%token COOKIE_ACK "cookie-ack" +%token ECNE "ecne" +%token CWR "cwr" +%token SHUTDOWN_COMPLETE "shutdown-complete" +%token ASCONF_ACK "asconf-ack" +%token FORWARD_TSN "forward-tsn" +%token ASCONF "asconf" +%token TSN "tsn" +%token STREAM "stream" +%token SSN "ssn" +%token PPID "ppid" +%token INIT_TAG "init-tag" +%token A_RWND "a-rwnd" +%token NUM_OSTREAMS "num-outbound-streams" +%token NUM_ISTREAMS "num-inbound-streams" +%token INIT_TSN "initial-tsn" +%token CUM_TSN_ACK "cum-tsn-ack" +%token NUM_GACK_BLOCKS "num-gap-ack-blocks" +%token NUM_DUP_TSNS "num-dup-tsns" +%token LOWEST_TSN "lowest-tsn" +%token SEQNO "seqno" +%token NEW_CUM_TSN "new-cum-tsn" + +%token VTAG "vtag" + +%token RT "rt" +%token RT0 "rt0" +%token RT2 "rt2" +%token RT4 "srh" +%token SEG_LEFT "seg-left" +%token ADDR "addr" +%token LAST_ENT "last-entry" +%token TAG "tag" +%token SID "sid" + +%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 IBRIPORT "ibriport" +%token OBRIPORT "obriport" +%token IBRIDGENAME "ibrname" +%token OBRIDGENAME "obrname" +%token PKTTYPE "pkttype" +%token CPU "cpu" +%token IIFGROUP "iifgroup" +%token OIFGROUP "oifgroup" +%token CGROUP "cgroup" +%token TIME "time" + +%token CLASSID "classid" +%token NEXTHOP "nexthop" + +%token CT "ct" +%token L3PROTOCOL "l3proto" +%token PROTO_SRC "proto-src" +%token PROTO_DST "proto-dst" +%token ZONE "zone" +%token DIRECTION "direction" +%token EVENT "event" +%token EXPECTATION "expectation" +%token EXPIRATION "expiration" +%token HELPER "helper" +%token LABEL "label" +%token STATE "state" +%token STATUS "status" +%token ORIGINAL "original" +%token REPLY "reply" + +%token COUNTER "counter" +%token NAME "name" +%token PACKETS "packets" +%token BYTES "bytes" +%token AVGPKT "avgpkt" + +%token LAST "last" +%token NEVER "never" + +%token COUNTERS "counters" +%token QUOTAS "quotas" +%token LIMITS "limits" +%token SYNPROXYS "synproxys" +%token HELPERS "helpers" + +%token LOG "log" +%token PREFIX "prefix" +%token GROUP "group" +%token SNAPLEN "snaplen" +%token QUEUE_THRESHOLD "queue-threshold" +%token LEVEL "level" + +%token LIMIT "limit" +%token RATE "rate" +%token BURST "burst" +%token OVER "over" +%token UNTIL "until" + +%token QUOTA "quota" +%token USED "used" + +%token SECMARK "secmark" +%token SECMARKS "secmarks" + +%token SECOND "second" +%token MINUTE "minute" +%token HOUR "hour" +%token DAY "day" +%token WEEK "week" + +%token _REJECT "reject" +%token WITH "with" +%token ICMPX "icmpx" + +%token SNAT "snat" +%token DNAT "dnat" +%token MASQUERADE "masquerade" +%token REDIRECT "redirect" +%token RANDOM "random" +%token FULLY_RANDOM "fully-random" +%token PERSISTENT "persistent" + +%token QUEUE "queue" +%token QUEUENUM "num" +%token BYPASS "bypass" +%token FANOUT "fanout" + +%token DUP "dup" +%token FWD "fwd" + +%token NUMGEN "numgen" +%token INC "inc" +%token MOD "mod" +%token OFFSET "offset" + +%token JHASH "jhash" +%token SYMHASH "symhash" +%token SEED "seed" + +%token POSITION "position" +%token INDEX "index" +%token COMMENT "comment" + +%token XML "xml" +%token JSON "json" +%token VM "vm" + +%token NOTRACK "notrack" + +%token EXISTS "exists" +%token MISSING "missing" + +%token EXTHDR "exthdr" + +%token IPSEC "ipsec" +%token REQID "reqid" +%token SPNUM "spnum" + +%token IN "in" +%token OUT "out" + +%token XT "xt" + +%type <limit_rate> limit_rate_pkts +%type <limit_rate> limit_rate_bytes + +%type <string> identifier type_identifier string comment_spec +%destructor { xfree($$); } identifier type_identifier string comment_spec + +%type <val> time_spec time_spec_or_num_s quota_used + +%type <expr> data_type_expr data_type_atom_expr +%destructor { expr_free($$); } data_type_expr data_type_atom_expr + +%type <cmd> line +%destructor { cmd_free($$); } line + +%type <cmd> base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd +%destructor { cmd_free($$); } base_cmd add_cmd replace_cmd create_cmd insert_cmd delete_cmd get_cmd list_cmd reset_cmd flush_cmd rename_cmd export_cmd monitor_cmd describe_cmd import_cmd destroy_cmd + +%type <handle> table_spec tableid_spec table_or_id_spec +%destructor { handle_free(&$$); } table_spec tableid_spec table_or_id_spec +%type <handle> chain_spec chainid_spec chain_or_id_spec +%destructor { handle_free(&$$); } chain_spec chainid_spec chain_or_id_spec + +%type <handle> flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec +%destructor { handle_free(&$$); } flowtable_spec chain_identifier ruleid_spec handle_spec position_spec rule_position ruleset_spec index_spec +%type <handle> set_spec setid_spec set_or_id_spec +%destructor { handle_free(&$$); } set_spec setid_spec set_or_id_spec +%type <handle> obj_spec objid_spec obj_or_id_spec +%destructor { handle_free(&$$); } obj_spec objid_spec obj_or_id_spec + +%type <handle> set_identifier flowtableid_spec flowtable_identifier obj_identifier +%destructor { handle_free(&$$); } set_identifier flowtableid_spec obj_identifier + +%type <handle> basehook_spec +%destructor { handle_free(&$$); } basehook_spec + +%type <val> family_spec family_spec_explicit +%type <val32> int_num chain_policy +%type <prio_spec> extended_prio_spec prio_spec +%type <string> extended_prio_name quota_unit basehook_device_name +%destructor { xfree($$); } extended_prio_name quota_unit basehook_device_name + +%type <expr> dev_spec +%destructor { xfree($$); } dev_spec + +%type <table> table_block_alloc table_block +%destructor { close_scope(state); table_free($$); } table_block_alloc +%type <chain> chain_block_alloc chain_block subchain_block +%destructor { close_scope(state); chain_free($$); } chain_block_alloc +%type <rule> rule rule_alloc +%destructor { rule_free($$); } rule + +%type <val> set_flag_list set_flag + +%type <val> set_policy_spec + +%type <set> set_block_alloc set_block +%destructor { set_free($$); } set_block_alloc + +%type <set> map_block_alloc map_block +%destructor { set_free($$); } map_block_alloc +%type <val> map_block_obj_type map_block_data_interval + +%type <flowtable> flowtable_block_alloc flowtable_block +%destructor { flowtable_free($$); } flowtable_block_alloc + +%type <obj> obj_block_alloc counter_block quota_block ct_helper_block ct_timeout_block ct_expect_block limit_block secmark_block synproxy_block +%destructor { obj_free($$); } obj_block_alloc + +%type <list> stmt_list stateful_stmt_list set_elem_stmt_list +%destructor { stmt_list_free($$); xfree($$); } stmt_list stateful_stmt_list set_elem_stmt_list +%type <stmt> stmt match_stmt verdict_stmt set_elem_stmt +%destructor { stmt_free($$); } stmt match_stmt verdict_stmt set_elem_stmt +%type <stmt> counter_stmt counter_stmt_alloc stateful_stmt last_stmt +%destructor { stmt_free($$); } counter_stmt counter_stmt_alloc stateful_stmt last_stmt +%type <stmt> payload_stmt +%destructor { stmt_free($$); } payload_stmt +%type <stmt> ct_stmt +%destructor { stmt_free($$); } ct_stmt +%type <stmt> meta_stmt +%destructor { stmt_free($$); } meta_stmt +%type <stmt> log_stmt log_stmt_alloc +%destructor { stmt_free($$); } log_stmt log_stmt_alloc +%type <val> level_type log_flags log_flags_tcp log_flag_tcp +%type <stmt> limit_stmt quota_stmt connlimit_stmt +%destructor { stmt_free($$); } limit_stmt quota_stmt connlimit_stmt +%type <val> limit_burst_pkts limit_burst_bytes limit_mode limit_bytes time_unit quota_mode +%type <stmt> reject_stmt reject_stmt_alloc +%destructor { stmt_free($$); } reject_stmt reject_stmt_alloc +%type <stmt> nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc +%destructor { stmt_free($$); } nat_stmt nat_stmt_alloc masq_stmt masq_stmt_alloc redir_stmt redir_stmt_alloc +%type <val> nf_nat_flags nf_nat_flag offset_opt +%type <stmt> tproxy_stmt +%destructor { stmt_free($$); } tproxy_stmt +%type <stmt> synproxy_stmt synproxy_stmt_alloc +%destructor { stmt_free($$); } synproxy_stmt synproxy_stmt_alloc +%type <stmt> chain_stmt +%destructor { stmt_free($$); } chain_stmt +%type <val> chain_stmt_type + +%type <stmt> queue_stmt queue_stmt_alloc queue_stmt_compat +%destructor { stmt_free($$); } queue_stmt queue_stmt_alloc queue_stmt_compat +%type <expr> queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr +%destructor { expr_free($$); } queue_stmt_expr_simple queue_stmt_expr queue_expr reject_with_expr +%type <val> queue_stmt_flags queue_stmt_flag +%type <stmt> dup_stmt +%destructor { stmt_free($$); } dup_stmt +%type <stmt> fwd_stmt +%destructor { stmt_free($$); } fwd_stmt +%type <stmt> set_stmt +%destructor { stmt_free($$); } set_stmt +%type <val> set_stmt_op +%type <stmt> map_stmt +%destructor { stmt_free($$); } map_stmt +%type <stmt> meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc +%destructor { stmt_free($$); } meter_stmt meter_stmt_alloc flow_stmt_legacy_alloc + +%type <expr> symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr +%destructor { expr_free($$); } symbol_expr verdict_expr integer_expr variable_expr chain_expr policy_expr +%type <expr> primary_expr shift_expr and_expr typeof_expr typeof_data_expr +%destructor { expr_free($$); } primary_expr shift_expr and_expr typeof_expr typeof_data_expr +%type <expr> exclusive_or_expr inclusive_or_expr +%destructor { expr_free($$); } exclusive_or_expr inclusive_or_expr +%type <expr> basic_expr +%destructor { expr_free($$); } basic_expr +%type <expr> set_ref_expr set_ref_symbol_expr +%destructor { expr_free($$); } set_ref_expr set_ref_symbol_expr + +%type <expr> multiton_rhs_expr +%destructor { expr_free($$); } multiton_rhs_expr +%type <expr> prefix_rhs_expr range_rhs_expr +%destructor { expr_free($$); } prefix_rhs_expr range_rhs_expr + +%type <expr> stmt_expr concat_stmt_expr map_stmt_expr map_stmt_expr_set +%destructor { expr_free($$); } stmt_expr concat_stmt_expr map_stmt_expr map_stmt_expr_set + +%type <expr> multiton_stmt_expr +%destructor { expr_free($$); } multiton_stmt_expr +%type <expr> prefix_stmt_expr range_stmt_expr +%destructor { expr_free($$); } prefix_stmt_expr range_stmt_expr + +%type <expr> primary_stmt_expr basic_stmt_expr +%destructor { expr_free($$); } primary_stmt_expr basic_stmt_expr +%type <expr> list_stmt_expr shift_stmt_expr +%destructor { expr_free($$); } list_stmt_expr shift_stmt_expr +%type <expr> and_stmt_expr exclusive_or_stmt_expr inclusive_or_stmt_expr +%destructor { expr_free($$); } and_stmt_expr exclusive_or_stmt_expr inclusive_or_stmt_expr + +%type <expr> concat_expr +%destructor { expr_free($$); } concat_expr + +%type <expr> map_expr +%destructor { expr_free($$); } map_expr + +%type <expr> verdict_map_stmt +%destructor { expr_free($$); } verdict_map_stmt + +%type <expr> verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr +%destructor { expr_free($$); } verdict_map_expr verdict_map_list_expr verdict_map_list_member_expr + +%type <expr> set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member +%destructor { expr_free($$); } set_expr set_block_expr set_list_expr set_list_member_expr flowtable_expr flowtable_list_expr flowtable_expr_member +%type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr +%destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr +%type <expr> set_elem_expr_stmt set_elem_expr_stmt_alloc +%destructor { expr_free($$); } set_elem_expr_stmt set_elem_expr_stmt_alloc + +%type <expr> meter_key_expr meter_key_expr_alloc +%destructor { expr_free($$); } meter_key_expr meter_key_expr_alloc + +%type <expr> expr initializer_expr keyword_expr +%destructor { expr_free($$); } expr initializer_expr keyword_expr + +%type <expr> rhs_expr concat_rhs_expr basic_rhs_expr +%destructor { expr_free($$); } rhs_expr concat_rhs_expr basic_rhs_expr +%type <expr> primary_rhs_expr list_rhs_expr shift_rhs_expr symbol_stmt_expr +%destructor { expr_free($$); } primary_rhs_expr list_rhs_expr shift_rhs_expr symbol_stmt_expr +%type <expr> and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr +%destructor { expr_free($$); } and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr + +%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj +%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj synproxy_obj + +%type <expr> relational_expr +%destructor { expr_free($$); } relational_expr +%type <val> relational_op + +%type <expr> payload_expr payload_raw_expr +%destructor { expr_free($$); } payload_expr payload_raw_expr +%type <val> payload_base_spec +%type <expr> eth_hdr_expr vlan_hdr_expr +%destructor { expr_free($$); } eth_hdr_expr vlan_hdr_expr +%type <val> eth_hdr_field vlan_hdr_field +%type <expr> arp_hdr_expr +%destructor { expr_free($$); } arp_hdr_expr +%type <val> arp_hdr_field +%type <expr> ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr +%destructor { expr_free($$); } ip_hdr_expr icmp_hdr_expr igmp_hdr_expr numgen_expr hash_expr +%type <val> ip_hdr_field icmp_hdr_field igmp_hdr_field +%type <val> ip_option_type ip_option_field +%type <expr> ip6_hdr_expr icmp6_hdr_expr +%destructor { expr_free($$); } ip6_hdr_expr icmp6_hdr_expr +%type <val> ip6_hdr_field icmp6_hdr_field +%type <expr> auth_hdr_expr esp_hdr_expr comp_hdr_expr +%destructor { expr_free($$); } auth_hdr_expr esp_hdr_expr comp_hdr_expr +%type <val> auth_hdr_field esp_hdr_field comp_hdr_field +%type <expr> udp_hdr_expr udplite_hdr_expr +%destructor { expr_free($$); } udp_hdr_expr udplite_hdr_expr +%type <val> udp_hdr_field udplite_hdr_field +%type <expr> dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc +%destructor { expr_free($$); } dccp_hdr_expr sctp_hdr_expr sctp_chunk_alloc +%type <val> dccp_hdr_field sctp_hdr_field +%type <val> sctp_chunk_type sctp_chunk_common_field +%type <val> sctp_chunk_data_field sctp_chunk_init_field +%type <val> sctp_chunk_sack_field +%type <expr> th_hdr_expr +%destructor { expr_free($$); } th_hdr_expr +%type <val> th_hdr_field + +%type <expr> exthdr_expr +%destructor { expr_free($$); } exthdr_expr +%type <expr> hbh_hdr_expr frag_hdr_expr dst_hdr_expr +%destructor { expr_free($$); } hbh_hdr_expr frag_hdr_expr dst_hdr_expr +%type <val> hbh_hdr_field frag_hdr_field dst_hdr_field +%type <expr> rt_hdr_expr rt0_hdr_expr rt2_hdr_expr rt4_hdr_expr +%destructor { expr_free($$); } rt_hdr_expr rt0_hdr_expr rt2_hdr_expr rt4_hdr_expr +%type <val> rt_hdr_field rt0_hdr_field rt2_hdr_field rt4_hdr_field +%type <expr> mh_hdr_expr +%destructor { expr_free($$); } mh_hdr_expr +%type <val> mh_hdr_field + +%type <expr> meta_expr +%destructor { expr_free($$); } meta_expr +%type <val> meta_key meta_key_qualified meta_key_unqualified numgen_type + +%type <expr> socket_expr +%destructor { expr_free($$); } socket_expr +%type<val> socket_key + +%type <val> nf_key_proto + +%type <expr> rt_expr +%destructor { expr_free($$); } rt_expr +%type <val> rt_key + +%type <expr> ct_expr +%destructor { expr_free($$); } ct_expr +%type <val> ct_key ct_dir ct_key_dir_optional ct_key_dir ct_key_proto_field + +%type <expr> fib_expr +%destructor { expr_free($$); } fib_expr +%type <val> fib_tuple fib_result fib_flag + +%type <expr> osf_expr +%type <val> osf_ttl +%destructor { expr_free($$); } osf_expr + +%type <val> markup_format +%type <string> monitor_event +%destructor { xfree($$); } monitor_event +%type <val> monitor_object monitor_format + +%type <val> synproxy_ts synproxy_sack + +%type <expr> tcp_hdr_expr +%destructor { expr_free($$); } tcp_hdr_expr +%type <val> tcp_hdr_field +%type <val> tcp_hdr_option_type +%type <val> tcp_hdr_option_sack +%type <val> tcpopt_field_maxseg tcpopt_field_mptcp tcpopt_field_sack tcpopt_field_tsopt tcpopt_field_window +%type <tcp_kind_field> tcp_hdr_option_kind_and_field + +%type <expr> inner_eth_expr inner_inet_expr inner_expr +%destructor { expr_free($$); } inner_eth_expr inner_inet_expr inner_expr + +%type <expr> vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr +%destructor { expr_free($$); } vxlan_hdr_expr geneve_hdr_expr gre_hdr_expr gretap_hdr_expr +%type <val> vxlan_hdr_field geneve_hdr_field gre_hdr_field + +%type <stmt> optstrip_stmt +%destructor { stmt_free($$); } optstrip_stmt + +%type <stmt> xt_stmt +%destructor { stmt_free($$); } xt_stmt + +%type <expr> boolean_expr +%destructor { expr_free($$); } boolean_expr +%type <val8> boolean_keys + +%type <expr> exthdr_exists_expr +%destructor { expr_free($$); } exthdr_exists_expr +%type <val> exthdr_key + +%type <val> ct_l4protoname ct_obj_type ct_cmd_type + +%type <list> timeout_states timeout_state +%destructor { xfree($$); } timeout_states timeout_state + +%type <val> xfrm_state_key xfrm_state_proto_key xfrm_dir xfrm_spnum +%type <expr> xfrm_expr +%destructor { expr_free($$); } xfrm_expr + +%type <expr> set_elem_key_expr +%destructor { expr_free($$); } set_elem_key_expr + +%% + +input : /* empty */ + | input line + { + if ($2 != NULL) { + $2->location = @2; + list_add_tail(&$2->list, state->cmds); + } + } + ; + +stmt_separator : NEWLINE + | SEMICOLON + ; + +opt_newline : NEWLINE + | /* empty */ + ; + +close_scope_ah : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_AH); }; +close_scope_arp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ARP); }; +close_scope_at : { scanner_pop_start_cond(nft->scanner, PARSER_SC_AT); }; +close_scope_comp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_COMP); }; +close_scope_ct : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CT); }; +close_scope_counter : { scanner_pop_start_cond(nft->scanner, PARSER_SC_COUNTER); }; +close_scope_last : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LAST); }; +close_scope_dccp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DCCP); }; +close_scope_destroy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_DESTROY); }; +close_scope_dst : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_DST); }; +close_scope_dup : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_DUP); }; +close_scope_esp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_ESP); }; +close_scope_eth : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ETH); }; +close_scope_export : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_EXPORT); }; +close_scope_fib : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FIB); }; +close_scope_frag : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_FRAG); }; +close_scope_fwd : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_FWD); }; +close_scope_gre : { scanner_pop_start_cond(nft->scanner, PARSER_SC_GRE); }; +close_scope_hash : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HASH); }; +close_scope_hbh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_HBH); }; +close_scope_ip : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP); }; +close_scope_ip6 : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IP6); }; +close_scope_vlan : { scanner_pop_start_cond(nft->scanner, PARSER_SC_VLAN); }; +close_scope_icmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_ICMP); }; +close_scope_igmp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_IGMP); }; +close_scope_import : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_IMPORT); }; +close_scope_ipsec : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_IPSEC); }; +close_scope_list : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_LIST); }; +close_scope_limit : { scanner_pop_start_cond(nft->scanner, PARSER_SC_LIMIT); }; +close_scope_meta : { scanner_pop_start_cond(nft->scanner, PARSER_SC_META); }; +close_scope_mh : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_MH); }; +close_scope_monitor : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_MONITOR); }; +close_scope_nat : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_NAT); }; +close_scope_numgen : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_NUMGEN); }; +close_scope_osf : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_OSF); }; +close_scope_policy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_POLICY); }; +close_scope_quota : { scanner_pop_start_cond(nft->scanner, PARSER_SC_QUOTA); }; +close_scope_queue : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_QUEUE); }; +close_scope_reject : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_REJECT); }; +close_scope_reset : { scanner_pop_start_cond(nft->scanner, PARSER_SC_CMD_RESET); }; +close_scope_rt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_RT); }; +close_scope_sctp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SCTP); }; +close_scope_sctp_chunk : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SCTP_CHUNK); }; +close_scope_secmark : { scanner_pop_start_cond(nft->scanner, PARSER_SC_SECMARK); }; +close_scope_socket : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_SOCKET); } +close_scope_tcp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TCP); }; +close_scope_tproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_TPROXY); }; +close_scope_type : { scanner_pop_start_cond(nft->scanner, PARSER_SC_TYPE); }; +close_scope_th : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_TH); }; +close_scope_udp : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDP); }; +close_scope_udplite : { scanner_pop_start_cond(nft->scanner, PARSER_SC_EXPR_UDPLITE); }; + +close_scope_log : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_LOG); } +close_scope_synproxy : { scanner_pop_start_cond(nft->scanner, PARSER_SC_STMT_SYNPROXY); } +close_scope_xt : { scanner_pop_start_cond(nft->scanner, PARSER_SC_XT); } + +common_block : INCLUDE QUOTED_STRING stmt_separator + { + if (scanner_include_file(nft, scanner, $2, &@$) < 0) { + xfree($2); + YYERROR; + } + xfree($2); + } + | DEFINE identifier '=' initializer_expr stmt_separator + { + struct scope *scope = current_scope(state); + + if (symbol_lookup(scope, $2) != NULL) { + erec_queue(error(&@2, "redefinition of symbol '%s'", $2), + state->msgs); + expr_free($4); + xfree($2); + YYERROR; + } + + symbol_bind(scope, $2, $4); + xfree($2); + } + | REDEFINE identifier '=' initializer_expr stmt_separator + { + struct scope *scope = current_scope(state); + + symbol_bind(scope, $2, $4); + xfree($2); + } + | UNDEFINE identifier stmt_separator + { + struct scope *scope = current_scope(state); + + if (symbol_unbind(scope, $2) < 0) { + erec_queue(error(&@2, "undefined symbol '%s'", $2), + state->msgs); + xfree($2); + YYERROR; + } + xfree($2); + } + | error stmt_separator + { + if (++state->nerrs == nft->parser_max_errors) + YYABORT; + yyerrok; + } + ; + +line : common_block { $$ = NULL; } + | stmt_separator { $$ = NULL; } + | base_cmd stmt_separator { $$ = $1; } + | base_cmd TOKEN_EOF + { + /* + * Very hackish workaround for bison >= 2.4: previous versions + * terminated parsing after EOF, 2.4+ tries to get further input + * in 'input' and calls the scanner again, causing a crash when + * the final input buffer has been popped. Terminate manually to + * avoid this. The correct fix should be to adjust the grammar + * to accept EOF in input, but for unknown reasons it does not + * work. + */ + if ($1 != NULL) { + $1->location = @1; + list_add_tail(&$1->list, state->cmds); + } + $$ = NULL; + YYACCEPT; + } + ; + +base_cmd : /* empty */ add_cmd { $$ = $1; } + | ADD add_cmd { $$ = $2; } + | REPLACE replace_cmd { $$ = $2; } + | CREATE create_cmd { $$ = $2; } + | INSERT insert_cmd { $$ = $2; } + | DELETE delete_cmd { $$ = $2; } + | GET get_cmd { $$ = $2; } + | LIST list_cmd close_scope_list { $$ = $2; } + | RESET reset_cmd close_scope_reset { $$ = $2; } + | FLUSH flush_cmd { $$ = $2; } + | RENAME rename_cmd { $$ = $2; } + | IMPORT import_cmd close_scope_import { $$ = $2; } + | EXPORT export_cmd close_scope_export { $$ = $2; } + | MONITOR monitor_cmd close_scope_monitor { $$ = $2; } + | DESCRIBE describe_cmd { $$ = $2; } + | DESTROY destroy_cmd close_scope_destroy { $$ = $2; } + ; + +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); + close_scope(state); + $$ = 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 '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + close_scope(state); + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_CHAIN, &$2, &@$, $5); + } + | RULE rule_position rule + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$2, &@$, $3); + } + | /* empty */ rule_position rule + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_RULE, &$1, &@$, $2); + } + | SET set_spec set_block_alloc + '{' set_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5); + } + | MAP set_spec map_block_alloc + '{' map_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SET, &$2, &@$, $5); + } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | FLOWTABLE flowtable_spec flowtable_block_alloc + '{' flowtable_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_FLOWTABLE, &$2, &@$, $5); + } + | COUNTER obj_spec close_scope_counter + { + struct obj *obj; + + obj = obj_alloc(&@$); + obj->type = NFT_OBJECT_COUNTER; + handle_merge(&obj->handle, &$2); + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, obj); + } + | COUNTER obj_spec counter_obj counter_config close_scope_counter + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3); + } + | COUNTER obj_spec counter_obj '{' counter_block '}' close_scope_counter + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_COUNTER, &$2, &@$, $3); + } + | QUOTA obj_spec quota_obj quota_config close_scope_quota + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3); + } + | QUOTA obj_spec quota_obj '{' quota_block '}' close_scope_quota + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_QUOTA, &$2, &@$, $3); + } + | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_HELPER, &$3, &@$, $4); + } + | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4); + } + | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_ADD, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4); + } + | LIMIT obj_spec limit_obj limit_config close_scope_limit + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3); + } + | LIMIT obj_spec limit_obj '{' limit_block '}' close_scope_limit + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3); + } + | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3); + } + | SECMARK obj_spec secmark_obj '{' secmark_block '}' close_scope_secmark + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3); + } + | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3); + } + | SYNPROXY obj_spec synproxy_obj '{' synproxy_block '}' close_scope_synproxy + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SYNPROXY, &$2, &@$, $3); + } + ; + +replace_cmd : RULE ruleid_spec rule + { + $$ = cmd_alloc(CMD_REPLACE, CMD_OBJ_RULE, &$2, &@$, $3); + } + ; + +create_cmd : TABLE table_spec + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &$2, &@$, NULL); + } + | TABLE table_spec table_block_alloc + '{' table_block '}' + { + handle_merge(&$3->handle, &$2); + close_scope(state); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_TABLE, &$2, &@$, $5); + } + | CHAIN chain_spec + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &$2, &@$, NULL); + } + | CHAIN chain_spec chain_block_alloc + '{' chain_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + close_scope(state); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_CHAIN, &$2, &@$, $5); + } + | SET set_spec set_block_alloc + '{' set_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &$2, &@$, $5); + } + | MAP set_spec map_block_alloc + '{' map_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SET, &$2, &@$, $5); + } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | FLOWTABLE flowtable_spec flowtable_block_alloc + '{' flowtable_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5); + } + | COUNTER obj_spec close_scope_counter + { + struct obj *obj; + + obj = obj_alloc(&@$); + obj->type = NFT_OBJECT_COUNTER; + handle_merge(&obj->handle, &$2); + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, obj); + } + | COUNTER obj_spec counter_obj counter_config close_scope_counter + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_COUNTER, &$2, &@$, $3); + } + | QUOTA obj_spec quota_obj quota_config close_scope_quota + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_QUOTA, &$2, &@$, $3); + } + | CT HELPER obj_spec ct_obj_alloc '{' ct_helper_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_HELPER, &$3, &@$, $4); + } + | CT TIMEOUT obj_spec ct_obj_alloc '{' ct_timeout_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_TIMEOUT, &$3, &@$, $4); + } + | CT EXPECTATION obj_spec ct_obj_alloc '{' ct_expect_block '}' close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_CREATE, NFT_OBJECT_CT_EXPECT, &$3, &@$, $4); + } + | LIMIT obj_spec limit_obj limit_config close_scope_limit + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3); + } + | SECMARK obj_spec secmark_obj secmark_config close_scope_secmark + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3); + } + | SYNPROXY obj_spec synproxy_obj synproxy_config close_scope_synproxy + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SYNPROXY, &$2, &@$, $3); + } + ; + +insert_cmd : RULE rule_position rule + { + $$ = cmd_alloc(CMD_INSERT, CMD_OBJ_RULE, &$2, &@$, $3); + } + ; + +table_or_id_spec : table_spec + | tableid_spec + ; + +chain_or_id_spec : chain_spec + | chainid_spec + ; + +set_or_id_spec : set_spec + | setid_spec + ; + +obj_or_id_spec : obj_spec + | objid_spec + ; + +delete_cmd : TABLE table_or_id_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_TABLE, &$2, &@$, NULL); + } + | CHAIN chain_or_id_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, NULL); + } + | CHAIN chain_spec chain_block_alloc + '{' chain_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_CHAIN, &$2, &@$, $5); + } + | RULE ruleid_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_RULE, &$2, &@$, NULL); + } + | SET set_or_id_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); + } + | MAP set_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SET, &$2, &@$, NULL); + } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | FLOWTABLE flowtable_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL); + } + | FLOWTABLE flowtableid_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL); + } + | FLOWTABLE flowtable_spec flowtable_block_alloc + '{' flowtable_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_FLOWTABLE, &$2, &@$, $5); + } + | COUNTER obj_or_id_spec close_scope_counter + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } + | QUOTA obj_or_id_spec close_scope_quota + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_QUOTA, &$2, &@$, NULL); + } + | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_DELETE, $2, &$3, &@$, $4); + if ($2 == NFT_OBJECT_CT_TIMEOUT) + init_list_head(&$4->ct_timeout.timeout_list); + } + | LIMIT obj_or_id_spec close_scope_limit + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL); + } + | SECMARK obj_or_id_spec close_scope_secmark + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } + | SYNPROXY obj_or_id_spec close_scope_synproxy + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SYNPROXY, &$2, &@$, NULL); + } + ; + +destroy_cmd : TABLE table_or_id_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_TABLE, &$2, &@$, NULL); + } + | CHAIN chain_or_id_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_CHAIN, &$2, &@$, NULL); + } + | RULE ruleid_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_RULE, &$2, &@$, NULL); + } + | SET set_or_id_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL); + } + | MAP set_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SET, &$2, &@$, NULL); + } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | FLOWTABLE flowtable_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL); + } + | FLOWTABLE flowtableid_spec + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL); + } + | FLOWTABLE flowtable_spec flowtable_block_alloc + '{' flowtable_block '}' + { + $5->location = @5; + handle_merge(&$3->handle, &$2); + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_FLOWTABLE, &$2, &@$, $5); + } + | COUNTER obj_or_id_spec close_scope_counter + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } + | QUOTA obj_or_id_spec close_scope_quota + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_QUOTA, &$2, &@$, NULL); + } + | CT ct_obj_type obj_spec ct_obj_alloc close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_DESTROY, $2, &$3, &@$, $4); + if ($2 == NFT_OBJECT_CT_TIMEOUT) + init_list_head(&$4->ct_timeout.timeout_list); + } + | LIMIT obj_or_id_spec close_scope_limit + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_LIMIT, &$2, &@$, NULL); + } + | SECMARK obj_or_id_spec close_scope_secmark + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } + | SYNPROXY obj_or_id_spec close_scope_synproxy + { + $$ = cmd_alloc(CMD_DESTROY, CMD_OBJ_SYNPROXY, &$2, &@$, NULL); + } + ; + + +get_cmd : ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_GET, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + ; + +list_cmd : TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, &@$, NULL); + } + | TABLES ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_TABLE, &$2, &@$, NULL); + } + | CHAIN chain_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAIN, &$2, &@$, NULL); + } + | CHAINS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_CHAINS, &$2, &@$, NULL); + } + | SETS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$2, &@$, NULL); + } + | SETS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SETS, &$3, &@$, NULL); + } + | SET set_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SET, &$2, &@$, NULL); + } + | COUNTERS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$2, &@$, NULL); + } + | COUNTERS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTERS, &$3, &@$, NULL); + } + | COUNTER obj_spec close_scope_counter + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_COUNTER, &$2, &@$, NULL); + } + | QUOTAS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$2, &@$, NULL); + } + | QUOTAS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTAS, &$3, &@$, NULL); + } + | QUOTA obj_spec close_scope_quota + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_QUOTA, &$2, &@$, NULL); + } + | LIMITS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$2, &@$, NULL); + } + | LIMITS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMITS, &$3, &@$, NULL); + } + | LIMIT obj_spec close_scope_limit + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL); + } + | SECMARKS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$2, &@$, NULL); + } + | SECMARKS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$3, &@$, NULL); + } + | SECMARK obj_spec close_scope_secmark + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } + | SYNPROXYS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$2, &@$, NULL); + } + | SYNPROXYS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXYS, &$3, &@$, NULL); + } + | SYNPROXY obj_spec close_scope_synproxy + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SYNPROXY, &$2, &@$, NULL); + } + | RULESET ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL); + } + | FLOW TABLES ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &$3, &@$, NULL); + } + | FLOW TABLE set_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$3, &@$, NULL); + } + | METERS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METERS, &$2, &@$, NULL); + } + | METER set_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_METER, &$2, &@$, NULL); + } + | FLOWTABLES ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLES, &$2, &@$, NULL); + } + | FLOWTABLE flowtable_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_FLOWTABLE, &$2, &@$, NULL); + } + | MAPS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAPS, &$2, &@$, NULL); + } + | MAP set_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_MAP, &$2, &@$, NULL); + } + | CT ct_obj_type obj_spec close_scope_ct + { + $$ = cmd_alloc_obj_ct(CMD_LIST, $2, &$3, &@$, NULL); + } + | CT ct_cmd_type TABLE table_spec close_scope_ct + { + $$ = cmd_alloc(CMD_LIST, $2, &$4, &@$, NULL); + } + | HOOKS basehook_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_HOOKS, &$2, &@$, NULL); + } + ; + +basehook_device_name : DEVICE STRING + { + $$ = $2; + } + ; + +basehook_spec : ruleset_spec + { + $$ = $1; + } + | ruleset_spec basehook_device_name + { + if ($2) { + $1.obj.name = $2; + $1.obj.location = @2; + } + $$ = $1; + } + ; + +reset_cmd : COUNTERS ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL); + } + | COUNTERS table_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$2, &@$, NULL); + } + | COUNTERS TABLE table_spec + { + /* alias of previous rule. */ + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTERS, &$3, &@$, NULL); + } + | COUNTER obj_spec close_scope_counter + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_COUNTER, &$2,&@$, NULL); + } + | QUOTAS ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL); + } + | QUOTAS TABLE table_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$3, &@$, NULL); + } + | QUOTAS table_spec + { + /* alias of previous rule. */ + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTAS, &$2, &@$, NULL); + } + | QUOTA obj_spec close_scope_quota + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_QUOTA, &$2, &@$, NULL); + } + | RULES ruleset_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL); + } + | RULES table_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL); + } + | RULES TABLE table_spec + { + /* alias of previous rule. */ + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL); + } + | RULES chain_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$2, &@$, NULL); + } + | RULES CHAIN chain_spec + { + /* alias of previous rule. */ + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULES, &$3, &@$, NULL); + } + | RULE ruleid_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_RULE, &$2, &@$, NULL); + } + | ELEMENT set_spec set_block_expr + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_ELEMENTS, &$2, &@$, $3); + } + | SET set_or_id_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_SET, &$2, &@$, NULL); + } + | MAP set_or_id_spec + { + $$ = cmd_alloc(CMD_RESET, CMD_OBJ_MAP, &$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); + } + | SET set_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_SET, &$2, &@$, NULL); + } + | MAP set_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_MAP, &$2, &@$, NULL); + } + | FLOW TABLE set_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &$3, &@$, NULL); + } + | METER set_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_METER, &$2, &@$, NULL); + } + | RULESET ruleset_spec + { + $$ = cmd_alloc(CMD_FLUSH, CMD_OBJ_RULESET, &$2, &@$, NULL); + } + ; + +rename_cmd : CHAIN chain_spec identifier + { + $$ = cmd_alloc(CMD_RENAME, CMD_OBJ_CHAIN, &$2, &@$, NULL); + $$->arg = $3; + } + ; + +import_cmd : RULESET markup_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc($2); + $$ = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &@$, markup); + } + | markup_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc($1); + $$ = cmd_alloc(CMD_IMPORT, CMD_OBJ_MARKUP, &h, &@$, markup); + } + ; + +export_cmd : RULESET markup_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc($2); + $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &@$, markup); + } + | markup_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct markup *markup = markup_alloc($1); + $$ = cmd_alloc(CMD_EXPORT, CMD_OBJ_MARKUP, &h, &@$, markup); + } + ; + +monitor_cmd : monitor_event monitor_object monitor_format + { + struct handle h = { .family = NFPROTO_UNSPEC }; + struct monitor *m = monitor_alloc($3, $2, $1); + m->location = @1; + $$ = cmd_alloc(CMD_MONITOR, CMD_OBJ_MONITOR, &h, &@$, m); + } + ; + +monitor_event : /* empty */ { $$ = NULL; } + | STRING { $$ = $1; } + ; + +monitor_object : /* empty */ { $$ = CMD_MONITOR_OBJ_ANY; } + | TABLES { $$ = CMD_MONITOR_OBJ_TABLES; } + | CHAINS { $$ = CMD_MONITOR_OBJ_CHAINS; } + | SETS { $$ = CMD_MONITOR_OBJ_SETS; } + | RULES { $$ = CMD_MONITOR_OBJ_RULES; } + | ELEMENTS { $$ = CMD_MONITOR_OBJ_ELEMS; } + | RULESET { $$ = CMD_MONITOR_OBJ_RULESET; } + | TRACE { $$ = CMD_MONITOR_OBJ_TRACE; } + ; + +monitor_format : /* empty */ { $$ = NFTNL_OUTPUT_DEFAULT; } + | markup_format + ; + +markup_format : XML { $$ = __NFT_OUTPUT_NOTSUPP; } + | JSON { $$ = NFTNL_OUTPUT_JSON; } + | VM JSON { $$ = NFTNL_OUTPUT_JSON; } + ; + +describe_cmd : primary_expr + { + struct handle h = { .family = NFPROTO_UNSPEC }; + $$ = cmd_alloc(CMD_DESCRIBE, CMD_OBJ_EXPR, &h, &@$, NULL); + $$->expr = $1; + } + ; + +table_block_alloc : /* empty */ + { + $$ = table_alloc(); + if (open_scope(state, &$$->scope) < 0) { + erec_queue(error(&@$, "too many levels of nesting"), + state->msgs); + state->nerrs++; + } + } + ; + +table_options : FLAGS STRING + { + if (strcmp($2, "dormant") == 0) { + $<table>0->flags |= TABLE_F_DORMANT; + xfree($2); + } else if (strcmp($2, "owner") == 0) { + $<table>0->flags |= TABLE_F_OWNER; + xfree($2); + } else { + erec_queue(error(&@2, "unknown table option %s", $2), + state->msgs); + xfree($2); + YYERROR; + } + } + | comment_spec + { + if (already_set($<table>0->comment, &@$, state)) { + xfree($1); + YYERROR; + } + $<table>0->comment = $1; + } + ; + +table_block : /* empty */ { $$ = $<table>-1; } + | table_block common_block + | table_block stmt_separator + | table_block table_options stmt_separator + | table_block CHAIN chain_identifier + chain_block_alloc '{' chain_block '}' + stmt_separator + { + $4->location = @3; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + close_scope(state); + list_add_tail(&$4->list, &$1->chains); + $$ = $1; + } + | table_block SET set_identifier + set_block_alloc '{' set_block '}' + stmt_separator + { + $4->location = @3; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->sets); + $$ = $1; + } + | table_block MAP set_identifier + map_block_alloc '{' map_block '}' + stmt_separator + { + $4->location = @3; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->sets); + $$ = $1; + } + + | table_block FLOWTABLE flowtable_identifier + flowtable_block_alloc '{' flowtable_block '}' + stmt_separator + { + $4->location = @3; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->flowtables); + $$ = $1; + } + | table_block COUNTER obj_identifier + obj_block_alloc '{' counter_block '}' + stmt_separator close_scope_counter + { + $4->location = @3; + $4->type = NFT_OBJECT_COUNTER; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } + | table_block QUOTA obj_identifier + obj_block_alloc '{' quota_block '}' + stmt_separator close_scope_quota + { + $4->location = @3; + $4->type = NFT_OBJECT_QUOTA; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } + | table_block CT HELPER obj_identifier obj_block_alloc '{' ct_helper_block '}' close_scope_ct stmt_separator + { + $5->location = @4; + $5->type = NFT_OBJECT_CT_HELPER; + handle_merge(&$5->handle, &$4); + handle_free(&$4); + list_add_tail(&$5->list, &$1->objs); + $$ = $1; + } + | table_block CT TIMEOUT obj_identifier obj_block_alloc '{' ct_timeout_block '}' close_scope_ct stmt_separator + { + $5->location = @4; + $5->type = NFT_OBJECT_CT_TIMEOUT; + handle_merge(&$5->handle, &$4); + handle_free(&$4); + list_add_tail(&$5->list, &$1->objs); + $$ = $1; + } + | table_block CT EXPECTATION obj_identifier obj_block_alloc '{' ct_expect_block '}' close_scope_ct stmt_separator + { + $5->location = @4; + $5->type = NFT_OBJECT_CT_EXPECT; + handle_merge(&$5->handle, &$4); + handle_free(&$4); + list_add_tail(&$5->list, &$1->objs); + $$ = $1; + } + | table_block LIMIT obj_identifier + obj_block_alloc '{' limit_block '}' + stmt_separator close_scope_limit + { + $4->location = @3; + $4->type = NFT_OBJECT_LIMIT; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } + | table_block SECMARK obj_identifier + obj_block_alloc '{' secmark_block '}' + stmt_separator close_scope_secmark + { + $4->location = @3; + $4->type = NFT_OBJECT_SECMARK; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } + | table_block SYNPROXY obj_identifier + obj_block_alloc '{' synproxy_block '}' + stmt_separator close_scope_synproxy + { + $4->location = @3; + $4->type = NFT_OBJECT_SYNPROXY; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } + ; + +chain_block_alloc : /* empty */ + { + $$ = chain_alloc(); + if (open_scope(state, &$$->scope) < 0) { + erec_queue(error(&@$, "too many levels of nesting"), + state->msgs); + state->nerrs++; + } + } + ; + +chain_block : /* empty */ { $$ = $<chain>-1; } + | chain_block common_block + | chain_block stmt_separator + | chain_block hook_spec stmt_separator + | chain_block policy_spec stmt_separator + | chain_block flags_spec stmt_separator + | chain_block rule stmt_separator + { + list_add_tail(&$2->list, &$1->rules); + $$ = $1; + } + | chain_block DEVICES '=' flowtable_expr stmt_separator + { + if ($$->dev_expr) { + list_splice_init(&$4->expressions, &$$->dev_expr->expressions); + expr_free($4); + break; + } + $$->dev_expr = $4; + } + | chain_block comment_spec stmt_separator + { + if (already_set($1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $1->comment = $2; + } + ; + +subchain_block : /* empty */ { $$ = $<chain>-1; } + | subchain_block stmt_separator + | subchain_block rule stmt_separator + { + list_add_tail(&$2->list, &$1->rules); + $$ = $1; + } + ; + +typeof_data_expr : primary_expr + { + struct expr *e = $1; + + if (e->etype == EXPR_SYMBOL && + strcmp("verdict", e->identifier) == 0) { + struct expr *v = verdict_expr_alloc(&@1, NF_ACCEPT, NULL); + + expr_free(e); + v->flags &= ~EXPR_F_CONSTANT; + e = v; + } + + if (expr_ops(e)->build_udata == NULL) { + erec_queue(error(&@1, "map data type '%s' lacks typeof serialization", expr_ops(e)->name), + state->msgs); + expr_free(e); + YYERROR; + } + $$ = e; + } + | typeof_expr DOT primary_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + +typeof_expr : primary_expr + { + if (expr_ops($1)->build_udata == NULL) { + erec_queue(error(&@1, "primary expression type '%s' lacks typeof serialization", expr_ops($1)->name), + state->msgs); + expr_free($1); + YYERROR; + } + + $$ = $1; + } + | typeof_expr DOT primary_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + + +set_block_alloc : /* empty */ + { + $$ = set_alloc(&internal_location); + } + ; + +set_block : /* empty */ { $$ = $<set>-1; } + | set_block common_block + | set_block stmt_separator + | set_block TYPE data_type_expr stmt_separator close_scope_type + { + $1->key = $3; + $$ = $1; + } + | set_block TYPEOF typeof_expr stmt_separator + { + $1->key = $3; + datatype_set($1->key, $3->dtype); + $$ = $1; + } + | set_block FLAGS set_flag_list stmt_separator + { + $1->flags = $3; + $$ = $1; + } + | set_block TIMEOUT time_spec stmt_separator + { + $1->timeout = $3; + $$ = $1; + } + | set_block GC_INTERVAL time_spec stmt_separator + { + $1->gc_int = $3; + $$ = $1; + } + | set_block stateful_stmt_list stmt_separator + { + list_splice_tail($2, &$1->stmt_list); + $$ = $1; + free($2); + } + | set_block ELEMENTS '=' set_block_expr + { + $1->init = $4; + $$ = $1; + } + | set_block AUTOMERGE + { + $1->automerge = true; + $$ = $1; + } + | set_block set_mechanism stmt_separator + | set_block comment_spec stmt_separator + { + if (already_set($1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $1->comment = $2; + $$ = $1; + } + ; + +set_block_expr : set_expr + | variable_expr + ; + +set_flag_list : set_flag_list COMMA set_flag + { + $$ = $1 | $3; + } + | set_flag + ; + +set_flag : CONSTANT { $$ = NFT_SET_CONSTANT; } + | INTERVAL { $$ = NFT_SET_INTERVAL; } + | TIMEOUT { $$ = NFT_SET_TIMEOUT; } + | DYNAMIC { $$ = NFT_SET_EVAL; } + ; + +map_block_alloc : /* empty */ + { + $$ = set_alloc(&internal_location); + } + ; + +map_block_obj_type : COUNTER close_scope_counter { $$ = NFT_OBJECT_COUNTER; } + | QUOTA close_scope_quota { $$ = NFT_OBJECT_QUOTA; } + | LIMIT close_scope_limit { $$ = NFT_OBJECT_LIMIT; } + | SECMARK close_scope_secmark { $$ = NFT_OBJECT_SECMARK; } + | SYNPROXY close_scope_synproxy { $$ = NFT_OBJECT_SYNPROXY; } + ; + +map_block_data_interval : INTERVAL { $$ = EXPR_F_INTERVAL; } + | { $$ = 0; } + ; + +map_block : /* empty */ { $$ = $<set>-1; } + | map_block common_block + | map_block stmt_separator + | map_block TIMEOUT time_spec stmt_separator + { + $1->timeout = $3; + $$ = $1; + } + | map_block GC_INTERVAL time_spec stmt_separator + { + $1->gc_int = $3; + $$ = $1; + } + | map_block TYPE + data_type_expr COLON map_block_data_interval data_type_expr + stmt_separator close_scope_type + { + $1->key = $3; + $1->data = $6; + $1->data->flags |= $5; + + $1->flags |= NFT_SET_MAP; + $$ = $1; + } + | map_block TYPEOF + typeof_expr COLON typeof_data_expr + stmt_separator + { + $1->key = $3; + datatype_set($1->key, $3->dtype); + $1->data = $5; + + $1->flags |= NFT_SET_MAP; + $$ = $1; + } + | map_block TYPEOF + typeof_expr COLON INTERVAL typeof_expr + stmt_separator + { + $1->key = $3; + datatype_set($1->key, $3->dtype); + $1->data = $6; + $1->data->flags |= EXPR_F_INTERVAL; + + $1->flags |= NFT_SET_MAP; + $$ = $1; + } + | map_block TYPE + data_type_expr COLON map_block_obj_type + stmt_separator close_scope_type + { + $1->key = $3; + $1->objtype = $5; + $1->flags |= NFT_SET_OBJECT; + $$ = $1; + } + | map_block FLAGS set_flag_list stmt_separator + { + $1->flags |= $3; + $$ = $1; + } + | map_block stateful_stmt_list stmt_separator + { + list_splice_tail($2, &$1->stmt_list); + $$ = $1; + free($2); + } + | map_block ELEMENTS '=' set_block_expr + { + $1->init = $4; + $$ = $1; + } + | map_block comment_spec stmt_separator + { + if (already_set($1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $1->comment = $2; + $$ = $1; + } + | map_block set_mechanism stmt_separator + ; + +set_mechanism : POLICY set_policy_spec close_scope_policy + { + $<set>0->policy = $2; + } + | SIZE NUM + { + $<set>0->desc.size = $2; + } + ; + +set_policy_spec : PERFORMANCE { $$ = NFT_SET_POL_PERFORMANCE; } + | MEMORY { $$ = NFT_SET_POL_MEMORY; } + ; + +flowtable_block_alloc : /* empty */ + { + $$ = flowtable_alloc(&internal_location); + } + ; + +flowtable_block : /* empty */ { $$ = $<flowtable>-1; } + | flowtable_block common_block + | flowtable_block stmt_separator + | flowtable_block HOOK STRING prio_spec stmt_separator + { + $$->hook.loc = @3; + $$->hook.name = chain_hookname_lookup($3); + if ($$->hook.name == NULL) { + erec_queue(error(&@3, "unknown chain hook"), + state->msgs); + xfree($3); + YYERROR; + } + xfree($3); + + $$->priority = $4; + } + | flowtable_block DEVICES '=' flowtable_expr stmt_separator + { + $$->dev_expr = $4; + } + | flowtable_block COUNTER close_scope_counter + { + $$->flags |= NFT_FLOWTABLE_COUNTER; + } + | flowtable_block FLAGS OFFLOAD stmt_separator + { + $$->flags |= FLOWTABLE_F_HW_OFFLOAD; + } + ; + +flowtable_expr : '{' flowtable_list_expr '}' + { + $2->location = @$; + $$ = $2; + } + | variable_expr + { + $1->location = @$; + $$ = $1; + } + ; + +flowtable_list_expr : flowtable_expr_member + { + $$ = compound_expr_alloc(&@$, EXPR_LIST); + compound_expr_add($$, $1); + } + | flowtable_list_expr COMMA flowtable_expr_member + { + compound_expr_add($1, $3); + $$ = $1; + } + | flowtable_list_expr COMMA opt_newline + ; + +flowtable_expr_member : QUOTED_STRING + { + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1); + + if (!expr) + YYERROR; + + $$ = expr; + } + | STRING + { + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $1); + + if (!expr) + YYERROR; + + $$ = expr; + } + | variable_expr + { + datatype_set($1->sym->expr, &ifname_type); + $$ = $1; + } + ; + +data_type_atom_expr : type_identifier + { + const struct datatype *dtype = datatype_lookup_byname($1); + if (dtype == NULL) { + erec_queue(error(&@1, "unknown datatype %s", $1), + state->msgs); + xfree($1); + YYERROR; + } + $$ = constant_expr_alloc(&@1, dtype, dtype->byteorder, + dtype->size, NULL); + xfree($1); + } + | TIME + { + $$ = constant_expr_alloc(&@1, &time_type, time_type.byteorder, + time_type.size, NULL); + } + ; + +data_type_expr : data_type_atom_expr + | data_type_expr DOT data_type_atom_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + +obj_block_alloc : /* empty */ + { + $$ = obj_alloc(&internal_location); + } + ; + +counter_block : /* empty */ { $$ = $<obj>-1; } + | counter_block common_block + | counter_block stmt_separator + | counter_block counter_config + { + $$ = $1; + } + | counter_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +quota_block : /* empty */ { $$ = $<obj>-1; } + | quota_block common_block + | quota_block stmt_separator + | quota_block quota_config + { + $$ = $1; + } + | quota_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +ct_helper_block : /* empty */ { $$ = $<obj>-1; } + | ct_helper_block common_block + | ct_helper_block stmt_separator + | ct_helper_block ct_helper_config + { + $$ = $1; + } + | ct_helper_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +ct_timeout_block : /*empty */ + { + $$ = $<obj>-1; + init_list_head(&$$->ct_timeout.timeout_list); + } + | ct_timeout_block common_block + | ct_timeout_block stmt_separator + | ct_timeout_block ct_timeout_config + { + $$ = $1; + } + | ct_timeout_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +ct_expect_block : /*empty */ { $$ = $<obj>-1; } + | ct_expect_block common_block + | ct_expect_block stmt_separator + | ct_expect_block ct_expect_config + { + $$ = $1; + } + | ct_expect_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +limit_block : /* empty */ { $$ = $<obj>-1; } + | limit_block common_block + | limit_block stmt_separator + | limit_block limit_config + { + $$ = $1; + } + | limit_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +secmark_block : /* empty */ { $$ = $<obj>-1; } + | secmark_block common_block + | secmark_block stmt_separator + | secmark_block secmark_config + { + $$ = $1; + } + | secmark_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +synproxy_block : /* empty */ { $$ = $<obj>-1; } + | synproxy_block common_block + | synproxy_block stmt_separator + | synproxy_block synproxy_config + { + $$ = $1; + } + | synproxy_block comment_spec + { + if (already_set($<obj>1->comment, &@2, state)) { + xfree($2); + YYERROR; + } + $<obj>1->comment = $2; + } + ; + +type_identifier : STRING { $$ = $1; } + | MARK { $$ = xstrdup("mark"); } + | DSCP { $$ = xstrdup("dscp"); } + | ECN { $$ = xstrdup("ecn"); } + | CLASSID { $$ = xstrdup("classid"); } + ; + +hook_spec : TYPE close_scope_type STRING HOOK STRING dev_spec prio_spec + { + const char *chain_type = chain_type_name_lookup($3); + + if (chain_type == NULL) { + erec_queue(error(&@3, "unknown chain type"), + state->msgs); + xfree($3); + YYERROR; + } + $<chain>0->type.loc = @3; + $<chain>0->type.str = xstrdup(chain_type); + xfree($3); + + $<chain>0->loc = @$; + $<chain>0->hook.loc = @5; + $<chain>0->hook.name = chain_hookname_lookup($5); + if ($<chain>0->hook.name == NULL) { + erec_queue(error(&@5, "unknown chain hook"), + state->msgs); + xfree($5); + YYERROR; + } + xfree($5); + + $<chain>0->dev_expr = $6; + $<chain>0->priority = $7; + $<chain>0->flags |= CHAIN_F_BASECHAIN; + } + ; + +prio_spec : PRIORITY extended_prio_spec + { + $$ = $2; + $$.loc = @$; + } + ; + +extended_prio_name : OUT + { + $$ = strdup("out"); + } + | STRING + ; + +extended_prio_spec : int_num + { + struct prio_spec spec = {0}; + + spec.expr = constant_expr_alloc(&@$, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, &$1); + $$ = spec; + } + | variable_expr + { + struct prio_spec spec = {0}; + + spec.expr = $1; + $$ = spec; + } + | extended_prio_name + { + struct prio_spec spec = {0}; + + spec.expr = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen($1) * BITS_PER_BYTE, + $1); + xfree($1); + $$ = spec; + } + | extended_prio_name PLUS NUM + { + struct prio_spec spec = {0}; + + char str[NFT_NAME_MAXLEN]; + snprintf(str, sizeof(str), "%s + %" PRIu64, $1, $3); + spec.expr = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(str) * BITS_PER_BYTE, + str); + xfree($1); + $$ = spec; + } + | extended_prio_name DASH NUM + { + struct prio_spec spec = {0}; + char str[NFT_NAME_MAXLEN]; + + snprintf(str, sizeof(str), "%s - %" PRIu64, $1, $3); + spec.expr = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(str) * BITS_PER_BYTE, + str); + xfree($1); + $$ = spec; + } + ; + +int_num : NUM { $$ = $1; } + | DASH NUM { $$ = -$2; } + ; + +dev_spec : DEVICE string + { + struct expr *expr = ifname_expr_alloc(&@$, state->msgs, $2); + + if (!expr) + YYERROR; + + $$ = compound_expr_alloc(&@$, EXPR_LIST); + compound_expr_add($$, expr); + + } + | DEVICE variable_expr + { + datatype_set($2->sym->expr, &ifname_type); + $$ = compound_expr_alloc(&@$, EXPR_LIST); + compound_expr_add($$, $2); + } + | DEVICES '=' flowtable_expr + { + $$ = $3; + } + | /* empty */ { $$ = NULL; } + ; + +flags_spec : FLAGS OFFLOAD + { + $<chain>0->flags |= CHAIN_F_HW_OFFLOAD; + } + ; + +policy_spec : POLICY policy_expr close_scope_policy + { + if ($<chain>0->policy) { + erec_queue(error(&@$, "you cannot set chain policy twice"), + state->msgs); + expr_free($2); + YYERROR; + } + $<chain>0->policy = $2; + $<chain>0->policy->location = @$; + } + ; + +policy_expr : variable_expr + { + datatype_set($1->sym->expr, &policy_type); + $$ = $1; + } + | chain_policy + { + $$ = constant_expr_alloc(&@$, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * + BITS_PER_BYTE, &$1); + } + ; + +chain_policy : ACCEPT { $$ = NF_ACCEPT; } + | DROP { $$ = NF_DROP; } + ; + +identifier : STRING + | LAST { $$ = xstrdup("last"); } + ; + +string : STRING + | QUOTED_STRING + | ASTERISK_STRING + ; + +time_spec : STRING + { + struct error_record *erec; + uint64_t res; + + erec = time_parse(&@1, $1, &res); + xfree($1); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = res; + } + ; + +/* compatibility kludge to allow either 60, 60s, 1m, ... */ +time_spec_or_num_s : NUM + | time_spec { $$ = $1 / 1000u; } + ; + +family_spec : /* empty */ { $$ = NFPROTO_IPV4; } + | family_spec_explicit + ; + +family_spec_explicit : IP close_scope_ip { $$ = NFPROTO_IPV4; } + | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; } + | INET { $$ = NFPROTO_INET; } + | ARP close_scope_arp { $$ = NFPROTO_ARP; } + | BRIDGE { $$ = NFPROTO_BRIDGE; } + | NETDEV { $$ = NFPROTO_NETDEV; } + ; + +table_spec : family_spec identifier + { + memset(&$$, 0, sizeof($$)); + $$.family = $1; + $$.table.location = @2; + $$.table.name = $2; + } + ; + +tableid_spec : family_spec HANDLE NUM + { + memset(&$$, 0, sizeof($$)); + $$.family = $1; + $$.handle.id = $3; + $$.handle.location = @3; + } + ; + +chain_spec : table_spec identifier + { + $$ = $1; + $$.chain.name = $2; + $$.chain.location = @2; + } + ; + +chainid_spec : table_spec HANDLE NUM + { + $$ = $1; + $$.handle.location = @3; + $$.handle.id = $3; + } + ; + +chain_identifier : identifier + { + memset(&$$, 0, sizeof($$)); + $$.chain.name = $1; + $$.chain.location = @1; + } + ; + +set_spec : table_spec identifier + { + $$ = $1; + $$.set.name = $2; + $$.set.location = @2; + } + ; + +setid_spec : table_spec HANDLE NUM + { + $$ = $1; + $$.handle.location = @3; + $$.handle.id = $3; + } + ; + +set_identifier : identifier + { + memset(&$$, 0, sizeof($$)); + $$.set.name = $1; + $$.set.location = @1; + } + ; + +flowtable_spec : table_spec identifier + { + $$ = $1; + $$.flowtable.name = $2; + $$.flowtable.location = @2; + } + ; + +flowtableid_spec : table_spec HANDLE NUM + { + $$ = $1; + $$.handle.location = @3; + $$.handle.id = $3; + } + ; + +flowtable_identifier : identifier + { + memset(&$$, 0, sizeof($$)); + $$.flowtable.name = $1; + $$.flowtable.location = @1; + } + ; + +obj_spec : table_spec identifier + { + $$ = $1; + $$.obj.name = $2; + $$.obj.location = @2; + } + ; + +objid_spec : table_spec HANDLE NUM + { + $$ = $1; + $$.handle.location = @3; + $$.handle.id = $3; + } + ; + +obj_identifier : identifier + { + memset(&$$, 0, sizeof($$)); + $$.obj.name = $1; + $$.obj.location = @1; + } + ; + +handle_spec : HANDLE NUM + { + memset(&$$, 0, sizeof($$)); + $$.handle.location = @2; + $$.handle.id = $2; + } + ; + +position_spec : POSITION NUM + { + memset(&$$, 0, sizeof($$)); + $$.position.location = @$; + $$.position.id = $2; + } + ; + +index_spec : INDEX NUM + { + memset(&$$, 0, sizeof($$)); + $$.index.location = @$; + $$.index.id = $2 + 1; + } + ; + +rule_position : chain_spec + { + $$ = $1; + } + | chain_spec position_spec + { + handle_merge(&$1, &$2); + $$ = $1; + } + | chain_spec handle_spec + { + $2.position.location = $2.handle.location; + $2.position.id = $2.handle.id; + $2.handle.id = 0; + handle_merge(&$1, &$2); + $$ = $1; + } + | chain_spec index_spec + { + handle_merge(&$1, &$2); + $$ = $1; + } + ; + +ruleid_spec : chain_spec handle_spec + { + handle_merge(&$1, &$2); + $$ = $1; + } + ; + +comment_spec : COMMENT string + { + if (strlen($2) > NFTNL_UDATA_COMMENT_MAXLEN) { + erec_queue(error(&@2, "comment too long, %d characters maximum allowed", + NFTNL_UDATA_COMMENT_MAXLEN), + state->msgs); + xfree($2); + YYERROR; + } + $$ = $2; + } + ; + +ruleset_spec : /* empty */ + { + memset(&$$, 0, sizeof($$)); + $$.family = NFPROTO_UNSPEC; + } + | family_spec_explicit + { + memset(&$$, 0, sizeof($$)); + $$.family = $1; + } + ; + +rule : rule_alloc + { + $$->comment = NULL; + } + | rule_alloc comment_spec + { + $$->comment = $2; + } + ; + +rule_alloc : 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); + } + ; + +stateful_stmt_list : stateful_stmt + { + $$ = xmalloc(sizeof(*$$)); + init_list_head($$); + list_add_tail(&$1->list, $$); + } + | stateful_stmt_list stateful_stmt + { + $$ = $1; + list_add_tail(&$2->list, $1); + } + ; + +stateful_stmt : counter_stmt close_scope_counter + | limit_stmt + | quota_stmt + | connlimit_stmt + | last_stmt close_scope_last + ; + +stmt : verdict_stmt + | match_stmt + | meter_stmt + | payload_stmt + | stateful_stmt + | meta_stmt + | log_stmt close_scope_log + | reject_stmt close_scope_reject + | nat_stmt close_scope_nat + | tproxy_stmt close_scope_tproxy + | queue_stmt + | ct_stmt + | masq_stmt close_scope_nat + | redir_stmt close_scope_nat + | dup_stmt close_scope_dup + | fwd_stmt close_scope_fwd + | set_stmt + | map_stmt + | synproxy_stmt close_scope_synproxy + | chain_stmt + | optstrip_stmt + | xt_stmt close_scope_xt + ; + +xt_stmt : XT STRING string + { + $$ = NULL; + xfree($2); + xfree($3); + erec_queue(error(&@$, "unsupported xtables compat expression, use iptables-nft with this ruleset"), + state->msgs); + YYERROR; + } + ; + +chain_stmt_type : JUMP { $$ = NFT_JUMP; } + | GOTO { $$ = NFT_GOTO; } + ; + +chain_stmt : chain_stmt_type chain_block_alloc '{' subchain_block '}' + { + $2->location = @2; + close_scope(state); + $4->location = @4; + $$ = chain_stmt_alloc(&@$, $4, $1); + } + ; + +verdict_stmt : verdict_expr + { + $$ = verdict_stmt_alloc(&@$, $1); + } + | verdict_map_stmt + { + $$ = verdict_stmt_alloc(&@$, $1); + } + ; + +verdict_map_stmt : concat_expr VMAP verdict_map_expr + { + $$ = map_expr_alloc(&@$, $1, $3); + } + ; + +verdict_map_expr : '{' verdict_map_list_expr '}' + { + $2->location = @$; + $$ = $2; + } + | set_ref_expr + ; + +verdict_map_list_expr : verdict_map_list_member_expr + { + $$ = set_expr_alloc(&@$, NULL); + compound_expr_add($$, $1); + } + | verdict_map_list_expr COMMA verdict_map_list_member_expr + { + compound_expr_add($1, $3); + $$ = $1; + } + | verdict_map_list_expr COMMA opt_newline + ; + +verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline + { + $$ = mapping_expr_alloc(&@2, $2, $4); + } + ; + +connlimit_stmt : CT COUNT NUM close_scope_ct + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $3; + } + | CT COUNT OVER NUM close_scope_ct + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $4; + $$->connlimit.flags = NFT_CONNLIMIT_F_INV; + } + ; + +counter_stmt : counter_stmt_alloc + | counter_stmt_alloc counter_args + +counter_stmt_alloc : COUNTER + { + $$ = counter_stmt_alloc(&@$); + } + | COUNTER NAME stmt_expr + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_COUNTER; + $$->objref.expr = $3; + } + ; + +counter_args : counter_arg + { + $<stmt>$ = $<stmt>0; + } + | counter_args counter_arg + ; + +counter_arg : PACKETS NUM + { + $<stmt>0->counter.packets = $2; + } + | BYTES NUM + { + $<stmt>0->counter.bytes = $2; + } + ; + +last_stmt : LAST + { + $$ = last_stmt_alloc(&@$); + } + | LAST USED NEVER + { + $$ = last_stmt_alloc(&@$); + } + | LAST USED time_spec + { + $$ = last_stmt_alloc(&@$); + $$->last.used = $3; + $$->last.set = true; + } + ; + +log_stmt : log_stmt_alloc + | log_stmt_alloc log_args + ; + +log_stmt_alloc : LOG + { + $$ = log_stmt_alloc(&@$); + } + ; + +log_args : log_arg + { + $<stmt>$ = $<stmt>0; + } + | log_args log_arg + ; + +log_arg : PREFIX string + { + struct scope *scope = current_scope(state); + bool done = false, another_var = false; + char *start, *end, scratch = '\0'; + struct expr *expr, *item; + struct symbol *sym; + enum { + PARSE_TEXT, + PARSE_VAR, + } prefix_state; + + /* No variables in log prefix, skip. */ + if (!strchr($2, '$')) { + expr = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen($2) + 1) * BITS_PER_BYTE, $2); + xfree($2); + $<stmt>0->log.prefix = expr; + $<stmt>0->log.flags |= STMT_LOG_PREFIX; + break; + } + + /* Parse variables in log prefix string using a + * state machine parser with two states. This + * parser creates list of expressions composed + * of constant and variable expressions. + */ + expr = compound_expr_alloc(&@$, EXPR_LIST); + + start = (char *)$2; + + if (*start != '$') { + prefix_state = PARSE_TEXT; + } else { + prefix_state = PARSE_VAR; + start++; + } + end = start; + + /* Not nice, but works. */ + while (!done) { + switch (prefix_state) { + case PARSE_TEXT: + while (*end != '\0' && *end != '$') + end++; + + if (*end == '\0') + done = true; + + *end = '\0'; + item = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen(start) + 1) * BITS_PER_BYTE, + start); + compound_expr_add(expr, item); + + if (done) + break; + + start = end + 1; + end = start; + + /* fall through */ + case PARSE_VAR: + while (isalnum(*end) || *end == '_') + end++; + + if (*end == '\0') + done = true; + else if (*end == '$') + another_var = true; + else + scratch = *end; + + *end = '\0'; + + sym = symbol_get(scope, start); + if (!sym) { + sym = symbol_lookup_fuzzy(scope, start); + if (sym) { + erec_queue(error(&@2, "unknown identifier '%s'; " + "did you mean identifier ‘%s’?", + start, sym->identifier), + state->msgs); + } else { + erec_queue(error(&@2, "unknown identifier '%s'", + start), + state->msgs); + } + expr_free(expr); + xfree($2); + YYERROR; + } + item = variable_expr_alloc(&@$, scope, sym); + compound_expr_add(expr, item); + + if (done) + break; + + /* Restore original byte after + * symbol lookup. + */ + if (scratch) { + *end = scratch; + scratch = '\0'; + } + + start = end; + if (another_var) { + another_var = false; + start++; + prefix_state = PARSE_VAR; + } else { + prefix_state = PARSE_TEXT; + } + end = start; + break; + } + } + + xfree($2); + $<stmt>0->log.prefix = expr; + $<stmt>0->log.flags |= STMT_LOG_PREFIX; + } + | GROUP NUM + { + $<stmt>0->log.group = $2; + $<stmt>0->log.flags |= STMT_LOG_GROUP; + } + | SNAPLEN NUM + { + $<stmt>0->log.snaplen = $2; + $<stmt>0->log.flags |= STMT_LOG_SNAPLEN; + } + | QUEUE_THRESHOLD NUM + { + $<stmt>0->log.qthreshold = $2; + $<stmt>0->log.flags |= STMT_LOG_QTHRESHOLD; + } + | LEVEL level_type + { + $<stmt>0->log.level = $2; + $<stmt>0->log.flags |= STMT_LOG_LEVEL; + } + | FLAGS log_flags + { + $<stmt>0->log.logflags |= $2; + } + ; + +level_type : string + { + if (!strcmp("emerg", $1)) + $$ = NFT_LOGLEVEL_EMERG; + else if (!strcmp("alert", $1)) + $$ = NFT_LOGLEVEL_ALERT; + else if (!strcmp("crit", $1)) + $$ = NFT_LOGLEVEL_CRIT; + else if (!strcmp("err", $1)) + $$ = NFT_LOGLEVEL_ERR; + else if (!strcmp("warn", $1)) + $$ = NFT_LOGLEVEL_WARNING; + else if (!strcmp("notice", $1)) + $$ = NFT_LOGLEVEL_NOTICE; + else if (!strcmp("info", $1)) + $$ = NFT_LOGLEVEL_INFO; + else if (!strcmp("debug", $1)) + $$ = NFT_LOGLEVEL_DEBUG; + else if (!strcmp("audit", $1)) + $$ = NFT_LOGLEVEL_AUDIT; + else { + erec_queue(error(&@1, "invalid log level"), + state->msgs); + xfree($1); + YYERROR; + } + xfree($1); + } + ; + +log_flags : TCP log_flags_tcp close_scope_tcp + { + $$ = $2; + } + | IP OPTIONS close_scope_ip + { + $$ = NF_LOG_IPOPT; + } + | SKUID + { + $$ = NF_LOG_UID; + } + | ETHER close_scope_eth + { + $$ = NF_LOG_MACDECODE; + } + | ALL + { + $$ = NF_LOG_MASK; + } + ; + +log_flags_tcp : log_flags_tcp COMMA log_flag_tcp + { + $$ = $1 | $3; + } + | log_flag_tcp + ; + +log_flag_tcp : SEQUENCE + { + $$ = NF_LOG_TCPSEQ; + } + | OPTIONS + { + $$ = NF_LOG_TCPOPT; + } + ; + +limit_stmt : LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit + { + if ($5 == 0) { + erec_queue(error(&@5, "packet limit burst must be > 0"), + state->msgs); + YYERROR; + } + $$ = limit_stmt_alloc(&@$); + $$->limit.rate = $4.rate; + $$->limit.unit = $4.unit; + $$->limit.burst = $5; + $$->limit.type = NFT_LIMIT_PKTS; + $$->limit.flags = $3; + } + | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit + { + $$ = limit_stmt_alloc(&@$); + $$->limit.rate = $4.rate; + $$->limit.unit = $4.unit; + $$->limit.burst = $5; + $$->limit.type = NFT_LIMIT_PKT_BYTES; + $$->limit.flags = $3; + } + | LIMIT NAME stmt_expr close_scope_limit + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_LIMIT; + $$->objref.expr = $3; + } + ; + +quota_mode : OVER { $$ = NFT_QUOTA_F_INV; } + | UNTIL { $$ = 0; } + | /* empty */ { $$ = 0; } + ; + +quota_unit : BYTES { $$ = xstrdup("bytes"); } + | STRING { $$ = $1; } + ; + +quota_used : /* empty */ { $$ = 0; } + | USED NUM quota_unit + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&@$, $3, &rate); + xfree($3); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = $2 * rate; + } + ; + +quota_stmt : QUOTA quota_mode NUM quota_unit quota_used close_scope_quota + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&@$, $4, &rate); + xfree($4); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = quota_stmt_alloc(&@$); + $$->quota.bytes = $3 * rate; + $$->quota.used = $5; + $$->quota.flags = $2; + } + | QUOTA NAME stmt_expr close_scope_quota + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_QUOTA; + $$->objref.expr = $3; + } + ; + +limit_mode : OVER { $$ = NFT_LIMIT_F_INV; } + | UNTIL { $$ = 0; } + | /* empty */ { $$ = 0; } + ; + +limit_burst_pkts : /* empty */ { $$ = 5; } + | BURST NUM PACKETS { $$ = $2; } + ; + +limit_rate_pkts : NUM SLASH time_unit + { + $$.rate = $1; + $$.unit = $3; + } + ; + +limit_burst_bytes : /* empty */ { $$ = 0; } + | BURST limit_bytes { $$ = $2; } + ; + +limit_rate_bytes : NUM STRING + { + struct error_record *erec; + uint64_t rate, unit; + + erec = rate_parse(&@$, $2, &rate, &unit); + xfree($2); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$.rate = rate * $1; + $$.unit = unit; + } + | limit_bytes SLASH time_unit + { + $$.rate = $1; + $$.unit = $3; + } + ; + +limit_bytes : NUM BYTES { $$ = $1; } + | NUM STRING + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&@$, $2, &rate); + xfree($2); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = $1 * rate; + } + ; + +time_unit : SECOND { $$ = 1ULL; } + | MINUTE { $$ = 1ULL * 60; } + | HOUR { $$ = 1ULL * 60 * 60; } + | DAY { $$ = 1ULL * 60 * 60 * 24; } + | WEEK { $$ = 1ULL * 60 * 60 * 24 * 7; } + ; + +reject_stmt : reject_stmt_alloc reject_opts + ; + +reject_stmt_alloc : _REJECT + { + $$ = reject_stmt_alloc(&@$); + } + ; + +reject_with_expr : STRING + { + $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, + current_scope(state), $1); + xfree($1); + } + | integer_expr { $$ = $1; } + ; + +reject_opts : /* empty */ + { + $<stmt>0->reject.type = -1; + $<stmt>0->reject.icmp_code = -1; + } + | WITH ICMP TYPE reject_with_expr close_scope_type close_scope_icmp + { + $<stmt>0->reject.family = NFPROTO_IPV4; + $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; + $<stmt>0->reject.expr = $4; + datatype_set($<stmt>0->reject.expr, &icmp_code_type); + } + | WITH ICMP reject_with_expr + { + $<stmt>0->reject.family = NFPROTO_IPV4; + $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; + $<stmt>0->reject.expr = $3; + datatype_set($<stmt>0->reject.expr, &icmp_code_type); + } + | WITH ICMP6 TYPE reject_with_expr close_scope_type close_scope_icmp + { + $<stmt>0->reject.family = NFPROTO_IPV6; + $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; + $<stmt>0->reject.expr = $4; + datatype_set($<stmt>0->reject.expr, &icmpv6_code_type); + } + | WITH ICMP6 reject_with_expr + { + $<stmt>0->reject.family = NFPROTO_IPV6; + $<stmt>0->reject.type = NFT_REJECT_ICMP_UNREACH; + $<stmt>0->reject.expr = $3; + datatype_set($<stmt>0->reject.expr, &icmpv6_code_type); + } + | WITH ICMPX TYPE reject_with_expr close_scope_type + { + $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH; + $<stmt>0->reject.expr = $4; + datatype_set($<stmt>0->reject.expr, &icmpx_code_type); + } + | WITH ICMPX reject_with_expr + { + $<stmt>0->reject.type = NFT_REJECT_ICMPX_UNREACH; + $<stmt>0->reject.expr = $3; + datatype_set($<stmt>0->reject.expr, &icmpx_code_type); + } + | WITH TCP close_scope_tcp RESET close_scope_reset + { + $<stmt>0->reject.type = NFT_REJECT_TCP_RST; + } + ; + +nat_stmt : nat_stmt_alloc nat_stmt_args + ; + +nat_stmt_alloc : SNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_SNAT); } + | DNAT { $$ = nat_stmt_alloc(&@$, __NFT_NAT_DNAT); } + ; + +tproxy_stmt : TPROXY TO stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = NFPROTO_UNSPEC; + $$->tproxy.addr = $3; + } + | TPROXY nf_key_proto TO stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = $2; + $$->tproxy.addr = $4; + } + | TPROXY TO COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = NFPROTO_UNSPEC; + $$->tproxy.port = $4; + } + | TPROXY TO stmt_expr COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = NFPROTO_UNSPEC; + $$->tproxy.addr = $3; + $$->tproxy.port = $5; + } + | TPROXY nf_key_proto TO stmt_expr COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = $2; + $$->tproxy.addr = $4; + $$->tproxy.port = $6; + } + | TPROXY nf_key_proto TO COLON stmt_expr + { + $$ = tproxy_stmt_alloc(&@$); + $$->tproxy.family = $2; + $$->tproxy.port = $5; + } + ; + +synproxy_stmt : synproxy_stmt_alloc + | synproxy_stmt_alloc synproxy_args + ; + +synproxy_stmt_alloc : SYNPROXY + { + $$ = synproxy_stmt_alloc(&@$); + } + | SYNPROXY NAME stmt_expr + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_SYNPROXY; + $$->objref.expr = $3; + } + ; + +synproxy_args : synproxy_arg + { + $<stmt>$ = $<stmt>0; + } + | synproxy_args synproxy_arg + ; + +synproxy_arg : MSS NUM + { + $<stmt>0->synproxy.mss = $2; + $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + } + | WSCALE NUM + { + $<stmt>0->synproxy.wscale = $2; + $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } + | TIMESTAMP + { + $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_TIMESTAMP; + } + | SACK_PERM + { + $<stmt>0->synproxy.flags |= NF_SYNPROXY_OPT_SACK_PERM; + } + ; + +synproxy_config : MSS NUM WSCALE NUM synproxy_ts synproxy_sack + { + struct synproxy *synproxy; + uint32_t flags = 0; + + synproxy = &$<obj>0->synproxy; + synproxy->mss = $2; + flags |= NF_SYNPROXY_OPT_MSS; + synproxy->wscale = $4; + flags |= NF_SYNPROXY_OPT_WSCALE; + if ($5) + flags |= $5; + if ($6) + flags |= $6; + synproxy->flags = flags; + } + | MSS NUM stmt_separator WSCALE NUM stmt_separator synproxy_ts synproxy_sack + { + struct synproxy *synproxy; + uint32_t flags = 0; + + synproxy = &$<obj>0->synproxy; + synproxy->mss = $2; + flags |= NF_SYNPROXY_OPT_MSS; + synproxy->wscale = $5; + flags |= NF_SYNPROXY_OPT_WSCALE; + if ($7) + flags |= $7; + if ($8) + flags |= $8; + synproxy->flags = flags; + } + ; + +synproxy_obj : /* empty */ + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_SYNPROXY; + } + ; + +synproxy_ts : /* empty */ { $$ = 0; } + | TIMESTAMP + { + $$ = NF_SYNPROXY_OPT_TIMESTAMP; + } + ; + +synproxy_sack : /* empty */ { $$ = 0; } + | SACK_PERM + { + $$ = NF_SYNPROXY_OPT_SACK_PERM; + } + ; + +primary_stmt_expr : symbol_expr { $$ = $1; } + | integer_expr { $$ = $1; } + | boolean_expr { $$ = $1; } + | meta_expr { $$ = $1; } + | rt_expr { $$ = $1; } + | ct_expr { $$ = $1; } + | numgen_expr { $$ = $1; } + | hash_expr { $$ = $1; } + | payload_expr { $$ = $1; } + | keyword_expr { $$ = $1; } + | socket_expr { $$ = $1; } + | osf_expr { $$ = $1; } + | '(' basic_stmt_expr ')' { $$ = $2; } + ; + +shift_stmt_expr : primary_stmt_expr + | shift_stmt_expr LSHIFT primary_stmt_expr + { + $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3); + } + | shift_stmt_expr RSHIFT primary_stmt_expr + { + $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3); + } + ; + +and_stmt_expr : shift_stmt_expr + | and_stmt_expr AMPERSAND shift_stmt_expr + { + $$ = binop_expr_alloc(&@$, OP_AND, $1, $3); + } + ; + +exclusive_or_stmt_expr : and_stmt_expr + | exclusive_or_stmt_expr CARET and_stmt_expr + { + $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3); + } + ; + +inclusive_or_stmt_expr : exclusive_or_stmt_expr + | inclusive_or_stmt_expr '|' exclusive_or_stmt_expr + { + $$ = binop_expr_alloc(&@$, OP_OR, $1, $3); + } + ; + +basic_stmt_expr : inclusive_or_stmt_expr + ; + +concat_stmt_expr : basic_stmt_expr + | concat_stmt_expr DOT primary_stmt_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + +map_stmt_expr_set : set_expr + | set_ref_expr + ; + +map_stmt_expr : concat_stmt_expr MAP map_stmt_expr_set + { + $$ = map_expr_alloc(&@$, $1, $3); + } + | concat_stmt_expr { $$ = $1; } + ; + +prefix_stmt_expr : basic_stmt_expr SLASH NUM + { + $$ = prefix_expr_alloc(&@$, $1, $3); + } + ; + +range_stmt_expr : basic_stmt_expr DASH basic_stmt_expr + { + $$ = range_expr_alloc(&@$, $1, $3); + } + ; + +multiton_stmt_expr : prefix_stmt_expr + | range_stmt_expr + ; + +stmt_expr : map_stmt_expr + | multiton_stmt_expr + | list_stmt_expr + ; + +nat_stmt_args : stmt_expr + { + $<stmt>0->nat.addr = $1; + } + | TO stmt_expr + { + $<stmt>0->nat.addr = $2; + } + | nf_key_proto TO stmt_expr + { + $<stmt>0->nat.family = $1; + $<stmt>0->nat.addr = $3; + } + | stmt_expr COLON stmt_expr + { + $<stmt>0->nat.addr = $1; + $<stmt>0->nat.proto = $3; + } + | TO stmt_expr COLON stmt_expr + { + $<stmt>0->nat.addr = $2; + $<stmt>0->nat.proto = $4; + } + | nf_key_proto TO stmt_expr COLON stmt_expr + { + $<stmt>0->nat.family = $1; + $<stmt>0->nat.addr = $3; + $<stmt>0->nat.proto = $5; + } + | COLON stmt_expr + { + $<stmt>0->nat.proto = $2; + } + | TO COLON stmt_expr + { + $<stmt>0->nat.proto = $3; + } + | nat_stmt_args nf_nat_flags + { + $<stmt>0->nat.flags = $2; + } + | nf_key_proto ADDR DOT PORT TO stmt_expr + { + $<stmt>0->nat.family = $1; + $<stmt>0->nat.addr = $6; + $<stmt>0->nat.type_flags = STMT_NAT_F_CONCAT; + } + | nf_key_proto INTERVAL TO stmt_expr + { + $<stmt>0->nat.family = $1; + $<stmt>0->nat.addr = $4; + } + | INTERVAL TO stmt_expr + { + $<stmt>0->nat.addr = $3; + } + | nf_key_proto PREFIX TO stmt_expr + { + $<stmt>0->nat.family = $1; + $<stmt>0->nat.addr = $4; + $<stmt>0->nat.type_flags = + STMT_NAT_F_PREFIX; + $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP; + } + | PREFIX TO stmt_expr + { + $<stmt>0->nat.addr = $3; + $<stmt>0->nat.type_flags = + STMT_NAT_F_PREFIX; + $<stmt>0->nat.flags |= NF_NAT_RANGE_NETMAP; + } + ; + +masq_stmt : masq_stmt_alloc masq_stmt_args + | masq_stmt_alloc + ; + +masq_stmt_alloc : MASQUERADE { $$ = nat_stmt_alloc(&@$, NFT_NAT_MASQ); } + ; + +masq_stmt_args : TO COLON stmt_expr + { + $<stmt>0->nat.proto = $3; + } + | TO COLON stmt_expr nf_nat_flags + { + $<stmt>0->nat.proto = $3; + $<stmt>0->nat.flags = $4; + } + | nf_nat_flags + { + $<stmt>0->nat.flags = $1; + } + ; + +redir_stmt : redir_stmt_alloc redir_stmt_arg + | redir_stmt_alloc + ; + +redir_stmt_alloc : REDIRECT { $$ = nat_stmt_alloc(&@$, NFT_NAT_REDIR); } + ; + +redir_stmt_arg : TO stmt_expr + { + $<stmt>0->nat.proto = $2; + } + | TO COLON stmt_expr + { + $<stmt>0->nat.proto = $3; + } + | nf_nat_flags + { + $<stmt>0->nat.flags = $1; + } + | TO stmt_expr nf_nat_flags + { + $<stmt>0->nat.proto = $2; + $<stmt>0->nat.flags = $3; + } + | TO COLON stmt_expr nf_nat_flags + { + $<stmt>0->nat.proto = $3; + $<stmt>0->nat.flags = $4; + } + ; + +dup_stmt : DUP TO stmt_expr + { + $$ = dup_stmt_alloc(&@$); + $$->dup.to = $3; + } + | DUP TO stmt_expr DEVICE stmt_expr + { + $$ = dup_stmt_alloc(&@$); + $$->dup.to = $3; + $$->dup.dev = $5; + } + ; + +fwd_stmt : FWD TO stmt_expr + { + $$ = fwd_stmt_alloc(&@$); + $$->fwd.dev = $3; + } + | FWD nf_key_proto TO stmt_expr DEVICE stmt_expr + { + $$ = fwd_stmt_alloc(&@$); + $$->fwd.family = $2; + $$->fwd.addr = $4; + $$->fwd.dev = $6; + } + ; + +nf_nat_flags : nf_nat_flag + | nf_nat_flags COMMA nf_nat_flag + { + $$ = $1 | $3; + } + ; + +nf_nat_flag : RANDOM { $$ = NF_NAT_RANGE_PROTO_RANDOM; } + | FULLY_RANDOM { $$ = NF_NAT_RANGE_PROTO_RANDOM_FULLY; } + | PERSISTENT { $$ = NF_NAT_RANGE_PERSISTENT; } + ; + +queue_stmt : queue_stmt_compat close_scope_queue + | QUEUE TO queue_stmt_expr close_scope_queue + { + $$ = queue_stmt_alloc(&@$, $3, 0); + } + | QUEUE FLAGS queue_stmt_flags TO queue_stmt_expr close_scope_queue + { + $$ = queue_stmt_alloc(&@$, $5, $3); + } + | QUEUE FLAGS queue_stmt_flags QUEUENUM queue_stmt_expr_simple close_scope_queue + { + $$ = queue_stmt_alloc(&@$, $5, $3); + } + ; + +queue_stmt_compat : queue_stmt_alloc + | queue_stmt_alloc queue_stmt_args + ; + +queue_stmt_alloc : QUEUE + { + $$ = queue_stmt_alloc(&@$, NULL, 0); + } + ; + +queue_stmt_args : queue_stmt_arg + { + $<stmt>$ = $<stmt>0; + } + | queue_stmt_args queue_stmt_arg + ; + +queue_stmt_arg : QUEUENUM queue_stmt_expr_simple + { + $<stmt>0->queue.queue = $2; + $<stmt>0->queue.queue->location = @$; + } + | queue_stmt_flags + { + $<stmt>0->queue.flags |= $1; + } + ; + +queue_expr : variable_expr + | integer_expr + ; + +queue_stmt_expr_simple : integer_expr + | variable_expr + | queue_expr DASH queue_expr + { + $$ = range_expr_alloc(&@$, $1, $3); + } + ; + +queue_stmt_expr : numgen_expr + | hash_expr + | map_expr + | queue_stmt_expr_simple + ; + +queue_stmt_flags : queue_stmt_flag + | queue_stmt_flags COMMA queue_stmt_flag + { + $$ = $1 | $3; + } + ; + +queue_stmt_flag : BYPASS { $$ = NFT_QUEUE_FLAG_BYPASS; } + | FANOUT { $$ = NFT_QUEUE_FLAG_CPU_FANOUT; } + ; + +set_elem_expr_stmt : set_elem_expr_stmt_alloc + | set_elem_expr_stmt_alloc set_elem_options + ; + +set_elem_expr_stmt_alloc: concat_expr + { + $$ = set_elem_expr_alloc(&@1, $1); + } + ; + +set_stmt : SET set_stmt_op set_elem_expr_stmt set_ref_expr + { + $$ = set_stmt_alloc(&@$); + $$->set.op = $2; + $$->set.key = $3; + $$->set.set = $4; + } + | set_stmt_op set_ref_expr '{' set_elem_expr_stmt '}' + { + $$ = set_stmt_alloc(&@$); + $$->set.op = $1; + $$->set.key = $4; + $$->set.set = $2; + } + | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list '}' + { + $$ = set_stmt_alloc(&@$); + $$->set.op = $1; + $$->set.key = $4; + $$->set.set = $2; + list_splice_tail($5, &$$->set.stmt_list); + free($5); + } + ; + +set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; } + | UPDATE { $$ = NFT_DYNSET_OP_UPDATE; } + | DELETE { $$ = NFT_DYNSET_OP_DELETE; } + ; + +map_stmt : set_stmt_op set_ref_expr '{' set_elem_expr_stmt COLON set_elem_expr_stmt '}' + { + $$ = map_stmt_alloc(&@$); + $$->map.op = $1; + $$->map.key = $4; + $$->map.data = $6; + $$->map.set = $2; + } + | set_stmt_op set_ref_expr '{' set_elem_expr_stmt stateful_stmt_list COLON set_elem_expr_stmt '}' + { + $$ = map_stmt_alloc(&@$); + $$->map.op = $1; + $$->map.key = $4; + $$->map.data = $7; + $$->map.set = $2; + list_splice_tail($5, &$$->map.stmt_list); + free($5); + } + ; + +meter_stmt : flow_stmt_legacy_alloc flow_stmt_opts '{' meter_key_expr stmt '}' + { + $1->meter.key = $4; + $1->meter.stmt = $5; + $$->location = @$; + $$ = $1; + } + | meter_stmt_alloc { $$ = $1; } + ; + +flow_stmt_legacy_alloc : FLOW + { + $$ = meter_stmt_alloc(&@$); + } + ; + +flow_stmt_opts : flow_stmt_opt + { + $<stmt>$ = $<stmt>0; + } + | flow_stmt_opts flow_stmt_opt + ; + +flow_stmt_opt : TABLE identifier + { + $<stmt>0->meter.name = $2; + } + ; + +meter_stmt_alloc : METER identifier '{' meter_key_expr stmt '}' + { + $$ = meter_stmt_alloc(&@$); + $$->meter.name = $2; + $$->meter.size = 0; + $$->meter.key = $4; + $$->meter.stmt = $5; + $$->location = @$; + } + | METER identifier SIZE NUM '{' meter_key_expr stmt '}' + { + $$ = meter_stmt_alloc(&@$); + $$->meter.name = $2; + $$->meter.size = $4; + $$->meter.key = $6; + $$->meter.stmt = $7; + $$->location = @$; + } + ; + +match_stmt : relational_expr + { + $$ = expr_stmt_alloc(&@$, $1); + } + ; + +variable_expr : '$' identifier + { + struct scope *scope = current_scope(state); + struct symbol *sym; + + sym = symbol_get(scope, $2); + if (!sym) { + sym = symbol_lookup_fuzzy(scope, $2); + if (sym) { + erec_queue(error(&@2, "unknown identifier '%s'; " + "did you mean identifier ‘%s’?", + $2, sym->identifier), + state->msgs); + } else { + erec_queue(error(&@2, "unknown identifier '%s'", $2), + state->msgs); + } + xfree($2); + YYERROR; + } + + $$ = variable_expr_alloc(&@$, scope, sym); + xfree($2); + } + ; + +symbol_expr : variable_expr + | string + { + $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, + current_scope(state), + $1); + xfree($1); + } + ; + +set_ref_expr : set_ref_symbol_expr + | variable_expr + ; + +set_ref_symbol_expr : AT identifier close_scope_at + { + $$ = symbol_expr_alloc(&@$, SYMBOL_SET, + current_scope(state), + $2); + xfree($2); + } + ; + +integer_expr : NUM + { + char str[64]; + + snprintf(str, sizeof(str), "%" PRIu64, $1); + $$ = symbol_expr_alloc(&@$, SYMBOL_VALUE, + current_scope(state), + str); + } + ; + +primary_expr : symbol_expr { $$ = $1; } + | integer_expr { $$ = $1; } + | payload_expr { $$ = $1; } + | exthdr_expr { $$ = $1; } + | exthdr_exists_expr { $$ = $1; } + | meta_expr { $$ = $1; } + | socket_expr { $$ = $1; } + | rt_expr { $$ = $1; } + | ct_expr { $$ = $1; } + | numgen_expr { $$ = $1; } + | hash_expr { $$ = $1; } + | fib_expr { $$ = $1; } + | osf_expr { $$ = $1; } + | xfrm_expr { $$ = $1; } + | '(' basic_expr ')' { $$ = $2; } + ; + +fib_expr : FIB fib_tuple fib_result close_scope_fib + { + if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { + erec_queue(error(&@2, "fib: need either saddr or daddr"), state->msgs); + YYERROR; + } + + if (($2 & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == + (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + erec_queue(error(&@2, "fib: saddr and daddr are mutually exclusive"), state->msgs); + YYERROR; + } + + if (($2 & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == + (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + erec_queue(error(&@2, "fib: iif and oif are mutually exclusive"), state->msgs); + YYERROR; + } + + $$ = fib_expr_alloc(&@$, $2, $3); + } + ; + +fib_result : OIF { $$ =NFT_FIB_RESULT_OIF; } + | OIFNAME { $$ =NFT_FIB_RESULT_OIFNAME; } + | TYPE close_scope_type { $$ =NFT_FIB_RESULT_ADDRTYPE; } + ; + +fib_flag : SADDR { $$ = NFTA_FIB_F_SADDR; } + | DADDR { $$ = NFTA_FIB_F_DADDR; } + | MARK { $$ = NFTA_FIB_F_MARK; } + | IIF { $$ = NFTA_FIB_F_IIF; } + | OIF { $$ = NFTA_FIB_F_OIF; } + ; + +fib_tuple : fib_flag DOT fib_tuple + { + $$ = $1 | $3; + } + | fib_flag + ; + +osf_expr : OSF osf_ttl HDRVERSION close_scope_osf + { + $$ = osf_expr_alloc(&@$, $2, NFT_OSF_F_VERSION); + } + | OSF osf_ttl NAME close_scope_osf + { + $$ = osf_expr_alloc(&@$, $2, 0); + } + ; + +osf_ttl : /* empty */ + { + $$ = NF_OSF_TTL_TRUE; + } + | TTL STRING + { + if (!strcmp($2, "loose")) + $$ = NF_OSF_TTL_LESS; + else if (!strcmp($2, "skip")) + $$ = NF_OSF_TTL_NOCHECK; + else { + erec_queue(error(&@2, "invalid ttl option"), + state->msgs); + xfree($2); + YYERROR; + } + xfree($2); + } + ; + +shift_expr : primary_expr + | shift_expr LSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3); + } + | shift_expr RSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3); + } + ; + +and_expr : shift_expr + | and_expr AMPERSAND shift_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_AND, $1, $3); + } + ; + +exclusive_or_expr : and_expr + | exclusive_or_expr CARET and_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3); + } + ; + +inclusive_or_expr : exclusive_or_expr + | inclusive_or_expr '|' exclusive_or_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_OR, $1, $3); + } + ; + +basic_expr : inclusive_or_expr + ; + +concat_expr : basic_expr + | concat_expr DOT basic_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + +prefix_rhs_expr : basic_rhs_expr SLASH NUM + { + $$ = prefix_expr_alloc(&@$, $1, $3); + } + ; + +range_rhs_expr : basic_rhs_expr DASH basic_rhs_expr + { + $$ = range_expr_alloc(&@$, $1, $3); + } + ; + +multiton_rhs_expr : prefix_rhs_expr + | range_rhs_expr + ; + +map_expr : concat_expr MAP rhs_expr + { + $$ = map_expr_alloc(&@$, $1, $3); + } + ; + +expr : concat_expr + | set_expr + | map_expr + ; + +set_expr : '{' set_list_expr '}' + { + $2->location = @$; + $$ = $2; + } + ; + +set_list_expr : set_list_member_expr + { + $$ = set_expr_alloc(&@$, NULL); + compound_expr_add($$, $1); + } + | set_list_expr COMMA set_list_member_expr + { + compound_expr_add($1, $3); + $$ = $1; + } + | set_list_expr COMMA opt_newline + ; + +set_list_member_expr : opt_newline set_expr opt_newline + { + $$ = $2; + } + | opt_newline set_elem_expr opt_newline + { + $$ = $2; + } + | opt_newline set_elem_expr COLON set_rhs_expr opt_newline + { + $$ = mapping_expr_alloc(&@2, $2, $4); + } + ; + +meter_key_expr : meter_key_expr_alloc + | meter_key_expr_alloc set_elem_options + { + $$->location = @$; + $$ = $1; + } + ; + +meter_key_expr_alloc : concat_expr + { + $$ = set_elem_expr_alloc(&@1, $1); + } + ; + +set_elem_expr : set_elem_expr_alloc + | set_elem_expr_alloc set_elem_expr_options + ; + +set_elem_key_expr : set_lhs_expr { $$ = $1; } + | ASTERISK { $$ = set_elem_catchall_expr_alloc(&@1); } + ; + +set_elem_expr_alloc : set_elem_key_expr set_elem_stmt_list + { + $$ = set_elem_expr_alloc(&@1, $1); + list_splice_tail($2, &$$->stmt_list); + xfree($2); + } + | set_elem_key_expr + { + $$ = set_elem_expr_alloc(&@1, $1); + } + ; + +set_elem_options : set_elem_option + { + $<expr>$ = $<expr>0; + } + | set_elem_options set_elem_option + ; + +set_elem_option : TIMEOUT time_spec + { + $<expr>0->timeout = $2; + } + | EXPIRES time_spec + { + $<expr>0->expiration = $2; + } + | comment_spec + { + if (already_set($<expr>0->comment, &@1, state)) { + xfree($1); + YYERROR; + } + $<expr>0->comment = $1; + } + ; + +set_elem_expr_options : set_elem_expr_option + { + $<expr>$ = $<expr>0; + } + | set_elem_expr_options set_elem_expr_option + ; + +set_elem_stmt_list : set_elem_stmt + { + $$ = xmalloc(sizeof(*$$)); + init_list_head($$); + list_add_tail(&$1->list, $$); + } + | set_elem_stmt_list set_elem_stmt + { + $$ = $1; + list_add_tail(&$2->list, $1); + } + ; + +set_elem_stmt : COUNTER close_scope_counter + { + $$ = counter_stmt_alloc(&@$); + } + | COUNTER PACKETS NUM BYTES NUM close_scope_counter + { + $$ = counter_stmt_alloc(&@$); + $$->counter.packets = $3; + $$->counter.bytes = $5; + } + | LIMIT RATE limit_mode limit_rate_pkts limit_burst_pkts close_scope_limit + { + if ($5 == 0) { + erec_queue(error(&@5, "limit burst must be > 0"), + state->msgs); + YYERROR; + } + $$ = limit_stmt_alloc(&@$); + $$->limit.rate = $4.rate; + $$->limit.unit = $4.unit; + $$->limit.burst = $5; + $$->limit.type = NFT_LIMIT_PKTS; + $$->limit.flags = $3; + } + | LIMIT RATE limit_mode limit_rate_bytes limit_burst_bytes close_scope_limit + { + if ($5 == 0) { + erec_queue(error(&@6, "limit burst must be > 0"), + state->msgs); + YYERROR; + } + $$ = limit_stmt_alloc(&@$); + $$->limit.rate = $4.rate; + $$->limit.unit = $4.unit; + $$->limit.burst = $5; + $$->limit.type = NFT_LIMIT_PKT_BYTES; + $$->limit.flags = $3; + } + | CT COUNT NUM close_scope_ct + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $3; + } + | CT COUNT OVER NUM close_scope_ct + { + $$ = connlimit_stmt_alloc(&@$); + $$->connlimit.count = $4; + $$->connlimit.flags = NFT_CONNLIMIT_F_INV; + } + | QUOTA quota_mode NUM quota_unit quota_used close_scope_quota + { + struct error_record *erec; + uint64_t rate; + + erec = data_unit_parse(&@$, $4, &rate); + xfree($4); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = quota_stmt_alloc(&@$); + $$->quota.bytes = $3 * rate; + $$->quota.used = $5; + $$->quota.flags = $2; + } + | LAST USED NEVER close_scope_last + { + $$ = last_stmt_alloc(&@$); + } + | LAST USED time_spec close_scope_last + { + $$ = last_stmt_alloc(&@$); + $$->last.used = $3; + $$->last.set = true; + } + ; + +set_elem_expr_option : TIMEOUT time_spec + { + $<expr>0->timeout = $2; + } + | EXPIRES time_spec + { + $<expr>0->expiration = $2; + } + | comment_spec + { + if (already_set($<expr>0->comment, &@1, state)) { + xfree($1); + YYERROR; + } + $<expr>0->comment = $1; + } + ; + +set_lhs_expr : concat_rhs_expr + ; + +set_rhs_expr : concat_rhs_expr + | verdict_expr + ; + +initializer_expr : rhs_expr + | list_rhs_expr + | '{' '}' { $$ = compound_expr_alloc(&@$, EXPR_SET); } + | DASH NUM + { + int32_t num = -$2; + + $$ = constant_expr_alloc(&@$, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(num) * BITS_PER_BYTE, + &num); + } + ; + +counter_config : PACKETS NUM BYTES NUM + { + struct counter *counter; + + counter = &$<obj>0->counter; + counter->packets = $2; + counter->bytes = $4; + } + ; + +counter_obj : /* empty */ + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_COUNTER; + } + ; + +quota_config : quota_mode NUM quota_unit quota_used + { + struct error_record *erec; + struct quota *quota; + uint64_t rate; + + erec = data_unit_parse(&@$, $3, &rate); + xfree($3); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + quota = &$<obj>0->quota; + quota->bytes = $2 * rate; + quota->used = $4; + quota->flags = $1; + } + ; + +quota_obj : /* empty */ + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_QUOTA; + } + ; + +secmark_config : string + { + int ret; + struct secmark *secmark; + + secmark = &$<obj>0->secmark; + ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1); + if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) { + erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs); + xfree($1); + YYERROR; + } + xfree($1); + } + ; + +secmark_obj : /* empty */ + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_SECMARK; + } + ; + +ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; } + | TIMEOUT { $$ = NFT_OBJECT_CT_TIMEOUT; } + | EXPECTATION { $$ = NFT_OBJECT_CT_EXPECT; } + ; + +ct_cmd_type : HELPERS { $$ = CMD_OBJ_CT_HELPERS; } + | TIMEOUT { $$ = CMD_OBJ_CT_TIMEOUTS; } + | EXPECTATION { $$ = CMD_OBJ_CT_EXPECTATIONS; } + ; + +ct_l4protoname : TCP close_scope_tcp { $$ = IPPROTO_TCP; } + | UDP close_scope_udp { $$ = IPPROTO_UDP; } + ; + +ct_helper_config : TYPE QUOTED_STRING PROTOCOL ct_l4protoname stmt_separator close_scope_type + { + struct ct_helper *ct; + int ret; + + ct = &$<obj>0->ct_helper; + + ret = snprintf(ct->name, sizeof(ct->name), "%s", $2); + if (ret <= 0 || ret >= (int)sizeof(ct->name)) { + erec_queue(error(&@2, "invalid name '%s', max length is %u\n", $2, (int)sizeof(ct->name)), state->msgs); + YYERROR; + } + xfree($2); + + ct->l4proto = $4; + } + | L3PROTOCOL family_spec_explicit stmt_separator + { + $<obj>0->ct_helper.l3proto = $2; + } + ; + +timeout_states : timeout_state + { + $$ = xmalloc(sizeof(*$$)); + init_list_head($$); + list_add_tail($1, $$); + } + | timeout_states COMMA timeout_state + { + list_add_tail($3, $1); + $$ = $1; + } + ; + +timeout_state : STRING COLON time_spec_or_num_s + { + struct timeout_state *ts; + + ts = xzalloc(sizeof(*ts)); + ts->timeout_str = $1; + ts->timeout_value = $3; + ts->location = @1; + init_list_head(&ts->head); + $$ = &ts->head; + } + ; + +ct_timeout_config : PROTOCOL ct_l4protoname stmt_separator + { + struct ct_timeout *ct; + int l4proto = $2; + + ct = &$<obj>0->ct_timeout; + ct->l4proto = l4proto; + } + | POLICY '=' '{' timeout_states '}' stmt_separator close_scope_policy + { + struct ct_timeout *ct; + + ct = &$<obj>0->ct_timeout; + list_splice_tail($4, &ct->timeout_list); + xfree($4); + } + | L3PROTOCOL family_spec_explicit stmt_separator + { + $<obj>0->ct_timeout.l3proto = $2; + } + ; + +ct_expect_config : PROTOCOL ct_l4protoname stmt_separator + { + $<obj>0->ct_expect.l4proto = $2; + } + | DPORT NUM stmt_separator + { + $<obj>0->ct_expect.dport = $2; + } + | TIMEOUT time_spec stmt_separator + { + $<obj>0->ct_expect.timeout = $2; + } + | SIZE NUM stmt_separator + { + $<obj>0->ct_expect.size = $2; + } + | L3PROTOCOL family_spec_explicit stmt_separator + { + $<obj>0->ct_expect.l3proto = $2; + } + ; + +ct_obj_alloc : /* empty */ + { + $$ = obj_alloc(&@$); + } + ; + +limit_config : RATE limit_mode limit_rate_pkts limit_burst_pkts + { + struct limit *limit; + + limit = &$<obj>0->limit; + limit->rate = $3.rate; + limit->unit = $3.unit; + limit->burst = $4; + limit->type = NFT_LIMIT_PKTS; + limit->flags = $2; + } + | RATE limit_mode limit_rate_bytes limit_burst_bytes + { + struct limit *limit; + + limit = &$<obj>0->limit; + limit->rate = $3.rate; + limit->unit = $3.unit; + limit->burst = $4; + limit->type = NFT_LIMIT_PKT_BYTES; + limit->flags = $2; + } + ; + +limit_obj : /* empty */ + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_LIMIT; + } + ; + +relational_expr : expr /* implicit */ rhs_expr + { + $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2); + } + | expr /* implicit */ list_rhs_expr + { + $$ = relational_expr_alloc(&@$, OP_IMPLICIT, $1, $2); + } + | expr /* implicit */ basic_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2); + } + | expr /* implicit */ list_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, OP_EQ, $1, $4, $2); + } + | expr relational_op basic_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3); + } + | expr relational_op list_rhs_expr SLASH list_rhs_expr + { + $$ = flagcmp_expr_alloc(&@$, $2, $1, $5, $3); + } + | expr relational_op rhs_expr + { + $$ = relational_expr_alloc(&@2, $2, $1, $3); + } + | expr relational_op list_rhs_expr + { + $$ = relational_expr_alloc(&@2, $2, $1, $3); + } + ; + +list_rhs_expr : basic_rhs_expr COMMA basic_rhs_expr + { + $$ = list_expr_alloc(&@$); + compound_expr_add($$, $1); + compound_expr_add($$, $3); + } + | list_rhs_expr COMMA basic_rhs_expr + { + $1->location = @$; + compound_expr_add($1, $3); + $$ = $1; + } + ; + +rhs_expr : concat_rhs_expr { $$ = $1; } + | set_expr { $$ = $1; } + | set_ref_symbol_expr { $$ = $1; } + ; + +shift_rhs_expr : primary_rhs_expr + | shift_rhs_expr LSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_LSHIFT, $1, $3); + } + | shift_rhs_expr RSHIFT primary_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_RSHIFT, $1, $3); + } + ; + +and_rhs_expr : shift_rhs_expr + | and_rhs_expr AMPERSAND shift_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_AND, $1, $3); + } + ; + +exclusive_or_rhs_expr : and_rhs_expr + | exclusive_or_rhs_expr CARET and_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_XOR, $1, $3); + } + ; + +inclusive_or_rhs_expr : exclusive_or_rhs_expr + | inclusive_or_rhs_expr '|' exclusive_or_rhs_expr + { + $$ = binop_expr_alloc(&@$, OP_OR, $1, $3); + } + ; + +basic_rhs_expr : inclusive_or_rhs_expr + ; + +concat_rhs_expr : basic_rhs_expr + | multiton_rhs_expr + | concat_rhs_expr DOT multiton_rhs_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + | concat_rhs_expr DOT basic_rhs_expr + { + struct location rhs[] = { + [1] = @2, + [2] = @3, + }; + + $$ = handle_concat_expr(&@$, $$, $1, $3, rhs); + } + ; + +boolean_keys : EXISTS { $$ = true; } + | MISSING { $$ = false; } + ; + +boolean_expr : boolean_keys + { + $$ = constant_expr_alloc(&@$, &boolean_type, + BYTEORDER_HOST_ENDIAN, + sizeof($1) * BITS_PER_BYTE, &$1); + } + ; + +keyword_expr : ETHER close_scope_eth { $$ = symbol_value(&@$, "ether"); } + | IP close_scope_ip { $$ = symbol_value(&@$, "ip"); } + | IP6 close_scope_ip6 { $$ = symbol_value(&@$, "ip6"); } + | VLAN close_scope_vlan { $$ = symbol_value(&@$, "vlan"); } + | ARP close_scope_arp { $$ = symbol_value(&@$, "arp"); } + | DNAT close_scope_nat { $$ = symbol_value(&@$, "dnat"); } + | SNAT close_scope_nat { $$ = symbol_value(&@$, "snat"); } + | ECN { $$ = symbol_value(&@$, "ecn"); } + | RESET close_scope_reset { $$ = symbol_value(&@$, "reset"); } + | DESTROY close_scope_destroy { $$ = symbol_value(&@$, "destroy"); } + | ORIGINAL { $$ = symbol_value(&@$, "original"); } + | REPLY { $$ = symbol_value(&@$, "reply"); } + | LABEL { $$ = symbol_value(&@$, "label"); } + | LAST close_scope_last { $$ = symbol_value(&@$, "last"); } + ; + +primary_rhs_expr : symbol_expr { $$ = $1; } + | integer_expr { $$ = $1; } + | boolean_expr { $$ = $1; } + | keyword_expr { $$ = $1; } + | TCP close_scope_tcp + { + uint8_t data = IPPROTO_TCP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | UDP close_scope_udp + { + uint8_t data = IPPROTO_UDP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | UDPLITE close_scope_udplite + { + uint8_t data = IPPROTO_UDPLITE; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | ESP close_scope_esp + { + uint8_t data = IPPROTO_ESP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | AH close_scope_ah + { + uint8_t data = IPPROTO_AH; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | ICMP close_scope_icmp + { + uint8_t data = IPPROTO_ICMP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | IGMP + { + uint8_t data = IPPROTO_IGMP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | ICMP6 close_scope_icmp + { + uint8_t data = IPPROTO_ICMPV6; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | GRE close_scope_gre + { + uint8_t data = IPPROTO_GRE; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | COMP close_scope_comp + { + uint8_t data = IPPROTO_COMP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | DCCP close_scope_dccp + { + uint8_t data = IPPROTO_DCCP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | SCTP close_scope_sctp + { + uint8_t data = IPPROTO_SCTP; + $$ = constant_expr_alloc(&@$, &inet_protocol_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | REDIRECT close_scope_nat + { + uint8_t data = ICMP_REDIRECT; + $$ = constant_expr_alloc(&@$, &icmp_type_type, + BYTEORDER_HOST_ENDIAN, + sizeof(data) * BITS_PER_BYTE, &data); + } + | '(' basic_rhs_expr ')' { $$ = $2; } + ; + +relational_op : EQ { $$ = OP_EQ; } + | NEQ { $$ = OP_NEQ; } + | LT { $$ = OP_LT; } + | GT { $$ = OP_GT; } + | GTE { $$ = OP_GTE; } + | LTE { $$ = OP_LTE; } + | NOT { $$ = OP_NEG; } + ; + +verdict_expr : ACCEPT + { + $$ = verdict_expr_alloc(&@$, NF_ACCEPT, NULL); + } + | DROP + { + $$ = verdict_expr_alloc(&@$, NF_DROP, NULL); + } + | CONTINUE + { + $$ = verdict_expr_alloc(&@$, NFT_CONTINUE, NULL); + } + | JUMP chain_expr + { + $$ = verdict_expr_alloc(&@$, NFT_JUMP, $2); + } + | GOTO chain_expr + { + $$ = verdict_expr_alloc(&@$, NFT_GOTO, $2); + } + | RETURN + { + $$ = verdict_expr_alloc(&@$, NFT_RETURN, NULL); + } + ; + +chain_expr : variable_expr + | identifier + { + $$ = constant_expr_alloc(&@$, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen($1) * BITS_PER_BYTE, + $1); + xfree($1); + } + ; + +meta_expr : META meta_key close_scope_meta + { + $$ = meta_expr_alloc(&@$, $2); + } + | meta_key_unqualified + { + $$ = meta_expr_alloc(&@$, $1); + } + | META STRING close_scope_meta + { + struct error_record *erec; + unsigned int key; + + erec = meta_key_parse(&@$, $2, &key); + xfree($2); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + $$ = meta_expr_alloc(&@$, key); + } + ; + +meta_key : meta_key_qualified + | meta_key_unqualified + ; + +meta_key_qualified : LENGTH { $$ = NFT_META_LEN; } + | PROTOCOL { $$ = NFT_META_PROTOCOL; } + | PRIORITY { $$ = NFT_META_PRIORITY; } + | RANDOM { $$ = NFT_META_PRANDOM; } + | SECMARK close_scope_secmark { $$ = NFT_META_SECMARK; } + ; + +meta_key_unqualified : 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; } + | IBRIPORT { $$ = NFT_META_BRI_IIFNAME; } + | OBRIPORT { $$ = NFT_META_BRI_OIFNAME; } + | IBRIDGENAME { $$ = NFT_META_BRI_IIFNAME; } + | OBRIDGENAME { $$ = NFT_META_BRI_OIFNAME; } + | PKTTYPE { $$ = NFT_META_PKTTYPE; } + | CPU { $$ = NFT_META_CPU; } + | IIFGROUP { $$ = NFT_META_IIFGROUP; } + | OIFGROUP { $$ = NFT_META_OIFGROUP; } + | CGROUP { $$ = NFT_META_CGROUP; } + | IPSEC close_scope_ipsec { $$ = NFT_META_SECPATH; } + | TIME { $$ = NFT_META_TIME_NS; } + | DAY { $$ = NFT_META_TIME_DAY; } + | HOUR { $$ = NFT_META_TIME_HOUR; } + ; + +meta_stmt : META meta_key SET stmt_expr close_scope_meta + { + switch ($2) { + case NFT_META_SECMARK: + switch ($4->etype) { + case EXPR_CT: + $$ = meta_stmt_alloc(&@$, $2, $4); + break; + default: + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_SECMARK; + $$->objref.expr = $4; + break; + } + break; + default: + $$ = meta_stmt_alloc(&@$, $2, $4); + break; + } + } + | meta_key_unqualified SET stmt_expr + { + $$ = meta_stmt_alloc(&@$, $1, $3); + } + | META STRING SET stmt_expr close_scope_meta + { + struct error_record *erec; + unsigned int key; + + erec = meta_key_parse(&@$, $2, &key); + xfree($2); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + + $$ = meta_stmt_alloc(&@$, key, $4); + } + | NOTRACK + { + $$ = notrack_stmt_alloc(&@$); + } + | FLOW OFFLOAD AT string close_scope_at + { + $$ = flow_offload_stmt_alloc(&@$, $4); + } + | FLOW ADD AT string close_scope_at + { + $$ = flow_offload_stmt_alloc(&@$, $4); + } + ; + +socket_expr : SOCKET socket_key close_scope_socket + { + $$ = socket_expr_alloc(&@$, $2, 0); + } + | SOCKET CGROUPV2 LEVEL NUM close_scope_socket + { + $$ = socket_expr_alloc(&@$, NFT_SOCKET_CGROUPV2, $4); + } + ; + +socket_key : TRANSPARENT { $$ = NFT_SOCKET_TRANSPARENT; } + | MARK { $$ = NFT_SOCKET_MARK; } + | WILDCARD { $$ = NFT_SOCKET_WILDCARD; } + ; + +offset_opt : /* empty */ { $$ = 0; } + | OFFSET NUM { $$ = $2; } + ; + +numgen_type : INC { $$ = NFT_NG_INCREMENTAL; } + | RANDOM { $$ = NFT_NG_RANDOM; } + ; + +numgen_expr : NUMGEN numgen_type MOD NUM offset_opt close_scope_numgen + { + $$ = numgen_expr_alloc(&@$, $2, $4, $5); + } + ; + +xfrm_spnum : SPNUM NUM { $$ = $2; } + | { $$ = 0; } + ; + +xfrm_dir : IN { $$ = XFRM_POLICY_IN; } + | OUT { $$ = XFRM_POLICY_OUT; } + ; + +xfrm_state_key : SPI { $$ = NFT_XFRM_KEY_SPI; } + | REQID { $$ = NFT_XFRM_KEY_REQID; } + ; + +xfrm_state_proto_key : DADDR { $$ = NFT_XFRM_KEY_DADDR_IP4; } + | SADDR { $$ = NFT_XFRM_KEY_SADDR_IP4; } + ; + +xfrm_expr : IPSEC xfrm_dir xfrm_spnum xfrm_state_key close_scope_ipsec + { + if ($3 > 255) { + erec_queue(error(&@3, "value too large"), state->msgs); + YYERROR; + } + $$ = xfrm_expr_alloc(&@$, $2, $3, $4); + } + | IPSEC xfrm_dir xfrm_spnum nf_key_proto xfrm_state_proto_key close_scope_ipsec + { + enum nft_xfrm_keys xfrmk = $5; + + switch ($4) { + case NFPROTO_IPV4: + break; + case NFPROTO_IPV6: + if ($5 == NFT_XFRM_KEY_SADDR_IP4) + xfrmk = NFT_XFRM_KEY_SADDR_IP6; + else if ($5 == NFT_XFRM_KEY_DADDR_IP4) + xfrmk = NFT_XFRM_KEY_DADDR_IP6; + break; + default: + YYERROR; + break; + } + + if ($3 > 255) { + erec_queue(error(&@3, "value too large"), state->msgs); + YYERROR; + } + + $$ = xfrm_expr_alloc(&@$, $2, $3, xfrmk); + } + ; + +hash_expr : JHASH expr MOD NUM SEED NUM offset_opt close_scope_hash + { + $$ = hash_expr_alloc(&@$, $4, true, $6, $7, NFT_HASH_JENKINS); + $$->hash.expr = $2; + } + | JHASH expr MOD NUM offset_opt close_scope_hash + { + $$ = hash_expr_alloc(&@$, $4, false, 0, $5, NFT_HASH_JENKINS); + $$->hash.expr = $2; + } + | SYMHASH MOD NUM offset_opt close_scope_hash + { + $$ = hash_expr_alloc(&@$, $3, false, 0, $4, NFT_HASH_SYM); + } + ; + +nf_key_proto : IP close_scope_ip { $$ = NFPROTO_IPV4; } + | IP6 close_scope_ip6 { $$ = NFPROTO_IPV6; } + ; + +rt_expr : RT rt_key close_scope_rt + { + $$ = rt_expr_alloc(&@$, $2, true); + } + | RT nf_key_proto rt_key close_scope_rt + { + enum nft_rt_keys rtk = $3; + + switch ($2) { + case NFPROTO_IPV4: + break; + case NFPROTO_IPV6: + if ($3 == NFT_RT_NEXTHOP4) + rtk = NFT_RT_NEXTHOP6; + break; + default: + YYERROR; + break; + } + + $$ = rt_expr_alloc(&@$, rtk, false); + } + ; + +rt_key : CLASSID { $$ = NFT_RT_CLASSID; } + | NEXTHOP { $$ = NFT_RT_NEXTHOP4; } + | MTU { $$ = NFT_RT_TCPMSS; } + | IPSEC close_scope_ipsec { $$ = NFT_RT_XFRM; } + ; + +ct_expr : CT ct_key close_scope_ct + { + $$ = ct_expr_alloc(&@$, $2, -1); + } + | CT ct_dir ct_key_dir close_scope_ct + { + $$ = ct_expr_alloc(&@$, $3, $2); + } + | CT ct_dir ct_key_proto_field close_scope_ct + { + $$ = ct_expr_alloc(&@$, $3, $2); + } + ; + +ct_dir : ORIGINAL { $$ = IP_CT_DIR_ORIGINAL; } + | REPLY { $$ = IP_CT_DIR_REPLY; } + ; + +ct_key : L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; } + | PROTOCOL { $$ = NFT_CT_PROTOCOL; } + | MARK { $$ = NFT_CT_MARK; } + | STATE { $$ = NFT_CT_STATE; } + | DIRECTION { $$ = NFT_CT_DIRECTION; } + | STATUS { $$ = NFT_CT_STATUS; } + | EXPIRATION { $$ = NFT_CT_EXPIRATION; } + | HELPER { $$ = NFT_CT_HELPER; } + | SADDR { $$ = NFT_CT_SRC; } + | DADDR { $$ = NFT_CT_DST; } + | PROTO_SRC { $$ = NFT_CT_PROTO_SRC; } + | PROTO_DST { $$ = NFT_CT_PROTO_DST; } + | LABEL { $$ = NFT_CT_LABELS; } + | EVENT { $$ = NFT_CT_EVENTMASK; } + | SECMARK close_scope_secmark { $$ = NFT_CT_SECMARK; } + | ID { $$ = NFT_CT_ID; } + | ct_key_dir_optional + ; + +ct_key_dir : SADDR { $$ = NFT_CT_SRC; } + | DADDR { $$ = NFT_CT_DST; } + | L3PROTOCOL { $$ = NFT_CT_L3PROTOCOL; } + | PROTOCOL { $$ = NFT_CT_PROTOCOL; } + | PROTO_SRC { $$ = NFT_CT_PROTO_SRC; } + | PROTO_DST { $$ = NFT_CT_PROTO_DST; } + | ct_key_dir_optional + ; + +ct_key_proto_field : IP SADDR close_scope_ip { $$ = NFT_CT_SRC_IP; } + | IP DADDR close_scope_ip { $$ = NFT_CT_DST_IP; } + | IP6 SADDR close_scope_ip6 { $$ = NFT_CT_SRC_IP6; } + | IP6 DADDR close_scope_ip6 { $$ = NFT_CT_DST_IP6; } + ; + +ct_key_dir_optional : BYTES { $$ = NFT_CT_BYTES; } + | PACKETS { $$ = NFT_CT_PKTS; } + | AVGPKT { $$ = NFT_CT_AVGPKT; } + | ZONE { $$ = NFT_CT_ZONE; } + ; + +symbol_stmt_expr : symbol_expr + | keyword_expr + ; + +list_stmt_expr : symbol_stmt_expr COMMA symbol_stmt_expr + { + $$ = list_expr_alloc(&@$); + compound_expr_add($$, $1); + compound_expr_add($$, $3); + } + | list_stmt_expr COMMA symbol_stmt_expr + { + $1->location = @$; + compound_expr_add($1, $3); + $$ = $1; + } + ; + +ct_stmt : CT ct_key SET stmt_expr close_scope_ct + { + switch ($2) { + case NFT_CT_HELPER: + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_CT_HELPER; + $$->objref.expr = $4; + break; + default: + $$ = ct_stmt_alloc(&@$, $2, -1, $4); + break; + } + } + | CT TIMEOUT SET stmt_expr close_scope_ct + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_CT_TIMEOUT; + $$->objref.expr = $4; + + } + | CT EXPECTATION SET stmt_expr close_scope_ct + { + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_CT_EXPECT; + $$->objref.expr = $4; + } + | CT ct_dir ct_key_dir_optional SET stmt_expr close_scope_ct + { + $$ = ct_stmt_alloc(&@$, $3, $2, $5); + } + ; + +payload_stmt : payload_expr SET stmt_expr + { + if ($1->etype == EXPR_EXTHDR) + $$ = exthdr_stmt_alloc(&@$, $1, $3); + else + $$ = payload_stmt_alloc(&@$, $1, $3); + } + ; + +payload_expr : payload_raw_expr + | eth_hdr_expr + | vlan_hdr_expr + | arp_hdr_expr + | ip_hdr_expr + | icmp_hdr_expr + | igmp_hdr_expr + | ip6_hdr_expr + | icmp6_hdr_expr + | auth_hdr_expr + | esp_hdr_expr + | comp_hdr_expr + | udp_hdr_expr + | udplite_hdr_expr + | tcp_hdr_expr close_scope_tcp + | dccp_hdr_expr + | sctp_hdr_expr + | th_hdr_expr + | vxlan_hdr_expr + | geneve_hdr_expr + | gre_hdr_expr + | gretap_hdr_expr + ; + +payload_raw_expr : AT payload_base_spec COMMA NUM COMMA NUM close_scope_at + { + $$ = payload_expr_alloc(&@$, NULL, 0); + payload_init_raw($$, $2, $4, $6); + $$->byteorder = BYTEORDER_BIG_ENDIAN; + $$->payload.is_raw = true; + } + ; + +payload_base_spec : LL_HDR { $$ = PROTO_BASE_LL_HDR; } + | NETWORK_HDR { $$ = PROTO_BASE_NETWORK_HDR; } + | TRANSPORT_HDR close_scope_th { $$ = PROTO_BASE_TRANSPORT_HDR; } + | STRING + { + if (!strcmp($1, "ih")) { + $$ = PROTO_BASE_INNER_HDR; + } else { + erec_queue(error(&@1, "unknown raw payload base"), state->msgs); + xfree($1); + YYERROR; + } + xfree($1); + } + ; + +eth_hdr_expr : ETHER eth_hdr_field close_scope_eth + { + $$ = payload_expr_alloc(&@$, &proto_eth, $2); + } + ; + +eth_hdr_field : SADDR { $$ = ETHHDR_SADDR; } + | DADDR { $$ = ETHHDR_DADDR; } + | TYPE close_scope_type { $$ = ETHHDR_TYPE; } + ; + +vlan_hdr_expr : VLAN vlan_hdr_field close_scope_vlan + { + $$ = payload_expr_alloc(&@$, &proto_vlan, $2); + } + ; + +vlan_hdr_field : ID { $$ = VLANHDR_VID; } + | CFI { $$ = VLANHDR_CFI; } + | DEI { $$ = VLANHDR_DEI; } + | PCP { $$ = VLANHDR_PCP; } + | TYPE close_scope_type { $$ = VLANHDR_TYPE; } + ; + +arp_hdr_expr : ARP arp_hdr_field close_scope_arp + { + $$ = payload_expr_alloc(&@$, &proto_arp, $2); + } + ; + +arp_hdr_field : HTYPE { $$ = ARPHDR_HRD; } + | PTYPE { $$ = ARPHDR_PRO; } + | HLEN { $$ = ARPHDR_HLN; } + | PLEN { $$ = ARPHDR_PLN; } + | OPERATION { $$ = ARPHDR_OP; } + | SADDR ETHER close_scope_eth { $$ = ARPHDR_SADDR_ETHER; } + | DADDR ETHER close_scope_eth { $$ = ARPHDR_DADDR_ETHER; } + | SADDR IP close_scope_ip { $$ = ARPHDR_SADDR_IP; } + | DADDR IP close_scope_ip { $$ = ARPHDR_DADDR_IP; } + ; + +ip_hdr_expr : IP ip_hdr_field close_scope_ip + { + $$ = payload_expr_alloc(&@$, &proto_ip, $2); + } + | IP OPTION ip_option_type ip_option_field close_scope_ip + { + $$ = ipopt_expr_alloc(&@$, $3, $4); + if (!$$) { + erec_queue(error(&@1, "unknown ip option type/field"), state->msgs); + YYERROR; + } + } + | IP OPTION ip_option_type close_scope_ip + { + $$ = ipopt_expr_alloc(&@$, $3, IPOPT_FIELD_TYPE); + $$->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } + ; + +ip_hdr_field : HDRVERSION { $$ = IPHDR_VERSION; } + | HDRLENGTH { $$ = IPHDR_HDRLENGTH; } + | DSCP { $$ = IPHDR_DSCP; } + | ECN { $$ = IPHDR_ECN; } + | 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; } + ; + +ip_option_type : LSRR { $$ = IPOPT_LSRR; } + | RR { $$ = IPOPT_RR; } + | SSRR { $$ = IPOPT_SSRR; } + | RA { $$ = IPOPT_RA; } + ; + +ip_option_field : TYPE close_scope_type { $$ = IPOPT_FIELD_TYPE; } + | LENGTH { $$ = IPOPT_FIELD_LENGTH; } + | VALUE { $$ = IPOPT_FIELD_VALUE; } + | PTR { $$ = IPOPT_FIELD_PTR; } + | ADDR { $$ = IPOPT_FIELD_ADDR_0; } + ; + +icmp_hdr_expr : ICMP icmp_hdr_field close_scope_icmp + { + $$ = payload_expr_alloc(&@$, &proto_icmp, $2); + } + ; + +icmp_hdr_field : TYPE close_scope_type { $$ = ICMPHDR_TYPE; } + | CODE { $$ = ICMPHDR_CODE; } + | CHECKSUM { $$ = ICMPHDR_CHECKSUM; } + | ID { $$ = ICMPHDR_ID; } + | SEQUENCE { $$ = ICMPHDR_SEQ; } + | GATEWAY { $$ = ICMPHDR_GATEWAY; } + | MTU { $$ = ICMPHDR_MTU; } + ; + +igmp_hdr_expr : IGMP igmp_hdr_field close_scope_igmp + { + $$ = payload_expr_alloc(&@$, &proto_igmp, $2); + } + ; + +igmp_hdr_field : TYPE close_scope_type { $$ = IGMPHDR_TYPE; } + | CHECKSUM { $$ = IGMPHDR_CHECKSUM; } + | MRT { $$ = IGMPHDR_MRT; } + | GROUP { $$ = IGMPHDR_GROUP; } + ; + +ip6_hdr_expr : IP6 ip6_hdr_field close_scope_ip6 + { + $$ = payload_expr_alloc(&@$, &proto_ip6, $2); + } + ; + +ip6_hdr_field : HDRVERSION { $$ = IP6HDR_VERSION; } + | DSCP { $$ = IP6HDR_DSCP; } + | ECN { $$ = IP6HDR_ECN; } + | FLOWLABEL { $$ = IP6HDR_FLOWLABEL; } + | LENGTH { $$ = IP6HDR_LENGTH; } + | NEXTHDR { $$ = IP6HDR_NEXTHDR; } + | HOPLIMIT { $$ = IP6HDR_HOPLIMIT; } + | SADDR { $$ = IP6HDR_SADDR; } + | DADDR { $$ = IP6HDR_DADDR; } + ; +icmp6_hdr_expr : ICMP6 icmp6_hdr_field close_scope_icmp + { + $$ = payload_expr_alloc(&@$, &proto_icmp6, $2); + } + ; + +icmp6_hdr_field : TYPE close_scope_type { $$ = ICMP6HDR_TYPE; } + | CODE { $$ = ICMP6HDR_CODE; } + | CHECKSUM { $$ = ICMP6HDR_CHECKSUM; } + | PPTR { $$ = ICMP6HDR_PPTR; } + | MTU { $$ = ICMP6HDR_MTU; } + | ID { $$ = ICMP6HDR_ID; } + | SEQUENCE { $$ = ICMP6HDR_SEQ; } + | MAXDELAY { $$ = ICMP6HDR_MAXDELAY; } + | TADDR { $$ = ICMP6HDR_TADDR; } + | DADDR { $$ = ICMP6HDR_DADDR; } + ; + +auth_hdr_expr : AH auth_hdr_field close_scope_ah + { + $$ = payload_expr_alloc(&@$, &proto_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 close_scope_esp + { + $$ = payload_expr_alloc(&@$, &proto_esp, $2); + } + ; + +esp_hdr_field : SPI { $$ = ESPHDR_SPI; } + | SEQUENCE { $$ = ESPHDR_SEQUENCE; } + ; + +comp_hdr_expr : COMP comp_hdr_field close_scope_comp + { + $$ = payload_expr_alloc(&@$, &proto_comp, $2); + } + ; + +comp_hdr_field : NEXTHDR { $$ = COMPHDR_NEXTHDR; } + | FLAGS { $$ = COMPHDR_FLAGS; } + | CPI { $$ = COMPHDR_CPI; } + ; + +udp_hdr_expr : UDP udp_hdr_field close_scope_udp + { + $$ = payload_expr_alloc(&@$, &proto_udp, $2); + } + ; + +udp_hdr_field : SPORT { $$ = UDPHDR_SPORT; } + | DPORT { $$ = UDPHDR_DPORT; } + | LENGTH { $$ = UDPHDR_LENGTH; } + | CHECKSUM { $$ = UDPHDR_CHECKSUM; } + ; + +udplite_hdr_expr : UDPLITE udplite_hdr_field close_scope_udplite + { + $$ = payload_expr_alloc(&@$, &proto_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(&@$, &proto_tcp, $2); + } + | TCP OPTION tcp_hdr_option_type + { + $$ = tcpopt_expr_alloc(&@$, $3, TCPOPT_COMMON_KIND); + $$->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } + | TCP OPTION tcp_hdr_option_kind_and_field + { + $$ = tcpopt_expr_alloc(&@$, $3.kind, $3.field); + } + | TCP OPTION AT close_scope_at tcp_hdr_option_type COMMA NUM COMMA NUM + { + $$ = tcpopt_expr_alloc(&@$, $5, 0); + tcpopt_init_raw($$, $5, $7, $9, 0); + } + ; + +inner_inet_expr : ip_hdr_expr + | icmp_hdr_expr + | igmp_hdr_expr + | ip6_hdr_expr + | icmp6_hdr_expr + | auth_hdr_expr + | esp_hdr_expr + | comp_hdr_expr + | udp_hdr_expr + | udplite_hdr_expr + | tcp_hdr_expr close_scope_tcp + | dccp_hdr_expr + | sctp_hdr_expr + | th_hdr_expr + ; + +inner_eth_expr : eth_hdr_expr + | vlan_hdr_expr + | arp_hdr_expr + ; + +inner_expr : inner_eth_expr + | inner_inet_expr + ; + +vxlan_hdr_expr : VXLAN vxlan_hdr_field + { + struct expr *expr; + + expr = payload_expr_alloc(&@$, &proto_vxlan, $2); + expr->payload.inner_desc = &proto_vxlan; + $$ = expr; + } + | VXLAN inner_expr + { + $$ = $2; + $$->location = @$; + $$->payload.inner_desc = &proto_vxlan; + } + ; + +vxlan_hdr_field : VNI { $$ = VXLANHDR_VNI; } + | FLAGS { $$ = VXLANHDR_FLAGS; } + ; + +geneve_hdr_expr : GENEVE geneve_hdr_field + { + struct expr *expr; + + expr = payload_expr_alloc(&@$, &proto_geneve, $2); + expr->payload.inner_desc = &proto_geneve; + $$ = expr; + } + | GENEVE inner_expr + { + $$ = $2; + $$->location = @$; + $$->payload.inner_desc = &proto_geneve; + } + ; + +geneve_hdr_field : VNI { $$ = GNVHDR_VNI; } + | TYPE { $$ = GNVHDR_TYPE; } + ; + +gre_hdr_expr : GRE gre_hdr_field close_scope_gre + { + $$ = payload_expr_alloc(&@$, &proto_gre, $2); + } + | GRE close_scope_gre inner_inet_expr + { + $$ = $3; + $$->payload.inner_desc = &proto_gre; + } + ; + +gre_hdr_field : HDRVERSION { $$ = GREHDR_VERSION; } + | FLAGS { $$ = GREHDR_FLAGS; } + | PROTOCOL { $$ = GREHDR_PROTOCOL; } + ; + +gretap_hdr_expr : GRETAP close_scope_gre inner_expr + { + $$ = $3; + $$->payload.inner_desc = &proto_gretap; + } + ; + +optstrip_stmt : RESET TCP OPTION tcp_hdr_option_type close_scope_tcp + { + $$ = optstrip_stmt_alloc(&@$, tcpopt_expr_alloc(&@$, + $4, TCPOPT_COMMON_KIND)); + } + ; + +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; } + ; + +tcp_hdr_option_kind_and_field : MSS tcpopt_field_maxseg + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MAXSEG, .field = $2 }; + $$ = kind_field; + } + | tcp_hdr_option_sack tcpopt_field_sack + { + struct tcp_kind_field kind_field = { .kind = $1, .field = $2 }; + $$ = kind_field; + } + | WINDOW tcpopt_field_window + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_WINDOW, .field = $2 }; + $$ = kind_field; + } + | TIMESTAMP tcpopt_field_tsopt + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_TIMESTAMP, .field = $2 }; + $$ = kind_field; + } + | tcp_hdr_option_type LENGTH + { + struct tcp_kind_field kind_field = { .kind = $1, .field = TCPOPT_COMMON_LENGTH }; + $$ = kind_field; + } + | MPTCP tcpopt_field_mptcp + { + struct tcp_kind_field kind_field = { .kind = TCPOPT_KIND_MPTCP, .field = $2 }; + $$ = kind_field; + } + ; + +tcp_hdr_option_sack : SACK { $$ = TCPOPT_KIND_SACK; } + | SACK0 { $$ = TCPOPT_KIND_SACK; } + | SACK1 { $$ = TCPOPT_KIND_SACK1; } + | SACK2 { $$ = TCPOPT_KIND_SACK2; } + | SACK3 { $$ = TCPOPT_KIND_SACK3; } + ; + +tcp_hdr_option_type : ECHO { $$ = TCPOPT_KIND_ECHO; } + | EOL { $$ = TCPOPT_KIND_EOL; } + | FASTOPEN { $$ = TCPOPT_KIND_FASTOPEN; } + | MD5SIG { $$ = TCPOPT_KIND_MD5SIG; } + | MPTCP { $$ = TCPOPT_KIND_MPTCP; } + | MSS { $$ = TCPOPT_KIND_MAXSEG; } + | NOP { $$ = TCPOPT_KIND_NOP; } + | SACK_PERM { $$ = TCPOPT_KIND_SACK_PERMITTED; } + | TIMESTAMP { $$ = TCPOPT_KIND_TIMESTAMP; } + | WINDOW { $$ = TCPOPT_KIND_WINDOW; } + | tcp_hdr_option_sack { $$ = $1; } + | NUM { + if ($1 > 255) { + erec_queue(error(&@1, "value too large"), state->msgs); + YYERROR; + } + $$ = $1; + } + ; + +tcpopt_field_sack : LEFT { $$ = TCPOPT_SACK_LEFT; } + | RIGHT { $$ = TCPOPT_SACK_RIGHT; } + ; + +tcpopt_field_window : COUNT { $$ = TCPOPT_WINDOW_COUNT; } + ; + +tcpopt_field_tsopt : TSVAL { $$ = TCPOPT_TS_TSVAL; } + | TSECR { $$ = TCPOPT_TS_TSECR; } + ; + +tcpopt_field_maxseg : SIZE { $$ = TCPOPT_MAXSEG_SIZE; } + ; + +tcpopt_field_mptcp : SUBTYPE { $$ = TCPOPT_MPTCP_SUBTYPE; } + ; + +dccp_hdr_expr : DCCP dccp_hdr_field close_scope_dccp + { + $$ = payload_expr_alloc(&@$, &proto_dccp, $2); + } + | DCCP OPTION NUM close_scope_dccp + { + if ($3 > DCCPOPT_TYPE_MAX) { + erec_queue(error(&@1, "value too large"), + state->msgs); + YYERROR; + } + $$ = dccpopt_expr_alloc(&@$, $3); + } + ; + +dccp_hdr_field : SPORT { $$ = DCCPHDR_SPORT; } + | DPORT { $$ = DCCPHDR_DPORT; } + | TYPE close_scope_type { $$ = DCCPHDR_TYPE; } + ; + +sctp_chunk_type : DATA { $$ = SCTP_CHUNK_TYPE_DATA; } + | INIT { $$ = SCTP_CHUNK_TYPE_INIT; } + | INIT_ACK { $$ = SCTP_CHUNK_TYPE_INIT_ACK; } + | SACK { $$ = SCTP_CHUNK_TYPE_SACK; } + | HEARTBEAT { $$ = SCTP_CHUNK_TYPE_HEARTBEAT; } + | HEARTBEAT_ACK { $$ = SCTP_CHUNK_TYPE_HEARTBEAT_ACK; } + | ABORT { $$ = SCTP_CHUNK_TYPE_ABORT; } + | SHUTDOWN { $$ = SCTP_CHUNK_TYPE_SHUTDOWN; } + | SHUTDOWN_ACK { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_ACK; } + | ERROR { $$ = SCTP_CHUNK_TYPE_ERROR; } + | COOKIE_ECHO { $$ = SCTP_CHUNK_TYPE_COOKIE_ECHO; } + | COOKIE_ACK { $$ = SCTP_CHUNK_TYPE_COOKIE_ACK; } + | ECNE { $$ = SCTP_CHUNK_TYPE_ECNE; } + | CWR { $$ = SCTP_CHUNK_TYPE_CWR; } + | SHUTDOWN_COMPLETE { $$ = SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE; } + | ASCONF_ACK { $$ = SCTP_CHUNK_TYPE_ASCONF_ACK; } + | FORWARD_TSN { $$ = SCTP_CHUNK_TYPE_FORWARD_TSN; } + | ASCONF { $$ = SCTP_CHUNK_TYPE_ASCONF; } + ; + +sctp_chunk_common_field : TYPE close_scope_type { $$ = SCTP_CHUNK_COMMON_TYPE; } + | FLAGS { $$ = SCTP_CHUNK_COMMON_FLAGS; } + | LENGTH { $$ = SCTP_CHUNK_COMMON_LENGTH; } + ; + +sctp_chunk_data_field : TSN { $$ = SCTP_CHUNK_DATA_TSN; } + | STREAM { $$ = SCTP_CHUNK_DATA_STREAM; } + | SSN { $$ = SCTP_CHUNK_DATA_SSN; } + | PPID { $$ = SCTP_CHUNK_DATA_PPID; } + ; + +sctp_chunk_init_field : INIT_TAG { $$ = SCTP_CHUNK_INIT_TAG; } + | A_RWND { $$ = SCTP_CHUNK_INIT_RWND; } + | NUM_OSTREAMS { $$ = SCTP_CHUNK_INIT_OSTREAMS; } + | NUM_ISTREAMS { $$ = SCTP_CHUNK_INIT_ISTREAMS; } + | INIT_TSN { $$ = SCTP_CHUNK_INIT_TSN; } + ; + +sctp_chunk_sack_field : CUM_TSN_ACK { $$ = SCTP_CHUNK_SACK_CTSN_ACK; } + | A_RWND { $$ = SCTP_CHUNK_SACK_RWND; } + | NUM_GACK_BLOCKS { $$ = SCTP_CHUNK_SACK_GACK_BLOCKS; } + | NUM_DUP_TSNS { $$ = SCTP_CHUNK_SACK_DUP_TSNS; } + ; + +sctp_chunk_alloc : sctp_chunk_type + { + $$ = sctp_chunk_expr_alloc(&@$, $1, SCTP_CHUNK_COMMON_TYPE); + $$->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } + | sctp_chunk_type sctp_chunk_common_field + { + $$ = sctp_chunk_expr_alloc(&@$, $1, $2); + } + | DATA sctp_chunk_data_field + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_DATA, $2); + } + | INIT sctp_chunk_init_field + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT, $2); + } + | INIT_ACK sctp_chunk_init_field + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_INIT_ACK, $2); + } + | SACK sctp_chunk_sack_field + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SACK, $2); + } + | SHUTDOWN CUM_TSN_ACK + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_SHUTDOWN, + SCTP_CHUNK_SHUTDOWN_CTSN_ACK); + } + | ECNE LOWEST_TSN + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ECNE, + SCTP_CHUNK_ECNE_CWR_MIN_TSN); + } + | CWR LOWEST_TSN + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_CWR, + SCTP_CHUNK_ECNE_CWR_MIN_TSN); + } + | ASCONF_ACK SEQNO + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF_ACK, + SCTP_CHUNK_ASCONF_SEQNO); + } + | FORWARD_TSN NEW_CUM_TSN + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_FORWARD_TSN, + SCTP_CHUNK_FORWARD_TSN_NCTSN); + } + | ASCONF SEQNO + { + $$ = sctp_chunk_expr_alloc(&@$, SCTP_CHUNK_TYPE_ASCONF, + SCTP_CHUNK_ASCONF_SEQNO); + } + ; + +sctp_hdr_expr : SCTP sctp_hdr_field close_scope_sctp + { + $$ = payload_expr_alloc(&@$, &proto_sctp, $2); + } + | SCTP CHUNK sctp_chunk_alloc close_scope_sctp_chunk close_scope_sctp + { + $$ = $3; + } + ; + +sctp_hdr_field : SPORT { $$ = SCTPHDR_SPORT; } + | DPORT { $$ = SCTPHDR_DPORT; } + | VTAG { $$ = SCTPHDR_VTAG; } + | CHECKSUM { $$ = SCTPHDR_CHECKSUM; } + ; + +th_hdr_expr : TRANSPORT_HDR th_hdr_field close_scope_th + { + $$ = payload_expr_alloc(&@$, &proto_th, $2); + if ($$) + $$->payload.is_raw = true; + } + ; + +th_hdr_field : SPORT { $$ = THDR_SPORT; } + | DPORT { $$ = THDR_DPORT; } + ; + +exthdr_expr : hbh_hdr_expr + | rt_hdr_expr + | rt0_hdr_expr + | rt2_hdr_expr + | rt4_hdr_expr + | frag_hdr_expr + | dst_hdr_expr + | mh_hdr_expr + ; + +hbh_hdr_expr : HBH hbh_hdr_field close_scope_hbh + { + $$ = exthdr_expr_alloc(&@$, &exthdr_hbh, $2); + } + ; + +hbh_hdr_field : NEXTHDR { $$ = HBHHDR_NEXTHDR; } + | HDRLENGTH { $$ = HBHHDR_HDRLENGTH; } + ; + +rt_hdr_expr : RT rt_hdr_field close_scope_rt + { + $$ = exthdr_expr_alloc(&@$, &exthdr_rt, $2); + } + ; + +rt_hdr_field : NEXTHDR { $$ = RTHDR_NEXTHDR; } + | HDRLENGTH { $$ = RTHDR_HDRLENGTH; } + | TYPE close_scope_type { $$ = RTHDR_TYPE; } + | SEG_LEFT { $$ = RTHDR_SEG_LEFT; } + ; + +rt0_hdr_expr : RT0 rt0_hdr_field close_scope_rt + { + $$ = exthdr_expr_alloc(&@$, &exthdr_rt0, $2); + } + ; + +rt0_hdr_field : ADDR '[' NUM ']' + { + $$ = RT0HDR_ADDR_1 + $3 - 1; + } + ; + +rt2_hdr_expr : RT2 rt2_hdr_field close_scope_rt + { + $$ = exthdr_expr_alloc(&@$, &exthdr_rt2, $2); + } + ; + +rt2_hdr_field : ADDR { $$ = RT2HDR_ADDR; } + ; + +rt4_hdr_expr : RT4 rt4_hdr_field close_scope_rt + { + $$ = exthdr_expr_alloc(&@$, &exthdr_rt4, $2); + } + ; + +rt4_hdr_field : LAST_ENT { $$ = RT4HDR_LASTENT; } + | FLAGS { $$ = RT4HDR_FLAGS; } + | TAG { $$ = RT4HDR_TAG; } + | SID '[' NUM ']' + { + $$ = RT4HDR_SID_1 + $3 - 1; + } + ; + +frag_hdr_expr : FRAG frag_hdr_field close_scope_frag + { + $$ = 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 close_scope_dst + { + $$ = exthdr_expr_alloc(&@$, &exthdr_dst, $2); + } + ; + +dst_hdr_field : NEXTHDR { $$ = DSTHDR_NEXTHDR; } + | HDRLENGTH { $$ = DSTHDR_HDRLENGTH; } + ; + +mh_hdr_expr : MH mh_hdr_field close_scope_mh + { + $$ = exthdr_expr_alloc(&@$, &exthdr_mh, $2); + } + ; + +mh_hdr_field : NEXTHDR { $$ = MHHDR_NEXTHDR; } + | HDRLENGTH { $$ = MHHDR_HDRLENGTH; } + | TYPE close_scope_type { $$ = MHHDR_TYPE; } + | RESERVED { $$ = MHHDR_RESERVED; } + | CHECKSUM { $$ = MHHDR_CHECKSUM; } + ; + +exthdr_exists_expr : EXTHDR exthdr_key + { + const struct exthdr_desc *desc; + + desc = exthdr_find_proto($2); + + /* Assume that NEXTHDR template is always + * the first one in list of templates. + */ + $$ = exthdr_expr_alloc(&@$, desc, 1); + $$->exthdr.flags = NFT_EXTHDR_F_PRESENT; + } + ; + +exthdr_key : HBH close_scope_hbh { $$ = IPPROTO_HOPOPTS; } + | RT close_scope_rt { $$ = IPPROTO_ROUTING; } + | FRAG close_scope_frag { $$ = IPPROTO_FRAGMENT; } + | DST close_scope_dst { $$ = IPPROTO_DSTOPTS; } + | MH close_scope_mh { $$ = IPPROTO_MH; } + ; + +%% diff --git a/src/parser_json.c b/src/parser_json.c new file mode 100644 index 0000000..199241a --- /dev/null +++ b/src/parser_json.c @@ -0,0 +1,4415 @@ +/* + * Copyright (c) Red Hat GmbH. Author: Phil Sutter <phil@nwl.cc> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <errno.h> +#include <syslog.h> + +#include <erec.h> +#include <expression.h> +#include <tcpopt.h> +#include <list.h> +#include <netlink.h> +#include <parser.h> +#include <rule.h> +#include <sctp_chunk.h> +#include <socket.h> + +#include <netdb.h> +#include <netinet/icmp6.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> +#include <net/if.h> +#include <linux/xfrm.h> + +#include <linux/netfilter.h> +#include <linux/netfilter/nf_conntrack_tuple_common.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_synproxy.h> +#include <linux/netfilter/nf_tables.h> +#include <jansson.h> + +#include <mnl.h> +#include <libnftnl/rule.h> +#include <linux/netfilter/nfnetlink.h> + +#define CTX_F_RHS (1 << 0) +#define CTX_F_STMT (1 << 1) +#define CTX_F_PRIMARY (1 << 2) +#define CTX_F_DTYPE (1 << 3) +#define CTX_F_SET_RHS (1 << 4) +#define CTX_F_MANGLE (1 << 5) +#define CTX_F_SES (1 << 6) /* set_elem_expr_stmt */ +#define CTX_F_MAP (1 << 7) /* LHS of map_expr */ +#define CTX_F_CONCAT (1 << 8) /* inside concat_expr */ + +struct json_ctx { + struct nft_ctx *nft; + struct list_head *msgs; + struct list_head *cmds; + uint32_t flags; +}; + +#define is_RHS(ctx) (ctx->flags & CTX_F_RHS) +#define is_STMT(ctx) (ctx->flags & CTX_F_STMT) +#define is_PRIMARY(ctx) (ctx->flags & CTX_F_PRIMARY) +#define is_DTYPE(ctx) (ctx->flags & CTX_F_DTYPE) +#define is_SET_RHS(ctx) (ctx->flags & CTX_F_SET_RHS) + +static char *ctx_flags_to_string(struct json_ctx *ctx) +{ + static char buf[1024]; + const char *sep = ""; + + buf[0] = '\0'; + + if (is_RHS(ctx)) { + strcat(buf, sep); + strcat(buf, "RHS"); + sep = ", "; + } + if (is_STMT(ctx)) { + strcat(buf, sep); + strcat(buf, "STMT"); + sep = ", "; + } + if (is_PRIMARY(ctx)) { + strcat(buf, sep); + strcat(buf, "PRIMARY"); + sep = ", "; + } + if (is_DTYPE(ctx)) { + strcat(buf, sep); + strcat(buf, "DTYPE"); + sep = ", "; + } + if (is_SET_RHS(ctx)) { + strcat(buf, sep); + strcat(buf, "SET_RHS"); + sep = ", "; + } + return buf; +} + +/* common parser entry points */ + +static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_rhs_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_stmt_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_primary_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_set_rhs_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_set_elem_expr_stmt(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_map_lhs_expr(struct json_ctx *ctx, json_t *root); +static struct expr *json_parse_concat_elem_expr(struct json_ctx *ctx, json_t *root); +static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root); + +/* parsing helpers */ + +const struct location *int_loc = &internal_location; +static struct input_descriptor json_indesc; + +static void json_lib_error(struct json_ctx *ctx, json_error_t *err) +{ + struct location loc = { + .indesc = &json_indesc, + .line_offset = err->position - err->column, + .first_line = err->line, + .last_line = err->line, + .first_column = err->column, + /* no information where problematic part ends :( */ + .last_column = err->column, + }; + + erec_queue(error(&loc, err->text), ctx->msgs); +} + +__attribute__((format(printf, 2, 3))) +static void json_error(struct json_ctx *ctx, const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, int_loc, fmt, ap); + va_end(ap); + erec_queue(erec, ctx->msgs); +} + +static const char *json_typename(const json_t *val) +{ + const char *type_name[] = { + [JSON_OBJECT] = "object", + [JSON_ARRAY] = "array", + [JSON_STRING] = "string", + [JSON_INTEGER] = "integer", + [JSON_REAL] = "real", + [JSON_TRUE] = "true", + [JSON_FALSE] = "false", + [JSON_NULL] = "null" + }; + + return type_name[json_typeof(val)]; +} + +static int json_unpack_err(struct json_ctx *ctx, + json_t *root, const char *fmt, ...) +{ + json_error_t err; + va_list ap; + int rc; + + va_start(ap, fmt); + rc = json_vunpack_ex(root, &err, 0, fmt, ap); + va_end(ap); + + if (rc) + json_lib_error(ctx, &err); + return rc; +} + +static int json_unpack_stmt(struct json_ctx *ctx, json_t *root, + const char **key, json_t **value) +{ + assert(key); + assert(value); + + if (json_object_size(root) != 1) { + json_error(ctx, "Malformed object (too many properties): '%s'.", + json_dumps(root, 0)); + return 1; + } + + json_object_foreach(root, *key, *value) + return 0; + + /* not reached */ + return 1; +} + +static int parse_family(const char *name, uint32_t *family) +{ + unsigned int i; + struct { + const char *name; + int val; + } family_tbl[] = { + { "ip", NFPROTO_IPV4 }, + { "ip6", NFPROTO_IPV6 }, + { "inet", NFPROTO_INET }, + { "arp", NFPROTO_ARP }, + { "bridge", NFPROTO_BRIDGE }, + { "netdev", NFPROTO_NETDEV } + }; + + assert(family); + + for (i = 0; i < array_size(family_tbl); i++) { + if (strcmp(name, family_tbl[i].name)) + continue; + + *family = family_tbl[i].val; + return 0; + } + return -1; +} + +static int json_parse_family(struct json_ctx *ctx, json_t *root) +{ + const char *family; + + if (!json_unpack(root, "{s:s}", "family", &family)) { + uint32_t familyval; + + if (parse_family(family, &familyval) || + (familyval != NFPROTO_IPV6 && + familyval != NFPROTO_IPV4)) { + json_error(ctx, "Invalid family '%s'.", family); + return -1; + } + return familyval; + } + + return NFPROTO_UNSPEC; +} + +static bool is_keyword(const char *keyword) +{ + const char *keywords[] = { + "ether", + "ip", + "ip6", + "vlan", + "arp", + "dnat", + "snat", + "ecn", + "reset", + "original", + "reply", + "label", + }; + unsigned int i; + + for (i = 0; i < array_size(keywords); i++) { + if (!strcmp(keyword, keywords[i])) + return true; + } + return false; +} + +static bool is_constant(const char *keyword) +{ + const char *constants[] = { + "tcp", + "udp", + "udplite", + "esp", + "ah", + "icmp", + "icmpv6", + "comp", + "dccp", + "sctp", + "redirect", + }; + unsigned int i; + + for (i = 0; i < array_size(constants); i++) { + if (!strcmp(keyword, constants[i])) + return true; + } + return false; +} + +static struct expr *json_parse_constant(struct json_ctx *ctx, const char *name) +{ + const struct { + const char *name; + uint8_t data; + const struct datatype *dtype; + } constant_tbl[] = { + { "tcp", IPPROTO_TCP, &inet_protocol_type }, + { "udp", IPPROTO_UDP, &inet_protocol_type }, + { "udplite", IPPROTO_UDPLITE, &inet_protocol_type }, + { "esp", IPPROTO_ESP, &inet_protocol_type }, + { "ah", IPPROTO_AH, &inet_protocol_type }, + { "icmp", IPPROTO_ICMP, &inet_protocol_type }, + { "icmpv6", IPPROTO_ICMPV6, &inet_protocol_type }, + { "comp", IPPROTO_COMP, &inet_protocol_type }, + { "dccp", IPPROTO_DCCP, &inet_protocol_type }, + { "sctp", IPPROTO_SCTP, &inet_protocol_type }, + { "redirect", ICMP_REDIRECT, &icmp_type_type }, + }; + unsigned int i; + + for (i = 0; i < array_size(constant_tbl); i++) { + if (strcmp(name, constant_tbl[i].name)) + continue; + return constant_expr_alloc(int_loc, + constant_tbl[i].dtype, + BYTEORDER_HOST_ENDIAN, + BITS_PER_BYTE, + &constant_tbl[i].data); + } + json_error(ctx, "Unknown constant '%s'.", name); + return NULL; +} + +/* this is a combination of symbol_expr, integer_expr, boolean_expr ... */ +static struct expr *json_parse_immediate(struct json_ctx *ctx, json_t *root) +{ + enum symbol_types symtype = SYMBOL_VALUE; + const char *str; + char buf[64] = {}; + + switch (json_typeof(root)) { + case JSON_STRING: + str = json_string_value(root); + if (str[0] == '@') { + symtype = SYMBOL_SET; + str++; + } else if (str[0] == '*' && str[1] == '\0') { + return set_elem_catchall_expr_alloc(int_loc); + } else if (is_keyword(str)) { + return symbol_expr_alloc(int_loc, + SYMBOL_VALUE, NULL, str); + } else if (is_constant(str)) { + return json_parse_constant(ctx, str); + } + break; + case JSON_INTEGER: + snprintf(buf, sizeof(buf), + "%" JSON_INTEGER_FORMAT, json_integer_value(root)); + str = buf; + break; + case JSON_TRUE: + case JSON_FALSE: + buf[0] = json_is_true(root); + return constant_expr_alloc(int_loc, &boolean_type, + BYTEORDER_HOST_ENDIAN, + BITS_PER_BYTE, buf); + default: + json_error(ctx, "Unexpected JSON type %s for immediate value.", + json_typename(root)); + return NULL; + } + + return symbol_expr_alloc(int_loc, symtype, NULL, str); +} + +static struct expr *json_parse_meta_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct error_record *erec; + unsigned int key; + const char *name; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &name)) + return NULL; + erec = meta_key_parse(int_loc, name, &key); + if (erec) { + erec_queue(erec, ctx->msgs); + return NULL; + } + return meta_expr_alloc(int_loc, key); +} + +static struct expr *json_parse_osf_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *key, *ttl; + uint32_t flagval = 0; + uint8_t ttlval = 0; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; + + if (!json_unpack(root, "{s:s}", "ttl", &ttl)) { + if (!strcmp(ttl, "loose")) { + ttlval = 1; + } else if (!strcmp(ttl, "skip")) { + ttlval = 2; + } else { + json_error(ctx, "Invalid osf ttl option '%s'.", ttl); + return NULL; + } + } + + if (!strcmp(key, "name")) { + return osf_expr_alloc(int_loc, ttlval, flagval); + } else if (!strcmp(key, "version")) { + flagval |= NFT_OSF_F_VERSION; + return osf_expr_alloc(int_loc, ttlval, flagval); + } + + json_error(ctx, "Invalid osf key value."); + return NULL; +} + +static struct expr *json_parse_socket_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *key; + int keyval = -1; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; + + if (!strcmp(key, "transparent")) + keyval = NFT_SOCKET_TRANSPARENT; + else if (!strcmp(key, "mark")) + keyval = NFT_SOCKET_MARK; + else if (!strcmp(key, "wildcard")) + keyval = NFT_SOCKET_WILDCARD; + + if (keyval == -1) { + json_error(ctx, "Invalid socket key value."); + return NULL; + } + + return socket_expr_alloc(int_loc, keyval, 0); +} + +static int json_parse_payload_field(const struct proto_desc *desc, + const char *name, int *field) +{ + unsigned int i; + + for (i = 0; i < PROTO_HDRS_MAX; i++) { + if (desc->templates[i].token && + !strcmp(desc->templates[i].token, name)) { + if (field) + *field = i; + return 0; + } + } + return 1; +} + +static int json_parse_tcp_option_type(const char *name, int *val) +{ + unsigned int i; + + for (i = 0; i < array_size(tcpopt_protocols); i++) { + if (tcpopt_protocols[i] && + !strcmp(tcpopt_protocols[i]->name, name)) { + if (val) + *val = i; + return 0; + } + } + /* special case for sack0 - sack3 */ + if (sscanf(name, "sack%u", &i) == 1 && i < 4) { + if (val && i == 0) + *val = TCPOPT_KIND_SACK; + else if (val && i > 0) + *val = TCPOPT_KIND_SACK1 + i - 1; + return 0; + } + return 1; +} + +static int json_parse_tcp_option_field(int type, const char *name, int *val) +{ + const struct exthdr_desc *desc; + unsigned int block = 0; + unsigned int i; + + switch (type) { + case TCPOPT_KIND_SACK1: + type = TCPOPT_KIND_SACK; + block = 1; + break; + case TCPOPT_KIND_SACK2: + type = TCPOPT_KIND_SACK; + block = 2; + break; + case TCPOPT_KIND_SACK3: + type = TCPOPT_KIND_SACK; + block = 3; + break; + } + + if (type < 0 || type >= (int)array_size(tcpopt_protocols)) + return 1; + + desc = tcpopt_protocols[type]; + if (!desc) + return 1; + + for (i = 0; i < array_size(desc->templates); i++) { + if (desc->templates[i].token && + !strcmp(desc->templates[i].token, name)) { + if (block) { + block--; + continue; + } + + if (val) + *val = i; + return 0; + } + } + return 1; +} + +static const struct proto_desc *proto_lookup_byname(const char *name) +{ + const struct proto_desc *proto_tbl[] = { + &proto_eth, + &proto_vlan, + &proto_arp, + &proto_ip, + &proto_icmp, + &proto_igmp, + &proto_ip6, + &proto_icmp6, + &proto_ah, + &proto_esp, + &proto_comp, + &proto_udp, + &proto_udplite, + &proto_tcp, + &proto_dccp, + &proto_sctp, + &proto_th, + &proto_vxlan, + &proto_gre, + &proto_gretap, + &proto_geneve, + }; + unsigned int i; + + for (i = 0; i < array_size(proto_tbl); i++) { + if (!strcmp(proto_tbl[i]->name, name)) + return proto_tbl[i]; + } + return NULL; +} + +static const struct proto_desc *inner_proto_lookup_byname(const char *name) +{ + const struct proto_desc *proto_tbl[] = { + &proto_geneve, + &proto_gre, + &proto_gretap, + &proto_vxlan, + }; + unsigned int i; + + for (i = 0; i < array_size(proto_tbl); i++) { + if (!strcmp(proto_tbl[i]->name, name)) + return proto_tbl[i]; + } + return NULL; +} + +static struct expr *json_parse_payload_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *tunnel, *protocol, *field, *base; + int offset, len, val; + struct expr *expr; + + if (!json_unpack(root, "{s:s, s:i, s:i}", + "base", &base, "offset", &offset, "len", &len)) { + if (!strcmp(base, "ll")) { + val = PROTO_BASE_LL_HDR; + } else if (!strcmp(base, "nh")) { + val = PROTO_BASE_NETWORK_HDR; + } else if (!strcmp(base, "th")) { + val = PROTO_BASE_TRANSPORT_HDR; + } else if (!strcmp(base, "ih")) { + val = PROTO_BASE_INNER_HDR; + } else { + json_error(ctx, "Invalid payload base '%s'.", base); + return NULL; + } + expr = payload_expr_alloc(int_loc, NULL, 0); + payload_init_raw(expr, val, offset, len); + expr->byteorder = BYTEORDER_BIG_ENDIAN; + expr->payload.is_raw = true; + return expr; + } else if (!json_unpack(root, "{s:s, s:s, s:s}", + "tunnel", &tunnel, "protocol", &protocol, "field", &field)) { + const struct proto_desc *proto = proto_lookup_byname(protocol); + const struct proto_desc *inner_proto = inner_proto_lookup_byname(tunnel); + + if (!inner_proto) { + json_error(ctx, "Unknown payload tunnel protocol '%s'.", + tunnel); + return NULL; + } + if (!proto) { + json_error(ctx, "Unknown payload protocol '%s'.", + protocol); + return NULL; + } + if (json_parse_payload_field(proto, field, &val)) { + json_error(ctx, "Unknown %s field '%s'.", + protocol, field); + return NULL; + } + expr = payload_expr_alloc(int_loc, proto, val); + expr->payload.inner_desc = inner_proto; + + if (proto == &proto_th) + expr->payload.is_raw = true; + + return expr; + } else if (!json_unpack(root, "{s:s, s:s}", + "protocol", &protocol, "field", &field)) { + const struct proto_desc *proto = proto_lookup_byname(protocol); + + if (!proto) { + json_error(ctx, "Unknown payload protocol '%s'.", + protocol); + return NULL; + } + if (json_parse_payload_field(proto, field, &val)) { + json_error(ctx, "Unknown %s field '%s'.", + protocol, field); + return NULL; + } + expr = payload_expr_alloc(int_loc, proto, val); + + if (proto == &proto_th) + expr->payload.is_raw = true; + + return expr; + } + json_error(ctx, "Invalid payload expression properties."); + return NULL; +} + +static struct expr *json_parse_tcp_option_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int fieldval, kind, offset, len; + const char *desc, *field; + struct expr *expr; + + if (!json_unpack(root, "{s:i, s:i, s:i}", + "base", &kind, "offset", &offset, "len", &len)) { + uint32_t flag = 0; + + if (kind < 0 || kind > 255) + return NULL; + + expr = tcpopt_expr_alloc(int_loc, kind, + TCPOPT_COMMON_KIND); + + if (offset == TCPOPT_COMMON_KIND && len == 8) + flag = NFT_EXTHDR_F_PRESENT; + + tcpopt_init_raw(expr, kind, offset, len, flag); + return expr; + } else if (!json_unpack(root, "{s:s}", "name", &desc)) { + if (json_parse_tcp_option_type(desc, &kind)) { + json_error(ctx, "Unknown tcp option name '%s'.", desc); + return NULL; + } + + if (json_unpack(root, "{s:s}", "field", &field)) { + expr = tcpopt_expr_alloc(int_loc, kind, + TCPOPT_COMMON_KIND); + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + return expr; + } + + if (json_parse_tcp_option_field(kind, field, &fieldval)) { + json_error(ctx, "Unknown tcp option field '%s'.", field); + return NULL; + } + + return tcpopt_expr_alloc(int_loc, kind, fieldval); + } + + json_error(ctx, "Invalid tcp option expression properties."); + return NULL; +} + +static int json_parse_ip_option_type(const char *name, int *val) +{ + unsigned int i; + + for (i = 0; i < array_size(ipopt_protocols); i++) { + if (ipopt_protocols[i] && + !strcmp(ipopt_protocols[i]->name, name)) { + if (val) + *val = i; + return 0; + } + } + return 1; +} + +static int json_parse_ip_option_field(int type, const char *name, int *val) +{ + unsigned int i; + const struct exthdr_desc *desc = ipopt_protocols[type]; + + for (i = 0; i < array_size(desc->templates); i++) { + if (desc->templates[i].token && + !strcmp(desc->templates[i].token, name)) { + if (val) + *val = i; + return 0; + } + } + return 1; +} + +static struct expr *json_parse_ip_option_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *desc, *field; + int descval, fieldval; + struct expr *expr; + + if (json_unpack_err(ctx, root, "{s:s}", "name", &desc)) + return NULL; + + if (json_parse_ip_option_type(desc, &descval)) { + json_error(ctx, "Unknown ip option name '%s'.", desc); + return NULL; + } + + if (json_unpack(root, "{s:s}", "field", &field)) { + expr = ipopt_expr_alloc(int_loc, descval, + IPOPT_FIELD_TYPE); + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + + return expr; + } + if (json_parse_ip_option_field(descval, field, &fieldval)) { + json_error(ctx, "Unknown ip option field '%s'.", field); + return NULL; + } + return ipopt_expr_alloc(int_loc, descval, fieldval); +} + +static int json_parse_sctp_chunk_field(const struct exthdr_desc *desc, + const char *name, int *val) +{ + unsigned int i; + + for (i = 0; i < array_size(desc->templates); i++) { + if (desc->templates[i].token && + !strcmp(desc->templates[i].token, name)) { + if (val) + *val = i; + return 0; + } + } + return 1; +} + +static struct expr *json_parse_sctp_chunk_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const struct exthdr_desc *desc; + const char *name, *field; + struct expr *expr; + int fieldval; + + if (json_unpack_err(ctx, root, "{s:s}", "name", &name)) + return NULL; + + desc = sctp_chunk_protocol_find(name); + if (!desc) { + json_error(ctx, "Unknown sctp chunk name '%s'.", name); + return NULL; + } + + if (json_unpack(root, "{s:s}", "field", &field)) { + expr = sctp_chunk_expr_alloc(int_loc, desc->type, + SCTP_CHUNK_COMMON_TYPE); + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + + return expr; + } + if (json_parse_sctp_chunk_field(desc, field, &fieldval)) { + json_error(ctx, "Unknown sctp chunk field '%s'.", field); + return NULL; + } + return sctp_chunk_expr_alloc(int_loc, desc->type, fieldval); +} + +static struct expr *json_parse_dccp_option_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int opt_type; + + if (json_unpack_err(ctx, root, "{s:i}", "type", &opt_type)) + return NULL; + + if (opt_type < DCCPOPT_TYPE_MIN || opt_type > DCCPOPT_TYPE_MAX) { + json_error(ctx, "Unknown dccp option type '%d'.", opt_type); + return NULL; + } + + return dccpopt_expr_alloc(int_loc, opt_type); +} + +static const struct exthdr_desc *exthdr_lookup_byname(const char *name) +{ + const struct exthdr_desc *exthdr_tbl[] = { + &exthdr_hbh, + &exthdr_rt, + &exthdr_rt0, + &exthdr_rt2, + &exthdr_rt4, + &exthdr_frag, + &exthdr_dst, + &exthdr_mh, + }; + unsigned int i; + + for (i = 0; i < array_size(exthdr_tbl); i++) { + if (!strcmp(exthdr_tbl[i]->name, name)) + return exthdr_tbl[i]; + } + return NULL; +} + +static int json_parse_exthdr_field(const struct exthdr_desc *desc, + const char *name, int *field) +{ + unsigned int i; + + for (i = 0; i < array_size(desc->templates); i++) { + if (desc->templates[i].token && + !strcmp(desc->templates[i].token, name)) { + if (field) + *field = i; + return 0; + } + } + return 1; +} + +static struct expr *json_parse_exthdr_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *name, *field = NULL; + struct expr *expr; + int offset = 0, fieldval; + const struct exthdr_desc *desc; + + if (json_unpack_err(ctx, root, "{s:s}", "name", &name)) + return NULL; + + desc = exthdr_lookup_byname(name); + if (!desc) { + json_error(ctx, "Invalid exthdr protocol '%s'.", name); + return NULL; + } + + if (json_unpack(root, "{s:s}", "field", &field)) { + expr = exthdr_expr_alloc(int_loc, desc, 1); + expr->exthdr.flags = NFT_EXTHDR_F_PRESENT; + return expr; + } + + if (json_parse_exthdr_field(desc, field, &fieldval)) { + json_error(ctx, "Unknown %s field %s.", desc->name, field); + return NULL; + } + + /* special treatment for rt0 */ + if (desc == &exthdr_rt0 && + json_unpack_err(ctx, root, "{s:i}", "offset", &offset)) + return NULL; + + return exthdr_expr_alloc(int_loc, desc, fieldval + offset); +} + +static struct expr *json_parse_rt_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const struct { + const char *name; + int val; + } rt_key_tbl[] = { + { "classid", NFT_RT_CLASSID }, + { "nexthop", NFT_RT_NEXTHOP4 }, + { "mtu", NFT_RT_TCPMSS }, + { "ipsec", NFT_RT_XFRM }, + }; + const char *key; + unsigned int i; + int familyval; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; + familyval = json_parse_family(ctx, root); + if (familyval < 0) + return NULL; + + for (i = 0; i < array_size(rt_key_tbl); i++) { + int val = rt_key_tbl[i].val; + bool invalid = true; + + if (strcmp(key, rt_key_tbl[i].name)) + continue; + + if (familyval) { + if (familyval == NFPROTO_IPV6 && + val == NFT_RT_NEXTHOP4) + val = NFT_RT_NEXTHOP6; + invalid = false; + } + return rt_expr_alloc(int_loc, val, invalid); + } + json_error(ctx, "Unknown rt key '%s'.", key); + return NULL; +} + +static bool ct_key_is_dir(enum nft_ct_keys key) +{ + const enum nft_ct_keys ct_dir_keys[] = { + NFT_CT_L3PROTOCOL, + NFT_CT_SRC, + NFT_CT_DST, + NFT_CT_PROTOCOL, + NFT_CT_PROTO_SRC, + NFT_CT_PROTO_DST, + NFT_CT_PKTS, + NFT_CT_BYTES, + NFT_CT_AVGPKT, + NFT_CT_ZONE, + NFT_CT_SRC_IP, + NFT_CT_DST_IP, + NFT_CT_SRC_IP6, + NFT_CT_DST_IP6, + }; + unsigned int i; + + for (i = 0; i < array_size(ct_dir_keys); i++) { + if (key == ct_dir_keys[i]) + return true; + } + return false; +} + +static struct expr *json_parse_ct_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int dirval = -1, keyval = -1; + const char *key, *dir; + unsigned int i; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; + + for (i = 0; i < array_size(ct_templates); i++) { + if (ct_templates[i].token && + !strcmp(key, ct_templates[i].token)) { + keyval = i; + break; + } + } + if (keyval == -1) { + json_error(ctx, "Unknown ct key '%s'.", key); + return NULL; + } + + if (!json_unpack(root, "{s:s}", "dir", &dir)) { + if (!strcmp(dir, "original")) { + dirval = IP_CT_DIR_ORIGINAL; + } else if (!strcmp(dir, "reply")) { + dirval = IP_CT_DIR_REPLY; + } else { + json_error(ctx, "Invalid direction '%s'.", dir); + return NULL; + } + + if (!ct_key_is_dir(keyval)) { + json_error(ctx, "Direction not supported by CT key '%s'.", key); + return NULL; + } + } + + return ct_expr_alloc(int_loc, keyval, dirval); +} + +static struct expr *json_parse_numgen_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int modeval, mod, offset = 0; + const char *mode; + + if (json_unpack_err(ctx, root, "{s:s, s:i}", + "mode", &mode, "mod", &mod)) + return NULL; + json_unpack(root, "{s:i}", "offset", &offset); + + if (!strcmp(mode, "inc")) { + modeval = NFT_NG_INCREMENTAL; + } else if (!strcmp(mode, "random")) { + modeval = NFT_NG_RANDOM; + } else { + json_error(ctx, "Unknown numgen mode '%s'.", mode); + return NULL; + } + + return numgen_expr_alloc(int_loc, modeval, mod, offset); +} + +static struct expr *json_parse_hash_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + int mod, offset = 0, seed = 0; + struct expr *expr, *hash_expr; + bool have_seed; + json_t *jexpr; + + + if (json_unpack_err(ctx, root, "{s:i}", "mod", &mod)) + return NULL; + json_unpack(root, "{s:i}", "offset", &offset); + + if (!strcmp(type, "symhash")) { + return hash_expr_alloc(int_loc, mod, false, 0, + offset, NFT_HASH_SYM); + } else if (strcmp(type, "jhash")) { + json_error(ctx, "Unknown hash type '%s'.", type); + return NULL; + } + + if (json_unpack_err(ctx, root, "{s:o}", "expr", &jexpr)) + return NULL; + expr = json_parse_expr(ctx, jexpr); + if (!expr) { + json_error(ctx, "Invalid jhash expression."); + return NULL; + } + have_seed = !json_unpack(root, "{s:i}", "seed", &seed); + + hash_expr = hash_expr_alloc(int_loc, mod, have_seed, + seed, offset, NFT_HASH_JENKINS); + hash_expr->hash.expr = expr; + return hash_expr; +} + +static int fib_flag_parse(const char *name, int *flags) +{ + const char *fib_flags[] = { + "saddr", + "daddr", + "mark", + "iif", + "oif", + }; + unsigned int i; + + for (i = 0; i < array_size(fib_flags); i++) { + if (!strcmp(name, fib_flags[i])) { + *flags |= (1 << i); + return 0; + } + } + return 1; +} + +static struct expr *json_parse_fib_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *fib_result_tbl[] = { + [NFT_FIB_RESULT_UNSPEC] = NULL, + [NFT_FIB_RESULT_OIF] = "oif", + [NFT_FIB_RESULT_OIFNAME] = "oifname", + [NFT_FIB_RESULT_ADDRTYPE] = "type", + }; + enum nft_fib_result resultval = NFT_FIB_RESULT_UNSPEC; + json_t *flags, *value; + const char *result; + unsigned int i; + size_t index; + int flagval = 0; + + if (json_unpack_err(ctx, root, "{s:s}", "result", &result)) + return NULL; + + for (i = 1; i < array_size(fib_result_tbl); i++) { + if (!strcmp(result, fib_result_tbl[i])) { + resultval = i; + break; + } + } + if (resultval == NFT_FIB_RESULT_UNSPEC) { + json_error(ctx, "Invalid fib result '%s'.", result); + return NULL; + } + + if (!json_unpack(root, "{s:o}", "flags", &flags)) { + const char *flag; + + if (json_is_string(flags)) { + flag = json_string_value(flags); + + if (fib_flag_parse(flag, &flagval)) { + json_error(ctx, "Invalid fib flag '%s'.", flag); + return NULL; + } + } else if (!json_is_array(flags)) { + json_error(ctx, "Unexpected object type in fib tuple."); + return NULL; + } + + json_array_foreach(flags, index, value) { + if (!json_is_string(value)) { + json_error(ctx, "Unexpected object type in fib flags array at index %zd.", index); + return NULL; + } + flag = json_string_value(value); + + if (fib_flag_parse(flag, &flagval)) { + json_error(ctx, "Invalid fib flag '%s'.", flag); + return NULL; + } + } + } + + /* sanity checks from fib_expr in parser_bison.y */ + + if ((flagval & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == 0) { + json_error(ctx, "fib: need either saddr or daddr"); + return NULL; + } + + if ((flagval & (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) == + (NFTA_FIB_F_SADDR|NFTA_FIB_F_DADDR)) { + json_error(ctx, "fib: saddr and daddr are mutually exclusive"); + return NULL; + } + + if ((flagval & (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) == + (NFTA_FIB_F_IIF|NFTA_FIB_F_OIF)) { + json_error(ctx, "fib: iif and oif are mutually exclusive"); + return NULL; + } + + return fib_expr_alloc(int_loc, flagval, resultval); +} + +static struct expr *json_parse_binop_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const struct { + const char *type; + enum ops op; + } op_tbl[] = { + { "|", OP_OR }, + { "^", OP_XOR }, + { "&", OP_AND }, + { ">>", OP_RSHIFT }, + { "<<", OP_LSHIFT }, + }; + enum ops thisop = OP_INVALID; + struct expr *left, *right; + json_t *jleft, *jright; + unsigned int i; + + for (i = 0; i < array_size(op_tbl); i++) { + if (strcmp(type, op_tbl[i].type)) + continue; + + thisop = op_tbl[i].op; + break; + } + if (thisop == OP_INVALID) { + json_error(ctx, "Invalid binop type '%s'.", type); + return NULL; + } + + if (json_unpack_err(ctx, root, "[o, o!]", &jleft, &jright)) + return NULL; + + left = json_parse_primary_expr(ctx, jleft); + if (!left) { + json_error(ctx, "Failed to parse LHS of binop expression."); + return NULL; + } + right = json_parse_rhs_expr(ctx, jright); + if (!right) { + json_error(ctx, "Failed to parse RHS of binop expression."); + expr_free(left); + return NULL; + } + return binop_expr_alloc(int_loc, thisop, left, right); +} + +static struct expr *json_parse_concat_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct expr *expr = NULL, *tmp; + json_t *value; + size_t index; + + if (!json_is_array(root)) { + json_error(ctx, "Unexpected concat object type %s.", + json_typename(root)); + return NULL; + } + + json_array_foreach(root, index, value) { + tmp = json_parse_concat_elem_expr(ctx, value); + if (!tmp) { + json_error(ctx, "Parsing expr at index %zd failed.", index); + expr_free(expr); + return NULL; + } + if (!expr) { + expr = tmp; + continue; + } + if (expr->etype != EXPR_CONCAT) { + struct expr *concat; + + concat = concat_expr_alloc(int_loc); + compound_expr_add(concat, expr); + expr = concat; + } + compound_expr_add(expr, tmp); + } + return expr; +} + +static struct expr *json_parse_prefix_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct expr *expr; + json_t *addr; + int len; + + if (json_unpack_err(ctx, root, "{s:o, s:i}", + "addr", &addr, "len", &len)) + return NULL; + + expr = json_parse_primary_expr(ctx, addr); + if (!expr) { + json_error(ctx, "Invalid address in prefix expr."); + return NULL; + } + return prefix_expr_alloc(int_loc, expr, len); +} + +static struct expr *json_parse_range_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct expr *expr_low, *expr_high; + json_t *low, *high; + + if (json_unpack_err(ctx, root, "[o, o!]", &low, &high)) + return NULL; + + expr_low = json_parse_primary_expr(ctx, low); + if (!expr_low) { + json_error(ctx, "Invalid low value in range expression."); + return NULL; + } + expr_high = json_parse_primary_expr(ctx, high); + if (!expr_high) { + json_error(ctx, "Invalid high value in range expression."); + return NULL; + } + return range_expr_alloc(int_loc, expr_low, expr_high); +} + +static struct expr *json_alloc_chain_expr(const char *chain) +{ + if (!chain) + return NULL; + + return constant_expr_alloc(int_loc, &string_type, BYTEORDER_HOST_ENDIAN, + strlen(chain) * BITS_PER_BYTE, chain); +} + +static struct expr *json_parse_verdict_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const struct { + int verdict; + const char *name; + bool need_chain; + } verdict_tbl[] = { + { NFT_CONTINUE, "continue", false }, + { NFT_JUMP, "jump", true }, + { NFT_GOTO, "goto", true }, + { NFT_RETURN, "return", false }, + { NF_ACCEPT, "accept", false }, + { NF_DROP, "drop", false }, + }; + const char *chain = NULL; + unsigned int i; + + for (i = 0; i < array_size(verdict_tbl); i++) { + if (strcmp(type, verdict_tbl[i].name)) + continue; + + if (verdict_tbl[i].need_chain && + json_unpack_err(ctx, root, "{s:s}", "target", &chain)) + return NULL; + + return verdict_expr_alloc(int_loc, verdict_tbl[i].verdict, + json_alloc_chain_expr(chain)); + } + json_error(ctx, "Unknown verdict '%s'.", type); + return NULL; +} + +static struct expr *json_parse_set_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct expr *expr, *set_expr = NULL; + json_t *value; + size_t index; + + if (!json_is_array(root)) { + expr = json_parse_immediate(ctx, root); + if (!expr) + return NULL; + + if (expr->etype == EXPR_SYMBOL && + expr->symtype == SYMBOL_SET) + return expr; + + expr = set_elem_expr_alloc(int_loc, expr); + set_expr = set_expr_alloc(int_loc, NULL); + compound_expr_add(set_expr, expr); + return set_expr; + } + + json_array_foreach(root, index, value) { + struct expr *expr; + json_t *jleft, *jright; + + if (!json_unpack(value, "[o, o!]", &jleft, &jright)) { + struct expr *expr2; + + expr = json_parse_rhs_expr(ctx, jleft); + if (!expr) { + json_error(ctx, "Invalid set elem at index %zu.", index); + expr_free(set_expr); + return NULL; + } + if (expr->etype != EXPR_SET_ELEM) + expr = set_elem_expr_alloc(int_loc, expr); + + expr2 = json_parse_set_rhs_expr(ctx, jright); + if (!expr2) { + json_error(ctx, "Invalid set elem at index %zu.", index); + expr_free(expr); + expr_free(set_expr); + return NULL; + } + expr2 = mapping_expr_alloc(int_loc, expr, expr2); + expr = expr2; + } else { + expr = json_parse_rhs_expr(ctx, value); + + if (!expr) { + json_error(ctx, "Invalid set elem at index %zu.", index); + expr_free(set_expr); + return NULL; + } + + if (expr->etype != EXPR_SET_ELEM) + expr = set_elem_expr_alloc(int_loc, expr); + } + + if (!set_expr) + set_expr = set_expr_alloc(int_loc, NULL); + compound_expr_add(set_expr, expr); + } + return set_expr; +} + +static struct expr *json_parse_map_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + json_t *jkey, *jdata; + struct expr *key, *data; + + if (json_unpack_err(ctx, root, "{s:o, s:o}", + "key", &jkey, "data", &jdata)) + return NULL; + + key = json_parse_map_lhs_expr(ctx, jkey); + if (!key) { + json_error(ctx, "Illegal map expression key."); + return NULL; + } + + data = json_parse_rhs_expr(ctx, jdata); + if (!data) { + json_error(ctx, "Illegal map expression data."); + expr_free(key); + return NULL; + } + + return map_expr_alloc(int_loc, key, data); +} + +static struct expr *json_parse_set_elem_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + struct expr *expr; + json_t *tmp; + uint64_t i; + + if (json_unpack_err(ctx, root, "{s:o}", "val", &tmp)) + return NULL; + + expr = json_parse_expr(ctx, tmp); + if (!expr) + return NULL; + + expr = set_elem_expr_alloc(int_loc, expr); + + if (!json_unpack(root, "{s:I}", "timeout", &i)) + expr->timeout = i * 1000; + if (!json_unpack(root, "{s:I}", "expires", &i)) + expr->expiration = i * 1000; + if (!json_unpack(root, "{s:s}", "comment", &expr->comment)) + expr->comment = xstrdup(expr->comment); + + return expr; +} + +static struct expr *json_parse_xfrm_expr(struct json_ctx *ctx, + const char *type, json_t *root) +{ + const char *key, *dir; + unsigned int i, spnum; + int dirval = -1, familyval, keyval = -1; + + if (json_unpack_err(ctx, root, "{s:s}", "key", &key)) + return NULL; + + for (i = 1; i < array_size(xfrm_templates); i++) { + if (strcmp(key, xfrm_templates[i].token)) + continue; + keyval = i; + break; + } + + if (keyval == -1) { + json_error(ctx, "Unknown xfrm key '%s'.", key); + return NULL; + } + + familyval = json_parse_family(ctx, root); + if (familyval < 0) + return NULL; + + if (!json_unpack(root, "{s:s}", "dir", &dir)) { + if (!strcmp(dir, "in")) { + dirval = XFRM_POLICY_IN; + } else if (!strcmp(dir, "out")) { + dirval = XFRM_POLICY_OUT; + } else { + json_error(ctx, "Invalid direction '%s'.", dir); + return NULL; + } + } + + spnum = 0; + if (!json_unpack(root, "{s:i}", "spnum", &spnum)) { + if (spnum > 255) { + json_error(ctx, "Invalid spnum'%d'.", spnum); + return NULL; + } + } + + switch (keyval) { + case NFT_XFRM_KEY_SADDR_IP4: + if (familyval == NFPROTO_IPV6) + keyval = NFT_XFRM_KEY_SADDR_IP6; + break; + case NFT_XFRM_KEY_DADDR_IP4: + if (familyval == NFPROTO_IPV6) + keyval = NFT_XFRM_KEY_DADDR_IP6; + break; + default: + break; + } + + return xfrm_expr_alloc(int_loc, dirval, spnum, keyval); +} + +static struct expr *json_parse_expr(struct json_ctx *ctx, json_t *root) +{ + const struct { + const char *name; + struct expr *(*cb)(struct json_ctx *, const char *, json_t *); + uint32_t flags; + } cb_tbl[] = { + { "concat", json_parse_concat_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_DTYPE | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP }, + { "set", json_parse_set_expr, CTX_F_RHS | CTX_F_STMT }, /* allow this as stmt expr because that allows set references */ + { "map", json_parse_map_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS }, + /* below three are multiton_rhs_expr */ + { "prefix", json_parse_prefix_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT }, + { "range", json_parse_range_expr, CTX_F_RHS | CTX_F_SET_RHS | CTX_F_STMT | CTX_F_CONCAT }, + { "payload", json_parse_payload_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "exthdr", json_parse_exthdr_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "tcp option", json_parse_tcp_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, + { "ip option", json_parse_ip_option_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, + { "sctp chunk", json_parse_sctp_chunk_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_CONCAT }, + { "dccp option", json_parse_dccp_option_expr, CTX_F_PRIMARY }, + { "meta", json_parse_meta_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "osf", json_parse_osf_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT }, + { "ipsec", json_parse_xfrm_expr, CTX_F_PRIMARY | CTX_F_MAP | CTX_F_CONCAT }, + { "socket", json_parse_socket_expr, CTX_F_PRIMARY | CTX_F_CONCAT }, + { "rt", json_parse_rt_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "ct", json_parse_ct_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_MANGLE | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "numgen", json_parse_numgen_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + /* below two are hash expr */ + { "jhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "symhash", json_parse_hash_expr, CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "fib", json_parse_fib_expr, CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "|", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "^", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "&", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { ">>", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "<<", json_parse_binop_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SET_RHS | CTX_F_SES | CTX_F_MAP | CTX_F_CONCAT }, + { "accept", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "drop", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "continue", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "jump", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "goto", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "return", json_parse_verdict_expr, CTX_F_RHS | CTX_F_SET_RHS }, + { "elem", json_parse_set_elem_expr, CTX_F_RHS | CTX_F_STMT | CTX_F_PRIMARY | CTX_F_SES }, + }; + struct expr *list; + const char *type; + unsigned int i; + json_t *value; + size_t index; + + + switch (json_typeof(root)) { + case JSON_ARRAY: + if (!(ctx->flags & (CTX_F_RHS | CTX_F_STMT))) { + json_error(ctx, "List expression only allowed on RHS or in statement expression."); + return NULL; + } + + if (is_PRIMARY(ctx)) { + json_error(ctx, "List expression not allowed as primary expression."); + return NULL; + } + + list = list_expr_alloc(int_loc); + json_array_foreach(root, index, value) { + struct expr *expr = json_parse_expr(ctx, value); + if (!expr) { + json_error(ctx, "Parsing list expression item at index %zu failed.", index); + expr_free(list); + return NULL; + } + compound_expr_add(list, expr); + } + return list; + case JSON_TRUE: + case JSON_FALSE: + if (!is_RHS(ctx) && !is_PRIMARY(ctx)) { + json_error(ctx, "Boolean values not allowed in this context."); + return NULL; + } + /* fall through */ + case JSON_STRING: + case JSON_INTEGER: + return json_parse_immediate(ctx, root); + default: + break; + } + + if (json_unpack_stmt(ctx, root, &type, &value)) + return NULL; + + for (i = 0; i < array_size(cb_tbl); i++) { + if (strcmp(type, cb_tbl[i].name)) + continue; + + if ((cb_tbl[i].flags & ctx->flags) != ctx->flags) { + json_error(ctx, "Expression type %s not allowed in context (%s).", + type, ctx_flags_to_string(ctx)); + return NULL; + } + + return cb_tbl[i].cb(ctx, type, value); + } + json_error(ctx, "Unknown expression type '%s'.", type); + return NULL; +} + +static struct expr *json_parse_flagged_expr(struct json_ctx *ctx, + uint32_t flags, json_t *root) +{ + uint32_t old_flags = ctx->flags; + struct expr *expr; + + ctx->flags |= flags; + expr = json_parse_expr(ctx, root); + ctx->flags = old_flags; + + return expr; +} + +static struct expr *json_parse_rhs_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_RHS, root); +} + +static struct expr *json_parse_stmt_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_STMT, root); +} + +static struct expr *json_parse_primary_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_PRIMARY, root); +} + +static struct expr *json_parse_set_rhs_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_SET_RHS, root); +} + +static struct expr *json_parse_mangle_lhs_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_MANGLE, root); +} + +static struct expr *json_parse_set_elem_expr_stmt(struct json_ctx *ctx, json_t *root) +{ + struct expr *expr = json_parse_flagged_expr(ctx, CTX_F_SES, root); + + if (expr && expr->etype != EXPR_SET_ELEM) + expr = set_elem_expr_alloc(int_loc, expr); + + return expr; +} + +static struct expr *json_parse_map_lhs_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_MAP, root); +} + +static struct expr *json_parse_concat_elem_expr(struct json_ctx *ctx, json_t *root) +{ + return json_parse_flagged_expr(ctx, CTX_F_CONCAT, root); +} + +static struct expr *json_parse_dtype_expr(struct json_ctx *ctx, json_t *root) +{ + if (json_is_string(root)) { + const struct datatype *dtype; + + dtype = datatype_lookup_byname(json_string_value(root)); + if (!dtype) { + json_error(ctx, "Invalid datatype '%s'.", + json_string_value(root)); + return NULL; + } + return constant_expr_alloc(int_loc, dtype, + dtype->byteorder, dtype->size, NULL); + } else if (json_is_array(root)) { + json_t *value; + size_t index; + struct expr *expr = concat_expr_alloc(int_loc); + + json_array_foreach(root, index, value) { + struct expr *i = json_parse_dtype_expr(ctx, value); + + if (!i) { + json_error(ctx, "Invalid datatype at index %zu.", index); + expr_free(expr); + return NULL; + } + compound_expr_add(expr, i); + } + return expr; + } + json_error(ctx, "Invalid set datatype."); + return NULL; +} + +static struct stmt *json_parse_match_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *left, *right, *rel_expr; + json_t *jleft, *jright; + const char *opstr = NULL; + enum ops op; + + if (json_unpack_err(ctx, value, "{s:o, s:o, s:s}", + "left", &jleft, + "right", &jright, + "op", &opstr)) + return NULL; + + for (op = OP_INVALID; op < __OP_MAX; op++) { + if (expr_op_symbols[op] && + !strcmp(opstr, expr_op_symbols[op])) + break; + } + switch (op) { + case OP_EQ ... OP_NEG: + break; + case __OP_MAX: + if (!strcmp(opstr, "in")) { + op = OP_IMPLICIT; + break; + } + /* fall through */ + default: + json_error(ctx, "Invalid relational op '%s'.", opstr); + return NULL; + } + + left = json_parse_expr(ctx, jleft); + if (!left) { + json_error(ctx, "Invalid LHS of relational."); + return NULL; + } + right = json_parse_rhs_expr(ctx, jright); + if (!right) { + expr_free(left); + json_error(ctx, "Invalid RHS of relational."); + return NULL; + } + + rel_expr = relational_expr_alloc(int_loc, op, left, right); + return expr_stmt_alloc(int_loc, rel_expr); +} + +static struct stmt *json_parse_counter_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + uint64_t packets, bytes; + struct stmt *stmt; + + if (json_is_null(value)) + return counter_stmt_alloc(int_loc); + + if (!json_unpack(value, "{s:I, s:I}", + "packets", &packets, + "bytes", &bytes)) { + stmt = counter_stmt_alloc(int_loc); + stmt->counter.packets = packets; + stmt->counter.bytes = bytes; + return stmt; + } + + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_COUNTER; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid counter reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static struct stmt *json_parse_last_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + int64_t used; + + if (json_is_null(value)) + return last_stmt_alloc(int_loc); + + if (!json_unpack(value, "{s:I}", "used", &used)) { + stmt = last_stmt_alloc(int_loc); + if (used != -1) { + stmt->last.used = used; + stmt->last.set = 1; + } + return stmt; + } + + return NULL; +} + +static struct stmt *json_parse_verdict_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *expr; + + expr = json_parse_verdict_expr(ctx, key, value); + if (expr) + return verdict_stmt_alloc(int_loc, expr); + + return NULL; +} + +static struct stmt *json_parse_mangle_stmt(struct json_ctx *ctx, + const char *type, json_t *root) +{ + json_t *jkey, *jvalue; + struct expr *key, *value; + struct stmt *stmt; + + if (json_unpack_err(ctx, root, "{s:o, s:o}", + "key", &jkey, "value", &jvalue)) + return NULL; + + key = json_parse_mangle_lhs_expr(ctx, jkey); + if (!key) { + json_error(ctx, "Invalid mangle statement key"); + return NULL; + } + value = json_parse_stmt_expr(ctx, jvalue); + if (!value) { + json_error(ctx, "Invalid mangle statement value"); + expr_free(key); + return NULL; + } + + switch (key->etype) { + case EXPR_EXTHDR: + return exthdr_stmt_alloc(int_loc, key, value); + case EXPR_PAYLOAD: + return payload_stmt_alloc(int_loc, key, value); + case EXPR_META: + stmt = meta_stmt_alloc(int_loc, key->meta.key, value); + expr_free(key); + return stmt; + case EXPR_CT: + if (key->ct.key == NFT_CT_HELPER) { + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_CT_HELPER; + stmt->objref.expr = value; + } else { + stmt = ct_stmt_alloc(int_loc, key->ct.key, + key->ct.direction, value); + } + expr_free(key); + return stmt; + default: + json_error(ctx, "Invalid mangle statement key expression type."); + return NULL; + } +} + +static uint64_t rate_to_bytes(uint64_t val, const char *unit) +{ + uint64_t bytes = val; + + if (!strcmp(unit, "kbytes")) + return bytes * 1024; + if (!strcmp(unit, "mbytes")) + return bytes * 1024 * 1024; + return bytes; +} + +static struct stmt *json_parse_quota_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + int inv = 0; + const char *val_unit = "bytes", *used_unit = "bytes"; + uint64_t val, used = 0; + + if (!json_unpack(value, "{s:I}", "val", &val)) { + json_unpack(value, "{s:b}", "inv", &inv); + json_unpack(value, "{s:s}", "val_unit", &val_unit); + json_unpack(value, "{s:I}", "used", &used); + json_unpack(value, "{s:s}", "used_unit", &used_unit); + stmt = quota_stmt_alloc(int_loc); + stmt->quota.bytes = rate_to_bytes(val, val_unit); + if (used) + stmt->quota.used = rate_to_bytes(used, used_unit); + stmt->quota.flags = (inv ? NFT_QUOTA_F_INV : 0); + return stmt; + } + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_QUOTA; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid quota reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static uint64_t seconds_from_unit(const char *unit) +{ + if (!strcmp(unit, "week")) + return 60 * 60 * 24 * 7; + if (!strcmp(unit, "day")) + return 60 * 60 * 24; + if (!strcmp(unit, "hour")) + return 60 * 60; + if (!strcmp(unit, "minute")) + return 60; + return 1; +} + +static struct stmt *json_parse_limit_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + uint64_t rate, burst = 0; + const char *rate_unit = "packets", *time, *burst_unit = "bytes"; + int inv = 0; + + if (!json_unpack(value, "{s:I, s:s}", + "rate", &rate, "per", &time)) { + json_unpack(value, "{s:s}", "rate_unit", &rate_unit); + json_unpack(value, "{s:b}", "inv", &inv); + json_unpack(value, "{s:I}", "burst", &burst); + json_unpack(value, "{s:s}", "burst_unit", &burst_unit); + + stmt = limit_stmt_alloc(int_loc); + + if (!strcmp(rate_unit, "packets")) { + if (burst == 0) + burst = 5; + + stmt->limit.type = NFT_LIMIT_PKTS; + stmt->limit.rate = rate; + stmt->limit.burst = burst; + } else { + stmt->limit.type = NFT_LIMIT_PKT_BYTES; + stmt->limit.rate = rate_to_bytes(rate, rate_unit); + stmt->limit.burst = rate_to_bytes(burst, burst_unit); + } + stmt->limit.unit = seconds_from_unit(time); + stmt->limit.flags = inv ? NFT_LIMIT_F_INV : 0; + return stmt; + } + + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_LIMIT; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid limit reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static struct stmt *json_parse_fwd_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + json_t *jaddr, *jdev; + struct stmt *stmt; + int familyval; + + if (json_unpack_err(ctx, value, "{s:o}", "dev", &jdev)) + return NULL; + + stmt = fwd_stmt_alloc(int_loc); + + stmt->fwd.dev = json_parse_stmt_expr(ctx, jdev); + if (!stmt->fwd.dev) { + json_error(ctx, "Invalid fwd dev value."); + goto out_err; + } + + familyval = json_parse_family(ctx, value); + if (familyval < 0) + goto out_err; + + if (familyval == NFPROTO_UNSPEC || + json_unpack(value, "{s:o}", "addr", &jaddr)) + return stmt; + + stmt->fwd.family = familyval; + stmt->fwd.addr = json_parse_stmt_expr(ctx, jaddr); + if (!stmt->fwd.addr) { + json_error(ctx, "Invalid fwd addr value."); + goto out_err; + } + + return stmt; +out_err: + stmt_free(stmt); + return NULL; +} + +static struct stmt *json_parse_flow_offload_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + const char *opstr, *flowtable; + + if (json_unpack_err(ctx, value, "{s:s, s:s}", + "op", &opstr, "flowtable", &flowtable)) + return NULL; + + if (strcmp(opstr, "add")) { + json_error(ctx, "Unknown flow offload statement op '%s'.", opstr); + return NULL; + } + + if (flowtable[0] != '@') { + json_error(ctx, "Illegal flowtable reference in flow offload statement."); + return NULL; + } + + return flow_offload_stmt_alloc(int_loc, xstrdup(flowtable + 1)); +} + +static struct stmt *json_parse_notrack_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + return notrack_stmt_alloc(int_loc); +} + +static struct stmt *json_parse_dup_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + struct expr *expr; + json_t *tmp; + + if (json_unpack_err(ctx, value, "{s:o}", "addr", &tmp)) + return NULL; + + expr = json_parse_stmt_expr(ctx, tmp); + if (!expr) { + json_error(ctx, "Illegal dup addr arg."); + return NULL; + } + + stmt = dup_stmt_alloc(int_loc); + stmt->dup.to = expr; + + if (json_unpack(value, "{s:o}", "dev", &tmp)) + return stmt; + + expr = json_parse_stmt_expr(ctx, tmp); + if (!expr) { + json_error(ctx, "Illegal dup dev."); + stmt_free(stmt); + return NULL; + } + stmt->dup.dev = expr; + return stmt; +} + +static struct stmt *json_parse_secmark_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt; + + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_SECMARK; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid secmark reference."); + stmt_free(stmt); + return NULL; + } + + return stmt; +} + +static int json_parse_nat_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "random", NF_NAT_RANGE_PROTO_RANDOM }, + { "fully-random", NF_NAT_RANGE_PROTO_RANDOM_FULLY }, + { "persistent", NF_NAT_RANGE_PERSISTENT }, + { "netmap", NF_NAT_RANGE_NETMAP }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid nat flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown nat flag '%s'.", flag); + return 1; +} + +static int json_parse_nat_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_nat_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid nat flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_nat_flag(ctx, value, &flags)) + json_error(ctx, "Parsing nat flag at index %zu failed.", + index); + } + return flags; +} + +static int json_parse_nat_type_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "interval", STMT_NAT_F_INTERVAL }, + { "prefix", STMT_NAT_F_PREFIX }, + { "concat", STMT_NAT_F_CONCAT }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid nat type flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown nat type flag '%s'.", flag); + return 1; +} + +static int json_parse_nat_type_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_nat_type_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid nat flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_nat_type_flag(ctx, value, &flags)) + json_error(ctx, "Parsing nat type flag at index %zu failed.", + index); + } + return flags; +} + +static int nat_type_parse(const char *type) +{ + const char * const nat_etypes[] = { + [NFT_NAT_SNAT] = "snat", + [NFT_NAT_DNAT] = "dnat", + [NFT_NAT_MASQ] = "masquerade", + [NFT_NAT_REDIR] = "redirect", + }; + size_t i; + + for (i = 0; i < array_size(nat_etypes); i++) { + if (!strcmp(type, nat_etypes[i])) + return i; + } + return -1; +} + +static struct stmt *json_parse_nat_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + int type, familyval; + struct stmt *stmt; + json_t *tmp; + + type = nat_type_parse(key); + if (type < 0) { + json_error(ctx, "Unknown nat type '%s'.", key); + return NULL; + } + + familyval = json_parse_family(ctx, value); + if (familyval < 0) + return NULL; + + stmt = nat_stmt_alloc(int_loc, type); + stmt->nat.family = familyval; + + if (!json_unpack(value, "{s:o}", "addr", &tmp)) { + stmt->nat.addr = json_parse_stmt_expr(ctx, tmp); + if (!stmt->nat.addr) { + json_error(ctx, "Invalid nat addr."); + stmt_free(stmt); + return NULL; + } + } + if (!json_unpack(value, "{s:o}", "port", &tmp)) { + stmt->nat.proto = json_parse_stmt_expr(ctx, tmp); + if (!stmt->nat.proto) { + json_error(ctx, "Invalid nat port."); + stmt_free(stmt); + return NULL; + } + } + if (!json_unpack(value, "{s:o}", "flags", &tmp)) { + int flags = json_parse_nat_flags(ctx, tmp); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->nat.flags = flags; + } + if (!json_unpack(value, "{s:o}", "type_flags", &tmp)) { + int flags = json_parse_nat_type_flags(ctx, tmp); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->nat.type_flags = flags; + } + + return stmt; +} + +static struct stmt *json_parse_tproxy_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + json_t *jaddr, *tmp; + struct stmt *stmt; + int familyval; + + stmt = tproxy_stmt_alloc(int_loc); + + familyval = json_parse_family(ctx, value); + if (familyval < 0) + goto out_free; + + stmt->tproxy.family = familyval; + + if (!json_unpack(value, "{s:o}", "addr", &jaddr)) { + stmt->tproxy.addr = json_parse_stmt_expr(ctx, jaddr); + if (!stmt->tproxy.addr) { + json_error(ctx, "Invalid addr."); + goto out_free; + } + } + if (!json_unpack(value, "{s:o}", "port", &tmp)) { + stmt->tproxy.port = json_parse_stmt_expr(ctx, tmp); + if (!stmt->tproxy.port) { + json_error(ctx, "Invalid port."); + goto out_free; + } + } + return stmt; + +out_free: + stmt_free(stmt); + return NULL; +} + +static struct stmt *json_parse_reject_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = reject_stmt_alloc(int_loc); + const struct datatype *dtype = NULL; + const char *type; + json_t *tmp; + + stmt->reject.type = -1; + stmt->reject.icmp_code = -1; + + if (!json_unpack(value, "{s:s}", "type", &type)) { + if (!strcmp(type, "tcp reset")) { + stmt->reject.type = NFT_REJECT_TCP_RST; + stmt->reject.icmp_code = 0; + } else if (!strcmp(type, "icmpx")) { + stmt->reject.type = NFT_REJECT_ICMPX_UNREACH; + dtype = &icmpx_code_type; + stmt->reject.icmp_code = 0; + } else if (!strcmp(type, "icmp")) { + stmt->reject.type = NFT_REJECT_ICMP_UNREACH; + stmt->reject.family = NFPROTO_IPV4; + dtype = &icmp_code_type; + stmt->reject.icmp_code = 0; + } else if (!strcmp(type, "icmpv6")) { + stmt->reject.type = NFT_REJECT_ICMP_UNREACH; + stmt->reject.family = NFPROTO_IPV6; + dtype = &icmpv6_code_type; + stmt->reject.icmp_code = 0; + } + } + if (!json_unpack(value, "{s:o}", "expr", &tmp)) { + stmt->reject.expr = json_parse_immediate(ctx, tmp); + if (!stmt->reject.expr) { + json_error(ctx, "Illegal reject expr."); + stmt_free(stmt); + return NULL; + } + datatype_set(stmt->reject.expr, dtype); + } + return stmt; +} + +static void json_parse_set_stmt_list(struct json_ctx *ctx, + struct list_head *stmt_list, + json_t *stmt_json) +{ + struct list_head *head; + struct stmt *tmp; + json_t *value; + size_t index; + + if (!stmt_json) + return; + + if (!json_is_array(stmt_json)) + json_error(ctx, "Unexpected object type in stmt"); + + head = stmt_list; + json_array_foreach(stmt_json, index, value) { + tmp = json_parse_stmt(ctx, value); + list_add(&tmp->list, head); + head = &tmp->list; + } +} + +static struct stmt *json_parse_set_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + const char *opstr, *set; + struct expr *expr, *expr2; + json_t *elem, *stmt_json; + struct stmt *stmt; + int op; + + if (json_unpack_err(ctx, value, "{s:s, s:o, s:s}", + "op", &opstr, "elem", &elem, "set", &set)) + return NULL; + + if (!strcmp(opstr, "add")) { + op = NFT_DYNSET_OP_ADD; + } else if (!strcmp(opstr, "update")) { + op = NFT_DYNSET_OP_UPDATE; + } else if (!strcmp(opstr, "delete")) { + op = NFT_DYNSET_OP_DELETE; + } else { + json_error(ctx, "Unknown set statement op '%s'.", opstr); + return NULL; + } + + expr = json_parse_set_elem_expr_stmt(ctx, elem); + if (!expr) { + json_error(ctx, "Illegal set statement element."); + return NULL; + } + + if (set[0] != '@') { + json_error(ctx, "Illegal set reference in set statement."); + expr_free(expr); + return NULL; + } + expr2 = symbol_expr_alloc(int_loc, SYMBOL_SET, NULL, set + 1); + + stmt = set_stmt_alloc(int_loc); + stmt->set.op = op; + stmt->set.key = expr; + stmt->set.set = expr2; + + if (!json_unpack(value, "{s:o}", "stmt", &stmt_json)) + json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json); + + return stmt; +} + +static struct stmt *json_parse_map_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *expr, *expr2, *expr_data; + json_t *elem, *data, *stmt_json; + const char *opstr, *set; + struct stmt *stmt; + int op; + + if (json_unpack_err(ctx, value, "{s:s, s:o, s:o, s:s}", + "op", &opstr, "elem", &elem, "data", &data, "map", &set)) + return NULL; + + if (!strcmp(opstr, "add")) { + op = NFT_DYNSET_OP_ADD; + } else if (!strcmp(opstr, "update")) { + op = NFT_DYNSET_OP_UPDATE; + } else if (!strcmp(opstr, "delete")) { + op = NFT_DYNSET_OP_DELETE; + } else { + json_error(ctx, "Unknown map statement op '%s'.", opstr); + return NULL; + } + + expr = json_parse_set_elem_expr_stmt(ctx, elem); + if (!expr) { + json_error(ctx, "Illegal map statement element."); + return NULL; + } + + expr_data = json_parse_set_elem_expr_stmt(ctx, data); + if (!expr_data) { + json_error(ctx, "Illegal map expression data."); + expr_free(expr); + return NULL; + } + + if (set[0] != '@') { + json_error(ctx, "Illegal map reference in map statement."); + expr_free(expr); + expr_free(expr_data); + return NULL; + } + expr2 = symbol_expr_alloc(int_loc, SYMBOL_SET, NULL, set + 1); + + stmt = map_stmt_alloc(int_loc); + stmt->map.op = op; + stmt->map.key = expr; + stmt->map.data = expr_data; + stmt->map.set = expr2; + + if (!json_unpack(value, "{s:o}", "stmt", &stmt_json)) + json_parse_set_stmt_list(ctx, &stmt->set.stmt_list, stmt_json); + + return stmt; +} + +static int json_parse_log_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "tcp sequence", NF_LOG_TCPSEQ }, + { "tcp options", NF_LOG_TCPOPT }, + { "ip options", NF_LOG_IPOPT }, + { "skuid", NF_LOG_UID }, + { "ether", NF_LOG_MACDECODE }, + { "all", NF_LOG_MASK }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid log flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown log flag '%s'.", flag); + return 1; +} + +static int json_parse_log_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_log_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid log flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_log_flag(ctx, value, &flags)) + json_error(ctx, "Parsing log flag at index %zu failed.", + index); + } + return flags; +} + +static struct stmt *json_parse_log_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + const char *tmpstr; + struct stmt *stmt; + json_t *jflags; + int tmp; + + stmt = log_stmt_alloc(int_loc); + + if (!json_unpack(value, "{s:s}", "prefix", &tmpstr)) { + stmt->log.prefix = constant_expr_alloc(int_loc, &string_type, + BYTEORDER_HOST_ENDIAN, + (strlen(tmpstr) + 1) * BITS_PER_BYTE, tmpstr); + stmt->log.flags |= STMT_LOG_PREFIX; + } + if (!json_unpack(value, "{s:i}", "group", &tmp)) { + stmt->log.group = tmp; + stmt->log.flags |= STMT_LOG_GROUP; + } + if (!json_unpack(value, "{s:i}", "snaplen", &tmp)) { + stmt->log.snaplen = tmp; + stmt->log.flags |= STMT_LOG_SNAPLEN; + } + if (!json_unpack(value, "{s:i}", "queue-threshold", &tmp)) { + stmt->log.qthreshold = tmp; + stmt->log.flags |= STMT_LOG_QTHRESHOLD; + } + if (!json_unpack(value, "{s:s}", "level", &tmpstr)) { + int level = log_level_parse(tmpstr); + + if (level < 0) { + json_error(ctx, "Invalid log level '%s'.", tmpstr); + stmt_free(stmt); + return NULL; + } + stmt->log.level = level; + stmt->log.flags |= STMT_LOG_LEVEL; + } + if (!json_unpack(value, "{s:o}", "flags", &jflags)) { + int flags = json_parse_log_flags(ctx, jflags); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->log.logflags = flags; + } + return stmt; +} + +static int json_parse_synproxy_flag(struct json_ctx *ctx, + json_t *root, int *flags) +{ + const struct { + const char *flag; + int val; + } flag_tbl[] = { + { "timestamp", NF_SYNPROXY_OPT_TIMESTAMP }, + { "sack-perm", NF_SYNPROXY_OPT_SACK_PERM }, + }; + const char *flag; + unsigned int i; + + assert(flags); + + if (!json_is_string(root)) { + json_error(ctx, "Invalid synproxy flag type %s, expected string.", + json_typename(root)); + return 1; + } + flag = json_string_value(root); + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(flag, flag_tbl[i].flag)) { + *flags |= flag_tbl[i].val; + return 0; + } + } + json_error(ctx, "Unknown synproxy flag '%s'.", flag); + return 1; +} + +static int json_parse_synproxy_flags(struct json_ctx *ctx, json_t *root) +{ + int flags = 0; + json_t *value; + size_t index; + + if (json_is_string(root)) { + json_parse_synproxy_flag(ctx, root, &flags); + return flags; + } else if (!json_is_array(root)) { + json_error(ctx, "Invalid synproxy flags type %s.", + json_typename(root)); + return -1; + } + json_array_foreach(root, index, value) { + if (json_parse_synproxy_flag(ctx, value, &flags)) + json_error(ctx, "Parsing synproxy flag at index %zu failed.", + index); + } + return flags; +} + +static struct stmt *json_parse_synproxy_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = NULL; + json_t *jflags; + int tmp, flags; + + if (json_typeof(value) == JSON_NULL) { + stmt = synproxy_stmt_alloc(int_loc); + return stmt; + } + + if (!json_unpack(value, "{s:i}", "mss", &tmp)) { + if (!stmt) + stmt = synproxy_stmt_alloc(int_loc); + if (tmp < 0) { + json_error(ctx, "Invalid synproxy mss value '%d'", tmp); + stmt_free(stmt); + return NULL; + } + stmt->synproxy.mss = tmp; + stmt->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + } + if (!json_unpack(value, "{s:i}", "wscale", &tmp)) { + if (!stmt) + stmt = synproxy_stmt_alloc(int_loc); + if (tmp < 0) { + json_error(ctx, "Invalid synproxy wscale value '%d'", tmp); + stmt_free(stmt); + return NULL; + } + stmt->synproxy.wscale = tmp; + stmt->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + } + if (!json_unpack(value, "{s:o}", "flags", &jflags)) { + if (!stmt) + stmt = synproxy_stmt_alloc(int_loc); + flags = json_parse_synproxy_flags(ctx, jflags); + + if (flags < 0) { + stmt_free(stmt); + return NULL; + } + stmt->synproxy.flags |= flags; + } + + if (!stmt) { + stmt = objref_stmt_alloc(int_loc); + stmt->objref.type = NFT_OBJECT_SYNPROXY; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid synproxy reference"); + stmt_free(stmt); + return NULL; + } + } + return stmt; +} + +static struct stmt *json_parse_cthelper_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = objref_stmt_alloc(int_loc); + + stmt->objref.type = NFT_OBJECT_CT_HELPER; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid ct helper reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static struct stmt *json_parse_cttimeout_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = objref_stmt_alloc(int_loc); + + stmt->objref.type = NFT_OBJECT_CT_TIMEOUT; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid ct timeout reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static struct stmt *json_parse_ctexpect_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = objref_stmt_alloc(int_loc); + + stmt->objref.type = NFT_OBJECT_CT_EXPECT; + stmt->objref.expr = json_parse_stmt_expr(ctx, value); + if (!stmt->objref.expr) { + json_error(ctx, "Invalid ct expectation reference."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static struct stmt *json_parse_meter_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + json_t *jkey, *jstmt; + struct stmt *stmt; + const char *name; + uint32_t size = 0; + + if (json_unpack_err(ctx, value, "{s:s, s:o, s:o}", + "name", &name, "key", &jkey, "stmt", &jstmt)) + return NULL; + json_unpack(value, "{s:i}", "size", &size); + + stmt = meter_stmt_alloc(int_loc); + stmt->meter.name = xstrdup(name); + stmt->meter.size = size; + + stmt->meter.key = json_parse_set_elem_expr_stmt(ctx, jkey); + if (!stmt->meter.key) { + json_error(ctx, "Invalid meter key."); + stmt_free(stmt); + return NULL; + } + + stmt->meter.stmt = json_parse_stmt(ctx, jstmt); + if (!stmt->meter.stmt) { + json_error(ctx, "Invalid meter statement."); + stmt_free(stmt); + return NULL; + } + return stmt; +} + +static int queue_flag_parse(const char *name, uint16_t *flags) +{ + if (!strcmp(name, "bypass")) + *flags |= NFT_QUEUE_FLAG_BYPASS; + else if (!strcmp(name, "fanout")) + *flags |= NFT_QUEUE_FLAG_CPU_FANOUT; + else + return 1; + return 0; +} + +static struct stmt *json_parse_queue_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *qexpr = NULL; + uint16_t flags = 0; + json_t *tmp; + + if (!json_unpack(value, "{s:o}", "num", &tmp)) { + qexpr = json_parse_stmt_expr(ctx, tmp); + if (!qexpr) { + json_error(ctx, "Invalid queue num."); + return NULL; + } + } + if (!json_unpack(value, "{s:o}", "flags", &tmp)) { + const char *flag; + size_t index; + json_t *val; + + if (json_is_string(tmp)) { + flag = json_string_value(tmp); + + if (queue_flag_parse(flag, &flags)) { + json_error(ctx, "Invalid queue flag '%s'.", + flag); + expr_free(qexpr); + return NULL; + } + } else if (!json_is_array(tmp)) { + json_error(ctx, "Unexpected object type in queue flags."); + expr_free(qexpr); + return NULL; + } + + json_array_foreach(tmp, index, val) { + if (!json_is_string(val)) { + json_error(ctx, "Invalid object in queue flag array at index %zu.", + index); + expr_free(qexpr); + return NULL; + } + flag = json_string_value(val); + + if (queue_flag_parse(flag, &flags)) { + json_error(ctx, "Invalid queue flag '%s'.", + flag); + expr_free(qexpr); + return NULL; + } + } + } + return queue_stmt_alloc(int_loc, qexpr, flags); +} + +static struct stmt *json_parse_connlimit_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct stmt *stmt = connlimit_stmt_alloc(int_loc); + + if (json_unpack_err(ctx, value, "{s:i}", + "val", &stmt->connlimit.count)) { + stmt_free(stmt); + return NULL; + } + + json_unpack(value, "{s:b}", "inv", &stmt->connlimit.flags); + if (stmt->connlimit.flags) + stmt->connlimit.flags = NFT_CONNLIMIT_F_INV; + + return stmt; +} + +static struct stmt *json_parse_optstrip_stmt(struct json_ctx *ctx, + const char *key, json_t *value) +{ + struct expr *expr = json_parse_expr(ctx, value); + + if (!expr || + expr->etype != EXPR_EXTHDR || + expr->exthdr.op != NFT_EXTHDR_OP_TCPOPT) { + json_error(ctx, "Illegal TCP optstrip argument"); + return NULL; + } + + return optstrip_stmt_alloc(int_loc, expr); +} + +static struct stmt *json_parse_stmt(struct json_ctx *ctx, json_t *root) +{ + struct { + const char *key; + struct stmt *(*cb)(struct json_ctx *, const char *, json_t *); + } stmt_parser_tbl[] = { + { "accept", json_parse_verdict_stmt }, + { "drop", json_parse_verdict_stmt }, + { "continue", json_parse_verdict_stmt }, + { "jump", json_parse_verdict_stmt }, + { "goto", json_parse_verdict_stmt }, + { "return", json_parse_verdict_stmt }, + { "match", json_parse_match_stmt }, + { "counter", json_parse_counter_stmt }, + { "mangle", json_parse_mangle_stmt }, + { "quota", json_parse_quota_stmt }, + { "last", json_parse_last_stmt }, + { "limit", json_parse_limit_stmt }, + { "flow", json_parse_flow_offload_stmt }, + { "fwd", json_parse_fwd_stmt }, + { "notrack", json_parse_notrack_stmt }, + { "dup", json_parse_dup_stmt }, + { "snat", json_parse_nat_stmt }, + { "dnat", json_parse_nat_stmt }, + { "masquerade", json_parse_nat_stmt }, + { "redirect", json_parse_nat_stmt }, + { "reject", json_parse_reject_stmt }, + { "set", json_parse_set_stmt }, + { "map", json_parse_map_stmt }, + { "log", json_parse_log_stmt }, + { "ct helper", json_parse_cthelper_stmt }, + { "ct timeout", json_parse_cttimeout_stmt }, + { "ct expectation", json_parse_ctexpect_stmt }, + { "meter", json_parse_meter_stmt }, + { "queue", json_parse_queue_stmt }, + { "ct count", json_parse_connlimit_stmt }, + { "tproxy", json_parse_tproxy_stmt }, + { "synproxy", json_parse_synproxy_stmt }, + { "reset", json_parse_optstrip_stmt }, + { "secmark", json_parse_secmark_stmt }, + }; + const char *type; + unsigned int i; + json_t *tmp; + + if (json_unpack_stmt(ctx, root, &type, &tmp)) + return NULL; + + /* Yes, verdict_map_stmt is actually an expression */ + if (!strcmp(type, "vmap")) { + struct expr *expr = json_parse_map_expr(ctx, type, tmp); + + if (!expr) { + json_error(ctx, "Illegal vmap statement."); + return NULL; + } + return verdict_stmt_alloc(int_loc, expr); + } + + if (!strcmp(type, "xt")) { + json_error(ctx, "unsupported xtables compat expression, use iptables-nft with this ruleset"); + return NULL; + } + + for (i = 0; i < array_size(stmt_parser_tbl); i++) { + if (!strcmp(type, stmt_parser_tbl[i].key)) + return stmt_parser_tbl[i].cb(ctx, stmt_parser_tbl[i].key, tmp); + } + + json_error(ctx, "Unknown statement object '%s'.", type); + return NULL; +} + +static struct cmd *json_parse_cmd_add_table(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) +{ + const char *family = "", *comment = NULL; + struct handle h = { + .table.location = *int_loc, + }; + struct table *table = NULL; + + if (json_unpack_err(ctx, root, "{s:s}", + "family", &family)) + return NULL; + + if (op != CMD_DELETE) { + if (json_unpack_err(ctx, root, "{s:s}", "name", &h.table.name)) + return NULL; + + json_unpack(root, "{s:s}", "comment", &comment); + } else if (op == CMD_DELETE && + json_unpack(root, "{s:s}", "name", &h.table.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { + json_error(ctx, "Either name or handle required to delete a table."); + return NULL; + } + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + if (h.table.name) + h.table.name = xstrdup(h.table.name); + + if (comment) { + table = table_alloc(); + handle_merge(&table->handle, &h); + table->comment = xstrdup(comment); + } + + if (op == CMD_ADD) + json_object_del(root, "handle"); + + return cmd_alloc(op, obj, &h, int_loc, table); +} + +static struct expr *parse_policy(const char *policy) +{ + int policy_num; + + if (!strcmp(policy, "accept")) + policy_num = NF_ACCEPT; + else if (!strcmp(policy, "drop")) + policy_num = NF_DROP; + else + return NULL; + + return constant_expr_alloc(int_loc, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, &policy_num); +} + +static struct cmd *json_parse_cmd_add_chain(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) +{ + struct handle h = { + .table.location = *int_loc, + }; + const char *family = "", *policy = "", *type, *hookstr, *name, *comment = NULL; + struct chain *chain = NULL; + int prio; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, + "table", &h.table.name)) + return NULL; + if (op != CMD_DELETE) { + if (json_unpack_err(ctx, root, "{s:s}", "name", &h.chain.name)) + return NULL; + + json_unpack(root, "{s:s}", "comment", &comment); + } else if (op == CMD_DELETE && + json_unpack(root, "{s:s}", "name", &h.chain.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { + json_error(ctx, "Either name or handle required to delete a chain."); + return NULL; + } + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + if (h.chain.name) + h.chain.name = xstrdup(h.chain.name); + + if (comment) { + chain = chain_alloc(); + handle_merge(&chain->handle, &h); + chain->comment = xstrdup(comment); + } + + if (op == CMD_DELETE || + op == CMD_LIST || + op == CMD_FLUSH || + json_unpack(root, "{s:s, s:s, s:i}", + "type", &type, "hook", &hookstr, "prio", &prio)) + return cmd_alloc(op, obj, &h, int_loc, chain); + + if (!chain) + chain = chain_alloc(); + + chain->flags |= CHAIN_F_BASECHAIN; + chain->type.str = xstrdup(type); + chain->priority.expr = constant_expr_alloc(int_loc, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, + &prio); + chain->hook.name = chain_hookname_lookup(hookstr); + if (!chain->hook.name) { + json_error(ctx, "Invalid chain hook '%s'.", hookstr); + chain_free(chain); + return NULL; + } + + if (!json_unpack(root, "{s:s}", "dev", &name)) { + struct expr *dev_expr, *expr; + + dev_expr = compound_expr_alloc(int_loc, EXPR_LIST); + expr = constant_expr_alloc(int_loc, &integer_type, + BYTEORDER_HOST_ENDIAN, + strlen(name) * BITS_PER_BYTE, + name); + compound_expr_add(dev_expr, expr); + chain->dev_expr = dev_expr; + } + + if (!json_unpack(root, "{s:s}", "policy", &policy)) { + chain->policy = parse_policy(policy); + if (!chain->policy) { + json_error(ctx, "Unknown policy '%s'.", policy); + chain_free(chain); + return NULL; + } + } + + if (op == CMD_ADD) + json_object_del(root, "handle"); + + handle_merge(&chain->handle, &h); + return cmd_alloc(op, obj, &h, int_loc, chain); +} + +static struct cmd *json_parse_cmd_add_rule(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) +{ + struct handle h = { + .table.location = *int_loc, + .chain.location = *int_loc, + .index.location = *int_loc, + }; + const char *family = "", *comment = NULL; + struct rule *rule; + size_t index; + json_t *tmp, *value; + + if (json_unpack_err(ctx, root, "{s:s, s:s, s:s}", + "family", &family, + "table", &h.table.name, + "chain", &h.chain.name)) + return NULL; + if (op != CMD_DELETE && + json_unpack_err(ctx, root, "{s:o}", "expr", &tmp)) + return NULL; + else if ((op == CMD_DELETE || op == CMD_DESTROY) && + json_unpack_err(ctx, root, "{s:I}", "handle", &h.handle.id)) + return NULL; + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + h.chain.name = xstrdup(h.chain.name); + + if (op == CMD_DELETE || op == CMD_DESTROY) + return cmd_alloc(op, obj, &h, int_loc, NULL); + + if (!json_is_array(tmp)) { + json_error(ctx, "Value of property \"expr\" must be an array."); + return NULL; + } + + if (!json_unpack(root, "{s:I}", "index", &h.index.id)) { + h.index.id++; + } + + rule = rule_alloc(int_loc, NULL); + + json_unpack(root, "{s:s}", "comment", &comment); + if (comment) + rule->comment = xstrdup(comment); + + json_array_foreach(tmp, index, value) { + struct stmt *stmt; + + if (!json_is_object(value)) { + json_error(ctx, "Unexpected expr array element of type %s, expected object.", + json_typename(value)); + rule_free(rule); + return NULL; + } + + stmt = json_parse_stmt(ctx, value); + + if (!stmt) { + json_error(ctx, "Parsing expr array at index %zd failed.", index); + rule_free(rule); + return NULL; + } + + rule_stmt_append(rule, stmt); + } + + if (op == CMD_ADD) + json_object_del(root, "handle"); + + return cmd_alloc(op, obj, &h, int_loc, rule); +} + +static int string_to_nft_object(const char *str) +{ + const char *obj_tbl[__NFT_OBJECT_MAX] = { + [NFT_OBJECT_COUNTER] = "counter", + [NFT_OBJECT_QUOTA] = "quota", + [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_SECMARK] = "secmark", + }; + unsigned int i; + + for (i = 0; i < NFT_OBJECT_MAX; i++) { + if (obj_tbl[i] && !strcmp(str, obj_tbl[i])) + return i; + } + return 0; +} + +static int string_to_set_flag(const char *str) +{ + const struct { + enum nft_set_flags val; + const char *name; + } flag_tbl[] = { + { NFT_SET_CONSTANT, "constant" }, + { NFT_SET_INTERVAL, "interval" }, + { NFT_SET_TIMEOUT, "timeout" }, + { NFT_SET_EVAL, "dynamic" }, + }; + unsigned int i; + + for (i = 0; i < array_size(flag_tbl); i++) { + if (!strcmp(str, flag_tbl[i].name)) + return flag_tbl[i].val; + } + return 0; +} + +static struct cmd *json_parse_cmd_add_set(struct json_ctx *ctx, json_t *root, + enum cmd_ops op, enum cmd_obj obj) +{ + struct handle h = { 0 }; + const char *family = "", *policy, *dtype_ext = NULL; + json_t *tmp, *stmt_json; + struct set *set; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, + "table", &h.table.name)) + return NULL; + if (op != CMD_DELETE && + json_unpack_err(ctx, root, "{s:s}", "name", &h.set.name)) { + return NULL; + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && + json_unpack(root, "{s:s}", "name", &h.set.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { + json_error(ctx, "Either name or handle required to delete a set."); + return NULL; + } + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + if (h.set.name) + h.set.name = xstrdup(h.set.name); + + switch (op) { + case CMD_DELETE: + case CMD_DESTROY: + case CMD_LIST: + case CMD_FLUSH: + case CMD_RESET: + return cmd_alloc(op, obj, &h, int_loc, NULL); + default: + break; + } + + set = set_alloc(&internal_location); + + if (json_unpack(root, "{s:o}", "type", &tmp)) { + json_error(ctx, "Invalid set type."); + set_free(set); + handle_free(&h); + return NULL; + } + set->key = json_parse_dtype_expr(ctx, tmp); + if (!set->key) { + json_error(ctx, "Invalid set type."); + set_free(set); + handle_free(&h); + return NULL; + } + + if (!json_unpack(root, "{s:s}", "map", &dtype_ext)) { + const struct datatype *dtype; + + set->objtype = string_to_nft_object(dtype_ext); + if (set->objtype) { + set->flags |= NFT_SET_OBJECT; + } else if ((dtype = datatype_lookup_byname(dtype_ext))) { + set->data = constant_expr_alloc(&netlink_location, + dtype, dtype->byteorder, + dtype->size, NULL); + set->flags |= NFT_SET_MAP; + } else { + json_error(ctx, "Invalid map type '%s'.", dtype_ext); + set_free(set); + handle_free(&h); + return NULL; + } + } + if (!json_unpack(root, "{s:s}", "policy", &policy)) { + if (!strcmp(policy, "performance")) + set->policy = NFT_SET_POL_PERFORMANCE; + else if (!strcmp(policy, "memory")) { + set->policy = NFT_SET_POL_MEMORY; + } else { + json_error(ctx, "Unknown set policy '%s'.", policy); + set_free(set); + handle_free(&h); + return NULL; + } + } + if (!json_unpack(root, "{s:o}", "flags", &tmp)) { + json_t *value; + size_t index; + + json_array_foreach(tmp, index, value) { + int flag; + + if (!json_is_string(value) || + !(flag = string_to_set_flag(json_string_value(value)))) { + json_error(ctx, "Invalid set flag at index %zu.", index); + set_free(set); + handle_free(&h); + return NULL; + } + set->flags |= flag; + } + } + if (!json_unpack(root, "{s:o}", "elem", &tmp)) { + set->init = json_parse_set_expr(ctx, "elem", tmp); + if (!set->init) { + json_error(ctx, "Invalid set elem expression."); + set_free(set); + handle_free(&h); + return NULL; + } + } + if (!json_unpack(root, "{s:I}", "timeout", &set->timeout)) + set->timeout *= 1000; + if (!json_unpack(root, "{s:i}", "gc-interval", &set->gc_int)) + set->gc_int *= 1000; + json_unpack(root, "{s:i}", "size", &set->desc.size); + + if (!json_unpack(root, "{s:o}", "stmt", &stmt_json)) + json_parse_set_stmt_list(ctx, &set->stmt_list, stmt_json); + + handle_merge(&set->handle, &h); + + if (op == CMD_ADD) + json_object_del(root, "handle"); + + return cmd_alloc(op, obj, &h, int_loc, set); +} + +static struct cmd *json_parse_cmd_add_element(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj cmd_obj) +{ + struct handle h = { 0 }; + const char *family; + struct expr *expr; + json_t *tmp; + + if (json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:o}", + "family", &family, + "table", &h.table.name, + "name", &h.set.name, + "elem", &tmp)) + return NULL; + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + h.set.name = xstrdup(h.set.name); + + expr = json_parse_set_expr(ctx, "elem", tmp); + if (!expr) { + json_error(ctx, "Invalid set."); + handle_free(&h); + return NULL; + } + return cmd_alloc(op, cmd_obj, &h, int_loc, expr); +} + +static struct expr *json_parse_flowtable_devs(struct json_ctx *ctx, + json_t *root) +{ + struct expr *tmp, *expr = compound_expr_alloc(int_loc, EXPR_LIST); + const char *dev; + json_t *value; + size_t index; + + if (!json_unpack(root, "s", &dev)) { + tmp = constant_expr_alloc(int_loc, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); + return expr; + } + if (!json_is_array(root)) { + expr_free(expr); + return NULL; + } + + json_array_foreach(root, index, value) { + if (json_unpack(value, "s", &dev)) { + json_error(ctx, "Invalid flowtable dev at index %zu.", + index); + expr_free(expr); + return NULL; + } + tmp = constant_expr_alloc(int_loc, &string_type, + BYTEORDER_HOST_ENDIAN, + strlen(dev) * BITS_PER_BYTE, dev); + compound_expr_add(expr, tmp); + } + return expr; +} + +static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj cmd_obj) +{ + const char *family, *hook, *hookstr; + struct flowtable *flowtable; + struct handle h = { 0 }; + json_t *devs = NULL; + int prio; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, + "table", &h.table.name)) + return NULL; + + if (op != CMD_DELETE && + json_unpack_err(ctx, root, "{s:s}", "name", &h.flowtable.name)) { + return NULL; + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && + json_unpack(root, "{s:s}", "name", &h.flowtable.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { + json_error(ctx, "Either name or handle required to delete a flowtable."); + return NULL; + } + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + if (h.flowtable.name) + h.flowtable.name = xstrdup(h.flowtable.name); + + if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) + return cmd_alloc(op, cmd_obj, &h, int_loc, NULL); + + if (json_unpack_err(ctx, root, "{s:s, s:i}", + "hook", &hook, + "prio", &prio)) { + handle_free(&h); + return NULL; + } + + json_unpack(root, "{s:o}", "dev", &devs); + + hookstr = chain_hookname_lookup(hook); + if (!hookstr) { + json_error(ctx, "Invalid flowtable hook '%s'.", hook); + handle_free(&h); + return NULL; + } + + flowtable = flowtable_alloc(int_loc); + flowtable->hook.name = hookstr; + flowtable->priority.expr = + constant_expr_alloc(int_loc, &integer_type, + BYTEORDER_HOST_ENDIAN, + sizeof(int) * BITS_PER_BYTE, &prio); + + if (devs) { + flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs); + if (!flowtable->dev_expr) { + json_error(ctx, "Invalid flowtable dev."); + flowtable_free(flowtable); + handle_free(&h); + return NULL; + } + } + return cmd_alloc(op, cmd_obj, &h, int_loc, flowtable); +} + +static int json_parse_ct_timeout_policy(struct json_ctx *ctx, + json_t *root, struct obj *obj) +{ + json_t *tmp, *val; + const char *key; + + if (json_unpack(root, "{s:o}", "policy", &tmp)) + return 0; + + if (!json_is_object(tmp)) { + json_error(ctx, "Invalid ct timeout policy."); + return 1; + } + + json_object_foreach(tmp, key, val) { + struct timeout_state *ts; + + if (!json_is_integer(val)) { + json_error(ctx, "Invalid ct timeout policy value for '%s'.", key); + return 1; + } + + ts = xzalloc(sizeof(*ts)); + ts->timeout_str = xstrdup(key); + ts->timeout_value = json_integer_value(val); + ts->location = *int_loc; + init_list_head(&ts->head); + list_add_tail(&ts->head, &obj->ct_timeout.timeout_list); + } + return 0; +} + +static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj cmd_obj) +{ + const char *family, *tmp, *rate_unit = "packets", *burst_unit = "bytes"; + uint32_t l3proto = NFPROTO_UNSPEC; + int inv = 0, flags = 0, i, j; + struct handle h = { 0 }; + struct obj *obj; + json_t *jflags; + + if (json_unpack_err(ctx, root, "{s:s, s:s}", + "family", &family, + "table", &h.table.name)) + return NULL; + if ((op != CMD_DELETE || + cmd_obj == NFT_OBJECT_CT_HELPER) && + json_unpack_err(ctx, root, "{s:s}", "name", &h.obj.name)) { + return NULL; + } else if ((op == CMD_DELETE || op == CMD_DESTROY) && + cmd_obj != NFT_OBJECT_CT_HELPER && + json_unpack(root, "{s:s}", "name", &h.obj.name) && + json_unpack(root, "{s:I}", "handle", &h.handle.id)) { + json_error(ctx, "Either name or handle required to delete an object."); + return NULL; + } + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + if (h.obj.name) + h.obj.name = xstrdup(h.obj.name); + + if (op == CMD_DELETE || op == CMD_LIST || op == CMD_DESTROY) { + if (cmd_obj == NFT_OBJECT_CT_HELPER) + return cmd_alloc_obj_ct(op, NFT_OBJECT_CT_HELPER, + &h, int_loc, obj_alloc(int_loc)); + return cmd_alloc(op, cmd_obj, &h, int_loc, NULL); + } + + obj = obj_alloc(int_loc); + + if (!json_unpack(root, "{s:s}", "comment", &obj->comment)) + obj->comment = xstrdup(obj->comment); + + switch (cmd_obj) { + case CMD_OBJ_COUNTER: + obj->type = NFT_OBJECT_COUNTER; + json_unpack(root, "{s:I}", "packets", &obj->counter.packets); + json_unpack(root, "{s:I}", "bytes", &obj->counter.bytes); + break; + case CMD_OBJ_QUOTA: + obj->type = NFT_OBJECT_QUOTA; + json_unpack(root, "{s:I}", "bytes", &obj->quota.bytes); + json_unpack(root, "{s:I}", "used", &obj->quota.used); + json_unpack(root, "{s:b}", "inv", &obj->quota.flags); + if (obj->quota.flags) + obj->quota.flags = NFT_QUOTA_F_INV; + break; + case CMD_OBJ_SECMARK: + obj->type = NFT_OBJECT_SECMARK; + if (!json_unpack(root, "{s:s}", "context", &tmp)) { + int ret; + ret = snprintf(obj->secmark.ctx, sizeof(obj->secmark.ctx), "%s", tmp); + if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) { + json_error(ctx, "Invalid secmark context '%s', max length is %zu.", + tmp, sizeof(obj->secmark.ctx)); + obj_free(obj); + return NULL; + } + } + break; + case NFT_OBJECT_CT_HELPER: + cmd_obj = CMD_OBJ_CT_HELPER; + obj->type = NFT_OBJECT_CT_HELPER; + if (!json_unpack(root, "{s:s}", "type", &tmp)) { + int ret; + + ret = snprintf(obj->ct_helper.name, + sizeof(obj->ct_helper.name), "%s", tmp); + if (ret < 0 || + ret >= (int)sizeof(obj->ct_helper.name)) { + json_error(ctx, "Invalid CT helper type '%s', max length is %zu.", + tmp, sizeof(obj->ct_helper.name)); + obj_free(obj); + return NULL; + } + } + if (!json_unpack(root, "{s:s}", "protocol", &tmp)) { + if (!strcmp(tmp, "tcp")) { + obj->ct_helper.l4proto = IPPROTO_TCP; + } else if (!strcmp(tmp, "udp")) { + obj->ct_helper.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct helper protocol '%s'.", tmp); + obj_free(obj); + return NULL; + } + } + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct helper l3proto '%s'.", tmp); + obj_free(obj); + return NULL; + } + obj->ct_helper.l3proto = l3proto; + break; + case NFT_OBJECT_CT_TIMEOUT: + cmd_obj = CMD_OBJ_CT_TIMEOUT; + obj->type = NFT_OBJECT_CT_TIMEOUT; + if (!json_unpack(root, "{s:s}", "protocol", &tmp)) { + if (!strcmp(tmp, "tcp")) { + obj->ct_timeout.l4proto = IPPROTO_TCP; + } else if (!strcmp(tmp, "udp")) { + obj->ct_timeout.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct timeout protocol '%s'.", tmp); + obj_free(obj); + return NULL; + } + } + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct timeout l3proto '%s'.", tmp); + obj_free(obj); + return NULL; + } + obj->ct_timeout.l3proto = l3proto; + + init_list_head(&obj->ct_timeout.timeout_list); + if (json_parse_ct_timeout_policy(ctx, root, obj)) { + obj_free(obj); + return NULL; + } + break; + case NFT_OBJECT_CT_EXPECT: + cmd_obj = CMD_OBJ_CT_EXPECT; + obj->type = NFT_OBJECT_CT_EXPECT; + if (!json_unpack(root, "{s:s}", "l3proto", &tmp) && + parse_family(tmp, &l3proto)) { + json_error(ctx, "Invalid ct expectation l3proto '%s'.", tmp); + obj_free(obj); + return NULL; + } + obj->ct_expect.l3proto = l3proto; + if (!json_unpack(root, "{s:s}", "protocol", &tmp)) { + if (!strcmp(tmp, "tcp")) { + obj->ct_expect.l4proto = IPPROTO_TCP; + } else if (!strcmp(tmp, "udp")) { + obj->ct_expect.l4proto = IPPROTO_UDP; + } else { + json_error(ctx, "Invalid ct expectation protocol '%s'.", tmp); + obj_free(obj); + return NULL; + } + } + if (!json_unpack(root, "{s:i}", "dport", &i)) + obj->ct_expect.dport = i; + if (!json_unpack(root, "{s:i}", "timeout", &i)) + obj->ct_expect.timeout = i; + if (!json_unpack(root, "{s:i}", "size", &i)) + obj->ct_expect.size = i; + break; + case CMD_OBJ_LIMIT: + obj->type = NFT_OBJECT_LIMIT; + if (json_unpack_err(ctx, root, "{s:I, s:s}", + "rate", &obj->limit.rate, + "per", &tmp)) { + obj_free(obj); + return NULL; + } + json_unpack(root, "{s:s}", "rate_unit", &rate_unit); + json_unpack(root, "{s:b}", "inv", &inv); + json_unpack(root, "{s:i}", "burst", &obj->limit.burst); + json_unpack(root, "{s:s}", "burst_unit", &burst_unit); + + if (!strcmp(rate_unit, "packets")) { + obj->limit.type = NFT_LIMIT_PKTS; + } else { + obj->limit.type = NFT_LIMIT_PKT_BYTES; + obj->limit.rate = rate_to_bytes(obj->limit.rate, + rate_unit); + obj->limit.burst = rate_to_bytes(obj->limit.burst, + burst_unit); + } + obj->limit.unit = seconds_from_unit(tmp); + obj->limit.flags = inv ? NFT_LIMIT_F_INV : 0; + break; + case CMD_OBJ_SYNPROXY: + obj->type = NFT_OBJECT_SYNPROXY; + if (json_unpack_err(ctx, root, "{s:i, s:i}", + "mss", &i, "wscale", &j)) { + obj_free(obj); + return NULL; + } + obj->synproxy.mss = i; + obj->synproxy.wscale = j; + obj->synproxy.flags |= NF_SYNPROXY_OPT_MSS; + obj->synproxy.flags |= NF_SYNPROXY_OPT_WSCALE; + if (!json_unpack(root, "{s:o}", "flags", &jflags)) { + flags = json_parse_synproxy_flags(ctx, jflags); + if (flags < 0) { + obj_free(obj); + return NULL; + } + obj->synproxy.flags |= flags; + } + break; + default: + BUG("Invalid CMD '%d'", cmd_obj); + } + + if (op == CMD_ADD) + json_object_del(root, "handle"); + + return cmd_alloc(op, cmd_obj, &h, int_loc, obj); +} + +static struct cmd *json_parse_cmd_add(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + struct { + const char *key; + enum cmd_obj obj; + struct cmd *(*cb)(struct json_ctx *, json_t *, + enum cmd_ops, enum cmd_obj); + } cmd_obj_table[] = { + { "table", CMD_OBJ_TABLE, json_parse_cmd_add_table }, + { "chain", CMD_OBJ_CHAIN, json_parse_cmd_add_chain }, + { "rule", CMD_OBJ_RULE, json_parse_cmd_add_rule }, + { "set", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "map", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element }, + { "flowtable", CMD_OBJ_FLOWTABLE, json_parse_cmd_add_flowtable }, + { "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object }, + { "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object }, + { "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object }, + { "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object }, + { "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object }, + { "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object }, + { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object } + }; + unsigned int i; + json_t *tmp; + + if (!json_is_object(root)) { + json_error(ctx, "Value of add command must be object (got %s instead).", + json_typename(root)); + return NULL; + } + + for (i = 0; i < array_size(cmd_obj_table); i++) { + tmp = json_object_get(root, cmd_obj_table[i].key); + if (!tmp) + continue; + + if (op == CMD_CREATE && cmd_obj_table[i].obj == CMD_OBJ_RULE) { + json_error(ctx, "Create command not available for rules."); + return NULL; + } + + return cmd_obj_table[i].cb(ctx, tmp, op, cmd_obj_table[i].obj); + } + json_error(ctx, "Unknown object passed to add command."); + return NULL; +} + +static struct cmd *json_parse_cmd_replace(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + struct handle h = { + .table.location = *int_loc, + .chain.location = *int_loc, + .index.location = *int_loc, + }; + json_t *tmp, *value; + const char *family; + struct rule *rule; + size_t index; + + if (json_unpack_err(ctx, root, "{s:o}", "rule", &tmp)) + return NULL; + root = tmp; + + if (json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:o}", + "family", &family, + "table", &h.table.name, + "chain", &h.chain.name, + "expr", &tmp)) + return NULL; + json_unpack(root, "{s:I}", "handle", &h.handle.id); + if (!json_unpack(root, "{s:I}", "index", &h.index.id)) { + h.index.id++; + } + + if (op == CMD_REPLACE && !h.handle.id) { + json_error(ctx, "Handle is required when replacing a rule."); + return NULL; + } + + if ((op == CMD_INSERT || op == CMD_ADD) && h.handle.id) { + h.position.id = h.handle.id; + h.handle.id = 0; + } + + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + + if (!json_is_array(tmp)) { + json_error(ctx, "Value of property \"expr\" must be an array."); + return NULL; + } + + h.table.name = xstrdup(h.table.name); + h.chain.name = xstrdup(h.chain.name); + + rule = rule_alloc(int_loc, NULL); + + if (!json_unpack(root, "{s:s}", "comment", &rule->comment)) + rule->comment = xstrdup(rule->comment); + + json_array_foreach(tmp, index, value) { + struct stmt *stmt; + + if (!json_is_object(value)) { + json_error(ctx, "Unexpected expr array element of type %s, expected object.", + json_typename(value)); + rule_free(rule); + return NULL; + } + + stmt = json_parse_stmt(ctx, value); + + if (!stmt) { + json_error(ctx, "Parsing expr array at index %zd failed.", + index); + rule_free(rule); + return NULL; + } + + rule_stmt_append(rule, stmt); + } + + if (op == CMD_REPLACE) + json_object_del(root, "handle"); + + return cmd_alloc(op, CMD_OBJ_RULE, &h, int_loc, rule); +} + +static struct cmd *json_parse_cmd_list_multiple(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj obj) +{ + struct handle h = { + .family = NFPROTO_UNSPEC, + }; + const char *tmp; + + if (!json_unpack(root, "{s:s}", "family", &tmp)) { + if (parse_family(tmp, &h.family)) { + json_error(ctx, "Unknown family '%s'.", tmp); + return NULL; + } + } + switch (obj) { + case CMD_OBJ_SETS: + case CMD_OBJ_COUNTERS: + case CMD_OBJ_CT_HELPERS: + if (!json_unpack(root, "{s:s}", "table", &tmp)) + h.table.name = xstrdup(tmp); + break; + default: + break; + } + if (obj == CMD_OBJ_CT_HELPERS && !h.table.name) { + json_error(ctx, "Listing ct helpers requires table reference."); + return NULL; + } + return cmd_alloc(op, obj, &h, int_loc, NULL); +} + +static struct cmd *json_parse_cmd_list(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + struct { + const char *key; + enum cmd_obj obj; + struct cmd *(*cb)(struct json_ctx *, json_t *, + enum cmd_ops, enum cmd_obj); + } cmd_obj_table[] = { + { "table", CMD_OBJ_TABLE, json_parse_cmd_add_table }, + { "tables", CMD_OBJ_TABLE, json_parse_cmd_list_multiple }, + { "chain", CMD_OBJ_CHAIN, json_parse_cmd_add_chain }, + { "chains", CMD_OBJ_CHAINS, json_parse_cmd_list_multiple }, + { "set", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "sets", CMD_OBJ_SETS, json_parse_cmd_list_multiple }, + { "map", CMD_OBJ_MAP, json_parse_cmd_add_set }, + { "maps", CMD_OBJ_MAPS, json_parse_cmd_list_multiple }, + { "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object }, + { "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple }, + { "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object }, + { "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple }, + { "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object }, + { "ct helpers", CMD_OBJ_CT_HELPERS, json_parse_cmd_list_multiple }, + { "ct timeout", NFT_OBJECT_CT_TIMEOUT, json_parse_cmd_add_object }, + { "ct expectation", NFT_OBJECT_CT_EXPECT, json_parse_cmd_add_object }, + { "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object }, + { "limits", CMD_OBJ_LIMIT, json_parse_cmd_list_multiple }, + { "ruleset", CMD_OBJ_RULESET, json_parse_cmd_list_multiple }, + { "meter", CMD_OBJ_METER, json_parse_cmd_add_set }, + { "meters", CMD_OBJ_METERS, json_parse_cmd_list_multiple }, + { "flowtables", CMD_OBJ_FLOWTABLES, json_parse_cmd_list_multiple }, + { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object }, + { "secmarks", CMD_OBJ_SECMARKS, json_parse_cmd_list_multiple }, + }; + unsigned int i; + json_t *tmp; + + if (!json_is_object(root)) { + json_error(ctx, "Value of list command must be object (got %s instead).", + json_typename(root)); + return NULL; + } + + for (i = 0; i < array_size(cmd_obj_table); i++) { + tmp = json_object_get(root, cmd_obj_table[i].key); + if (!tmp) + continue; + + return cmd_obj_table[i].cb(ctx, tmp, op, cmd_obj_table[i].obj); + } + json_error(ctx, "Unknown object passed to list command."); + return NULL; +} + +static struct cmd *json_parse_cmd_reset_rule(struct json_ctx *ctx, + json_t *root, enum cmd_ops op, + enum cmd_obj obj) +{ + struct handle h = { + .family = NFPROTO_UNSPEC, + }; + const char *family = NULL, *table = NULL, *chain = NULL; + + + if (obj == CMD_OBJ_RULE && + json_unpack_err(ctx, root, "{s:s, s:s, s:s, s:I}", + "family", &family, "table", &table, + "chain", &chain, "handle", &h.handle.id)) + return NULL; + else if (obj == CMD_OBJ_RULES) { + json_unpack(root, "{s:s}", "family", &family); + json_unpack(root, "{s:s}", "table", &table); + json_unpack(root, "{s:s}", "chain", &chain); + } + + if (family && parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + if (table) { + h.table.name = xstrdup(table); + if (chain) + h.chain.name = xstrdup(chain); + } + return cmd_alloc(op, obj, &h, int_loc, NULL); +} + +static struct cmd *json_parse_cmd_reset(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + struct { + const char *key; + enum cmd_obj obj; + struct cmd *(*cb)(struct json_ctx *, json_t *, + enum cmd_ops, enum cmd_obj); + } cmd_obj_table[] = { + { "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object }, + { "counters", CMD_OBJ_COUNTERS, json_parse_cmd_list_multiple }, + { "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object }, + { "quotas", CMD_OBJ_QUOTAS, json_parse_cmd_list_multiple }, + { "rule", CMD_OBJ_RULE, json_parse_cmd_reset_rule }, + { "rules", CMD_OBJ_RULES, json_parse_cmd_reset_rule }, + { "element", CMD_OBJ_ELEMENTS, json_parse_cmd_add_element }, + { "set", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "map", CMD_OBJ_MAP, json_parse_cmd_add_set }, + }; + unsigned int i; + json_t *tmp; + + if (!json_is_object(root)) { + json_error(ctx, "Value of reset command must be object (got %s instead).", + json_typename(root)); + return NULL; + } + + for (i = 0; i < array_size(cmd_obj_table); i++) { + tmp = json_object_get(root, cmd_obj_table[i].key); + if (!tmp) + continue; + + return cmd_obj_table[i].cb(ctx, tmp, op, cmd_obj_table[i].obj); + } + json_error(ctx, "Unknown object passed to reset command."); + return NULL; +} + +static struct cmd *json_parse_cmd_flush(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + struct { + const char *key; + enum cmd_obj obj; + struct cmd *(*cb)(struct json_ctx *, json_t *, + enum cmd_ops, enum cmd_obj); + } cmd_obj_table[] = { + { "table", CMD_OBJ_TABLE, json_parse_cmd_add_table }, + { "chain", CMD_OBJ_CHAIN, json_parse_cmd_add_chain }, + { "set", CMD_OBJ_SET, json_parse_cmd_add_set }, + { "map", CMD_OBJ_MAP, json_parse_cmd_add_set }, + { "meter", CMD_OBJ_METER, json_parse_cmd_add_set }, + { "ruleset", CMD_OBJ_RULESET, json_parse_cmd_list_multiple }, + }; + unsigned int i; + json_t *tmp; + + if (!json_is_object(root)) { + json_error(ctx, "Value of flush command must be object (got %s instead).", + json_typename(root)); + return NULL; + } + + for (i = 0; i < array_size(cmd_obj_table); i++) { + tmp = json_object_get(root, cmd_obj_table[i].key); + if (!tmp) + continue; + + return cmd_obj_table[i].cb(ctx, tmp, op, cmd_obj_table[i].obj); + } + json_error(ctx, "Unknown object passed to flush command."); + return NULL; +} + +static struct cmd *json_parse_cmd_rename(struct json_ctx *ctx, + json_t *root, enum cmd_ops op) +{ + const char *family, *newname; + struct handle h = { 0 }; + struct cmd *cmd; + + if (json_unpack_err(ctx, root, "{s:{s:s, s:s, s:s, s:s}}", "chain", + "family", &family, + "table", &h.table.name, + "name", &h.chain.name, + "newname", &newname)) + return NULL; + if (parse_family(family, &h.family)) { + json_error(ctx, "Unknown family '%s'.", family); + return NULL; + } + h.table.name = xstrdup(h.table.name); + h.chain.name = xstrdup(h.chain.name); + + cmd = cmd_alloc(op, CMD_OBJ_CHAIN, &h, int_loc, NULL); + cmd->arg = xstrdup(newname); + return cmd; +} + +static struct cmd *json_parse_cmd(struct json_ctx *ctx, json_t *root) +{ + struct { + const char *key; + enum cmd_ops op; + struct cmd *(*cb)(struct json_ctx *ctx, json_t *, enum cmd_ops); + } parse_cb_table[] = { + { "add", CMD_ADD, json_parse_cmd_add }, + { "replace", CMD_REPLACE, json_parse_cmd_replace }, + { "create", CMD_CREATE, json_parse_cmd_add }, + { "insert", CMD_INSERT, json_parse_cmd_replace }, + { "delete", CMD_DELETE, json_parse_cmd_add }, + { "list", CMD_LIST, json_parse_cmd_list }, + { "reset", CMD_RESET, json_parse_cmd_reset }, + { "flush", CMD_FLUSH, json_parse_cmd_flush }, + { "rename", CMD_RENAME, json_parse_cmd_rename }, + { "destroy", CMD_DESTROY, json_parse_cmd_add }, + //{ "export", CMD_EXPORT, json_parse_cmd_export }, + //{ "monitor", CMD_MONITOR, json_parse_cmd_monitor }, + //{ "describe", CMD_DESCRIBE, json_parse_cmd_describe } + }; + unsigned int i; + json_t *tmp; + + for (i = 0; i < array_size(parse_cb_table); i++) { + tmp = json_object_get(root, parse_cb_table[i].key); + if (!tmp) + continue; + + return parse_cb_table[i].cb(ctx, tmp, parse_cb_table[i].op); + } + /* to accept 'list ruleset' output 1:1, try add command */ + return json_parse_cmd_add(ctx, root, CMD_ADD); +} + +static int json_verify_metainfo(struct json_ctx *ctx, json_t *root) +{ + int schema_version; + + if (!json_unpack(root, "{s:i}", "json_schema_version", &schema_version)) { + if (schema_version > JSON_SCHEMA_VERSION) { + json_error(ctx, + "Schema version %d not supported, maximum" + " supported version is %d\n", + schema_version, JSON_SCHEMA_VERSION); + return 1; + } + } + + return 0; +} + +struct json_cmd_assoc { + struct json_cmd_assoc *next; + struct hlist_node hnode; + const struct cmd *cmd; + json_t *json; +}; + +#define CMD_ASSOC_HSIZE 512 +static struct hlist_head json_cmd_assoc_hash[CMD_ASSOC_HSIZE]; +static struct json_cmd_assoc *json_cmd_assoc_list; + +static void json_cmd_assoc_free(void) +{ + struct json_cmd_assoc *cur; + struct hlist_node *pos, *n; + int i; + + while (json_cmd_assoc_list) { + cur = json_cmd_assoc_list->next; + free(json_cmd_assoc_list); + json_cmd_assoc_list = cur; + } + + for (i = 0; i < CMD_ASSOC_HSIZE; i++) { + hlist_for_each_entry_safe(cur, pos, n, + &json_cmd_assoc_hash[i], hnode) { + hlist_del(&cur->hnode); + free(cur); + } + } +} + +static void json_cmd_assoc_add(json_t *json, const struct cmd *cmd) +{ + struct json_cmd_assoc *new = xzalloc(sizeof *new); + + new->json = json; + new->cmd = cmd; + new->next = json_cmd_assoc_list; + + json_cmd_assoc_list = new; +} + +static json_t *seqnum_to_json(const uint32_t seqnum) +{ + struct json_cmd_assoc *cur; + struct hlist_node *n; + int key; + + while (json_cmd_assoc_list) { + cur = json_cmd_assoc_list; + json_cmd_assoc_list = cur->next; + + key = cur->cmd->seqnum % CMD_ASSOC_HSIZE; + hlist_add_head(&cur->hnode, &json_cmd_assoc_hash[key]); + } + + key = seqnum % CMD_ASSOC_HSIZE; + hlist_for_each_entry(cur, n, &json_cmd_assoc_hash[key], hnode) { + if (cur->cmd->seqnum == seqnum) + return cur->json; + } + + return NULL; +} + +static int __json_parse(struct json_ctx *ctx) +{ + json_t *tmp, *value; + size_t index; + + if (json_unpack_err(ctx, ctx->nft->json_root, + "{s:o}", "nftables", &tmp)) + return -1; + + if (!json_is_array(tmp)) { + json_error(ctx, "Value of property \"nftables\" must be an array."); + return -1; + } + + json_array_foreach(tmp, index, value) { + /* this is more or less from parser_bison.y:716 */ + LIST_HEAD(list); + struct cmd *cmd; + json_t *tmp2; + + if (!json_is_object(value)) { + json_error(ctx, "Unexpected command array element of type %s, expected object.", json_typename(value)); + return -1; + } + + tmp2 = json_object_get(value, "metainfo"); + if (tmp2) { + if (json_verify_metainfo(ctx, tmp2)) { + json_error(ctx, "Metainfo verification failed."); + return -1; + } + continue; + } + + cmd = json_parse_cmd(ctx, value); + + if (!cmd) { + json_error(ctx, "Parsing command array at index %zd failed.", index); + return -1; + } + + list_add_tail(&cmd->list, &list); + + list_splice_tail(&list, ctx->cmds); + + if (nft_output_echo(&ctx->nft->output)) + json_cmd_assoc_add(value, cmd); + } + + return 0; +} + +int nft_parse_json_buffer(struct nft_ctx *nft, const char *buf, + struct list_head *msgs, struct list_head *cmds) +{ + struct json_ctx ctx = { + .nft = nft, + .msgs = msgs, + .cmds = cmds, + }; + int ret; + + json_indesc.type = INDESC_BUFFER; + json_indesc.data = buf; + + parser_init(nft, nft->state, msgs, cmds, nft->top_scope); + nft->json_root = json_loads(buf, 0, NULL); + if (!nft->json_root) + return -EINVAL; + + ret = __json_parse(&ctx); + + if (!nft_output_echo(&nft->output)) { + json_decref(nft->json_root); + nft->json_root = NULL; + } + return ret; +} + +int nft_parse_json_filename(struct nft_ctx *nft, const char *filename, + struct list_head *msgs, struct list_head *cmds) +{ + struct json_ctx ctx = { + .nft = nft, + .msgs = msgs, + .cmds = cmds, + }; + json_error_t err; + int ret; + + json_indesc.type = INDESC_FILE; + json_indesc.name = filename; + + parser_init(nft, nft->state, msgs, cmds, nft->top_scope); + nft->json_root = json_load_file(filename, 0, &err); + if (!nft->json_root) + return -EINVAL; + + ret = __json_parse(&ctx); + + if (!nft_output_echo(&nft->output)) { + json_decref(nft->json_root); + nft->json_root = NULL; + } + return ret; +} + +static int json_echo_error(struct netlink_mon_handler *monh, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, int_loc, fmt, ap); + va_end(ap); + erec_queue(erec, monh->ctx->msgs); + + return MNL_CB_ERROR; +} + +static uint64_t handle_from_nlmsg(const struct nlmsghdr *nlh) +{ + struct nftnl_table *nlt; + struct nftnl_chain *nlc; + struct nftnl_rule *nlr; + struct nftnl_set *nls; + struct nftnl_obj *nlo; + uint64_t handle = 0; + uint32_t flags; + + switch (NFNL_MSG_TYPE(nlh->nlmsg_type)) { + case NFT_MSG_NEWTABLE: + nlt = netlink_table_alloc(nlh); + handle = nftnl_table_get_u64(nlt, NFTNL_TABLE_HANDLE); + nftnl_table_free(nlt); + break; + case NFT_MSG_NEWCHAIN: + nlc = netlink_chain_alloc(nlh); + handle = nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE); + nftnl_chain_free(nlc); + break; + case NFT_MSG_NEWRULE: + nlr = netlink_rule_alloc(nlh); + handle = nftnl_rule_get_u64(nlr, NFTNL_RULE_HANDLE); + nftnl_rule_free(nlr); + break; + case NFT_MSG_NEWSET: + nls = netlink_set_alloc(nlh); + flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (!set_is_anonymous(flags)) + handle = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE); + nftnl_set_free(nls); + break; + case NFT_MSG_NEWOBJ: + nlo = netlink_obj_alloc(nlh); + handle = nftnl_obj_get_u64(nlo, NFTNL_OBJ_HANDLE); + nftnl_obj_free(nlo); + break; + } + return handle; +} +int json_events_cb(const struct nlmsghdr *nlh, struct netlink_mon_handler *monh) +{ + uint64_t handle = handle_from_nlmsg(nlh); + json_t *tmp, *json; + void *iter; + + if (!handle) + return MNL_CB_OK; + + json = seqnum_to_json(nlh->nlmsg_seq); + if (!json) { + json_echo_error(monh, "No JSON command found with seqnum %lu\n", + nlh->nlmsg_seq); + return MNL_CB_OK; + } + + tmp = json_object_get(json, "add"); + if (!tmp) + tmp = json_object_get(json, "insert"); + if (!tmp) + /* assume loading JSON dump */ + tmp = json; + + iter = json_object_iter(tmp); + if (!iter) { + json_echo_error(monh, "Empty JSON object in cmd list\n"); + return MNL_CB_OK; + } + json = json_object_iter_value(iter); + if (!json_is_object(json) || json_object_iter_next(tmp, iter)) { + json_echo_error(monh, "Malformed JSON object in cmd list\n"); + return MNL_CB_OK; + } + + json_object_set_new(json, "handle", json_integer(handle)); + return MNL_CB_OK; +} + +void json_print_echo(struct nft_ctx *ctx) +{ + if (!ctx->json_root) { + if (!ctx->json_echo) + return; + + ctx->json_echo = json_pack("{s:o}", "nftables", ctx->json_echo); + json_dumpf(ctx->json_echo, ctx->output.output_fp, JSON_PRESERVE_ORDER); + json_decref(ctx->json_echo); + ctx->json_echo = NULL; + fprintf(ctx->output.output_fp, "\n"); + fflush(ctx->output.output_fp); + } else { + json_dumpf(ctx->json_root, ctx->output.output_fp, JSON_PRESERVE_ORDER); + json_cmd_assoc_free(); + json_decref(ctx->json_root); + ctx->json_root = NULL; + } +} diff --git a/src/payload.c b/src/payload.c new file mode 100644 index 0000000..140ca50 --- /dev/null +++ b/src/payload.c @@ -0,0 +1,1494 @@ +/* + * Payload expression and related functions. + * + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <net/if_arp.h> +#include <arpa/inet.h> +#include <linux/netfilter.h> +#include <linux/if_ether.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> + +#include <rule.h> +#include <expression.h> +#include <statement.h> +#include <payload.h> +#include <gmputil.h> +#include <utils.h> +#include <json.h> + +bool payload_is_known(const struct expr *expr) +{ + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc; + + desc = expr->payload.desc; + tmpl = expr->payload.tmpl; + + return desc && tmpl && desc != &proto_unknown && + tmpl != &proto_unknown_template; +} + +static void payload_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const struct proto_desc *desc; + const struct proto_hdr_template *tmpl; + + if (expr->payload.inner_desc && + expr->payload.inner_desc != expr->payload.desc) + nft_print(octx, "%s ", expr->payload.inner_desc->name); + + desc = expr->payload.desc; + tmpl = expr->payload.tmpl; + if (payload_is_known(expr)) + nft_print(octx, "%s %s", desc->name, tmpl->token); + else + nft_print(octx, "@%s,%u,%u", + proto_base_tokens[expr->payload.base], + expr->payload.offset, expr->len); +} + +bool payload_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->payload.desc == e2->payload.desc && + e1->payload.tmpl == e2->payload.tmpl && + e1->payload.base == e2->payload.base && + e1->payload.offset == e2->payload.offset; +} + +static void payload_expr_clone(struct expr *new, const struct expr *expr) +{ + new->payload.inner_desc = expr->payload.inner_desc; + new->payload.desc = expr->payload.desc; + new->payload.tmpl = expr->payload.tmpl; + new->payload.base = expr->payload.base; + new->payload.offset = expr->payload.offset; +} + +/** + * payload_expr_pctx_update - update protocol context based on payload match + * + * @ctx: protocol context + * @expr: relational payload expression + * + * Update protocol context for relational payload expressions. + */ +static void payload_expr_pctx_update(struct proto_ctx *ctx, + const struct location *loc, + const struct expr *left, + const struct expr *right) +{ + const struct proto_desc *base, *desc; + unsigned int proto = 0; + + /* Export the data in the correct byte order */ + assert(right->len / BITS_PER_BYTE <= sizeof(proto)); + mpz_export_data(constant_data_ptr(proto, right->len), right->value, + right->byteorder, right->len / BITS_PER_BYTE); + + base = ctx->protocol[left->payload.base].desc; + desc = proto_find_upper(base, proto); + + if (!desc) { + if (base == &proto_icmp) { + /* proto 0 is ECHOREPLY, just pretend its ECHO. + * Not doing this would need an additional marker + * bit to tell when icmp.type was set. + */ + ctx->th_dep.icmp.type = proto ? proto : ICMP_ECHO; + } else if (base == &proto_icmp6) { + if (proto == ICMP6_ECHO_REPLY) + proto = ICMP6_ECHO_REQUEST; + ctx->th_dep.icmp.type = proto; + } + return; + } + + assert(desc->base <= PROTO_BASE_MAX); + if (desc->base == base->base) { + assert(base->length > 0); + + if (!left->payload.is_raw) { + if (desc->base == PROTO_BASE_LL_HDR && + ctx->stacked_ll_count < PROTO_CTX_NUM_PROTOS) { + ctx->stacked_ll[ctx->stacked_ll_count] = base; + ctx->stacked_ll_count++; + } + } + } + proto_ctx_update(ctx, desc->base, loc, desc); +} + +#define NFTNL_UDATA_SET_KEY_PAYLOAD_DESC 0 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE 1 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_BASE 2 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET 3 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_LEN 4 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC 5 +#define NFTNL_UDATA_SET_KEY_PAYLOAD_MAX 6 + +static unsigned int expr_payload_type(const struct proto_desc *desc, + const struct proto_hdr_template *tmpl) +{ + if (desc->id == PROTO_DESC_UNKNOWN) + return 0; + + return (unsigned int)(tmpl - &desc->templates[0]); +} + +static int payload_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + const struct proto_hdr_template *tmpl = expr->payload.tmpl; + const struct proto_desc *desc = expr->payload.desc; + unsigned int type = expr_payload_type(desc, tmpl); + + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_DESC, desc->id); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE, type); + + if (desc->id == 0) { + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_BASE, + expr->payload.base); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET, + expr->payload.offset); + } + if (expr->dtype->type == TYPE_INTEGER) + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_LEN, expr->len); + + if (expr->payload.inner_desc) { + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC, + expr->payload.inner_desc->id); + } + + return 0; +} + +const struct proto_desc *find_proto_desc(const struct nftnl_udata *ud) +{ + return proto_find_desc(nftnl_udata_get_u32(ud)); +} + +static int payload_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_KEY_PAYLOAD_DESC: + case NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE: + case NFTNL_UDATA_SET_KEY_PAYLOAD_BASE: + case NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET: + case NFTNL_UDATA_SET_KEY_PAYLOAD_LEN: + case NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *payload_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_KEY_PAYLOAD_MAX + 1] = {}; + unsigned int type, base, offset, len = 0; + const struct proto_desc *desc; + bool is_raw = false; + struct expr *expr; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + payload_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_SET_KEY_PAYLOAD_DESC] || + !ud[NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE]) + return NULL; + + desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_DESC]); + if (!desc) { + if (!ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE] || + !ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET]) + return NULL; + + base = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_BASE]); + offset = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_OFFSET]); + is_raw = true; + } + + type = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_TYPE]); + if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN]) + len = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_LEN]); + + expr = payload_expr_alloc(&internal_location, desc, type); + + if (len) + expr->len = len; + + if (is_raw) { + struct datatype *dtype; + + expr->payload.base = base; + expr->payload.offset = offset; + expr->payload.is_raw = true; + expr->len = len; + dtype = datatype_clone(&xinteger_type); + dtype->size = len; + dtype->byteorder = BYTEORDER_BIG_ENDIAN; + __datatype_set(expr, dtype); + } + + if (ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]) { + desc = find_proto_desc(ud[NFTNL_UDATA_SET_KEY_PAYLOAD_INNER_DESC]); + expr->payload.inner_desc = desc; + } + + return expr; +} + +const struct expr_ops payload_expr_ops = { + .type = EXPR_PAYLOAD, + .name = "payload", + .print = payload_expr_print, + .json = payload_expr_json, + .cmp = payload_expr_cmp, + .clone = payload_expr_clone, + .pctx_update = payload_expr_pctx_update, + .build_udata = payload_expr_build_udata, + .parse_udata = payload_expr_parse_udata, +}; + +/* + * We normally use 'meta l4proto' to fetch the last l4 header of the + * ipv6 extension header chain so we will also match + * tcp after a fragmentation header, for instance. + * For consistency we also use meta l4proto for ipv4. + * + * If user specifically asks for nexthdr x, don't add another (useless) + * meta dependency. + */ +static bool proto_key_is_protocol(const struct proto_desc *desc, unsigned int type) +{ + if (type == desc->protocol_key) + return true; + + if (desc == &proto_ip6 && type == IP6HDR_NEXTHDR) + return true; + if (desc == &proto_ip && type == IPHDR_PROTOCOL) + return true; + + return false; +} + +struct expr *payload_expr_alloc(const struct location *loc, + const struct proto_desc *desc, + unsigned int type) +{ + const struct proto_hdr_template *tmpl; + enum proto_bases base; + struct expr *expr; + unsigned int flags = 0; + + if (desc != NULL) { + tmpl = &desc->templates[type]; + base = desc->base; + if (proto_key_is_protocol(desc, type)) + flags = EXPR_F_PROTOCOL; + } else { + tmpl = &proto_unknown_template; + base = PROTO_BASE_INVALID; + desc = &proto_unknown; + } + + expr = expr_alloc(loc, EXPR_PAYLOAD, tmpl->dtype, + tmpl->byteorder, tmpl->len); + expr->flags |= flags; + + expr->payload.desc = desc; + expr->payload.tmpl = tmpl; + expr->payload.base = base; + expr->payload.offset = tmpl->offset; + + return expr; +} + +void payload_init_raw(struct expr *expr, enum proto_bases base, + unsigned int offset, unsigned int len) +{ + enum th_hdr_fields thf; + + expr->payload.base = base; + expr->payload.offset = offset; + expr->len = len; + expr->dtype = &xinteger_type; + + if (base != PROTO_BASE_TRANSPORT_HDR) + return; + if (len != 16) + return; + + switch (offset) { + case 0: + thf = THDR_SPORT; + /* fall through */ + case 16: + if (offset == 16) + thf = THDR_DPORT; + expr->payload.tmpl = &proto_th.templates[thf]; + expr->payload.desc = &proto_th; + expr->dtype = &inet_service_type; + expr->payload.desc = &proto_th; + break; + default: + break; + } +} + +unsigned int payload_hdr_field(const struct expr *expr) +{ + return expr->payload.tmpl - expr->payload.desc->templates; +} + +static void payload_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + expr_print(stmt->payload.expr, octx); + nft_print(octx, " set "); + expr_print(stmt->payload.val, octx); +} + +static void payload_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->payload.expr); + expr_free(stmt->payload.val); +} + +static const struct stmt_ops payload_stmt_ops = { + .type = STMT_PAYLOAD, + .name = "payload", + .print = payload_stmt_print, + .json = payload_stmt_json, + .destroy = payload_stmt_destroy, +}; + +struct stmt *payload_stmt_alloc(const struct location *loc, + struct expr *expr, struct expr *val) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &payload_stmt_ops); + stmt->payload.expr = expr; + stmt->payload.val = val; + return stmt; +} + +static int payload_add_dependency(struct eval_ctx *ctx, + const struct proto_desc *desc, + const struct proto_desc *upper, + const struct expr *expr, + struct stmt **res) +{ + const struct proto_hdr_template *tmpl; + struct expr *dep, *left, *right; + struct proto_ctx *pctx; + unsigned int stmt_len; + struct stmt *stmt; + int protocol; + + protocol = proto_find_num(desc, upper); + if (protocol < 0) + return expr_error(ctx->msgs, expr, + "conflicting protocols specified: %s vs. %s", + desc->name, upper->name); + + tmpl = &desc->templates[desc->protocol_key]; + if (tmpl->meta_key) + left = meta_expr_alloc(&expr->location, tmpl->meta_key); + else + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + + right = constant_expr_alloc(&expr->location, tmpl->dtype, + tmpl->dtype->byteorder, tmpl->len, + constant_data_ptr(protocol, tmpl->len)); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + + stmt_len = ctx->stmt_len; + ctx->stmt_len = 0; + + stmt = expr_stmt_alloc(&dep->location, dep); + if (stmt_evaluate(ctx, stmt) < 0) { + return expr_error(ctx->msgs, expr, + "dependency statement is invalid"); + } + ctx->stmt_len = stmt_len; + + if (ctx->inner_desc) { + if (tmpl->meta_key) + left->meta.inner_desc = ctx->inner_desc; + else + left->payload.inner_desc = ctx->inner_desc; + } + + pctx = eval_proto_ctx(ctx); + relational_expr_pctx_update(pctx, dep); + *res = stmt; + return 0; +} + +static const struct proto_desc * +payload_get_get_ll_hdr(const struct proto_ctx *pctx) +{ + switch (pctx->family) { + case NFPROTO_INET: + return &proto_inet; + case NFPROTO_BRIDGE: + return &proto_eth; + case NFPROTO_NETDEV: + return &proto_netdev; + default: + break; + } + + return NULL; +} + +static const struct proto_desc * +payload_gen_special_dependency(struct eval_ctx *ctx, const struct expr *expr) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + + switch (expr->payload.base) { + case PROTO_BASE_LL_HDR: + return payload_get_get_ll_hdr(pctx); + case PROTO_BASE_TRANSPORT_HDR: + if (expr->payload.desc == &proto_icmp || + expr->payload.desc == &proto_icmp6 || + expr->payload.desc == &proto_igmp) { + const struct proto_desc *desc, *desc_upper; + struct stmt *nstmt; + + desc = pctx->protocol[PROTO_BASE_LL_HDR].desc; + if (!desc) { + desc = payload_get_get_ll_hdr(pctx); + if (!desc) + break; + } + + /* this tunnel protocol does not encapsulate an inner + * link layer, use proto_netdev which relies on + * NFT_META_PROTOCOL for dependencies. + */ + if (expr->payload.inner_desc && + !(expr->payload.inner_desc->inner.flags & NFT_INNER_LL)) + desc = &proto_netdev; + + desc_upper = &proto_ip6; + if (expr->payload.desc == &proto_icmp || + expr->payload.desc == &proto_igmp) + desc_upper = &proto_ip; + + if (payload_add_dependency(ctx, desc, desc_upper, + expr, &nstmt) < 0) + return NULL; + + list_add_tail(&nstmt->list, &ctx->stmt->list); + return desc_upper; + } + return &proto_inet_service; + default: + break; + } + return NULL; +} + +/** + * 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 stmt **res) +{ + const struct hook_proto_desc *h; + const struct proto_desc *desc; + struct proto_ctx *pctx; + unsigned int stmt_len; + struct stmt *stmt; + uint16_t type; + + pctx = eval_proto_ctx(ctx); + h = &hook_proto_desc[pctx->family]; + if (expr->payload.base < h->base) { + if (expr->payload.base < h->base - 1) + return expr_error(ctx->msgs, expr, + "payload base is invalid for this " + "family"); + + if (proto_dev_type(expr->payload.desc, &type) < 0) + return expr_error(ctx->msgs, expr, + "protocol specification is invalid " + "for this family"); + + stmt_len = ctx->stmt_len; + ctx->stmt_len = 0; + + stmt = meta_stmt_meta_iiftype(&expr->location, type); + if (stmt_evaluate(ctx, stmt) < 0) { + return expr_error(ctx->msgs, expr, + "dependency statement is invalid"); + } + *res = stmt; + + ctx->stmt_len = stmt_len; + + return 0; + } + + desc = pctx->protocol[expr->payload.base - 1].desc; + /* Special case for mixed IPv4/IPv6 and bridge tables */ + if (desc == NULL) + desc = payload_gen_special_dependency(ctx, expr); + + if (desc == NULL) + return expr_error(ctx->msgs, expr, + "ambiguous payload specification: " + "no %s protocol specified", + proto_base_names[expr->payload.base - 1]); + + if (pctx->family == NFPROTO_BRIDGE && desc == &proto_eth) { + /* prefer netdev proto, which adds dependencies based + * on skb->protocol. + * + * This has the advantage that we will also match + * vlan encapsulated traffic. + * + * eth_hdr(skb)->type would not match, as nft_payload + * will pretend vlan tag was not offloaded, i.e. + * type is ETH_P_8021Q in such a case, but skb->protocol + * would still match the l3 header type. + */ + if (expr->payload.desc == &proto_ip || + expr->payload.desc == &proto_ip6) + desc = &proto_netdev; + } + + return payload_add_dependency(ctx, desc, expr->payload.desc, expr, res); +} + +int exthdr_gen_dependency(struct eval_ctx *ctx, const struct expr *expr, + const struct proto_desc *dependency, + enum proto_bases pb, struct stmt **res) +{ + const struct proto_desc *desc; + struct proto_ctx *pctx; + + pctx = eval_proto_ctx(ctx); + desc = pctx->protocol[pb].desc; + if (desc == NULL) { + if (expr->exthdr.op == NFT_EXTHDR_OP_TCPOPT) { + switch (pctx->family) { + case NFPROTO_NETDEV: + case NFPROTO_BRIDGE: + case NFPROTO_INET: + desc = &proto_inet_service; + goto found; + default: + break; + } + } + + return expr_error(ctx->msgs, expr, + "Cannot generate dependency: " + "no %s protocol specified", + proto_base_names[pb]); + } + + found: + return payload_add_dependency(ctx, desc, dependency, expr, res); +} + +/** + * payload_is_stacked - return whether a payload protocol match defines a stacked + * protocol on the same layer + * + * @desc: current protocol description on this layer + * @expr: payload match + */ +bool payload_is_stacked(const struct proto_desc *desc, const struct expr *expr) +{ + const struct proto_desc *next; + + if (expr->left->etype != EXPR_PAYLOAD || + !(expr->left->flags & EXPR_F_PROTOCOL) || + expr->op != OP_EQ) + return false; + + next = proto_find_upper(desc, mpz_get_be16(expr->right->value)); + return next && next->base == desc->base; +} + +void payload_dependency_reset(struct payload_dep_ctx *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +static bool payload_dependency_store_icmp_type(struct payload_dep_ctx *ctx, + const struct stmt *stmt) +{ + struct expr *dep = stmt->expr; + const struct proto_desc *desc; + const struct expr *right; + uint8_t type; + + if (dep->left->etype != EXPR_PAYLOAD) + return false; + + right = dep->right; + if (right->etype != EXPR_VALUE || right->len != BITS_PER_BYTE) + return false; + + desc = dep->left->payload.desc; + if (desc == &proto_icmp) { + type = mpz_get_uint8(right->value); + + if (type == ICMP_ECHOREPLY) + type = ICMP_ECHO; + + ctx->icmp_type = type; + + return type == ICMP_ECHO; + } else if (desc == &proto_icmp6) { + type = mpz_get_uint8(right->value); + + ctx->icmp_type = type; + return type == ICMP6_ECHO_REQUEST || type == ICMP6_ECHO_REPLY; + } + + return false; +} + +/** + * payload_dependency_store - store a possibly redundant protocol match + * + * @ctx: payload dependency context + * @stmt: payload match + * @base: base of payload match + */ +void payload_dependency_store(struct payload_dep_ctx *ctx, + struct stmt *stmt, enum proto_bases base) +{ + bool ignore_dep = payload_dependency_store_icmp_type(ctx, stmt); + + if (ignore_dep) + return; + + ctx->pdeps[base + 1] = stmt; +} + +/** + * payload_dependency_exists - there is a payload dependency in place + * @ctx: payload dependency context + * @base: payload protocol base + * + * Check if we have seen a protocol key payload expression for this base, we can + * usually remove it if we can infer it from another payload expression in the + * upper base. + */ +bool payload_dependency_exists(const struct payload_dep_ctx *ctx, + enum proto_bases base) +{ + if (ctx->pdeps[base]) + return true; + + return base == PROTO_BASE_TRANSPORT_HDR && + ctx->pdeps[PROTO_BASE_INNER_HDR]; +} + +/** + * payload_dependency_get - return a payload dependency if available + * @ctx: payload dependency context + * @base: payload protocol base + * + * If we have seen a protocol key payload expression for this base, we return + * it. + */ +struct expr *payload_dependency_get(struct payload_dep_ctx *ctx, + enum proto_bases base) +{ + if (ctx->pdeps[base]) + return ctx->pdeps[base]->expr; + + if (base == PROTO_BASE_TRANSPORT_HDR && + ctx->pdeps[PROTO_BASE_INNER_HDR]) + return ctx->pdeps[PROTO_BASE_INNER_HDR]->expr; + + return NULL; +} + +static void __payload_dependency_release(struct payload_dep_ctx *ctx, + enum proto_bases base) +{ + list_del(&ctx->pdeps[base]->list); + stmt_free(ctx->pdeps[base]); + + if (ctx->pdeps[base] == ctx->prev) + ctx->prev = NULL; + ctx->pdeps[base] = NULL; +} + +void payload_dependency_release(struct payload_dep_ctx *ctx, + enum proto_bases base) +{ + if (ctx->pdeps[base]) + __payload_dependency_release(ctx, base); + else if (base == PROTO_BASE_TRANSPORT_HDR && + ctx->pdeps[PROTO_BASE_INNER_HDR]) + __payload_dependency_release(ctx, PROTO_BASE_INNER_HDR); +} + +static uint8_t icmp_dep_to_type(enum icmp_hdr_field_type t) +{ + switch (t) { + case PROTO_ICMP_ANY: + BUG("Invalid map for simple dependency"); + case PROTO_ICMP_ECHO: return ICMP_ECHO; + case PROTO_ICMP6_ECHO: return ICMP6_ECHO_REQUEST; + case PROTO_ICMP_MTU: return ICMP_DEST_UNREACH; + case PROTO_ICMP_ADDRESS: return ICMP_REDIRECT; + case PROTO_ICMP6_MTU: return ICMP6_PACKET_TOO_BIG; + case PROTO_ICMP6_MGMQ: return MLD_LISTENER_QUERY; + case PROTO_ICMP6_PPTR: return ICMP6_PARAM_PROB; + case PROTO_ICMP6_REDIRECT: return ND_REDIRECT; + case PROTO_ICMP6_ADDRESS: return ND_NEIGHBOR_SOLICIT; + } + + BUG("Missing icmp type mapping"); +} + +static bool icmp_dep_type_match(enum icmp_hdr_field_type t, uint8_t type) +{ + switch (t) { + case PROTO_ICMP_ECHO: + return type == ICMP_ECHO || type == ICMP_ECHOREPLY; + case PROTO_ICMP6_ECHO: + return type == ICMP6_ECHO_REQUEST || type == ICMP6_ECHO_REPLY; + case PROTO_ICMP6_ADDRESS: + return type == ND_NEIGHBOR_SOLICIT || + type == ND_NEIGHBOR_ADVERT || + type == ND_REDIRECT || + type == MLD_LISTENER_QUERY || + type == MLD_LISTENER_REPORT || + type == MLD_LISTENER_REDUCTION; + case PROTO_ICMP_ADDRESS: + case PROTO_ICMP_MTU: + case PROTO_ICMP6_MTU: + case PROTO_ICMP6_MGMQ: + case PROTO_ICMP6_PPTR: + case PROTO_ICMP6_REDIRECT: + return icmp_dep_to_type(t) == type; + case PROTO_ICMP_ANY: + return true; + } + BUG("Missing icmp type mapping"); +} + +static bool payload_may_dependency_kill_icmp(struct payload_dep_ctx *ctx, struct expr *expr) +{ + const struct expr *dep = payload_dependency_get(ctx, expr->payload.base); + enum icmp_hdr_field_type icmp_dep; + + icmp_dep = expr->payload.tmpl->icmp_dep; + if (icmp_dep == PROTO_ICMP_ANY) + return false; + + if (dep->left->payload.desc != expr->payload.desc) + return false; + + if (expr->payload.tmpl->icmp_dep == PROTO_ICMP_ECHO || + expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ECHO || + expr->payload.tmpl->icmp_dep == PROTO_ICMP6_ADDRESS) + return false; + + return ctx->icmp_type == icmp_dep_to_type(icmp_dep); +} + +static bool payload_may_dependency_kill_ll(struct payload_dep_ctx *ctx, struct expr *expr) +{ + const struct expr *dep = payload_dependency_get(ctx, expr->payload.base); + + /* Never remove a 'vlan type 0x...' expression, they are never added + * implicitly + */ + if (dep->left->payload.desc == &proto_vlan) + return false; + + /* 'vlan id 2' implies 'ether type 8021Q'. If a different protocol is + * tested, this is not a redundant expression. + */ + if (dep->left->payload.desc == &proto_eth && + dep->right->etype == EXPR_VALUE && dep->right->len == 16) + return mpz_get_uint16(dep->right->value) == ETH_P_8021Q; + + return true; +} + +static bool payload_may_dependency_kill(struct payload_dep_ctx *ctx, + unsigned int family, struct expr *expr) +{ + struct expr *dep = payload_dependency_get(ctx, expr->payload.base); + + /* Protocol key payload expression at network base such as 'ip6 nexthdr' + * need to be left in place since it implicitly restricts matching to + * IPv6 for the bridge, inet and netdev families. + */ + switch (family) { + case NFPROTO_BRIDGE: + case NFPROTO_NETDEV: + case NFPROTO_INET: + if (dep->left->etype == EXPR_PAYLOAD && + dep->left->payload.base == PROTO_BASE_NETWORK_HDR && + (dep->left->payload.desc == &proto_ip || + dep->left->payload.desc == &proto_ip6) && + expr->payload.base == PROTO_BASE_TRANSPORT_HDR) + return false; + /* Do not kill + * ether type vlan and vlan type ip and ip protocol icmp + * into + * ip protocol icmp + * as this lacks ether type vlan. + * More generally speaking, do not kill protocol type + * for stacked protocols if we only have protcol type matches. + */ + if (dep->left->etype == EXPR_PAYLOAD && dep->op == OP_EQ && + expr->payload.base == dep->left->payload.base) { + if (expr->flags & EXPR_F_PROTOCOL) + return false; + + if (expr->payload.base == PROTO_BASE_LL_HDR) + return payload_may_dependency_kill_ll(ctx, expr); + } + + break; + } + + if (expr->payload.base != PROTO_BASE_TRANSPORT_HDR) + return true; + + if (dep->left->payload.base != PROTO_BASE_TRANSPORT_HDR) + return true; + + if (dep->left->payload.desc == &proto_icmp) + return payload_may_dependency_kill_icmp(ctx, expr); + + if (dep->left->payload.desc == &proto_icmp6) + return payload_may_dependency_kill_icmp(ctx, expr); + + return true; +} + +/** + * payload_dependency_kill - kill a redundant payload dependency + * + * @ctx: payload dependency context + * @expr: higher layer payload expression + * + * Kill a redundant payload expression if a higher layer payload expression + * implies its existence. Skip this if the dependency is a network payload and + * we are in bridge, netdev and inet families. + */ +void payload_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr, + unsigned int family) +{ + if (expr->payload.desc != &proto_unknown && + payload_dependency_exists(ctx, expr->payload.base) && + payload_may_dependency_kill(ctx, family, expr)) + payload_dependency_release(ctx, expr->payload.base); +} + +void exthdr_dependency_kill(struct payload_dep_ctx *ctx, struct expr *expr, + unsigned int family) +{ + switch (expr->exthdr.op) { + case NFT_EXTHDR_OP_TCPOPT: + if (payload_dependency_exists(ctx, PROTO_BASE_TRANSPORT_HDR)) + payload_dependency_release(ctx, PROTO_BASE_TRANSPORT_HDR); + break; + case NFT_EXTHDR_OP_IPV6: + if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR)) + payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR); + break; + case NFT_EXTHDR_OP_IPV4: + if (payload_dependency_exists(ctx, PROTO_BASE_NETWORK_HDR)) + payload_dependency_release(ctx, PROTO_BASE_NETWORK_HDR); + break; + default: + break; + } +} + +static const struct proto_desc *get_stacked_desc(const struct proto_ctx *ctx, + const struct proto_desc *top, + const struct expr *e, + unsigned int *skip) +{ + unsigned int i, total, payload_offset = e->payload.offset; + + assert(e->etype == EXPR_PAYLOAD); + + if (e->payload.base != PROTO_BASE_LL_HDR || + payload_offset < top->length) { + *skip = 0; + return top; + } + + for (i = 0, total = 0; i < ctx->stacked_ll_count; i++) { + const struct proto_desc *stacked; + + stacked = ctx->stacked_ll[i]; + if (payload_offset < stacked->length) { + *skip = total; + return stacked; + } + + payload_offset -= stacked->length; + total += stacked->length; + } + + *skip = total; + return top; +} + +/** + * payload_expr_complete - fill in type information of a raw payload expr + * + * @expr: the payload expression + * @ctx: protocol 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 proto_ctx *ctx) +{ + unsigned int payload_offset = expr->payload.offset; + const struct proto_desc *desc; + const struct proto_hdr_template *tmpl; + unsigned int i, total; + + assert(expr->etype == EXPR_PAYLOAD); + + desc = ctx->protocol[expr->payload.base].desc; + if (desc == NULL || desc == &proto_inet) + return; + assert(desc->base == expr->payload.base); + + desc = get_stacked_desc(ctx, desc, expr, &total); + payload_offset -= total; + + for (i = 0; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset != payload_offset || + tmpl->len != expr->len) + continue; + + if (tmpl->meta_key && i == 0) + continue; + + if (tmpl->icmp_dep && ctx->th_dep.icmp.type && + !icmp_dep_type_match(tmpl->icmp_dep, + ctx->th_dep.icmp.type)) + continue; + + expr->dtype = tmpl->dtype; + expr->payload.desc = desc; + expr->byteorder = tmpl->byteorder; + expr->payload.tmpl = tmpl; + return; + } +} + +static unsigned int mask_to_offset(const struct expr *mask) +{ + return mask ? mpz_scan1(mask->value, 0) : 0; +} + +static unsigned int mask_length(const struct expr *mask) +{ + unsigned long off; + + off = mask_to_offset(mask); + + return mpz_scan0(mask->value, off + 1); +} + +/** + * payload_expr_trim - trim payload expression according to mask + * + * @expr: the payload expression + * @mask: mask to use when searching templates + * @ctx: protocol context + * + * Walk the template list and determine if a match can be found without + * using the provided mask. + * + * If the mask has to be used, trim the payload expression length accordingly, + * adjust the payload offset and return true to let the caller know that the + * mask can be removed. This function also returns the shift for the right hand + * constant side of the expression. + */ +bool payload_expr_trim(struct expr *expr, struct expr *mask, + const struct proto_ctx *ctx, unsigned int *shift) +{ + unsigned int payload_offset = expr->payload.offset; + unsigned int mask_offset = mask_to_offset(mask); + unsigned int mask_len = mask_length(mask); + const struct proto_hdr_template *tmpl; + unsigned int payload_len = expr->len; + const struct proto_desc *desc; + unsigned int off, i, len = 0; + unsigned int total; + + assert(expr->etype == EXPR_PAYLOAD); + + desc = ctx->protocol[expr->payload.base].desc; + if (desc == NULL) + return false; + + assert(desc->base == expr->payload.base); + + desc = get_stacked_desc(ctx, desc, expr, &total); + payload_offset -= total; + + off = round_up(mask->len, BITS_PER_BYTE) - mask_len; + payload_offset += off; + + for (i = 1; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + if (tmpl->offset != payload_offset) + continue; + + if (tmpl->len > payload_len) + return false; + + payload_len -= tmpl->len; + payload_offset += tmpl->len; + len += tmpl->len; + if (payload_len == 0) + return false; + + if (mask_offset + len == mask_len) { + expr->payload.offset += off; + expr->len = len; + *shift = mask_offset; + return true; + } + } + + return false; +} + +/** + * 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: protocol 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 proto_ctx *ctx) +{ + unsigned int payload_offset = expr->payload.offset; + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc; + unsigned int i, total; + struct expr *new; + + assert(expr->etype == EXPR_PAYLOAD); + + desc = ctx->protocol[expr->payload.base].desc; + if (desc == NULL || desc == &proto_unknown) + goto raw; + + assert(desc->base == expr->payload.base); + + desc = get_stacked_desc(ctx, desc, expr, &total); + payload_offset -= total; + + for (i = 1; i < array_size(desc->templates); i++) { + tmpl = &desc->templates[i]; + + if (tmpl->len == 0) + break; + + if (tmpl->offset != payload_offset) + continue; + + if (tmpl->icmp_dep && ctx->th_dep.icmp.type && + !icmp_dep_type_match(tmpl->icmp_dep, + ctx->th_dep.icmp.type)) + 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; + payload_offset += tmpl->len; + if (expr->len == 0) + return; + } else if (expr->len > 0) { + new = payload_expr_alloc(&expr->location, desc, i); + new->len = expr->len; + list_add_tail(&new->list, list); + return; + } else + break; + } +raw: + new = payload_expr_alloc(&expr->location, NULL, 0); + payload_init_raw(new, expr->payload.base, payload_offset, + expr->len); + + if (expr->payload.inner_desc) + new->dtype = &integer_type; + + list_add_tail(&new->list, list); +} + +static 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_can_merge - return whether two payload expressions can be merged + * + * @e1: first payload expression + * @e2: second payload expression + */ +bool payload_can_merge(const struct expr *e1, const struct expr *e2) +{ + unsigned int total; + + if (e1->payload.inner_desc != e2->payload.inner_desc) + return false; + + if (!payload_is_adjacent(e1, e2)) + return false; + + if (e1->payload.offset % BITS_PER_BYTE || e1->len % BITS_PER_BYTE || + e2->payload.offset % BITS_PER_BYTE || e2->len % BITS_PER_BYTE) + return false; + + total = e1->len + e2->len; + if (total < e1->len || total > (NFT_REG_SIZE * BITS_PER_BYTE)) + return false; + + /* could return true after this, the expressions are mergeable. + * + * However, there are some caveats. + * + * Loading anything <= sizeof(u32) with base >= network header + * is fast, because its handled directly from eval loop in the + * kernel. + * + * We thus restrict merging a bit more. + */ + + /* can still be handled by fastpath after merge */ + if (total <= NFT_REG32_SIZE * BITS_PER_BYTE) + return true; + + /* Linklayer base is not handled in fastpath, merge */ + if (e1->payload.base == PROTO_BASE_LL_HDR) + return true; + + /* Also merge if at least one expression is already + * above REG32 size, in this case merging is faster. + */ + if (e1->len > (NFT_REG32_SIZE * BITS_PER_BYTE) || + e2->len > (NFT_REG32_SIZE * BITS_PER_BYTE)) + 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; + expr->payload.inner_desc = e1->payload.inner_desc; + + return expr; +} + +static struct stmt * +__payload_gen_icmp_simple_dependency(struct eval_ctx *ctx, const struct expr *expr, + const struct datatype *icmp_type, + const struct proto_desc *desc, + uint8_t type) +{ + struct expr *left, *right, *dep; + + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + right = constant_expr_alloc(&expr->location, icmp_type, + BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE, + constant_data_ptr(type, BITS_PER_BYTE)); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + return expr_stmt_alloc(&dep->location, dep); +} + +static struct stmt * +__payload_gen_icmp_echo_dependency(struct eval_ctx *ctx, const struct expr *expr, + uint8_t echo, uint8_t reply, + const struct datatype *icmp_type, + const struct proto_desc *desc) +{ + struct expr *left, *right, *dep, *set; + + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + + set = set_expr_alloc(&expr->location, NULL); + + right = constant_expr_alloc(&expr->location, icmp_type, + BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE, + constant_data_ptr(echo, BITS_PER_BYTE)); + right = set_elem_expr_alloc(&expr->location, right); + compound_expr_add(set, right); + + right = constant_expr_alloc(&expr->location, icmp_type, + BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE, + constant_data_ptr(reply, BITS_PER_BYTE)); + right = set_elem_expr_alloc(&expr->location, right); + compound_expr_add(set, right); + + dep = relational_expr_alloc(&expr->location, OP_IMPLICIT, left, set); + return expr_stmt_alloc(&dep->location, dep); +} + +static struct stmt * +__payload_gen_icmp6_addr_dependency(struct eval_ctx *ctx, const struct expr *expr, + const struct proto_desc *desc) +{ + static const uint8_t icmp_addr_types[] = { + MLD_LISTENER_QUERY, + MLD_LISTENER_REPORT, + MLD_LISTENER_REDUCTION, + ND_NEIGHBOR_SOLICIT, + ND_NEIGHBOR_ADVERT, + ND_REDIRECT + }; + struct expr *left, *right, *dep, *set; + size_t i; + + left = payload_expr_alloc(&expr->location, desc, desc->protocol_key); + + set = set_expr_alloc(&expr->location, NULL); + + for (i = 0; i < array_size(icmp_addr_types); ++i) { + right = constant_expr_alloc(&expr->location, &icmp6_type_type, + BYTEORDER_BIG_ENDIAN, BITS_PER_BYTE, + constant_data_ptr(icmp_addr_types[i], + BITS_PER_BYTE)); + right = set_elem_expr_alloc(&expr->location, right); + compound_expr_add(set, right); + } + + dep = relational_expr_alloc(&expr->location, OP_IMPLICIT, left, set); + return expr_stmt_alloc(&dep->location, dep); +} + +int payload_gen_icmp_dependency(struct eval_ctx *ctx, const struct expr *expr, + struct stmt **res) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc; + struct stmt *stmt = NULL; + uint8_t type; + + assert(expr->etype == EXPR_PAYLOAD); + + tmpl = expr->payload.tmpl; + desc = expr->payload.desc; + + switch (tmpl->icmp_dep) { + case PROTO_ICMP_ANY: + BUG("No dependency needed"); + break; + case PROTO_ICMP_ECHO: + /* do not test ICMP_ECHOREPLY here: its 0 */ + if (pctx->th_dep.icmp.type == ICMP_ECHO) + goto done; + + type = ICMP_ECHO; + if (pctx->th_dep.icmp.type) + goto bad_proto; + + stmt = __payload_gen_icmp_echo_dependency(ctx, expr, + ICMP_ECHO, ICMP_ECHOREPLY, + &icmp_type_type, + desc); + break; + case PROTO_ICMP_MTU: + case PROTO_ICMP_ADDRESS: + type = icmp_dep_to_type(tmpl->icmp_dep); + if (pctx->th_dep.icmp.type == type) + goto done; + if (pctx->th_dep.icmp.type) + goto bad_proto; + stmt = __payload_gen_icmp_simple_dependency(ctx, expr, + &icmp_type_type, + desc, type); + break; + case PROTO_ICMP6_ECHO: + if (pctx->th_dep.icmp.type == ICMP6_ECHO_REQUEST || + pctx->th_dep.icmp.type == ICMP6_ECHO_REPLY) + goto done; + + type = ICMP6_ECHO_REQUEST; + if (pctx->th_dep.icmp.type) + goto bad_proto; + + stmt = __payload_gen_icmp_echo_dependency(ctx, expr, + ICMP6_ECHO_REQUEST, + ICMP6_ECHO_REPLY, + &icmp6_type_type, + desc); + break; + case PROTO_ICMP6_ADDRESS: + if (icmp_dep_type_match(PROTO_ICMP6_ADDRESS, + pctx->th_dep.icmp.type)) + goto done; + type = ND_NEIGHBOR_SOLICIT; + if (pctx->th_dep.icmp.type) + goto bad_proto; + stmt = __payload_gen_icmp6_addr_dependency(ctx, expr, desc); + break; + case PROTO_ICMP6_REDIRECT: + case PROTO_ICMP6_MTU: + case PROTO_ICMP6_MGMQ: + case PROTO_ICMP6_PPTR: + type = icmp_dep_to_type(tmpl->icmp_dep); + if (pctx->th_dep.icmp.type == type) + goto done; + if (pctx->th_dep.icmp.type) + goto bad_proto; + stmt = __payload_gen_icmp_simple_dependency(ctx, expr, + &icmp6_type_type, + desc, type); + break; + break; + default: + BUG("Unhandled icmp dependency code"); + } + + pctx->th_dep.icmp.type = type; + + if (stmt_evaluate(ctx, stmt) < 0) + return expr_error(ctx->msgs, expr, + "icmp dependency statement is invalid"); +done: + *res = stmt; + return 0; + +bad_proto: + return expr_error(ctx->msgs, expr, "incompatible icmp match: rule has %d, need %u", + pctx->th_dep.icmp.type, type); +} + +int payload_gen_inner_dependency(struct eval_ctx *ctx, const struct expr *expr, + struct stmt **res) +{ + struct proto_ctx *pctx = eval_proto_ctx(ctx); + const struct proto_hdr_template *tmpl; + const struct proto_desc *desc, *inner_desc; + struct expr *left, *right, *dep; + struct stmt *stmt = NULL; + int protocol; + + assert(expr->etype == EXPR_PAYLOAD); + + inner_desc = expr->payload.inner_desc; + desc = pctx->protocol[inner_desc->base - 1].desc; + if (desc == NULL) + desc = &proto_ip; + + tmpl = &inner_desc->templates[0]; + assert(tmpl); + + protocol = proto_find_num(desc, inner_desc); + if (protocol < 0) + return expr_error(ctx->msgs, expr, + "conflicting protocols specified: %s vs. %s", + desc->name, inner_desc->name); + + left = meta_expr_alloc(&expr->location, tmpl->meta_key); + + right = constant_expr_alloc(&expr->location, tmpl->dtype, + tmpl->dtype->byteorder, tmpl->len, + constant_data_ptr(protocol, tmpl->len)); + + dep = relational_expr_alloc(&expr->location, OP_EQ, left, right); + stmt = expr_stmt_alloc(&dep->location, dep); + + *res = stmt; + return 0; +} diff --git a/src/print.c b/src/print.c new file mode 100644 index 0000000..8aefa96 --- /dev/null +++ b/src/print.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Phil Sutter <phil@nwl.cc> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <stdarg.h> +#include <nftables.h> +#include <utils.h> + +int nft_print(struct output_ctx *octx, const char *fmt, ...) +{ + int ret; + va_list arg; + + va_start(arg, fmt); + ret = vfprintf(octx->output_fp, fmt, arg); + va_end(arg); + fflush(octx->output_fp); + + return ret; +} + +int nft_gmp_print(struct output_ctx *octx, const char *fmt, ...) +{ + int ret; + va_list arg; + + va_start(arg, fmt); + ret = gmp_vfprintf(octx->output_fp, fmt, arg); + va_end(arg); + fflush(octx->output_fp); + + return ret; +} diff --git a/src/proto.c b/src/proto.c new file mode 100644 index 0000000..553b6a4 --- /dev/null +++ b/src/proto.c @@ -0,0 +1,1320 @@ +/* + * Protocol header and type definitions and related functions. + * + * Copyright (c) 2014 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. + * + */ + +#include <nft.h> + +#include <stddef.h> +#include <net/if_arp.h> +#include <arpa/inet.h> +#include <linux/netfilter.h> + +#include <expression.h> +#include <headers.h> +#include <proto.h> +#include <gmputil.h> +#include <utils.h> + +const char *proto_base_names[] = { + [PROTO_BASE_INVALID] = "invalid", + [PROTO_BASE_LL_HDR] = "link layer", + [PROTO_BASE_NETWORK_HDR] = "network layer", + [PROTO_BASE_TRANSPORT_HDR] = "transport layer", + [PROTO_BASE_INNER_HDR] = "payload data", +}; + +const char *proto_base_tokens[] = { + [PROTO_BASE_INVALID] = "invalid", + [PROTO_BASE_LL_HDR] = "ll", + [PROTO_BASE_NETWORK_HDR] = "nh", + [PROTO_BASE_TRANSPORT_HDR] = "th", + [PROTO_BASE_INNER_HDR] = "ih", +}; + +const struct proto_hdr_template proto_unknown_template = + PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0); + +const struct proto_desc proto_unknown = { + .name = "unknown", + .base = PROTO_BASE_INVALID, +}; + +/** + * proto_find_upper - find higher layer protocol description by protocol value + * linking it to the lower layer protocol + * + * @base: lower layer protocol description + * @num: protocol value + */ +const struct proto_desc * +proto_find_upper(const struct proto_desc *base, unsigned int num) +{ + unsigned int i; + + for (i = 0; i < array_size(base->protocols); i++) { + if (!base->protocols[i].desc) + break; + if (base->protocols[i].num == num) + return base->protocols[i].desc; + } + return NULL; +} + +/** + * proto_find_num - return protocol number linking two protocols together + * + * @base: lower layer protocol description + * @desc: upper layer protocol description + */ +int proto_find_num(const struct proto_desc *base, + const struct proto_desc *desc) +{ + unsigned int i; + + for (i = 0; i < array_size(base->protocols); i++) { + if (!base->protocols[i].desc) + break; + if (base->protocols[i].desc == desc) + return base->protocols[i].num; + } + return -1; +} + +static const struct proto_desc *inner_protocols[] = { + &proto_vxlan, + &proto_geneve, + &proto_gre, + &proto_gretap, +}; + +const struct proto_desc *proto_find_inner(uint32_t type, uint32_t hdrsize, + uint32_t flags) +{ + const struct proto_desc *desc; + unsigned int i; + + for (i = 0; i < array_size(inner_protocols); i++) { + desc = inner_protocols[i]; + if (desc->inner.type == type && + desc->inner.hdrsize == hdrsize && + desc->inner.flags == flags) + return inner_protocols[i]; + } + + return &proto_unknown; +} + +static const struct dev_proto_desc dev_proto_desc[] = { + DEV_PROTO_DESC(ARPHRD_ETHER, &proto_eth), +}; + +/** + * proto_dev_type - return arphrd type linking a device and a protocol together + * + * @desc: the protocol description + * @res: pointer to result + */ +int proto_dev_type(const struct proto_desc *desc, uint16_t *res) +{ + const struct proto_desc *base; + unsigned int i, j; + + for (i = 0; i < array_size(dev_proto_desc); i++) { + base = dev_proto_desc[i].desc; + if (base == desc) { + *res = dev_proto_desc[i].type; + return 0; + } + for (j = 0; j < array_size(base->protocols); j++) { + if (!base->protocols[j].desc) + break; + if (base->protocols[j].desc == desc) { + *res = dev_proto_desc[i].type; + return 0; + } + } + } + return -1; +} + +/** + * proto_dev_desc - return protocol description for an arphrd type + * + * @type: the arphrd type + */ +const struct proto_desc *proto_dev_desc(uint16_t type) +{ + unsigned int i; + + for (i = 0; i < array_size(dev_proto_desc); i++) { + if (dev_proto_desc[i].type == type) + return dev_proto_desc[i].desc; + } + return NULL; +} + +const struct hook_proto_desc hook_proto_desc[] = { + [NFPROTO_BRIDGE] = HOOK_PROTO_DESC(PROTO_BASE_LL_HDR, &proto_eth), + [NFPROTO_NETDEV] = HOOK_PROTO_DESC(PROTO_BASE_LL_HDR, &proto_netdev), + [NFPROTO_INET] = HOOK_PROTO_DESC(PROTO_BASE_LL_HDR, &proto_inet), + [NFPROTO_IPV4] = HOOK_PROTO_DESC(PROTO_BASE_NETWORK_HDR, &proto_ip), + [NFPROTO_IPV6] = HOOK_PROTO_DESC(PROTO_BASE_NETWORK_HDR, &proto_ip6), + [NFPROTO_ARP] = HOOK_PROTO_DESC(PROTO_BASE_NETWORK_HDR, &proto_arp), +}; + +static void proto_ctx_debug(const struct proto_ctx *ctx, enum proto_bases base, + unsigned int debug_mask) +{ + unsigned int i; + + if (!(debug_mask & NFT_DEBUG_PROTO_CTX)) + return; + + if (base == PROTO_BASE_LL_HDR && ctx->stacked_ll_count) { + pr_debug(" saved ll headers:"); + for (i = 0; i < ctx->stacked_ll_count; i++) + pr_debug(" %s", ctx->stacked_ll[i]->name); + } + + pr_debug("update %s protocol context%s:\n", + proto_base_names[base], ctx->inner ? " (inner)" : ""); + + for (i = PROTO_BASE_LL_HDR; i <= PROTO_BASE_MAX; i++) { + pr_debug(" %-20s: %s", + proto_base_names[i], + ctx->protocol[i].desc ? ctx->protocol[i].desc->name : + "none"); + if (i == base) + pr_debug(" <-"); + pr_debug("\n"); + } + pr_debug("\n"); +} + +/** + * proto_ctx_init - initialize protocol context for a given hook family + * + * @ctx: protocol context + * @family: hook family + * @debug_mask: display debugging information + */ +void proto_ctx_init(struct proto_ctx *ctx, unsigned int family, + unsigned int debug_mask, bool inner) +{ + const struct hook_proto_desc *h = &hook_proto_desc[family]; + + memset(ctx, 0, sizeof(*ctx)); + ctx->family = family; + ctx->protocol[h->base].desc = h->desc; + ctx->debug_mask = debug_mask; + ctx->inner = inner; + + proto_ctx_debug(ctx, h->base, debug_mask); +} + +/** + * proto_ctx_update: update protocol context for given protocol base + * + * @ctx: protocol context + * @base: protocol base + * @loc: location of the relational expression definiting the context + * @desc: protocol description for the given layer + */ +void proto_ctx_update(struct proto_ctx *ctx, enum proto_bases base, + const struct location *loc, + const struct proto_desc *desc) +{ + bool found = false; + unsigned int i; + + switch (base) { + case PROTO_BASE_LL_HDR: + case PROTO_BASE_NETWORK_HDR: + break; + case PROTO_BASE_TRANSPORT_HDR: + if (ctx->protocol[base].num_protos >= PROTO_CTX_NUM_PROTOS) + break; + + for (i = 0; i < ctx->protocol[base].num_protos; i++) { + if (ctx->protocol[base].protos[i].desc == desc) { + found = true; + break; + } + } + if (!found) { + i = ctx->protocol[base].num_protos++; + ctx->protocol[base].protos[i].desc = desc; + ctx->protocol[base].protos[i].location = *loc; + } + break; + case PROTO_BASE_INNER_HDR: + break; + default: + BUG("unknown protocol base %d", base); + } + + ctx->protocol[base].location = *loc; + ctx->protocol[base].desc = desc; + + proto_ctx_debug(ctx, base, ctx->debug_mask); +} + +bool proto_ctx_is_ambiguous(struct proto_ctx *ctx, enum proto_bases base) +{ + return ctx->protocol[base].num_protos > 1; +} + +const struct proto_desc *proto_ctx_find_conflict(struct proto_ctx *ctx, + enum proto_bases base, + const struct proto_desc *desc) +{ + unsigned int i; + + switch (base) { + case PROTO_BASE_LL_HDR: + case PROTO_BASE_NETWORK_HDR: + if (desc != ctx->protocol[base].desc) + return ctx->protocol[base].desc; + break; + case PROTO_BASE_TRANSPORT_HDR: + for (i = 0; i < ctx->protocol[base].num_protos; i++) { + if (desc != ctx->protocol[base].protos[i].desc) + return ctx->protocol[base].protos[i].desc; + } + break; + default: + BUG("unknown protocol base %d", base); + } + + return NULL; +} + +#define HDR_TEMPLATE(__name, __dtype, __type, __member) \ + PROTO_HDR_TEMPLATE(__name, __dtype, \ + BYTEORDER_BIG_ENDIAN, \ + offsetof(__type, __member) * 8, \ + field_sizeof(__type, __member) * 8) + +#define HDR_FIELD(__name, __struct, __member) \ + HDR_TEMPLATE(__name, &integer_type, __struct, __member) +#define HDR_HEX_FIELD(__name, __struct, __member) \ + HDR_TEMPLATE(__name, &xinteger_type, __struct, __member) +#define HDR_BITFIELD(__name, __dtype, __offset, __len) \ + PROTO_HDR_TEMPLATE(__name, __dtype, BYTEORDER_BIG_ENDIAN, \ + __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 proto_desc proto_ah = { + .name = "ah", + .id = PROTO_DESC_AH, + .base = PROTO_BASE_TRANSPORT_HDR, + .protocol_key = AHHDR_NEXTHDR, + .protocols = { + PROTO_LINK(IPPROTO_ESP, &proto_esp), + PROTO_LINK(IPPROTO_AH, &proto_ah), + PROTO_LINK(IPPROTO_COMP, &proto_comp), + PROTO_LINK(IPPROTO_UDP, &proto_udp), + PROTO_LINK(IPPROTO_UDPLITE, &proto_udplite), + PROTO_LINK(IPPROTO_TCP, &proto_tcp), + PROTO_LINK(IPPROTO_DCCP, &proto_dccp), + PROTO_LINK(IPPROTO_SCTP, &proto_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), + }, + .format = { + .order = { + AHHDR_SPI, AHHDR_HDRLENGTH, AHHDR_NEXTHDR, + }, + .filter = (1 << AHHDR_RESERVED) | (1 << AHHDR_SEQUENCE) + }, +}; + +/* + * ESP + */ + +#define ESPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct ip_esp_hdr, __member) + +const struct proto_desc proto_esp = { + .name = "esp", + .id = PROTO_DESC_ESP, + .base = PROTO_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 proto_desc proto_comp = { + .name = "comp", + .id = PROTO_DESC_COMP, + .base = PROTO_BASE_TRANSPORT_HDR, + .protocol_key = COMPHDR_NEXTHDR, + .protocols = { + PROTO_LINK(IPPROTO_ESP, &proto_esp), + PROTO_LINK(IPPROTO_AH, &proto_ah), + PROTO_LINK(IPPROTO_COMP, &proto_comp), + PROTO_LINK(IPPROTO_UDP, &proto_udp), + PROTO_LINK(IPPROTO_UDPLITE, &proto_udplite), + PROTO_LINK(IPPROTO_TCP, &proto_tcp), + PROTO_LINK(IPPROTO_DCCP, &proto_dccp), + PROTO_LINK(IPPROTO_SCTP, &proto_sctp), + }, + .templates = { + [COMPHDR_NEXTHDR] = INET_PROTOCOL("nexthdr", struct ip_comp_hdr, nexthdr), + [COMPHDR_FLAGS] = HDR_TEMPLATE("flags", &bitmask_type, struct ip_comp_hdr, flags), + [COMPHDR_CPI] = COMPHDR_FIELD("cpi", cpi), + }, +}; + +/* + * ICMP + */ + +#include <netinet/ip_icmp.h> + +static const struct symbol_table icmp_type_tbl = { + .base = BASE_DECIMAL, + .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("router-advertisement", ICMP_ROUTERADVERT), + SYMBOL("router-solicitation", ICMP_ROUTERSOLICIT), + 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 + }, +}; + +const struct datatype icmp_type_type = { + .type = TYPE_ICMP_TYPE, + .name = "icmp_type", + .desc = "ICMP type", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &icmp_type_tbl, +}; + +#define ICMP46HDR_FIELD(__token, __dtype, __struct, __member, __dep) \ + { \ + .token = (__token), \ + .dtype = &__dtype, \ + .byteorder = BYTEORDER_BIG_ENDIAN, \ + .offset = offsetof(__struct, __member) * 8, \ + .len = field_sizeof(__struct, __member) * 8, \ + .icmp_dep = (__dep), \ + } + +#define ICMPHDR_FIELD(__token, __member, __dep) \ + ICMP46HDR_FIELD(__token, integer_type, struct icmphdr, __member, __dep) + +#define ICMPHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct icmphdr, __member) + +const struct proto_desc proto_icmp = { + .name = "icmp", + .id = PROTO_DESC_ICMP, + .base = PROTO_BASE_TRANSPORT_HDR, + .protocol_key = ICMPHDR_TYPE, + .checksum_key = ICMPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .templates = { + [ICMPHDR_TYPE] = ICMPHDR_TYPE("type", &icmp_type_type, type), + [ICMPHDR_CODE] = ICMPHDR_TYPE("code", &icmp_code_type, code), + [ICMPHDR_CHECKSUM] = ICMPHDR_FIELD("checksum", checksum, PROTO_ICMP_ANY), + [ICMPHDR_ID] = ICMPHDR_FIELD("id", un.echo.id, PROTO_ICMP_ECHO), + [ICMPHDR_SEQ] = ICMPHDR_FIELD("sequence", un.echo.sequence, PROTO_ICMP_ECHO), + [ICMPHDR_GATEWAY] = ICMPHDR_FIELD("gateway", un.gateway, PROTO_ICMP_ADDRESS), + [ICMPHDR_MTU] = ICMPHDR_FIELD("mtu", un.frag.mtu, PROTO_ICMP_MTU), + }, +}; + +/* + * IGMP + */ + +#include <netinet/igmp.h> + +#ifndef IGMP_V3_MEMBERSHIP_REPORT +#define IGMP_V3_MEMBERSHIP_REPORT 0x22 +#endif + +static const struct symbol_table igmp_type_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("membership-query", IGMP_MEMBERSHIP_QUERY), + SYMBOL("membership-report-v1", IGMP_V1_MEMBERSHIP_REPORT), + SYMBOL("membership-report-v2", IGMP_V2_MEMBERSHIP_REPORT), + SYMBOL("membership-report-v3", IGMP_V3_MEMBERSHIP_REPORT), + SYMBOL("leave-group", IGMP_V2_LEAVE_GROUP), + SYMBOL_LIST_END + }, +}; + +const struct datatype igmp_type_type = { + .type = TYPE_IGMP_TYPE, + .name = "igmp_type", + .desc = "IGMP type", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &igmp_type_tbl, +}; + +#define IGMPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct igmp, __member) +#define IGMPHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct igmp, __member) + +const struct proto_desc proto_igmp = { + .name = "igmp", + .id = PROTO_DESC_IGMP, + .base = PROTO_BASE_TRANSPORT_HDR, + .checksum_key = IGMPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .templates = { + [IGMPHDR_TYPE] = IGMPHDR_TYPE("type", &igmp_type_type, igmp_type), + [IGMPHDR_MRT] = IGMPHDR_FIELD("mrt", igmp_code), + [IGMPHDR_CHECKSUM] = IGMPHDR_FIELD("checksum", igmp_cksum), + [IGMPHDR_GROUP] = IGMPHDR_FIELD("group", igmp_group), + }, +}; + +/* + * UDP/UDP-Lite + */ + +#include <netinet/udp.h> +#define UDPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct udphdr, __member) + +const struct proto_desc proto_udp = { + .name = "udp", + .id = PROTO_DESC_UDP, + .base = PROTO_BASE_TRANSPORT_HDR, + .checksum_key = UDPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .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), + }, + .protocols = { + PROTO_LINK(0, &proto_vxlan), + PROTO_LINK(0, &proto_geneve), + }, +}; + +const struct proto_desc proto_udplite = { + .name = "udplite", + .id = PROTO_DESC_UDPLITE, + .base = PROTO_BASE_TRANSPORT_HDR, + .templates = { + [UDPHDR_SPORT] = INET_SERVICE("sport", struct udphdr, source), + [UDPHDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest), + [UDPHDR_CSUMCOV] = UDPHDR_FIELD("csumcov", len), + [UDPHDR_CHECKSUM] = UDPHDR_FIELD("checksum", check), + }, +}; + +/* + * TCP + */ + +#include <netinet/tcp.h> + +static const struct symbol_table tcp_flag_tbl = { + .base = BASE_HEXADECIMAL, + .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("urg", TCP_FLAG_URG), + SYMBOL("ecn", TCP_FLAG_ECN), + SYMBOL("cwr", TCP_FLAG_CWR), + SYMBOL_LIST_END + }, +}; + +const struct datatype tcp_flag_type = { + .type = TYPE_TCP_FLAG, + .name = "tcp_flag", + .desc = "TCP flag", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &bitmask_type, + .sym_tbl = &tcp_flag_tbl, +}; + +#define TCPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct tcphdr, __member) + +const struct proto_desc proto_tcp = { + .name = "tcp", + .id = PROTO_DESC_TCP, + .base = PROTO_BASE_TRANSPORT_HDR, + .checksum_key = TCPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .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] = HDR_BITFIELD("doff", &integer_type, + (12 * BITS_PER_BYTE), 4), + [TCPHDR_RESERVED] = HDR_BITFIELD("reserved", &integer_type, + (12 * BITS_PER_BYTE) + 4, 4), + [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), + }, + .format = { + .filter = (1 << TCPHDR_SEQ) | (1 << TCPHDR_ACKSEQ) | + (1 << TCPHDR_DOFF) | (1 << TCPHDR_RESERVED) | + (1 << TCPHDR_URGPTR), + }, +}; + +/* + * DCCP + */ + +static const struct symbol_table dccp_pkttype_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("request", DCCP_PKT_REQUEST), + SYMBOL("response", DCCP_PKT_RESPONSE), + SYMBOL("data", DCCP_PKT_DATA), + SYMBOL("ack", DCCP_PKT_ACK), + SYMBOL("dataack", DCCP_PKT_DATAACK), + SYMBOL("closereq", DCCP_PKT_CLOSEREQ), + SYMBOL("close", DCCP_PKT_CLOSE), + SYMBOL("reset", DCCP_PKT_RESET), + SYMBOL("sync", DCCP_PKT_SYNC), + SYMBOL("syncack", DCCP_PKT_SYNCACK), + SYMBOL_LIST_END + }, +}; + +const struct datatype dccp_pkttype_type = { + .type = TYPE_DCCP_PKTTYPE, + .name = "dccp_pkttype", + .desc = "DCCP packet type", + .byteorder = BYTEORDER_INVALID, + .size = 4, + .basetype = &integer_type, + .sym_tbl = &dccp_pkttype_tbl, +}; + + +#define DCCPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct dccp_hdr, __member) + +const struct proto_desc proto_dccp = { + .name = "dccp", + .id = PROTO_DESC_DCCP, + .base = PROTO_BASE_TRANSPORT_HDR, + .templates = { + [DCCPHDR_SPORT] = INET_SERVICE("sport", struct dccp_hdr, dccph_sport), + [DCCPHDR_DPORT] = INET_SERVICE("dport", struct dccp_hdr, dccph_dport), + [DCCPHDR_TYPE] = HDR_BITFIELD("type", &dccp_pkttype_type, + (8 * BITS_PER_BYTE) + 3, 4), + }, +}; + +/* + * SCTP + */ + +#define SCTPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct sctphdr, __member) + +const struct proto_desc proto_sctp = { + .name = "sctp", + .id = PROTO_DESC_SCTP, + .base = PROTO_BASE_TRANSPORT_HDR, + .checksum_key = SCTPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_SCTP, + .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), + }, +}; + +/* + * Dummy Transpor Header (common udp/tcp/dccp/sctp fields) + */ +const struct proto_desc proto_th = { + .name = "th", + .id = PROTO_DESC_TH, + .base = PROTO_BASE_TRANSPORT_HDR, + .templates = { + [THDR_SPORT] = INET_SERVICE("sport", struct udphdr, source), + [THDR_DPORT] = INET_SERVICE("dport", struct udphdr, dest), + }, +}; + +/* + * IPv4 + */ + +#include <netinet/ip.h> + +static const struct symbol_table dscp_type_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("cs0", 0x00), + SYMBOL("cs1", 0x08), + SYMBOL("cs2", 0x10), + SYMBOL("cs3", 0x18), + SYMBOL("cs4", 0x20), + SYMBOL("cs5", 0x28), + SYMBOL("cs6", 0x30), + SYMBOL("cs7", 0x38), + SYMBOL("df", 0x00), + SYMBOL("be", 0x00), + SYMBOL("lephb", 0x01), + SYMBOL("af11", 0x0a), + SYMBOL("af12", 0x0c), + SYMBOL("af13", 0x0e), + SYMBOL("af21", 0x12), + SYMBOL("af22", 0x14), + SYMBOL("af23", 0x16), + SYMBOL("af31", 0x1a), + SYMBOL("af32", 0x1c), + SYMBOL("af33", 0x1e), + SYMBOL("af41", 0x22), + SYMBOL("af42", 0x24), + SYMBOL("af43", 0x26), + SYMBOL("va", 0x2c), + SYMBOL("ef", 0x2e), + SYMBOL_LIST_END + }, +}; + +const struct datatype dscp_type = { + .type = TYPE_DSCP, + .name = "dscp", + .desc = "Differentiated Services Code Point", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 6, + .basetype = &integer_type, + .basefmt = "0x%.2Zx", + .sym_tbl = &dscp_type_tbl, +}; + +static const struct symbol_table ecn_type_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("not-ect", 0x00), + SYMBOL("ect1", 0x01), + SYMBOL("ect0", 0x02), + SYMBOL("ce", 0x03), + SYMBOL_LIST_END + }, +}; + +const struct datatype ecn_type = { + .type = TYPE_ECN, + .name = "ecn", + .desc = "Explicit Congestion Notification", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 2, + .basetype = &integer_type, + .basefmt = "0x%.1Zx", + .sym_tbl = &ecn_type_tbl, +}; + +#define GREHDR_TEMPLATE(__name, __dtype, __member) \ + HDR_TEMPLATE(__name, __dtype, struct grehdr, __member) +#define GREHDR_TYPE(__name, __member) \ + GREHDR_TEMPLATE(__name, ðertype_type, __member) + +const struct proto_desc proto_gre = { + .name = "gre", + .id = PROTO_DESC_GRE, + .base = PROTO_BASE_TRANSPORT_HDR, + .templates = { + [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8), + [GREHDR_FLAGS] = HDR_BITFIELD("flags", &integer_type, 0, 5), + [GREHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 13, 3), + [GREHDR_PROTOCOL] = HDR_BITFIELD("protocol", ðertype_type, 16, 16), + }, + .inner = { + .hdrsize = sizeof(struct grehdr), + .flags = NFT_INNER_NH | NFT_INNER_TH, + .type = NFT_INNER_GENEVE + 1, + }, +}; + +const struct proto_desc proto_gretap = { + .name = "gretap", + .id = PROTO_DESC_GRETAP, + .base = PROTO_BASE_TRANSPORT_HDR, + .templates = { + [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8), + }, + .inner = { + .hdrsize = sizeof(struct grehdr), + .flags = NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH, + .type = NFT_INNER_GENEVE + 2, + }, +}; + +#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 proto_desc proto_ip = { + .name = "ip", + .id = PROTO_DESC_IP, + .base = PROTO_BASE_NETWORK_HDR, + .checksum_key = IPHDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .protocols = { + PROTO_LINK(IPPROTO_ICMP, &proto_icmp), + PROTO_LINK(IPPROTO_IGMP, &proto_igmp), + PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6), + PROTO_LINK(IPPROTO_ESP, &proto_esp), + PROTO_LINK(IPPROTO_AH, &proto_ah), + PROTO_LINK(IPPROTO_COMP, &proto_comp), + PROTO_LINK(IPPROTO_UDP, &proto_udp), + PROTO_LINK(IPPROTO_UDPLITE, &proto_udplite), + PROTO_LINK(IPPROTO_TCP, &proto_tcp), + PROTO_LINK(IPPROTO_DCCP, &proto_dccp), + PROTO_LINK(IPPROTO_SCTP, &proto_sctp), + PROTO_LINK(IPPROTO_GRE, &proto_gre), + PROTO_LINK(IPPROTO_GRE, &proto_gretap), + }, + .templates = { + [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8), + [IPHDR_VERSION] = HDR_BITFIELD("version", &integer_type, 0, 4), + [IPHDR_HDRLENGTH] = HDR_BITFIELD("hdrlength", &integer_type, 4, 4), + [IPHDR_DSCP] = HDR_BITFIELD("dscp", &dscp_type, 8, 6), + [IPHDR_ECN] = HDR_BITFIELD("ecn", &ecn_type, 14, 2), + [IPHDR_LENGTH] = IPHDR_FIELD("length", tot_len), + [IPHDR_ID] = IPHDR_FIELD("id", id), + [IPHDR_FRAG_OFF] = HDR_HEX_FIELD("frag-off", struct iphdr, 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), + }, + .format = { + .order = { + IPHDR_SADDR, IPHDR_DADDR, IPHDR_DSCP, IPHDR_ECN, + IPHDR_TTL, IPHDR_ID, IPHDR_PROTOCOL, IPHDR_LENGTH, + }, + .filter = (1 << IPHDR_VERSION) | (1 << IPHDR_HDRLENGTH) | + (1 << IPHDR_FRAG_OFF), + }, + .pseudohdr = { + IPHDR_SADDR, IPHDR_DADDR, IPHDR_PROTOCOL, IPHDR_LENGTH, + }, +}; + +/* + * ICMPv6 + */ + +#include <netinet/icmp6.h> + +#define IND_NEIGHBOR_SOLICIT 141 +#define IND_NEIGHBOR_ADVERT 142 +#define ICMPV6_MLD2_REPORT 143 + +static const struct symbol_table icmp6_type_tbl = { + .base = BASE_DECIMAL, + .symbols = { + SYMBOL("destination-unreachable", ICMP6_DST_UNREACH), + SYMBOL("packet-too-big", ICMP6_PACKET_TOO_BIG), + SYMBOL("time-exceeded", ICMP6_TIME_EXCEEDED), + SYMBOL("parameter-problem", ICMP6_PARAM_PROB), + SYMBOL("echo-request", ICMP6_ECHO_REQUEST), + SYMBOL("echo-reply", ICMP6_ECHO_REPLY), + SYMBOL("mld-listener-query", MLD_LISTENER_QUERY), + SYMBOL("mld-listener-report", MLD_LISTENER_REPORT), + SYMBOL("mld-listener-done", MLD_LISTENER_REDUCTION), + SYMBOL("mld-listener-reduction", MLD_LISTENER_REDUCTION), + SYMBOL("nd-router-solicit", ND_ROUTER_SOLICIT), + SYMBOL("nd-router-advert", ND_ROUTER_ADVERT), + SYMBOL("nd-neighbor-solicit", ND_NEIGHBOR_SOLICIT), + SYMBOL("nd-neighbor-advert", ND_NEIGHBOR_ADVERT), + SYMBOL("nd-redirect", ND_REDIRECT), + SYMBOL("router-renumbering", ICMP6_ROUTER_RENUMBERING), + SYMBOL("ind-neighbor-solicit", IND_NEIGHBOR_SOLICIT), + SYMBOL("ind-neighbor-advert", IND_NEIGHBOR_ADVERT), + SYMBOL("mld2-listener-report", ICMPV6_MLD2_REPORT), + SYMBOL_LIST_END + }, +}; + +const struct datatype icmp6_type_type = { + .type = TYPE_ICMP6_TYPE, + .name = "icmpv6_type", + .desc = "ICMPv6 type", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &icmp6_type_tbl, +}; + +#define ICMP6HDR_FIELD(__token, __member, __dep) \ + ICMP46HDR_FIELD(__token, integer_type, struct icmp6_hdr, __member, __dep) +#define ICMP6HDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct icmp6_hdr, __member) + +const struct proto_desc proto_icmp6 = { + .name = "icmpv6", + .id = PROTO_DESC_ICMPV6, + .base = PROTO_BASE_TRANSPORT_HDR, + .protocol_key = ICMP6HDR_TYPE, + .checksum_key = ICMP6HDR_CHECKSUM, + .checksum_type = NFT_PAYLOAD_CSUM_INET, + .templates = { + [ICMP6HDR_TYPE] = ICMP6HDR_TYPE("type", &icmp6_type_type, icmp6_type), + [ICMP6HDR_CODE] = ICMP6HDR_TYPE("code", &icmpv6_code_type, icmp6_code), + [ICMP6HDR_CHECKSUM] = ICMP6HDR_FIELD("checksum", icmp6_cksum, PROTO_ICMP_ANY), + [ICMP6HDR_PPTR] = ICMP6HDR_FIELD("parameter-problem", icmp6_pptr, PROTO_ICMP6_PPTR), + [ICMP6HDR_MTU] = ICMP6HDR_FIELD("mtu", icmp6_mtu, PROTO_ICMP6_MTU), + [ICMP6HDR_ID] = ICMP6HDR_FIELD("id", icmp6_id, PROTO_ICMP6_ECHO), + [ICMP6HDR_SEQ] = ICMP6HDR_FIELD("sequence", icmp6_seq, PROTO_ICMP6_ECHO), + [ICMP6HDR_MAXDELAY] = ICMP6HDR_FIELD("max-delay", icmp6_maxdelay, PROTO_ICMP6_MGMQ), + [ICMP6HDR_TADDR] = ICMP46HDR_FIELD("taddr", ip6addr_type, + struct nd_neighbor_solicit, nd_ns_target, + PROTO_ICMP6_ADDRESS), + [ICMP6HDR_DADDR] = ICMP46HDR_FIELD("daddr", ip6addr_type, + struct nd_redirect, nd_rd_dst, + PROTO_ICMP6_REDIRECT), + }, +}; + +/* + * 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 proto_desc proto_ip6 = { + .name = "ip6", + .id = PROTO_DESC_IP6, + .base = PROTO_BASE_NETWORK_HDR, + .protocols = { + PROTO_LINK(IPPROTO_ESP, &proto_esp), + PROTO_LINK(IPPROTO_AH, &proto_ah), + PROTO_LINK(IPPROTO_COMP, &proto_comp), + PROTO_LINK(IPPROTO_UDP, &proto_udp), + PROTO_LINK(IPPROTO_UDPLITE, &proto_udplite), + PROTO_LINK(IPPROTO_TCP, &proto_tcp), + PROTO_LINK(IPPROTO_DCCP, &proto_dccp), + PROTO_LINK(IPPROTO_SCTP, &proto_sctp), + PROTO_LINK(IPPROTO_ICMP, &proto_icmp), + PROTO_LINK(IPPROTO_IGMP, &proto_igmp), + PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6), + PROTO_LINK(IPPROTO_GRE, &proto_gre), + PROTO_LINK(IPPROTO_GRE, &proto_gretap), + }, + .templates = { + [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8), + [IP6HDR_VERSION] = HDR_BITFIELD("version", &integer_type, 0, 4), + [IP6HDR_DSCP] = HDR_BITFIELD("dscp", &dscp_type, 4, 6), + [IP6HDR_ECN] = HDR_BITFIELD("ecn", &ecn_type, 10, 2), + [IP6HDR_FLOWLABEL] = HDR_BITFIELD("flowlabel", &integer_type, 12, 20), + [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), + }, + .format = { + .order = { + IP6HDR_SADDR, IP6HDR_DADDR, IP6HDR_DSCP, IP6HDR_ECN, + IP6HDR_HOPLIMIT, IP6HDR_FLOWLABEL, IP6HDR_NEXTHDR, + IP6HDR_LENGTH, + }, + .filter = (1 << IP6HDR_VERSION), + }, + .pseudohdr = { + IP6HDR_SADDR, IP6HDR_DADDR, IP6HDR_NEXTHDR, IP6HDR_LENGTH, + }, +}; + +/* + * Dummy protocol for mixed IPv4/IPv6 tables. The protocol is set at the link + * layer header, the upper layer protocols are IPv4 and IPv6. + */ + +const struct proto_desc proto_inet = { + .name = "inet", + .base = PROTO_BASE_LL_HDR, + .protocols = { + PROTO_LINK(NFPROTO_IPV4, &proto_ip), + PROTO_LINK(NFPROTO_IPV6, &proto_ip6), + }, + .templates = { + [0] = PROTO_META_TEMPLATE("nfproto", &nfproto_type, NFT_META_NFPROTO, 8), + }, +}; + +/* + * Dummy protocol for cases where the network layer protocol isn't known + * (IPv4 or IPv6), The higher layer protocols are the protocols common to + * both. + */ + +const struct proto_desc proto_inet_service = { + .name = "inet-service", + .base = PROTO_BASE_TRANSPORT_HDR, + .protocol_key = 0, + .protocols = { + PROTO_LINK(IPPROTO_ESP, &proto_esp), + PROTO_LINK(IPPROTO_AH, &proto_ah), + PROTO_LINK(IPPROTO_COMP, &proto_comp), + PROTO_LINK(IPPROTO_UDP, &proto_udp), + PROTO_LINK(IPPROTO_UDPLITE, &proto_udplite), + PROTO_LINK(IPPROTO_TCP, &proto_tcp), + PROTO_LINK(IPPROTO_DCCP, &proto_dccp), + PROTO_LINK(IPPROTO_SCTP, &proto_sctp), + PROTO_LINK(IPPROTO_ICMP, &proto_icmp), + PROTO_LINK(IPPROTO_IGMP, &proto_igmp), + PROTO_LINK(IPPROTO_ICMPV6, &proto_icmp6), + PROTO_LINK(IPPROTO_GRE, &proto_gre), + PROTO_LINK(IPPROTO_GRE, &proto_gretap), + }, + .templates = { + [0] = PROTO_META_TEMPLATE("l4proto", &inet_protocol_type, NFT_META_L4PROTO, 8), + }, +}; + +/* + * ARP + */ + +#include <net/if_arp.h> + +static const struct symbol_table arpop_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("request", __constant_htons(ARPOP_REQUEST)), + SYMBOL("reply", __constant_htons(ARPOP_REPLY)), + SYMBOL("rrequest", __constant_htons(ARPOP_RREQUEST)), + SYMBOL("rreply", __constant_htons(ARPOP_RREPLY)), + SYMBOL("inrequest", __constant_htons(ARPOP_InREQUEST)), + SYMBOL("inreply", __constant_htons(ARPOP_InREPLY)), + SYMBOL("nak", __constant_htons(ARPOP_NAK)), + SYMBOL_LIST_END + }, +}; + +const struct datatype arpop_type = { + .type = TYPE_ARPOP, + .name = "arp_op", + .desc = "ARP operation", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .basetype = &integer_type, + .sym_tbl = &arpop_tbl, +}; + +#define ARPHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct arp_hdr, __member) +#define ARPHDR_FIELD(__name, __member) \ + HDR_FIELD(__name, struct arp_hdr, __member) + +const struct proto_desc proto_arp = { + .name = "arp", + .id = PROTO_DESC_ARP, + .base = PROTO_BASE_NETWORK_HDR, + .templates = { + [ARPHDR_HRD] = ARPHDR_FIELD("htype", htype), + [ARPHDR_PRO] = ARPHDR_TYPE("ptype", ðertype_type, ptype), + [ARPHDR_HLN] = ARPHDR_FIELD("hlen", hlen), + [ARPHDR_PLN] = ARPHDR_FIELD("plen", plen), + [ARPHDR_OP] = ARPHDR_TYPE("operation", &arpop_type, oper), + [ARPHDR_SADDR_ETHER] = ARPHDR_TYPE("saddr ether", ðeraddr_type, sha), + [ARPHDR_SADDR_IP] = ARPHDR_TYPE("saddr ip", &ipaddr_type, spa), + [ARPHDR_DADDR_ETHER] = ARPHDR_TYPE("daddr ether", ðeraddr_type, tha), + [ARPHDR_DADDR_IP] = ARPHDR_TYPE("daddr ip", &ipaddr_type, tpa), + }, + .format = { + .filter = (1 << ARPHDR_HRD) | (1 << ARPHDR_PRO) | + (1 << ARPHDR_HLN) | (1 << ARPHDR_PLN) | + (1 << ARPHDR_SADDR_ETHER) | (1 << ARPHDR_DADDR_ETHER) | + (1 << ARPHDR_SADDR_IP) | (1 << ARPHDR_DADDR_IP), + }, +}; + +/* + * VLAN + */ + +#include <net/ethernet.h> + +#define VLANHDR_BITFIELD(__name, __offset, __len) \ + HDR_BITFIELD(__name, &integer_type, __offset, __len) +#define VLANHDR_TYPE(__name, __type, __member) \ + HDR_TYPE(__name, __type, struct vlan_hdr, __member) + +const struct proto_desc proto_vlan = { + .name = "vlan", + .id = PROTO_DESC_VLAN, + .base = PROTO_BASE_LL_HDR, + .protocol_key = VLANHDR_TYPE, + .length = sizeof(struct vlan_hdr) * BITS_PER_BYTE, + .protocols = { + PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip), + PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp), + PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6), + PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan), + PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan), + + }, + .templates = { + [VLANHDR_PCP] = VLANHDR_BITFIELD("pcp", 0, 3), + [VLANHDR_DEI] = VLANHDR_BITFIELD("dei", 3, 1), + [VLANHDR_CFI] = VLANHDR_BITFIELD("cfi", 3, 1), + [VLANHDR_VID] = VLANHDR_BITFIELD("id", 4, 12), + [VLANHDR_TYPE] = VLANHDR_TYPE("type", ðertype_type, vlan_type), + }, +}; + +/* + * Ethernet + */ + +const struct datatype etheraddr_type = { + .type = TYPE_ETHERADDR, + .name = "ether_addr", + .desc = "Ethernet address", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = ETH_ALEN * BITS_PER_BYTE, + .basetype = &lladdr_type, +}; + +static const struct symbol_table ethertype_tbl = { + .base = BASE_HEXADECIMAL, + .symbols = { + SYMBOL("ip", __constant_htons(ETH_P_IP)), + SYMBOL("arp", __constant_htons(ETH_P_ARP)), + SYMBOL("ip6", __constant_htons(ETH_P_IPV6)), + SYMBOL("8021q", __constant_htons(ETH_P_8021Q)), + SYMBOL("8021ad", __constant_htons(ETH_P_8021AD)), + + /* for compatibility with older versions */ + SYMBOL("vlan", __constant_htons(ETH_P_8021Q)), + SYMBOL_LIST_END + }, +}; + +static void ethertype_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(ðertype_tbl, expr, false, octx); +} + +const struct datatype ethertype_type = { + .type = TYPE_ETHERTYPE, + .name = "ether_type", + .desc = "Ethernet protocol", + .byteorder = BYTEORDER_BIG_ENDIAN, + .size = 2 * BITS_PER_BYTE, + .basetype = &integer_type, + .basefmt = "0x%.4Zx", + .print = ethertype_print, + .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) \ + PROTO_HDR_TEMPLATE(__name, ðeraddr_type, \ + BYTEORDER_BIG_ENDIAN, \ + offsetof(struct ether_header, __member) * 8, \ + field_sizeof(struct ether_header, __member) * 8) + +const struct proto_desc proto_eth = { + .name = "ether", + .id = PROTO_DESC_ETHER, + .base = PROTO_BASE_LL_HDR, + .protocol_key = ETHHDR_TYPE, + .length = sizeof(struct ether_header) * BITS_PER_BYTE, + .protocols = { + PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip), + PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp), + PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6), + PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan), + PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan), + }, + .templates = { + [ETHHDR_DADDR] = ETHHDR_ADDR("daddr", ether_dhost), + [ETHHDR_SADDR] = ETHHDR_ADDR("saddr", ether_shost), + [ETHHDR_TYPE] = ETHHDR_TYPE("type", ether_type), + }, + .format = { + .order = { + ETHHDR_SADDR, ETHHDR_DADDR, ETHHDR_TYPE, + }, + }, + +}; + +/* + * VXLAN + */ + +const struct proto_desc proto_vxlan = { + .name = "vxlan", + .id = PROTO_DESC_VXLAN, + .base = PROTO_BASE_INNER_HDR, + .templates = { + [VXLANHDR_FLAGS] = HDR_BITFIELD("flags", &bitmask_type, 0, 8), + [VXLANHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24), + }, + .protocols = { + PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip), + PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp), + PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6), + PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan), + }, + .inner = { + .hdrsize = sizeof(struct vxlanhdr), + .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH, + .type = NFT_INNER_VXLAN, + }, +}; + +/* + * GENEVE + */ + +const struct proto_desc proto_geneve = { + .name = "geneve", + .id = PROTO_DESC_GENEVE, + .base = PROTO_BASE_INNER_HDR, + .templates = { + [GNVHDR_TYPE] = HDR_TYPE("type", ðertype_type, struct gnvhdr, type), + [GNVHDR_VNI] = HDR_BITFIELD("vni", &integer_type, (4 * BITS_PER_BYTE), 24), + }, + .protocols = { + PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip), + PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp), + PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6), + PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan), + }, + .inner = { + .hdrsize = sizeof(struct gnvhdr), + .flags = NFT_INNER_HDRSIZE | NFT_INNER_LL | NFT_INNER_NH | NFT_INNER_TH, + .type = NFT_INNER_GENEVE, + }, +}; + + +/* + * Dummy protocol for netdev tables. + */ +const struct proto_desc proto_netdev = { + .name = "netdev", + .base = PROTO_BASE_LL_HDR, + .protocols = { + PROTO_LINK(__constant_htons(ETH_P_IP), &proto_ip), + PROTO_LINK(__constant_htons(ETH_P_ARP), &proto_arp), + PROTO_LINK(__constant_htons(ETH_P_IPV6), &proto_ip6), + PROTO_LINK(__constant_htons(ETH_P_8021Q), &proto_vlan), + PROTO_LINK(__constant_htons(ETH_P_8021AD), &proto_vlan), + }, + .templates = { + [0] = PROTO_META_TEMPLATE("protocol", ðertype_type, NFT_META_PROTOCOL, 16), + }, +}; + +static const struct proto_desc *const proto_definitions[PROTO_DESC_MAX + 1] = { + [PROTO_DESC_AH] = &proto_ah, + [PROTO_DESC_ESP] = &proto_esp, + [PROTO_DESC_COMP] = &proto_comp, + [PROTO_DESC_ICMP] = &proto_icmp, + [PROTO_DESC_IGMP] = &proto_igmp, + [PROTO_DESC_UDP] = &proto_udp, + [PROTO_DESC_UDPLITE] = &proto_udplite, + [PROTO_DESC_TCP] = &proto_tcp, + [PROTO_DESC_DCCP] = &proto_dccp, + [PROTO_DESC_SCTP] = &proto_sctp, + [PROTO_DESC_TH] = &proto_th, + [PROTO_DESC_IP] = &proto_ip, + [PROTO_DESC_IP6] = &proto_ip6, + [PROTO_DESC_ICMPV6] = &proto_icmp6, + [PROTO_DESC_ARP] = &proto_arp, + [PROTO_DESC_VLAN] = &proto_vlan, + [PROTO_DESC_ETHER] = &proto_eth, + [PROTO_DESC_VXLAN] = &proto_vxlan, + [PROTO_DESC_GENEVE] = &proto_geneve, + [PROTO_DESC_GRE] = &proto_gre, + [PROTO_DESC_GRETAP] = &proto_gretap, +}; + +const struct proto_desc *proto_find_desc(enum proto_desc_id desc_id) +{ + if (desc_id >= PROTO_DESC_UNKNOWN && + desc_id <= PROTO_DESC_MAX) + return proto_definitions[desc_id]; + + return NULL; +} diff --git a/src/rt.c b/src/rt.c new file mode 100644 index 0000000..f5c8055 --- /dev/null +++ b/src/rt.c @@ -0,0 +1,211 @@ +/* + * Routing expression related definition and types. + * + * Copyright (c) 2016 Anders K. Pedersen <akp@cohaesio.com> + * + * 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. + */ + +#include <nft.h> + +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <linux/netfilter.h> + +#include <nftables.h> +#include <expression.h> +#include <datatype.h> +#include <rt.h> +#include <rule.h> +#include <json.h> + +void realm_table_rt_init(struct nft_ctx *ctx) +{ + ctx->output.tbl.realm = rt_symbol_table_init("/etc/iproute2/rt_realms"); +} + +void realm_table_rt_exit(struct nft_ctx *ctx) +{ + rt_symbol_table_free(ctx->output.tbl.realm); +} + +static void realm_type_print(const struct expr *expr, struct output_ctx *octx) +{ + return symbolic_constant_print(octx->tbl.realm, expr, true, octx); +} + +static struct error_record *realm_type_parse(struct parse_ctx *ctx, + const struct expr *sym, + struct expr **res) +{ + return symbolic_constant_parse(ctx, sym, ctx->tbl->realm, res); +} + +const struct datatype realm_type = { + .type = TYPE_REALM, + .name = "realm", + .desc = "routing realm", + .byteorder = BYTEORDER_HOST_ENDIAN, + .size = 4 * BITS_PER_BYTE, + .basetype = &integer_type, + .print = realm_type_print, + .parse = realm_type_parse, + .flags = DTYPE_F_PREFIX, +}; + +const struct rt_template rt_templates[] = { + [NFT_RT_CLASSID] = RT_TEMPLATE("classid", + &realm_type, + 4 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, + false), + [NFT_RT_NEXTHOP4] = RT_TEMPLATE("nexthop", + &ipaddr_type, + 4 * BITS_PER_BYTE, + BYTEORDER_BIG_ENDIAN, + true), + [NFT_RT_NEXTHOP6] = RT_TEMPLATE("nexthop", + &ip6addr_type, + 16 * BITS_PER_BYTE, + BYTEORDER_BIG_ENDIAN, + true), + [NFT_RT_TCPMSS] = RT_TEMPLATE("mtu", + &integer_type, + 2 * BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, + false), + [NFT_RT_XFRM] = RT_TEMPLATE("ipsec", + &boolean_type, + BITS_PER_BYTE, + BYTEORDER_HOST_ENDIAN, + false), +}; + +static void rt_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + const char *ip = ""; + + switch (expr->rt.key) { + case NFT_RT_NEXTHOP4: + ip = "ip "; + break; + case NFT_RT_NEXTHOP6: + ip = "ip6 "; + break; + default: + break; + } + + nft_print(octx, "rt %s%s", ip, rt_templates[expr->rt.key].token); +} + +static bool rt_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->rt.key == e2->rt.key; +} + +static void rt_expr_clone(struct expr *new, const struct expr *expr) +{ + new->rt.key = expr->rt.key; +} + +#define NFTNL_UDATA_RT_KEY 0 +#define NFTNL_UDATA_RT_MAX 1 + +static int rt_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_RT_KEY, expr->rt.key); + + return 0; +} + +static int rt_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_RT_KEY: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *rt_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_RT_MAX + 1] = {}; + uint32_t key; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + rt_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_RT_KEY]) + return NULL; + + key = nftnl_udata_get_u32(ud[NFTNL_UDATA_RT_KEY]); + + return rt_expr_alloc(&internal_location, key, false); +} + +const struct expr_ops rt_expr_ops = { + .type = EXPR_RT, + .name = "rt", + .print = rt_expr_print, + .json = rt_expr_json, + .cmp = rt_expr_cmp, + .clone = rt_expr_clone, + .parse_udata = rt_expr_parse_udata, + .build_udata = rt_expr_build_udata, +}; + +struct expr *rt_expr_alloc(const struct location *loc, enum nft_rt_keys key, + bool invalid) +{ + const struct rt_template *tmpl = &rt_templates[key]; + struct expr *expr; + + if (invalid && tmpl->invalid) + expr = expr_alloc(loc, EXPR_RT, &invalid_type, + tmpl->byteorder, 0); + else + expr = expr_alloc(loc, EXPR_RT, tmpl->dtype, + tmpl->byteorder, tmpl->len); + expr->rt.key = key; + + return expr; +} + +void rt_expr_update_type(struct proto_ctx *ctx, struct expr *expr) +{ + const struct proto_desc *desc; + + switch (expr->rt.key) { + case NFT_RT_NEXTHOP4: + desc = ctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc == &proto_ip) + datatype_set(expr, &ipaddr_type); + else if (desc == &proto_ip6) { + expr->rt.key++; + datatype_set(expr, &ip6addr_type); + } + expr->len = expr->dtype->size; + break; + default: + break; + } +} diff --git a/src/rule.c b/src/rule.c new file mode 100644 index 0000000..739b7a5 --- /dev/null +++ b/src/rule.c @@ -0,0 +1,2799 @@ +/* + * Copyright (c) 2008-2012 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> +#include <errno.h> + +#include <statement.h> +#include <rule.h> +#include <utils.h> +#include <netdb.h> +#include <netlink.h> +#include <mnl.h> +#include <misspell.h> +#include <json.h> +#include <cache.h> +#include <owner.h> +#include <intervals.h> +#include "nftutils.h" + +#include <libnftnl/common.h> +#include <libnftnl/ruleset.h> +#include <netinet/ip.h> +#include <linux/netfilter.h> +#include <linux/netfilter_arp.h> +#include <linux/netfilter_ipv4.h> +#include <linux/netfilter/nf_synproxy.h> +#include <net/if.h> +#include <linux/netfilter_bridge.h> + +static const char *const tcp_state_to_name[] = { + [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = "syn_sent", + [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = "syn_recv", + [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = "established", + [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = "fin_wait", + [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = "close_wait", + [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = "last_ack", + [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = "time_wait", + [NFTNL_CTTIMEOUT_TCP_CLOSE] = "close", + [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = "syn_sent2", + [NFTNL_CTTIMEOUT_TCP_RETRANS] = "retrans", + [NFTNL_CTTIMEOUT_TCP_UNACK] = "unack", +}; + +static const char *const udp_state_to_name[] = { + [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = "unreplied", + [NFTNL_CTTIMEOUT_UDP_REPLIED] = "replied", +}; + +static uint32_t tcp_dflt_timeout[] = { + [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = 120, + [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = 60, + [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = 432000, + [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = 120, + [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = 60, + [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = 30, + [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = 120, + [NFTNL_CTTIMEOUT_TCP_CLOSE] = 10, + [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = 120, + [NFTNL_CTTIMEOUT_TCP_RETRANS] = 300, + [NFTNL_CTTIMEOUT_TCP_UNACK] = 300, +}; + +static uint32_t udp_dflt_timeout[] = { + [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30, + [NFTNL_CTTIMEOUT_UDP_REPLIED] = 120, +}; + +struct timeout_protocol timeout_protocol[UINT8_MAX + 1] = { + [IPPROTO_TCP] = { + .array_size = NFTNL_CTTIMEOUT_TCP_MAX, + .state_to_name = tcp_state_to_name, + .dflt_timeout = tcp_dflt_timeout, + }, + [IPPROTO_UDP] = { + .array_size = NFTNL_CTTIMEOUT_UDP_MAX, + .state_to_name = udp_state_to_name, + .dflt_timeout = udp_dflt_timeout, + }, +}; + +int timeout_str2num(uint16_t l4proto, struct timeout_state *ts) +{ + unsigned int i; + + for (i = 0; i < timeout_protocol[l4proto].array_size; i++) { + if (!strcmp(timeout_protocol[l4proto].state_to_name[i], ts->timeout_str)) { + ts->timeout_index = i; + return 0; + } + } + return -1; +} + +void handle_free(struct handle *h) +{ + xfree(h->table.name); + xfree(h->chain.name); + xfree(h->set.name); + xfree(h->flowtable.name); + xfree(h->obj.name); +} + +void handle_merge(struct handle *dst, const struct handle *src) +{ + if (dst->family == 0) + dst->family = src->family; + if (dst->table.name == NULL && src->table.name != NULL) { + dst->table.name = xstrdup(src->table.name); + dst->table.location = src->table.location; + } + if (dst->chain.name == NULL && src->chain.name != NULL) { + dst->chain.name = xstrdup(src->chain.name); + dst->chain.location = src->chain.location; + } + if (dst->set.name == NULL && src->set.name != NULL) { + dst->set.name = xstrdup(src->set.name); + dst->set.location = src->set.location; + } + if (dst->flowtable.name == NULL && src->flowtable.name != NULL) + dst->flowtable.name = xstrdup(src->flowtable.name); + if (dst->obj.name == NULL && src->obj.name != NULL) + dst->obj.name = xstrdup(src->obj.name); + if (dst->handle.id == 0) + dst->handle = src->handle; + if (dst->position.id == 0) + dst->position = src->position; + if (dst->index.id == 0) + dst->index = src->index; +} + +/* internal ID to uniquely identify a set in the batch */ +static uint32_t set_id; + +struct set *set_alloc(const struct location *loc) +{ + struct set *set; + + assert(loc); + + set = xzalloc(sizeof(*set)); + set->refcnt = 1; + set->handle.set_id = ++set_id; + set->location = *loc; + + init_list_head(&set->stmt_list); + + return set; +} + +struct set *set_clone(const struct set *set) +{ + struct set *new_set; + + new_set = set_alloc(&internal_location); + handle_merge(&new_set->handle, &set->handle); + new_set->flags = set->flags; + new_set->gc_int = set->gc_int; + new_set->timeout = set->timeout; + new_set->key = expr_clone(set->key); + if (set->data) + new_set->data = expr_clone(set->data); + new_set->objtype = set->objtype; + new_set->policy = set->policy; + new_set->automerge = set->automerge; + new_set->desc = set->desc; + init_list_head(&new_set->stmt_list); + + return new_set; +} + +struct set *set_get(struct set *set) +{ + set->refcnt++; + return set; +} + +void set_free(struct set *set) +{ + struct stmt *stmt, *next; + + if (--set->refcnt > 0) + return; + + expr_free(set->init); + if (set->comment) + xfree(set->comment); + handle_free(&set->handle); + list_for_each_entry_safe(stmt, next, &set->stmt_list, list) + stmt_free(stmt); + expr_free(set->key); + expr_free(set->data); + xfree(set); +} + +struct set *set_lookup_fuzzy(const char *set_name, + const struct nft_cache *cache, + const struct table **t) +{ + struct string_misspell_state st; + struct table *table; + struct set *set; + + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (set_is_anonymous(set->flags)) + continue; + if (string_misspell_update(set->handle.set.name, + set_name, set, &st)) + *t = table; + } + } + return st.obj; +} + +struct set *set_lookup_global(uint32_t family, const char *table, + const char *name, struct nft_cache *cache) +{ + struct table *t; + + t = table_cache_find(&cache->table_cache, table, family); + if (t == NULL) + return NULL; + + return set_cache_find(t, name); +} + +struct print_fmt_options { + const char *tab; + const char *nl; + const char *table; + const char *family; + const char *stmt_separator; +}; + +const char *set_policy2str(uint32_t policy) +{ + switch (policy) { + case NFT_SET_POL_PERFORMANCE: + return "performance"; + case NFT_SET_POL_MEMORY: + return "memory"; + default: + return "unknown"; + } +} + +static void set_print_key(const struct expr *expr, struct output_ctx *octx) +{ + const struct datatype *dtype = expr->dtype; + + if (dtype->size || dtype->type == TYPE_VERDICT) + nft_print(octx, "%s", dtype->name); + else + expr_print(expr, octx); +} + +static void set_print_key_and_data(const struct set *set, struct output_ctx *octx) +{ + bool use_typeof = set->key_typeof_valid; + + nft_print(octx, "%s ", use_typeof ? "typeof" : "type"); + + if (use_typeof) + expr_print(set->key, octx); + else + set_print_key(set->key, octx); + + if (set_is_datamap(set->flags)) { + nft_print(octx, " : "); + if (set->data->flags & EXPR_F_INTERVAL) + nft_print(octx, "interval "); + + if (use_typeof) + expr_print(set->data, octx); + else + set_print_key(set->data, octx); + } else if (set_is_objmap(set->flags)) { + nft_print(octx, " : %s", obj_type_name(set->objtype)); + } +} + +static void set_print_declaration(const struct set *set, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + const char *delim = ""; + struct stmt *stmt; + const char *type; + uint32_t flags; + + if (set_is_meter(set->flags)) + type = "meter"; + else if (set_is_map(set->flags)) + type = "map"; + else + type = "set"; + + nft_print(octx, "%s%s", opts->tab, type); + + if (opts->family != NULL) + nft_print(octx, " %s", opts->family); + + if (opts->table != NULL) + nft_print(octx, " %s", opts->table); + + nft_print(octx, " %s {", set->handle.set.name); + + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, set->handle.handle.id); + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + + set_print_key_and_data(set, octx); + + nft_print(octx, "%s", opts->stmt_separator); + + if (!(set->flags & (NFT_SET_CONSTANT))) { + if (set->policy != NFT_SET_POL_PERFORMANCE) { + nft_print(octx, "%s%spolicy %s%s", + opts->tab, opts->tab, + set_policy2str(set->policy), + opts->stmt_separator); + } + + if (set->desc.size > 0) { + nft_print(octx, "%s%ssize %u%s", + opts->tab, opts->tab, + set->desc.size, + opts->stmt_separator); + } + } + + flags = set->flags; + /* "timeout" flag is redundant if a default timeout exists */ + if (set->timeout) + flags &= ~NFT_SET_TIMEOUT; + + if (flags & (NFT_SET_CONSTANT | NFT_SET_INTERVAL | NFT_SET_TIMEOUT | NFT_SET_EVAL)) { + nft_print(octx, "%s%sflags ", opts->tab, opts->tab); + if (set->flags & NFT_SET_CONSTANT) { + nft_print(octx, "%sconstant", delim); + delim = ","; + } + if (set->flags & NFT_SET_EVAL) { + nft_print(octx, "%sdynamic", delim); + delim = ","; + } + if (set->flags & NFT_SET_INTERVAL) { + nft_print(octx, "%sinterval", delim); + delim = ","; + } + if (set->flags & NFT_SET_TIMEOUT) { + nft_print(octx, "%stimeout", delim); + delim = ","; + } + nft_print(octx, "%s", opts->stmt_separator); + } + + if (!list_empty(&set->stmt_list)) { + unsigned int flags = octx->flags; + + nft_print(octx, "%s%s", opts->tab, opts->tab); + + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + list_for_each_entry(stmt, &set->stmt_list, list) { + stmt_print(stmt, octx); + if (!list_is_last(&stmt->list, &set->stmt_list)) + nft_print(octx, " "); + } + octx->flags = flags; + + nft_print(octx, "%s", opts->stmt_separator); + } + + if (set->automerge) + nft_print(octx, "%s%sauto-merge%s", opts->tab, opts->tab, + opts->stmt_separator); + + if (set->timeout) { + nft_print(octx, "%s%stimeout ", opts->tab, opts->tab); + time_print(set->timeout, octx); + nft_print(octx, "%s", opts->stmt_separator); + } + if (set->gc_int) { + nft_print(octx, "%s%sgc-interval ", opts->tab, opts->tab); + time_print(set->gc_int, octx); + nft_print(octx, "%s", opts->stmt_separator); + } + + if (set->comment) { + nft_print(octx, "%s%scomment \"%s\"%s", + opts->tab, opts->tab, + set->comment, + opts->stmt_separator); + } +} + +static void do_set_print(const struct set *set, struct print_fmt_options *opts, + struct output_ctx *octx) +{ + set_print_declaration(set, opts, octx); + + if ((set_is_meter(set->flags) && nft_output_stateless(octx)) || + nft_output_terse(octx)) { + nft_print(octx, "%s}%s", opts->tab, opts->nl); + return; + } + + if (set->init != NULL && set->init->size > 0) { + nft_print(octx, "%s%selements = ", opts->tab, opts->tab); + expr_print(set->init, octx); + nft_print(octx, "%s", opts->nl); + } + nft_print(octx, "%s}%s", opts->tab, opts->nl); +} + +void set_print(const struct set *s, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + + do_set_print(s, &opts, octx); +} + +void set_print_plain(const struct set *s, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "", + .nl = " ", + .table = s->handle.table.name, + .family = family2str(s->handle.family), + .stmt_separator = "; ", + }; + + do_set_print(s, &opts, octx); +} + +struct rule *rule_alloc(const struct location *loc, const struct handle *h) +{ + struct rule *rule; + + assert(loc); + + rule = xzalloc(sizeof(*rule)); + rule->location = *loc; + init_list_head(&rule->list); + init_list_head(&rule->stmts); + rule->refcnt = 1; + if (h != NULL) + rule->handle = *h; + + return rule; +} + +struct rule *rule_get(struct rule *rule) +{ + rule->refcnt++; + return rule; +} + +void rule_free(struct rule *rule) +{ + if (--rule->refcnt > 0) + return; + stmt_list_free(&rule->stmts); + handle_free(&rule->handle); + xfree(rule->comment); + xfree(rule); +} + +void rule_print(const struct rule *rule, struct output_ctx *octx) +{ + const struct stmt *stmt; + + list_for_each_entry(stmt, &rule->stmts, list) { + stmt->ops->print(stmt, octx); + if (!list_is_last(&stmt->list, &rule->stmts)) + nft_print(octx, " "); + } + + if (rule->comment) + nft_print(octx, " comment \"%s\"", rule->comment); + + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, rule->handle.handle.id); +} + +struct rule *rule_lookup(const struct chain *chain, uint64_t handle) +{ + struct rule *rule; + + list_for_each_entry(rule, &chain->rules, list) { + if (rule->handle.handle.id == handle) + return rule; + } + return NULL; +} + +struct rule *rule_lookup_by_index(const struct chain *chain, uint64_t index) +{ + struct rule *rule; + + list_for_each_entry(rule, &chain->rules, list) { + if (!--index) + return rule; + } + return NULL; +} + +void rule_stmt_append(struct rule *rule, struct stmt *stmt) +{ + list_add_tail(&stmt->list, &rule->stmts); + rule->num_stmts++; +} + +void rule_stmt_insert_at(struct rule *rule, struct stmt *nstmt, + struct stmt *stmt) +{ + list_add_tail(&nstmt->list, &stmt->list); + rule->num_stmts++; +} + +struct scope *scope_alloc(void) +{ + struct scope *scope = xzalloc(sizeof(struct scope)); + + init_list_head(&scope->symbols); + + return scope; +} + +struct scope *scope_init(struct scope *scope, const struct scope *parent) +{ + scope->parent = parent; + return scope; +} + +void scope_release(const struct scope *scope) +{ + struct symbol *sym, *next; + + list_for_each_entry_safe(sym, next, &scope->symbols, list) { + assert(sym->refcnt == 1); + list_del(&sym->list); + xfree(sym->identifier); + expr_free(sym->expr); + xfree(sym); + } +} + +void scope_free(struct scope *scope) +{ + scope_release(scope); + xfree(scope); +} + +void symbol_bind(struct scope *scope, const char *identifier, struct expr *expr) +{ + struct symbol *sym; + + sym = xzalloc(sizeof(*sym)); + sym->identifier = xstrdup(identifier); + sym->expr = expr; + sym->refcnt = 1; + + list_add(&sym->list, &scope->symbols); +} + +struct symbol *symbol_get(const struct scope *scope, const char *identifier) +{ + struct symbol *sym; + + sym = symbol_lookup(scope, identifier); + if (!sym) + return NULL; + + sym->refcnt++; + + return sym; +} + +static void symbol_put(struct symbol *sym) +{ + if (--sym->refcnt == 0) { + xfree(sym->identifier); + expr_free(sym->expr); + xfree(sym); + } +} + +static void symbol_remove(struct symbol *sym) +{ + list_del(&sym->list); + symbol_put(sym); +} + +int symbol_unbind(const struct scope *scope, const char *identifier) +{ + struct symbol *sym, *next; + + list_for_each_entry_safe(sym, next, &scope->symbols, list) { + if (!strcmp(sym->identifier, identifier)) + symbol_remove(sym); + } + + return 0; +} + +struct symbol *symbol_lookup(const struct scope *scope, const char *identifier) +{ + struct symbol *sym; + + while (scope != NULL) { + list_for_each_entry(sym, &scope->symbols, list) { + if (!strcmp(sym->identifier, identifier)) + return sym; + } + scope = scope->parent; + } + return NULL; +} + +struct symbol *symbol_lookup_fuzzy(const struct scope *scope, + const char *identifier) +{ + struct string_misspell_state st; + struct symbol *sym; + + string_misspell_init(&st); + + while (scope != NULL) { + list_for_each_entry(sym, &scope->symbols, list) + string_misspell_update(sym->identifier, identifier, + sym, &st); + + scope = scope->parent; + } + return st.obj; +} + +static const char * const chain_type_str_array[] = { + "filter", + "nat", + "route", + NULL, +}; + +const char *chain_type_name_lookup(const char *name) +{ + int i; + + for (i = 0; chain_type_str_array[i]; i++) { + if (!strcmp(name, chain_type_str_array[i])) + return chain_type_str_array[i]; + } + + return NULL; +} + +static const char * const chain_hookname_str_array[] = { + "prerouting", + "input", + "forward", + "postrouting", + "output", + "ingress", + "egress", + NULL, +}; + +const char *chain_hookname_lookup(const char *name) +{ + int i; + + for (i = 0; chain_hookname_str_array[i]; i++) { + if (!strcmp(name, chain_hookname_str_array[i])) + return chain_hookname_str_array[i]; + } + + return NULL; +} + +/* internal ID to uniquely identify a set in the batch */ +static uint32_t chain_id; + +struct chain *chain_alloc(void) +{ + struct chain *chain; + + chain = xzalloc(sizeof(*chain)); + chain->location = internal_location; + chain->refcnt = 1; + chain->handle.chain_id = ++chain_id; + init_list_head(&chain->rules); + init_list_head(&chain->scope.symbols); + + chain->policy = NULL; + return chain; +} + +struct chain *chain_get(struct chain *chain) +{ + chain->refcnt++; + return chain; +} + +void chain_free(struct chain *chain) +{ + struct rule *rule, *next; + int i; + + if (--chain->refcnt > 0) + return; + list_for_each_entry_safe(rule, next, &chain->rules, list) + rule_free(rule); + handle_free(&chain->handle); + scope_release(&chain->scope); + xfree(chain->type.str); + expr_free(chain->dev_expr); + for (i = 0; i < chain->dev_array_len; i++) + xfree(chain->dev_array[i]); + xfree(chain->dev_array); + expr_free(chain->priority.expr); + expr_free(chain->policy); + xfree(chain->comment); + xfree(chain); +} + +struct chain *chain_binding_lookup(const struct table *table, + const char *chain_name) +{ + struct chain *chain; + + list_for_each_entry(chain, &table->chain_bindings, cache.list) { + if (!strcmp(chain->handle.chain.name, chain_name)) + return chain; + } + return NULL; +} + +struct chain *chain_lookup_fuzzy(const struct handle *h, + const struct nft_cache *cache, + const struct table **t) +{ + struct string_misspell_state st; + struct table *table; + struct chain *chain; + + if (!h->chain.name) + return NULL; + + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + if (string_misspell_update(chain->handle.chain.name, + h->chain.name, chain, &st)) + *t = table; + } + } + return st.obj; +} + +const char *family2str(unsigned int family) +{ + switch (family) { + case NFPROTO_IPV4: + return "ip"; + case NFPROTO_IPV6: + return "ip6"; + case NFPROTO_INET: + return "inet"; + case NFPROTO_NETDEV: + return "netdev"; + case NFPROTO_ARP: + return "arp"; + case NFPROTO_BRIDGE: + return "bridge"; + default: + break; + } + return "unknown"; +} + +const char *hooknum2str(unsigned int family, unsigned int hooknum) +{ + switch (family) { + case NFPROTO_IPV4: + case NFPROTO_BRIDGE: + case NFPROTO_IPV6: + case NFPROTO_INET: + switch (hooknum) { + case NF_INET_PRE_ROUTING: + return "prerouting"; + case NF_INET_LOCAL_IN: + return "input"; + case NF_INET_FORWARD: + return "forward"; + case NF_INET_POST_ROUTING: + return "postrouting"; + case NF_INET_LOCAL_OUT: + return "output"; + case NF_INET_INGRESS: + return "ingress"; + default: + break; + }; + break; + case NFPROTO_ARP: + switch (hooknum) { + case NF_ARP_IN: + return "input"; + case NF_ARP_FORWARD: + return "forward"; + case NF_ARP_OUT: + return "output"; + case __NF_ARP_INGRESS: + return "ingress"; + default: + break; + } + break; + case NFPROTO_NETDEV: + switch (hooknum) { + case NF_NETDEV_INGRESS: + return "ingress"; + case NF_NETDEV_EGRESS: + return "egress"; + } + break; + default: + break; + }; + + return "unknown"; +} + +const char *chain_policy2str(uint32_t policy) +{ + switch (policy) { + case NF_DROP: + return "drop"; + case NF_ACCEPT: + return "accept"; + } + return "unknown"; +} + +struct prio_tag { + int val; + const char *str; +}; + +static const struct prio_tag std_prios[] = { + { NF_IP_PRI_RAW, "raw" }, + { NF_IP_PRI_MANGLE, "mangle" }, + { NF_IP_PRI_NAT_DST, "dstnat" }, + { NF_IP_PRI_FILTER, "filter" }, + { NF_IP_PRI_SECURITY, "security" }, + { NF_IP_PRI_NAT_SRC, "srcnat" }, +}; + +static const struct prio_tag bridge_std_prios[] = { + { NF_BR_PRI_NAT_DST_BRIDGED, "dstnat" }, + { NF_BR_PRI_FILTER_BRIDGED, "filter" }, + { NF_BR_PRI_NAT_DST_OTHER, "out" }, + { NF_BR_PRI_NAT_SRC, "srcnat" }, +}; + +static bool std_prio_family_hook_compat(int prio, int family, int hook) +{ + /* bridge family has different values */ + if (family == NFPROTO_BRIDGE) { + switch (prio) { + case NF_BR_PRI_NAT_DST_BRIDGED: + if (hook == NF_BR_PRE_ROUTING) + return true; + break; + case NF_BR_PRI_FILTER_BRIDGED: + return true; + case NF_BR_PRI_NAT_DST_OTHER: + if (hook == NF_BR_LOCAL_OUT) + return true; + break; + case NF_BR_PRI_NAT_SRC: + if (hook == NF_BR_POST_ROUTING) + return true; + } + return false; + } + switch(prio) { + case NF_IP_PRI_FILTER: + switch (family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_ARP: + case NFPROTO_NETDEV: + return true; + } + break; + case NF_IP_PRI_RAW: + case NF_IP_PRI_MANGLE: + case NF_IP_PRI_SECURITY: + switch (family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + return true; + } + break; + case NF_IP_PRI_NAT_DST: + switch(family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (hook == NF_INET_PRE_ROUTING || + hook == NF_INET_LOCAL_OUT) + return true; + } + break; + case NF_IP_PRI_NAT_SRC: + switch(family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (hook == NF_INET_LOCAL_IN || + hook == NF_INET_POST_ROUTING) + return true; + } + } + return false; +} + +int std_prio_lookup(const char *std_prio_name, int family, int hook) +{ + const struct prio_tag *prio_arr; + size_t i, arr_size; + + if (family == NFPROTO_BRIDGE) { + prio_arr = bridge_std_prios; + arr_size = array_size(bridge_std_prios); + } else { + prio_arr = std_prios; + arr_size = array_size(std_prios); + } + + for (i = 0; i < arr_size; ++i) { + if (strcmp(prio_arr[i].str, std_prio_name) == 0 && + std_prio_family_hook_compat(prio_arr[i].val, family, hook)) + return prio_arr[i].val; + } + return NF_IP_PRI_LAST; +} + +static const char *prio2str(const struct output_ctx *octx, + char *buf, size_t bufsize, int family, int hook, + const struct expr *expr) +{ + const struct prio_tag *prio_arr; + int std_prio, offset, prio; + const char *std_prio_str; + const int reach = 10; + size_t i, arr_size; + + mpz_export_data(&prio, expr->value, BYTEORDER_HOST_ENDIAN, sizeof(int)); + if (family == NFPROTO_BRIDGE) { + prio_arr = bridge_std_prios; + arr_size = array_size(bridge_std_prios); + } else { + prio_arr = std_prios; + arr_size = array_size(std_prios); + } + + if (!nft_output_numeric_prio(octx)) { + for (i = 0; i < arr_size; ++i) { + std_prio = prio_arr[i].val; + std_prio_str = prio_arr[i].str; + if (abs(prio - std_prio) <= reach) { + if (!std_prio_family_hook_compat(std_prio, + family, hook)) + break; + offset = prio - std_prio; + strncpy(buf, std_prio_str, bufsize); + if (offset > 0) + snprintf(buf + strlen(buf), + bufsize - strlen(buf), " + %d", + offset); + else if (offset < 0) + snprintf(buf + strlen(buf), + bufsize - strlen(buf), " - %d", + -offset); + return buf; + } + } + } + snprintf(buf, bufsize, "%d", prio); + return buf; +} + +static void chain_print_declaration(const struct chain *chain, + struct output_ctx *octx) +{ + char priobuf[STD_PRIO_BUFSIZE]; + int policy, i; + + if (chain->flags & CHAIN_F_BINDING) + return; + + nft_print(octx, "\tchain %s {", chain->handle.chain.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); + if (chain->comment) + nft_print(octx, "\n\t\tcomment \"%s\"", chain->comment); + nft_print(octx, "\n"); + if (chain->flags & CHAIN_F_BASECHAIN) { + nft_print(octx, "\t\ttype %s hook %s", chain->type.str, + hooknum2str(chain->handle.family, chain->hook.num)); + if (chain->dev_array_len == 1) { + nft_print(octx, " device \"%s\"", chain->dev_array[0]); + } else if (chain->dev_array_len > 1) { + nft_print(octx, " devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { + nft_print(octx, "%s", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " }"); + } + nft_print(octx, " priority %s;", + prio2str(octx, priobuf, sizeof(priobuf), + chain->handle.family, chain->hook.num, + chain->priority.expr)); + if (chain->policy) { + mpz_export_data(&policy, chain->policy->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + nft_print(octx, " policy %s;", + chain_policy2str(policy)); + } + if (chain->flags & CHAIN_F_HW_OFFLOAD) + nft_print(octx, " flags offload;"); + + nft_print(octx, "\n"); + } +} + +void chain_rules_print(const struct chain *chain, struct output_ctx *octx, + const char *indent) +{ + unsigned int flags = octx->flags; + struct rule *rule; + + if (chain->flags & CHAIN_F_BINDING) + octx->flags &= ~NFT_CTX_OUTPUT_HANDLE; + + list_for_each_entry(rule, &chain->rules, list) { + nft_print(octx, "\t\t%s", indent ? : ""); + rule_print(rule, octx); + nft_print(octx, "\n"); + } + + octx->flags = flags; +} + +static void chain_print(const struct chain *chain, struct output_ctx *octx) +{ + chain_print_declaration(chain, octx); + chain_rules_print(chain, octx, NULL); + nft_print(octx, "\t}\n"); +} + +void chain_print_plain(const struct chain *chain, struct output_ctx *octx) +{ + char priobuf[STD_PRIO_BUFSIZE]; + int policy; + + nft_print(octx, "chain %s %s %s", family2str(chain->handle.family), + chain->handle.table.name, chain->handle.chain.name); + + if (chain->flags & CHAIN_F_BASECHAIN) { + mpz_export_data(&policy, chain->policy->value, + BYTEORDER_HOST_ENDIAN, sizeof(int)); + nft_print(octx, " { type %s hook %s ", + chain->type.str, chain->hook.name); + + if (chain->dev_array_len > 0) { + int i; + + nft_print(octx, "devices = { "); + for (i = 0; i < chain->dev_array_len; i++) { + nft_print(octx, "%s", chain->dev_array[i]); + if (i + 1 != chain->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " } "); + } + nft_print(octx, "priority %s; policy %s; }", + prio2str(octx, priobuf, sizeof(priobuf), + chain->handle.family, chain->hook.num, + chain->priority.expr), + chain_policy2str(policy)); + } + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); +} + +struct table *table_alloc(void) +{ + struct table *table; + + table = xzalloc(sizeof(*table)); + table->location = internal_location; + init_list_head(&table->chains); + init_list_head(&table->sets); + init_list_head(&table->objs); + init_list_head(&table->flowtables); + init_list_head(&table->chain_bindings); + init_list_head(&table->scope.symbols); + table->refcnt = 1; + + cache_init(&table->chain_cache); + cache_init(&table->set_cache); + cache_init(&table->obj_cache); + cache_init(&table->ft_cache); + + return table; +} + +void table_free(struct table *table) +{ + struct chain *chain, *next; + struct flowtable *ft, *nft; + struct set *set, *nset; + struct obj *obj, *nobj; + + if (--table->refcnt > 0) + return; + if (table->comment) + xfree(table->comment); + list_for_each_entry_safe(chain, next, &table->chains, list) + chain_free(chain); + list_for_each_entry_safe(chain, next, &table->chain_bindings, cache.list) + chain_free(chain); + /* this is implicitly releasing chains in the hashtable cache */ + list_for_each_entry_safe(chain, next, &table->chain_cache.list, cache.list) + chain_free(chain); + list_for_each_entry_safe(set, nset, &table->sets, list) + set_free(set); + /* this is implicitly releasing sets in the hashtable cache */ + list_for_each_entry_safe(set, nset, &table->set_cache.list, cache.list) + set_free(set); + list_for_each_entry_safe(ft, nft, &table->flowtables, list) + flowtable_free(ft); + /* this is implicitly releasing flowtables in the hashtable cache */ + list_for_each_entry_safe(ft, nft, &table->ft_cache.list, cache.list) + flowtable_free(ft); + list_for_each_entry_safe(obj, nobj, &table->objs, list) + obj_free(obj); + /* this is implicitly releasing objs in the hashtable cache */ + list_for_each_entry_safe(obj, nobj, &table->obj_cache.list, cache.list) + obj_free(obj); + + handle_free(&table->handle); + scope_release(&table->scope); + cache_free(&table->chain_cache); + cache_free(&table->set_cache); + cache_free(&table->obj_cache); + cache_free(&table->ft_cache); + xfree(table); +} + +struct table *table_get(struct table *table) +{ + table->refcnt++; + return table; +} + +struct table *table_lookup_fuzzy(const struct handle *h, + const struct nft_cache *cache) +{ + struct string_misspell_state st; + struct table *table; + + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + string_misspell_update(table->handle.table.name, + h->table.name, table, &st); + } + return st.obj; +} + +static const char *table_flags_name[TABLE_FLAGS_MAX] = { + "dormant", + "owner", +}; + +const char *table_flag_name(uint32_t flag) +{ + if (flag >= TABLE_FLAGS_MAX) + return "unknown"; + + return table_flags_name[flag]; +} + +static void table_print_flags(const struct table *table, const char **delim, + struct output_ctx *octx) +{ + uint32_t flags = table->flags; + bool comma = false; + int i; + + if (!table->flags) + return; + + nft_print(octx, "\tflags "); + for (i = 0; i < TABLE_FLAGS_MAX; i++) { + if (flags & (1 << i)) { + if (comma) + nft_print(octx, ","); + + nft_print(octx, "%s", table_flag_name(i)); + comma = true; + } + } + nft_print(octx, "\n"); + *delim = "\n"; +} + +static void table_print(const struct table *table, struct output_ctx *octx) +{ + struct flowtable *flowtable; + struct chain *chain; + struct obj *obj; + struct set *set; + const char *delim = ""; + const char *family = family2str(table->handle.family); + + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + + nft_print(octx, "table %s %s {", family, table->handle.table.name); + if (nft_output_handle(octx) || table->flags & TABLE_F_OWNER) + nft_print(octx, " #"); + if (nft_output_handle(octx)) + nft_print(octx, " handle %" PRIu64, table->handle.handle.id); + if (table->flags & TABLE_F_OWNER) + nft_print(octx, " progname %s", get_progname(table->owner)); + + nft_print(octx, "\n"); + table_print_flags(table, &delim, octx); + + if (table->comment) + nft_print(octx, "\tcomment \"%s\"\n", table->comment); + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { + nft_print(octx, "%s", delim); + obj_print(obj, octx); + delim = "\n"; + } + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (set_is_anonymous(set->flags)) + continue; + nft_print(octx, "%s", delim); + set_print(set, octx); + delim = "\n"; + } + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { + nft_print(octx, "%s", delim); + flowtable_print(flowtable, octx); + delim = "\n"; + } + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + nft_print(octx, "%s", delim); + chain_print(chain, octx); + delim = "\n"; + } + nft_print(octx, "}\n"); +} + +struct cmd *cmd_alloc(enum cmd_ops op, enum cmd_obj obj, + const struct handle *h, const struct location *loc, + void *data) +{ + struct cmd *cmd; + + assert(loc); + + cmd = xzalloc(sizeof(*cmd)); + init_list_head(&cmd->list); + cmd->op = op; + cmd->obj = obj; + cmd->handle = *h; + cmd->location = *loc; + cmd->data = data; + cmd->attr = xzalloc_array(NFT_NLATTR_LOC_MAX, + sizeof(struct nlerr_loc)); + cmd->attr_array_len = NFT_NLATTR_LOC_MAX; + init_list_head(&cmd->collapse_list); + + return cmd; +} + +struct markup *markup_alloc(uint32_t format) +{ + struct markup *markup; + + markup = xmalloc(sizeof(struct markup)); + markup->format = format; + + return markup; +} + +void markup_free(struct markup *m) +{ + xfree(m); +} + +struct monitor *monitor_alloc(uint32_t format, uint32_t type, const char *event) +{ + struct monitor *mon; + + mon = xmalloc(sizeof(struct monitor)); + mon->format = format; + mon->type = type; + mon->event = event; + mon->flags = 0; + + return mon; +} + +void monitor_free(struct monitor *m) +{ + xfree(m->event); + xfree(m); +} + +void cmd_free(struct cmd *cmd) +{ + handle_free(&cmd->handle); + if (cmd->data != NULL) { + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + expr_free(cmd->expr); + if (cmd->elem.set) + set_free(cmd->elem.set); + break; + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: + case CMD_OBJ_SETELEMS: + set_free(cmd->set); + break; + 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; + case CMD_OBJ_EXPR: + expr_free(cmd->expr); + break; + case CMD_OBJ_MONITOR: + monitor_free(cmd->monitor); + break; + case CMD_OBJ_MARKUP: + markup_free(cmd->markup); + break; + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: + case CMD_OBJ_SYNPROXY: + obj_free(cmd->object); + break; + case CMD_OBJ_FLOWTABLE: + flowtable_free(cmd->flowtable); + break; + default: + BUG("invalid command object type %u\n", cmd->obj); + } + } + xfree(cmd->attr); + xfree(cmd->arg); + xfree(cmd); +} + +#include <netlink.h> +#include <mnl.h> + +static int __do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, + struct set *set, struct expr *expr, uint32_t flags) +{ + expr->set_flags |= set->flags; + if (mnl_nft_setelem_add(ctx, cmd, set, expr, flags) < 0) + return -1; + + return 0; +} + +static int do_add_elements(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct expr *init = cmd->expr; + struct set *set = cmd->elem.set; + + if (set_is_non_concat_range(set) && + set_to_intervals(set, init, true) < 0) + return -1; + + return __do_add_elements(ctx, cmd, set, init, flags); +} + +static int do_add_setelems(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct set *set = cmd->set; + + return __do_add_elements(ctx, cmd, set, set->init, flags); +} + +static int do_add_set(struct netlink_ctx *ctx, struct cmd *cmd, + uint32_t flags) +{ + struct set *set = cmd->set; + + if (set->init != NULL) { + /* Update set->init->size (NFTNL_SET_DESC_SIZE) before adding + * the set to the kernel. Calling this from do_add_setelems() + * comes too late which might result in spurious ENFILE errors. + */ + if (set_is_non_concat_range(set) && + set_to_intervals(set, set->init, true) < 0) + return -1; + } + + if (mnl_nft_set_add(ctx, cmd, flags) < 0) + return -1; + + if (set_is_anonymous(set->flags)) + return __do_add_elements(ctx, cmd, set, set->init, flags); + + return 0; +} + +static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) +{ + uint32_t flags = excl ? NLM_F_EXCL : 0; + + if (nft_output_echo(&ctx->nft->output)) + flags |= NLM_F_ECHO; + + switch (cmd->obj) { + case CMD_OBJ_TABLE: + return mnl_nft_table_add(ctx, cmd, flags); + case CMD_OBJ_CHAIN: + return mnl_nft_chain_add(ctx, cmd, flags); + case CMD_OBJ_RULE: + return mnl_nft_rule_add(ctx, cmd, flags | NLM_F_APPEND); + case CMD_OBJ_SET: + return do_add_set(ctx, cmd, flags); + case CMD_OBJ_SETELEMS: + return do_add_setelems(ctx, cmd, flags); + case CMD_OBJ_ELEMENTS: + return do_add_elements(ctx, cmd, flags); + case CMD_OBJ_COUNTER: + case CMD_OBJ_QUOTA: + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: + case CMD_OBJ_SYNPROXY: + return mnl_nft_obj_add(ctx, cmd, flags); + case CMD_OBJ_FLOWTABLE: + return mnl_nft_flowtable_add(ctx, cmd, flags); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int do_command_replace(struct netlink_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_RULE: + return mnl_nft_rule_replace(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int do_command_insert(struct netlink_ctx *ctx, struct cmd *cmd) +{ + uint32_t flags = 0; + + if (nft_output_echo(&ctx->nft->output)) + flags |= NLM_F_ECHO; + + switch (cmd->obj) { + case CMD_OBJ_RULE: + return mnl_nft_rule_add(ctx, cmd, flags); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int do_delete_setelems(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct expr *expr = cmd->elem.expr; + struct set *set = cmd->elem.set; + + if (set_is_non_concat_range(set) && + set_to_intervals(set, expr, false) < 0) + return -1; + + if (mnl_nft_setelem_del(ctx, cmd, &cmd->handle, cmd->elem.expr) < 0) + return -1; + + return 0; +} + +static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_TABLE: + return mnl_nft_table_del(ctx, cmd); + case CMD_OBJ_CHAIN: + return mnl_nft_chain_del(ctx, cmd); + case CMD_OBJ_RULE: + return mnl_nft_rule_del(ctx, cmd); + case CMD_OBJ_SET: + return mnl_nft_set_del(ctx, cmd); + case CMD_OBJ_ELEMENTS: + return do_delete_setelems(ctx, cmd); + case CMD_OBJ_COUNTER: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_COUNTER); + case CMD_OBJ_QUOTA: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_QUOTA); + case CMD_OBJ_CT_HELPER: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_CT_HELPER); + case CMD_OBJ_CT_TIMEOUT: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_CT_TIMEOUT); + case CMD_OBJ_CT_EXPECT: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_CT_EXPECT); + case CMD_OBJ_LIMIT: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SECMARK); + case CMD_OBJ_SYNPROXY: + return mnl_nft_obj_del(ctx, cmd, NFT_OBJECT_SYNPROXY); + case CMD_OBJ_FLOWTABLE: + return mnl_nft_flowtable_del(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +static int do_list_table(struct netlink_ctx *ctx, struct table *table) +{ + table_print(table, &ctx->nft->output); + return 0; +} + +static int do_list_sets(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + struct set *set; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + + list_for_each_entry(set, &table->set_cache.list, cache.list) { + if (cmd->obj == CMD_OBJ_SETS && + !set_is_literal(set->flags)) + continue; + if (cmd->obj == CMD_OBJ_METERS && + !set_is_meter(set->flags)) + continue; + if (cmd->obj == CMD_OBJ_MAPS && + !map_is_literal(set->flags)) + continue; + set_print(set, &ctx->nft->output); + } + + nft_print(&ctx->nft->output, "}\n"); + } + return 0; +} + +struct obj *obj_alloc(const struct location *loc) +{ + struct obj *obj; + + assert(loc); + + obj = xzalloc(sizeof(*obj)); + obj->location = *loc; + + obj->refcnt = 1; + return obj; +} + +struct obj *obj_get(struct obj *obj) +{ + obj->refcnt++; + return obj; +} + +void obj_free(struct obj *obj) +{ + if (--obj->refcnt > 0) + return; + xfree(obj->comment); + handle_free(&obj->handle); + if (obj->type == NFT_OBJECT_CT_TIMEOUT) { + struct timeout_state *ts, *next; + + list_for_each_entry_safe(ts, next, &obj->ct_timeout.timeout_list, head) { + list_del(&ts->head); + xfree(ts->timeout_str); + xfree(ts); + } + } + xfree(obj); +} + +struct obj *obj_lookup_fuzzy(const char *obj_name, + const struct nft_cache *cache, + const struct table **t) +{ + struct string_misspell_state st; + struct table *table; + struct obj *obj; + + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { + if (string_misspell_update(obj->handle.obj.name, + obj_name, obj, &st)) + *t = table; + } + } + return st.obj; +} + +static void print_proto_name_proto(uint8_t l4, struct output_ctx *octx) +{ + char name[NFT_PROTONAME_MAXSIZE]; + + if (nft_getprotobynumber(l4, name, sizeof(name))) + nft_print(octx, "%s", name); + else + nft_print(octx, "%d", l4); +} + +static void print_proto_timeout_policy(uint8_t l4, const uint32_t *timeout, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + bool comma = false; + unsigned int i; + + nft_print(octx, "%s%spolicy = { ", opts->tab, opts->tab); + for (i = 0; i < timeout_protocol[l4].array_size; i++) { + if (timeout[i] != timeout_protocol[l4].dflt_timeout[i]) { + uint64_t timeout_ms; + + if (comma) + nft_print(octx, ", "); + timeout_ms = timeout[i] * 1000u; + nft_print(octx, "%s : ", + timeout_protocol[l4].state_to_name[i]); + time_print(timeout_ms, octx); + comma = true; + } + } + nft_print(octx, " }%s", opts->stmt_separator); +} + +static const char *synproxy_sack_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_SACK_PERM) + return "sack-perm"; + + return ""; +} + +static const char *synproxy_timestamp_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_TIMESTAMP) + return "timestamp"; + + return ""; +} + +static void obj_print_comment(const struct obj *obj, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + if (obj->comment) + nft_print(octx, "%s%s%scomment \"%s\"", + opts->nl, opts->tab, opts->tab, + obj->comment); +} + +static void obj_print_data(const struct obj *obj, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + switch (obj->type) { + case NFT_OBJECT_COUNTER: + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + if (nft_output_stateless(octx)) + nft_print(octx, "%s", opts->nl); + else + nft_print(octx, "%s%s%spackets %" PRIu64 " bytes %" PRIu64 "%s", + opts->nl, opts->tab, opts->tab, + obj->counter.packets, obj->counter.bytes, opts->nl); + break; + case NFT_OBJECT_QUOTA: { + const char *data_unit; + uint64_t bytes; + + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + data_unit = get_rate(obj->quota.bytes, &bytes); + nft_print(octx, "%s%" PRIu64 " %s", + obj->quota.flags & NFT_QUOTA_F_INV ? "over " : "", + bytes, data_unit); + if (!nft_output_stateless(octx) && obj->quota.used) { + data_unit = get_rate(obj->quota.used, &bytes); + nft_print(octx, " used %" PRIu64 " %s", + bytes, data_unit); + } + nft_print(octx, "%s", opts->nl); + } + break; + case NFT_OBJECT_SECMARK: + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + nft_print(octx, "\"%s\"%s", obj->secmark.ctx, opts->nl); + break; + case NFT_OBJECT_CT_HELPER: + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s", opts->nl); + nft_print(octx, "%s%stype \"%s\" protocol ", + opts->tab, opts->tab, obj->ct_helper.name); + print_proto_name_proto(obj->ct_helper.l4proto, octx); + nft_print(octx, "%s", opts->stmt_separator); + nft_print(octx, "%s%sl3proto %s%s", + opts->tab, opts->tab, + family2str(obj->ct_helper.l3proto), + opts->stmt_separator); + break; + case NFT_OBJECT_CT_TIMEOUT: + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s", opts->nl); + nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab); + print_proto_name_proto(obj->ct_timeout.l4proto, octx); + nft_print(octx, "%s", opts->stmt_separator); + nft_print(octx, "%s%sl3proto %s%s", + opts->tab, opts->tab, + family2str(obj->ct_timeout.l3proto), + opts->stmt_separator); + print_proto_timeout_policy(obj->ct_timeout.l4proto, + obj->ct_timeout.timeout, opts, octx); + break; + case NFT_OBJECT_CT_EXPECT: + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s", opts->nl); + nft_print(octx, "%s%sprotocol ", opts->tab, opts->tab); + print_proto_name_proto(obj->ct_expect.l4proto, octx); + nft_print(octx, "%s", opts->stmt_separator); + nft_print(octx, "%s%sdport %d%s", + opts->tab, opts->tab, + obj->ct_expect.dport, + opts->stmt_separator); + nft_print(octx, "%s%stimeout ", opts->tab, opts->tab); + time_print(obj->ct_expect.timeout, octx); + nft_print(octx, "%s", opts->stmt_separator); + nft_print(octx, "%s%ssize %d%s", + opts->tab, opts->tab, + obj->ct_expect.size, + opts->stmt_separator); + nft_print(octx, "%s%sl3proto %s%s", + opts->tab, opts->tab, + family2str(obj->ct_expect.l3proto), + opts->stmt_separator); + break; + case NFT_OBJECT_LIMIT: { + bool inv = obj->limit.flags & NFT_LIMIT_F_INV; + const char *data_unit; + uint64_t rate; + + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + switch (obj->limit.type) { + case NFT_LIMIT_PKTS: + nft_print(octx, "rate %s%" PRIu64 "/%s", + inv ? "over " : "", obj->limit.rate, + get_unit(obj->limit.unit)); + if (obj->limit.burst > 0 && obj->limit.burst != 5) + nft_print(octx, " burst %u packets", + obj->limit.burst); + break; + case NFT_LIMIT_PKT_BYTES: + data_unit = get_rate(obj->limit.rate, &rate); + + nft_print(octx, "rate %s%" PRIu64 " %s/%s", + inv ? "over " : "", rate, data_unit, + get_unit(obj->limit.unit)); + if (obj->limit.burst > 0) { + uint64_t burst; + + data_unit = get_rate(obj->limit.burst, &burst); + nft_print(octx, " burst %"PRIu64" %s", + burst, data_unit); + } + break; + } + nft_print(octx, "%s", opts->nl); + } + break; + case NFT_OBJECT_SYNPROXY: { + uint32_t flags = obj->synproxy.flags; + const char *sack_str = synproxy_sack_to_str(flags); + const char *ts_str = synproxy_timestamp_to_str(flags); + + nft_print(octx, " %s {", obj->handle.obj.name); + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + + obj_print_comment(obj, opts, octx); + + if (flags & NF_SYNPROXY_OPT_MSS) { + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + nft_print(octx, "mss %u", obj->synproxy.mss); + } + if (flags & NF_SYNPROXY_OPT_WSCALE) { + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + nft_print(octx, "wscale %u", obj->synproxy.wscale); + } + if (flags & (NF_SYNPROXY_OPT_TIMESTAMP | NF_SYNPROXY_OPT_SACK_PERM)) { + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + nft_print(octx, "%s %s", ts_str, sack_str); + } + nft_print(octx, "%s", opts->stmt_separator); + } + break; + default: + nft_print(octx, " unknown {%s", opts->nl); + break; + } +} + +static const char * const obj_type_name_array[] = { + [NFT_OBJECT_COUNTER] = "counter", + [NFT_OBJECT_QUOTA] = "quota", + [NFT_OBJECT_CT_HELPER] = "ct helper", + [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_CT_TIMEOUT] = "ct timeout", + [NFT_OBJECT_SECMARK] = "secmark", + [NFT_OBJECT_SYNPROXY] = "synproxy", + [NFT_OBJECT_CT_EXPECT] = "ct expectation", +}; + +const char *obj_type_name(unsigned int type) +{ + assert(type <= NFT_OBJECT_MAX && obj_type_name_array[type]); + + return obj_type_name_array[type]; +} + +static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = { + [NFT_OBJECT_COUNTER] = CMD_OBJ_COUNTER, + [NFT_OBJECT_QUOTA] = CMD_OBJ_QUOTA, + [NFT_OBJECT_CT_HELPER] = CMD_OBJ_CT_HELPER, + [NFT_OBJECT_LIMIT] = CMD_OBJ_LIMIT, + [NFT_OBJECT_CT_TIMEOUT] = CMD_OBJ_CT_TIMEOUT, + [NFT_OBJECT_SECMARK] = CMD_OBJ_SECMARK, + [NFT_OBJECT_SYNPROXY] = CMD_OBJ_SYNPROXY, + [NFT_OBJECT_CT_EXPECT] = CMD_OBJ_CT_EXPECT, +}; + +enum cmd_obj obj_type_to_cmd(uint32_t type) +{ + assert(type <= NFT_OBJECT_MAX && obj_type_cmd_array[type]); + + return obj_type_cmd_array[type]; +} + +static void obj_print_declaration(const struct obj *obj, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + nft_print(octx, "%s%s", opts->tab, obj_type_name(obj->type)); + + if (opts->family != NULL) + nft_print(octx, " %s", opts->family); + + if (opts->table != NULL) + nft_print(octx, " %s", opts->table); + + obj_print_data(obj, opts, octx); + + nft_print(octx, "%s}%s", opts->tab, opts->nl); +} + +void obj_print(const struct obj *obj, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + + obj_print_declaration(obj, &opts, octx); +} + +void obj_print_plain(const struct obj *obj, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "", + .nl = " ", + .table = obj->handle.table.name, + .family = family2str(obj->handle.family), + .stmt_separator = "; ", + }; + + obj_print_declaration(obj, &opts, octx); +} + +static int do_list_obj(struct netlink_ctx *ctx, struct cmd *cmd, uint32_t type) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + struct table *table; + struct obj *obj; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + if (cmd->handle.table.name != NULL && + strcmp(cmd->handle.table.name, table->handle.table.name)) + continue; + + if (list_empty(&table->obj_cache.list)) + continue; + + nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + + list_for_each_entry(obj, &table->obj_cache.list, cache.list) { + if (obj->type != type || + (cmd->handle.obj.name != NULL && + strcmp(cmd->handle.obj.name, obj->handle.obj.name))) + continue; + + obj_print_declaration(obj, &opts, &ctx->nft->output); + } + + nft_print(&ctx->nft->output, "}\n"); + } + return 0; +} + +struct flowtable *flowtable_alloc(const struct location *loc) +{ + struct flowtable *flowtable; + + assert(loc); + + flowtable = xzalloc(sizeof(*flowtable)); + flowtable->location = *loc; + + flowtable->refcnt = 1; + return flowtable; +} + +struct flowtable *flowtable_get(struct flowtable *flowtable) +{ + flowtable->refcnt++; + return flowtable; +} + +void flowtable_free(struct flowtable *flowtable) +{ + int i; + + if (--flowtable->refcnt > 0) + return; + handle_free(&flowtable->handle); + expr_free(flowtable->priority.expr); + expr_free(flowtable->dev_expr); + + if (flowtable->dev_array != NULL) { + for (i = 0; i < flowtable->dev_array_len; i++) + xfree(flowtable->dev_array[i]); + xfree(flowtable->dev_array); + } + xfree(flowtable); +} + +static void flowtable_print_declaration(const struct flowtable *flowtable, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + char priobuf[STD_PRIO_BUFSIZE]; + int i; + + nft_print(octx, "%sflowtable", opts->tab); + + if (opts->family != NULL) + nft_print(octx, " %s", opts->family); + + if (opts->table != NULL) + nft_print(octx, " %s", opts->table); + + nft_print(octx, " %s {", flowtable->handle.flowtable.name); + + if (nft_output_handle(octx)) + nft_print(octx, " # handle %" PRIu64, flowtable->handle.handle.id); + nft_print(octx, "%s", opts->nl); + nft_print(octx, "%s%shook %s priority %s%s", + opts->tab, opts->tab, + hooknum2str(NFPROTO_NETDEV, flowtable->hook.num), + prio2str(octx, priobuf, sizeof(priobuf), NFPROTO_NETDEV, + flowtable->hook.num, flowtable->priority.expr), + opts->stmt_separator); + + if (flowtable->dev_array_len > 0) { + nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); + for (i = 0; i < flowtable->dev_array_len; i++) { + nft_print(octx, "%s", flowtable->dev_array[i]); + if (i + 1 != flowtable->dev_array_len) + nft_print(octx, ", "); + } + nft_print(octx, " }%s", opts->stmt_separator); + } + + if (flowtable->flags & NFT_FLOWTABLE_HW_OFFLOAD) + nft_print(octx, "%s%sflags offload%s", opts->tab, opts->tab, + opts->stmt_separator); + + if (flowtable->flags & NFT_FLOWTABLE_COUNTER) + nft_print(octx, "%s%scounter%s", opts->tab, opts->tab, + opts->stmt_separator); +} + +static void do_flowtable_print(const struct flowtable *flowtable, + struct print_fmt_options *opts, + struct output_ctx *octx) +{ + flowtable_print_declaration(flowtable, opts, octx); + nft_print(octx, "%s}%s", opts->tab, opts->nl); +} + +void flowtable_print(const struct flowtable *s, struct output_ctx *octx) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + + do_flowtable_print(s, &opts, octx); +} + +struct flowtable *flowtable_lookup_fuzzy(const char *ft_name, + const struct nft_cache *cache, + const struct table **t) +{ + struct string_misspell_state st; + struct table *table; + struct flowtable *ft; + + string_misspell_init(&st); + + list_for_each_entry(table, &cache->table_cache.list, cache.list) { + list_for_each_entry(ft, &table->ft_cache.list, cache.list) { + if (string_misspell_update(ft->handle.flowtable.name, + ft_name, ft, &st)) + *t = table; + } + } + return st.obj; +} + +static int do_list_flowtable(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table) +{ + struct flowtable *ft; + + ft = ft_cache_find(table, cmd->handle.flowtable.name); + if (!ft) + return -1; + + nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + + flowtable_print(ft, &ctx->nft->output); + nft_print(&ctx->nft->output, "}\n"); + + return 0; +} + +static int do_list_flowtables(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct print_fmt_options opts = { + .tab = "\t", + .nl = "\n", + .stmt_separator = "\n", + }; + struct flowtable *flowtable; + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + nft_print(&ctx->nft->output, "table %s %s {\n", + family2str(table->handle.family), + table->handle.table.name); + + list_for_each_entry(flowtable, &table->ft_cache.list, cache.list) { + flowtable_print_declaration(flowtable, &opts, &ctx->nft->output); + nft_print(&ctx->nft->output, "%s}%s", opts.tab, opts.nl); + } + + nft_print(&ctx->nft->output, "}\n"); + } + return 0; +} + +static int do_list_ruleset(struct netlink_ctx *ctx, struct cmd *cmd) +{ + unsigned int family = cmd->handle.family; + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (family != NFPROTO_UNSPEC && + table->handle.family != family) + continue; + + if (do_list_table(ctx, table) < 0) + return -1; + } + + return 0; +} + +static int do_list_tables(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + nft_print(&ctx->nft->output, "table %s %s\n", + family2str(table->handle.family), + table->handle.table.name); + } + + return 0; +} + +static void table_print_declaration(struct table *table, + struct output_ctx *octx) +{ + const char *family = family2str(table->handle.family); + + if (table->has_xt_stmts) + fprintf(octx->error_fp, + "# Warning: table %s %s is managed by iptables-nft, do not touch!\n", + family, table->handle.table.name); + + nft_print(octx, "table %s %s {\n", family, table->handle.table.name); +} + +static int do_list_chain(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table) +{ + struct chain *chain; + + table_print_declaration(table, &ctx->nft->output); + + chain = chain_cache_find(table, cmd->handle.chain.name); + if (chain) + chain_print(chain, &ctx->nft->output); + + nft_print(&ctx->nft->output, "}\n"); + + return 0; +} + +static int do_list_chains(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table; + struct chain *chain; + + list_for_each_entry(table, &ctx->nft->cache.table_cache.list, cache.list) { + if (cmd->handle.family != NFPROTO_UNSPEC && + cmd->handle.family != table->handle.family) + continue; + + table_print_declaration(table, &ctx->nft->output); + + list_for_each_entry(chain, &table->chain_cache.list, cache.list) { + chain_print_declaration(chain, &ctx->nft->output); + nft_print(&ctx->nft->output, "\t}\n"); + } + nft_print(&ctx->nft->output, "}\n"); + } + + return 0; +} + +static void __do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, + struct set *set) +{ + struct table *table = table_alloc(); + + table->handle.table.name = xstrdup(cmd->handle.table.name); + table->handle.family = cmd->handle.family; + table_print_declaration(table, &ctx->nft->output); + table_free(table); + + set_print(set, &ctx->nft->output); + nft_print(&ctx->nft->output, "}\n"); +} + +static int do_list_set(struct netlink_ctx *ctx, struct cmd *cmd, + struct table *table) +{ + struct set *set = cmd->set; + + if (!set) { + set = set_cache_find(table, cmd->handle.set.name); + if (set == NULL) + return -1; + } + + __do_list_set(ctx, cmd, set); + + return 0; +} + +static int do_list_hooks(struct netlink_ctx *ctx, struct cmd *cmd) +{ + const char *devname = cmd->handle.obj.name; + int hooknum = -1; + + if (cmd->handle.chain.name) + hooknum = cmd->handle.chain_id; + + return mnl_nft_dump_nf_hooks(ctx, cmd->handle.family, hooknum, devname); +} + +static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table = NULL; + + if (nft_output_json(&ctx->nft->output)) + return do_command_list_json(ctx, cmd); + + if (cmd->handle.table.name != NULL) + table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + switch (cmd->obj) { + case CMD_OBJ_TABLE: + if (!cmd->handle.table.name) + return do_list_tables(ctx, cmd); + return do_list_table(ctx, table); + case CMD_OBJ_CHAIN: + return do_list_chain(ctx, cmd, table); + case CMD_OBJ_CHAINS: + return do_list_chains(ctx, cmd); + case CMD_OBJ_SETS: + return do_list_sets(ctx, cmd); + case CMD_OBJ_SET: + return do_list_set(ctx, cmd, table); + case CMD_OBJ_RULESET: + case CMD_OBJ_RULES: + case CMD_OBJ_RULE: + return do_list_ruleset(ctx, cmd); + case CMD_OBJ_METERS: + return do_list_sets(ctx, cmd); + case CMD_OBJ_METER: + return do_list_set(ctx, cmd, table); + case CMD_OBJ_MAPS: + return do_list_sets(ctx, cmd); + case CMD_OBJ_MAP: + return do_list_set(ctx, cmd, table); + case CMD_OBJ_COUNTER: + case CMD_OBJ_COUNTERS: + return do_list_obj(ctx, cmd, NFT_OBJECT_COUNTER); + case CMD_OBJ_QUOTA: + case CMD_OBJ_QUOTAS: + return do_list_obj(ctx, cmd, NFT_OBJECT_QUOTA); + case CMD_OBJ_CT_HELPER: + case CMD_OBJ_CT_HELPERS: + return do_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER); + case CMD_OBJ_CT_TIMEOUT: + case CMD_OBJ_CT_TIMEOUTS: + return do_list_obj(ctx, cmd, NFT_OBJECT_CT_TIMEOUT); + case CMD_OBJ_CT_EXPECT: + case CMD_OBJ_CT_EXPECTATIONS: + return do_list_obj(ctx, cmd, NFT_OBJECT_CT_EXPECT); + case CMD_OBJ_LIMIT: + case CMD_OBJ_LIMITS: + return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + return do_list_obj(ctx, cmd, NFT_OBJECT_SECMARK); + case CMD_OBJ_SYNPROXY: + case CMD_OBJ_SYNPROXYS: + return do_list_obj(ctx, cmd, NFT_OBJECT_SYNPROXY); + case CMD_OBJ_FLOWTABLE: + return do_list_flowtable(ctx, cmd, table); + case CMD_OBJ_FLOWTABLES: + return do_list_flowtables(ctx, cmd); + case CMD_OBJ_HOOKS: + return do_list_hooks(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + return 0; +} + +static int do_get_setelems(struct netlink_ctx *ctx, struct cmd *cmd, bool reset) +{ + struct set *set, *new_set; + struct expr *init; + int err; + + set = cmd->elem.set; + + /* Create a list of elements based of what we got from command line. */ + if (set_is_non_concat_range(set)) + init = get_set_intervals(set, cmd->expr); + else + init = cmd->expr; + + new_set = set_clone(set); + + /* Fetch from kernel the elements that have been requested .*/ + err = netlink_get_setelem(ctx, &cmd->handle, &cmd->location, + cmd->elem.set, new_set, init, reset); + if (err >= 0) + __do_list_set(ctx, cmd, new_set); + + if (set_is_non_concat_range(set)) + expr_free(init); + + set_free(new_set); + + return err; +} + +static int do_command_get(struct netlink_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_ELEMENTS: + return do_get_setelems(ctx, cmd, false); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + return 0; +} + +static int do_command_reset(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct obj *obj, *next; + struct table *table; + bool dump = false; + uint32_t type; + int ret; + + switch (cmd->obj) { + case CMD_OBJ_COUNTERS: + dump = true; + /* fall through */ + case CMD_OBJ_COUNTER: + type = NFT_OBJECT_COUNTER; + break; + case CMD_OBJ_QUOTAS: + dump = true; + /* fall through */ + case CMD_OBJ_QUOTA: + type = NFT_OBJECT_QUOTA; + break; + case CMD_OBJ_RULES: + ret = netlink_reset_rules(ctx, cmd, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); + case CMD_OBJ_RULE: + return netlink_reset_rules(ctx, cmd, false); + case CMD_OBJ_ELEMENTS: + return do_get_setelems(ctx, cmd, true); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + ret = netlink_list_setelems(ctx, &cmd->handle, cmd->set, true); + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + + ret = netlink_reset_objs(ctx, cmd, type, dump); + list_for_each_entry_safe(obj, next, &ctx->list, list) { + table = table_cache_find(&ctx->nft->cache.table_cache, + obj->handle.table.name, + obj->handle.family); + if (!obj_cache_find(table, obj->handle.obj.name, obj->type)) { + list_del(&obj->list); + obj_cache_add(obj, table); + } + } + if (ret < 0) + return ret; + + return do_command_list(ctx, cmd); +} + +static int do_command_flush(struct netlink_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->obj) { + case CMD_OBJ_TABLE: + case CMD_OBJ_CHAIN: + return mnl_nft_rule_del(ctx, cmd); + case CMD_OBJ_SET: + case CMD_OBJ_MAP: + case CMD_OBJ_METER: + return mnl_nft_setelem_flush(ctx, cmd); + case CMD_OBJ_RULESET: + return mnl_nft_table_del(ctx, cmd); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int do_command_rename(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct table *table = table_cache_find(&ctx->nft->cache.table_cache, + cmd->handle.table.name, + cmd->handle.family); + const struct chain *chain; + + switch (cmd->obj) { + case CMD_OBJ_CHAIN: + chain = chain_cache_find(table, cmd->handle.chain.name); + + return mnl_nft_chain_rename(ctx, cmd, chain); + default: + BUG("invalid command object type %u\n", cmd->obj); + } + return 0; +} + +static int do_command_monitor(struct netlink_ctx *ctx, struct cmd *cmd) +{ + struct netlink_mon_handler monhandler = { + .monitor_flags = cmd->monitor->flags, + .format = cmd->monitor->format, + .ctx = ctx, + .loc = &cmd->location, + .cache = &ctx->nft->cache, + .debug_mask = ctx->nft->debug_mask, + }; + + if (nft_output_json(&ctx->nft->output)) + monhandler.format = NFTNL_OUTPUT_JSON; + + return netlink_monitor(&monhandler, ctx->nft->nf_sock); +} + +static int do_command_describe(struct netlink_ctx *ctx, struct cmd *cmd, + struct output_ctx *octx) +{ + expr_describe(cmd->expr, octx); + return 0; +} + +struct cmd *cmd_alloc_obj_ct(enum cmd_ops op, int type, const struct handle *h, + const struct location *loc, struct obj *obj) +{ + enum cmd_obj cmd_obj; + + if (obj) + obj->type = type; + + switch (type) { + case NFT_OBJECT_CT_HELPER: + cmd_obj = CMD_OBJ_CT_HELPER; + break; + case NFT_OBJECT_CT_TIMEOUT: + cmd_obj = CMD_OBJ_CT_TIMEOUT; + break; + case NFT_OBJECT_CT_EXPECT: + cmd_obj = CMD_OBJ_CT_EXPECT; + break; + default: + BUG("missing type mapping"); + } + + return cmd_alloc(op, cmd_obj, h, loc, obj); +} + +int do_command(struct netlink_ctx *ctx, struct cmd *cmd) +{ + switch (cmd->op) { + case CMD_ADD: + return do_command_add(ctx, cmd, false); + case CMD_CREATE: + return do_command_add(ctx, cmd, true); + case CMD_INSERT: + return do_command_insert(ctx, cmd); + case CMD_REPLACE: + return do_command_replace(ctx, cmd); + case CMD_DELETE: + case CMD_DESTROY: + return do_command_delete(ctx, cmd); + case CMD_GET: + return do_command_get(ctx, cmd); + case CMD_LIST: + return do_command_list(ctx, cmd); + case CMD_RESET: + return do_command_reset(ctx, cmd); + case CMD_FLUSH: + return do_command_flush(ctx, cmd); + case CMD_RENAME: + return do_command_rename(ctx, cmd); + case CMD_IMPORT: + case CMD_EXPORT: + errno = EOPNOTSUPP; + return -1; + case CMD_MONITOR: + return do_command_monitor(ctx, cmd); + case CMD_DESCRIBE: + return do_command_describe(ctx, cmd, &ctx->nft->output); + default: + BUG("invalid command object type %u\n", cmd->obj); + } +} + +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 bool relational_ops_match(const struct expr *e1, const struct expr *e2) +{ + enum ops op1, op2; + + op1 = e1->op == OP_IMPLICIT ? OP_EQ : e1->op; + op2 = e2->op == OP_IMPLICIT ? OP_EQ : e2->op; + + return op1 == op2; +} + +static void payload_do_merge(struct stmt *sa[], unsigned int n) +{ + struct expr *last, *this, *expr1, *expr2; + struct stmt *stmt; + unsigned int i, j; + + qsort(sa, n, sizeof(sa[0]), payload_match_stmt_cmp); + + last = sa[0]->expr; + for (j = 0, i = 1; i < n; i++) { + stmt = sa[i]; + this = stmt->expr; + + if (!payload_can_merge(last->left, this->left) || + !relational_ops_match(last, this)) { + last = this; + j = i; + continue; + } + + expr1 = payload_expr_join(last->left, this->left); + expr2 = constant_expr_join(last->right, this->right); + + /* We can merge last into this, but we can't replace + * the statement associated with this if it does contain + * a higher level protocol. + * + * ether type ip ip saddr X ether saddr Y + * ... can be changed to + * ether type ip ether saddr Y ip saddr X + * ... but not + * ip saddr X ether type ip ether saddr Y + * + * The latter form means we perform ip saddr test before + * ensuring ip dependency, plus it makes decoding harder + * since we don't know the type of the network header + * right away. + * + * So, if we're about to replace a statement + * containing a protocol identifier, just swap this and last + * and replace the other one (i.e., replace 'load ether type ip' + * with the combined 'load both ether type and saddr') and not + * the other way around. + */ + if (this->left->flags & EXPR_F_PROTOCOL) { + struct expr *tmp = last; + + last = this; + this = tmp; + + expr1->flags |= EXPR_F_PROTOCOL; + stmt = sa[j]; + assert(stmt->expr == this); + j = i; + } + + expr_free(last->left); + last->left = expr1; + + expr_free(last->right); + last->right = expr2; + + list_del(&stmt->list); + stmt_free(stmt); + } +} + +/** + * stmt_reduce - reduce statements in rule + * + * @rule: nftables rule + * + * This function aims to: + * + * - remove redundant statement, e.g. remove 'meta protocol ip' if family is ip + * - merge consecutive payload match statements + * + * 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 stmt_reduce(const struct rule *rule) +{ + struct stmt *stmt, *dstmt = NULL, *next; + struct stmt *sa[rule->num_stmts]; + unsigned int idx = 0; + + list_for_each_entry_safe(stmt, next, &rule->stmts, list) { + /* delete this redundant statement */ + if (dstmt) { + list_del(&dstmt->list); + stmt_free(dstmt); + dstmt = NULL; + } + + /* Must not merge across other statements */ + if (stmt->ops->type != STMT_EXPRESSION) { + if (idx >= 2) + payload_do_merge(sa, idx); + idx = 0; + continue; + } + + if (stmt->expr->etype != EXPR_RELATIONAL) + continue; + if (stmt->expr->right->etype != EXPR_VALUE) + continue; + + if (stmt->expr->left->etype == EXPR_PAYLOAD) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + case OP_NEQ: + break; + default: + continue; + } + + sa[idx++] = stmt; + } else if (stmt->expr->left->etype == EXPR_META) { + switch (stmt->expr->op) { + case OP_EQ: + case OP_IMPLICIT: + if (stmt->expr->left->meta.key == NFT_META_PROTOCOL && + !stmt->expr->left->meta.inner_desc) { + uint16_t protocol; + + protocol = mpz_get_uint16(stmt->expr->right->value); + if ((rule->handle.family == NFPROTO_IPV4 && + protocol == ETH_P_IP) || + (rule->handle.family == NFPROTO_IPV6 && + protocol == ETH_P_IPV6)) + dstmt = stmt; + } + break; + default: + break; + } + } + } + + if (idx > 1) + payload_do_merge(sa, idx); +} + +struct error_record *rule_postprocess(struct rule *rule) +{ + stmt_reduce(rule); + return NULL; +} diff --git a/src/scanner.c b/src/scanner.c new file mode 100644 index 0000000..7810f55 --- /dev/null +++ b/src/scanner.c @@ -0,0 +1,8546 @@ +#line 2 "scanner.c" + +#line 4 "scanner.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +/* %not-for-header */ +/* %if-c-only */ +/* %if-not-reentrant */ +/* %endif */ +/* %endif */ +/* %ok-for-header */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 6 +#define YY_FLEX_SUBMINOR_VERSION 4 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* %if-c++-only */ +/* %endif */ + +/* %if-c-only */ +#ifdef yy_create_buffer +#define nft__create_buffer_ALREADY_DEFINED +#else +#define yy_create_buffer nft__create_buffer +#endif + +#ifdef yy_delete_buffer +#define nft__delete_buffer_ALREADY_DEFINED +#else +#define yy_delete_buffer nft__delete_buffer +#endif + +#ifdef yy_scan_buffer +#define nft__scan_buffer_ALREADY_DEFINED +#else +#define yy_scan_buffer nft__scan_buffer +#endif + +#ifdef yy_scan_string +#define nft__scan_string_ALREADY_DEFINED +#else +#define yy_scan_string nft__scan_string +#endif + +#ifdef yy_scan_bytes +#define nft__scan_bytes_ALREADY_DEFINED +#else +#define yy_scan_bytes nft__scan_bytes +#endif + +#ifdef yy_init_buffer +#define nft__init_buffer_ALREADY_DEFINED +#else +#define yy_init_buffer nft__init_buffer +#endif + +#ifdef yy_flush_buffer +#define nft__flush_buffer_ALREADY_DEFINED +#else +#define yy_flush_buffer nft__flush_buffer +#endif + +#ifdef yy_load_buffer_state +#define nft__load_buffer_state_ALREADY_DEFINED +#else +#define yy_load_buffer_state nft__load_buffer_state +#endif + +#ifdef yy_switch_to_buffer +#define nft__switch_to_buffer_ALREADY_DEFINED +#else +#define yy_switch_to_buffer nft__switch_to_buffer +#endif + +#ifdef yypush_buffer_state +#define nft_push_buffer_state_ALREADY_DEFINED +#else +#define yypush_buffer_state nft_push_buffer_state +#endif + +#ifdef yypop_buffer_state +#define nft_pop_buffer_state_ALREADY_DEFINED +#else +#define yypop_buffer_state nft_pop_buffer_state +#endif + +#ifdef yyensure_buffer_stack +#define nft_ensure_buffer_stack_ALREADY_DEFINED +#else +#define yyensure_buffer_stack nft_ensure_buffer_stack +#endif + +#ifdef yylex +#define nft_lex_ALREADY_DEFINED +#else +#define yylex nft_lex +#endif + +#ifdef yyrestart +#define nft_restart_ALREADY_DEFINED +#else +#define yyrestart nft_restart +#endif + +#ifdef yylex_init +#define nft_lex_init_ALREADY_DEFINED +#else +#define yylex_init nft_lex_init +#endif + +#ifdef yylex_init_extra +#define nft_lex_init_extra_ALREADY_DEFINED +#else +#define yylex_init_extra nft_lex_init_extra +#endif + +#ifdef yylex_destroy +#define nft_lex_destroy_ALREADY_DEFINED +#else +#define yylex_destroy nft_lex_destroy +#endif + +#ifdef yyget_debug +#define nft_get_debug_ALREADY_DEFINED +#else +#define yyget_debug nft_get_debug +#endif + +#ifdef yyset_debug +#define nft_set_debug_ALREADY_DEFINED +#else +#define yyset_debug nft_set_debug +#endif + +#ifdef yyget_extra +#define nft_get_extra_ALREADY_DEFINED +#else +#define yyget_extra nft_get_extra +#endif + +#ifdef yyset_extra +#define nft_set_extra_ALREADY_DEFINED +#else +#define yyset_extra nft_set_extra +#endif + +#ifdef yyget_in +#define nft_get_in_ALREADY_DEFINED +#else +#define yyget_in nft_get_in +#endif + +#ifdef yyset_in +#define nft_set_in_ALREADY_DEFINED +#else +#define yyset_in nft_set_in +#endif + +#ifdef yyget_out +#define nft_get_out_ALREADY_DEFINED +#else +#define yyget_out nft_get_out +#endif + +#ifdef yyset_out +#define nft_set_out_ALREADY_DEFINED +#else +#define yyset_out nft_set_out +#endif + +#ifdef yyget_leng +#define nft_get_leng_ALREADY_DEFINED +#else +#define yyget_leng nft_get_leng +#endif + +#ifdef yyget_text +#define nft_get_text_ALREADY_DEFINED +#else +#define yyget_text nft_get_text +#endif + +#ifdef yyget_lineno +#define nft_get_lineno_ALREADY_DEFINED +#else +#define yyget_lineno nft_get_lineno +#endif + +#ifdef yyset_lineno +#define nft_set_lineno_ALREADY_DEFINED +#else +#define yyset_lineno nft_set_lineno +#endif + +#ifdef yyget_column +#define nft_get_column_ALREADY_DEFINED +#else +#define yyget_column nft_get_column +#endif + +#ifdef yyset_column +#define nft_set_column_ALREADY_DEFINED +#else +#define yyset_column nft_set_column +#endif + +#ifdef yywrap +#define nft_wrap_ALREADY_DEFINED +#else +#define yywrap nft_wrap +#endif + +/* %endif */ + +#ifdef yyget_lval +#define nft_get_lval_ALREADY_DEFINED +#else +#define yyget_lval nft_get_lval +#endif + +#ifdef yyset_lval +#define nft_set_lval_ALREADY_DEFINED +#else +#define yyset_lval nft_set_lval +#endif + +#ifdef yyget_lloc +#define nft_get_lloc_ALREADY_DEFINED +#else +#define yyget_lloc nft_get_lloc +#endif + +#ifdef yyset_lloc +#define nft_set_lloc_ALREADY_DEFINED +#else +#define yyset_lloc nft_set_lloc +#endif + +#ifdef yyalloc +#define nft_alloc_ALREADY_DEFINED +#else +#define yyalloc nft_alloc +#endif + +#ifdef yyrealloc +#define nft_realloc_ALREADY_DEFINED +#else +#define yyrealloc nft_realloc +#endif + +#ifdef yyfree +#define nft_free_ALREADY_DEFINED +#else +#define yyfree nft_free +#endif + +/* %if-c-only */ + +/* %endif */ + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +/* %if-c-only */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +/* %endif */ + +/* %if-tables-serialization */ +/* %endif */ +/* end standard C headers. */ + +/* %if-c-or-c++ */ +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX (~(size_t)0) +#endif + +#endif /* ! C99 */ + +#endif /* ! FLEXINT_H */ + +/* %endif */ + +/* begin standard C++ headers. */ +/* %if-c++-only */ +/* %endif */ + +/* TODO: this is always defined, so inline it */ +#define yyconst const + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define yynoreturn __attribute__((__noreturn__)) +#else +#define yynoreturn +#endif + +/* %not-for-header */ +/* Returned upon end-of-file. */ +#define YY_NULL 0 +/* %ok-for-header */ + +/* %not-for-header */ +/* Promotes a possibly negative, possibly signed char to an + * integer in range [0..255] for use as an array index. + */ +#define YY_SC_TO_UI(c) ((YY_CHAR) (c)) +/* %ok-for-header */ + +/* %if-reentrant */ + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* %endif */ + +/* %if-not-reentrant */ +/* %endif */ + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin , yyscanner ) +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k. + * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. + * Ditto for the __ia64__ case accordingly. + */ +#define YY_BUF_SIZE 32768 +#else +#define YY_BUF_SIZE 16384 +#endif /* __ia64__ */ +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +/* %if-not-reentrant */ +/* %endif */ + +/* %if-c-only */ +/* %if-not-reentrant */ +/* %endif */ +/* %endif */ + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires + * access to the local variable yy_act. Since yyless() is a macro, it would break + * existing scanners that call yyless() from OUTSIDE yylex. + * One obvious solution it to make yy_act a global. I tried that, and saw + * a 5% performance hit in a non-yylineno scanner, because yy_act is + * normally declared as a register variable-- so it is not worth it. + */ + #define YY_LESS_LINENO(n) \ + do { \ + int yyl;\ + for ( yyl = n; yyl < yyleng; ++yyl )\ + if ( yytext[yyl] == '\n' )\ + --yylineno;\ + }while(0) + #define YY_LINENO_REWIND_TO(dst) \ + do {\ + const char *p;\ + for ( p = yy_cp-1; p >= (dst); --p)\ + if ( *p == '\n' )\ + --yylineno;\ + }while(0) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { +/* %if-c-only */ + FILE *yy_input_file; +/* %endif */ + +/* %if-c++-only */ +/* %endif */ + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + int yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* %if-c-only Standard (non-C++) definition */ +/* %not-for-header */ +/* %if-not-reentrant */ +/* %endif */ +/* %ok-for-header */ + +/* %endif */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +/* %if-c-only Standard (non-C++) definition */ + +/* %if-not-reentrant */ +/* %not-for-header */ +/* %ok-for-header */ + +/* %endif */ + +void yyrestart ( FILE *input_file , yyscan_t yyscanner ); +void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size , yyscan_t yyscanner ); +void yy_delete_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yy_flush_buffer ( YY_BUFFER_STATE b , yyscan_t yyscanner ); +void yypush_buffer_state ( YY_BUFFER_STATE new_buffer , yyscan_t yyscanner ); +void yypop_buffer_state ( yyscan_t yyscanner ); + +static void yyensure_buffer_stack ( yyscan_t yyscanner ); +static void yy_load_buffer_state ( yyscan_t yyscanner ); +static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file , yyscan_t yyscanner ); +#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER , yyscanner) + +YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner ); +YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner ); + +/* %endif */ + +void *yyalloc ( yy_size_t , yyscan_t yyscanner ); +void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner ); +void yyfree ( void * , yyscan_t yyscanner ); + +#define yy_new_buffer yy_create_buffer +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + yyensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */ +/* Begin user sect3 */ + +#define nft_wrap(yyscanner) (/*CONSTCOND*/1) +#define YY_SKIP_YYWRAP + +#define FLEX_DEBUG +typedef flex_uint8_t YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +/* %% [1.5] DFA */ + +/* %if-c-only Standard (non-C++) definition */ + +static yy_state_type yy_get_previous_state ( yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans ( yy_state_type current_state , yyscan_t yyscanner); +static int yy_get_next_buffer ( yyscan_t yyscanner ); +static void yynoreturn yy_fatal_error ( const char* msg , yyscan_t yyscanner ); + +/* %endif */ + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ +/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\ + yyleng = (int) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ +/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\ + yyg->yy_c_buf_p = yy_cp; +/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */ +#define YY_NUM_RULES 412 +#define YY_END_OF_BUFFER 413 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static const flex_int16_t yy_accept[2383] = + { 0, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + + 398, 398, 398, 398, 398, 398, 398, 398, 398, 398, + 413, 411, 407, 406, 408, 33, 411, 411, 39, 29, + 21, 22, 37, 42, 13, 36, 14, 35, 400, 400, + 400, 15, 16, 7, 40, 11, 38, 404, 404, 19, + 411, 20, 27, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 17, 31, 18, 407, 408, + 411, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 407, 408, 3, 0, 402, + 0, 410, 403, 404, 0, 0, 400, 0, 0, 0, + 398, 398, 398, 398, 400, 396, 23, 5, 1, 9, + 25, 404, 0, 0, 405, 404, 404, 250, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 345, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 2, + 404, 404, 404, 404, 404, 404, 404, 404, 10, 404, + 404, 12, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 184, 404, 404, 404, 6, 404, 404, 404, 8, + 404, 404, 316, 404, 404, 4, 404, 404, 404, 404, + 404, 404, 32, 404, 404, 404, 404, 404, 404, 404, + 404, 306, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 164, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 395, 407, 0, 0, 408, + 0, 409, 404, 404, 404, 404, 404, 404, 404, 162, + 163, 404, 404, 404, 404, 404, 404, 404, 404, 172, + 404, 404, 32, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 6, 404, 404, + 404, 404, 197, 195, 404, 77, 404, 404, 404, 404, + 4, 404, 4, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 6, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 385, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 392, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 4, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 4, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 6, 404, 404, 404, 404, 6, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 403, 0, 400, 0, 398, 0, 396, 399, 0, + 0, 0, 398, 0, 400, 396, 396, 396, 404, 0, + 0, 0, 404, 80, 382, 30, 178, 404, 404, 404, + 404, 404, 404, 404, 334, 404, 150, 404, 404, 404, + 404, 404, 404, 404, 315, 370, 404, 188, 404, 253, + 404, 404, 404, 404, 372, 404, 404, 404, 404, 371, + + 404, 404, 86, 404, 266, 404, 311, 404, 404, 404, + 404, 404, 319, 404, 404, 404, 404, 404, 404, 245, + 404, 404, 404, 404, 404, 404, 129, 404, 61, 404, + 404, 404, 404, 404, 404, 404, 34, 404, 404, 404, + 322, 373, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 307, 308, 404, 404, + 404, 404, 59, 404, 404, 404, 404, 404, 309, 404, + 404, 404, 268, 404, 404, 404, 257, 404, 404, 404, + 404, 264, 404, 28, 0, 409, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 238, 404, + 404, 404, 404, 243, 80, 404, 404, 404, 404, 404, + 404, 404, 198, 404, 191, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 207, 404, 404, 404, + 404, 404, 209, 404, 210, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 173, 174, 175, 404, + 383, 404, 404, 404, 404, 404, 61, 404, 404, 404, + 404, 59, 404, 404, 404, 404, 252, 255, 404, 404, + + 404, 368, 404, 404, 393, 404, 404, 364, 404, 404, + 136, 404, 340, 404, 404, 343, 342, 404, 404, 404, + 404, 404, 285, 404, 188, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 293, 404, 290, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 377, 404, 404, 404, 403, 0, + 0, 400, 0, 0, 398, 0, 401, 0, 401, 396, + 0, 400, 0, 396, 0, 396, 396, 404, 0, 0, + 0, 0, 0, 0, 397, 404, 404, 404, 404, 404, + 404, 254, 404, 404, 404, 404, 269, 404, 404, 404, + + 404, 404, 155, 72, 404, 404, 404, 404, 404, 404, + 404, 404, 110, 404, 312, 404, 404, 75, 404, 404, + 53, 149, 404, 404, 232, 242, 404, 404, 404, 404, + 404, 404, 78, 404, 404, 404, 404, 74, 124, 404, + 87, 404, 318, 404, 317, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 58, 270, 404, 107, 404, 404, + 154, 404, 404, 404, 404, 231, 404, 169, 404, 404, + 404, 171, 41, 404, 404, 181, 404, 404, 182, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 145, 404, 404, 404, 404, 350, 121, + 404, 404, 404, 235, 404, 404, 404, 404, 404, 404, + 404, 310, 187, 312, 404, 404, 194, 404, 404, 196, + 404, 110, 404, 404, 404, 148, 404, 404, 140, 404, + 144, 404, 404, 404, 404, 404, 272, 404, 404, 203, + 206, 404, 223, 404, 404, 404, 211, 404, 404, 404, + 212, 404, 231, 404, 404, 404, 404, 404, 384, 404, + 404, 110, 53, 404, 116, 404, 404, 58, 404, 49, + 404, 404, 58, 404, 404, 404, 404, 404, 367, 404, + + 404, 404, 404, 124, 404, 404, 404, 404, 404, 404, + 404, 273, 284, 404, 404, 404, 274, 404, 404, 404, + 294, 291, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 161, 232, 152, 404, + 404, 231, 404, 403, 0, 0, 400, 398, 0, 0, + 0, 0, 396, 396, 0, 0, 177, 0, 396, 396, + 396, 396, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 404, 404, 404, 404, 57, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 166, 404, + 404, 404, 404, 256, 404, 89, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 96, 404, + 404, 389, 365, 139, 404, 404, 112, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 135, 146, 404, 404, 404, 404, 404, + 404, 88, 404, 404, 404, 404, 404, 326, 325, 404, + 404, 404, 56, 404, 404, 404, 404, 404, 404, 263, + 168, 179, 404, 180, 167, 404, 123, 362, 404, 261, + 354, 404, 404, 404, 404, 359, 404, 404, 404, 352, + 360, 404, 404, 404, 404, 240, 404, 404, 404, 404, + 239, 244, 404, 404, 404, 404, 404, 199, 404, 404, + + 404, 125, 141, 404, 404, 404, 404, 147, 271, 260, + 404, 225, 404, 404, 404, 221, 404, 404, 224, 404, + 213, 214, 215, 216, 404, 404, 227, 226, 404, 404, + 404, 57, 404, 404, 119, 139, 112, 146, 128, 404, + 404, 56, 51, 52, 404, 404, 404, 404, 390, 391, + 404, 404, 404, 404, 404, 404, 278, 404, 404, 404, + 281, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 304, 404, 404, 404, 69, 404, 404, 404, 134, + 133, 135, 404, 404, 404, 153, 404, 404, 404, 404, + 0, 0, 0, 401, 0, 0, 0, 396, 396, 396, + + 176, 0, 0, 396, 396, 0, 396, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 71, 404, 165, + 337, 404, 404, 404, 404, 83, 44, 85, 404, 404, + 54, 404, 404, 386, 404, 92, 388, 404, 404, 265, + 267, 63, 404, 404, 233, 404, 404, 404, 91, 404, + 84, 404, 24, 404, 404, 404, 79, 404, 404, 363, + 404, 404, 404, 404, 404, 404, 404, 106, 404, 404, + 158, 404, 404, 151, 90, 404, 76, 26, 404, 404, + 404, 65, 404, 404, 404, 70, 170, 404, 404, 82, + 404, 346, 404, 404, 404, 357, 404, 404, 404, 404, + + 404, 361, 404, 404, 404, 404, 404, 404, 404, 404, + 189, 200, 130, 404, 404, 404, 143, 142, 109, 404, + 202, 404, 208, 222, 230, 404, 404, 404, 404, 205, + 204, 404, 48, 404, 404, 115, 113, 127, 404, 404, + 50, 262, 404, 404, 369, 137, 138, 404, 404, 404, + 296, 289, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 404, 404, 292, 337, 404, 404, + 404, 404, 404, 404, 404, 376, 404, 404, 375, 396, + 0, 0, 0, 0, 396, 396, 0, 396, 396, 0, + 0, 0, 396, 396, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 404, 97, 404, + 404, 120, 404, 94, 55, 100, 60, 105, 404, 404, + 404, 330, 404, 320, 321, 43, 404, 404, 387, 93, + 327, 381, 404, 332, 111, 404, 323, 324, 333, 404, + 404, 404, 404, 81, 404, 64, 394, 366, 404, 102, + 258, 404, 404, 404, 404, 404, 358, 347, 404, 122, + 404, 404, 404, 185, 404, 234, 404, 404, 404, 404, + 404, 201, 404, 404, 249, 404, 404, 229, 404, 404, + 228, 404, 344, 120, 404, 394, 404, 404, 404, 404, + 338, 404, 404, 404, 404, 404, 404, 404, 404, 404, + + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 259, 404, 131, 404, 404, 404, 404, 396, 0, 0, + 0, 0, 0, 396, 396, 396, 396, 396, 0, 0, + 396, 396, 0, 396, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 404, 98, 73, + 47, 104, 404, 404, 329, 335, 99, 404, 331, 336, + 95, 246, 45, 157, 404, 374, 46, 404, 404, 404, + 404, 351, 404, 404, 192, 193, 404, 404, 241, 190, + 404, 404, 248, 404, 220, 251, 404, 404, 126, 404, + 117, 374, 404, 251, 404, 339, 404, 404, 404, 404, + + 404, 404, 275, 295, 404, 404, 404, 404, 404, 404, + 404, 279, 68, 404, 67, 404, 404, 404, 404, 404, + 396, 0, 0, 0, 396, 396, 0, 396, 396, 0, + 396, 396, 0, 396, 0, 396, 396, 0, 396, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 404, 62, 404, 404, 328, 183, 353, + 404, 404, 349, 348, 237, 404, 186, 247, 404, 218, + 219, 62, 118, 404, 313, 404, 404, 404, 404, 404, + 404, 276, 404, 404, 404, 404, 404, 404, 404, 404, + 404, 404, 404, 404, 380, 378, 0, 0, 0, 0, + + 0, 396, 396, 396, 396, 396, 396, 396, 396, 396, + 396, 0, 0, 396, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 404, 156, 404, 356, 404, 404, 404, 114, + 404, 341, 287, 283, 404, 404, 404, 404, 404, 303, + 404, 404, 404, 404, 404, 404, 404, 404, 404, 404, + 160, 404, 0, 0, 396, 396, 396, 0, 396, 396, + 0, 396, 396, 0, 396, 396, 0, 396, 396, 396, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 103, 355, + + 404, 108, 404, 404, 282, 300, 288, 404, 299, 305, + 404, 404, 404, 404, 404, 404, 66, 404, 404, 404, + 0, 0, 0, 396, 0, 396, 396, 396, 396, 396, + 396, 396, 396, 396, 396, 396, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 404, 404, + 404, 404, 302, 404, 404, 404, 280, 404, 404, 159, + 404, 0, 0, 0, 396, 396, 0, 396, 396, 0, + 396, 396, 0, 396, 396, 0, 396, 396, 0, 396, + 396, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 236, 404, 404, 277, 404, 404, 404, 404, 404, 404, + 0, 0, 0, 0, 396, 396, 396, 396, 396, 396, + 396, 396, 396, 396, 396, 396, 396, 0, 396, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 217, 314, 404, 404, 404, 404, 404, 379, 0, + 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, + 396, 396, 396, 396, 396, 396, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 404, 404, 404, 404, 132, + 0, 396, 396, 396, 396, 396, 396, 396, 396, 396, + 396, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 404, 404, + 404, 404, 396, 396, 396, 396, 396, 396, 396, 396, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 404, 404, 404, 286, 396, 0, 0, 0, 0, + 0, 0, 0, 0, 301, 404, 404, 0, 298, 404, + 297, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 5, 6, 7, 8, 1, 9, 1, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 22, 25, 22, 26, 27, 28, + 29, 30, 1, 31, 32, 32, 32, 32, 32, 33, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34, 35, 34, 34, + 36, 37, 38, 39, 40, 1, 41, 42, 43, 44, + + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[70] = + { 0, + 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 3, 1, 2, 4, 5, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 1, 1, 1, 1, + 1, 9, 9, 10, 10, 1, 3, 11, 1, 10, + 9, 9, 9, 12, 9, 9, 10, 13, 10, 10, + 10, 10, 13, 10, 10, 10, 10, 10, 13, 10, + 10, 10, 10, 10, 10, 10, 1, 1, 2 + } ; + +static const flex_int16_t yy_base[2695] = + { 0, + 0, 68, 29, 72, 26, 85, 99, 164, 39, 96, + 27, 102, 34, 108, 58, 229, 76, 171, 249, 271, + 128, 295, 71, 187, 184, 356, 70, 193, 38, 179, + 156, 332, 207, 318, 0, 197, 379, 250, 159, 338, + 212, 400, 0, 256, 0, 260, 71, 401, 215, 414, + 402, 477, 309, 481, 102, 464, 330, 490, 54, 292, + 247, 473, 76, 393, 3472, 491, 90, 498, 450, 507, + 228, 511, 108, 565, 519, 577, 458, 569, 172, 584, + 492, 588, 543, 592, 606, 665, 685, 744, 555, 618, + 133, 683, 347, 758, 185, 736, 3466, 602, 3451, 649, + + 627, 700, 426, 708, 50, 673, 635, 759, 3409, 751, + 3468, 9866, 3423, 9866, 3415, 3385, 3390, 3378, 9866, 9866, + 9866, 9866, 9866, 9866, 9866, 9866, 234, 9866, 807, 132, + 268, 3340, 9866, 54, 3329, 245, 9866, 860, 364, 3331, + 353, 9866, 9866, 815, 3251, 645, 853, 856, 553, 888, + 779, 898, 514, 912, 925, 932, 946, 905, 521, 953, + 972, 991, 833, 996, 964, 9866, 9866, 9866, 669, 765, + 3303, 977, 1017, 1029, 1031, 3261, 1053, 1055, 1063, 263, + 726, 1077, 1087, 1111, 3257, 405, 1122, 1128, 1117, 193, + 380, 1148, 1155, 1160, 1175, 874, 323, 1189, 1205, 1204, + + 3254, 1226, 997, 1221, 1249, 1241, 750, 933, 1259, 802, + 1271, 1268, 1163, 259, 1294, 1299, 948, 1320, 384, 1310, + 1309, 3235, 1333, 1336, 522, 1356, 1354, 1365, 3228, 618, + 1386, 1395, 1405, 1396, 144, 3204, 1418, 1425, 1433, 559, + 1442, 1021, 579, 1459, 1402, 1475, 1498, 1195, 1327, 1472, + 825, 1490, 1114, 1505, 1521, 1532, 1537, 1552, 1542, 1552, + 1370, 1572, 378, 1574, 938, 1580, 408, 656, 1576, 1608, + 3204, 783, 1596, 1625, 340, 1616, 1645, 1634, 1636, 1658, + 1686, 3202, 3190, 1688, 1714, 372, 3164, 3154, 599, 1666, + 1715, 417, 1697, 1742, 235, 868, 1747, 1762, 1149, 1755, + + 1714, 1790, 3135, 934, 1792, 1727, 1795, 585, 1786, 669, + 1314, 1170, 1801, 1827, 725, 1231, 3127, 1813, 1838, 3119, + 1275, 1532, 3112, 1044, 738, 3125, 3120, 9866, 3114, 9866, + 3115, 9866, 9866, 631, 3104, 1879, 1889, 3088, 3087, 0, + 1931, 1960, 1968, 1976, 3063, 2010, 9866, 9866, 9866, 9866, + 9866, 2039, 3042, 3035, 9866, 3007, 3002, 771, 1162, 1297, + 1039, 1321, 1371, 1455, 927, 1906, 1574, 1453, 853, 2963, + 2984, 1660, 1120, 1591, 1671, 1728, 1774, 2972, 1556, 1236, + 1869, 1240, 1880, 1483, 1883, 1425, 1699, 1643, 2013, 1721, + 1755, 1372, 1814, 1840, 2032, 1904, 2012, 2033, 1784, 2010, + + 2062, 2064, 1882, 2063, 2009, 1543, 2065, 2001, 2066, 1544, + 2078, 1756, 1610, 2067, 2096, 2080, 2082, 2083, 2098, 2101, + 2115, 2116, 1854, 2117, 2118, 2120, 2119, 2129, 2127, 2132, + 2146, 2153, 2133, 2163, 2165, 2168, 2175, 1999, 2177, 2181, + 2187, 2189, 2191, 1933, 2195, 2198, 2201, 2205, 2207, 2209, + 2000, 2218, 2215, 2221, 2225, 1934, 1525, 2000, 3006, 2001, + 2990, 9866, 426, 2231, 2233, 2237, 2242, 2238, 2248, 2002, + 2251, 2253, 2254, 2257, 2259, 2260, 2268, 2272, 2283, 2265, + 2269, 2287, 2289, 2296, 2300, 2307, 2310, 2311, 2318, 2322, + 2324, 2328, 2337, 2339, 445, 2330, 2351, 2359, 2365, 2368, + + 2375, 2369, 2379, 2946, 2389, 2392, 2385, 2401, 2409, 2415, + 2419, 2422, 2423, 2424, 2425, 2428, 2429, 2432, 2435, 2433, + 2441, 2442, 2461, 2470, 2473, 2474, 2480, 2481, 2485, 2483, + 2488, 2500, 2514, 2490, 2501, 332, 2517, 2502, 962, 2520, + 2924, 2537, 2540, 2544, 2549, 2551, 2552, 2553, 2580, 2550, + 2557, 2564, 2568, 2581, 2590, 2591, 2607, 2933, 2457, 2604, + 2583, 2610, 2613, 2617, 2619, 2620, 2630, 2634, 2640, 2637, + 2647, 2651, 2652, 2657, 2641, 2664, 2670, 2667, 2678, 2688, + 2694, 2697, 2702, 2706, 2707, 2720, 2729, 2708, 2747, 2759, + 2755, 2765, 2922, 2711, 2710, 2758, 2773, 2775, 2797, 2805, + + 2774, 2918, 2793, 2811, 2808, 2812, 631, 2915, 2813, 2816, + 2814, 2832, 2831, 2819, 2846, 2847, 2817, 2850, 2861, 2874, + 2868, 2876, 2877, 2878, 2883, 2890, 2888, 2892, 2894, 2896, + 2897, 2906, 2907, 2909, 2911, 2920, 2933, 2940, 2938, 2941, + 2942, 0, 2982, 2992, 2940, 3034, 3017, 0, 0, 1519, + 3069, 3077, 9866, 3085, 2936, 3123, 2913, 842, 3152, 2910, + 2891, 3181, 2871, 0, 2705, 2947, 2950, 3010, 2951, 3027, + 3032, 3175, 3011, 3100, 2959, 3120, 3037, 2856, 2854, 3113, + 3174, 3103, 3122, 3123, 3114, 3151, 3179, 3171, 3195, 3178, + 3198, 3209, 3217, 3221, 3218, 3224, 3225, 3226, 3227, 3228, + + 3230, 3233, 3238, 3239, 3240, 3245, 3244, 3247, 3268, 3271, + 3274, 3275, 3280, 3278, 3284, 3279, 3281, 3292, 3298, 3285, + 3301, 3291, 3295, 3302, 3307, 3311, 3330, 3333, 3335, 3337, + 3340, 3342, 3341, 3343, 3347, 3348, 3349, 3352, 3356, 3357, + 3361, 3353, 3364, 3383, 3390, 3391, 3392, 3399, 3400, 3405, + 3404, 3406, 3411, 3419, 3401, 3418, 3414, 3421, 3423, 3428, + 3433, 3440, 3445, 3449, 3454, 3459, 3460, 3462, 3464, 3467, + 3468, 3469, 3472, 3473, 3475, 3480, 3486, 3490, 3495, 3502, + 3504, 3507, 3510, 3516, 2898, 9866, 2823, 3517, 3521, 3522, + 3525, 3528, 3530, 3531, 3533, 3536, 3538, 3545, 3551, 3549, + + 3552, 3554, 3564, 3560, 3569, 3573, 3580, 3581, 3583, 3584, + 3599, 3588, 3601, 3602, 3604, 2813, 3610, 3616, 3614, 3620, + 3622, 3625, 3630, 3634, 2788, 3638, 3640, 3644, 3642, 3651, + 3653, 3658, 3656, 3660, 3661, 3662, 3664, 3668, 3666, 3670, + 3673, 3674, 3688, 3694, 3700, 3701, 3704, 3710, 3709, 3707, + 3717, 3720, 3723, 3731, 3728, 3733, 3736, 3739, 3750, 3752, + 3754, 3757, 3759, 3760, 3765, 3766, 3767, 3769, 3768, 3771, + 3781, 3786, 3783, 3772, 3795, 3809, 3810, 3813, 3815, 3818, + 3821, 3822, 3823, 3824, 3825, 3826, 3829, 3828, 3830, 3833, + 3842, 3837, 3844, 3845, 3868, 3871, 3852, 3872, 3873, 3874, + + 3879, 3880, 3881, 3886, 3889, 3890, 3891, 3892, 3894, 3895, + 3900, 3908, 3909, 3922, 3926, 3924, 3930, 3936, 3937, 3939, + 3941, 3942, 3943, 3944, 3946, 3950, 3952, 3953, 3959, 3963, + 3986, 3991, 3960, 3965, 3972, 3976, 3981, 3995, 3998, 4000, + 4002, 4005, 4008, 4009, 4012, 4013, 4017, 4019, 4029, 4031, + 4032, 4039, 4045, 4048, 4049, 4051, 4052, 4059, 0, 4090, + 4100, 4091, 2814, 2812, 4108, 4046, 9866, 4134, 2790, 2757, + 2720, 4114, 4156, 4188, 0, 2750, 1292, 4131, 2749, 2709, + 2691, 4219, 4166, 441, 9866, 4190, 4157, 4158, 4186, 4129, + 4180, 4181, 4189, 4187, 4216, 4236, 4185, 4238, 4242, 4243, + + 4246, 4244, 4247, 4254, 4256, 4258, 4257, 4260, 4270, 4273, + 4262, 4274, 4276, 4277, 4286, 4293, 4300, 4304, 4305, 4306, + 4307, 4312, 4314, 4315, 4317, 4320, 4322, 4323, 4326, 4328, + 4336, 4338, 4341, 4355, 4356, 4357, 4359, 4362, 4364, 4369, + 4371, 4372, 4373, 4375, 4378, 4383, 4386, 4391, 4393, 4405, + 4407, 4410, 4412, 4413, 4415, 4419, 4420, 4421, 4422, 4428, + 4425, 4441, 4427, 4451, 4454, 4457, 4461, 4463, 4464, 4467, + 4468, 4477, 4470, 4481, 4478, 4483, 4484, 4490, 4499, 4501, + 4511, 4512, 4514, 4517, 4519, 4521, 4522, 4528, 4529, 4534, + 4530, 4532, 4535, 4540, 4548, 4550, 4551, 4556, 4558, 4567, + + 4561, 4570, 4572, 4577, 4579, 4580, 4587, 4589, 4588, 4590, + 4593, 4591, 4596, 4599, 4617, 4601, 4622, 4627, 4629, 4630, + 4637, 4632, 4638, 4639, 4645, 4641, 4640, 4643, 4646, 4648, + 4649, 4653, 4656, 4658, 4682, 4685, 4686, 4687, 4688, 4689, + 4695, 4696, 4697, 4699, 4701, 4698, 4702, 4704, 4706, 4716, + 4717, 4718, 4732, 4737, 4740, 4738, 4745, 4748, 4746, 4749, + 4751, 4753, 4755, 4759, 4760, 4766, 4768, 4782, 4783, 4787, + 4795, 4788, 4789, 4799, 4790, 4809, 4817, 4818, 4819, 4821, + 4822, 4823, 4826, 4827, 4828, 4833, 4837, 4839, 4849, 4855, + 4856, 4859, 4868, 4872, 4869, 4882, 4876, 4883, 4888, 4889, + + 4895, 4898, 4900, 4903, 4904, 4910, 4909, 4917, 4918, 4919, + 4927, 4930, 4932, 4933, 4937, 4938, 4964, 4946, 4947, 4948, + 4959, 4969, 4970, 4974, 4982, 4977, 4987, 4990, 4992, 4997, + 4996, 5004, 5000, 5005, 5009, 5010, 5014, 5016, 5018, 5024, + 5048, 5031, 5036, 0, 5073, 2689, 1419, 5081, 5105, 4313, + 5108, 2660, 0, 2644, 0, 2624, 9866, 5117, 137, 789, + 2640, 4058, 2636, 2629, 2609, 521, 5125, 5162, 0, 5135, + 595, 5115, 5156, 5038, 5154, 5042, 5159, 5160, 5161, 5177, + 5180, 5187, 5190, 5199, 5200, 5204, 5207, 5206, 5050, 5208, + 5209, 5214, 5221, 5153, 5216, 5222, 5226, 5227, 5228, 5238, + + 5240, 5250, 5254, 5256, 5257, 5259, 5261, 5264, 5268, 5269, + 5270, 5276, 5277, 5280, 5285, 5288, 5287, 5290, 5297, 5300, + 5304, 5306, 5311, 5314, 5318, 5316, 5319, 5323, 5324, 5326, + 5327, 5330, 5338, 5346, 5347, 5354, 5356, 5358, 5360, 5361, + 5365, 5373, 5374, 5376, 5378, 5377, 5382, 5384, 5387, 5388, + 5392, 5389, 5397, 5404, 5405, 5406, 5418, 5419, 5421, 5420, + 5423, 5426, 5427, 5434, 5435, 5437, 5438, 5447, 5442, 5449, + 5457, 5461, 5464, 5469, 5470, 5471, 5477, 5473, 5476, 5478, + 5481, 5483, 5491, 5488, 5487, 5492, 5499, 5500, 5511, 5504, + 5510, 5520, 5523, 5526, 5527, 5532, 5537, 5539, 5540, 5542, + + 5547, 5550, 5553, 5555, 5559, 5556, 5560, 5561, 5570, 5571, + 5573, 5577, 5576, 5582, 5587, 5590, 5594, 5599, 5600, 5603, + 5604, 5605, 5608, 5613, 5614, 5616, 5621, 5623, 5626, 5627, + 5631, 5632, 5634, 5637, 5635, 5640, 5644, 5645, 5650, 5655, + 5661, 5663, 5673, 5671, 5674, 5677, 5682, 5689, 5684, 5690, + 5693, 5694, 5695, 5713, 5697, 5721, 5700, 5723, 5708, 5724, + 5726, 5727, 5729, 5735, 5736, 5743, 5744, 5747, 5750, 5755, + 5762, 5763, 5767, 5774, 5770, 5776, 5777, 5778, 5781, 5784, + 5794, 5805, 5798, 5813, 5800, 5804, 5817, 5812, 5818, 5820, + 5851, 5861, 5885, 2607, 5888, 2589, 2588, 2558, 2556, 2553, + + 9866, 5897, 5907, 2542, 2540, 0, 2532, 2527, 2524, 2470, + 694, 0, 5917, 2392, 3046, 697, 5918, 5826, 5931, 5912, + 5933, 5934, 5935, 5936, 5938, 5940, 5941, 5943, 5946, 5947, + 5948, 5949, 5950, 5953, 5954, 5962, 5967, 5969, 5981, 5983, + 5986, 5988, 5990, 5991, 5993, 5996, 5997, 6002, 6003, 6004, + 6007, 6009, 6017, 6019, 6023, 6025, 6026, 6031, 6041, 6043, + 6047, 6049, 6052, 6053, 6054, 6061, 6063, 6060, 6067, 6070, + 6073, 6075, 6076, 6081, 6083, 6089, 6090, 6091, 6099, 6103, + 6104, 6105, 6109, 6111, 6112, 6113, 6119, 6123, 6125, 6127, + 6132, 6133, 6139, 6141, 6140, 6147, 6149, 6153, 6154, 6155, + + 6161, 6159, 6168, 6171, 6173, 6175, 6181, 6183, 6197, 6199, + 6191, 6203, 6205, 6207, 6211, 6213, 6217, 6218, 6219, 6221, + 6227, 6233, 6229, 6235, 6239, 6240, 6245, 6247, 6256, 6249, + 6251, 6257, 6263, 6267, 6268, 6269, 6271, 6275, 6277, 6279, + 6283, 6284, 6287, 6290, 6295, 6297, 6299, 6301, 6303, 6305, + 6307, 6311, 6334, 6310, 6317, 6321, 6319, 6338, 6340, 6345, + 6341, 6351, 6353, 6356, 6358, 6360, 6362, 6364, 6369, 6371, + 6368, 6374, 6377, 6375, 6379, 6384, 6390, 6391, 6388, 6422, + 2488, 2473, 2445, 2432, 0, 2424, 0, 2422, 2416, 0, + 6432, 2387, 2373, 2372, 6440, 2369, 2368, 2363, 818, 900, + + 1071, 6469, 6479, 1199, 1556, 0, 1600, 6412, 6401, 6459, + 6468, 6494, 6495, 6496, 6497, 6498, 6499, 6500, 6502, 6504, + 6505, 6506, 6508, 6509, 6510, 6511, 6514, 6512, 6513, 6515, + 6518, 6526, 6532, 6544, 6545, 6547, 6548, 6549, 6556, 6558, + 6559, 6560, 6561, 6562, 6564, 6563, 6565, 6567, 6571, 6577, + 6578, 6582, 6579, 6594, 6595, 6598, 6605, 6606, 6608, 6611, + 6613, 6616, 6614, 6617, 6625, 6621, 6626, 6627, 6628, 6634, + 6632, 6640, 6644, 6645, 6647, 6649, 6656, 6658, 6663, 6664, + 6667, 6671, 6675, 6676, 6679, 6682, 6684, 6686, 6699, 6691, + 6697, 6702, 6703, 6705, 6717, 6718, 6726, 6708, 6735, 6741, + + 6736, 6746, 6753, 6754, 6740, 6752, 6748, 6764, 6762, 6763, + 6767, 6768, 6769, 6774, 6776, 6775, 6781, 6804, 2359, 2358, + 2342, 2332, 2327, 2324, 2305, 2281, 2267, 2263, 6817, 6827, + 2260, 2252, 0, 6837, 2241, 2213, 2177, 1683, 0, 1841, + 1852, 0, 6868, 2195, 1901, 1980, 6876, 6807, 6780, 6865, + 6873, 6891, 6892, 6893, 6894, 6895, 6899, 6901, 6902, 6903, + 6904, 6911, 6912, 6913, 6914, 6915, 6922, 6923, 6930, 6931, + 6932, 6934, 6935, 6942, 6941, 6944, 6945, 6949, 6951, 6952, + 6953, 6954, 6961, 6962, 6963, 6971, 6980, 6981, 6982, 6984, + 6985, 6990, 6991, 6993, 6995, 6997, 6999, 7002, 7003, 7011, + + 7013, 7014, 7019, 7023, 7029, 7031, 7032, 7035, 7042, 7043, + 7045, 7047, 7049, 7051, 7064, 7065, 7066, 7073, 7069, 7075, + 9866, 2180, 2172, 2169, 0, 2141, 0, 2123, 2121, 0, + 2120, 2099, 0, 7114, 2096, 2085, 2072, 7122, 7132, 2070, + 2065, 2063, 2071, 2122, 2171, 2178, 2278, 7161, 7171, 2301, + 2319, 0, 7181, 7079, 7081, 7196, 7197, 7082, 7093, 7151, + 7198, 7203, 7199, 7204, 7206, 7208, 7209, 7217, 7218, 7219, + 7226, 7227, 7232, 7235, 7236, 7237, 7239, 7240, 7246, 7250, + 7247, 7255, 7248, 7259, 7263, 7266, 7267, 7277, 7268, 7283, + 7286, 7296, 7299, 7297, 7300, 7305, 2000, 1997, 1994, 1993, + + 1991, 1990, 1989, 1984, 1983, 1943, 1942, 1940, 7340, 1939, + 1935, 0, 7350, 820, 1932, 1921, 1890, 2425, 0, 2509, + 2547, 0, 2585, 2695, 0, 7358, 1910, 2698, 2700, 7366, + 7381, 7307, 7315, 7309, 7380, 7316, 7381, 7383, 7398, 7318, + 7399, 7400, 7403, 7404, 7409, 7417, 7418, 7419, 7420, 7426, + 7430, 7433, 7438, 7436, 7439, 7440, 7441, 7449, 7453, 7467, + 7454, 7457, 1860, 1858, 1854, 0, 1847, 0, 1843, 1837, + 0, 1832, 1798, 0, 1768, 1754, 0, 9866, 1745, 1724, + 7505, 7515, 1722, 1716, 1694, 2702, 2752, 2755, 2756, 2801, + 2859, 2934, 7523, 2948, 3003, 0, 7533, 3047, 7469, 7470, + + 7473, 7476, 7538, 7547, 7483, 7548, 7550, 7551, 7552, 7553, + 7554, 7555, 7556, 7560, 7565, 7562, 7566, 7569, 7568, 7570, + 1690, 1684, 1679, 1666, 1662, 1653, 1639, 1635, 1634, 1633, + 1624, 1616, 1603, 1545, 1515, 1514, 0, 7604, 1520, 1497, + 1496, 1467, 3004, 0, 3085, 3091, 0, 3093, 3095, 0, + 3154, 3215, 0, 1458, 3337, 3396, 7613, 7623, 7628, 7574, + 7637, 7638, 7571, 7640, 7639, 7641, 7642, 7643, 7646, 7647, + 7648, 1449, 1448, 1432, 0, 1412, 0, 1410, 1403, 0, + 1395, 1392, 0, 1389, 1373, 0, 1363, 1359, 0, 1313, + 1290, 7648, 7684, 1289, 1284, 1252, 3452, 3457, 3462, 3597, + + 3698, 3704, 3996, 4027, 4116, 4121, 4125, 0, 7694, 1256, + 7649, 7650, 7683, 7651, 7684, 7710, 7712, 7711, 7713, 7714, + 1244, 1231, 1209, 1205, 0, 1176, 0, 1169, 0, 1165, + 0, 1162, 0, 1124, 0, 1119, 1117, 0, 7709, 1125, + 1111, 1102, 1089, 4126, 0, 4127, 4252, 0, 4351, 4378, + 0, 4824, 4873, 0, 4927, 4962, 0, 4994, 5032, 7754, + 7764, 7723, 7725, 7744, 7732, 7731, 7755, 7781, 7727, 1093, + 0, 9866, 0, 1088, 0, 1070, 0, 1055, 0, 1054, + 0, 1048, 0, 1038, 0, 7783, 1037, 1035, 1020, 987, + 5038, 969, 5039, 964, 5046, 943, 5087, 921, 5088, 917, + + 5090, 5091, 0, 7791, 920, 7783, 7782, 7784, 7785, 7729, + 901, 0, 0, 0, 0, 0, 0, 0, 893, 0, + 9866, 869, 853, 826, 819, 5092, 797, 5138, 776, 5141, + 769, 5204, 756, 5628, 740, 5691, 738, 7812, 7811, 7812, + 7826, 7827, 0, 9866, 9866, 9866, 9866, 9866, 9866, 0, + 745, 661, 644, 591, 572, 563, 555, 517, 5808, 489, + 481, 7828, 7829, 7832, 7831, 9866, 452, 414, 328, 310, + 286, 272, 232, 141, 7839, 7840, 7842, 98, 7843, 7844, + 7846, 9866, 7903, 7916, 7927, 7934, 7946, 7953, 7959, 7965, + 7971, 7983, 7992, 7999, 8010, 8016, 8022, 8028, 8034, 8043, + + 8050, 8057, 8063, 8075, 8081, 8087, 8093, 8099, 8105, 8111, + 8117, 8128, 8139, 8145, 8151, 8157, 8163, 8171, 8177, 8183, + 8189, 8195, 8201, 8212, 8218, 8224, 8230, 8236, 8242, 8248, + 8254, 8260, 8266, 8272, 8278, 8284, 8290, 8298, 8304, 8310, + 8316, 8322, 8328, 8334, 8340, 8346, 8352, 8358, 8364, 8370, + 8376, 8382, 8388, 8394, 8400, 8406, 8412, 8418, 8424, 8430, + 8436, 8442, 8448, 8454, 8460, 8466, 8472, 8478, 8484, 8490, + 8496, 8502, 8508, 8514, 8520, 8526, 8532, 8538, 8544, 8550, + 8556, 8562, 8568, 8574, 8580, 8586, 8592, 8598, 8604, 8610, + 8616, 8622, 8628, 8634, 8640, 8646, 8652, 8658, 8664, 8670, + + 8676, 8682, 8688, 8694, 8700, 8706, 8712, 8718, 8724, 8730, + 8738, 8744, 8750, 8756, 8762, 8768, 8774, 8780, 8786, 8792, + 8798, 8804, 8810, 8816, 8822, 8828, 8834, 8840, 8846, 8852, + 8858, 8864, 8870, 8876, 8882, 8888, 8894, 8900, 8906, 8912, + 8918, 8924, 8930, 8936, 8942, 8948, 8954, 8962, 8968, 8974, + 8980, 8986, 8992, 8998, 9004, 9010, 9016, 9022, 9028, 9034, + 9040, 9046, 9052, 9058, 9064, 9070, 9076, 9082, 9088, 9094, + 9100, 9106, 9112, 9118, 9124, 9130, 9140, 9147, 9153, 9159, + 9165, 9171, 9177, 9183, 9189, 9195, 9201, 9207, 9213, 9219, + 9225, 9231, 9237, 9243, 9249, 9255, 9261, 9267, 9273, 9279, + + 9285, 9291, 9297, 9303, 9309, 9315, 9325, 9332, 9338, 9344, + 9350, 9356, 9362, 9368, 9374, 9380, 9386, 9392, 9398, 9404, + 9410, 9416, 9422, 9428, 9434, 9440, 9446, 9452, 9458, 9464, + 9470, 9476, 9482, 9488, 9494, 9500, 9506, 9512, 9522, 9529, + 9535, 9541, 9547, 9553, 9559, 9565, 9571, 9577, 9583, 9589, + 9595, 9601, 9607, 9613, 9619, 9625, 9631, 9637, 9643, 9649, + 9655, 9661, 9667, 9673, 9679, 9685, 9691, 9697, 9703, 9709, + 9715, 9721, 9727, 9733, 9739, 9745, 9751, 9757, 9763, 9769, + 9775, 9781, 9787, 9793, 9799, 9805, 9811, 9817, 9823, 9829, + 9835, 9841, 9847, 9853 + + } ; + +static const flex_int16_t yy_def[2695] = + { 0, + 2382, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 8, + 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 37, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2382, 2382, 2382, 2382, 2382, 2382, 2383, 2384, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2385, 2382, 2382, 129, + 129, 2382, 2382, 2382, 2382, 2382, 2382, 2385, 2385, 2386, + 2382, 2382, 2382, 138, 138, 138, 138, 138, 138, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2382, 2382, 2382, 2382, 2382, + 2387, 138, 2385, 2385, 2385, 161, 2385, 2385, 138, 138, + 138, 138, 138, 2385, 152, 177, 2385, 2385, 2385, 161, + 2385, 2385, 2385, 2385, 2385, 138, 182, 2385, 2385, 2385, + + 161, 2385, 138, 2385, 2385, 138, 182, 138, 2385, 177, + 2385, 2385, 2385, 161, 2385, 2385, 138, 2385, 177, 2385, + 2385, 215, 2385, 2385, 138, 2385, 2385, 2385, 161, 2385, + 2385, 2385, 2385, 138, 182, 161, 2385, 138, 138, 182, + 138, 138, 177, 177, 2385, 2385, 2385, 161, 215, 2385, + 2385, 138, 182, 138, 182, 2385, 2385, 2385, 2385, 138, + 138, 2385, 177, 244, 2385, 2385, 161, 215, 138, 2385, + 161, 215, 138, 2385, 161, 138, 2385, 244, 2385, 244, + 2385, 161, 152, 2385, 2385, 161, 152, 215, 138, 138, + 2385, 177, 244, 2385, 161, 215, 138, 138, 182, 138, + + 138, 2385, 152, 177, 2385, 2385, 161, 215, 138, 177, + 215, 2385, 138, 2385, 177, 2385, 161, 138, 2385, 152, + 2385, 244, 161, 215, 2385, 2382, 2382, 2382, 2383, 2382, + 2384, 2382, 2382, 2385, 2382, 2382, 2382, 2388, 2389, 2390, + 2382, 2382, 2382, 2382, 337, 2382, 2382, 2382, 2382, 2382, + 2382, 138, 2391, 2382, 2382, 352, 352, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 352, + 352, 352, 2385, 2385, 2385, 2385, 2385, 352, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2382, 2382, 2392, 2382, + 2387, 2382, 352, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 352, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 352, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 352, 2385, 2385, 352, 2385, + 352, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 352, 352, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 352, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 352, 2385, 2385, 2385, 2385, 352, 352, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2393, 2382, 2382, 2394, 2382, 2395, 2396, 2390, 646, + 2382, 2382, 2382, 2382, 644, 2382, 2397, 2397, 352, 2398, + 2399, 2382, 659, 659, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 659, 659, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2392, 2382, 659, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 659, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 659, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2400, 2382, + 2382, 2401, 2402, 2382, 2382, 2403, 2382, 2404, 2405, 2406, + 2382, 2401, 2382, 2382, 2407, 2408, 2408, 2385, 2409, 2410, + 2411, 2382, 982, 983, 2382, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2400, 2382, 2382, 972, 2382, 2412, 2403, + 2413, 2414, 2415, 2416, 2417, 2382, 2382, 2382, 2418, 2418, + 2419, 2418, 2382, 2420, 2421, 2422, 2382, 2382, 2423, 1268, + 1270, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2382, 2382, 2424, 2425, 2382, 2426, 2427, 2428, 2429, 2430, + + 2382, 2382, 2382, 2382, 2431, 2432, 2382, 2433, 2434, 2435, + 2436, 2437, 2382, 2438, 2438, 2439, 2438, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2382, + 2382, 2440, 2441, 2442, 2443, 2444, 2445, 2382, 2446, 2447, + 2382, 2382, 2448, 2449, 2382, 2382, 2450, 2451, 2452, 2453, + + 2454, 2382, 2382, 2382, 2455, 2456, 2382, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2382, 2457, 2458, + 2382, 2459, 2460, 2461, 2462, 2463, 2464, 2465, 2382, 2382, + 2382, 2466, 2467, 2382, 2468, 2469, 2470, 2471, 2472, 2382, + 2473, 2474, 2382, 2382, 2475, 2476, 2382, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2382, 2477, 2478, 2479, 2480, 2481, 2482, 2382, 2483, 2484, + 2382, 2485, 2486, 2382, 2382, 2487, 2488, 2382, 2382, 2382, + 2489, 2490, 2491, 2492, 2493, 2494, 2495, 2382, 2382, 2382, + 2496, 2497, 2382, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2498, 2499, 2382, 2500, + + 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2382, 2382, + 2509, 2510, 2382, 2511, 2512, 2513, 2514, 2515, 2516, 2382, + 2517, 2518, 2382, 2519, 2520, 2382, 2382, 2521, 2522, 2382, + 2382, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2523, 2524, 2525, 2526, 2527, 2528, 2382, 2529, + 2530, 2382, 2531, 2532, 2382, 2533, 2534, 2382, 2535, 2536, + 2382, 2382, 2382, 2537, 2538, 2539, 2540, 2541, 2542, 2543, + 2544, 2545, 2382, 2382, 2546, 2547, 2382, 2548, 2385, 2385, + + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, 2385, + 2549, 2550, 2382, 2551, 2552, 2553, 2554, 2555, 2556, 2557, + 2558, 2559, 2560, 2561, 2382, 2562, 2563, 2382, 2382, 2564, + 2565, 2566, 2567, 2568, 2382, 2569, 2570, 2382, 2571, 2572, + 2382, 2573, 2574, 2382, 2575, 2576, 2382, 2382, 2577, 2577, + 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, + 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2382, 2584, 2585, + 2382, 2586, 2587, 2382, 2588, 2589, 2382, 2590, 2591, 2592, + 2593, 2382, 2382, 2382, 2594, 2595, 2596, 2597, 2598, 2599, + + 2600, 2601, 2602, 2603, 2604, 2382, 2605, 2606, 2382, 2382, + 2607, 2607, 2607, 2607, 2607, 2607, 2607, 2607, 2607, 2607, + 2608, 2382, 2609, 2610, 2611, 2612, 2613, 2614, 2615, 2616, + 2617, 2618, 2619, 2620, 2621, 2382, 2622, 2623, 2382, 2382, + 2624, 2625, 2626, 2627, 2628, 2382, 2629, 2630, 2382, 2631, + 2632, 2382, 2633, 2634, 2382, 2635, 2636, 2637, 2638, 2382, + 2382, 2639, 2639, 2639, 2639, 2639, 2639, 2639, 2639, 2640, + 2641, 2382, 2642, 2382, 2643, 2382, 2644, 2382, 2645, 2382, + 2646, 2382, 2647, 2648, 2649, 2382, 2382, 2650, 2651, 2652, + 2653, 2654, 2655, 2656, 2657, 2658, 2659, 2660, 2661, 2662, + + 2382, 2663, 2664, 2382, 2382, 2639, 2639, 2639, 2639, 2639, + 2382, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2382, 2672, + 2382, 2673, 2674, 2382, 2675, 2382, 2676, 2382, 2677, 2382, + 2678, 2382, 2679, 2382, 2680, 2681, 2682, 2382, 2639, 2639, + 2639, 2639, 2683, 2382, 2382, 2382, 2382, 2382, 2382, 2684, + 2382, 2685, 2686, 2687, 2688, 2689, 2690, 2691, 2382, 2692, + 2382, 2639, 2639, 2639, 2639, 2382, 2693, 2382, 2382, 2382, + 2382, 2382, 2382, 2694, 2639, 2639, 2639, 2382, 2639, 2639, + 2639, 0, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382 + + } ; + +static const flex_int16_t yy_nxt[9936] = + { 0, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 130, + 130, 130, 130, 130, 131, 132, 133, 134, 135, 136, + 137, 138, 138, 139, 139, 140, 141, 142, 143, 139, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, + 139, 154, 155, 156, 157, 158, 159, 160, 161, 162, + 163, 164, 139, 165, 139, 139, 166, 167, 168, 169, + 172, 170, 172, 169, 171, 170, 173, 177, 171, 178, + 180, 347, 348, 174, 175, 176, 169, 176, 170, 194, + 231, 171, 192, 232, 193, 195, 276, 169, 320, 170, + + 196, 197, 171, 169, 198, 170, 185, 220, 171, 169, + 199, 170, 321, 200, 171, 172, 201, 202, 203, 173, + 257, 219, 204, 209, 223, 194, 174, 175, 205, 220, + 176, 224, 258, 172, 259, 985, 177, 180, 178, 179, + 180, 181, 182, 183, 273, 172, 184, 185, 176, 192, + 186, 193, 973, 187, 188, 209, 189, 190, 265, 266, + 176, 220, 975, 194, 191, 169, 2382, 170, 206, 195, + 171, 207, 169, 217, 170, 218, 235, 171, 985, 219, + 169, 220, 170, 221, 370, 171, 176, 222, 169, 195, + 170, 236, 334, 171, 169, 2382, 170, 180, 169, 171, + + 170, 252, 253, 171, 179, 180, 181, 182, 183, 192, + 227, 184, 185, 203, 194, 186, 233, 204, 187, 188, + 287, 189, 190, 205, 280, 225, 281, 313, 235, 191, + 169, 231, 170, 469, 232, 171, 226, 192, 227, 193, + 223, 228, 229, 236, 219, 333, 230, 224, 194, 234, + 235, 169, 488, 170, 254, 255, 171, 169, 219, 170, + 185, 169, 171, 170, 257, 236, 171, 256, 237, 985, + 335, 196, 197, 350, 351, 198, 258, 185, 259, 598, + 280, 199, 281, 599, 200, 345, 282, 201, 202, 206, + 235, 203, 207, 169, 208, 170, 209, 185, 171, 469, + + 210, 277, 2382, 211, 212, 236, 213, 214, 215, 985, + 216, 206, 145, 203, 207, 148, 208, 515, 209, 169, + 363, 170, 210, 985, 171, 211, 212, 473, 213, 214, + 215, 2382, 216, 169, 276, 170, 139, 146, 171, 169, + 217, 170, 218, 152, 171, 220, 219, 985, 220, 157, + 221, 269, 160, 176, 222, 355, 195, 169, 257, 170, + 234, 235, 171, 495, 333, 985, 270, 271, 272, 219, + 258, 334, 259, 180, 663, 333, 236, 209, 334, 237, + 252, 253, 853, 220, 500, 192, 227, 274, 275, 203, + 235, 333, 233, 194, 169, 579, 170, 225, 219, 171, + + 335, 169, 169, 170, 170, 236, 171, 171, 226, 192, + 227, 193, 469, 228, 229, 169, 335, 170, 230, 238, + 171, 239, 240, 241, 242, 481, 568, 590, 508, 334, + 243, 244, 245, 246, 489, 334, 247, 248, 249, 250, + 209, 251, 254, 255, 260, 482, 220, 261, 185, 262, + 257, 985, 573, 263, 264, 256, 334, 595, 265, 266, + 267, 268, 258, 257, 259, 169, 206, 170, 334, 787, + 171, 318, 574, 1271, 169, 258, 170, 259, 169, 171, + 170, 319, 169, 171, 170, 222, 1271, 171, 816, 985, + 677, 169, 169, 170, 170, 208, 171, 171, 185, 169, + + 203, 170, 278, 220, 171, 209, 273, 279, 169, 677, + 170, 220, 169, 171, 170, 274, 235, 171, 985, 260, + 265, 266, 261, 269, 262, 333, 985, 277, 263, 264, + 257, 236, 333, 265, 266, 267, 268, 209, 270, 271, + 272, 172, 258, 220, 259, 192, 1512, 274, 275, 275, + 335, 288, 208, 195, 985, 185, 176, 335, 985, 278, + 220, 403, 172, 280, 279, 281, 169, 283, 170, 282, + 169, 171, 170, 284, 404, 171, 285, 286, 169, 363, + 170, 428, 525, 171, 289, 169, 473, 170, 290, 169, + 171, 170, 985, 169, 171, 170, 291, 309, 171, 370, + + 985, 384, 222, 169, 385, 170, 310, 334, 171, 985, + 386, 203, 209, 538, 311, 387, 209, 312, 220, 169, + 172, 170, 220, 542, 171, 283, 274, 1517, 985, 333, + 334, 284, 287, 289, 285, 286, 280, 290, 281, 334, + 1517, 192, 333, 622, 334, 291, 206, 288, 252, 195, + 169, 222, 170, 209, 335, 171, 363, 292, 293, 294, + 309, 222, 529, 592, 295, 296, 169, 335, 170, 310, + 457, 171, 458, 314, 169, 459, 170, 311, 315, 171, + 312, 985, 221, 316, 169, 317, 170, 322, 192, 171, + 924, 364, 365, 323, 324, 677, 575, 325, 985, 366, + + 367, 169, 368, 170, 369, 206, 171, 252, 222, 169, + 334, 170, 209, 624, 171, 334, 292, 293, 294, 1512, + 334, 320, 1706, 295, 296, 297, 235, 298, 299, 300, + 301, 985, 302, 303, 985, 321, 304, 169, 305, 170, + 306, 236, 171, 307, 308, 169, 314, 170, 206, 333, + 171, 315, 169, 318, 170, 221, 316, 171, 317, 169, + 169, 170, 170, 319, 171, 171, 458, 222, 460, 629, + 2289, 459, 364, 365, 335, 985, 334, 985, 313, 235, + 474, 367, 333, 368, 297, 369, 298, 299, 300, 301, + 333, 302, 303, 985, 236, 304, 641, 305, 334, 306, + + 203, 235, 307, 308, 2382, 334, 985, 335, 505, 219, + 222, 322, 192, 985, 975, 335, 236, 323, 324, 393, + 394, 325, 336, 575, 337, 337, 337, 337, 337, 337, + 337, 337, 338, 395, 985, 1938, 333, 334, 339, 339, + 578, 340, 334, 1839, 333, 1506, 508, 339, 339, 339, + 341, 339, 339, 334, 342, 985, 985, 356, 357, 343, + 509, 335, 358, 985, 333, 344, 359, 975, 360, 335, + 340, 333, 361, 556, 977, 362, 448, 352, 352, 352, + 352, 352, 352, 352, 352, 338, 449, 977, 450, 335, + 985, 352, 352, 370, 2289, 371, 335, 372, 378, 333, + + 352, 352, 352, 352, 352, 352, 373, 379, 600, 333, + 374, 375, 380, 376, 381, 382, 333, 377, 2238, 383, + 364, 493, 334, 333, 335, 1512, 2224, 334, 494, 367, + 388, 368, 389, 369, 335, 2260, 333, 985, 333, 396, + 397, 335, 390, 333, 398, 391, 399, 392, 335, 333, + 400, 401, 405, 402, 985, 425, 406, 333, 985, 426, + 407, 335, 427, 335, 333, 411, 408, 671, 335, 412, + 409, 410, 413, 414, 335, 333, 416, 417, 508, 415, + 985, 384, 335, 333, 385, 334, 418, 420, 613, 335, + 506, 421, 419, 429, 422, 387, 384, 430, 571, 519, + + 335, 985, 333, 423, 424, 386, 985, 333, 335, 856, + 387, 431, 432, 433, 434, 688, 435, 463, 455, 371, + 436, 372, 437, 456, 985, 438, 439, 335, 333, 440, + 373, 442, 335, 443, 374, 375, 441, 376, 444, 445, + 333, 377, 333, 364, 493, 2324, 446, 451, 452, 453, + 333, 366, 367, 335, 368, 447, 369, 393, 394, 454, + 2289, 541, 2196, 2238, 333, 335, 333, 335, 464, 384, + 420, 395, 385, 2189, 421, 335, 465, 422, 386, 2186, + 2183, 425, 467, 387, 466, 426, 423, 424, 427, 335, + 468, 335, 640, 405, 667, 2180, 1842, 406, 334, 416, + + 417, 407, 471, 334, 470, 356, 357, 408, 985, 418, + 358, 409, 410, 2177, 359, 419, 360, 463, 2224, 371, + 361, 372, 333, 362, 472, 475, 985, 2289, 333, 378, + 373, 333, 476, 333, 374, 375, 2196, 376, 379, 333, + 2192, 377, 2238, 380, 2137, 381, 382, 335, 477, 2189, + 478, 393, 394, 335, 370, 479, 335, 429, 335, 333, + 683, 487, 334, 420, 335, 395, 333, 421, 485, 334, + 422, 333, 505, 333, 333, 431, 432, 433, 425, 483, + 424, 333, 426, 484, 335, 486, 333, 2186, 490, 607, + 2183, 335, 416, 417, 2180, 485, 335, 334, 335, 335, + + 333, 2177, 418, 513, 334, 425, 335, 430, 419, 426, + 425, 335, 427, 665, 426, 333, 333, 491, 626, 492, + 514, 431, 432, 433, 1269, 335, 451, 452, 453, 496, + 2272, 388, 333, 389, 2224, 551, 985, 333, 454, 500, + 335, 335, 333, 390, 499, 497, 391, 333, 392, 412, + 534, 333, 413, 414, 425, 552, 2125, 335, 426, 415, + 333, 427, 335, 388, 498, 389, 501, 335, 443, 2125, + 333, 2157, 335, 444, 445, 390, 335, 2243, 502, 333, + 392, 446, 333, 356, 504, 335, 333, 691, 358, 411, + 447, 630, 359, 412, 360, 335, 413, 414, 361, 393, + + 394, 362, 507, 415, 335, 333, 503, 335, 333, 2196, + 333, 335, 420, 395, 2085, 2238, 421, 975, 425, 422, + 333, 333, 426, 636, 1262, 511, 510, 512, 423, 424, + 335, 333, 333, 335, 442, 335, 443, 1262, 2137, 518, + 666, 444, 445, 492, 333, 335, 335, 333, 516, 446, + 451, 452, 453, 517, 521, 417, 335, 335, 447, 425, + 393, 394, 454, 426, 418, 333, 522, 333, 334, 335, + 419, 625, 335, 334, 520, 553, 333, 523, 417, 448, + 668, 334, 333, 333, 2189, 554, 334, 418, 2077, 449, + 335, 450, 335, 419, 524, 420, 411, 333, 2186, 421, + + 412, 335, 422, 413, 526, 527, 333, 335, 335, 430, + 415, 423, 424, 333, 2074, 484, 333, 2183, 384, 669, + 2071, 566, 335, 431, 432, 433, 411, 386, 2180, 333, + 530, 335, 387, 413, 414, 2068, 333, 2177, 335, 531, + 415, 335, 364, 533, 2382, 425, 416, 417, 448, 426, + 366, 367, 427, 368, 335, 369, 547, 2224, 532, 2382, + 450, 335, 419, 524, 333, 699, 333, 536, 357, 451, + 452, 453, 358, 2125, 2125, 2382, 359, 535, 360, 364, + 493, 454, 361, 333, 539, 362, 333, 537, 367, 335, + 368, 335, 369, 379, 333, 985, 540, 676, 380, 543, + + 381, 382, 544, 412, 985, 383, 413, 414, 335, 333, + 334, 335, 670, 415, 545, 448, 420, 546, 334, 335, + 421, 2196, 2085, 422, 695, 449, 457, 450, 458, 555, + 548, 459, 423, 424, 335, 2081, 364, 365, 429, 2137, + 2012, 557, 549, 333, 366, 367, 550, 368, 333, 369, + 558, 364, 365, 333, 333, 333, 431, 432, 433, 366, + 367, 370, 368, 333, 369, 559, 342, 333, 335, 334, + 2189, 343, 637, 335, 560, 334, 334, 344, 335, 335, + 335, 1706, 425, 333, 403, 333, 426, 334, 335, 427, + 638, 333, 335, 985, 563, 561, 455, 404, 364, 564, + + 689, 456, 333, 451, 562, 453, 565, 367, 335, 368, + 335, 369, 393, 394, 569, 454, 335, 334, 570, 333, + 429, 333, 364, 564, 430, 1847, 567, 335, 2077, 334, + 366, 367, 334, 368, 675, 369, 333, 985, 431, 432, + 572, 2186, 364, 365, 335, 684, 335, 333, 429, 2074, + 565, 367, 430, 368, 333, 369, 333, 701, 2183, 2071, + 2180, 335, 364, 365, 2068, 429, 431, 432, 576, 549, + 366, 580, 335, 368, 411, 369, 429, 334, 2177, 335, + 583, 335, 333, 431, 432, 433, 420, 2175, 582, 334, + 421, 2125, 334, 422, 431, 432, 433, 333, 411, 333, + + 581, 334, 423, 424, 2122, 679, 593, 335, 1839, 2066, + 333, 680, 584, 334, 384, 2122, 334, 385, 681, 2142, + 985, 682, 335, 386, 335, 333, 333, 420, 387, 420, + 685, 585, 333, 421, 422, 335, 422, 411, 333, 333, + 334, 2085, 700, 423, 424, 423, 424, 1942, 588, 2137, + 335, 335, 334, 333, 429, 334, 596, 335, 589, 416, + 417, 601, 384, 335, 335, 385, 333, 333, 610, 418, + 2012, 386, 431, 432, 433, 594, 387, 425, 335, 2077, + 704, 426, 616, 686, 427, 333, 597, 417, 602, 356, + 357, 335, 335, 1933, 358, 333, 418, 608, 359, 705, + + 360, 333, 419, 333, 361, 603, 379, 362, 364, 365, + 335, 380, 609, 381, 382, 732, 604, 367, 383, 368, + 335, 369, 605, 2074, 606, 333, 335, 687, 335, 713, + 393, 394, 623, 365, 611, 617, 614, 417, 333, 618, + 366, 367, 619, 368, 395, 369, 418, 364, 493, 333, + 335, 333, 615, 620, 621, 366, 367, 1930, 368, 627, + 369, 384, 2071, 335, 385, 333, 1512, 706, 1927, 388, + 386, 389, 2068, 632, 335, 387, 335, 1842, 985, 2125, + 333, 390, 633, 2122, 628, 2122, 392, 707, 425, 985, + 335, 333, 634, 333, 333, 522, 643, 643, 643, 643, + + 643, 643, 643, 643, 336, 335, 644, 644, 644, 644, + 644, 644, 644, 644, 338, 333, 335, 333, 335, 335, + 645, 645, 722, 696, 690, 1948, 1706, 985, 692, 645, + 645, 645, 646, 645, 645, 693, 342, 697, 985, 694, + 335, 343, 335, 698, 333, 333, 2085, 344, 650, 650, + 650, 650, 650, 650, 650, 650, 338, 1942, 672, 673, + 2012, 710, 645, 645, 1833, 2077, 674, 1933, 2074, 335, + 335, 645, 645, 645, 645, 645, 645, 651, 651, 651, + 651, 651, 651, 651, 651, 652, 652, 652, 652, 652, + 652, 652, 652, 654, 654, 654, 654, 654, 654, 654, + + 654, 458, 458, 458, 460, 1952, 459, 459, 1930, 2071, + 333, 333, 333, 333, 1927, 2068, 2066, 985, 2001, 1998, + 333, 333, 1925, 333, 333, 1998, 653, 656, 656, 656, + 656, 656, 656, 656, 656, 335, 335, 335, 335, 767, + 780, 657, 658, 333, 333, 335, 335, 727, 335, 335, + 657, 657, 657, 657, 657, 658, 659, 659, 659, 659, + 659, 659, 659, 659, 711, 714, 702, 724, 335, 335, + 659, 659, 703, 333, 333, 333, 333, 333, 333, 659, + 659, 659, 659, 659, 659, 712, 708, 720, 2017, 333, + 1942, 333, 709, 333, 333, 1698, 2019, 2012, 335, 335, + + 335, 335, 335, 335, 715, 716, 717, 333, 985, 333, + 1833, 1829, 333, 728, 335, 723, 335, 725, 335, 335, + 718, 719, 721, 726, 1933, 733, 333, 333, 333, 333, + 333, 333, 335, 729, 335, 730, 731, 335, 333, 735, + 333, 736, 737, 333, 333, 1690, 1930, 1839, 1687, 734, + 738, 335, 335, 335, 335, 335, 335, 333, 739, 985, + 740, 741, 742, 335, 333, 335, 1927, 746, 335, 335, + 757, 744, 758, 747, 333, 750, 333, 743, 745, 333, + 749, 751, 335, 748, 760, 752, 333, 753, 333, 335, + 754, 755, 333, 756, 2001, 759, 2022, 1998, 333, 335, + + 333, 335, 333, 1842, 335, 1998, 333, 762, 985, 333, + 1702, 335, 333, 335, 985, 985, 333, 335, 333, 768, + 333, 765, 761, 335, 763, 335, 333, 335, 769, 333, + 772, 335, 333, 764, 335, 766, 333, 335, 1942, 770, + 771, 335, 333, 335, 333, 335, 773, 774, 333, 333, + 778, 335, 779, 333, 335, 775, 776, 335, 781, 333, + 777, 335, 333, 782, 333, 333, 1698, 335, 333, 335, + 333, 333, 783, 335, 335, 788, 333, 1833, 335, 333, + 333, 790, 784, 333, 335, 1506, 791, 335, 1933, 335, + 335, 793, 1690, 335, 333, 335, 335, 789, 333, 794, + + 333, 335, 792, 2025, 335, 335, 1930, 333, 335, 672, + 673, 333, 799, 795, 798, 985, 797, 796, 333, 335, + 692, 333, 333, 335, 802, 335, 1706, 800, 803, 333, + 1687, 694, 335, 333, 801, 333, 335, 804, 985, 333, + 805, 333, 806, 335, 1952, 724, 335, 335, 333, 1927, + 333, 809, 1925, 750, 335, 746, 985, 1823, 335, 751, + 335, 807, 333, 752, 335, 808, 335, 1820, 754, 755, + 333, 810, 746, 335, 811, 335, 333, 671, 812, 333, + 333, 814, 815, 1685, 1820, 813, 333, 335, 1837, 817, + 333, 672, 673, 1698, 1265, 335, 333, 1833, 1506, 674, + + 333, 335, 1502, 333, 335, 335, 729, 1267, 730, 731, + 762, 335, 333, 705, 818, 335, 772, 1269, 822, 819, + 333, 335, 820, 823, 821, 335, 333, 763, 335, 985, + 333, 826, 827, 333, 333, 333, 333, 335, 824, 333, + 333, 1690, 828, 333, 333, 335, 333, 1255, 685, 1687, + 2019, 335, 333, 333, 829, 335, 728, 1823, 335, 335, + 335, 335, 985, 832, 335, 335, 830, 746, 335, 335, + 1820, 335, 333, 812, 831, 696, 749, 335, 335, 833, + 835, 333, 834, 836, 333, 333, 832, 708, 838, 837, + 746, 333, 333, 709, 333, 698, 333, 335, 1820, 333, + + 735, 333, 679, 1491, 839, 878, 335, 985, 680, 335, + 335, 333, 333, 333, 841, 681, 335, 335, 682, 335, + 735, 335, 840, 845, 335, 333, 335, 843, 333, 846, + 842, 333, 733, 749, 1839, 847, 335, 335, 335, 844, + 763, 852, 732, 778, 851, 848, 985, 855, 333, 1698, + 335, 333, 1265, 335, 671, 333, 335, 1695, 814, 849, + 333, 333, 333, 333, 333, 1506, 861, 975, 333, 672, + 673, 857, 2022, 335, 850, 333, 335, 854, 1690, 333, + 335, 1255, 859, 1687, 985, 335, 335, 335, 335, 335, + 829, 333, 333, 335, 333, 729, 868, 730, 731, 869, + + 335, 333, 333, 860, 335, 870, 864, 865, 862, 863, + 1842, 737, 866, 1685, 1497, 333, 335, 335, 333, 335, + 871, 333, 985, 750, 333, 872, 335, 335, 333, 751, + 333, 333, 1253, 752, 1510, 753, 874, 880, 867, 755, + 335, 333, 873, 335, 875, 333, 335, 876, 333, 335, + 781, 333, 333, 335, 1265, 335, 335, 882, 333, 879, + 696, 661, 333, 333, 881, 1506, 335, 1501, 333, 1255, + 335, 672, 673, 335, 884, 333, 335, 335, 333, 883, + 698, 333, 894, 335, 885, 1497, 886, 335, 335, 333, + 709, 747, 726, 335, 891, 887, 888, 730, 731, 333, + + 335, 889, 890, 335, 960, 333, 335, 896, 333, 770, + 893, 892, 762, 333, 335, 895, 333, 333, 333, 333, + 2025, 333, 333, 1952, 335, 2096, 897, 2144, 985, 892, + 335, 333, 985, 335, 1265, 985, 898, 985, 335, 985, + 333, 335, 335, 335, 335, 750, 335, 335, 675, 902, + 734, 751, 903, 899, 900, 752, 335, 753, 333, 734, + 901, 755, 762, 911, 904, 335, 333, 905, 912, 333, + 333, 715, 716, 717, 661, 975, 333, 2019, 653, 763, + 2147, 2022, 1255, 335, 333, 333, 333, 718, 719, 985, + 750, 335, 985, 985, 335, 335, 751, 908, 716, 717, + + 752, 335, 753, 906, 333, 754, 755, 897, 333, 335, + 335, 335, 907, 718, 719, 1253, 333, 762, 913, 333, + 909, 915, 333, 333, 333, 333, 2150, 333, 333, 335, + 333, 918, 735, 335, 763, 920, 914, 963, 985, 648, + 916, 335, 333, 333, 335, 1132, 772, 335, 335, 335, + 335, 917, 335, 335, 928, 335, 1125, 333, 333, 934, + 922, 333, 764, 672, 673, 921, 1095, 335, 335, 923, + 926, 674, 333, 927, 715, 716, 717, 727, 735, 333, + 929, 931, 335, 335, 2025, 333, 335, 333, 333, 333, + 718, 719, 762, 930, 333, 933, 985, 335, 932, 333, + + 786, 333, 998, 333, 335, 333, 935, 333, 333, 763, + 335, 997, 335, 335, 335, 986, 981, 333, 333, 335, + 333, 936, 333, 942, 335, 938, 335, 937, 335, 939, + 335, 333, 335, 335, 940, 661, 705, 948, 975, 941, + 947, 943, 335, 335, 333, 335, 945, 335, 944, 333, + 748, 333, 333, 333, 972, 946, 335, 949, 333, 2153, + 744, 333, 333, 952, 950, 963, 951, 745, 925, 335, + 333, 985, 919, 1952, 335, 910, 335, 335, 335, 953, + 956, 877, 858, 335, 958, 985, 335, 335, 729, 825, + 730, 731, 462, 957, 988, 335, 954, 960, 955, 961, + + 961, 961, 961, 961, 961, 961, 961, 336, 786, 962, + 962, 962, 962, 962, 962, 962, 962, 963, 967, 967, + 967, 333, 333, 964, 964, 688, 678, 677, 2096, 2144, + 967, 967, 964, 964, 964, 965, 964, 964, 333, 342, + 985, 985, 969, 333, 343, 664, 335, 335, 333, 663, + 344, 966, 966, 966, 966, 966, 966, 966, 966, 963, + 662, 2382, 2030, 335, 987, 964, 964, 661, 335, 993, + 994, 1269, 1706, 335, 964, 964, 964, 964, 964, 964, + 990, 989, 655, 985, 985, 967, 651, 651, 651, 651, + 651, 651, 651, 651, 652, 652, 652, 652, 652, 652, + + 652, 652, 654, 654, 654, 654, 654, 654, 654, 654, + 2019, 333, 338, 648, 333, 642, 2147, 332, 2022, 330, + 2150, 343, 985, 327, 333, 333, 326, 344, 985, 971, + 985, 333, 985, 333, 333, 344, 335, 971, 973, 335, + 974, 974, 974, 974, 974, 974, 974, 974, 975, 335, + 335, 1002, 639, 995, 976, 976, 335, 999, 335, 335, + 996, 635, 333, 976, 976, 976, 976, 976, 976, 978, + 978, 978, 978, 978, 978, 978, 978, 963, 1004, 2025, + 631, 1003, 333, 978, 978, 333, 333, 335, 612, 333, + 333, 985, 978, 978, 978, 978, 978, 978, 982, 982, + + 982, 982, 982, 982, 982, 982, 333, 335, 334, 333, + 335, 335, 983, 984, 335, 335, 1000, 591, 985, 1005, + 333, 983, 983, 983, 983, 983, 984, 991, 333, 333, + 992, 335, 333, 1001, 335, 333, 333, 333, 333, 333, + 2153, 333, 1007, 587, 333, 335, 586, 1006, 577, 333, + 333, 333, 985, 335, 335, 333, 333, 335, 333, 534, + 335, 335, 335, 335, 335, 1009, 335, 1008, 1011, 335, + 1012, 1010, 528, 1015, 335, 335, 335, 1017, 1016, 333, + 335, 335, 333, 335, 1014, 333, 333, 1013, 1020, 333, + 333, 333, 333, 1018, 334, 333, 333, 1021, 500, 1019, + + 480, 469, 333, 333, 335, 462, 333, 335, 363, 333, + 335, 335, 333, 333, 335, 335, 335, 335, 333, 1023, + 335, 335, 333, 1032, 1024, 1022, 1027, 335, 335, 1025, + 1026, 335, 1030, 1028, 335, 1031, 1034, 335, 335, 1029, + 1033, 333, 1035, 335, 333, 1036, 333, 335, 333, 1037, + 1038, 333, 333, 333, 333, 1040, 354, 349, 333, 333, + 333, 1039, 2096, 333, 333, 346, 335, 333, 333, 335, + 1041, 335, 333, 335, 985, 333, 335, 335, 335, 335, + 332, 1042, 1045, 335, 335, 335, 1046, 1043, 335, 335, + 1049, 1048, 335, 335, 333, 330, 1044, 335, 1052, 1047, + + 335, 333, 333, 333, 1053, 1050, 1051, 1056, 1055, 1054, + 333, 333, 333, 328, 1057, 333, 333, 333, 327, 335, + 1058, 2208, 333, 1059, 326, 333, 335, 335, 335, 333, + 333, 1060, 333, 985, 333, 335, 335, 335, 1061, 333, + 335, 335, 335, 1065, 333, 1062, 1069, 335, 1068, 1066, + 335, 333, 1063, 1067, 335, 335, 333, 335, 1064, 335, + 333, 1072, 1070, 1071, 335, 333, 1073, 2382, 222, 335, + 333, 333, 1075, 333, 1074, 333, 335, 2245, 333, 333, + 333, 335, 2144, 333, 333, 335, 333, 2248, 1076, 985, + 335, 333, 1077, 1078, 985, 335, 335, 333, 335, 985, + + 335, 333, 1079, 335, 335, 335, 333, 1080, 335, 335, + 222, 335, 1082, 333, 1083, 333, 335, 1086, 333, 1081, + 1085, 333, 335, 1084, 1088, 222, 335, 333, 333, 1087, + 275, 335, 333, 333, 1090, 1091, 333, 1089, 335, 333, + 335, 333, 333, 335, 333, 2382, 335, 333, 2382, 333, + 1094, 2382, 335, 335, 2382, 1092, 333, 335, 335, 1093, + 333, 335, 333, 333, 335, 333, 335, 335, 2382, 335, + 1096, 333, 335, 1101, 335, 333, 1097, 1103, 1099, 1098, + 333, 335, 1105, 1100, 333, 335, 1102, 335, 335, 1104, + 335, 333, 333, 1108, 333, 333, 335, 1109, 2382, 333, + + 335, 2382, 1106, 1010, 1107, 335, 1113, 1110, 1112, 335, + 333, 1111, 333, 333, 2382, 333, 335, 335, 2382, 335, + 335, 333, 2147, 1115, 335, 333, 1114, 333, 1119, 2382, + 1127, 333, 1117, 333, 985, 335, 333, 335, 335, 1116, + 335, 333, 1118, 1120, 1123, 333, 335, 1121, 1124, 333, + 335, 333, 335, 333, 1126, 333, 335, 2382, 335, 1122, + 1128, 335, 333, 2382, 333, 2382, 335, 333, 1130, 333, + 335, 333, 333, 333, 335, 333, 335, 333, 335, 333, + 335, 333, 1129, 2382, 333, 333, 1134, 335, 1136, 335, + 1131, 2382, 335, 1133, 335, 1135, 335, 335, 335, 333, + + 335, 1138, 335, 1139, 335, 333, 335, 2382, 1137, 335, + 335, 333, 333, 2382, 1145, 333, 1146, 1140, 333, 1143, + 333, 333, 1141, 2251, 335, 1144, 1142, 2382, 333, 2150, + 335, 333, 1147, 2382, 333, 985, 335, 335, 1149, 333, + 335, 985, 333, 335, 333, 335, 335, 333, 1148, 2382, + 333, 1151, 1077, 335, 1150, 1153, 335, 1154, 1152, 335, + 1155, 333, 2382, 333, 335, 333, 1157, 335, 333, 335, + 333, 333, 335, 1160, 1156, 335, 333, 333, 333, 333, + 333, 1158, 333, 333, 1159, 2382, 335, 1161, 335, 2382, + 335, 2382, 333, 335, 333, 335, 335, 333, 1162, 1166, + + 2382, 335, 335, 335, 335, 335, 333, 335, 335, 1163, + 1164, 1169, 1165, 2382, 1168, 1167, 1170, 335, 1171, 335, + 333, 333, 335, 1175, 333, 1173, 333, 1176, 1174, 333, + 1172, 335, 333, 333, 333, 333, 333, 333, 1177, 333, + 333, 333, 2382, 2382, 333, 335, 335, 2382, 333, 335, + 2382, 335, 2382, 333, 335, 333, 333, 335, 335, 335, + 335, 335, 335, 333, 335, 335, 335, 1178, 1045, 335, + 1180, 1179, 1186, 335, 1184, 1183, 1181, 1188, 335, 333, + 335, 335, 333, 333, 333, 333, 1182, 1185, 335, 1187, + 333, 333, 333, 2382, 1189, 1190, 1192, 333, 2382, 1191, + + 333, 333, 333, 333, 335, 333, 333, 335, 335, 335, + 335, 333, 1193, 1194, 2382, 335, 335, 335, 1196, 333, + 333, 1195, 335, 1197, 2382, 335, 335, 335, 335, 1199, + 335, 335, 1055, 333, 1202, 333, 335, 333, 1200, 1198, + 1206, 333, 2382, 1031, 335, 335, 1052, 333, 333, 1203, + 333, 1201, 333, 333, 333, 333, 1211, 333, 335, 2382, + 335, 333, 335, 333, 333, 2382, 335, 1204, 2382, 2382, + 333, 333, 335, 335, 333, 335, 333, 335, 335, 335, + 335, 1205, 335, 333, 1212, 2382, 335, 333, 335, 335, + 1213, 1210, 333, 1209, 1208, 335, 335, 333, 1207, 335, + + 1219, 335, 333, 1221, 1214, 1220, 333, 1218, 335, 333, + 1216, 333, 335, 333, 1215, 1222, 333, 335, 1217, 333, + 333, 2254, 335, 333, 333, 1223, 2382, 335, 333, 2382, + 333, 335, 2382, 985, 335, 1224, 335, 1052, 335, 1225, + 333, 335, 333, 333, 335, 335, 1227, 2382, 335, 335, + 333, 1229, 2153, 335, 1226, 335, 333, 1232, 1228, 333, + 333, 1230, 333, 333, 985, 335, 2382, 335, 335, 2382, + 333, 963, 1231, 2382, 1234, 335, 2382, 1233, 1081, 2382, + 1235, 335, 2382, 975, 335, 335, 2382, 335, 335, 1236, + 1507, 1237, 1239, 342, 1238, 335, 1242, 2382, 343, 1243, + + 2382, 1241, 2382, 1507, 344, 2382, 1240, 1245, 1245, 1245, + 1245, 1245, 1245, 1245, 1245, 960, 963, 1246, 1246, 1246, + 1246, 1246, 1246, 1246, 1246, 1250, 1250, 1250, 1250, 1250, + 1250, 1250, 1250, 963, 1248, 967, 967, 967, 342, 963, + 333, 2257, 333, 343, 2382, 2382, 2096, 967, 967, 344, + 2208, 2245, 2144, 985, 1256, 2382, 963, 1248, 985, 969, + 2382, 342, 985, 985, 985, 335, 343, 335, 333, 333, + 1257, 1273, 344, 1258, 1258, 1258, 1258, 1258, 1258, 1258, + 1258, 2382, 1276, 1270, 1270, 1270, 1270, 1270, 1270, 1270, + 1270, 333, 333, 335, 335, 2382, 333, 333, 333, 2382, + + 333, 333, 967, 973, 1274, 1259, 1259, 1259, 1259, 1259, + 1259, 1259, 1259, 975, 2382, 963, 335, 335, 2382, 1260, + 1260, 335, 335, 335, 1277, 335, 335, 333, 1260, 1260, + 1260, 1260, 1260, 1260, 1267, 1279, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1269, 1272, 1275, 333, 1278, 333, + 1270, 1270, 335, 333, 333, 333, 985, 333, 333, 1270, + 1270, 1270, 1270, 1270, 1270, 333, 2382, 333, 333, 333, + 2382, 333, 335, 333, 335, 1280, 2382, 2248, 335, 335, + 335, 333, 335, 335, 333, 333, 1286, 333, 333, 985, + 335, 1282, 335, 335, 335, 1281, 335, 333, 335, 2382, + + 1284, 1283, 1288, 1285, 333, 1293, 335, 2382, 1287, 335, + 335, 333, 335, 335, 1289, 333, 333, 333, 333, 1290, + 2382, 2382, 335, 333, 1296, 333, 333, 1291, 333, 335, + 1292, 333, 1294, 333, 333, 1295, 335, 333, 2382, 333, + 335, 335, 335, 335, 2382, 1299, 1297, 333, 335, 333, + 335, 335, 333, 335, 2382, 1302, 335, 1300, 335, 335, + 342, 1298, 335, 1305, 335, 343, 333, 333, 333, 1301, + 333, 344, 335, 333, 335, 333, 2147, 335, 1303, 1304, + 333, 2382, 333, 333, 333, 1307, 333, 2382, 985, 333, + 1306, 335, 335, 335, 333, 335, 1308, 333, 335, 1312, + + 335, 1309, 333, 2251, 333, 335, 1313, 335, 335, 335, + 2382, 335, 1310, 1311, 335, 985, 333, 1315, 333, 335, + 2382, 333, 335, 333, 333, 2382, 333, 335, 1314, 335, + 333, 333, 333, 333, 1318, 1316, 333, 1320, 333, 333, + 1317, 335, 2382, 335, 2382, 1321, 335, 1322, 335, 335, + 1319, 335, 333, 1325, 1323, 335, 335, 335, 335, 2382, + 1328, 335, 333, 335, 335, 333, 2382, 1324, 333, 1326, + 1331, 1334, 333, 2382, 333, 333, 1327, 335, 333, 333, + 2382, 333, 2382, 2382, 1332, 1329, 1330, 335, 333, 333, + 335, 1335, 333, 335, 333, 333, 2382, 335, 1333, 335, + + 335, 333, 1337, 335, 335, 1339, 335, 1341, 1336, 2382, + 333, 2382, 333, 335, 335, 1344, 1340, 335, 1338, 335, + 335, 1345, 333, 333, 1347, 333, 335, 1342, 333, 2382, + 333, 2382, 333, 333, 1343, 335, 1346, 335, 2382, 333, + 333, 333, 1348, 333, 1349, 333, 333, 335, 335, 2382, + 335, 333, 2382, 335, 1351, 335, 1350, 335, 335, 333, + 2382, 333, 333, 1353, 335, 335, 335, 333, 335, 333, + 335, 335, 333, 963, 1352, 1354, 335, 1357, 333, 1358, + 2382, 333, 1356, 333, 335, 1355, 335, 335, 333, 1359, + 333, 333, 335, 1360, 335, 1362, 1363, 335, 333, 333, + + 333, 333, 333, 335, 333, 1361, 335, 333, 335, 2382, + 333, 1364, 333, 335, 2382, 335, 335, 2382, 1365, 2382, + 1366, 1369, 2382, 335, 335, 335, 335, 335, 333, 335, + 1367, 1372, 335, 333, 1374, 335, 1368, 335, 333, 1370, + 333, 333, 1376, 333, 1377, 1373, 1371, 1375, 333, 333, + 333, 333, 333, 335, 333, 1379, 333, 333, 335, 333, + 333, 1378, 1341, 335, 333, 335, 335, 333, 335, 333, + 963, 1381, 1393, 335, 335, 335, 335, 335, 2382, 335, + 1384, 335, 335, 1388, 335, 335, 1380, 1382, 1385, 335, + 1390, 1383, 335, 333, 335, 1389, 333, 333, 333, 333, + + 333, 2382, 1386, 1387, 1392, 1391, 333, 333, 333, 333, + 333, 2382, 333, 333, 2382, 333, 2382, 333, 335, 2382, + 2382, 335, 335, 335, 335, 335, 1394, 333, 333, 333, + 2382, 335, 335, 335, 335, 335, 1397, 335, 335, 1398, + 335, 1396, 335, 333, 1395, 1400, 1401, 1399, 333, 333, + 2382, 333, 335, 335, 335, 1295, 333, 333, 1402, 333, + 333, 1403, 333, 1404, 333, 2382, 333, 2382, 335, 1405, + 333, 333, 2382, 335, 335, 1406, 335, 333, 2382, 333, + 2382, 335, 335, 2382, 335, 335, 1407, 335, 1408, 335, + 1409, 335, 1411, 333, 333, 335, 335, 1410, 333, 333, + + 333, 333, 335, 1414, 335, 1412, 333, 1413, 1415, 1420, + 333, 2382, 1421, 1422, 1423, 1424, 2382, 2382, 335, 335, + 333, 1416, 2382, 335, 335, 335, 335, 2382, 333, 333, + 333, 335, 333, 333, 333, 335, 1417, 333, 333, 333, + 1418, 1428, 1342, 1354, 333, 335, 1419, 1426, 333, 2150, + 333, 2382, 1425, 335, 335, 335, 1427, 335, 335, 335, + 333, 985, 335, 335, 335, 2382, 333, 333, 1429, 335, + 333, 1430, 2382, 335, 1432, 335, 1431, 1438, 2382, 333, + 333, 1433, 1434, 333, 1435, 335, 1436, 333, 2382, 1440, + 1437, 335, 335, 333, 333, 335, 1446, 1439, 2254, 333, + + 333, 2382, 2382, 1442, 335, 335, 333, 2382, 335, 333, + 985, 333, 335, 1441, 333, 333, 1444, 1453, 335, 335, + 333, 333, 2382, 1445, 335, 335, 1443, 1448, 333, 333, + 333, 335, 1449, 1447, 335, 1342, 335, 2382, 333, 335, + 335, 333, 2382, 333, 333, 335, 335, 1450, 333, 333, + 2382, 1454, 2153, 335, 335, 335, 1451, 333, 333, 333, + 1452, 1455, 1456, 335, 985, 2382, 335, 1459, 335, 335, + 333, 1458, 2382, 335, 335, 333, 1457, 1462, 1464, 2382, + 333, 333, 335, 335, 335, 333, 1460, 2257, 333, 1467, + 1461, 1468, 2382, 333, 1469, 335, 1470, 1463, 333, 985, + + 335, 333, 1471, 333, 1466, 335, 335, 333, 333, 2382, + 335, 333, 1465, 335, 2382, 333, 333, 1473, 335, 2208, + 333, 333, 1474, 335, 1472, 333, 335, 333, 335, 333, + 2382, 985, 335, 335, 1478, 333, 335, 1475, 1476, 1479, + 335, 335, 333, 2382, 1482, 335, 335, 333, 1477, 333, + 335, 1480, 335, 333, 335, 1481, 1483, 2303, 1485, 333, + 335, 333, 1488, 2245, 2248, 2382, 2382, 335, 1487, 985, + 2382, 2251, 335, 1484, 335, 985, 985, 1303, 335, 1486, + 2382, 2382, 1520, 985, 335, 1354, 335, 1490, 1491, 1489, + 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1250, 1250, + + 1250, 1250, 1250, 1250, 1250, 1250, 967, 967, 967, 967, + 967, 967, 2254, 2257, 2382, 2208, 2303, 2245, 967, 967, + 2382, 967, 967, 2382, 985, 985, 333, 985, 985, 985, + 1494, 2382, 1502, 1494, 1503, 1503, 1503, 1503, 1503, 1503, + 1503, 1503, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, + 2382, 335, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, + 2382, 2382, 2382, 2248, 333, 333, 2251, 333, 2382, 2382, + 333, 333, 333, 967, 1518, 985, 967, 1267, 985, 1514, + 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1269, 333, 335, + 335, 333, 335, 1515, 1515, 335, 335, 335, 333, 985, + + 1523, 333, 1515, 1515, 1515, 1515, 1515, 1515, 1519, 1521, + 333, 333, 1522, 335, 1524, 333, 335, 333, 333, 333, + 333, 1525, 2382, 335, 1526, 333, 335, 333, 2382, 2254, + 2382, 1527, 333, 333, 1528, 335, 335, 333, 333, 333, + 335, 985, 335, 335, 335, 335, 2382, 1529, 1531, 333, + 335, 333, 335, 1535, 1530, 1532, 1538, 335, 335, 1533, + 2382, 333, 335, 335, 335, 333, 1534, 333, 333, 2382, + 333, 1540, 333, 1536, 335, 333, 335, 1545, 1537, 333, + 333, 333, 1542, 1541, 2382, 1539, 335, 333, 333, 2382, + 335, 333, 335, 335, 1543, 335, 333, 335, 333, 333, + + 335, 333, 1544, 2382, 335, 335, 335, 1550, 333, 1547, + 1546, 333, 335, 335, 1548, 333, 335, 333, 2382, 2382, + 1549, 335, 333, 335, 335, 333, 335, 333, 1551, 333, + 333, 1552, 1554, 335, 333, 333, 335, 333, 333, 2382, + 335, 333, 335, 1555, 1553, 2382, 1558, 335, 1559, 333, + 335, 1556, 335, 2382, 335, 335, 1563, 333, 333, 335, + 335, 1557, 335, 335, 1560, 333, 335, 333, 1561, 333, + 1562, 333, 333, 1564, 335, 1565, 333, 2382, 1569, 1566, + 2382, 1567, 335, 335, 333, 333, 1570, 333, 333, 333, + 335, 1568, 335, 333, 335, 333, 335, 335, 333, 333, + + 333, 335, 1573, 333, 1572, 1575, 1571, 1576, 333, 335, + 335, 2382, 335, 335, 335, 333, 333, 333, 335, 1574, + 335, 1580, 2382, 335, 335, 335, 2382, 1577, 335, 333, + 333, 333, 333, 335, 333, 1578, 1579, 333, 333, 1581, + 335, 335, 335, 1584, 2382, 333, 333, 1582, 333, 333, + 1583, 1587, 2382, 333, 335, 335, 335, 335, 333, 335, + 333, 2382, 335, 335, 1585, 1590, 2382, 1589, 333, 1586, + 335, 335, 333, 335, 335, 333, 2382, 1588, 335, 2382, + 333, 333, 333, 335, 333, 335, 1591, 333, 333, 333, + 1600, 1525, 333, 335, 333, 2382, 1592, 335, 333, 333, + + 335, 1593, 333, 333, 1595, 335, 335, 335, 1535, 335, + 333, 333, 335, 335, 335, 333, 2382, 335, 1601, 335, + 1594, 333, 333, 335, 335, 1607, 1596, 335, 335, 1597, + 1598, 333, 1599, 1601, 333, 335, 335, 333, 333, 1605, + 335, 1602, 1603, 333, 1606, 1604, 335, 335, 333, 2382, + 333, 333, 2382, 333, 2382, 2382, 335, 1608, 333, 335, + 2382, 333, 335, 335, 333, 2382, 333, 333, 335, 2382, + 333, 333, 333, 335, 1611, 335, 335, 1609, 335, 1610, + 1614, 333, 333, 335, 333, 1612, 335, 333, 333, 335, + 1616, 335, 335, 333, 1615, 335, 335, 335, 333, 1617, + + 1613, 333, 1618, 2382, 2382, 333, 335, 335, 2382, 335, + 333, 333, 335, 335, 333, 333, 333, 1620, 335, 333, + 1619, 1525, 2382, 335, 333, 333, 335, 333, 1623, 1621, + 335, 1622, 333, 1624, 333, 335, 335, 333, 333, 335, + 335, 335, 333, 333, 335, 333, 333, 1625, 333, 335, + 335, 333, 335, 2257, 2382, 333, 333, 335, 1627, 335, + 1626, 333, 335, 335, 2382, 985, 333, 335, 335, 1628, + 335, 335, 333, 335, 333, 1629, 335, 1635, 1634, 1632, + 335, 335, 333, 1630, 333, 333, 335, 2382, 333, 1631, + 1633, 335, 2382, 333, 1580, 333, 2382, 335, 1636, 335, + + 333, 333, 1637, 1638, 333, 333, 333, 335, 333, 335, + 335, 333, 1639, 335, 2382, 1640, 2303, 1580, 335, 333, + 335, 1641, 1643, 2382, 333, 335, 335, 1642, 985, 335, + 335, 335, 333, 335, 333, 333, 335, 333, 333, 1648, + 333, 1650, 2382, 1644, 335, 2382, 333, 333, 1645, 335, + 2382, 1646, 1653, 1647, 333, 333, 1616, 335, 333, 335, + 335, 333, 335, 335, 1651, 335, 333, 1649, 1652, 2382, + 1656, 335, 335, 333, 333, 1657, 1659, 2382, 333, 335, + 335, 333, 1654, 335, 1655, 333, 335, 333, 333, 333, + 1663, 335, 333, 2382, 1658, 333, 2382, 2382, 335, 335, + + 2382, 2382, 1660, 335, 1661, 333, 335, 1662, 1664, 333, + 335, 333, 335, 335, 335, 333, 333, 335, 1670, 1672, + 335, 1666, 1665, 333, 333, 1668, 1667, 1674, 333, 333, + 335, 333, 1669, 2303, 335, 1671, 335, 333, 2382, 2382, + 335, 335, 1673, 2382, 2382, 985, 2382, 2382, 335, 335, + 2382, 2382, 2382, 335, 335, 2382, 335, 2382, 1675, 2382, + 2382, 2382, 335, 1676, 1679, 2382, 2382, 1677, 1680, 1680, + 1680, 1680, 1680, 1680, 1680, 1680, 1491, 1678, 1681, 1681, + 1681, 1681, 1681, 1681, 1681, 1681, 967, 967, 967, 967, + 967, 967, 2382, 2382, 2382, 2382, 2382, 2382, 967, 967, + + 2382, 967, 967, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 1494, 2382, 2382, 1494, 1691, 1691, 1691, 1691, 1691, 1691, + 1691, 1691, 1502, 333, 1692, 1692, 1692, 1692, 1692, 1692, + 1692, 1692, 1702, 2382, 1703, 1703, 1703, 1703, 1703, 1703, + 1703, 1703, 333, 1269, 333, 333, 333, 333, 335, 333, + 1707, 333, 333, 967, 333, 985, 967, 333, 333, 333, + 333, 333, 2382, 1707, 333, 333, 2382, 335, 2382, 335, + 335, 335, 335, 333, 335, 1708, 335, 335, 333, 335, + 333, 2382, 335, 335, 335, 335, 335, 1713, 1710, 335, + 335, 1716, 333, 1709, 333, 1712, 1711, 333, 335, 333, + + 2382, 333, 333, 335, 333, 335, 1715, 333, 333, 1717, + 1719, 1714, 1718, 333, 333, 333, 2382, 335, 333, 335, + 333, 2382, 335, 2382, 335, 1720, 335, 335, 333, 335, + 333, 2382, 335, 335, 333, 1722, 333, 333, 335, 335, + 335, 1724, 333, 335, 2382, 335, 1725, 1721, 1726, 1727, + 2382, 2382, 333, 335, 333, 335, 1723, 2382, 333, 335, + 333, 335, 335, 333, 333, 333, 2382, 335, 2382, 1729, + 2382, 333, 333, 2382, 333, 1731, 1728, 335, 333, 335, + 2382, 333, 1730, 335, 333, 335, 333, 333, 335, 335, + 335, 1732, 333, 1734, 333, 1735, 335, 335, 1737, 335, + + 333, 333, 333, 335, 1733, 1738, 335, 1739, 2382, 335, + 333, 335, 335, 1736, 333, 333, 333, 335, 1743, 335, + 333, 1740, 333, 333, 333, 335, 335, 335, 1742, 1741, + 333, 2382, 2382, 1744, 333, 335, 333, 2382, 333, 335, + 335, 335, 2382, 333, 333, 335, 2382, 335, 335, 335, + 333, 333, 333, 2382, 1747, 335, 1748, 1745, 333, 335, + 333, 335, 1746, 335, 333, 333, 333, 1751, 335, 335, + 333, 1750, 333, 2382, 1749, 335, 335, 335, 1752, 333, + 1753, 1755, 333, 335, 333, 335, 333, 1754, 2382, 335, + 335, 335, 333, 1759, 333, 335, 2382, 335, 1761, 1756, + + 2382, 2382, 333, 1758, 335, 1757, 2382, 335, 333, 335, + 333, 335, 1760, 1762, 333, 1763, 333, 335, 333, 335, + 2382, 1764, 333, 2382, 333, 1769, 1767, 335, 333, 333, + 333, 1765, 333, 335, 2382, 335, 1768, 1766, 333, 335, + 333, 335, 1770, 335, 333, 1771, 333, 335, 1773, 335, + 333, 333, 2382, 335, 335, 335, 333, 335, 333, 1774, + 333, 1772, 333, 335, 2382, 335, 2382, 333, 333, 335, + 1775, 335, 2382, 1776, 333, 335, 335, 1777, 333, 333, + 333, 335, 333, 335, 1779, 335, 333, 335, 333, 1780, + 333, 1781, 335, 335, 333, 333, 1782, 1778, 333, 335, + + 1783, 333, 2382, 335, 335, 335, 333, 335, 333, 1785, + 333, 335, 333, 335, 333, 335, 333, 2382, 333, 335, + 335, 333, 333, 335, 1784, 1793, 335, 1786, 333, 2382, + 333, 335, 333, 335, 1789, 335, 2382, 335, 2382, 335, + 2382, 335, 1787, 335, 1788, 333, 335, 335, 1794, 333, + 1792, 333, 333, 335, 1790, 335, 333, 335, 1791, 1801, + 1796, 1798, 333, 1795, 333, 1797, 2382, 333, 2382, 333, + 335, 333, 2382, 333, 335, 333, 335, 335, 1799, 333, + 333, 335, 333, 2382, 2382, 333, 333, 335, 333, 335, + 333, 1800, 335, 1802, 335, 333, 335, 1805, 335, 333, + + 335, 333, 333, 2382, 335, 335, 1803, 335, 1804, 1809, + 335, 335, 333, 335, 2382, 335, 2382, 1806, 2382, 2382, + 335, 2382, 1807, 333, 335, 1808, 335, 335, 1810, 1811, + 1813, 1817, 1814, 1812, 1816, 2382, 2382, 335, 1815, 1818, + 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1829, 335, 1830, + 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1834, 1834, 1834, + 1834, 1834, 1834, 1834, 1834, 2382, 2382, 2382, 2382, 1848, + 333, 1261, 1261, 2382, 2382, 2382, 2382, 2382, 2382, 333, + 1261, 1261, 1261, 1261, 1261, 1261, 1843, 1843, 1843, 1843, + 1843, 1843, 1843, 1843, 1702, 335, 1844, 1844, 1844, 1844, + + 1844, 1844, 1844, 1844, 335, 333, 333, 333, 333, 333, + 333, 333, 1850, 333, 2382, 333, 333, 333, 1849, 333, + 333, 333, 333, 333, 333, 333, 333, 2382, 2382, 333, + 335, 335, 335, 335, 335, 335, 335, 333, 335, 1851, + 335, 335, 335, 333, 335, 335, 335, 335, 335, 335, + 335, 335, 1858, 1853, 335, 333, 333, 1852, 333, 333, + 333, 1854, 335, 1856, 1855, 1857, 2382, 333, 335, 333, + 333, 333, 333, 333, 333, 333, 333, 2382, 333, 2382, + 335, 335, 333, 335, 335, 335, 2382, 2382, 333, 333, + 333, 1859, 335, 333, 335, 335, 335, 335, 335, 335, + + 335, 335, 1860, 335, 1863, 333, 333, 335, 2382, 333, + 2382, 1861, 1865, 335, 335, 335, 333, 333, 335, 333, + 1864, 2382, 333, 1862, 333, 333, 1867, 333, 333, 2382, + 335, 335, 333, 1868, 335, 1866, 333, 333, 333, 333, + 2382, 335, 335, 333, 335, 333, 1871, 335, 1869, 335, + 335, 333, 335, 335, 1870, 333, 333, 335, 333, 1872, + 333, 335, 335, 335, 335, 1875, 1877, 333, 335, 333, + 335, 1873, 1879, 1874, 333, 333, 335, 1876, 333, 1880, + 335, 335, 333, 335, 1878, 335, 333, 333, 1882, 1884, + 333, 1881, 335, 333, 335, 333, 2382, 333, 2382, 335, + + 335, 2382, 333, 335, 1883, 2382, 1886, 335, 333, 1885, + 333, 335, 335, 333, 333, 335, 333, 2382, 335, 333, + 335, 1887, 335, 1888, 2382, 2382, 1893, 335, 333, 333, + 1890, 1900, 1901, 335, 1889, 335, 2382, 333, 335, 335, + 1891, 335, 1894, 1897, 335, 1898, 333, 333, 1892, 1899, + 1895, 333, 333, 335, 335, 1905, 2382, 333, 1903, 333, + 1907, 1896, 335, 333, 333, 333, 1902, 1908, 1909, 2382, + 2382, 335, 335, 333, 333, 333, 335, 335, 333, 333, + 333, 1904, 335, 1913, 335, 333, 333, 333, 335, 335, + 335, 333, 333, 1911, 1910, 1906, 2382, 2382, 335, 335, + + 335, 1912, 2382, 335, 335, 335, 1915, 2382, 2382, 2382, + 335, 335, 335, 2382, 1917, 1916, 335, 335, 333, 1914, + 1918, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 2382, + 2382, 2382, 1919, 1920, 1934, 1934, 1934, 1934, 1934, 1934, + 1934, 1934, 1829, 335, 1935, 1935, 1935, 1935, 1935, 1935, + 1935, 1935, 1938, 1954, 1939, 1939, 1939, 1939, 1939, 1939, + 1939, 1939, 1506, 2382, 2382, 2382, 2382, 2382, 1505, 1505, + 2382, 2382, 2382, 2382, 2382, 2382, 333, 1505, 1505, 1505, + 1505, 1505, 1505, 1948, 333, 1949, 1949, 1949, 1949, 1949, + 1949, 1949, 1949, 1953, 1953, 1953, 1953, 1953, 1953, 1953, + + 1953, 335, 333, 333, 333, 333, 333, 1516, 1516, 335, + 333, 2382, 333, 333, 333, 333, 1516, 1516, 1516, 1516, + 1516, 1516, 333, 333, 333, 333, 333, 335, 335, 335, + 335, 335, 2382, 333, 333, 335, 1955, 335, 335, 335, + 335, 333, 333, 333, 1957, 333, 333, 335, 335, 335, + 335, 335, 333, 333, 1956, 333, 333, 1958, 335, 335, + 333, 2382, 333, 333, 333, 333, 335, 335, 335, 2382, + 335, 335, 333, 333, 333, 2382, 1959, 335, 335, 1961, + 335, 335, 333, 1960, 1964, 335, 1962, 335, 335, 335, + 335, 333, 333, 333, 1963, 333, 333, 335, 335, 335, + + 1967, 333, 333, 1966, 333, 1968, 333, 335, 333, 1965, + 333, 2382, 1975, 333, 333, 1969, 335, 335, 335, 2382, + 335, 335, 333, 2382, 333, 333, 335, 335, 1972, 335, + 333, 335, 1970, 335, 333, 335, 1971, 1974, 335, 335, + 333, 1977, 333, 333, 1978, 1979, 333, 335, 1973, 335, + 335, 1980, 1976, 333, 333, 335, 333, 2382, 333, 335, + 333, 1990, 333, 2382, 2382, 335, 2382, 335, 335, 2382, + 2382, 335, 1981, 1982, 2382, 333, 333, 333, 335, 335, + 333, 335, 1987, 335, 333, 335, 333, 335, 1983, 1984, + 333, 1985, 333, 333, 1986, 1991, 2382, 2382, 2382, 1989, + + 335, 335, 335, 1988, 333, 335, 2382, 2382, 2382, 335, + 2382, 335, 2382, 2382, 2382, 335, 2382, 335, 335, 1993, + 2382, 1995, 1992, 2032, 2382, 2382, 1994, 2382, 2382, 335, + 1996, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2013, + 2013, 2013, 2013, 2013, 2013, 2013, 2013, 1938, 2382, 2014, + 2014, 2014, 2014, 2014, 2014, 2014, 2014, 1506, 2382, 2382, + 2382, 2382, 333, 1693, 1693, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 1693, 1693, 1693, 1693, 1693, 1693, 2026, 2026, + 2026, 2026, 2026, 2026, 2026, 2026, 1948, 335, 2027, 2027, + 2027, 2027, 2027, 2027, 2027, 2027, 2030, 2382, 2031, 2031, + + 2031, 2031, 2031, 2031, 2031, 2031, 1706, 333, 333, 333, + 333, 2382, 1705, 1705, 333, 333, 2382, 333, 985, 333, + 333, 1705, 1705, 1705, 1705, 1705, 1705, 2382, 333, 333, + 333, 2382, 335, 335, 335, 335, 2033, 333, 333, 335, + 335, 2034, 335, 333, 335, 335, 333, 333, 333, 2037, + 333, 333, 2035, 335, 335, 335, 2036, 333, 333, 333, + 2038, 333, 335, 335, 2382, 2382, 333, 2039, 335, 2048, + 333, 335, 335, 335, 333, 335, 335, 333, 333, 333, + 2382, 2382, 335, 335, 335, 2040, 335, 2041, 333, 2043, + 2044, 335, 2046, 2045, 333, 335, 2382, 333, 2382, 335, + + 2382, 2042, 335, 335, 335, 2047, 2049, 333, 333, 2053, + 333, 333, 2050, 335, 2382, 2382, 333, 2382, 333, 335, + 333, 2051, 335, 2056, 2052, 2057, 333, 333, 2055, 333, + 2054, 2382, 335, 335, 2382, 335, 335, 2382, 2382, 2058, + 2059, 335, 2060, 335, 2382, 335, 2382, 2382, 2062, 2382, + 2382, 335, 335, 2382, 335, 2382, 2061, 2078, 2078, 2078, + 2078, 2078, 2078, 2078, 2078, 2081, 2099, 2082, 2082, 2082, + 2082, 2082, 2082, 2082, 2082, 2093, 2093, 2093, 2093, 2093, + 2093, 2093, 2093, 2097, 2097, 2097, 2097, 2097, 2097, 2097, + 2097, 333, 333, 2382, 333, 985, 2030, 2382, 2098, 2098, + + 2098, 2098, 2098, 2098, 2098, 2098, 1706, 2382, 2382, 333, + 333, 333, 1845, 1845, 333, 333, 335, 335, 985, 335, + 333, 1845, 1845, 1845, 1845, 1845, 1845, 2102, 333, 333, + 333, 333, 2101, 2100, 335, 335, 335, 333, 2382, 335, + 335, 333, 2382, 2104, 333, 335, 2382, 333, 2382, 333, + 333, 333, 333, 335, 335, 335, 335, 2103, 2382, 2108, + 333, 2382, 335, 2105, 333, 333, 335, 2106, 333, 335, + 2382, 2107, 335, 2109, 335, 335, 335, 335, 333, 2113, + 333, 333, 2115, 2110, 333, 335, 2111, 333, 2112, 335, + 335, 2382, 2114, 335, 333, 2116, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 335, 2382, 335, 335, 2382, 2117, 335, + 2382, 2118, 335, 2382, 2382, 2382, 2120, 2159, 2382, 335, + 2382, 2119, 2138, 2138, 2138, 2138, 2138, 2138, 2138, 2138, + 2081, 2382, 2139, 2139, 2139, 2139, 2139, 2139, 2139, 2139, + 2154, 2154, 2154, 2154, 2154, 2154, 2154, 2154, 2157, 333, + 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 333, 333, + 985, 333, 333, 333, 333, 333, 333, 333, 2382, 2164, + 2165, 333, 2382, 333, 335, 2382, 333, 333, 2382, 333, + 333, 333, 333, 335, 335, 333, 335, 335, 335, 335, + 335, 335, 335, 2162, 2382, 2382, 335, 2160, 335, 2382, + + 2161, 335, 335, 2166, 335, 335, 335, 335, 2382, 2382, + 335, 2382, 2163, 2382, 2168, 2167, 2169, 2382, 2212, 2192, + 2170, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2171, + 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2157, 333, + 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 333, 333, + 333, 333, 333, 333, 333, 2217, 2382, 333, 333, 333, + 333, 333, 333, 2382, 335, 2239, 2239, 2239, 2239, 2239, + 2239, 2239, 2239, 335, 335, 335, 335, 335, 335, 335, + 2211, 2215, 335, 335, 335, 335, 335, 335, 2214, 2382, + 2382, 2382, 2220, 2262, 333, 333, 2213, 2216, 2218, 2192, + + 2219, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2260, + 2382, 2261, 2261, 2261, 2261, 2261, 2261, 2261, 2261, 335, + 335, 333, 333, 333, 333, 333, 2286, 2286, 2286, 2286, + 2286, 2286, 2286, 2286, 333, 2264, 333, 2382, 333, 2382, + 333, 2263, 333, 333, 2382, 2382, 335, 335, 335, 335, + 335, 2382, 2382, 2382, 2382, 333, 2382, 2269, 2382, 335, + 2382, 335, 2267, 335, 2268, 335, 333, 335, 335, 2265, + 2266, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2260, + 335, 2305, 2305, 2305, 2305, 2305, 2305, 2305, 2305, 2307, + 2308, 335, 333, 333, 333, 333, 333, 2382, 2306, 2309, + + 2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321, 2338, 2338, + 2338, 2338, 2338, 2338, 2338, 2338, 2382, 335, 335, 335, + 335, 335, 333, 333, 2310, 2339, 2340, 2382, 985, 2361, + 2361, 2361, 2361, 2361, 2361, 2361, 2361, 333, 333, 333, + 333, 2341, 333, 333, 2342, 2382, 2382, 335, 335, 985, + 333, 333, 2363, 333, 333, 333, 2382, 333, 2382, 2382, + 2382, 2362, 335, 335, 335, 335, 2382, 335, 335, 2382, + 2364, 2365, 2377, 2382, 2382, 335, 335, 2382, 335, 335, + 335, 2376, 335, 2382, 2382, 2382, 2375, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2380, 2382, 2382, 2382, 2379, 2382, + + 2382, 2382, 2381, 329, 329, 329, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 329, 331, 331, 331, 331, + 331, 331, 331, 331, 331, 331, 331, 331, 331, 334, + 334, 334, 334, 334, 2382, 334, 334, 2382, 334, 334, + 353, 353, 353, 2382, 2382, 353, 461, 461, 461, 461, + 461, 461, 461, 461, 461, 461, 461, 461, 461, 647, + 647, 647, 2382, 2382, 647, 645, 645, 645, 2382, 2382, + 645, 649, 2382, 649, 2382, 2382, 649, 660, 660, 660, + 2382, 2382, 660, 785, 785, 785, 785, 785, 785, 785, + 785, 785, 785, 785, 785, 785, 959, 2382, 2382, 2382, + + 959, 959, 2382, 959, 959, 964, 964, 964, 2382, 2382, + 964, 968, 2382, 968, 2382, 2382, 968, 968, 968, 2382, + 2382, 968, 970, 2382, 970, 2382, 2382, 970, 976, 976, + 976, 2382, 2382, 976, 979, 979, 979, 2382, 2382, 979, + 980, 980, 980, 2382, 2382, 980, 1244, 1244, 1244, 1244, + 2382, 1244, 1244, 2382, 1244, 1244, 1247, 1247, 2382, 2382, + 2382, 1247, 1247, 1249, 1249, 1249, 2382, 2382, 1249, 1250, + 1250, 2382, 2382, 2382, 2382, 1250, 1251, 2382, 1251, 2382, + 2382, 1251, 1251, 1251, 2382, 2382, 1251, 1252, 1252, 1252, + 2382, 2382, 1252, 1254, 1254, 1254, 2382, 2382, 1254, 1261, + + 2382, 1261, 2382, 2382, 1261, 1260, 1260, 1260, 2382, 2382, + 1260, 1263, 1263, 1263, 2382, 2382, 1263, 1264, 1264, 1264, + 2382, 2382, 1264, 1266, 2382, 1266, 2382, 1266, 1266, 1493, + 2382, 1493, 2382, 2382, 1493, 1493, 1493, 2382, 2382, 1493, + 1495, 2382, 1495, 2382, 2382, 1495, 1495, 1495, 2382, 2382, + 1495, 1496, 1496, 1496, 2382, 2382, 1496, 1498, 2382, 1498, + 2382, 2382, 1498, 1499, 1499, 1499, 2382, 2382, 1499, 1500, + 2382, 1500, 2382, 2382, 1500, 1504, 2382, 1504, 1504, 1504, + 2382, 2382, 1504, 1505, 1505, 1505, 2382, 2382, 1505, 1508, + 1508, 1508, 2382, 2382, 1508, 1509, 1509, 1509, 2382, 2382, + + 1509, 1511, 1511, 1511, 2382, 1511, 1511, 1516, 2382, 1516, + 2382, 2382, 1516, 1251, 2382, 1251, 2382, 2382, 1251, 1251, + 1251, 2382, 2382, 1251, 1682, 1682, 1682, 2382, 2382, 1682, + 1683, 1683, 1683, 2382, 2382, 1683, 1684, 1684, 1684, 2382, + 2382, 1684, 1686, 1686, 1686, 2382, 2382, 1686, 1688, 1688, + 1688, 2382, 2382, 1688, 1689, 1689, 1689, 2382, 2382, 1689, + 1693, 1693, 1693, 2382, 2382, 1693, 1694, 2382, 1694, 2382, + 2382, 1694, 1696, 1696, 1696, 2382, 2382, 1696, 1697, 1697, + 1697, 2382, 2382, 1697, 1699, 2382, 1699, 2382, 1699, 1699, + 1700, 1700, 1700, 2382, 1700, 1700, 1701, 2382, 1701, 2382, + + 2382, 1701, 1704, 2382, 1704, 1704, 1704, 2382, 1704, 1704, + 1705, 1705, 1705, 2382, 1705, 1705, 1819, 1819, 1819, 2382, + 2382, 1819, 1821, 1821, 1821, 2382, 2382, 1821, 1822, 1822, + 1822, 2382, 2382, 1822, 1824, 2382, 1824, 2382, 2382, 1824, + 1825, 1825, 1825, 2382, 2382, 1825, 1826, 2382, 1826, 2382, + 2382, 1826, 1827, 1827, 1827, 2382, 2382, 1827, 1828, 2382, + 1828, 2382, 2382, 1828, 1831, 1831, 1831, 2382, 2382, 1831, + 1832, 1832, 1832, 2382, 2382, 1832, 1835, 1835, 1835, 2382, + 2382, 1835, 1836, 1836, 1836, 2382, 2382, 1836, 1838, 1838, + 1838, 2382, 1838, 1838, 1840, 1840, 1840, 2382, 1840, 1840, + + 1841, 1841, 1841, 2382, 1841, 1841, 1845, 1845, 1845, 2382, + 1845, 1845, 1846, 2382, 1846, 2382, 2382, 1846, 1683, 1683, + 1683, 2382, 2382, 1683, 1922, 1922, 1922, 2382, 2382, 1922, + 1923, 1923, 1923, 2382, 2382, 1923, 1924, 1924, 1924, 2382, + 2382, 1924, 1926, 1926, 1926, 2382, 2382, 1926, 1928, 1928, + 1928, 2382, 2382, 1928, 1929, 1929, 1929, 2382, 2382, 1929, + 1931, 1931, 1931, 2382, 2382, 1931, 1932, 1932, 1932, 2382, + 2382, 1932, 1936, 1936, 1936, 2382, 2382, 1936, 1937, 2382, + 1937, 2382, 2382, 1937, 1940, 1940, 1940, 2382, 2382, 1940, + 1941, 1941, 1941, 2382, 2382, 1941, 1943, 2382, 1943, 2382, + + 1943, 1943, 1944, 1944, 1944, 2382, 1944, 1944, 1945, 2382, + 1945, 2382, 2382, 1945, 1946, 1946, 1946, 2382, 1946, 1946, + 1947, 2382, 1947, 2382, 2382, 1947, 1950, 1950, 1950, 2382, + 1950, 1950, 1951, 1951, 1951, 2382, 1951, 1951, 1997, 1997, + 1997, 2382, 2382, 1997, 1999, 1999, 1999, 2382, 2382, 1999, + 2000, 2000, 2000, 2382, 2382, 2000, 2002, 2382, 2002, 2382, + 2382, 2002, 2003, 2003, 2003, 2382, 2382, 2003, 2004, 2382, + 2004, 2382, 2382, 2004, 2005, 2005, 2005, 2382, 2382, 2005, + 2006, 2382, 2006, 2382, 2382, 2006, 2007, 2007, 2007, 2382, + 2382, 2007, 2008, 2382, 2008, 2382, 2382, 2008, 2010, 2010, + + 2010, 2382, 2382, 2010, 2011, 2011, 2011, 2382, 2382, 2011, + 2015, 2015, 2015, 2382, 2382, 2015, 2016, 2016, 2016, 2382, + 2382, 2016, 2018, 2018, 2018, 2382, 2018, 2018, 2020, 2020, + 2020, 2382, 2020, 2020, 2021, 2021, 2021, 2382, 2021, 2021, + 2023, 2023, 2023, 2382, 2023, 2023, 2024, 2024, 2024, 2382, + 2024, 2024, 2028, 2028, 2028, 2382, 2028, 2028, 2029, 2382, + 2029, 2382, 2382, 2029, 1923, 1923, 1923, 2382, 2382, 1923, + 2063, 2063, 2063, 2382, 2382, 2063, 2064, 2064, 2064, 2382, + 2382, 2064, 2065, 2065, 2065, 2382, 2382, 2065, 2067, 2067, + 2067, 2382, 2382, 2067, 2069, 2069, 2069, 2382, 2382, 2069, + + 2070, 2070, 2070, 2382, 2382, 2070, 2072, 2072, 2072, 2382, + 2382, 2072, 2073, 2073, 2073, 2382, 2382, 2073, 2075, 2075, + 2075, 2382, 2382, 2075, 2076, 2076, 2076, 2382, 2382, 2076, + 2079, 2079, 2079, 2382, 2382, 2079, 2080, 2382, 2080, 2382, + 2382, 2080, 1831, 2382, 1831, 1831, 1831, 2382, 2382, 1831, + 2083, 2083, 2083, 2382, 2382, 2083, 2084, 2084, 2084, 2382, + 2382, 2084, 2086, 2382, 2086, 2382, 2086, 2086, 2087, 2087, + 2087, 2382, 2087, 2087, 2088, 2382, 2088, 2382, 2382, 2088, + 2089, 2089, 2089, 2382, 2089, 2089, 2090, 2382, 2090, 2382, + 2382, 2090, 2091, 2091, 2091, 2382, 2091, 2091, 2092, 2382, + + 2092, 2382, 2382, 2092, 2094, 2094, 2094, 2382, 2094, 2094, + 2095, 2095, 2095, 2382, 2095, 2095, 2121, 2121, 2121, 2382, + 2382, 2121, 2123, 2123, 2123, 2382, 2382, 2123, 2124, 2124, + 2124, 2382, 2382, 2124, 2126, 2382, 2126, 2382, 2382, 2126, + 2127, 2127, 2127, 2382, 2382, 2127, 2128, 2382, 2128, 2382, + 2382, 2128, 2129, 2129, 2129, 2382, 2382, 2129, 2130, 2382, + 2130, 2382, 2382, 2130, 2131, 2131, 2131, 2382, 2382, 2131, + 2132, 2382, 2132, 2382, 2382, 2132, 2133, 2133, 2133, 2382, + 2382, 2133, 2134, 2382, 2134, 2382, 2382, 2134, 2135, 2135, + 2135, 2382, 2382, 2135, 2136, 2136, 2136, 2382, 2382, 2136, + + 2140, 2140, 2140, 2382, 2382, 2140, 2141, 2141, 2141, 2382, + 2382, 2141, 2143, 2143, 2143, 2382, 2143, 2143, 2145, 2145, + 2145, 2382, 2145, 2145, 2146, 2146, 2146, 2382, 2146, 2146, + 2148, 2148, 2148, 2382, 2148, 2148, 2149, 2149, 2149, 2382, + 2149, 2149, 2151, 2151, 2151, 2382, 2151, 2151, 2152, 2152, + 2152, 2382, 2152, 2152, 2155, 2155, 2155, 2382, 2155, 2155, + 2156, 2382, 2156, 2382, 2382, 2156, 1950, 2382, 1950, 1950, + 1950, 2382, 1950, 1950, 2064, 2064, 2064, 2382, 2382, 2064, + 2172, 2172, 2172, 2382, 2382, 2172, 2173, 2173, 2173, 2382, + 2382, 2173, 2174, 2174, 2174, 2382, 2382, 2174, 2176, 2176, + + 2176, 2382, 2382, 2176, 2178, 2178, 2178, 2382, 2382, 2178, + 2179, 2179, 2179, 2382, 2382, 2179, 2181, 2181, 2181, 2382, + 2382, 2181, 2182, 2182, 2182, 2382, 2382, 2182, 2184, 2184, + 2184, 2382, 2382, 2184, 2185, 2185, 2185, 2382, 2382, 2185, + 2187, 2187, 2187, 2382, 2382, 2187, 2188, 2188, 2188, 2382, + 2382, 2188, 2190, 2190, 2190, 2382, 2382, 2190, 2191, 2382, + 2191, 2382, 2382, 2191, 2194, 2194, 2194, 2382, 2382, 2194, + 2195, 2195, 2195, 2382, 2382, 2195, 2197, 2382, 2197, 2382, + 2197, 2197, 2198, 2198, 2198, 2382, 2198, 2198, 2199, 2382, + 2199, 2382, 2382, 2199, 2200, 2200, 2200, 2382, 2200, 2200, + + 2201, 2382, 2201, 2382, 2382, 2201, 2202, 2202, 2202, 2382, + 2202, 2202, 2203, 2382, 2203, 2382, 2382, 2203, 2204, 2204, + 2204, 2382, 2204, 2204, 2205, 2382, 2205, 2382, 2382, 2205, + 2206, 2206, 2206, 2382, 2206, 2206, 2207, 2207, 2207, 2382, + 2207, 2207, 334, 334, 334, 334, 334, 2382, 334, 334, + 2382, 334, 334, 2221, 2221, 2221, 2382, 2382, 2221, 2222, + 2222, 2222, 2382, 2382, 2222, 2223, 2223, 2223, 2382, 2382, + 2223, 2225, 2382, 2225, 2382, 2382, 2225, 2226, 2226, 2226, + 2382, 2382, 2226, 2227, 2382, 2227, 2382, 2382, 2227, 2228, + 2228, 2228, 2382, 2382, 2228, 2229, 2382, 2229, 2382, 2382, + + 2229, 2230, 2230, 2230, 2382, 2382, 2230, 2231, 2382, 2231, + 2382, 2382, 2231, 2232, 2232, 2232, 2382, 2382, 2232, 2233, + 2382, 2233, 2382, 2382, 2233, 2234, 2234, 2234, 2382, 2382, + 2234, 2235, 2382, 2235, 2382, 2382, 2235, 2236, 2236, 2236, + 2382, 2382, 2236, 2237, 2237, 2237, 2382, 2382, 2237, 2241, + 2241, 2241, 2382, 2382, 2241, 2242, 2242, 2242, 2382, 2382, + 2242, 2244, 2244, 2244, 2382, 2244, 2244, 2246, 2246, 2246, + 2382, 2246, 2246, 2247, 2247, 2247, 2382, 2247, 2247, 2249, + 2249, 2249, 2382, 2249, 2249, 2250, 2250, 2250, 2382, 2250, + 2250, 2252, 2252, 2252, 2382, 2252, 2252, 2253, 2253, 2253, + + 2382, 2253, 2253, 2255, 2255, 2255, 2382, 2255, 2255, 2256, + 2256, 2256, 2382, 2256, 2256, 2258, 2258, 2258, 2382, 2258, + 2258, 2259, 2382, 2259, 2382, 2382, 2259, 334, 334, 334, + 334, 334, 2382, 334, 334, 2382, 334, 334, 2173, 2173, + 2173, 2382, 2382, 2173, 2270, 2270, 2270, 2382, 2382, 2270, + 2271, 2271, 2271, 2382, 2382, 2271, 2273, 2382, 2273, 2382, + 2382, 2273, 2274, 2274, 2274, 2382, 2382, 2274, 2275, 2382, + 2275, 2382, 2382, 2275, 2276, 2276, 2276, 2382, 2382, 2276, + 2277, 2382, 2277, 2382, 2382, 2277, 2278, 2278, 2278, 2382, + 2382, 2278, 2279, 2382, 2279, 2382, 2382, 2279, 2280, 2280, + + 2280, 2382, 2382, 2280, 2281, 2382, 2281, 2382, 2382, 2281, + 2282, 2282, 2282, 2382, 2382, 2282, 2283, 2382, 2283, 2382, + 2382, 2283, 2284, 2284, 2284, 2382, 2382, 2284, 2285, 2382, + 2285, 2382, 2382, 2285, 2287, 2287, 2287, 2382, 2382, 2287, + 2288, 2288, 2288, 2382, 2382, 2288, 2290, 2382, 2290, 2382, + 2290, 2290, 2291, 2291, 2291, 2382, 2291, 2291, 2292, 2382, + 2292, 2382, 2382, 2292, 2293, 2293, 2293, 2382, 2293, 2293, + 2294, 2382, 2294, 2382, 2382, 2294, 2295, 2295, 2295, 2382, + 2295, 2295, 2296, 2382, 2296, 2382, 2382, 2296, 2297, 2297, + 2297, 2382, 2297, 2297, 2298, 2382, 2298, 2382, 2382, 2298, + + 2299, 2299, 2299, 2382, 2299, 2299, 2300, 2382, 2300, 2382, + 2382, 2300, 2301, 2301, 2301, 2382, 2301, 2301, 2302, 2302, + 2302, 2382, 2302, 2302, 334, 334, 334, 334, 334, 2382, + 334, 334, 2382, 334, 334, 2311, 2311, 2311, 2382, 2382, + 2311, 2312, 2382, 2312, 2382, 2382, 2312, 2313, 2382, 2313, + 2382, 2382, 2313, 2314, 2382, 2314, 2382, 2382, 2314, 2315, + 2382, 2315, 2382, 2382, 2315, 2316, 2382, 2316, 2382, 2382, + 2316, 2317, 2382, 2317, 2382, 2382, 2317, 2318, 2382, 2318, + 2382, 2382, 2318, 2319, 2319, 2319, 2382, 2382, 2319, 2320, + 2382, 2320, 2382, 2382, 2320, 2322, 2322, 2322, 2382, 2382, + + 2322, 2323, 2323, 2323, 2382, 2382, 2323, 2325, 2382, 2325, + 2382, 2325, 2325, 2326, 2326, 2326, 2382, 2326, 2326, 2327, + 2382, 2327, 2382, 2327, 2327, 2328, 2328, 2328, 2382, 2328, + 2328, 2329, 2382, 2329, 2382, 2329, 2329, 2330, 2330, 2330, + 2382, 2330, 2330, 2331, 2382, 2331, 2382, 2331, 2331, 2332, + 2332, 2332, 2382, 2332, 2332, 2333, 2382, 2333, 2382, 2333, + 2333, 2334, 2334, 2334, 2382, 2334, 2334, 2335, 2382, 2335, + 2382, 2335, 2335, 2336, 2336, 2336, 2382, 2336, 2336, 2337, + 2382, 2337, 2382, 2382, 2337, 2343, 2382, 2343, 2382, 2382, + 2343, 2344, 2382, 2344, 2382, 2382, 2344, 2345, 2382, 2345, + + 2382, 2382, 2345, 2346, 2382, 2346, 2382, 2382, 2346, 2347, + 2382, 2347, 2382, 2382, 2347, 2348, 2382, 2348, 2382, 2382, + 2348, 2349, 2382, 2349, 2382, 2382, 2349, 2350, 2382, 2350, + 2382, 2382, 2350, 2351, 2351, 2351, 2382, 2382, 2351, 2352, + 2382, 2352, 2382, 2352, 2352, 2353, 2382, 2353, 2382, 2353, + 2353, 2354, 2382, 2354, 2382, 2354, 2354, 2355, 2382, 2355, + 2382, 2355, 2355, 2356, 2382, 2356, 2382, 2356, 2356, 2357, + 2382, 2357, 2382, 2357, 2357, 2358, 2382, 2358, 2382, 2358, + 2358, 2359, 2359, 2359, 2382, 2359, 2359, 2360, 2382, 2360, + 2382, 2360, 2360, 2366, 2382, 2366, 2382, 2382, 2366, 2272, + + 2382, 2272, 2382, 2382, 2272, 2367, 2382, 2367, 2382, 2367, + 2367, 2368, 2382, 2368, 2382, 2368, 2368, 2369, 2382, 2369, + 2382, 2369, 2369, 2370, 2382, 2370, 2382, 2370, 2370, 2371, + 2382, 2371, 2382, 2371, 2371, 2372, 2382, 2372, 2382, 2372, + 2372, 2373, 2382, 2373, 2382, 2373, 2373, 2374, 2382, 2374, + 2382, 2374, 2374, 2378, 2382, 2378, 2382, 2378, 2378, 2324, + 2382, 2324, 2382, 2324, 2324, 111, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382 + } ; + +static const flex_int16_t yy_chk[9936] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 11, 2, 3, 4, 2, 4, 3, 5, 4, 5, + 9, 134, 134, 3, 3, 11, 6, 3, 6, 13, + 29, 6, 9, 29, 9, 13, 59, 10, 105, 10, + + 15, 15, 10, 12, 15, 12, 15, 59, 12, 14, + 15, 14, 105, 15, 14, 4, 15, 15, 17, 4, + 47, 27, 17, 63, 23, 27, 4, 4, 17, 63, + 4, 23, 47, 67, 47, 2378, 6, 10, 6, 7, + 7, 7, 7, 7, 55, 12, 7, 7, 67, 10, + 7, 10, 1259, 7, 7, 73, 7, 7, 55, 55, + 12, 73, 1259, 14, 7, 8, 130, 8, 21, 14, + 8, 21, 18, 21, 18, 21, 91, 18, 2374, 21, + 30, 21, 30, 21, 235, 30, 21, 21, 24, 21, + 24, 91, 235, 24, 28, 130, 28, 31, 36, 28, + + 36, 39, 39, 36, 8, 8, 8, 8, 8, 31, + 31, 8, 8, 18, 39, 8, 31, 18, 8, 8, + 79, 8, 8, 18, 79, 25, 79, 95, 95, 8, + 16, 30, 16, 190, 30, 16, 25, 25, 25, 25, + 24, 25, 25, 95, 28, 127, 25, 24, 28, 33, + 33, 38, 190, 38, 41, 41, 38, 44, 33, 44, + 41, 46, 44, 46, 49, 33, 46, 41, 33, 2373, + 127, 16, 16, 136, 136, 16, 49, 16, 49, 295, + 71, 16, 71, 295, 16, 131, 71, 16, 16, 19, + 61, 19, 19, 60, 19, 60, 19, 19, 60, 214, + + 19, 61, 131, 19, 19, 61, 19, 19, 19, 2372, + 19, 20, 20, 20, 20, 20, 20, 214, 20, 34, + 180, 34, 20, 2371, 34, 20, 20, 180, 20, 20, + 20, 131, 20, 32, 60, 32, 20, 22, 32, 40, + 22, 40, 22, 22, 40, 60, 22, 2370, 22, 22, + 22, 53, 22, 22, 22, 141, 22, 26, 53, 26, + 34, 34, 26, 197, 141, 2369, 53, 53, 53, 34, + 53, 197, 53, 32, 536, 139, 34, 57, 197, 34, + 40, 40, 536, 57, 275, 32, 32, 57, 57, 93, + 93, 191, 32, 40, 64, 275, 64, 26, 93, 64, + + 139, 42, 48, 42, 48, 93, 42, 48, 26, 26, + 26, 26, 286, 26, 26, 50, 191, 50, 26, 37, + 50, 37, 37, 37, 37, 186, 263, 286, 219, 263, + 37, 37, 37, 37, 191, 219, 37, 37, 37, 37, + 64, 37, 42, 42, 51, 186, 64, 51, 42, 51, + 48, 2368, 267, 51, 51, 42, 186, 292, 51, 51, + 51, 51, 48, 50, 48, 56, 103, 56, 292, 463, + 56, 103, 267, 984, 62, 50, 62, 50, 52, 62, + 52, 103, 54, 52, 54, 103, 984, 54, 495, 2367, + 463, 58, 66, 58, 66, 69, 58, 66, 69, 68, + + 77, 68, 69, 69, 68, 77, 56, 69, 70, 495, + 70, 77, 72, 70, 72, 77, 62, 72, 2361, 52, + 56, 56, 52, 54, 52, 153, 2360, 62, 52, 52, + 54, 62, 159, 52, 52, 52, 52, 58, 54, 54, + 54, 68, 54, 58, 54, 81, 1266, 58, 58, 66, + 153, 81, 70, 81, 2358, 70, 68, 159, 1266, 70, + 70, 153, 75, 72, 70, 72, 74, 75, 74, 72, + 78, 74, 78, 75, 153, 78, 75, 75, 76, 225, + 76, 159, 225, 76, 83, 80, 225, 80, 83, 82, + 80, 82, 2357, 84, 82, 84, 83, 89, 84, 240, + + 2356, 149, 83, 98, 149, 98, 89, 240, 98, 2355, + 149, 78, 74, 240, 89, 149, 78, 89, 74, 90, + 76, 90, 78, 243, 90, 76, 78, 1271, 2354, 230, + 243, 76, 80, 84, 76, 76, 80, 84, 80, 308, + 1271, 82, 334, 308, 308, 84, 85, 82, 85, 82, + 100, 84, 100, 85, 230, 100, 289, 85, 85, 85, + 90, 98, 230, 289, 85, 85, 86, 334, 86, 90, + 169, 86, 169, 101, 106, 169, 106, 90, 101, 106, + 90, 2353, 101, 101, 92, 101, 92, 107, 107, 92, + 607, 146, 146, 107, 107, 607, 268, 107, 2352, 146, + + 146, 102, 146, 102, 146, 86, 102, 86, 100, 104, + 268, 104, 86, 310, 104, 268, 86, 86, 86, 1511, + 310, 106, 1516, 86, 86, 87, 92, 87, 87, 87, + 87, 1511, 87, 87, 1516, 106, 87, 96, 87, 96, + 87, 92, 96, 87, 87, 88, 102, 88, 104, 325, + 88, 102, 110, 104, 110, 102, 102, 110, 102, 94, + 108, 94, 108, 104, 94, 108, 170, 104, 170, 315, + 2351, 170, 181, 181, 325, 2337, 315, 2335, 96, 96, + 181, 181, 358, 181, 88, 181, 88, 88, 88, 88, + 151, 88, 88, 2333, 96, 88, 325, 88, 207, 88, + + 94, 94, 88, 88, 1260, 207, 2331, 358, 207, 94, + 110, 108, 108, 2329, 1260, 151, 94, 108, 108, 151, + 151, 108, 129, 272, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 151, 2327, 2014, 251, 272, 129, 129, + 272, 129, 272, 1699, 163, 2014, 210, 129, 129, 129, + 129, 129, 129, 210, 129, 1699, 2325, 144, 144, 129, + 210, 251, 144, 2324, 369, 129, 144, 658, 144, 163, + 129, 138, 144, 251, 658, 144, 163, 138, 138, 138, + 138, 138, 138, 138, 138, 138, 163, 658, 163, 369, + 2323, 138, 138, 147, 2322, 147, 138, 147, 148, 150, + + 138, 138, 138, 138, 138, 138, 147, 148, 296, 152, + 147, 147, 148, 147, 148, 148, 158, 147, 2319, 148, + 196, 196, 296, 154, 150, 1700, 2311, 296, 196, 196, + 150, 196, 150, 196, 152, 2305, 155, 1700, 365, 152, + 152, 158, 150, 156, 152, 150, 152, 150, 154, 265, + 152, 152, 154, 152, 2300, 158, 154, 157, 2298, 158, + 154, 155, 158, 365, 160, 155, 154, 365, 156, 155, + 154, 154, 155, 155, 265, 165, 156, 156, 304, 155, + 2296, 208, 157, 161, 208, 304, 156, 157, 304, 160, + 208, 157, 156, 160, 157, 208, 217, 160, 265, 217, + + 165, 2294, 162, 157, 157, 217, 2292, 164, 161, 539, + 217, 160, 160, 160, 161, 539, 161, 172, 165, 172, + 161, 172, 161, 165, 2290, 161, 161, 162, 173, 161, + 172, 162, 164, 162, 172, 172, 161, 172, 162, 162, + 174, 172, 175, 203, 203, 2289, 162, 164, 164, 164, + 361, 203, 203, 173, 203, 162, 203, 173, 173, 164, + 2288, 242, 2287, 2284, 177, 174, 178, 175, 173, 242, + 174, 173, 242, 2282, 174, 361, 173, 174, 242, 2280, + 2278, 175, 175, 242, 174, 175, 174, 174, 175, 177, + 175, 178, 324, 177, 361, 2276, 1701, 177, 324, 178, + + 178, 177, 178, 324, 177, 179, 179, 177, 1701, 178, + 179, 177, 177, 2274, 179, 178, 179, 182, 2270, 182, + 179, 182, 184, 179, 179, 182, 2243, 2242, 189, 183, + 182, 373, 182, 187, 182, 182, 2241, 182, 183, 188, + 2240, 182, 2237, 183, 2236, 183, 183, 184, 183, 2234, + 183, 184, 184, 189, 253, 184, 373, 189, 187, 192, + 373, 189, 253, 187, 188, 184, 193, 187, 188, 253, + 187, 194, 253, 359, 213, 189, 189, 189, 188, 187, + 187, 312, 188, 187, 192, 188, 195, 2232, 192, 299, + 2230, 193, 192, 192, 2228, 193, 194, 299, 359, 213, + + 198, 2226, 192, 213, 299, 193, 312, 213, 192, 193, + 194, 195, 193, 359, 194, 200, 199, 194, 312, 195, + 213, 213, 213, 213, 1704, 198, 195, 195, 195, 198, + 2224, 198, 204, 198, 2223, 248, 1704, 202, 195, 248, + 200, 199, 316, 198, 200, 199, 198, 380, 198, 199, + 248, 382, 199, 199, 200, 248, 2222, 204, 200, 199, + 205, 200, 202, 204, 199, 204, 202, 316, 202, 2221, + 209, 2210, 380, 202, 202, 204, 382, 2196, 204, 212, + 204, 202, 211, 206, 206, 205, 321, 382, 206, 205, + 202, 316, 206, 205, 206, 209, 205, 205, 206, 209, + + 209, 206, 209, 205, 212, 215, 205, 211, 360, 2195, + 216, 321, 211, 209, 2194, 2191, 211, 977, 212, 211, + 221, 220, 212, 321, 977, 212, 211, 212, 211, 211, + 215, 218, 362, 360, 215, 216, 215, 977, 2190, 216, + 360, 215, 215, 216, 223, 221, 220, 224, 215, 215, + 216, 216, 216, 215, 220, 220, 218, 362, 215, 221, + 218, 218, 216, 221, 220, 227, 221, 226, 311, 223, + 220, 311, 224, 311, 218, 249, 228, 223, 223, 224, + 362, 249, 363, 392, 2188, 249, 249, 223, 2187, 224, + 227, 224, 226, 223, 224, 227, 226, 231, 2185, 227, + + 226, 228, 227, 226, 226, 228, 232, 363, 392, 228, + 226, 227, 227, 245, 2184, 227, 233, 2182, 261, 363, + 2181, 261, 231, 228, 228, 228, 231, 261, 2179, 237, + 231, 232, 261, 231, 231, 2178, 386, 2176, 245, 232, + 231, 233, 234, 234, 1247, 232, 245, 245, 233, 232, + 234, 234, 232, 234, 237, 234, 245, 2174, 233, 1247, + 233, 386, 245, 233, 368, 386, 364, 238, 238, 237, + 237, 237, 238, 2173, 2172, 1247, 238, 237, 238, 239, + 239, 237, 238, 250, 241, 238, 246, 239, 239, 368, + 239, 364, 239, 241, 384, 2154, 241, 368, 241, 244, + + 241, 241, 244, 244, 2142, 241, 244, 244, 250, 247, + 244, 246, 364, 244, 244, 250, 246, 244, 244, 384, + 246, 2141, 2140, 246, 384, 250, 457, 250, 457, 250, + 246, 457, 246, 246, 247, 2139, 252, 252, 247, 2136, + 2135, 252, 247, 256, 252, 252, 247, 252, 257, 252, + 254, 254, 254, 259, 406, 410, 247, 247, 247, 254, + 254, 255, 254, 258, 254, 255, 650, 379, 256, 255, + 2134, 650, 322, 257, 256, 322, 255, 650, 259, 406, + 410, 1705, 256, 262, 257, 367, 256, 322, 258, 256, + 322, 266, 379, 1705, 259, 257, 259, 257, 260, 260, + + 379, 259, 374, 258, 258, 258, 260, 260, 262, 260, + 367, 260, 262, 262, 264, 258, 266, 264, 264, 270, + 266, 413, 269, 269, 266, 1707, 262, 374, 2133, 264, + 269, 269, 264, 269, 367, 269, 274, 1707, 266, 266, + 266, 2132, 273, 273, 270, 374, 413, 279, 270, 2131, + 273, 273, 270, 273, 388, 273, 277, 388, 2130, 2129, + 2128, 274, 276, 276, 2127, 274, 270, 270, 270, 274, + 276, 276, 279, 276, 278, 276, 279, 278, 2126, 388, + 279, 277, 375, 274, 274, 274, 277, 2125, 278, 278, + 277, 2124, 278, 277, 279, 279, 279, 281, 280, 284, + + 277, 280, 277, 277, 2123, 372, 290, 375, 1838, 2122, + 387, 372, 280, 280, 290, 2121, 280, 290, 372, 2085, + 1838, 372, 281, 290, 284, 285, 291, 281, 290, 284, + 375, 281, 390, 284, 281, 387, 284, 293, 306, 376, + 293, 2084, 387, 281, 281, 284, 284, 2083, 284, 2080, + 285, 291, 293, 294, 285, 293, 293, 390, 285, 291, + 291, 297, 301, 306, 376, 301, 391, 412, 301, 291, + 2079, 301, 285, 285, 285, 291, 301, 306, 294, 2076, + 390, 306, 306, 376, 306, 377, 294, 294, 297, 297, + 297, 391, 412, 2075, 297, 399, 294, 300, 297, 391, + + 297, 302, 294, 305, 297, 297, 300, 297, 298, 298, + 377, 300, 300, 300, 300, 412, 298, 298, 300, 298, + 399, 298, 298, 2073, 298, 393, 302, 377, 305, 399, + 302, 302, 309, 309, 302, 307, 305, 305, 314, 307, + 309, 309, 307, 309, 302, 309, 305, 313, 313, 319, + 393, 394, 305, 307, 307, 313, 313, 2072, 313, 313, + 313, 318, 2070, 314, 318, 423, 1840, 393, 2069, 314, + 318, 314, 2067, 318, 319, 318, 394, 1841, 1840, 2065, + 381, 314, 319, 2064, 314, 2063, 314, 394, 319, 1841, + 423, 383, 319, 403, 385, 319, 336, 336, 336, 336, + + 336, 336, 336, 336, 337, 381, 337, 337, 337, 337, + 337, 337, 337, 337, 337, 396, 383, 366, 403, 385, + 337, 337, 403, 385, 381, 2027, 1845, 2017, 383, 337, + 337, 337, 337, 337, 337, 383, 337, 385, 1845, 383, + 396, 337, 366, 385, 444, 456, 2016, 337, 341, 341, + 341, 341, 341, 341, 341, 341, 341, 2015, 366, 366, + 2011, 396, 341, 341, 2010, 2008, 366, 2007, 2006, 444, + 456, 341, 341, 341, 341, 341, 341, 342, 342, 342, + 342, 342, 342, 342, 342, 343, 343, 343, 343, 343, + 343, 343, 343, 344, 344, 344, 344, 344, 344, 344, + + 344, 458, 460, 458, 460, 1846, 458, 460, 2005, 2004, + 438, 451, 408, 470, 2003, 2002, 2001, 1846, 2000, 1999, + 405, 400, 1998, 397, 389, 1997, 343, 346, 346, 346, + 346, 346, 346, 346, 346, 438, 451, 408, 470, 438, + 451, 346, 346, 395, 398, 405, 400, 408, 397, 389, + 346, 346, 346, 346, 346, 346, 352, 352, 352, 352, + 352, 352, 352, 352, 397, 400, 389, 405, 395, 398, + 352, 352, 389, 401, 404, 402, 407, 409, 414, 352, + 352, 352, 352, 352, 352, 398, 395, 402, 1942, 411, + 1941, 416, 395, 417, 418, 1940, 1943, 1937, 401, 404, + + 402, 407, 409, 414, 401, 401, 401, 415, 1943, 419, + 1936, 1935, 420, 409, 411, 404, 416, 407, 417, 418, + 401, 401, 402, 407, 1932, 414, 421, 422, 424, 425, + 427, 426, 415, 411, 419, 411, 411, 420, 429, 416, + 428, 417, 418, 430, 433, 1931, 1929, 1944, 1928, 415, + 419, 421, 422, 424, 425, 427, 426, 431, 420, 1944, + 421, 422, 424, 429, 432, 428, 1926, 427, 430, 433, + 432, 426, 432, 428, 434, 430, 435, 425, 426, 436, + 429, 430, 431, 428, 433, 430, 437, 430, 439, 432, + 430, 430, 440, 431, 1924, 432, 1945, 1923, 441, 434, + + 442, 435, 443, 1946, 436, 1922, 445, 435, 1945, 446, + 1844, 437, 447, 439, 1837, 1946, 448, 440, 449, 439, + 450, 437, 434, 441, 435, 442, 453, 443, 440, 452, + 442, 445, 454, 436, 446, 437, 455, 447, 1836, 441, + 441, 448, 464, 449, 465, 450, 443, 445, 466, 468, + 449, 453, 450, 467, 452, 446, 447, 454, 452, 469, + 448, 455, 471, 453, 472, 473, 1835, 464, 474, 465, + 475, 476, 454, 466, 468, 464, 480, 1832, 467, 477, + 481, 466, 455, 478, 469, 1831, 467, 471, 1828, 472, + 473, 469, 1827, 474, 479, 475, 476, 465, 482, 472, + + 483, 480, 468, 1947, 477, 481, 1826, 484, 478, 474, + 474, 485, 477, 473, 476, 1947, 475, 474, 486, 479, + 478, 487, 488, 482, 481, 483, 1950, 478, 482, 489, + 1825, 478, 484, 490, 479, 491, 485, 483, 1950, 492, + 484, 496, 485, 486, 1951, 482, 487, 488, 493, 1824, + 494, 488, 1823, 487, 489, 486, 1951, 1822, 490, 487, + 491, 486, 497, 487, 492, 487, 496, 1821, 487, 487, + 498, 489, 491, 493, 490, 494, 499, 493, 491, 500, + 502, 493, 494, 1820, 1819, 492, 501, 497, 1698, 496, + 503, 494, 494, 1697, 1696, 498, 507, 1694, 1693, 494, + + 505, 499, 1692, 506, 500, 502, 497, 1514, 497, 497, + 500, 501, 508, 502, 497, 503, 501, 1514, 501, 498, + 509, 507, 499, 502, 500, 505, 510, 500, 506, 1514, + 511, 505, 506, 512, 513, 514, 515, 508, 503, 516, + 517, 1689, 507, 518, 520, 509, 519, 1688, 505, 1686, + 2018, 510, 521, 522, 508, 511, 509, 1684, 512, 513, + 514, 515, 2018, 511, 516, 517, 509, 511, 518, 520, + 1683, 519, 523, 511, 510, 519, 513, 521, 522, 512, + 517, 524, 515, 518, 525, 526, 522, 520, 520, 519, + 522, 527, 528, 520, 530, 519, 529, 523, 1682, 531, + + 521, 534, 559, 1681, 521, 559, 524, 1510, 559, 525, + 526, 532, 535, 538, 524, 559, 527, 528, 559, 530, + 523, 529, 523, 528, 531, 533, 534, 526, 537, 529, + 525, 540, 526, 527, 2020, 530, 532, 535, 538, 527, + 528, 535, 530, 532, 534, 531, 2020, 538, 542, 1509, + 533, 543, 1508, 537, 533, 544, 540, 1507, 533, 532, + 545, 550, 546, 547, 548, 1505, 544, 1504, 551, 537, + 537, 540, 2021, 542, 533, 552, 543, 537, 1500, 553, + 544, 1499, 542, 1498, 2021, 545, 550, 546, 547, 548, + 542, 549, 554, 551, 561, 543, 550, 543, 543, 551, + + 552, 555, 556, 543, 553, 552, 547, 547, 545, 546, + 2023, 547, 548, 1497, 1496, 560, 549, 554, 557, 561, + 553, 562, 2023, 549, 563, 554, 555, 556, 564, 549, + 565, 566, 1494, 549, 1265, 549, 555, 561, 549, 549, + 560, 567, 554, 557, 556, 568, 562, 557, 570, 563, + 562, 569, 575, 564, 1264, 565, 566, 564, 571, 560, + 566, 1263, 572, 573, 563, 1261, 567, 1256, 574, 1254, + 568, 565, 565, 570, 566, 576, 569, 575, 578, 565, + 566, 577, 575, 571, 567, 1252, 568, 572, 573, 579, + 567, 571, 568, 574, 573, 569, 570, 569, 569, 580, + + 576, 571, 572, 578, 1246, 581, 577, 578, 582, 574, + 574, 573, 577, 583, 579, 576, 665, 584, 585, 588, + 2024, 595, 594, 2028, 580, 2029, 579, 2086, 981, 577, + 581, 586, 2024, 582, 980, 2028, 580, 2029, 583, 2086, + 587, 665, 584, 585, 588, 583, 595, 594, 580, 584, + 582, 583, 585, 581, 582, 583, 586, 583, 589, 584, + 583, 583, 586, 594, 586, 587, 591, 588, 595, 596, + 590, 587, 587, 587, 979, 976, 592, 2087, 971, 586, + 2088, 2089, 970, 589, 597, 601, 598, 587, 587, 2087, + 589, 591, 2088, 2089, 596, 590, 589, 591, 591, 591, + + 589, 592, 589, 589, 603, 589, 589, 590, 599, 597, + 601, 598, 590, 591, 591, 969, 600, 598, 596, 605, + 592, 598, 604, 606, 609, 611, 2090, 610, 617, 603, + 614, 601, 597, 599, 598, 603, 597, 964, 2090, 963, + 599, 600, 613, 612, 605, 825, 600, 604, 606, 609, + 611, 600, 610, 617, 611, 614, 816, 615, 616, 617, + 605, 618, 599, 604, 604, 604, 787, 613, 612, 606, + 609, 604, 619, 610, 612, 612, 612, 613, 614, 621, + 612, 614, 615, 616, 2091, 620, 618, 622, 623, 624, + 612, 612, 618, 613, 625, 616, 2091, 619, 615, 627, + + 785, 626, 679, 628, 621, 629, 618, 630, 631, 618, + 620, 678, 622, 623, 624, 663, 661, 632, 633, 625, + 634, 619, 635, 625, 627, 621, 626, 620, 628, 622, + 629, 636, 630, 631, 623, 660, 628, 631, 657, 624, + 630, 626, 632, 633, 637, 634, 628, 635, 627, 639, + 630, 638, 640, 641, 655, 629, 636, 632, 666, 2092, + 634, 667, 669, 635, 633, 645, 634, 634, 608, 637, + 675, 2092, 602, 2094, 639, 593, 638, 640, 641, 636, + 639, 558, 541, 666, 641, 2094, 667, 669, 637, 504, + 637, 637, 461, 640, 669, 675, 637, 643, 638, 643, + + 643, 643, 643, 643, 643, 643, 643, 644, 459, 644, + 644, 644, 644, 644, 644, 644, 644, 644, 647, 647, + 647, 668, 673, 644, 644, 378, 371, 370, 2095, 2143, + 647, 647, 644, 644, 644, 644, 644, 644, 670, 644, + 2095, 2143, 647, 671, 644, 357, 668, 673, 677, 356, + 644, 646, 646, 646, 646, 646, 646, 646, 646, 646, + 354, 1515, 2098, 670, 668, 646, 646, 353, 671, 673, + 673, 1515, 2098, 677, 646, 646, 646, 646, 646, 646, + 671, 670, 345, 1515, 2098, 647, 651, 651, 651, 651, + 651, 651, 651, 651, 652, 652, 652, 652, 652, 652, + + 652, 652, 654, 654, 654, 654, 654, 654, 654, 654, + 2145, 674, 339, 338, 682, 335, 2146, 331, 2148, 329, + 2149, 651, 2145, 327, 680, 685, 326, 651, 2146, 652, + 2148, 676, 2149, 683, 684, 652, 674, 654, 656, 682, + 656, 656, 656, 656, 656, 656, 656, 656, 656, 680, + 685, 682, 323, 674, 656, 656, 676, 680, 683, 684, + 676, 320, 686, 656, 656, 656, 656, 656, 656, 659, + 659, 659, 659, 659, 659, 659, 659, 659, 684, 2151, + 317, 683, 688, 659, 659, 681, 672, 686, 303, 690, + 687, 2151, 659, 659, 659, 659, 659, 659, 662, 662, + + 662, 662, 662, 662, 662, 662, 689, 688, 288, 691, + 681, 672, 662, 662, 690, 687, 681, 287, 662, 687, + 692, 662, 662, 662, 662, 662, 662, 672, 693, 695, + 672, 689, 694, 681, 691, 696, 697, 698, 699, 700, + 2152, 701, 691, 283, 702, 692, 282, 689, 271, 703, + 704, 705, 2152, 693, 695, 707, 706, 694, 708, 236, + 696, 697, 698, 699, 700, 693, 701, 692, 694, 702, + 696, 693, 229, 699, 703, 704, 705, 702, 701, 709, + 707, 706, 710, 708, 698, 711, 712, 697, 706, 714, + 716, 713, 717, 704, 222, 715, 720, 708, 201, 705, + + 185, 176, 722, 718, 709, 171, 723, 710, 145, 719, + 711, 712, 721, 724, 714, 716, 713, 717, 725, 710, + 715, 720, 726, 716, 710, 709, 713, 722, 718, 711, + 712, 723, 714, 713, 719, 715, 718, 721, 724, 713, + 717, 727, 719, 725, 728, 721, 729, 726, 730, 722, + 723, 731, 733, 732, 734, 725, 140, 135, 735, 736, + 737, 724, 2155, 738, 742, 132, 727, 739, 740, 728, + 726, 729, 741, 730, 2155, 743, 731, 733, 732, 734, + 118, 728, 732, 735, 736, 737, 732, 730, 738, 742, + 735, 734, 739, 740, 744, 117, 731, 741, 738, 733, + + 743, 745, 746, 747, 739, 736, 737, 741, 740, 739, + 748, 749, 755, 116, 741, 751, 750, 752, 115, 744, + 741, 2156, 753, 743, 113, 757, 745, 746, 747, 756, + 754, 744, 758, 2156, 759, 748, 749, 755, 745, 760, + 751, 750, 752, 749, 761, 746, 752, 753, 751, 750, + 757, 762, 747, 750, 756, 754, 763, 758, 748, 759, + 764, 755, 753, 754, 760, 765, 756, 111, 109, 761, + 766, 767, 760, 768, 759, 769, 762, 2197, 770, 771, + 772, 763, 2198, 773, 774, 764, 775, 2199, 761, 2197, + 765, 776, 762, 764, 2198, 766, 767, 777, 768, 2199, + + 769, 778, 765, 770, 771, 772, 779, 766, 773, 774, + 99, 775, 768, 780, 770, 781, 776, 774, 782, 767, + 772, 783, 777, 771, 776, 97, 778, 784, 788, 775, + 65, 779, 789, 790, 778, 779, 791, 777, 780, 792, + 781, 793, 794, 782, 795, 0, 783, 796, 0, 797, + 783, 0, 784, 788, 0, 780, 798, 789, 790, 781, + 800, 791, 799, 801, 792, 802, 793, 794, 0, 795, + 788, 804, 796, 793, 797, 803, 789, 795, 791, 790, + 805, 798, 797, 792, 806, 800, 794, 799, 801, 796, + 802, 807, 808, 800, 809, 810, 804, 800, 0, 812, + + 803, 0, 798, 800, 799, 805, 804, 801, 803, 806, + 811, 802, 813, 814, 0, 815, 807, 808, 0, 809, + 810, 817, 2200, 806, 812, 819, 805, 818, 810, 0, + 818, 820, 808, 821, 2200, 811, 822, 813, 814, 807, + 815, 823, 809, 811, 814, 824, 817, 812, 815, 826, + 819, 827, 818, 829, 817, 828, 820, 0, 821, 813, + 820, 822, 830, 0, 831, 0, 823, 833, 822, 832, + 824, 834, 835, 836, 826, 837, 827, 839, 829, 838, + 828, 840, 821, 0, 841, 842, 827, 830, 829, 831, + 823, 0, 833, 826, 832, 828, 834, 835, 836, 843, + + 837, 831, 839, 832, 838, 844, 840, 0, 830, 841, + 842, 845, 846, 0, 840, 847, 841, 834, 850, 838, + 849, 848, 836, 2201, 843, 839, 837, 0, 851, 2202, + 844, 852, 842, 0, 853, 2201, 845, 846, 844, 855, + 847, 2202, 854, 850, 856, 849, 848, 857, 843, 0, + 858, 846, 845, 851, 845, 848, 852, 849, 847, 853, + 850, 859, 0, 860, 855, 861, 852, 854, 862, 856, + 863, 864, 857, 855, 851, 858, 865, 866, 867, 869, + 868, 853, 870, 874, 854, 0, 859, 856, 860, 0, + 861, 0, 871, 862, 873, 863, 864, 872, 858, 862, + + 0, 865, 866, 867, 869, 868, 875, 870, 874, 859, + 860, 867, 861, 0, 866, 864, 868, 871, 869, 873, + 876, 877, 872, 873, 878, 871, 879, 874, 872, 880, + 870, 875, 881, 882, 883, 884, 885, 886, 875, 888, + 887, 889, 0, 0, 890, 876, 877, 0, 892, 878, + 0, 879, 0, 891, 880, 893, 894, 881, 882, 883, + 884, 885, 886, 897, 888, 887, 889, 876, 888, 890, + 882, 880, 888, 892, 886, 885, 883, 890, 891, 895, + 893, 894, 896, 898, 899, 900, 884, 887, 897, 889, + 901, 902, 903, 0, 891, 892, 894, 904, 0, 893, + + 905, 906, 907, 908, 895, 909, 910, 896, 898, 899, + 900, 911, 895, 896, 0, 901, 902, 903, 900, 912, + 913, 899, 904, 901, 0, 905, 906, 907, 908, 904, + 909, 910, 903, 914, 909, 916, 911, 915, 906, 903, + 915, 917, 0, 908, 912, 913, 911, 918, 919, 910, + 920, 907, 921, 922, 923, 924, 922, 925, 914, 0, + 916, 926, 915, 927, 928, 0, 917, 912, 0, 0, + 929, 933, 918, 919, 930, 920, 934, 921, 922, 923, + 924, 914, 925, 935, 924, 0, 926, 936, 927, 928, + 925, 921, 937, 920, 919, 929, 933, 931, 918, 930, + + 931, 934, 932, 933, 926, 932, 938, 930, 935, 939, + 928, 940, 936, 941, 927, 934, 942, 937, 929, 943, + 944, 2203, 931, 945, 946, 935, 0, 932, 947, 0, + 948, 938, 0, 2203, 939, 936, 940, 932, 941, 938, + 949, 942, 950, 951, 943, 944, 941, 0, 945, 946, + 952, 943, 2204, 947, 940, 948, 953, 946, 942, 954, + 955, 944, 956, 957, 2204, 949, 0, 950, 951, 0, + 958, 966, 945, 1262, 948, 952, 0, 947, 948, 0, + 949, 953, 0, 1262, 954, 955, 0, 956, 957, 950, + 1262, 951, 953, 966, 952, 958, 957, 0, 966, 958, + + 0, 956, 0, 1262, 966, 0, 954, 960, 960, 960, + 960, 960, 960, 960, 960, 961, 962, 961, 961, 961, + 961, 961, 961, 961, 961, 965, 965, 965, 965, 965, + 965, 965, 965, 965, 962, 968, 968, 968, 962, 972, + 990, 2205, 978, 962, 0, 0, 2206, 968, 968, 962, + 2207, 2244, 2246, 2205, 972, 0, 978, 972, 2206, 968, + 0, 972, 2207, 2244, 2246, 990, 972, 978, 987, 988, + 972, 987, 972, 973, 973, 973, 973, 973, 973, 973, + 973, 983, 990, 983, 983, 983, 983, 983, 983, 983, + 983, 991, 992, 987, 988, 0, 997, 989, 994, 0, + + 993, 986, 968, 974, 988, 974, 974, 974, 974, 974, + 974, 974, 974, 974, 0, 986, 991, 992, 0, 974, + 974, 997, 989, 994, 991, 993, 986, 995, 974, 974, + 974, 974, 974, 974, 982, 994, 982, 982, 982, 982, + 982, 982, 982, 982, 982, 986, 989, 996, 993, 998, + 982, 982, 995, 999, 1000, 1002, 982, 1001, 1003, 982, + 982, 982, 982, 982, 982, 1004, 0, 1005, 1007, 1006, + 0, 1008, 996, 1011, 998, 995, 0, 2247, 999, 1000, + 1002, 1009, 1001, 1003, 1010, 1012, 1002, 1013, 1014, 2247, + 1004, 998, 1005, 1007, 1006, 996, 1008, 1015, 1011, 0, + + 1000, 999, 1006, 1001, 1016, 1011, 1009, 0, 1005, 1010, + 1012, 1017, 1013, 1014, 1007, 1018, 1019, 1020, 1021, 1008, + 0, 0, 1015, 1022, 1014, 1023, 1024, 1009, 1025, 1016, + 1010, 1026, 1012, 1027, 1028, 1013, 1017, 1029, 1250, 1030, + 1018, 1019, 1020, 1021, 0, 1019, 1016, 1031, 1022, 1032, + 1023, 1024, 1033, 1025, 0, 1024, 1026, 1020, 1027, 1028, + 1250, 1017, 1029, 1028, 1030, 1250, 1034, 1035, 1036, 1023, + 1037, 1250, 1031, 1038, 1032, 1039, 2249, 1033, 1025, 1027, + 1040, 0, 1041, 1042, 1043, 1030, 1044, 0, 2249, 1045, + 1029, 1034, 1035, 1036, 1046, 1037, 1031, 1047, 1038, 1036, + + 1039, 1032, 1048, 2250, 1049, 1040, 1037, 1041, 1042, 1043, + 0, 1044, 1034, 1035, 1045, 2250, 1050, 1042, 1051, 1046, + 0, 1052, 1047, 1053, 1054, 0, 1055, 1048, 1040, 1049, + 1056, 1057, 1058, 1059, 1047, 1044, 1061, 1049, 1063, 1060, + 1046, 1050, 0, 1051, 0, 1050, 1052, 1051, 1053, 1054, + 1048, 1055, 1062, 1054, 1052, 1056, 1057, 1058, 1059, 0, + 1057, 1061, 1064, 1063, 1060, 1065, 0, 1053, 1066, 1055, + 1060, 1063, 1067, 0, 1068, 1069, 1056, 1062, 1070, 1071, + 0, 1073, 0, 0, 1061, 1058, 1059, 1064, 1072, 1075, + 1065, 1064, 1074, 1066, 1076, 1077, 0, 1067, 1062, 1068, + + 1069, 1078, 1066, 1070, 1071, 1068, 1073, 1070, 1065, 0, + 1079, 0, 1080, 1072, 1075, 1073, 1069, 1074, 1067, 1076, + 1077, 1074, 1081, 1082, 1077, 1083, 1078, 1071, 1084, 0, + 1085, 0, 1086, 1087, 1072, 1079, 1075, 1080, 0, 1088, + 1089, 1091, 1079, 1092, 1080, 1090, 1093, 1081, 1082, 0, + 1083, 1094, 0, 1084, 1083, 1085, 1082, 1086, 1087, 1095, + 0, 1096, 1097, 1085, 1088, 1089, 1091, 1098, 1092, 1099, + 1090, 1093, 1101, 1095, 1084, 1086, 1094, 1089, 1100, 1090, + 0, 1102, 1088, 1103, 1095, 1087, 1096, 1097, 1104, 1091, + 1105, 1106, 1098, 1094, 1099, 1097, 1098, 1101, 1107, 1109, + + 1108, 1110, 1112, 1100, 1111, 1095, 1102, 1113, 1103, 0, + 1114, 1100, 1116, 1104, 0, 1105, 1106, 0, 1101, 0, + 1102, 1105, 0, 1107, 1109, 1108, 1110, 1112, 1115, 1111, + 1103, 1108, 1113, 1117, 1110, 1114, 1104, 1116, 1118, 1106, + 1119, 1120, 1112, 1122, 1113, 1109, 1107, 1111, 1121, 1123, + 1124, 1127, 1126, 1115, 1128, 1116, 1125, 1129, 1117, 1130, + 1131, 1115, 1117, 1118, 1132, 1119, 1120, 1133, 1122, 1134, + 1125, 1118, 1134, 1121, 1123, 1124, 1127, 1126, 0, 1128, + 1122, 1125, 1129, 1127, 1130, 1131, 1117, 1118, 1123, 1132, + 1129, 1121, 1133, 1135, 1134, 1128, 1136, 1137, 1138, 1139, + + 1140, 0, 1125, 1126, 1131, 1130, 1141, 1142, 1143, 1146, + 1144, 0, 1145, 1147, 0, 1148, 0, 1149, 1135, 0, + 0, 1136, 1137, 1138, 1139, 1140, 1135, 1150, 1151, 1152, + 0, 1141, 1142, 1143, 1146, 1144, 1139, 1145, 1147, 1141, + 1148, 1138, 1149, 1153, 1136, 1143, 1144, 1142, 1154, 1156, + 0, 1155, 1150, 1151, 1152, 1142, 1157, 1159, 1145, 1158, + 1160, 1147, 1161, 1148, 1162, 0, 1163, 0, 1153, 1150, + 1164, 1165, 0, 1154, 1156, 1152, 1155, 1166, 0, 1167, + 0, 1157, 1159, 0, 1158, 1160, 1153, 1161, 1154, 1162, + 1155, 1163, 1158, 1168, 1169, 1164, 1165, 1156, 1170, 1172, + + 1173, 1175, 1166, 1164, 1167, 1159, 1171, 1162, 1165, 1171, + 1174, 0, 1171, 1171, 1171, 1171, 0, 0, 1168, 1169, + 1176, 1166, 0, 1170, 1172, 1173, 1175, 0, 1177, 1178, + 1179, 1171, 1180, 1181, 1182, 1174, 1168, 1183, 1184, 1185, + 1169, 1175, 1169, 1173, 1186, 1176, 1170, 1173, 1187, 2252, + 1188, 0, 1172, 1177, 1178, 1179, 1174, 1180, 1181, 1182, + 1189, 2252, 1183, 1184, 1185, 0, 1190, 1191, 1176, 1186, + 1192, 1177, 0, 1187, 1180, 1188, 1178, 1187, 0, 1193, + 1195, 1181, 1182, 1194, 1183, 1189, 1184, 1197, 0, 1189, + 1186, 1190, 1191, 1196, 1198, 1192, 1196, 1188, 2253, 1199, + + 1200, 0, 0, 1192, 1193, 1195, 1201, 0, 1194, 1202, + 2253, 1203, 1197, 1191, 1204, 1205, 1194, 1204, 1196, 1198, + 1207, 1206, 0, 1195, 1199, 1200, 1193, 1198, 1208, 1209, + 1210, 1201, 1200, 1197, 1202, 1197, 1203, 0, 1211, 1204, + 1205, 1212, 0, 1213, 1214, 1207, 1206, 1201, 1215, 1216, + 0, 1205, 2255, 1208, 1209, 1210, 1202, 1218, 1219, 1220, + 1203, 1206, 1207, 1211, 2255, 0, 1212, 1210, 1213, 1214, + 1221, 1209, 0, 1215, 1216, 1217, 1208, 1215, 1217, 0, + 1222, 1223, 1218, 1219, 1220, 1224, 1211, 2256, 1226, 1219, + 1214, 1220, 0, 1225, 1220, 1221, 1220, 1216, 1227, 2256, + + 1217, 1228, 1220, 1229, 1218, 1222, 1223, 1231, 1230, 0, + 1224, 1233, 1217, 1226, 0, 1232, 1234, 1224, 1225, 2258, + 1235, 1236, 1225, 1227, 1223, 1237, 1228, 1238, 1229, 1239, + 0, 2258, 1231, 1230, 1229, 1240, 1233, 1226, 1227, 1230, + 1232, 1234, 1242, 0, 1233, 1235, 1236, 1243, 1228, 1274, + 1237, 1231, 1238, 1276, 1239, 1232, 1234, 2259, 1236, 1241, + 1240, 1289, 1241, 2291, 2293, 0, 0, 1242, 1240, 2259, + 0, 2295, 1243, 1235, 1274, 2291, 2293, 1238, 1276, 1238, + 0, 0, 1274, 2295, 1241, 1242, 1289, 1243, 1245, 1242, + 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1245, 1248, 1248, + + 1248, 1248, 1248, 1248, 1248, 1248, 1249, 1249, 1249, 1251, + 1251, 1251, 2297, 2299, 0, 2301, 2302, 2326, 1249, 1249, + 0, 1251, 1251, 0, 2297, 2299, 1272, 2301, 2302, 2326, + 1249, 0, 1258, 1251, 1258, 1258, 1258, 1258, 1258, 1258, + 1258, 1258, 1267, 1267, 1267, 1267, 1267, 1267, 1267, 1267, + 1270, 1272, 1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270, + 0, 0, 0, 2328, 1294, 1275, 2330, 1273, 0, 0, + 1277, 1278, 1279, 1249, 1272, 2328, 1251, 1268, 2330, 1268, + 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1280, 1294, + 1275, 1281, 1273, 1268, 1268, 1277, 1278, 1279, 1282, 1268, + + 1278, 1283, 1268, 1268, 1268, 1268, 1268, 1268, 1273, 1275, + 1284, 1285, 1277, 1280, 1279, 1286, 1281, 1288, 1287, 1290, + 1291, 1280, 0, 1282, 1281, 1292, 1283, 1295, 0, 2332, + 0, 1282, 1293, 1296, 1283, 1284, 1285, 1297, 1298, 1299, + 1286, 2332, 1288, 1287, 1290, 1291, 0, 1284, 1286, 1300, + 1292, 1301, 1295, 1291, 1285, 1287, 1295, 1293, 1296, 1288, + 0, 1302, 1297, 1298, 1299, 1303, 1290, 1304, 1305, 0, + 1306, 1298, 1307, 1292, 1300, 1308, 1301, 1303, 1293, 1309, + 1310, 1311, 1300, 1299, 0, 1297, 1302, 1312, 1313, 0, + 1303, 1314, 1304, 1305, 1301, 1306, 1315, 1307, 1317, 1316, + + 1308, 1318, 1302, 0, 1309, 1310, 1311, 1308, 1319, 1305, + 1304, 1320, 1312, 1313, 1306, 1321, 1314, 1322, 0, 0, + 1307, 1315, 1323, 1317, 1316, 1324, 1318, 1326, 1310, 1325, + 1327, 1311, 1316, 1319, 1328, 1329, 1320, 1330, 1331, 0, + 1321, 1332, 1322, 1318, 1315, 0, 1321, 1323, 1322, 1333, + 1324, 1319, 1326, 0, 1325, 1327, 1326, 1334, 1335, 1328, + 1329, 1320, 1330, 1331, 1323, 1336, 1332, 1337, 1324, 1338, + 1325, 1339, 1340, 1327, 1333, 1328, 1341, 0, 1332, 1329, + 0, 1330, 1334, 1335, 1342, 1343, 1333, 1344, 1346, 1345, + 1336, 1331, 1337, 1347, 1338, 1348, 1339, 1340, 1349, 1350, + + 1352, 1341, 1338, 1351, 1337, 1340, 1336, 1341, 1353, 1342, + 1343, 0, 1344, 1346, 1345, 1354, 1355, 1356, 1347, 1339, + 1348, 1346, 0, 1349, 1350, 1352, 0, 1343, 1351, 1357, + 1358, 1360, 1359, 1353, 1361, 1344, 1345, 1362, 1363, 1347, + 1354, 1355, 1356, 1352, 0, 1364, 1365, 1350, 1366, 1367, + 1351, 1356, 0, 1369, 1357, 1358, 1360, 1359, 1368, 1361, + 1370, 0, 1362, 1363, 1354, 1359, 0, 1358, 1371, 1355, + 1364, 1365, 1372, 1366, 1367, 1373, 0, 1357, 1369, 0, + 1374, 1375, 1376, 1368, 1378, 1370, 1363, 1379, 1377, 1380, + 1379, 1368, 1381, 1371, 1382, 0, 1366, 1372, 1385, 1384, + + 1373, 1369, 1383, 1386, 1373, 1374, 1375, 1376, 1373, 1378, + 1387, 1388, 1379, 1377, 1380, 1390, 0, 1381, 1379, 1382, + 1372, 1391, 1389, 1385, 1384, 1389, 1374, 1383, 1386, 1375, + 1377, 1392, 1378, 1383, 1393, 1387, 1388, 1394, 1395, 1387, + 1390, 1382, 1384, 1396, 1388, 1385, 1391, 1389, 1397, 0, + 1398, 1399, 0, 1400, 0, 0, 1392, 1390, 1401, 1393, + 0, 1402, 1394, 1395, 1403, 0, 1404, 1406, 1396, 0, + 1405, 1407, 1408, 1397, 1395, 1398, 1399, 1393, 1400, 1394, + 1399, 1409, 1410, 1401, 1411, 1396, 1402, 1413, 1412, 1403, + 1401, 1404, 1406, 1414, 1400, 1405, 1407, 1408, 1415, 1404, + + 1397, 1416, 1405, 0, 0, 1417, 1409, 1410, 0, 1411, + 1418, 1419, 1413, 1412, 1420, 1421, 1422, 1407, 1414, 1423, + 1406, 1412, 0, 1415, 1424, 1425, 1416, 1426, 1414, 1411, + 1417, 1413, 1427, 1415, 1428, 1418, 1419, 1429, 1430, 1420, + 1421, 1422, 1431, 1432, 1423, 1433, 1435, 1417, 1434, 1424, + 1425, 1436, 1426, 2334, 0, 1437, 1438, 1427, 1420, 1428, + 1418, 1439, 1429, 1430, 0, 2334, 1440, 1431, 1432, 1425, + 1433, 1435, 1441, 1434, 1442, 1426, 1436, 1434, 1433, 1431, + 1437, 1438, 1444, 1429, 1443, 1445, 1439, 0, 1446, 1430, + 1432, 1440, 0, 1447, 1439, 1449, 0, 1441, 1436, 1442, + + 1448, 1450, 1437, 1438, 1451, 1452, 1453, 1444, 1455, 1443, + 1445, 1457, 1440, 1446, 0, 1441, 2336, 1443, 1447, 1459, + 1449, 1442, 1446, 0, 1454, 1448, 1450, 1445, 2336, 1451, + 1452, 1453, 1456, 1455, 1458, 1460, 1457, 1461, 1462, 1453, + 1463, 1455, 0, 1447, 1459, 0, 1464, 1465, 1448, 1454, + 0, 1451, 1459, 1452, 1466, 1467, 1454, 1456, 1468, 1458, + 1460, 1469, 1461, 1462, 1456, 1463, 1470, 1454, 1458, 0, + 1463, 1464, 1465, 1471, 1472, 1464, 1465, 0, 1473, 1466, + 1467, 1475, 1460, 1468, 1462, 1474, 1469, 1476, 1477, 1478, + 1469, 1470, 1479, 0, 1464, 1480, 0, 0, 1471, 1472, + + 0, 0, 1466, 1473, 1467, 1481, 1475, 1468, 1470, 1483, + 1474, 1485, 1476, 1477, 1478, 1486, 1482, 1479, 1478, 1482, + 1480, 1473, 1471, 1488, 1484, 1475, 1474, 1484, 1487, 1489, + 1481, 1490, 1477, 2359, 1483, 1479, 1485, 1518, 0, 0, + 1486, 1482, 1483, 0, 0, 2359, 0, 0, 1488, 1484, + 0, 0, 0, 1487, 1489, 0, 1490, 0, 1485, 0, + 0, 0, 1518, 1487, 1490, 0, 0, 1488, 1491, 1491, + 1491, 1491, 1491, 1491, 1491, 1491, 1492, 1489, 1492, 1492, + 1492, 1492, 1492, 1492, 1492, 1492, 1493, 1493, 1493, 1495, + 1495, 1495, 0, 0, 0, 0, 0, 0, 1493, 1493, + + 0, 1495, 1495, 0, 0, 0, 0, 0, 0, 0, + 1493, 0, 0, 1495, 1502, 1502, 1502, 1502, 1502, 1502, + 1502, 1502, 1503, 1520, 1503, 1503, 1503, 1503, 1503, 1503, + 1503, 1503, 1513, 1517, 1513, 1513, 1513, 1513, 1513, 1513, + 1513, 1513, 1519, 1517, 1521, 1522, 1523, 1524, 1520, 1525, + 1517, 1526, 1527, 1493, 1528, 1517, 1495, 1529, 1530, 1531, + 1532, 1533, 0, 1517, 1534, 1535, 0, 1519, 0, 1521, + 1522, 1523, 1524, 1536, 1525, 1519, 1526, 1527, 1537, 1528, + 1538, 0, 1529, 1530, 1531, 1532, 1533, 1529, 1523, 1534, + 1535, 1532, 1539, 1522, 1540, 1525, 1524, 1541, 1536, 1542, + + 0, 1543, 1544, 1537, 1545, 1538, 1531, 1546, 1547, 1533, + 1538, 1530, 1535, 1548, 1549, 1550, 0, 1539, 1551, 1540, + 1552, 0, 1541, 0, 1542, 1539, 1543, 1544, 1553, 1545, + 1554, 0, 1546, 1547, 1555, 1544, 1556, 1557, 1548, 1549, + 1550, 1547, 1558, 1551, 0, 1552, 1548, 1543, 1550, 1552, + 0, 0, 1559, 1553, 1560, 1554, 1546, 0, 1561, 1555, + 1562, 1556, 1557, 1563, 1564, 1565, 0, 1558, 0, 1555, + 0, 1568, 1566, 0, 1567, 1558, 1554, 1559, 1569, 1560, + 0, 1570, 1556, 1561, 1571, 1562, 1572, 1573, 1563, 1564, + 1565, 1559, 1574, 1562, 1575, 1563, 1568, 1566, 1565, 1567, + + 1576, 1577, 1578, 1569, 1561, 1566, 1570, 1567, 0, 1571, + 1579, 1572, 1573, 1564, 1580, 1581, 1582, 1574, 1573, 1575, + 1583, 1569, 1584, 1585, 1586, 1576, 1577, 1578, 1572, 1570, + 1587, 0, 0, 1576, 1588, 1579, 1589, 0, 1590, 1580, + 1581, 1582, 0, 1591, 1592, 1583, 0, 1584, 1585, 1586, + 1593, 1595, 1594, 0, 1581, 1587, 1583, 1579, 1596, 1588, + 1597, 1589, 1580, 1590, 1598, 1599, 1600, 1588, 1591, 1592, + 1602, 1585, 1601, 0, 1584, 1593, 1595, 1594, 1589, 1603, + 1591, 1594, 1604, 1596, 1605, 1597, 1606, 1593, 0, 1598, + 1599, 1600, 1607, 1598, 1608, 1602, 0, 1601, 1600, 1595, + + 0, 0, 1611, 1597, 1603, 1596, 0, 1604, 1609, 1605, + 1610, 1606, 1599, 1600, 1612, 1601, 1613, 1607, 1614, 1608, + 0, 1603, 1615, 0, 1616, 1608, 1606, 1611, 1617, 1618, + 1619, 1604, 1620, 1609, 0, 1610, 1607, 1605, 1621, 1612, + 1623, 1613, 1609, 1614, 1622, 1610, 1624, 1615, 1614, 1616, + 1625, 1626, 0, 1617, 1618, 1619, 1627, 1620, 1628, 1615, + 1630, 1612, 1631, 1621, 0, 1623, 0, 1629, 1632, 1622, + 1616, 1624, 0, 1620, 1633, 1625, 1626, 1622, 1634, 1635, + 1636, 1627, 1637, 1628, 1626, 1630, 1638, 1631, 1639, 1627, + 1640, 1628, 1629, 1632, 1641, 1642, 1629, 1625, 1643, 1633, + + 1632, 1644, 0, 1634, 1635, 1636, 1645, 1637, 1646, 1635, + 1647, 1638, 1648, 1639, 1649, 1640, 1650, 0, 1651, 1641, + 1642, 1654, 1652, 1643, 1634, 1652, 1644, 1639, 1655, 0, + 1657, 1645, 1656, 1646, 1644, 1647, 0, 1648, 0, 1649, + 0, 1650, 1640, 1651, 1643, 1653, 1654, 1652, 1653, 1658, + 1650, 1659, 1661, 1655, 1648, 1657, 1660, 1656, 1649, 1660, + 1655, 1657, 1662, 1654, 1663, 1656, 0, 1664, 0, 1665, + 1653, 1666, 0, 1667, 1658, 1668, 1659, 1661, 1658, 1671, + 1669, 1660, 1670, 0, 0, 1672, 1674, 1662, 1673, 1663, + 1675, 1659, 1664, 1661, 1665, 1676, 1666, 1664, 1667, 1679, + + 1668, 1677, 1678, 0, 1671, 1669, 1662, 1670, 1663, 1669, + 1672, 1674, 1709, 1673, 0, 1675, 0, 1665, 0, 0, + 1676, 0, 1666, 1708, 1679, 1668, 1677, 1678, 1670, 1671, + 1673, 1678, 1674, 1672, 1677, 0, 0, 1709, 1675, 1680, + 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1691, 1708, 1691, + 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1695, 1695, 1695, + 1695, 1695, 1695, 1695, 1695, 0, 0, 0, 0, 1708, + 1710, 1695, 1695, 0, 0, 0, 0, 0, 0, 1711, + 1695, 1695, 1695, 1695, 1695, 1695, 1702, 1702, 1702, 1702, + 1702, 1702, 1702, 1702, 1703, 1710, 1703, 1703, 1703, 1703, + + 1703, 1703, 1703, 1703, 1711, 1712, 1713, 1714, 1715, 1716, + 1717, 1718, 1711, 1719, 0, 1720, 1721, 1722, 1710, 1723, + 1724, 1725, 1726, 1728, 1729, 1727, 1730, 0, 0, 1731, + 1712, 1713, 1714, 1715, 1716, 1717, 1718, 1732, 1719, 1713, + 1720, 1721, 1722, 1733, 1723, 1724, 1725, 1726, 1728, 1729, + 1727, 1730, 1728, 1719, 1731, 1734, 1735, 1717, 1736, 1737, + 1738, 1720, 1732, 1723, 1721, 1727, 0, 1739, 1733, 1740, + 1741, 1742, 1743, 1744, 1746, 1745, 1747, 0, 1748, 0, + 1734, 1735, 1749, 1736, 1737, 1738, 0, 0, 1750, 1751, + 1753, 1733, 1739, 1752, 1740, 1741, 1742, 1743, 1744, 1746, + + 1745, 1747, 1736, 1748, 1742, 1754, 1755, 1749, 0, 1756, + 0, 1740, 1745, 1750, 1751, 1753, 1757, 1758, 1752, 1759, + 1743, 0, 1760, 1741, 1761, 1763, 1752, 1762, 1764, 0, + 1754, 1755, 1766, 1753, 1756, 1749, 1765, 1767, 1768, 1769, + 0, 1757, 1758, 1771, 1759, 1770, 1756, 1760, 1754, 1761, + 1763, 1772, 1762, 1764, 1755, 1773, 1774, 1766, 1775, 1759, + 1776, 1765, 1767, 1768, 1769, 1763, 1767, 1777, 1771, 1778, + 1770, 1761, 1769, 1762, 1779, 1780, 1772, 1765, 1781, 1770, + 1773, 1774, 1782, 1775, 1768, 1776, 1783, 1784, 1773, 1776, + 1785, 1771, 1777, 1786, 1778, 1787, 0, 1788, 0, 1779, + + 1780, 0, 1790, 1781, 1774, 0, 1779, 1782, 1791, 1777, + 1789, 1783, 1784, 1792, 1793, 1785, 1794, 0, 1786, 1798, + 1787, 1780, 1788, 1782, 0, 0, 1788, 1790, 1795, 1796, + 1785, 1795, 1796, 1791, 1784, 1789, 0, 1797, 1792, 1793, + 1786, 1794, 1789, 1793, 1798, 1794, 1799, 1801, 1787, 1794, + 1790, 1805, 1800, 1795, 1796, 1800, 0, 1802, 1798, 1807, + 1802, 1792, 1797, 1806, 1803, 1804, 1797, 1803, 1804, 0, + 0, 1799, 1801, 1809, 1810, 1808, 1805, 1800, 1811, 1812, + 1813, 1799, 1802, 1808, 1807, 1814, 1816, 1815, 1806, 1803, + 1804, 1849, 1817, 1806, 1805, 1801, 0, 0, 1809, 1810, + + 1808, 1807, 0, 1811, 1812, 1813, 1810, 0, 0, 0, + 1814, 1816, 1815, 0, 1814, 1812, 1849, 1817, 1848, 1809, + 1815, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 0, + 0, 0, 1816, 1817, 1829, 1829, 1829, 1829, 1829, 1829, + 1829, 1829, 1830, 1848, 1830, 1830, 1830, 1830, 1830, 1830, + 1830, 1830, 1834, 1848, 1834, 1834, 1834, 1834, 1834, 1834, + 1834, 1834, 1834, 0, 0, 0, 0, 0, 1834, 1834, + 0, 0, 0, 0, 0, 0, 1850, 1834, 1834, 1834, + 1834, 1834, 1834, 1843, 1851, 1843, 1843, 1843, 1843, 1843, + 1843, 1843, 1843, 1847, 1847, 1847, 1847, 1847, 1847, 1847, + + 1847, 1850, 1852, 1853, 1854, 1855, 1856, 1847, 1847, 1851, + 1857, 0, 1858, 1859, 1860, 1861, 1847, 1847, 1847, 1847, + 1847, 1847, 1862, 1863, 1864, 1865, 1866, 1852, 1853, 1854, + 1855, 1856, 0, 1867, 1868, 1857, 1853, 1858, 1859, 1860, + 1861, 1869, 1870, 1871, 1858, 1872, 1873, 1862, 1863, 1864, + 1865, 1866, 1875, 1874, 1854, 1876, 1877, 1865, 1867, 1868, + 1878, 0, 1879, 1880, 1881, 1882, 1869, 1870, 1871, 0, + 1872, 1873, 1883, 1884, 1885, 0, 1868, 1875, 1874, 1870, + 1876, 1877, 1886, 1869, 1874, 1878, 1871, 1879, 1880, 1881, + 1882, 1887, 1888, 1889, 1873, 1890, 1891, 1883, 1884, 1885, + + 1881, 1892, 1893, 1878, 1894, 1882, 1895, 1886, 1896, 1877, + 1897, 0, 1894, 1898, 1899, 1884, 1887, 1888, 1889, 0, + 1890, 1891, 1900, 0, 1901, 1902, 1892, 1893, 1890, 1894, + 1903, 1895, 1887, 1896, 1904, 1897, 1888, 1893, 1898, 1899, + 1905, 1897, 1906, 1907, 1898, 1899, 1908, 1900, 1892, 1901, + 1902, 1900, 1895, 1909, 1910, 1903, 1911, 0, 1912, 1904, + 1913, 1912, 1914, 0, 0, 1905, 0, 1906, 1907, 0, + 0, 1908, 1901, 1902, 0, 1915, 1916, 1917, 1909, 1910, + 1919, 1911, 1909, 1912, 1918, 1913, 1920, 1914, 1905, 1906, + 1954, 1907, 1955, 1958, 1908, 1914, 0, 0, 0, 1911, + + 1915, 1916, 1917, 1910, 1959, 1919, 0, 0, 0, 1918, + 0, 1920, 0, 0, 0, 1954, 0, 1955, 1958, 1917, + 0, 1919, 1916, 1954, 0, 0, 1918, 0, 0, 1959, + 1920, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1938, + 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1939, 0, 1939, + 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 0, 0, + 0, 0, 1960, 1939, 1939, 0, 0, 0, 0, 0, + 0, 0, 1939, 1939, 1939, 1939, 1939, 1939, 1948, 1948, + 1948, 1948, 1948, 1948, 1948, 1948, 1949, 1960, 1949, 1949, + 1949, 1949, 1949, 1949, 1949, 1949, 1953, 0, 1953, 1953, + + 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1956, 1957, 1961, + 1963, 0, 1953, 1953, 1962, 1964, 0, 1965, 1953, 1966, + 1967, 1953, 1953, 1953, 1953, 1953, 1953, 0, 1968, 1969, + 1970, 0, 1956, 1957, 1961, 1963, 1956, 1971, 1972, 1962, + 1964, 1957, 1965, 1973, 1966, 1967, 1974, 1975, 1976, 1966, + 1977, 1978, 1961, 1968, 1969, 1970, 1962, 1979, 1981, 1983, + 1969, 1980, 1971, 1972, 0, 0, 1982, 1970, 1973, 1982, + 1984, 1974, 1975, 1976, 1985, 1977, 1978, 1986, 1987, 1989, + 0, 0, 1979, 1981, 1983, 1972, 1980, 1974, 1988, 1977, + 1978, 1982, 1980, 1979, 1990, 1984, 0, 1991, 0, 1985, + + 0, 1976, 1986, 1987, 1989, 1981, 1983, 1992, 1994, 1987, + 1993, 1995, 1984, 1988, 0, 0, 1996, 0, 2032, 1990, + 2034, 1985, 1991, 1990, 1986, 1990, 2033, 2036, 1989, 2040, + 1988, 0, 1992, 1994, 0, 1993, 1995, 0, 0, 1991, + 1992, 1996, 1993, 2032, 0, 2034, 0, 0, 1995, 0, + 0, 2033, 2036, 0, 2040, 0, 1994, 2009, 2009, 2009, + 2009, 2009, 2009, 2009, 2009, 2013, 2033, 2013, 2013, 2013, + 2013, 2013, 2013, 2013, 2013, 2026, 2026, 2026, 2026, 2026, + 2026, 2026, 2026, 2030, 2030, 2030, 2030, 2030, 2030, 2030, + 2030, 2035, 2037, 0, 2038, 2026, 2031, 0, 2031, 2031, + + 2031, 2031, 2031, 2031, 2031, 2031, 2031, 0, 0, 2039, + 2041, 2042, 2031, 2031, 2043, 2044, 2035, 2037, 2031, 2038, + 2045, 2031, 2031, 2031, 2031, 2031, 2031, 2038, 2046, 2047, + 2048, 2049, 2037, 2035, 2039, 2041, 2042, 2050, 0, 2043, + 2044, 2051, 0, 2041, 2052, 2045, 0, 2054, 0, 2053, + 2055, 2056, 2057, 2046, 2047, 2048, 2049, 2039, 0, 2048, + 2058, 0, 2050, 2045, 2059, 2061, 2051, 2046, 2062, 2052, + 0, 2047, 2054, 2049, 2053, 2055, 2056, 2057, 2060, 2054, + 2099, 2100, 2056, 2051, 2101, 2058, 2052, 2102, 2053, 2059, + 2061, 0, 2055, 2062, 2105, 2057, 0, 0, 0, 0, + + 0, 0, 0, 2060, 0, 2099, 2100, 0, 2058, 2101, + 0, 2059, 2102, 0, 0, 0, 2062, 2101, 0, 2105, + 0, 2060, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, + 2082, 0, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, + 2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093, 2097, 2103, + 2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097, 2104, 2106, + 2093, 2107, 2108, 2109, 2110, 2111, 2112, 2113, 0, 2112, + 2113, 2114, 0, 2116, 2103, 0, 2115, 2117, 0, 2119, + 2118, 2120, 2163, 2104, 2106, 2160, 2107, 2108, 2109, 2110, + 2111, 2112, 2113, 2108, 0, 0, 2114, 2103, 2116, 0, + + 2104, 2115, 2117, 2114, 2119, 2118, 2120, 2163, 0, 0, + 2160, 0, 2111, 0, 2116, 2115, 2118, 0, 2160, 2138, + 2119, 2138, 2138, 2138, 2138, 2138, 2138, 2138, 2138, 2120, + 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2158, 2159, + 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2161, 2162, + 2165, 2164, 2166, 2167, 2168, 2166, 0, 2169, 2170, 2171, + 2211, 2212, 2214, 0, 2159, 2192, 2192, 2192, 2192, 2192, + 2192, 2192, 2192, 2161, 2162, 2165, 2164, 2166, 2167, 2168, + 2159, 2164, 2169, 2170, 2171, 2211, 2212, 2214, 2162, 0, + 0, 0, 2171, 2212, 2213, 2215, 2161, 2165, 2168, 2193, + + 2169, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2209, + 0, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2213, + 2215, 2216, 2218, 2217, 2219, 2220, 2239, 2239, 2239, 2239, + 2239, 2239, 2239, 2239, 2262, 2215, 2263, 0, 2269, 0, + 2310, 2213, 2266, 2265, 0, 0, 2216, 2218, 2217, 2219, + 2220, 0, 0, 0, 0, 2264, 0, 2220, 0, 2262, + 0, 2263, 2218, 2269, 2219, 2310, 2267, 2266, 2265, 2216, + 2217, 2260, 2260, 2260, 2260, 2260, 2260, 2260, 2260, 2261, + 2264, 2261, 2261, 2261, 2261, 2261, 2261, 2261, 2261, 2265, + 2266, 2267, 2268, 2307, 2306, 2308, 2309, 0, 2264, 2267, + + 2286, 2286, 2286, 2286, 2286, 2286, 2286, 2286, 2304, 2304, + 2304, 2304, 2304, 2304, 2304, 2304, 0, 2268, 2307, 2306, + 2308, 2309, 2339, 2340, 2268, 2306, 2307, 0, 2304, 2338, + 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2341, 2342, 2362, + 2363, 2308, 2365, 2364, 2309, 0, 0, 2339, 2340, 2338, + 2375, 2376, 2340, 2377, 2379, 2380, 0, 2381, 0, 0, + 0, 2339, 2341, 2342, 2362, 2363, 0, 2365, 2364, 0, + 2341, 2342, 2364, 0, 0, 2375, 2376, 0, 2377, 2379, + 2380, 2363, 2381, 0, 0, 0, 2362, 0, 0, 0, + 0, 0, 0, 0, 2377, 0, 0, 0, 2376, 0, + + 0, 0, 2380, 2383, 2383, 2383, 2383, 2383, 2383, 2383, + 2383, 2383, 2383, 2383, 2383, 2383, 2384, 2384, 2384, 2384, + 2384, 2384, 2384, 2384, 2384, 2384, 2384, 2384, 2384, 2385, + 2385, 2385, 2385, 2385, 0, 2385, 2385, 0, 2385, 2385, + 2386, 2386, 2386, 0, 0, 2386, 2387, 2387, 2387, 2387, + 2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387, 2388, + 2388, 2388, 0, 0, 2388, 2389, 2389, 2389, 0, 0, + 2389, 2390, 0, 2390, 0, 0, 2390, 2391, 2391, 2391, + 0, 0, 2391, 2392, 2392, 2392, 2392, 2392, 2392, 2392, + 2392, 2392, 2392, 2392, 2392, 2392, 2393, 0, 0, 0, + + 2393, 2393, 0, 2393, 2393, 2394, 2394, 2394, 0, 0, + 2394, 2395, 0, 2395, 0, 0, 2395, 2395, 2395, 0, + 0, 2395, 2396, 0, 2396, 0, 0, 2396, 2397, 2397, + 2397, 0, 0, 2397, 2398, 2398, 2398, 0, 0, 2398, + 2399, 2399, 2399, 0, 0, 2399, 2400, 2400, 2400, 2400, + 0, 2400, 2400, 0, 2400, 2400, 2401, 2401, 0, 0, + 0, 2401, 2401, 2402, 2402, 2402, 0, 0, 2402, 2403, + 2403, 0, 0, 0, 0, 2403, 2404, 0, 2404, 0, + 0, 2404, 2404, 2404, 0, 0, 2404, 2405, 2405, 2405, + 0, 0, 2405, 2406, 2406, 2406, 0, 0, 2406, 2407, + + 0, 2407, 0, 0, 2407, 2408, 2408, 2408, 0, 0, + 2408, 2409, 2409, 2409, 0, 0, 2409, 2410, 2410, 2410, + 0, 0, 2410, 2411, 0, 2411, 0, 2411, 2411, 2412, + 0, 2412, 0, 0, 2412, 2412, 2412, 0, 0, 2412, + 2413, 0, 2413, 0, 0, 2413, 2413, 2413, 0, 0, + 2413, 2414, 2414, 2414, 0, 0, 2414, 2415, 0, 2415, + 0, 0, 2415, 2416, 2416, 2416, 0, 0, 2416, 2417, + 0, 2417, 0, 0, 2417, 2418, 0, 2418, 2418, 2418, + 0, 0, 2418, 2419, 2419, 2419, 0, 0, 2419, 2420, + 2420, 2420, 0, 0, 2420, 2421, 2421, 2421, 0, 0, + + 2421, 2422, 2422, 2422, 0, 2422, 2422, 2423, 0, 2423, + 0, 0, 2423, 2424, 0, 2424, 0, 0, 2424, 2424, + 2424, 0, 0, 2424, 2425, 2425, 2425, 0, 0, 2425, + 2426, 2426, 2426, 0, 0, 2426, 2427, 2427, 2427, 0, + 0, 2427, 2428, 2428, 2428, 0, 0, 2428, 2429, 2429, + 2429, 0, 0, 2429, 2430, 2430, 2430, 0, 0, 2430, + 2431, 2431, 2431, 0, 0, 2431, 2432, 0, 2432, 0, + 0, 2432, 2433, 2433, 2433, 0, 0, 2433, 2434, 2434, + 2434, 0, 0, 2434, 2435, 0, 2435, 0, 2435, 2435, + 2436, 2436, 2436, 0, 2436, 2436, 2437, 0, 2437, 0, + + 0, 2437, 2438, 0, 2438, 2438, 2438, 0, 2438, 2438, + 2439, 2439, 2439, 0, 2439, 2439, 2440, 2440, 2440, 0, + 0, 2440, 2441, 2441, 2441, 0, 0, 2441, 2442, 2442, + 2442, 0, 0, 2442, 2443, 0, 2443, 0, 0, 2443, + 2444, 2444, 2444, 0, 0, 2444, 2445, 0, 2445, 0, + 0, 2445, 2446, 2446, 2446, 0, 0, 2446, 2447, 0, + 2447, 0, 0, 2447, 2448, 2448, 2448, 0, 0, 2448, + 2449, 2449, 2449, 0, 0, 2449, 2450, 2450, 2450, 0, + 0, 2450, 2451, 2451, 2451, 0, 0, 2451, 2452, 2452, + 2452, 0, 2452, 2452, 2453, 2453, 2453, 0, 2453, 2453, + + 2454, 2454, 2454, 0, 2454, 2454, 2455, 2455, 2455, 0, + 2455, 2455, 2456, 0, 2456, 0, 0, 2456, 2457, 2457, + 2457, 0, 0, 2457, 2458, 2458, 2458, 0, 0, 2458, + 2459, 2459, 2459, 0, 0, 2459, 2460, 2460, 2460, 0, + 0, 2460, 2461, 2461, 2461, 0, 0, 2461, 2462, 2462, + 2462, 0, 0, 2462, 2463, 2463, 2463, 0, 0, 2463, + 2464, 2464, 2464, 0, 0, 2464, 2465, 2465, 2465, 0, + 0, 2465, 2466, 2466, 2466, 0, 0, 2466, 2467, 0, + 2467, 0, 0, 2467, 2468, 2468, 2468, 0, 0, 2468, + 2469, 2469, 2469, 0, 0, 2469, 2470, 0, 2470, 0, + + 2470, 2470, 2471, 2471, 2471, 0, 2471, 2471, 2472, 0, + 2472, 0, 0, 2472, 2473, 2473, 2473, 0, 2473, 2473, + 2474, 0, 2474, 0, 0, 2474, 2475, 2475, 2475, 0, + 2475, 2475, 2476, 2476, 2476, 0, 2476, 2476, 2477, 2477, + 2477, 0, 0, 2477, 2478, 2478, 2478, 0, 0, 2478, + 2479, 2479, 2479, 0, 0, 2479, 2480, 0, 2480, 0, + 0, 2480, 2481, 2481, 2481, 0, 0, 2481, 2482, 0, + 2482, 0, 0, 2482, 2483, 2483, 2483, 0, 0, 2483, + 2484, 0, 2484, 0, 0, 2484, 2485, 2485, 2485, 0, + 0, 2485, 2486, 0, 2486, 0, 0, 2486, 2487, 2487, + + 2487, 0, 0, 2487, 2488, 2488, 2488, 0, 0, 2488, + 2489, 2489, 2489, 0, 0, 2489, 2490, 2490, 2490, 0, + 0, 2490, 2491, 2491, 2491, 0, 2491, 2491, 2492, 2492, + 2492, 0, 2492, 2492, 2493, 2493, 2493, 0, 2493, 2493, + 2494, 2494, 2494, 0, 2494, 2494, 2495, 2495, 2495, 0, + 2495, 2495, 2496, 2496, 2496, 0, 2496, 2496, 2497, 0, + 2497, 0, 0, 2497, 2498, 2498, 2498, 0, 0, 2498, + 2499, 2499, 2499, 0, 0, 2499, 2500, 2500, 2500, 0, + 0, 2500, 2501, 2501, 2501, 0, 0, 2501, 2502, 2502, + 2502, 0, 0, 2502, 2503, 2503, 2503, 0, 0, 2503, + + 2504, 2504, 2504, 0, 0, 2504, 2505, 2505, 2505, 0, + 0, 2505, 2506, 2506, 2506, 0, 0, 2506, 2507, 2507, + 2507, 0, 0, 2507, 2508, 2508, 2508, 0, 0, 2508, + 2509, 2509, 2509, 0, 0, 2509, 2510, 0, 2510, 0, + 0, 2510, 2511, 0, 2511, 2511, 2511, 0, 0, 2511, + 2512, 2512, 2512, 0, 0, 2512, 2513, 2513, 2513, 0, + 0, 2513, 2514, 0, 2514, 0, 2514, 2514, 2515, 2515, + 2515, 0, 2515, 2515, 2516, 0, 2516, 0, 0, 2516, + 2517, 2517, 2517, 0, 2517, 2517, 2518, 0, 2518, 0, + 0, 2518, 2519, 2519, 2519, 0, 2519, 2519, 2520, 0, + + 2520, 0, 0, 2520, 2521, 2521, 2521, 0, 2521, 2521, + 2522, 2522, 2522, 0, 2522, 2522, 2523, 2523, 2523, 0, + 0, 2523, 2524, 2524, 2524, 0, 0, 2524, 2525, 2525, + 2525, 0, 0, 2525, 2526, 0, 2526, 0, 0, 2526, + 2527, 2527, 2527, 0, 0, 2527, 2528, 0, 2528, 0, + 0, 2528, 2529, 2529, 2529, 0, 0, 2529, 2530, 0, + 2530, 0, 0, 2530, 2531, 2531, 2531, 0, 0, 2531, + 2532, 0, 2532, 0, 0, 2532, 2533, 2533, 2533, 0, + 0, 2533, 2534, 0, 2534, 0, 0, 2534, 2535, 2535, + 2535, 0, 0, 2535, 2536, 2536, 2536, 0, 0, 2536, + + 2537, 2537, 2537, 0, 0, 2537, 2538, 2538, 2538, 0, + 0, 2538, 2539, 2539, 2539, 0, 2539, 2539, 2540, 2540, + 2540, 0, 2540, 2540, 2541, 2541, 2541, 0, 2541, 2541, + 2542, 2542, 2542, 0, 2542, 2542, 2543, 2543, 2543, 0, + 2543, 2543, 2544, 2544, 2544, 0, 2544, 2544, 2545, 2545, + 2545, 0, 2545, 2545, 2546, 2546, 2546, 0, 2546, 2546, + 2547, 0, 2547, 0, 0, 2547, 2548, 0, 2548, 2548, + 2548, 0, 2548, 2548, 2549, 2549, 2549, 0, 0, 2549, + 2550, 2550, 2550, 0, 0, 2550, 2551, 2551, 2551, 0, + 0, 2551, 2552, 2552, 2552, 0, 0, 2552, 2553, 2553, + + 2553, 0, 0, 2553, 2554, 2554, 2554, 0, 0, 2554, + 2555, 2555, 2555, 0, 0, 2555, 2556, 2556, 2556, 0, + 0, 2556, 2557, 2557, 2557, 0, 0, 2557, 2558, 2558, + 2558, 0, 0, 2558, 2559, 2559, 2559, 0, 0, 2559, + 2560, 2560, 2560, 0, 0, 2560, 2561, 2561, 2561, 0, + 0, 2561, 2562, 2562, 2562, 0, 0, 2562, 2563, 0, + 2563, 0, 0, 2563, 2564, 2564, 2564, 0, 0, 2564, + 2565, 2565, 2565, 0, 0, 2565, 2566, 0, 2566, 0, + 2566, 2566, 2567, 2567, 2567, 0, 2567, 2567, 2568, 0, + 2568, 0, 0, 2568, 2569, 2569, 2569, 0, 2569, 2569, + + 2570, 0, 2570, 0, 0, 2570, 2571, 2571, 2571, 0, + 2571, 2571, 2572, 0, 2572, 0, 0, 2572, 2573, 2573, + 2573, 0, 2573, 2573, 2574, 0, 2574, 0, 0, 2574, + 2575, 2575, 2575, 0, 2575, 2575, 2576, 2576, 2576, 0, + 2576, 2576, 2577, 2577, 2577, 2577, 2577, 0, 2577, 2577, + 0, 2577, 2577, 2578, 2578, 2578, 0, 0, 2578, 2579, + 2579, 2579, 0, 0, 2579, 2580, 2580, 2580, 0, 0, + 2580, 2581, 0, 2581, 0, 0, 2581, 2582, 2582, 2582, + 0, 0, 2582, 2583, 0, 2583, 0, 0, 2583, 2584, + 2584, 2584, 0, 0, 2584, 2585, 0, 2585, 0, 0, + + 2585, 2586, 2586, 2586, 0, 0, 2586, 2587, 0, 2587, + 0, 0, 2587, 2588, 2588, 2588, 0, 0, 2588, 2589, + 0, 2589, 0, 0, 2589, 2590, 2590, 2590, 0, 0, + 2590, 2591, 0, 2591, 0, 0, 2591, 2592, 2592, 2592, + 0, 0, 2592, 2593, 2593, 2593, 0, 0, 2593, 2594, + 2594, 2594, 0, 0, 2594, 2595, 2595, 2595, 0, 0, + 2595, 2596, 2596, 2596, 0, 2596, 2596, 2597, 2597, 2597, + 0, 2597, 2597, 2598, 2598, 2598, 0, 2598, 2598, 2599, + 2599, 2599, 0, 2599, 2599, 2600, 2600, 2600, 0, 2600, + 2600, 2601, 2601, 2601, 0, 2601, 2601, 2602, 2602, 2602, + + 0, 2602, 2602, 2603, 2603, 2603, 0, 2603, 2603, 2604, + 2604, 2604, 0, 2604, 2604, 2605, 2605, 2605, 0, 2605, + 2605, 2606, 0, 2606, 0, 0, 2606, 2607, 2607, 2607, + 2607, 2607, 0, 2607, 2607, 0, 2607, 2607, 2608, 2608, + 2608, 0, 0, 2608, 2609, 2609, 2609, 0, 0, 2609, + 2610, 2610, 2610, 0, 0, 2610, 2611, 0, 2611, 0, + 0, 2611, 2612, 2612, 2612, 0, 0, 2612, 2613, 0, + 2613, 0, 0, 2613, 2614, 2614, 2614, 0, 0, 2614, + 2615, 0, 2615, 0, 0, 2615, 2616, 2616, 2616, 0, + 0, 2616, 2617, 0, 2617, 0, 0, 2617, 2618, 2618, + + 2618, 0, 0, 2618, 2619, 0, 2619, 0, 0, 2619, + 2620, 2620, 2620, 0, 0, 2620, 2621, 0, 2621, 0, + 0, 2621, 2622, 2622, 2622, 0, 0, 2622, 2623, 0, + 2623, 0, 0, 2623, 2624, 2624, 2624, 0, 0, 2624, + 2625, 2625, 2625, 0, 0, 2625, 2626, 0, 2626, 0, + 2626, 2626, 2627, 2627, 2627, 0, 2627, 2627, 2628, 0, + 2628, 0, 0, 2628, 2629, 2629, 2629, 0, 2629, 2629, + 2630, 0, 2630, 0, 0, 2630, 2631, 2631, 2631, 0, + 2631, 2631, 2632, 0, 2632, 0, 0, 2632, 2633, 2633, + 2633, 0, 2633, 2633, 2634, 0, 2634, 0, 0, 2634, + + 2635, 2635, 2635, 0, 2635, 2635, 2636, 0, 2636, 0, + 0, 2636, 2637, 2637, 2637, 0, 2637, 2637, 2638, 2638, + 2638, 0, 2638, 2638, 2639, 2639, 2639, 2639, 2639, 0, + 2639, 2639, 0, 2639, 2639, 2640, 2640, 2640, 0, 0, + 2640, 2641, 0, 2641, 0, 0, 2641, 2642, 0, 2642, + 0, 0, 2642, 2643, 0, 2643, 0, 0, 2643, 2644, + 0, 2644, 0, 0, 2644, 2645, 0, 2645, 0, 0, + 2645, 2646, 0, 2646, 0, 0, 2646, 2647, 0, 2647, + 0, 0, 2647, 2648, 2648, 2648, 0, 0, 2648, 2649, + 0, 2649, 0, 0, 2649, 2650, 2650, 2650, 0, 0, + + 2650, 2651, 2651, 2651, 0, 0, 2651, 2652, 0, 2652, + 0, 2652, 2652, 2653, 2653, 2653, 0, 2653, 2653, 2654, + 0, 2654, 0, 2654, 2654, 2655, 2655, 2655, 0, 2655, + 2655, 2656, 0, 2656, 0, 2656, 2656, 2657, 2657, 2657, + 0, 2657, 2657, 2658, 0, 2658, 0, 2658, 2658, 2659, + 2659, 2659, 0, 2659, 2659, 2660, 0, 2660, 0, 2660, + 2660, 2661, 2661, 2661, 0, 2661, 2661, 2662, 0, 2662, + 0, 2662, 2662, 2663, 2663, 2663, 0, 2663, 2663, 2664, + 0, 2664, 0, 0, 2664, 2665, 0, 2665, 0, 0, + 2665, 2666, 0, 2666, 0, 0, 2666, 2667, 0, 2667, + + 0, 0, 2667, 2668, 0, 2668, 0, 0, 2668, 2669, + 0, 2669, 0, 0, 2669, 2670, 0, 2670, 0, 0, + 2670, 2671, 0, 2671, 0, 0, 2671, 2672, 0, 2672, + 0, 0, 2672, 2673, 2673, 2673, 0, 0, 2673, 2674, + 0, 2674, 0, 2674, 2674, 2675, 0, 2675, 0, 2675, + 2675, 2676, 0, 2676, 0, 2676, 2676, 2677, 0, 2677, + 0, 2677, 2677, 2678, 0, 2678, 0, 2678, 2678, 2679, + 0, 2679, 0, 2679, 2679, 2680, 0, 2680, 0, 2680, + 2680, 2681, 2681, 2681, 0, 2681, 2681, 2682, 0, 2682, + 0, 2682, 2682, 2683, 0, 2683, 0, 0, 2683, 2684, + + 0, 2684, 0, 0, 2684, 2685, 0, 2685, 0, 2685, + 2685, 2686, 0, 2686, 0, 2686, 2686, 2687, 0, 2687, + 0, 2687, 2687, 2688, 0, 2688, 0, 2688, 2688, 2689, + 0, 2689, 0, 2689, 2689, 2690, 0, 2690, 0, 2690, + 2690, 2691, 0, 2691, 0, 2691, 2691, 2692, 0, 2692, + 0, 2692, 2692, 2693, 0, 2693, 0, 2693, 2693, 2694, + 0, 2694, 0, 2694, 2694, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, 2382, + 2382, 2382, 2382, 2382, 2382 + } ; + +/* Table of booleans, true if rule could match eol. */ +static const flex_int32_t yy_rule_can_match_eol[413] = + { 0, +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, }; + +static const flex_int16_t yy_rule_linenum[412] = + { 0, + 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, + 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, + 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, + 300, 302, 304, 305, 306, 307, 309, 312, 313, 314, + 317, 318, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 330, 331, 333, 335, 336, 337, 338, 340, + 342, 343, 344, 345, 346, 347, 348, 350, 351, 353, + 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, + 364, 365, 366, 367, 370, 371, 372, 374, 375, 376, + + 377, 378, 379, 380, 381, 383, 384, 386, 387, 390, + 391, 392, 395, 396, 397, 398, 399, 400, 401, 404, + 405, 406, 407, 409, 411, 415, 416, 417, 420, 421, + 423, 424, 425, 426, 429, 431, 432, 433, 435, 437, + 438, 441, 442, 443, 445, 447, 449, 452, 454, 455, + 457, 459, 460, 463, 464, 465, 466, 467, 469, 470, + 471, 475, 476, 478, 480, 482, 484, 485, 487, 488, + 490, 491, 493, 494, 495, 497, 498, 500, 502, 503, + 504, 505, 506, 509, 511, 514, 517, 519, 520, 522, + 525, 527, 529, 533, 534, 535, 536, 538, 539, 541, + + 542, 547, 548, 549, 550, 553, 554, 555, 556, 557, + 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, + 568, 569, 572, 573, 574, 575, 576, 577, 579, 580, + 582, 584, 585, 587, 588, 589, 590, 591, 592, 593, + 596, 599, 601, 602, 605, 606, 608, 609, 612, 615, + 617, 619, 621, 623, 625, 627, 629, 630, 632, 635, + 638, 641, 644, 645, 647, 649, 650, 652, 654, 656, + 659, 660, 664, 665, 666, 667, 668, 669, 670, 671, + 672, 673, 674, 675, 676, 677, 678, 679, 680, 682, + 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, + + 693, 694, 695, 696, 697, 700, 701, 702, 703, 704, + 706, 708, 710, 711, 714, 716, 718, 719, 720, 721, + 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, + 732, 733, 734, 735, 736, 737, 738, 741, 742, 743, + 744, 745, 746, 749, 752, 754, 755, 756, 757, 758, + 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, + 769, 770, 773, 775, 778, 779, 782, 785, 786, 788, + 789, 791, 793, 795, 797, 798, 799, 800, 801, 802, + 805, 807, 810, 811, 812, 815, 816, 818, 820, 822, + 823, 825, 826, 829, 831, 833, 838, 844, 849, 859, + + 872, 877, 883, 888, 893, 897, 902, 903, 904, 907, + 916 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "scanner.l" +/* + * Copyright (c) 2007-2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ +#line 12 "scanner.l" + +#include <nft.h> + +#include <limits.h> +#include <glob.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/netfilter.h> +#include <sys/stat.h> + +#include <nftables.h> +#include <erec.h> +#include <rule.h> +#include <parser.h> +#include "parser_bison.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) \ +{ \ + result = 0; \ + errno = 0; \ + \ + while (result < max_size) { \ + int chr = fgetc(yyin); \ + \ + if (chr != EOF) { \ + buf[result++] = chr; \ + if (chr == '\n' || chr == ' ') \ + break; \ + continue; \ + } \ + \ + if (ferror(yyin)) { \ + if (errno != EINTR) { \ + YY_FATAL_ERROR("input in flex scanner failed"); \ + break; \ + } \ + errno = 0; \ + clearerr(yyin); \ + } \ + break; \ + } \ +} + +static void scanner_pop_buffer(yyscan_t scanner); + + +static void init_pos(struct input_descriptor *indesc) +{ + indesc->lineno = 1; + indesc->column = 1; + indesc->token_offset = 0; + 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->last_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; +} + +static void scanner_push_start_cond(void *scanner, enum startcond_type type); + +#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); + +#line 3986 "scanner.c" + +#line 3988 "scanner.c" + +#define INITIAL 0 +#define SCANSTATE_ARP 1 +#define SCANSTATE_AT 2 +#define SCANSTATE_CT 3 +#define SCANSTATE_COUNTER 4 +#define SCANSTATE_ETH 5 +#define SCANSTATE_GRE 6 +#define SCANSTATE_ICMP 7 +#define SCANSTATE_IGMP 8 +#define SCANSTATE_IP 9 +#define SCANSTATE_IP6 10 +#define SCANSTATE_LAST 11 +#define SCANSTATE_LIMIT 12 +#define SCANSTATE_META 13 +#define SCANSTATE_POLICY 14 +#define SCANSTATE_QUOTA 15 +#define SCANSTATE_SCTP 16 +#define SCANSTATE_SECMARK 17 +#define SCANSTATE_TCP 18 +#define SCANSTATE_TYPE 19 +#define SCANSTATE_VLAN 20 +#define SCANSTATE_XT 21 +#define SCANSTATE_CMD_DESTROY 22 +#define SCANSTATE_CMD_EXPORT 23 +#define SCANSTATE_CMD_IMPORT 24 +#define SCANSTATE_CMD_LIST 25 +#define SCANSTATE_CMD_MONITOR 26 +#define SCANSTATE_CMD_RESET 27 +#define SCANSTATE_EXPR_AH 28 +#define SCANSTATE_EXPR_COMP 29 +#define SCANSTATE_EXPR_DCCP 30 +#define SCANSTATE_EXPR_DST 31 +#define SCANSTATE_EXPR_ESP 32 +#define SCANSTATE_EXPR_FIB 33 +#define SCANSTATE_EXPR_FRAG 34 +#define SCANSTATE_EXPR_HASH 35 +#define SCANSTATE_EXPR_HBH 36 +#define SCANSTATE_EXPR_IPSEC 37 +#define SCANSTATE_EXPR_MH 38 +#define SCANSTATE_EXPR_NUMGEN 39 +#define SCANSTATE_EXPR_OSF 40 +#define SCANSTATE_EXPR_QUEUE 41 +#define SCANSTATE_EXPR_RT 42 +#define SCANSTATE_EXPR_SCTP_CHUNK 43 +#define SCANSTATE_EXPR_SOCKET 44 +#define SCANSTATE_EXPR_TH 45 +#define SCANSTATE_EXPR_UDP 46 +#define SCANSTATE_EXPR_UDPLITE 47 +#define SCANSTATE_STMT_DUP 48 +#define SCANSTATE_STMT_FWD 49 +#define SCANSTATE_STMT_LOG 50 +#define SCANSTATE_STMT_NAT 51 +#define SCANSTATE_STMT_REJECT 52 +#define SCANSTATE_STMT_SYNPROXY 53 +#define SCANSTATE_STMT_TPROXY 54 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +/* %if-c-only */ +#include <unistd.h> +/* %endif */ +/* %if-c++-only */ +/* %endif */ +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* %if-c-only Reentrant structure and macros (non-C++). */ +/* %if-reentrant */ + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + YYSTYPE * yylval_r; + + YYLTYPE * yylloc_r; + + }; /* end struct yyguts_t */ + +/* %if-c-only */ + +static int yy_init_globals ( yyscan_t yyscanner ); + +/* %endif */ + +/* %if-reentrant */ + + /* This must go here because YYSTYPE and YYLTYPE are included + * from bison output in section 1.*/ + # define yylval yyg->yylval_r + + # define yylloc yyg->yylloc_r + +int yylex_init (yyscan_t* scanner); + +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); + +/* %endif */ + +/* %endif End reentrant structures and macros. */ + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int yylex_destroy ( yyscan_t yyscanner ); + +int yyget_debug ( yyscan_t yyscanner ); + +void yyset_debug ( int debug_flag , yyscan_t yyscanner ); + +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); + +void yyset_extra ( YY_EXTRA_TYPE user_defined , yyscan_t yyscanner ); + +FILE *yyget_in ( yyscan_t yyscanner ); + +void yyset_in ( FILE * _in_str , yyscan_t yyscanner ); + +FILE *yyget_out ( yyscan_t yyscanner ); + +void yyset_out ( FILE * _out_str , yyscan_t yyscanner ); + + int yyget_leng ( yyscan_t yyscanner ); + +char *yyget_text ( yyscan_t yyscanner ); + +int yyget_lineno ( yyscan_t yyscanner ); + +void yyset_lineno ( int _line_number , yyscan_t yyscanner ); + +int yyget_column ( yyscan_t yyscanner ); + +void yyset_column ( int _column_no , yyscan_t yyscanner ); + +/* %if-bison-bridge */ + +YYSTYPE * yyget_lval ( yyscan_t yyscanner ); + +void yyset_lval ( YYSTYPE * yylval_param , yyscan_t yyscanner ); + + YYLTYPE *yyget_lloc ( yyscan_t yyscanner ); + + void yyset_lloc ( YYLTYPE * yylloc_param , yyscan_t yyscanner ); + +/* %endif */ + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap ( yyscan_t yyscanner ); +#else +extern int yywrap ( yyscan_t yyscanner ); +#endif +#endif + +/* %not-for-header */ +#ifndef YY_NO_UNPUT + +#endif +/* %ok-for-header */ + +/* %endif */ + +#ifndef yytext_ptr +static void yy_flex_strncpy ( char *, const char *, int , yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen ( const char * , yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT +/* %if-c-only Standard (non-C++) definition */ +/* %not-for-header */ +#ifdef __cplusplus +static int yyinput ( yyscan_t yyscanner ); +#else +static int input ( yyscan_t yyscanner ); +#endif +/* %ok-for-header */ + +/* %endif */ +#endif + +/* %if-c-only */ + + static void yy_push_state ( int _new_state , yyscan_t yyscanner); + + static void yy_pop_state ( yyscan_t yyscanner ); + + static int yy_top_state ( yyscan_t yyscanner ); + +/* %endif */ + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#ifdef __ia64__ +/* On IA-64, the buffer size is 16k, not 8k */ +#define YY_READ_BUF_SIZE 16384 +#else +#define YY_READ_BUF_SIZE 8192 +#endif /* __ia64__ */ +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* %if-c-only Standard (non-C++) definition */ +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0) +/* %endif */ +/* %if-c++-only C++ definition */ +/* %endif */ +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ +/* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ +/* %if-c++-only C++ definition \ */\ +/* %endif */ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +/* %if-c-only */ +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +#endif + +/* %if-tables-serialization structures and prototypes */ +/* %not-for-header */ +/* %ok-for-header */ + +/* %not-for-header */ +/* %tables-yydmap generated elements */ +/* %endif */ +/* end tables serialization structures and prototypes */ + +/* %ok-for-header */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 +/* %if-c-only Standard (non-C++) definition */ + +extern int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner); + +#define YY_DECL int yylex \ + (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only C++ definition */ +/* %endif */ +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK /*LINTED*/break; +#endif + +/* %% [6.0] YY_RULE_SETUP definition goes here */ +#define YY_RULE_SETUP \ + if ( yyleng > 0 ) \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = \ + (yytext[yyleng - 1] == '\n'); \ + YY_USER_ACTION + +/* %not-for-header */ +/** The main scanner function which does all the work. + */ +YY_DECL +{ + yy_state_type yy_current_state; + char *yy_cp, *yy_bp; + int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yylval = yylval_param; + + yylloc = yylloc_param; + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) +/* %if-c-only */ + yyin = stdin; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + + if ( ! yyout ) +/* %if-c-only */ + yyout = stdout; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + + if ( ! YY_CURRENT_BUFFER ) { + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_load_buffer_state( yyscanner ); + } + + { +/* %% [7.0] user's declarations go here */ +#line 258 "scanner.l" + + +#line 4404 "scanner.c" + + while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ + { +/* %% [8.0] yymore()-related code goes here */ + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + +/* %% [9.0] code to set up and find next match goes here */ + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); +yy_match: + do + { + YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 2383 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 9866 ); + +yy_find_action: +/* %% [10.0] code to find the action number goes here */ + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +/* %% [11.0] code for yylineno update goes here */ + + if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] ) + { + int yyl; + for ( yyl = 0; yyl < yyleng; ++yyl ) + if ( yytext[yyl] == '\n' ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + } + +do_action: /* This label is used only to access EOF actions. */ + +/* %% [12.0] debug code goes here */ + if ( yy_flex_debug ) + { + if ( yy_act == 0 ) + fprintf( stderr, "--scanner backing up\n" ); + else if ( yy_act < 412 ) + fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n", + (long)yy_rule_linenum[yy_act], yytext ); + else if ( yy_act == 412 ) + fprintf( stderr, "--accepting default rule (\"%s\")\n", + yytext ); + else if ( yy_act == 413 ) + fprintf( stderr, "--(end of buffer or a NUL)\n" ); + else + fprintf( stderr, "--EOF (start condition %d)\n", YY_START ); + } + + switch ( yy_act ) + { /* beginning of action switch */ +/* %% [13.0] actions go here */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 260 "scanner.l" +{ return EQ; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 261 "scanner.l" +{ return EQ; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 262 "scanner.l" +{ return NEQ; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 263 "scanner.l" +{ return NEQ; } + YY_BREAK +case 5: +YY_RULE_SETUP +#line 264 "scanner.l" +{ return LTE; } + YY_BREAK +case 6: +YY_RULE_SETUP +#line 265 "scanner.l" +{ return LTE; } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 266 "scanner.l" +{ return LT; } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 267 "scanner.l" +{ return LT; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 268 "scanner.l" +{ return GTE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 269 "scanner.l" +{ return GTE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 270 "scanner.l" +{ return GT; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 271 "scanner.l" +{ return GT; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 272 "scanner.l" +{ return COMMA; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 273 "scanner.l" +{ return DOT; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 274 "scanner.l" +{ return COLON; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 275 "scanner.l" +{ return SEMICOLON; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 276 "scanner.l" +{ return '{'; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 277 "scanner.l" +{ return '}'; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 278 "scanner.l" +{ return '['; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 279 "scanner.l" +{ return ']'; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 280 "scanner.l" +{ return '('; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 281 "scanner.l" +{ return ')'; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 282 "scanner.l" +{ return LSHIFT; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 283 "scanner.l" +{ return LSHIFT; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 284 "scanner.l" +{ return RSHIFT; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 285 "scanner.l" +{ return RSHIFT; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 286 "scanner.l" +{ return CARET; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 287 "scanner.l" +{ return CARET; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 288 "scanner.l" +{ return AMPERSAND; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 289 "scanner.l" +{ return AMPERSAND; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 290 "scanner.l" +{ return '|'; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 291 "scanner.l" +{ return '|'; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 292 "scanner.l" +{ return NOT; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 293 "scanner.l" +{ return NOT; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 294 "scanner.l" +{ return SLASH; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 295 "scanner.l" +{ return DASH; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 296 "scanner.l" +{ return ASTERISK; } + YY_BREAK +case 38: +YY_RULE_SETUP +#line 297 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; } + YY_BREAK +case 39: +YY_RULE_SETUP +#line 298 "scanner.l" +{ return '$'; } + YY_BREAK +case 40: +YY_RULE_SETUP +#line 299 "scanner.l" +{ return '='; } + YY_BREAK +case 41: +YY_RULE_SETUP +#line 300 "scanner.l" +{ return VMAP; } + YY_BREAK +case 42: +YY_RULE_SETUP +#line 302 "scanner.l" +{ return PLUS; } + YY_BREAK +case 43: +YY_RULE_SETUP +#line 304 "scanner.l" +{ return INCLUDE; } + YY_BREAK +case 44: +YY_RULE_SETUP +#line 305 "scanner.l" +{ return DEFINE; } + YY_BREAK +case 45: +YY_RULE_SETUP +#line 306 "scanner.l" +{ return REDEFINE; } + YY_BREAK +case 46: +YY_RULE_SETUP +#line 307 "scanner.l" +{ return UNDEFINE; } + YY_BREAK +case 47: +YY_RULE_SETUP +#line 309 "scanner.l" +{ return DESCRIBE; } + YY_BREAK + +case 48: +YY_RULE_SETUP +#line 312 "scanner.l" +{ return CHAINS; } + YY_BREAK +case 49: +YY_RULE_SETUP +#line 313 "scanner.l" +{ return SETS; } + YY_BREAK +case 50: +YY_RULE_SETUP +#line 314 "scanner.l" +{ return TABLES; } + YY_BREAK + + +case 51: +YY_RULE_SETUP +#line 317 "scanner.l" +{ return RULES; } + YY_BREAK +case 52: +YY_RULE_SETUP +#line 318 "scanner.l" +{ return TRACE; } + YY_BREAK + +case 53: +YY_RULE_SETUP +#line 320 "scanner.l" +{ return HOOK; } + YY_BREAK +case 54: +YY_RULE_SETUP +#line 321 "scanner.l" +{ return DEVICE; } + YY_BREAK +case 55: +YY_RULE_SETUP +#line 322 "scanner.l" +{ return DEVICES; } + YY_BREAK +case 56: +YY_RULE_SETUP +#line 323 "scanner.l" +{ return TABLE; } + YY_BREAK +case 57: +YY_RULE_SETUP +#line 324 "scanner.l" +{ return CHAIN; } + YY_BREAK +case 58: +YY_RULE_SETUP +#line 325 "scanner.l" +{ return RULE; } + YY_BREAK +case 59: +YY_RULE_SETUP +#line 326 "scanner.l" +{ return SET; } + YY_BREAK +case 60: +YY_RULE_SETUP +#line 327 "scanner.l" +{ return ELEMENT; } + YY_BREAK +case 61: +YY_RULE_SETUP +#line 328 "scanner.l" +{ return MAP; } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 329 "scanner.l" +{ return FLOWTABLE; } + YY_BREAK +case 63: +YY_RULE_SETUP +#line 330 "scanner.l" +{ return HANDLE; } + YY_BREAK +case 64: +YY_RULE_SETUP +#line 331 "scanner.l" +{ return RULESET; } + YY_BREAK +case 65: +YY_RULE_SETUP +#line 333 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; } + YY_BREAK + +case 66: +YY_RULE_SETUP +#line 335 "scanner.l" +{ return TRANSPARENT; } + YY_BREAK +case 67: +YY_RULE_SETUP +#line 336 "scanner.l" +{ return WILDCARD; } + YY_BREAK +case 68: +YY_RULE_SETUP +#line 337 "scanner.l" +{ return CGROUPV2; } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 338 "scanner.l" +{ return LEVEL; } + YY_BREAK + +case 70: +YY_RULE_SETUP +#line 340 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; } + YY_BREAK +case 71: +YY_RULE_SETUP +#line 342 "scanner.l" +{ return ACCEPT; } + YY_BREAK +case 72: +YY_RULE_SETUP +#line 343 "scanner.l" +{ return DROP; } + YY_BREAK +case 73: +YY_RULE_SETUP +#line 344 "scanner.l" +{ return CONTINUE; } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 345 "scanner.l" +{ return JUMP; } + YY_BREAK +case 75: +YY_RULE_SETUP +#line 346 "scanner.l" +{ return GOTO; } + YY_BREAK +case 76: +YY_RULE_SETUP +#line 347 "scanner.l" +{ return RETURN; } + YY_BREAK +case 77: +YY_RULE_SETUP +#line 348 "scanner.l" +{ return TO; } /* XXX: SCANSTATE_IP is a workaround */ + YY_BREAK +case 78: +YY_RULE_SETUP +#line 350 "scanner.l" +{ return INET; } + YY_BREAK +case 79: +YY_RULE_SETUP +#line 351 "scanner.l" +{ return NETDEV; } + YY_BREAK +case 80: +YY_RULE_SETUP +#line 353 "scanner.l" +{ return ADD; } + YY_BREAK +case 81: +YY_RULE_SETUP +#line 354 "scanner.l" +{ return REPLACE; } + YY_BREAK +case 82: +YY_RULE_SETUP +#line 355 "scanner.l" +{ return UPDATE; } + YY_BREAK +case 83: +YY_RULE_SETUP +#line 356 "scanner.l" +{ return CREATE; } + YY_BREAK +case 84: +YY_RULE_SETUP +#line 357 "scanner.l" +{ return INSERT; } + YY_BREAK +case 85: +YY_RULE_SETUP +#line 358 "scanner.l" +{ return DELETE; } + YY_BREAK +case 86: +YY_RULE_SETUP +#line 359 "scanner.l" +{ return GET; } + YY_BREAK +case 87: +YY_RULE_SETUP +#line 360 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; } + YY_BREAK +case 88: +YY_RULE_SETUP +#line 361 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; } + YY_BREAK +case 89: +YY_RULE_SETUP +#line 362 "scanner.l" +{ return FLUSH; } + YY_BREAK +case 90: +YY_RULE_SETUP +#line 363 "scanner.l" +{ return RENAME; } + YY_BREAK +case 91: +YY_RULE_SETUP +#line 364 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; } + YY_BREAK +case 92: +YY_RULE_SETUP +#line 365 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; } + YY_BREAK +case 93: +YY_RULE_SETUP +#line 366 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; } + YY_BREAK +case 94: +YY_RULE_SETUP +#line 367 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CMD_DESTROY); return DESTROY; } + YY_BREAK +case 95: +YY_RULE_SETUP +#line 370 "scanner.l" +{ return POSITION; } + YY_BREAK +case 96: +YY_RULE_SETUP +#line 371 "scanner.l" +{ return INDEX; } + YY_BREAK +case 97: +YY_RULE_SETUP +#line 372 "scanner.l" +{ return COMMENT; } + YY_BREAK +case 98: +YY_RULE_SETUP +#line 374 "scanner.l" +{ return CONSTANT; } + YY_BREAK +case 99: +YY_RULE_SETUP +#line 375 "scanner.l" +{ return INTERVAL; } + YY_BREAK +case 100: +YY_RULE_SETUP +#line 376 "scanner.l" +{ return DYNAMIC; } + YY_BREAK +case 101: +YY_RULE_SETUP +#line 377 "scanner.l" +{ return AUTOMERGE; } + YY_BREAK +case 102: +YY_RULE_SETUP +#line 378 "scanner.l" +{ return TIMEOUT; } + YY_BREAK +case 103: +YY_RULE_SETUP +#line 379 "scanner.l" +{ return GC_INTERVAL; } + YY_BREAK +case 104: +YY_RULE_SETUP +#line 380 "scanner.l" +{ return ELEMENTS; } + YY_BREAK +case 105: +YY_RULE_SETUP +#line 381 "scanner.l" +{ return EXPIRES; } + YY_BREAK +case 106: +YY_RULE_SETUP +#line 383 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_POLICY); return POLICY; } + YY_BREAK +case 107: +YY_RULE_SETUP +#line 384 "scanner.l" +{ return SIZE; } + YY_BREAK + +case 108: +YY_RULE_SETUP +#line 386 "scanner.l" +{ return PERFORMANCE; } + YY_BREAK +case 109: +YY_RULE_SETUP +#line 387 "scanner.l" +{ return MEMORY; } + YY_BREAK + +case 110: +YY_RULE_SETUP +#line 390 "scanner.l" +{ return FLOW; } + YY_BREAK +case 111: +YY_RULE_SETUP +#line 391 "scanner.l" +{ return OFFLOAD; } + YY_BREAK +case 112: +YY_RULE_SETUP +#line 392 "scanner.l" +{ return METER; } + YY_BREAK + +case 113: +YY_RULE_SETUP +#line 395 "scanner.l" +{ return METERS; } + YY_BREAK +case 114: +YY_RULE_SETUP +#line 396 "scanner.l" +{ return FLOWTABLES; } + YY_BREAK +case 115: +YY_RULE_SETUP +#line 397 "scanner.l" +{ return LIMITS; } + YY_BREAK +case 116: +YY_RULE_SETUP +#line 398 "scanner.l" +{ return MAPS; } + YY_BREAK +case 117: +YY_RULE_SETUP +#line 399 "scanner.l" +{ return SECMARKS; } + YY_BREAK +case 118: +YY_RULE_SETUP +#line 400 "scanner.l" +{ return SYNPROXYS; } + YY_BREAK +case 119: +YY_RULE_SETUP +#line 401 "scanner.l" +{ return HOOKS; } + YY_BREAK + +case 120: +YY_RULE_SETUP +#line 404 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; } + YY_BREAK +case 121: +YY_RULE_SETUP +#line 405 "scanner.l" +{ return NAME; } + YY_BREAK +case 122: +YY_RULE_SETUP +#line 406 "scanner.l" +{ return PACKETS; } + YY_BREAK +case 123: +YY_RULE_SETUP +#line 407 "scanner.l" +{ return BYTES; } + YY_BREAK +case 124: +YY_RULE_SETUP +#line 409 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; } + YY_BREAK + +case 125: +YY_RULE_SETUP +#line 411 "scanner.l" +{ return NEVER; } + YY_BREAK + + +case 126: +YY_RULE_SETUP +#line 415 "scanner.l" +{ return COUNTERS; } + YY_BREAK +case 127: +YY_RULE_SETUP +#line 416 "scanner.l" +{ return QUOTAS; } + YY_BREAK +case 128: +YY_RULE_SETUP +#line 417 "scanner.l" +{ return RULES; } + YY_BREAK + +case 129: +YY_RULE_SETUP +#line 420 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; } + YY_BREAK +case 130: +YY_RULE_SETUP +#line 421 "scanner.l" +{ return PREFIX; } + YY_BREAK + +case 131: +YY_RULE_SETUP +#line 423 "scanner.l" +{ return SNAPLEN; } + YY_BREAK +case 132: +YY_RULE_SETUP +#line 424 "scanner.l" +{ return QUEUE_THRESHOLD; } + YY_BREAK +case 133: +YY_RULE_SETUP +#line 425 "scanner.l" +{ return LEVEL; } + YY_BREAK +case 134: +YY_RULE_SETUP +#line 426 "scanner.l" +{ return GROUP; } + YY_BREAK + +case 135: +YY_RULE_SETUP +#line 429 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;} + YY_BREAK + +case 136: +YY_RULE_SETUP +#line 431 "scanner.l" +{ return QUEUENUM;} + YY_BREAK +case 137: +YY_RULE_SETUP +#line 432 "scanner.l" +{ return BYPASS;} + YY_BREAK +case 138: +YY_RULE_SETUP +#line 433 "scanner.l" +{ return FANOUT;} + YY_BREAK + +case 139: +YY_RULE_SETUP +#line 435 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; } + YY_BREAK + +case 140: +YY_RULE_SETUP +#line 437 "scanner.l" +{ return RATE; } + YY_BREAK +case 141: +YY_RULE_SETUP +#line 438 "scanner.l" +{ return BURST; } + YY_BREAK +/* time_unit */ +case 142: +YY_RULE_SETUP +#line 441 "scanner.l" +{ return SECOND; } + YY_BREAK +case 143: +YY_RULE_SETUP +#line 442 "scanner.l" +{ return MINUTE; } + YY_BREAK +case 144: +YY_RULE_SETUP +#line 443 "scanner.l" +{ return WEEK; } + YY_BREAK + +case 145: +YY_RULE_SETUP +#line 445 "scanner.l" +{ return OVER; } + YY_BREAK +case 146: +YY_RULE_SETUP +#line 447 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; } + YY_BREAK + +case 147: +YY_RULE_SETUP +#line 449 "scanner.l" +{ return UNTIL; } + YY_BREAK + +case 148: +YY_RULE_SETUP +#line 452 "scanner.l" +{ return USED; } + YY_BREAK +case 149: +YY_RULE_SETUP +#line 454 "scanner.l" +{ return HOUR; } + YY_BREAK +case 150: +YY_RULE_SETUP +#line 455 "scanner.l" +{ return DAY; } + YY_BREAK +case 151: +YY_RULE_SETUP +#line 457 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; } + YY_BREAK + +case 152: +YY_RULE_SETUP +#line 459 "scanner.l" +{ return WITH; } + YY_BREAK +case 153: +YY_RULE_SETUP +#line 460 "scanner.l" +{ return ICMPX; } + YY_BREAK + +case 154: +YY_RULE_SETUP +#line 463 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; } + YY_BREAK +case 155: +YY_RULE_SETUP +#line 464 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; } + YY_BREAK +case 156: +YY_RULE_SETUP +#line 465 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; } + YY_BREAK +case 157: +YY_RULE_SETUP +#line 466 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; } + YY_BREAK +case 158: +YY_RULE_SETUP +#line 467 "scanner.l" +{ return RANDOM; } + YY_BREAK + +case 159: +YY_RULE_SETUP +#line 469 "scanner.l" +{ return FULLY_RANDOM; } + YY_BREAK +case 160: +YY_RULE_SETUP +#line 470 "scanner.l" +{ return PERSISTENT; } + YY_BREAK +case 161: +YY_RULE_SETUP +#line 471 "scanner.l" +{ return PORT; } + YY_BREAK + + +case 162: +YY_RULE_SETUP +#line 475 "scanner.l" +{ return LL_HDR; } + YY_BREAK +case 163: +YY_RULE_SETUP +#line 476 "scanner.l" +{ return NETWORK_HDR; } + YY_BREAK + +case 164: +YY_RULE_SETUP +#line 478 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; } + YY_BREAK +case 165: +YY_RULE_SETUP +#line 480 "scanner.l" +{ return BRIDGE; } + YY_BREAK +case 166: +YY_RULE_SETUP +#line 482 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_ETH); return ETHER; } + YY_BREAK + +case 167: +YY_RULE_SETUP +#line 484 "scanner.l" +{ return SADDR; } + YY_BREAK +case 168: +YY_RULE_SETUP +#line 485 "scanner.l" +{ return DADDR; } + YY_BREAK + +case 169: +YY_RULE_SETUP +#line 487 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; } + YY_BREAK +case 170: +YY_RULE_SETUP +#line 488 "scanner.l" +{ return TYPEOF; } + YY_BREAK +case 171: +YY_RULE_SETUP +#line 490 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; } + YY_BREAK +case 172: +YY_RULE_SETUP +#line 491 "scanner.l" +{ return ID; } + YY_BREAK + +case 173: +YY_RULE_SETUP +#line 493 "scanner.l" +{ return CFI; } + YY_BREAK +case 174: +YY_RULE_SETUP +#line 494 "scanner.l" +{ return DEI; } + YY_BREAK +case 175: +YY_RULE_SETUP +#line 495 "scanner.l" +{ return PCP; } + YY_BREAK + +case 176: +YY_RULE_SETUP +#line 497 "scanner.l" +{ yylval->string = xstrdup(yytext); return STRING; } + YY_BREAK +case 177: +YY_RULE_SETUP +#line 498 "scanner.l" +{ yylval->string = xstrdup(yytext); return STRING; } + YY_BREAK +case 178: +YY_RULE_SETUP +#line 500 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_ARP); return ARP; } + YY_BREAK + +case 179: +YY_RULE_SETUP +#line 502 "scanner.l" +{ return HTYPE; } + YY_BREAK +case 180: +YY_RULE_SETUP +#line 503 "scanner.l" +{ return PTYPE; } + YY_BREAK +case 181: +YY_RULE_SETUP +#line 504 "scanner.l" +{ return HLEN; } + YY_BREAK +case 182: +YY_RULE_SETUP +#line 505 "scanner.l" +{ return PLEN; } + YY_BREAK +case 183: +YY_RULE_SETUP +#line 506 "scanner.l" +{ return OPERATION; } + YY_BREAK + +case 184: +YY_RULE_SETUP +#line 509 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; } + YY_BREAK + +case 185: +YY_RULE_SETUP +#line 511 "scanner.l" +{ return HDRVERSION; } + YY_BREAK + + +case 186: +YY_RULE_SETUP +#line 514 "scanner.l" +{ return HDRLENGTH; } + YY_BREAK + + +case 187: +YY_RULE_SETUP +#line 517 "scanner.l" +{ return DSCP; } + YY_BREAK + +case 188: +YY_RULE_SETUP +#line 519 "scanner.l" +{ return ECN; } + YY_BREAK +case 189: +YY_RULE_SETUP +#line 520 "scanner.l" +{ return LENGTH; } + YY_BREAK + +case 190: +YY_RULE_SETUP +#line 522 "scanner.l" +{ return FRAG_OFF; } + YY_BREAK + + +case 191: +YY_RULE_SETUP +#line 525 "scanner.l" +{ return TTL; } + YY_BREAK + +case 192: +YY_RULE_SETUP +#line 527 "scanner.l" +{ return PROTOCOL; } + YY_BREAK + +case 193: +YY_RULE_SETUP +#line 529 "scanner.l" +{ return CHECKSUM; } + YY_BREAK + + +case 194: +YY_RULE_SETUP +#line 533 "scanner.l" +{ return LSRR; } + YY_BREAK +case 195: +YY_RULE_SETUP +#line 534 "scanner.l" +{ return RR; } + YY_BREAK +case 196: +YY_RULE_SETUP +#line 535 "scanner.l" +{ return SSRR; } + YY_BREAK +case 197: +YY_RULE_SETUP +#line 536 "scanner.l" +{ return RA; } + YY_BREAK +case 198: +YY_RULE_SETUP +#line 538 "scanner.l" +{ return PTR; } + YY_BREAK +case 199: +YY_RULE_SETUP +#line 539 "scanner.l" +{ return VALUE; } + YY_BREAK +case 200: +YY_RULE_SETUP +#line 541 "scanner.l" +{ return OPTION; } + YY_BREAK +case 201: +YY_RULE_SETUP +#line 542 "scanner.l" +{ return OPTIONS; } + YY_BREAK + + +/* tcp header fields */ +case 202: +YY_RULE_SETUP +#line 547 "scanner.l" +{ return ACKSEQ; } + YY_BREAK +case 203: +YY_RULE_SETUP +#line 548 "scanner.l" +{ return DOFF; } + YY_BREAK +case 204: +YY_RULE_SETUP +#line 549 "scanner.l" +{ return WINDOW; } + YY_BREAK +case 205: +YY_RULE_SETUP +#line 550 "scanner.l" +{ return URGPTR; } + YY_BREAK +/* tcp option types */ +case 206: +YY_RULE_SETUP +#line 553 "scanner.l" +{ return ECHO; } + YY_BREAK +case 207: +YY_RULE_SETUP +#line 554 "scanner.l" +{ return EOL; } + YY_BREAK +case 208: +YY_RULE_SETUP +#line 555 "scanner.l" +{ return MSS; } + YY_BREAK +case 209: +YY_RULE_SETUP +#line 556 "scanner.l" +{ return MSS; } + YY_BREAK +case 210: +YY_RULE_SETUP +#line 557 "scanner.l" +{ return NOP; } + YY_BREAK +case 211: +YY_RULE_SETUP +#line 558 "scanner.l" +{ return NOP; } + YY_BREAK +case 212: +YY_RULE_SETUP +#line 559 "scanner.l" +{ return SACK; } + YY_BREAK +case 213: +YY_RULE_SETUP +#line 560 "scanner.l" +{ return SACK0; } + YY_BREAK +case 214: +YY_RULE_SETUP +#line 561 "scanner.l" +{ return SACK1; } + YY_BREAK +case 215: +YY_RULE_SETUP +#line 562 "scanner.l" +{ return SACK2; } + YY_BREAK +case 216: +YY_RULE_SETUP +#line 563 "scanner.l" +{ return SACK3; } + YY_BREAK +case 217: +YY_RULE_SETUP +#line 564 "scanner.l" +{ return SACK_PERM; } + YY_BREAK +case 218: +YY_RULE_SETUP +#line 565 "scanner.l" +{ return SACK_PERM; } + YY_BREAK +case 219: +YY_RULE_SETUP +#line 566 "scanner.l" +{ return TIMESTAMP; } + YY_BREAK +case 220: +YY_RULE_SETUP +#line 567 "scanner.l" +{ return FASTOPEN; } + YY_BREAK +case 221: +YY_RULE_SETUP +#line 568 "scanner.l" +{ return MPTCP; } + YY_BREAK +case 222: +YY_RULE_SETUP +#line 569 "scanner.l" +{ return MD5SIG; } + YY_BREAK +/* tcp option fields */ +case 223: +YY_RULE_SETUP +#line 572 "scanner.l" +{ return LEFT; } + YY_BREAK +case 224: +YY_RULE_SETUP +#line 573 "scanner.l" +{ return RIGHT; } + YY_BREAK +case 225: +YY_RULE_SETUP +#line 574 "scanner.l" +{ return COUNT; } + YY_BREAK +case 226: +YY_RULE_SETUP +#line 575 "scanner.l" +{ return TSVAL; } + YY_BREAK +case 227: +YY_RULE_SETUP +#line 576 "scanner.l" +{ return TSECR; } + YY_BREAK +case 228: +YY_RULE_SETUP +#line 577 "scanner.l" +{ return SUBTYPE; } + YY_BREAK +case 229: +YY_RULE_SETUP +#line 579 "scanner.l" +{ return OPTIONS; } + YY_BREAK +case 230: +YY_RULE_SETUP +#line 580 "scanner.l" +{ return OPTION; } + YY_BREAK + +case 231: +YY_RULE_SETUP +#line 582 "scanner.l" +{ return TIME; } + YY_BREAK +case 232: +YY_RULE_SETUP +#line 584 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; } + YY_BREAK +case 233: +YY_RULE_SETUP +#line 585 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; } + YY_BREAK + +case 234: +YY_RULE_SETUP +#line 587 "scanner.l" +{ return GATEWAY; } + YY_BREAK +case 235: +YY_RULE_SETUP +#line 588 "scanner.l" +{ return CODE; } + YY_BREAK +case 236: +YY_RULE_SETUP +#line 589 "scanner.l" +{ return PPTR; } + YY_BREAK +case 237: +YY_RULE_SETUP +#line 590 "scanner.l" +{ return MAXDELAY; } + YY_BREAK +case 238: +YY_RULE_SETUP +#line 591 "scanner.l" +{ return MTU; } + YY_BREAK +case 239: +YY_RULE_SETUP +#line 592 "scanner.l" +{ return TADDR; } + YY_BREAK +case 240: +YY_RULE_SETUP +#line 593 "scanner.l" +{ return DADDR; } + YY_BREAK + + +case 241: +YY_RULE_SETUP +#line 596 "scanner.l" +{ return SEQUENCE; } + YY_BREAK + +case 242: +YY_RULE_SETUP +#line 599 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; } + YY_BREAK + +case 243: +YY_RULE_SETUP +#line 601 "scanner.l" +{ return MRT; } + YY_BREAK +case 244: +YY_RULE_SETUP +#line 602 "scanner.l" +{ return GROUP; } + YY_BREAK + +case 245: +YY_RULE_SETUP +#line 605 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; } + YY_BREAK +case 246: +YY_RULE_SETUP +#line 606 "scanner.l" +{ return PRIORITY; } + YY_BREAK + +case 247: +YY_RULE_SETUP +#line 608 "scanner.l" +{ return FLOWLABEL; } + YY_BREAK +case 248: +YY_RULE_SETUP +#line 609 "scanner.l" +{ return HOPLIMIT; } + YY_BREAK + + +case 249: +YY_RULE_SETUP +#line 612 "scanner.l" +{ return NEXTHDR; } + YY_BREAK + +case 250: +YY_RULE_SETUP +#line 615 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; } + YY_BREAK + +case 251: +YY_RULE_SETUP +#line 617 "scanner.l" +{ return RESERVED; } + YY_BREAK + +case 252: +YY_RULE_SETUP +#line 619 "scanner.l" +{ return SPI; } + YY_BREAK +case 253: +YY_RULE_SETUP +#line 621 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; } + YY_BREAK +case 254: +YY_RULE_SETUP +#line 623 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; } + YY_BREAK + +case 255: +YY_RULE_SETUP +#line 625 "scanner.l" +{ return CPI; } + YY_BREAK + +case 256: +YY_RULE_SETUP +#line 627 "scanner.l" +{ return FLAGS; } + YY_BREAK +case 257: +YY_RULE_SETUP +#line 629 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; } + YY_BREAK +case 258: +YY_RULE_SETUP +#line 630 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; } + YY_BREAK + +case 259: +YY_RULE_SETUP +#line 632 "scanner.l" +{ return CSUMCOV; } + YY_BREAK + + +case 260: +YY_RULE_SETUP +#line 635 "scanner.l" +{ return SPORT; } + YY_BREAK + + +case 261: +YY_RULE_SETUP +#line 638 "scanner.l" +{ return DPORT; } + YY_BREAK + + +case 262: +YY_RULE_SETUP +#line 641 "scanner.l" +{ return OPTION; } + YY_BREAK + +case 263: +YY_RULE_SETUP +#line 644 "scanner.l" +{ return VXLAN; } + YY_BREAK +case 264: +YY_RULE_SETUP +#line 645 "scanner.l" +{ return VNI; } + YY_BREAK +case 265: +YY_RULE_SETUP +#line 647 "scanner.l" +{ return GENEVE; } + YY_BREAK +case 266: +YY_RULE_SETUP +#line 649 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; } + YY_BREAK +case 267: +YY_RULE_SETUP +#line 650 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; } + YY_BREAK +case 268: +YY_RULE_SETUP +#line 652 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; } + YY_BREAK +case 269: +YY_RULE_SETUP +#line 654 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; } + YY_BREAK +case 270: +YY_RULE_SETUP +#line 656 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; } + YY_BREAK + +case 271: +YY_RULE_SETUP +#line 659 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; } + YY_BREAK +case 272: +YY_RULE_SETUP +#line 660 "scanner.l" +{ return VTAG; } + YY_BREAK + + +case 273: +YY_RULE_SETUP +#line 664 "scanner.l" +{ return DATA; } + YY_BREAK +case 274: +YY_RULE_SETUP +#line 665 "scanner.l" +{ return INIT; } + YY_BREAK +case 275: +YY_RULE_SETUP +#line 666 "scanner.l" +{ return INIT_ACK; } + YY_BREAK +case 276: +YY_RULE_SETUP +#line 667 "scanner.l" +{ return HEARTBEAT; } + YY_BREAK +case 277: +YY_RULE_SETUP +#line 668 "scanner.l" +{ return HEARTBEAT_ACK; } + YY_BREAK +case 278: +YY_RULE_SETUP +#line 669 "scanner.l" +{ return ABORT; } + YY_BREAK +case 279: +YY_RULE_SETUP +#line 670 "scanner.l" +{ return SHUTDOWN; } + YY_BREAK +case 280: +YY_RULE_SETUP +#line 671 "scanner.l" +{ return SHUTDOWN_ACK; } + YY_BREAK +case 281: +YY_RULE_SETUP +#line 672 "scanner.l" +{ return ERROR; } + YY_BREAK +case 282: +YY_RULE_SETUP +#line 673 "scanner.l" +{ return COOKIE_ECHO; } + YY_BREAK +case 283: +YY_RULE_SETUP +#line 674 "scanner.l" +{ return COOKIE_ACK; } + YY_BREAK +case 284: +YY_RULE_SETUP +#line 675 "scanner.l" +{ return ECNE; } + YY_BREAK +case 285: +YY_RULE_SETUP +#line 676 "scanner.l" +{ return CWR; } + YY_BREAK +case 286: +YY_RULE_SETUP +#line 677 "scanner.l" +{ return SHUTDOWN_COMPLETE; } + YY_BREAK +case 287: +YY_RULE_SETUP +#line 678 "scanner.l" +{ return ASCONF_ACK; } + YY_BREAK +case 288: +YY_RULE_SETUP +#line 679 "scanner.l" +{ return FORWARD_TSN; } + YY_BREAK +case 289: +YY_RULE_SETUP +#line 680 "scanner.l" +{ return ASCONF; } + YY_BREAK +case 290: +YY_RULE_SETUP +#line 682 "scanner.l" +{ return TSN; } + YY_BREAK +case 291: +YY_RULE_SETUP +#line 683 "scanner.l" +{ return SACK; } + YY_BREAK +case 292: +YY_RULE_SETUP +#line 684 "scanner.l" +{ return STREAM; } + YY_BREAK +case 293: +YY_RULE_SETUP +#line 685 "scanner.l" +{ return SSN; } + YY_BREAK +case 294: +YY_RULE_SETUP +#line 686 "scanner.l" +{ return PPID; } + YY_BREAK +case 295: +YY_RULE_SETUP +#line 687 "scanner.l" +{ return INIT_TAG; } + YY_BREAK +case 296: +YY_RULE_SETUP +#line 688 "scanner.l" +{ return A_RWND; } + YY_BREAK +case 297: +YY_RULE_SETUP +#line 689 "scanner.l" +{ return NUM_OSTREAMS; } + YY_BREAK +case 298: +YY_RULE_SETUP +#line 690 "scanner.l" +{ return NUM_ISTREAMS; } + YY_BREAK +case 299: +YY_RULE_SETUP +#line 691 "scanner.l" +{ return INIT_TSN; } + YY_BREAK +case 300: +YY_RULE_SETUP +#line 692 "scanner.l" +{ return CUM_TSN_ACK; } + YY_BREAK +case 301: +YY_RULE_SETUP +#line 693 "scanner.l" +{ return NUM_GACK_BLOCKS; } + YY_BREAK +case 302: +YY_RULE_SETUP +#line 694 "scanner.l" +{ return NUM_DUP_TSNS; } + YY_BREAK +case 303: +YY_RULE_SETUP +#line 695 "scanner.l" +{ return LOWEST_TSN; } + YY_BREAK +case 304: +YY_RULE_SETUP +#line 696 "scanner.l" +{ return SEQNO; } + YY_BREAK +case 305: +YY_RULE_SETUP +#line 697 "scanner.l" +{ return NEW_CUM_TSN; } + YY_BREAK + +case 306: +YY_RULE_SETUP +#line 700 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; } + YY_BREAK +case 307: +YY_RULE_SETUP +#line 701 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; } + YY_BREAK +case 308: +YY_RULE_SETUP +#line 702 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; } + YY_BREAK +case 309: +YY_RULE_SETUP +#line 703 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; } + YY_BREAK +case 310: +YY_RULE_SETUP +#line 704 "scanner.l" +{ return ADDR; } + YY_BREAK +case 311: +YY_RULE_SETUP +#line 706 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; } + YY_BREAK +case 312: +YY_RULE_SETUP +#line 708 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; } + YY_BREAK + +case 313: +YY_RULE_SETUP +#line 710 "scanner.l" +{ return RESERVED2; } + YY_BREAK +case 314: +YY_RULE_SETUP +#line 711 "scanner.l" +{ return MORE_FRAGMENTS; } + YY_BREAK + +case 315: +YY_RULE_SETUP +#line 714 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; } + YY_BREAK +case 316: +YY_RULE_SETUP +#line 716 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; } + YY_BREAK +case 317: +YY_RULE_SETUP +#line 718 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_META); return META; } + YY_BREAK +case 318: +YY_RULE_SETUP +#line 719 "scanner.l" +{ return MARK; } + YY_BREAK +case 319: +YY_RULE_SETUP +#line 720 "scanner.l" +{ return IIF; } + YY_BREAK +case 320: +YY_RULE_SETUP +#line 721 "scanner.l" +{ return IIFNAME; } + YY_BREAK +case 321: +YY_RULE_SETUP +#line 722 "scanner.l" +{ return IIFTYPE; } + YY_BREAK +case 322: +YY_RULE_SETUP +#line 723 "scanner.l" +{ return OIF; } + YY_BREAK +case 323: +YY_RULE_SETUP +#line 724 "scanner.l" +{ return OIFNAME; } + YY_BREAK +case 324: +YY_RULE_SETUP +#line 725 "scanner.l" +{ return OIFTYPE; } + YY_BREAK +case 325: +YY_RULE_SETUP +#line 726 "scanner.l" +{ return SKUID; } + YY_BREAK +case 326: +YY_RULE_SETUP +#line 727 "scanner.l" +{ return SKGID; } + YY_BREAK +case 327: +YY_RULE_SETUP +#line 728 "scanner.l" +{ return NFTRACE; } + YY_BREAK +case 328: +YY_RULE_SETUP +#line 729 "scanner.l" +{ return RTCLASSID; } + YY_BREAK +case 329: +YY_RULE_SETUP +#line 730 "scanner.l" +{ return IBRIPORT; } + YY_BREAK +case 330: +YY_RULE_SETUP +#line 731 "scanner.l" +{ return IBRIDGENAME; } + YY_BREAK +case 331: +YY_RULE_SETUP +#line 732 "scanner.l" +{ return OBRIPORT; } + YY_BREAK +case 332: +YY_RULE_SETUP +#line 733 "scanner.l" +{ return OBRIDGENAME; } + YY_BREAK +case 333: +YY_RULE_SETUP +#line 734 "scanner.l" +{ return PKTTYPE; } + YY_BREAK +case 334: +YY_RULE_SETUP +#line 735 "scanner.l" +{ return CPU; } + YY_BREAK +case 335: +YY_RULE_SETUP +#line 736 "scanner.l" +{ return IIFGROUP; } + YY_BREAK +case 336: +YY_RULE_SETUP +#line 737 "scanner.l" +{ return OIFGROUP; } + YY_BREAK +case 337: +YY_RULE_SETUP +#line 738 "scanner.l" +{ return CGROUP; } + YY_BREAK + +case 338: +YY_RULE_SETUP +#line 741 "scanner.l" +{ return NEXTHOP; } + YY_BREAK +case 339: +YY_RULE_SETUP +#line 742 "scanner.l" +{ return SEG_LEFT; } + YY_BREAK +case 340: +YY_RULE_SETUP +#line 743 "scanner.l" +{ return MTU; } + YY_BREAK +case 341: +YY_RULE_SETUP +#line 744 "scanner.l" +{ return LAST_ENT; } + YY_BREAK +case 342: +YY_RULE_SETUP +#line 745 "scanner.l" +{ return TAG; } + YY_BREAK +case 343: +YY_RULE_SETUP +#line 746 "scanner.l" +{ return SID; } + YY_BREAK + + +case 344: +YY_RULE_SETUP +#line 749 "scanner.l" +{ return CLASSID; } + YY_BREAK + +case 345: +YY_RULE_SETUP +#line 752 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; } + YY_BREAK + +case 346: +YY_RULE_SETUP +#line 754 "scanner.l" +{ return AVGPKT; } + YY_BREAK +case 347: +YY_RULE_SETUP +#line 755 "scanner.l" +{ return L3PROTOCOL; } + YY_BREAK +case 348: +YY_RULE_SETUP +#line 756 "scanner.l" +{ return PROTO_SRC; } + YY_BREAK +case 349: +YY_RULE_SETUP +#line 757 "scanner.l" +{ return PROTO_DST; } + YY_BREAK +case 350: +YY_RULE_SETUP +#line 758 "scanner.l" +{ return ZONE; } + YY_BREAK +case 351: +YY_RULE_SETUP +#line 759 "scanner.l" +{ return ORIGINAL; } + YY_BREAK +case 352: +YY_RULE_SETUP +#line 760 "scanner.l" +{ return REPLY; } + YY_BREAK +case 353: +YY_RULE_SETUP +#line 761 "scanner.l" +{ return DIRECTION; } + YY_BREAK +case 354: +YY_RULE_SETUP +#line 762 "scanner.l" +{ return EVENT; } + YY_BREAK +case 355: +YY_RULE_SETUP +#line 763 "scanner.l" +{ return EXPECTATION; } + YY_BREAK +case 356: +YY_RULE_SETUP +#line 764 "scanner.l" +{ return EXPIRATION; } + YY_BREAK +case 357: +YY_RULE_SETUP +#line 765 "scanner.l" +{ return HELPER; } + YY_BREAK +case 358: +YY_RULE_SETUP +#line 766 "scanner.l" +{ return HELPERS; } + YY_BREAK +case 359: +YY_RULE_SETUP +#line 767 "scanner.l" +{ return LABEL; } + YY_BREAK +case 360: +YY_RULE_SETUP +#line 768 "scanner.l" +{ return STATE; } + YY_BREAK +case 361: +YY_RULE_SETUP +#line 769 "scanner.l" +{ return STATUS; } + YY_BREAK +case 362: +YY_RULE_SETUP +#line 770 "scanner.l" +{ return COUNT; } + YY_BREAK + +case 363: +YY_RULE_SETUP +#line 773 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; } + YY_BREAK + +case 364: +YY_RULE_SETUP +#line 775 "scanner.l" +{ return INC; } + YY_BREAK + +case 365: +YY_RULE_SETUP +#line 778 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return JHASH; } + YY_BREAK +case 366: +YY_RULE_SETUP +#line 779 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return SYMHASH; } + YY_BREAK + +case 367: +YY_RULE_SETUP +#line 782 "scanner.l" +{ return SEED; } + YY_BREAK + + +case 368: +YY_RULE_SETUP +#line 785 "scanner.l" +{ return MOD; } + YY_BREAK +case 369: +YY_RULE_SETUP +#line 786 "scanner.l" +{ return OFFSET; } + YY_BREAK + +case 370: +YY_RULE_SETUP +#line 788 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; } + YY_BREAK +case 371: +YY_RULE_SETUP +#line 789 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; } + YY_BREAK +case 372: +YY_RULE_SETUP +#line 791 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; } + YY_BREAK +case 373: +YY_RULE_SETUP +#line 793 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; } + YY_BREAK +case 374: +YY_RULE_SETUP +#line 795 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; } + YY_BREAK + +case 375: +YY_RULE_SETUP +#line 797 "scanner.l" +{ return WSCALE; } + YY_BREAK +case 376: +YY_RULE_SETUP +#line 798 "scanner.l" +{ return MSS; } + YY_BREAK +case 377: +YY_RULE_SETUP +#line 799 "scanner.l" +{ return MSS; } + YY_BREAK +case 378: +YY_RULE_SETUP +#line 800 "scanner.l" +{ return TIMESTAMP; } + YY_BREAK +case 379: +YY_RULE_SETUP +#line 801 "scanner.l" +{ return SACK_PERM; } + YY_BREAK +case 380: +YY_RULE_SETUP +#line 802 "scanner.l" +{ return SACK_PERM; } + YY_BREAK + +case 381: +YY_RULE_SETUP +#line 805 "scanner.l" +{ return NOTRACK; } + YY_BREAK +case 382: +YY_RULE_SETUP +#line 807 "scanner.l" +{ return ALL; } + YY_BREAK + +case 383: +YY_RULE_SETUP +#line 810 "scanner.l" +{ return XML; } + YY_BREAK +case 384: +YY_RULE_SETUP +#line 811 "scanner.l" +{ return JSON; } + YY_BREAK +case 385: +YY_RULE_SETUP +#line 812 "scanner.l" +{ return VM; } + YY_BREAK + +case 386: +YY_RULE_SETUP +#line 815 "scanner.l" +{ return EXISTS; } + YY_BREAK +case 387: +YY_RULE_SETUP +#line 816 "scanner.l" +{ return MISSING; } + YY_BREAK +case 388: +YY_RULE_SETUP +#line 818 "scanner.l" +{ return EXTHDR; } + YY_BREAK +case 389: +YY_RULE_SETUP +#line 820 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_IPSEC); return IPSEC; } + YY_BREAK + +case 390: +YY_RULE_SETUP +#line 822 "scanner.l" +{ return REQID; } + YY_BREAK +case 391: +YY_RULE_SETUP +#line 823 "scanner.l" +{ return SPNUM; } + YY_BREAK +case 392: +YY_RULE_SETUP +#line 825 "scanner.l" +{ return IN; } + YY_BREAK +case 393: +YY_RULE_SETUP +#line 826 "scanner.l" +{ return OUT; } + YY_BREAK + +case 394: +YY_RULE_SETUP +#line 829 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; } + YY_BREAK +case 395: +YY_RULE_SETUP +#line 831 "scanner.l" +{ scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; } + YY_BREAK +case 396: +YY_RULE_SETUP +#line 833 "scanner.l" +{ + yylval->string = xstrdup(yytext); + return STRING; + } + YY_BREAK +case 397: +YY_RULE_SETUP +#line 838 "scanner.l" +{ + yytext[yyleng - 1] = '\0'; + yylval->string = xstrdup(yytext + 1); + return STRING; + } + YY_BREAK +case 398: +YY_RULE_SETUP +#line 844 "scanner.l" +{ + yylval->string = xstrdup(yytext); + return STRING; + } + YY_BREAK +case 399: +YY_RULE_SETUP +#line 849 "scanner.l" +{ + errno = 0; + yylval->val = strtoull(yytext, NULL, 16); + if (errno != 0) { + yylval->string = xstrdup(yytext); + return STRING; + } + return NUM; + } + YY_BREAK +case 400: +YY_RULE_SETUP +#line 859 "scanner.l" +{ + int base = yytext[0] == '0' ? 8 : 10; + char *end; + + errno = 0; + yylval->val = strtoull(yytext, &end, base); + if (errno != 0 || *end) { + yylval->string = xstrdup(yytext); + return STRING; + } + return NUM; + } + YY_BREAK +case 401: +/* rule 401 can match eol */ +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +YY_LINENO_REWIND_TO(yy_cp - 1); +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 872 "scanner.l" +{ + yylval->string = xstrdup(yytext); + return STRING; + } + YY_BREAK +case 402: +/* rule 402 can match eol */ +YY_RULE_SETUP +#line 877 "scanner.l" +{ + yytext[yyleng - 1] = '\0'; + yylval->string = xstrdup(yytext + 1); + return QUOTED_STRING; + } + YY_BREAK +case 403: +YY_RULE_SETUP +#line 883 "scanner.l" +{ + yylval->string = xstrdup(yytext); + return ASTERISK_STRING; + } + YY_BREAK +case 404: +YY_RULE_SETUP +#line 888 "scanner.l" +{ + yylval->string = xstrdup(yytext); + return STRING; + } + YY_BREAK +case 405: +/* rule 405 can match eol */ +YY_RULE_SETUP +#line 893 "scanner.l" +{ + reset_pos(yyget_extra(yyscanner), yylloc); + } + YY_BREAK +case 406: +/* rule 406 can match eol */ +YY_RULE_SETUP +#line 897 "scanner.l" +{ + reset_pos(yyget_extra(yyscanner), yylloc); + return NEWLINE; + } + YY_BREAK +case 407: +YY_RULE_SETUP +#line 902 "scanner.l" + + YY_BREAK +case 408: +YY_RULE_SETUP +#line 903 "scanner.l" + + YY_BREAK +case 409: +/* rule 409 can match eol */ +YY_RULE_SETUP +#line 904 "scanner.l" +{ + reset_pos(yyget_extra(yyscanner), yylloc); + } + YY_BREAK +case 410: +*yy_cp = yyg->yy_hold_char; /* undo effects of setting up yytext */ +yyg->yy_c_buf_p = yy_cp -= 1; +YY_DO_BEFORE_ACTION; /* set up yytext again */ +YY_RULE_SETUP +#line 907 "scanner.l" + + YY_BREAK +case YY_STATE_EOF(INITIAL): +case YY_STATE_EOF(SCANSTATE_ARP): +case YY_STATE_EOF(SCANSTATE_AT): +case YY_STATE_EOF(SCANSTATE_CT): +case YY_STATE_EOF(SCANSTATE_COUNTER): +case YY_STATE_EOF(SCANSTATE_ETH): +case YY_STATE_EOF(SCANSTATE_GRE): +case YY_STATE_EOF(SCANSTATE_ICMP): +case YY_STATE_EOF(SCANSTATE_IGMP): +case YY_STATE_EOF(SCANSTATE_IP): +case YY_STATE_EOF(SCANSTATE_IP6): +case YY_STATE_EOF(SCANSTATE_LAST): +case YY_STATE_EOF(SCANSTATE_LIMIT): +case YY_STATE_EOF(SCANSTATE_META): +case YY_STATE_EOF(SCANSTATE_POLICY): +case YY_STATE_EOF(SCANSTATE_QUOTA): +case YY_STATE_EOF(SCANSTATE_SCTP): +case YY_STATE_EOF(SCANSTATE_SECMARK): +case YY_STATE_EOF(SCANSTATE_TCP): +case YY_STATE_EOF(SCANSTATE_TYPE): +case YY_STATE_EOF(SCANSTATE_VLAN): +case YY_STATE_EOF(SCANSTATE_XT): +case YY_STATE_EOF(SCANSTATE_CMD_DESTROY): +case YY_STATE_EOF(SCANSTATE_CMD_EXPORT): +case YY_STATE_EOF(SCANSTATE_CMD_IMPORT): +case YY_STATE_EOF(SCANSTATE_CMD_LIST): +case YY_STATE_EOF(SCANSTATE_CMD_MONITOR): +case YY_STATE_EOF(SCANSTATE_CMD_RESET): +case YY_STATE_EOF(SCANSTATE_EXPR_AH): +case YY_STATE_EOF(SCANSTATE_EXPR_COMP): +case YY_STATE_EOF(SCANSTATE_EXPR_DCCP): +case YY_STATE_EOF(SCANSTATE_EXPR_DST): +case YY_STATE_EOF(SCANSTATE_EXPR_ESP): +case YY_STATE_EOF(SCANSTATE_EXPR_FIB): +case YY_STATE_EOF(SCANSTATE_EXPR_FRAG): +case YY_STATE_EOF(SCANSTATE_EXPR_HASH): +case YY_STATE_EOF(SCANSTATE_EXPR_HBH): +case YY_STATE_EOF(SCANSTATE_EXPR_IPSEC): +case YY_STATE_EOF(SCANSTATE_EXPR_MH): +case YY_STATE_EOF(SCANSTATE_EXPR_NUMGEN): +case YY_STATE_EOF(SCANSTATE_EXPR_OSF): +case YY_STATE_EOF(SCANSTATE_EXPR_QUEUE): +case YY_STATE_EOF(SCANSTATE_EXPR_RT): +case YY_STATE_EOF(SCANSTATE_EXPR_SCTP_CHUNK): +case YY_STATE_EOF(SCANSTATE_EXPR_SOCKET): +case YY_STATE_EOF(SCANSTATE_EXPR_TH): +case YY_STATE_EOF(SCANSTATE_EXPR_UDP): +case YY_STATE_EOF(SCANSTATE_EXPR_UDPLITE): +case YY_STATE_EOF(SCANSTATE_STMT_DUP): +case YY_STATE_EOF(SCANSTATE_STMT_FWD): +case YY_STATE_EOF(SCANSTATE_STMT_LOG): +case YY_STATE_EOF(SCANSTATE_STMT_NAT): +case YY_STATE_EOF(SCANSTATE_STMT_REJECT): +case YY_STATE_EOF(SCANSTATE_STMT_SYNPROXY): +case YY_STATE_EOF(SCANSTATE_STMT_TPROXY): +#line 909 "scanner.l" +{ + update_pos(yyget_extra(yyscanner), yylloc, 1); + scanner_pop_buffer(yyscanner); + if (YY_CURRENT_BUFFER == NULL) + return TOKEN_EOF; + } + YY_BREAK +case 411: +YY_RULE_SETUP +#line 916 "scanner.l" +{ return JUNK; } + YY_BREAK +case 412: +YY_RULE_SETUP +#line 918 "scanner.l" +YY_FATAL_ERROR( "flex scanner jammed" ); + YY_BREAK +#line 6781 "scanner.c" + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; +/* %if-c-only */ + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { +/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */ + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( yywrap( yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of user's declarations */ +} /* end of yylex */ +/* %ok-for-header */ + +/* %if-c++-only */ +/* %not-for-header */ +/* %ok-for-header */ + +/* %endif */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +/* %if-c-only */ +static int yy_get_next_buffer (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + char *source = yyg->yytext_ptr; + int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr - 1); + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yyrealloc( (void *) b->yy_ch_buf, + (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = NULL; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin , yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc( + (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + /* "- 2" to take care of EOB's */ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +/* %if-c-only */ +/* %not-for-header */ + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + yy_state_type yy_current_state; + char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +/* %% [15.0] code to get the start state into yy_current_state goes here */ + yy_current_state = yyg->yy_start; + yy_current_state += YY_AT_BOL(); + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { +/* %% [16.0] code to find the next state goes here */ + YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 2383 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ +/* %if-c-only */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ +/* %% [17.0] code to find the next state, and perhaps do backing up, goes here */ + char *yy_cp = yyg->yy_c_buf_p; + + YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 2383 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_current_state == 2382); + + (void)yyg; + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_UNPUT +/* %if-c-only */ + +/* %endif */ +#endif + +/* %if-c-only */ +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr); + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin , yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap( yyscanner ) ) + return 0; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + +/* %% [19.0] update BOL and yylineno */ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = (c == '\n'); + if ( YY_CURRENT_BUFFER_LVALUE->yy_at_bol ) + + do{ yylineno++; + yycolumn=0; + }while(0) +; + + return c; +} +/* %if-c-only */ +#endif /* ifndef YY_NO_INPUT */ +/* %endif */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ +/* %if-c-only */ + void yyrestart (FILE * input_file , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + yyensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + yy_create_buffer( yyin, YY_BUF_SIZE , yyscanner); + } + + yy_init_buffer( YY_CURRENT_BUFFER, input_file , yyscanner); + yy_load_buffer_state( yyscanner ); +} + +/* %if-c++-only */ +/* %endif */ + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ +/* %if-c-only */ + void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * yypop_buffer_state(); + * yypush_buffer_state(new_buffer); + */ + yyensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + yy_load_buffer_state( yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/* %if-c-only */ +static void yy_load_buffer_state (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; +/* %if-c-only */ + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ +/* %if-c-only */ + YY_BUFFER_STATE yy_create_buffer (FILE * file, int size , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) , yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file , yyscanner); + + return b; +} + +/* %if-c++-only */ +/* %endif */ + +/** Destroy the buffer. + * @param b a buffer created with yy_create_buffer() + * @param yyscanner The scanner object. + */ +/* %if-c-only */ + void yy_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yyfree( (void *) b->yy_ch_buf , yyscanner ); + + yyfree( (void *) b , yyscanner ); +} + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a yyrestart() or at EOF. + */ +/* %if-c-only */ + static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_flush_buffer( b , yyscanner); + +/* %if-c-only */ + b->yy_input_file = file; +/* %endif */ +/* %if-c++-only */ +/* %endif */ + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then yy_init_buffer was _probably_ + * called from yyrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + +/* %if-c-only */ + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + +/* %endif */ +/* %if-c++-only */ +/* %endif */ + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ +/* %if-c-only */ + void yy_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + yy_load_buffer_state( yyscanner ); +} + +/* %if-c-or-c++ */ +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +/* %if-c-only */ +void yypush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + yyensure_buffer_stack(yyscanner); + + /* This block is copied from yy_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from yy_switch_to_buffer. */ + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} +/* %endif */ + +/* %if-c-or-c++ */ +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +/* %if-c-only */ +void yypop_buffer_state (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + yy_delete_buffer(YY_CURRENT_BUFFER , yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + yy_load_buffer_state( yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} +/* %endif */ + +/* %if-c-or-c++ */ +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +/* %if-c-only */ +static void yyensure_buffer_stack (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + yy_size_t num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + yy_size_t grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)yyrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return NULL; + + b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) , yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = NULL; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b , yyscanner ); + + return b; +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan a string. The next call to yylex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * yy_scan_bytes() instead. + */ +YY_BUFFER_STATE yy_scan_string (const char * yystr , yyscan_t yyscanner) +{ + + return yy_scan_bytes( yystr, (int) strlen(yystr) , yyscanner); +} +/* %endif */ + +/* %if-c-only */ +/** Setup the input buffer state to scan the given bytes. The next call to yylex() will + * scan from a @e copy of @a bytes. + * @param yybytes the byte buffer to scan + * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = (yy_size_t) (_yybytes_len + 2); + buf = (char *) yyalloc( n , yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n , yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} +/* %endif */ + +/* %if-c-only */ + static void yy_push_state (int _new_state , yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr >= yyg->yy_start_stack_depth ) + { + yy_size_t new_size; + + yyg->yy_start_stack_depth += YY_START_STACK_INCR; + new_size = (yy_size_t) yyg->yy_start_stack_depth * sizeof( int ); + + if ( ! yyg->yy_start_stack ) + yyg->yy_start_stack = (int *) yyalloc( new_size , yyscanner ); + + else + yyg->yy_start_stack = (int *) yyrealloc( + (void *) yyg->yy_start_stack, new_size , yyscanner ); + + if ( ! yyg->yy_start_stack ) + YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); + } + + yyg->yy_start_stack[yyg->yy_start_stack_ptr++] = YY_START; + + BEGIN(_new_state); +} + +/* %if-c-only */ + static void yy_pop_state (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( --yyg->yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yyg->yy_start_stack[yyg->yy_start_stack_ptr]); +} + +/* %if-c-only */ + static int yy_top_state (yyscan_t yyscanner) +/* %endif */ +/* %if-c++-only */ +/* %endif */ +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyg->yy_start_stack[yyg->yy_start_stack_ptr - 1]; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +/* %if-c-only */ +static void yynoreturn yy_fatal_error (const char* msg , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} +/* %endif */ +/* %if-c++-only */ +/* %endif */ + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/* %if-c-only */ +/* %if-reentrant */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/* %endif */ + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int yyget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int yyget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *yyget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int yyget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *yyget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/* %if-reentrant */ + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void yyset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/* %endif */ + +/** Set the current line number. + * @param _line_number line number + * @param yyscanner The scanner object. + */ +void yyset_lineno (int _line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_lineno called with no buffer" ); + + yylineno = _line_number; +} + +/** Set the current column. + * @param _column_no column number + * @param yyscanner The scanner object. + */ +void yyset_column (int _column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + YY_FATAL_ERROR( "yyset_column called with no buffer" ); + + yycolumn = _column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param _in_str A readable stream. + * @param yyscanner The scanner object. + * @see yy_switch_to_buffer + */ +void yyset_in (FILE * _in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = _in_str ; +} + +void yyset_out (FILE * _out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = _out_str ; +} + +int yyget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void yyset_debug (int _bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = _bdebug ; +} + +/* %endif */ + +/* %if-reentrant */ +/* Accessor methods for yylval and yylloc */ + +/* %if-bison-bridge */ + +YYSTYPE * yyget_lval (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylval; +} + +void yyset_lval (YYSTYPE * yylval_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylval = yylval_param; +} + +YYLTYPE *yyget_lloc (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yylloc; +} + +void yyset_lloc (YYLTYPE * yylloc_param , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yylloc = yylloc_param; +} + +/* %endif */ + +/* User-visible API */ + +/* yylex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ +int yylex_init(yyscan_t* ptr_yy_globals) +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* yylex_init_extra has the same functionality as yylex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to yyalloc in + * the yyextra field. + */ +int yylex_init_extra( YY_EXTRA_TYPE yy_user_defined, yyscan_t* ptr_yy_globals ) +{ + struct yyguts_t dummy_yyguts; + + yyset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) yyalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + yyset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* %endif if-c-only */ + +/* %if-c-only */ +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from yylex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = NULL; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = NULL; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = NULL; + yyout = NULL; +#endif + + /* For future reference: Set errno on error, since we are called by + * yylex_init() + */ + return 0; +} +/* %endif */ + +/* %if-c-only SNIP! this currently causes conflicts with the c++ scanner */ +/* yylex_destroy is for both reentrant and non-reentrant scanners. */ +int yylex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + yy_delete_buffer( YY_CURRENT_BUFFER , yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + yypop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + yyfree(yyg->yy_buffer_stack , yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + yyfree( yyg->yy_start_stack , yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * yylex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + +/* %if-reentrant */ + /* Destroy the main struct (reentrant only). */ + yyfree ( yyscanner , yyscanner ); + yyscanner = NULL; +/* %endif */ + return 0; +} +/* %endif */ + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, const char * s2, int n , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (const char * s , yyscan_t yyscanner) +{ + int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *yyalloc (yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + return malloc(size); +} + +void *yyrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return realloc(ptr, size); +} + +void yyfree (void * ptr , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + (void)yyg; + free( (char *) ptr ); /* see yyrealloc() for (char *) cast */ +} + +/* %if-tables-serialization definitions */ +/* %define-yytables The name for this specific scanner's tables. */ +#define YYTABLES_NAME "yytables" +/* %endif */ + +/* %ok-for-header */ + +#line 918 "scanner.l" + + +static void scanner_push_indesc(struct parser_state *state, + struct input_descriptor *indesc) +{ + if (!state->indesc) + list_add_tail(&indesc->list, &state->indesc_list); + else + list_add(&indesc->list, &state->indesc->list); + + state->indesc = indesc; +} + +static void scanner_pop_indesc(struct parser_state *state) +{ + if (!list_is_first(&state->indesc->list, &state->indesc_list)) { + state->indesc = list_entry(state->indesc->list.prev, + struct input_descriptor, list); + } else { + state->indesc = NULL; + } +} + +static void scanner_pop_buffer(yyscan_t scanner) +{ + struct parser_state *state = yyget_extra(scanner); + + yypop_buffer_state(scanner); + scanner_pop_indesc(state); +} + +static void scanner_push_file(struct nft_ctx *nft, void *scanner, + FILE *f, const char *filename, + const struct location *loc, + const struct input_descriptor *parent_indesc) +{ + struct parser_state *state = yyget_extra(scanner); + struct input_descriptor *indesc; + YY_BUFFER_STATE b; + + b = yy_create_buffer(f, YY_BUF_SIZE, scanner); + yypush_buffer_state(b, scanner); + + indesc = xzalloc(sizeof(struct input_descriptor)); + + if (loc != NULL) + indesc->location = *loc; + indesc->type = INDESC_FILE; + indesc->name = xstrdup(filename); + indesc->f = f; + if (!parent_indesc) { + indesc->depth = 1; + } else { + indesc->depth = parent_indesc->depth + 1; + } + init_pos(indesc); + + scanner_push_indesc(state, indesc); +} + +enum nft_include_type { + NFT_INCLUDE, + NFT_CMDLINE, +}; + +static bool __is_useable(unsigned int type, enum nft_include_type t) +{ + type &= S_IFMT; + switch (type) { + case S_IFREG: return true; + case S_IFIFO: + return t == NFT_CMDLINE; /* disallow include /path/to/fifo */ + default: + break; + } + + return false; +} + +/* need to use stat() to, fopen() will block for named fifos */ +static bool filename_is_useable(const char *name) +{ + struct stat sb; + int err; + + err = stat(name, &sb); + if (err) + return false; + + return __is_useable(sb.st_mode, NFT_INCLUDE); +} + +static bool fp_is_useable(FILE *fp, enum nft_include_type t) +{ + int fd = fileno(fp); + struct stat sb; + int err; + + if (fd < 0) + return false; + + err = fstat(fd, &sb); + if (err < 0) + return false; + + return __is_useable(sb.st_mode, t); +} + +static int include_file(struct nft_ctx *nft, void *scanner, + const char *filename, const struct location *loc, + const struct input_descriptor *parent_indesc, + enum nft_include_type includetype) + +{ + struct parser_state *state = yyget_extra(scanner); + struct error_record *erec; + FILE *f; + + if (parent_indesc && parent_indesc->depth == MAX_INCLUDE_DEPTH) { + erec = error(loc, "Include nested too deeply, max %u levels", + MAX_INCLUDE_DEPTH); + goto err; + } + + if (includetype == NFT_INCLUDE && !filename_is_useable(filename)) { + erec = error(loc, "Not a regular file: \"%s\"\n", filename); + goto err; + } + + f = fopen(filename, "r"); + if (f == NULL) { + erec = error(loc, "Could not open file \"%s\": %s\n", + filename, strerror(errno)); + goto err; + } + + if (!fp_is_useable(f, includetype)) { + fclose(f); + erec = error(loc, "Not a regular file: \"%s\"\n", filename); + goto err; + } + + scanner_push_file(nft, scanner, f, filename, loc, parent_indesc); + return 0; +err: + erec_queue(erec, state->msgs); + return -1; +} + +static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern, + const struct location *loc) +{ + struct parser_state *state = yyget_extra(scanner); + struct input_descriptor *indesc = state->indesc; + struct error_record *erec = NULL; + bool wildcard = false; + glob_t glob_data; + unsigned int i; + int flags = 0; + int ret; + char *p; + + /* This function can return four meaningful values: + * + * -1 means that there was an error. + * 0 means that a single non-wildcard match was done. + * 1 means that there are no wildcards in the pattern and the + * search can continue. + * 2 means that there are wildcards in the pattern and the search + * can continue. + * + * The diffrence is needed, because there is a semantic difference + * between patterns with wildcards and no wildcards. Not finding a + * non-wildcard file is an error but not finding any matches for a + * wildcard pattern is not. + */ + + /* There shouldn't be a need to use escape characters in include paths. + */ + flags |= GLOB_NOESCAPE; + + /* Mark directories so we can filter them out (also links). */ + flags |= GLOB_MARK; + + /* If there is no match, glob() doesn't set GLOB_MAGCHAR even if there + * are wildcard characters in the pattern. We need to look for (luckily + * well-known and not likely to change) magic characters ourselves. In a + * perfect world, we could use glob() itself to figure out if there are + * wildcards in the pattern. + */ + p = (char *)pattern; + while (*p) { + if (*p == '*' || *p == '?' || *p == '[') { + wildcard = true; + break; + } + p++; + } + + ret = glob(pattern, flags, NULL, &glob_data); + if (ret == 0) { + char *path; + int len; + + /* reverse alphabetical order due to stack */ + for (i = glob_data.gl_pathc; i > 0; i--) { + + path = glob_data.gl_pathv[i-1]; + + /* ignore directories */ + len = strlen(path); + if (len == 0 || path[len - 1] == '/') + continue; + + ret = include_file(nft, scanner, path, loc, indesc, NFT_INCLUDE); + if (ret != 0) + goto err; + } + + globfree(&glob_data); + + /* If no wildcards and we found the file, stop the search (with + * 0). In case of wildcards we need to still continue the + * search, because other matches might be in other include + * directories. We handled the case with a non-wildcard pattern + * and no matches already before. + */ + return wildcard ? 2 : 0; + } else if (ret == GLOB_NOMATCH) { + globfree(&glob_data); + + /* We need to tell the caller whether wildcards were used in + * case of no match, because the semantics for handling the + * cases are different. + */ + return wildcard ? 2 : 1; + } + + erec = error(loc, "Failed to glob the pattern %s", pattern); + + /* intentional fall through */ +err: + if (erec) + erec_queue(erec, state->msgs); + globfree(&glob_data); + return -1; +} + +int scanner_read_file(struct nft_ctx *nft, const char *filename, + const struct location *loc) +{ + return include_file(nft, nft->scanner, filename, loc, NULL, NFT_CMDLINE); +} + +static bool search_in_include_path(const char *filename) +{ + return (strncmp(filename, "./", strlen("./")) != 0 && + strncmp(filename, "../", strlen("../")) != 0 && + filename[0] != '/'); +} + +int scanner_include_file(struct nft_ctx *nft, void *scanner, + const char *filename, const struct location *loc) +{ + struct parser_state *state = yyget_extra(scanner); + struct error_record *erec; + char buf[PATH_MAX]; + unsigned int i; + int ret = -1; + + if (search_in_include_path(filename)) { + for (i = 0; i < nft->num_include_paths; i++) { + ret = snprintf(buf, sizeof(buf), "%s/%s", + nft->include_paths[i], filename); + if (ret < 0 || ret >= PATH_MAX) { + erec = error(loc, "Too long file path \"%s/%s\"\n", + nft->include_paths[i], filename); + erec_queue(erec, state->msgs); + return -1; + } + + ret = include_glob(nft, scanner, buf, loc); + + /* error was already handled */ + if (ret == -1) + return -1; + /* no wildcards and file was processed: break early. */ + if (ret == 0) + return 0; + + /* else 1 (no wildcards) or 2 (wildcards): keep + * searching. + */ + } + } else { + /* an absolute path (starts with '/') */ + ret = include_glob(nft, scanner, filename, loc); + } + + /* handle the case where no file was found */ + if (ret == -1) + return -1; + else if (ret == 0 || ret == 2) + return 0; + + /* 1 means an error, because there are no more include directories to + * search, and the pattern does not have wildcard characters. + */ + erec = error(loc, "File not found: %s", filename); + 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); + struct input_descriptor *new_indesc; + YY_BUFFER_STATE b; + + new_indesc = xzalloc(sizeof(struct input_descriptor)); + memcpy(new_indesc, indesc, sizeof(*new_indesc)); + new_indesc->data = buffer; + new_indesc->name = xstrdup(indesc->name); + scanner_push_indesc(state, new_indesc); + + b = yy_scan_string(buffer, scanner); + assert(b != NULL); + init_pos(state->indesc); +} + +void *scanner_init(struct parser_state *state) +{ + yyscan_t scanner; + + yylex_init_extra(state, &scanner); + yyset_out(NULL, scanner); + + state->startcond_active = xzalloc_array(__SC_MAX, + sizeof(*state->startcond_active)); + return scanner; +} + +static void input_descriptor_destroy(const struct input_descriptor *indesc) +{ + if (indesc->name) + xfree(indesc->name); + xfree(indesc); +} + +static void input_descriptor_list_destroy(struct parser_state *state) +{ + struct input_descriptor *indesc, *next; + + list_for_each_entry_safe(indesc, next, &state->indesc_list, list) { + if (indesc->f) { + fclose(indesc->f); + indesc->f = NULL; + } + list_del(&indesc->list); + input_descriptor_destroy(indesc); + } +} + +void scanner_destroy(struct nft_ctx *nft) +{ + struct parser_state *state = yyget_extra(nft->scanner); + + input_descriptor_list_destroy(state); + xfree(state->startcond_active); + + yylex_destroy(nft->scanner); +} + +static void scanner_push_start_cond(void *scanner, enum startcond_type type) +{ + struct parser_state *state = yyget_extra(scanner); + + state->startcond_type = type; + state->startcond_active[type]++; + + yy_push_state((int)type, scanner); +} + +void scanner_pop_start_cond(void *scanner, enum startcond_type t) +{ + struct parser_state *state = yyget_extra(scanner); + + state->startcond_active[t]--; + + if (state->startcond_type != t) { + state->flex_state_pop++; + return; /* Can't pop just yet! */ + } + + while (state->flex_state_pop) { + state->flex_state_pop--; + state->startcond_type = yy_top_state(scanner); + yy_pop_state(scanner); + + t = state->startcond_type; + if (state->startcond_active[t]) + return; + } + + state->startcond_type = yy_top_state(scanner); + + yy_pop_state(scanner); +} + diff --git a/src/scanner.l b/src/scanner.l new file mode 100644 index 0000000..88376b7 --- /dev/null +++ b/src/scanner.l @@ -0,0 +1,1326 @@ +/* + * Copyright (c) 2007-2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +%{ + +#include <nft.h> + +#include <limits.h> +#include <glob.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/types.h> +#include <linux/netfilter.h> +#include <sys/stat.h> + +#include <nftables.h> +#include <erec.h> +#include <rule.h> +#include <parser.h> +#include "parser_bison.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) \ +{ \ + result = 0; \ + errno = 0; \ + \ + while (result < max_size) { \ + int chr = fgetc(yyin); \ + \ + if (chr != EOF) { \ + buf[result++] = chr; \ + if (chr == '\n' || chr == ' ') \ + break; \ + continue; \ + } \ + \ + if (ferror(yyin)) { \ + if (errno != EINTR) { \ + YY_FATAL_ERROR("input in flex scanner failed"); \ + break; \ + } \ + errno = 0; \ + clearerr(yyin); \ + } \ + break; \ + } \ +} + +static void scanner_pop_buffer(yyscan_t scanner); + + +static void init_pos(struct input_descriptor *indesc) +{ + indesc->lineno = 1; + indesc->column = 1; + indesc->token_offset = 0; + 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->last_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; +} + +static void scanner_push_start_cond(void *scanner, enum startcond_type type); + +#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}+ +letter [a-zA-Z] +string ({letter}|[_.])({letter}|{digit}|[/\-_\.])* +quotedstring \"[^"]*\" +asteriskstring ({string}\*|{string}\\\*|\\\*|{string}\\\*{string}) +comment #.*$ +comment_line ^[ \t]*#.*\n +slash \/ + +timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)?([0-9]+ms)? + +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})) +v620_rfc4291 ((:)(:{ip4addr})) +v621 ((({hex4}:){1})((:{hex4}){1})) +v622 ((({hex4}:){2})(:)) +v62_rfc4291 ((:)(:[fF]{4})(:{ip4addr})) +v62 ({v620}|{v621}|{v622}|{v62_rfc4291}|{v620_rfc4291}) +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}) +ip6addr_rfc2732 (\[{ip6addr}\]) + +classid ({hexdigit}{1,4}:{hexdigit}{1,4}) +addrstring ({macaddr}|{ip4addr}|{ip6addr}) + +%option prefix="nft_" +%option outfile="lex.yy.c" +%option reentrant +%option noyywrap +%option nounput +%option bison-bridge +%option bison-locations +%option debug +%option yylineno +%option nodefault +%option warn +%option stack +%s SCANSTATE_ARP +%s SCANSTATE_AT +%s SCANSTATE_CT +%s SCANSTATE_COUNTER +%s SCANSTATE_ETH +%s SCANSTATE_GRE +%s SCANSTATE_ICMP +%s SCANSTATE_IGMP +%s SCANSTATE_IP +%s SCANSTATE_IP6 +%s SCANSTATE_LAST +%s SCANSTATE_LIMIT +%s SCANSTATE_META +%s SCANSTATE_POLICY +%s SCANSTATE_QUOTA +%s SCANSTATE_SCTP +%s SCANSTATE_SECMARK +%s SCANSTATE_TCP +%s SCANSTATE_TYPE +%s SCANSTATE_VLAN +%s SCANSTATE_XT +%s SCANSTATE_CMD_DESTROY +%s SCANSTATE_CMD_EXPORT +%s SCANSTATE_CMD_IMPORT +%s SCANSTATE_CMD_LIST +%s SCANSTATE_CMD_MONITOR +%s SCANSTATE_CMD_RESET +%s SCANSTATE_EXPR_AH +%s SCANSTATE_EXPR_COMP +%s SCANSTATE_EXPR_DCCP +%s SCANSTATE_EXPR_DST +%s SCANSTATE_EXPR_ESP +%s SCANSTATE_EXPR_FIB +%s SCANSTATE_EXPR_FRAG +%s SCANSTATE_EXPR_HASH +%s SCANSTATE_EXPR_HBH +%s SCANSTATE_EXPR_IPSEC +%s SCANSTATE_EXPR_MH +%s SCANSTATE_EXPR_NUMGEN +%s SCANSTATE_EXPR_OSF +%s SCANSTATE_EXPR_QUEUE +%s SCANSTATE_EXPR_RT +%s SCANSTATE_EXPR_SCTP_CHUNK +%s SCANSTATE_EXPR_SOCKET +%s SCANSTATE_EXPR_TH +%s SCANSTATE_EXPR_UDP +%s SCANSTATE_EXPR_UDPLITE + +%s SCANSTATE_STMT_DUP +%s SCANSTATE_STMT_FWD +%s SCANSTATE_STMT_LOG +%s SCANSTATE_STMT_NAT +%s SCANSTATE_STMT_REJECT +%s SCANSTATE_STMT_SYNPROXY +%s SCANSTATE_STMT_TPROXY + +%% + +"==" { return EQ; } +"eq" { return EQ; } +"!=" { return NEQ; } +"ne" { return NEQ; } +"<=" { return LTE; } +"le" { return LTE; } +"<" { return LT; } +"lt" { return LT; } +">=" { return GTE; } +"ge" { return GTE; } +">" { return GT; } +"gt" { return GT; } +"," { return COMMA; } +"." { return DOT; } +":" { return COLON; } +";" { return SEMICOLON; } +"{" { return '{'; } +"}" { return '}'; } +"[" { return '['; } +"]" { return ']'; } +"(" { return '('; } +")" { return ')'; } +"<<" { return LSHIFT; } +"lshift" { return LSHIFT; } +">>" { return RSHIFT; } +"rshift" { return RSHIFT; } +"^" { return CARET; } +"xor" { return CARET; } +"&" { return AMPERSAND; } +"and" { return AMPERSAND; } +"|" { return '|'; } +"or" { return '|'; } +"!" { return NOT; } +"not" { return NOT; } +"/" { return SLASH; } +"-" { return DASH; } +"*" { return ASTERISK; } +"@" { scanner_push_start_cond(yyscanner, SCANSTATE_AT); return AT; } +"$" { return '$'; } +"=" { return '='; } +"vmap" { return VMAP; } + +"+" { return PLUS; } + +"include" { return INCLUDE; } +"define" { return DEFINE; } +"redefine" { return REDEFINE; } +"undefine" { return UNDEFINE; } + +"describe" { return DESCRIBE; } + +<SCANSTATE_CMD_LIST,SCANSTATE_CMD_MONITOR>{ + "chains" { return CHAINS; } + "sets" { return SETS; } + "tables" { return TABLES; } +} +<SCANSTATE_CMD_MONITOR>{ + "rules" { return RULES; } + "trace" { return TRACE; } +} +"hook" { return HOOK; } +"device" { return DEVICE; } +"devices" { return DEVICES; } +"table" { return TABLE; } +"chain" { return CHAIN; } +"rule" { return RULE; } +"set" { return SET; } +"element" { return ELEMENT; } +"map" { return MAP; } +"flowtable" { return FLOWTABLE; } +"handle" { return HANDLE; } +"ruleset" { return RULESET; } + +"socket" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SOCKET); return SOCKET; } +<SCANSTATE_EXPR_SOCKET>{ + "transparent" { return TRANSPARENT; } + "wildcard" { return WILDCARD; } + "cgroupv2" { return CGROUPV2; } + "level" { return LEVEL; } +} +"tproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_TPROXY); return TPROXY; } + +"accept" { return ACCEPT; } +"drop" { return DROP; } +"continue" { return CONTINUE; } +"jump" { return JUMP; } +"goto" { return GOTO; } +"return" { return RETURN; } +<SCANSTATE_EXPR_QUEUE,SCANSTATE_STMT_DUP,SCANSTATE_STMT_FWD,SCANSTATE_STMT_NAT,SCANSTATE_STMT_TPROXY,SCANSTATE_IP,SCANSTATE_IP6>"to" { return TO; } /* XXX: SCANSTATE_IP is a workaround */ + +"inet" { return INET; } +"netdev" { return NETDEV; } + +"add" { return ADD; } +"replace" { return REPLACE; } +"update" { return UPDATE; } +"create" { return CREATE; } +"insert" { return INSERT; } +"delete" { return DELETE; } +"get" { return GET; } +"list" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_LIST); return LIST; } +"reset" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_RESET); return RESET; } +"flush" { return FLUSH; } +"rename" { return RENAME; } +"import" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_IMPORT); return IMPORT; } +"export" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_EXPORT); return EXPORT; } +"monitor" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_MONITOR); return MONITOR; } +"destroy" { scanner_push_start_cond(yyscanner, SCANSTATE_CMD_DESTROY); return DESTROY; } + + +"position" { return POSITION; } +"index" { return INDEX; } +"comment" { return COMMENT; } + +"constant" { return CONSTANT; } +"interval" { return INTERVAL; } +"dynamic" { return DYNAMIC; } +"auto-merge" { return AUTOMERGE; } +"timeout" { return TIMEOUT; } +"gc-interval" { return GC_INTERVAL; } +"elements" { return ELEMENTS; } +"expires" { return EXPIRES; } + +"policy" { scanner_push_start_cond(yyscanner, SCANSTATE_POLICY); return POLICY; } +"size" { return SIZE; } +<SCANSTATE_POLICY>{ + "performance" { return PERFORMANCE; } + "memory" { return MEMORY; } +} + +"flow" { return FLOW; } +"offload" { return OFFLOAD; } +"meter" { return METER; } + +<SCANSTATE_CMD_LIST>{ + "meters" { return METERS; } + "flowtables" { return FLOWTABLES; } + "limits" { return LIMITS; } + "maps" { return MAPS; } + "secmarks" { return SECMARKS; } + "synproxys" { return SYNPROXYS; } + "hooks" { return HOOKS; } +} + +"counter" { scanner_push_start_cond(yyscanner, SCANSTATE_COUNTER); return COUNTER; } +<SCANSTATE_COUNTER,SCANSTATE_LIMIT,SCANSTATE_QUOTA,SCANSTATE_STMT_SYNPROXY,SCANSTATE_EXPR_OSF>"name" { return NAME; } +<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT>"packets" { return PACKETS; } +<SCANSTATE_COUNTER,SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"bytes" { return BYTES; } + +"last" { scanner_push_start_cond(yyscanner, SCANSTATE_LAST); return LAST; } +<SCANSTATE_LAST>{ + "never" { return NEVER; } +} + +<SCANSTATE_CMD_LIST,SCANSTATE_CMD_RESET>{ + "counters" { return COUNTERS; } + "quotas" { return QUOTAS; } + "rules" { return RULES; } +} + +"log" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_LOG); return LOG; } +<SCANSTATE_STMT_LOG,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"prefix" { return PREFIX; } +<SCANSTATE_STMT_LOG>{ + "snaplen" { return SNAPLEN; } + "queue-threshold" { return QUEUE_THRESHOLD; } + "level" { return LEVEL; } + "group" { return GROUP; } +} + +"queue" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_QUEUE); return QUEUE;} +<SCANSTATE_EXPR_QUEUE>{ + "num" { return QUEUENUM;} + "bypass" { return BYPASS;} + "fanout" { return FANOUT;} +} +"limit" { scanner_push_start_cond(yyscanner, SCANSTATE_LIMIT); return LIMIT; } +<SCANSTATE_LIMIT>{ + "rate" { return RATE; } + "burst" { return BURST; } + + /* time_unit */ + "second" { return SECOND; } + "minute" { return MINUTE; } + "week" { return WEEK; } +} +<SCANSTATE_CT,SCANSTATE_LIMIT,SCANSTATE_QUOTA>"over" { return OVER; } + +"quota" { scanner_push_start_cond(yyscanner, SCANSTATE_QUOTA); return QUOTA; } +<SCANSTATE_QUOTA>{ + "until" { return UNTIL; } +} + +<SCANSTATE_QUOTA,SCANSTATE_LAST>"used" { return USED; } + +"hour" { return HOUR; } +"day" { return DAY; } + +"reject" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_REJECT); return _REJECT; } +<SCANSTATE_STMT_REJECT>{ + "with" { return WITH; } + "icmpx" { return ICMPX; } +} + +"snat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return SNAT; } +"dnat" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return DNAT; } +"masquerade" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return MASQUERADE; } +"redirect" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_NAT); return REDIRECT; } +"random" { return RANDOM; } +<SCANSTATE_STMT_NAT>{ + "fully-random" { return FULLY_RANDOM; } + "persistent" { return PERSISTENT; } + "port" { return PORT; } +} + +<SCANSTATE_AT>{ + "ll" { return LL_HDR; } + "nh" { return NETWORK_HDR; } +} +"th" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_TH); return TRANSPORT_HDR; } + +"bridge" { return BRIDGE; } + +"ether" { scanner_push_start_cond(yyscanner, SCANSTATE_ETH); return ETHER; } +<SCANSTATE_ARP,SCANSTATE_CT,SCANSTATE_ETH,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_FIB,SCANSTATE_EXPR_IPSEC>{ + "saddr" { return SADDR; } + "daddr" { return DADDR; } +} +"type" { scanner_push_start_cond(yyscanner, SCANSTATE_TYPE); return TYPE; } +"typeof" { return TYPEOF; } + +"vlan" { scanner_push_start_cond(yyscanner, SCANSTATE_VLAN); return VLAN; } +<SCANSTATE_CT,SCANSTATE_EXPR_FRAG,SCANSTATE_VLAN,SCANSTATE_IP,SCANSTATE_ICMP>"id" { return ID; } +<SCANSTATE_VLAN>{ + "cfi" { return CFI; } + "dei" { return DEI; } + "pcp" { return PCP; } +} +"8021ad" { yylval->string = xstrdup(yytext); return STRING; } +"8021q" { yylval->string = xstrdup(yytext); return STRING; } + +"arp" { scanner_push_start_cond(yyscanner, SCANSTATE_ARP); return ARP; } +<SCANSTATE_ARP>{ + "htype" { return HTYPE; } + "ptype" { return PTYPE; } + "hlen" { return HLEN; } + "plen" { return PLEN; } + "operation" { return OPERATION; } +} + +"ip" { scanner_push_start_cond(yyscanner, SCANSTATE_IP); return IP; } +<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_EXPR_OSF,SCANSTATE_GRE>{ + "version" { return HDRVERSION; } +} +<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP>{ + "hdrlength" { return HDRLENGTH; } +} +<SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_TYPE>{ + "dscp" { return DSCP; } +} +"ecn" { return ECN; } +<SCANSTATE_EXPR_UDP,SCANSTATE_IP,SCANSTATE_IP6,SCANSTATE_META,SCANSTATE_TCP,SCANSTATE_SCTP,SCANSTATE_EXPR_SCTP_CHUNK>"length" { return LENGTH; } +<SCANSTATE_EXPR_FRAG,SCANSTATE_IP>{ + "frag-off" { return FRAG_OFF; } +} +<SCANSTATE_EXPR_OSF,SCANSTATE_IP>{ + "ttl" { return TTL; } +} +<SCANSTATE_CT,SCANSTATE_IP,SCANSTATE_META,SCANSTATE_TYPE,SCANSTATE_GRE>"protocol" { return PROTOCOL; } +<SCANSTATE_EXPR_MH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE,SCANSTATE_ICMP,SCANSTATE_IGMP,SCANSTATE_IP,SCANSTATE_SCTP,SCANSTATE_TCP>{ + "checksum" { return CHECKSUM; } +} + +<SCANSTATE_IP>{ + "lsrr" { return LSRR; } + "rr" { return RR; } + "ssrr" { return SSRR; } + "ra" { return RA; } + + "ptr" { return PTR; } + "value" { return VALUE; } + + "option" { return OPTION; } + "options" { return OPTIONS; } +} + +<SCANSTATE_TCP>{ + /* tcp header fields */ + "ackseq" { return ACKSEQ; } + "doff" { return DOFF; } + "window" { return WINDOW; } + "urgptr" { return URGPTR; } + + /* tcp option types */ + "echo" { return ECHO; } + "eol" { return EOL; } + "maxseg" { return MSS; } + "mss" { return MSS; } + "nop" { return NOP; } + "noop" { return NOP; } + "sack" { return SACK; } + "sack0" { return SACK0; } + "sack1" { return SACK1; } + "sack2" { return SACK2; } + "sack3" { return SACK3; } + "sack-permitted" { return SACK_PERM; } + "sack-perm" { return SACK_PERM; } + "timestamp" { return TIMESTAMP; } + "fastopen" { return FASTOPEN; } + "mptcp" { return MPTCP; } + "md5sig" { return MD5SIG; } + + /* tcp option fields */ + "left" { return LEFT; } + "right" { return RIGHT; } + "count" { return COUNT; } + "tsval" { return TSVAL; } + "tsecr" { return TSECR; } + "subtype" { return SUBTYPE; } + + "options" { return OPTIONS; } + "option" { return OPTION; } +} +"time" { return TIME; } + +"icmp" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP; } +"icmpv6" { scanner_push_start_cond(yyscanner, SCANSTATE_ICMP); return ICMP6; } +<SCANSTATE_ICMP>{ + "gateway" { return GATEWAY; } + "code" { return CODE; } + "param-problem" { return PPTR; } + "max-delay" { return MAXDELAY; } + "mtu" { return MTU; } + "taddr" { return TADDR; } + "daddr" { return DADDR; } +} +<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_ICMP,SCANSTATE_TCP>{ + "sequence" { return SEQUENCE; } +} + +"igmp" { scanner_push_start_cond(yyscanner, SCANSTATE_IGMP); return IGMP; } +<SCANSTATE_IGMP>{ + "mrt" { return MRT; } + "group" { return GROUP; } +} + +"ip6" { scanner_push_start_cond(yyscanner, SCANSTATE_IP6); return IP6; } +"priority" { return PRIORITY; } +<SCANSTATE_IP6>{ + "flowlabel" { return FLOWLABEL; } + "hoplimit" { return HOPLIMIT; } +} +<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_COMP,SCANSTATE_EXPR_DST,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_HBH,SCANSTATE_EXPR_MH,SCANSTATE_EXPR_RT,SCANSTATE_IP6>{ + "nexthdr" { return NEXTHDR; } +} + +"ah" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_AH); return AH; } +<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_FRAG,SCANSTATE_EXPR_MH,SCANSTATE_TCP>{ + "reserved" { return RESERVED; } +} +<SCANSTATE_EXPR_AH,SCANSTATE_EXPR_ESP,SCANSTATE_EXPR_IPSEC>"spi" { return SPI; } + +"esp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_ESP); return ESP; } + +"comp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_COMP); return COMP; } +<SCANSTATE_EXPR_COMP>{ + "cpi" { return CPI; } +} +"flags" { return FLAGS; } + +"udp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDP); return UDP; } +"udplite" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_UDPLITE); return UDPLITE; } +<SCANSTATE_EXPR_UDPLITE>{ + "csumcov" { return CSUMCOV; } +} +<SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{ + "sport" { return SPORT; } +} +<SCANSTATE_CT,SCANSTATE_EXPR_DCCP,SCANSTATE_SCTP,SCANSTATE_TCP,SCANSTATE_EXPR_TH,SCANSTATE_EXPR_UDP,SCANSTATE_EXPR_UDPLITE>{ + "dport" { return DPORT; } +} +<SCANSTATE_EXPR_DCCP>{ + "option" { return OPTION; } +} + +"vxlan" { return VXLAN; } +"vni" { return VNI; } + +"geneve" { return GENEVE; } + +"gre" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRE; } +"gretap" { scanner_push_start_cond(yyscanner, SCANSTATE_GRE); return GRETAP; } + +"tcp" { scanner_push_start_cond(yyscanner, SCANSTATE_TCP); return TCP; } + +"dccp" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DCCP); return DCCP; } + +"sctp" { scanner_push_start_cond(yyscanner, SCANSTATE_SCTP); return SCTP; } + +<SCANSTATE_SCTP>{ + "chunk" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_SCTP_CHUNK); return CHUNK; } + "vtag" { return VTAG; } +} + +<SCANSTATE_EXPR_SCTP_CHUNK>{ + "data" { return DATA; } + "init" { return INIT; } + "init-ack" { return INIT_ACK; } + "heartbeat" { return HEARTBEAT; } + "heartbeat-ack" { return HEARTBEAT_ACK; } + "abort" { return ABORT; } + "shutdown" { return SHUTDOWN; } + "shutdown-ack" { return SHUTDOWN_ACK; } + "error" { return ERROR; } + "cookie-echo" { return COOKIE_ECHO; } + "cookie-ack" { return COOKIE_ACK; } + "ecne" { return ECNE; } + "cwr" { return CWR; } + "shutdown-complete" { return SHUTDOWN_COMPLETE; } + "asconf-ack" { return ASCONF_ACK; } + "forward-tsn" { return FORWARD_TSN; } + "asconf" { return ASCONF; } + + "tsn" { return TSN; } + "sack" { return SACK; } + "stream" { return STREAM; } + "ssn" { return SSN; } + "ppid" { return PPID; } + "init-tag" { return INIT_TAG; } + "a-rwnd" { return A_RWND; } + "num-outbound-streams" { return NUM_OSTREAMS; } + "num-inbound-streams" { return NUM_ISTREAMS; } + "initial-tsn" { return INIT_TSN; } + "cum-tsn-ack" { return CUM_TSN_ACK; } + "num-gap-ack-blocks" { return NUM_GACK_BLOCKS; } + "num-dup-tsns" { return NUM_DUP_TSNS; } + "lowest-tsn" { return LOWEST_TSN; } + "seqno" { return SEQNO; } + "new-cum-tsn" { return NEW_CUM_TSN; } +} + +"rt" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT; } +"rt0" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT0; } +"rt2" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT2; } +"srh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_RT); return RT4; } +<SCANSTATE_EXPR_RT,SCANSTATE_STMT_NAT,SCANSTATE_IP,SCANSTATE_IP6>"addr" { return ADDR; } + +"hbh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HBH); return HBH; } + +"frag" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FRAG); return FRAG; } +<SCANSTATE_EXPR_FRAG>{ + "reserved2" { return RESERVED2; } + "more-fragments" { return MORE_FRAGMENTS; } +} + +"dst" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_DST); return DST; } + +"mh" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_MH); return MH; } + +"meta" { scanner_push_start_cond(yyscanner, SCANSTATE_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; } +"ibriport" { return IBRIPORT; } +"ibrname" { return IBRIDGENAME; } +"obriport" { return OBRIPORT; } +"obrname" { return OBRIDGENAME; } +"pkttype" { return PKTTYPE; } +"cpu" { return CPU; } +"iifgroup" { return IIFGROUP; } +"oifgroup" { return OIFGROUP; } +"cgroup" { return CGROUP; } + +<SCANSTATE_EXPR_RT>{ + "nexthop" { return NEXTHOP; } + "seg-left" { return SEG_LEFT; } + "mtu" { return MTU; } + "last-entry" { return LAST_ENT; } + "tag" { return TAG; } + "sid" { return SID; } +} +<SCANSTATE_EXPR_RT,SCANSTATE_TYPE>{ + "classid" { return CLASSID; } +} + +"ct" { scanner_push_start_cond(yyscanner, SCANSTATE_CT); return CT; } +<SCANSTATE_CT>{ + "avgpkt" { return AVGPKT; } + "l3proto" { return L3PROTOCOL; } + "proto-src" { return PROTO_SRC; } + "proto-dst" { return PROTO_DST; } + "zone" { return ZONE; } + "original" { return ORIGINAL; } + "reply" { return REPLY; } + "direction" { return DIRECTION; } + "event" { return EVENT; } + "expectation" { return EXPECTATION; } + "expiration" { return EXPIRATION; } + "helper" { return HELPER; } + "helpers" { return HELPERS; } + "label" { return LABEL; } + "state" { return STATE; } + "status" { return STATUS; } + "count" { return COUNT; } +} + +"numgen" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_NUMGEN); return NUMGEN; } +<SCANSTATE_EXPR_NUMGEN>{ + "inc" { return INC; } +} + +"jhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return JHASH; } +"symhash" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_HASH); return SYMHASH; } + +<SCANSTATE_EXPR_HASH>{ + "seed" { return SEED; } +} +<SCANSTATE_EXPR_HASH,SCANSTATE_EXPR_NUMGEN>{ + "mod" { return MOD; } + "offset" { return OFFSET; } +} +"dup" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_DUP); return DUP; } +"fwd" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_FWD); return FWD; } + +"fib" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_FIB); return FIB; } + +"osf" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_OSF); return OSF; } + +"synproxy" { scanner_push_start_cond(yyscanner, SCANSTATE_STMT_SYNPROXY); return SYNPROXY; } +<SCANSTATE_STMT_SYNPROXY>{ + "wscale" { return WSCALE; } + "maxseg" { return MSS; } + "mss" { return MSS; } + "timestamp" { return TIMESTAMP; } + "sack-permitted" { return SACK_PERM; } + "sack-perm" { return SACK_PERM; } +} + +"notrack" { return NOTRACK; } + +"all" { return ALL; } + +<SCANSTATE_CMD_EXPORT,SCANSTATE_CMD_IMPORT,SCANSTATE_CMD_MONITOR>{ + "xml" { return XML; } + "json" { return JSON; } + "vm" { return VM; } +} + +"exists" { return EXISTS; } +"missing" { return MISSING; } + +"exthdr" { return EXTHDR; } + +"ipsec" { scanner_push_start_cond(yyscanner, SCANSTATE_EXPR_IPSEC); return IPSEC; } +<SCANSTATE_EXPR_IPSEC>{ + "reqid" { return REQID; } + "spnum" { return SPNUM; } + + "in" { return IN; } + "out" { return OUT; } +} + +"secmark" { scanner_push_start_cond(yyscanner, SCANSTATE_SECMARK); return SECMARK; } + +"xt" { scanner_push_start_cond(yyscanner, SCANSTATE_XT); return XT; } + +{addrstring} { + yylval->string = xstrdup(yytext); + return STRING; + } + +{ip6addr_rfc2732} { + yytext[yyleng - 1] = '\0'; + yylval->string = xstrdup(yytext + 1); + return STRING; + } + +{timestring} { + yylval->string = xstrdup(yytext); + return STRING; + } + +{hexstring} { + errno = 0; + yylval->val = strtoull(yytext, NULL, 16); + if (errno != 0) { + yylval->string = xstrdup(yytext); + return STRING; + } + return NUM; + } + +{decstring} { + int base = yytext[0] == '0' ? 8 : 10; + char *end; + + errno = 0; + yylval->val = strtoull(yytext, &end, base); + if (errno != 0 || *end) { + yylval->string = xstrdup(yytext); + return STRING; + } + return NUM; + } + +{classid}/[ \t\n:\-},] { + yylval->string = xstrdup(yytext); + return STRING; + } + +{quotedstring} { + yytext[yyleng - 1] = '\0'; + yylval->string = xstrdup(yytext + 1); + return QUOTED_STRING; + } + +{asteriskstring} { + yylval->string = xstrdup(yytext); + return ASTERISK_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}+ +{space}+ +{comment_line} { + reset_pos(yyget_extra(yyscanner), yylloc); + } +{comment} + +<<EOF>> { + update_pos(yyget_extra(yyscanner), yylloc, 1); + scanner_pop_buffer(yyscanner); + if (YY_CURRENT_BUFFER == NULL) + return TOKEN_EOF; + } + +. { return JUNK; } + +%% + +static void scanner_push_indesc(struct parser_state *state, + struct input_descriptor *indesc) +{ + if (!state->indesc) + list_add_tail(&indesc->list, &state->indesc_list); + else + list_add(&indesc->list, &state->indesc->list); + + state->indesc = indesc; +} + +static void scanner_pop_indesc(struct parser_state *state) +{ + if (!list_is_first(&state->indesc->list, &state->indesc_list)) { + state->indesc = list_entry(state->indesc->list.prev, + struct input_descriptor, list); + } else { + state->indesc = NULL; + } +} + +static void scanner_pop_buffer(yyscan_t scanner) +{ + struct parser_state *state = yyget_extra(scanner); + + yypop_buffer_state(scanner); + scanner_pop_indesc(state); +} + +static void scanner_push_file(struct nft_ctx *nft, void *scanner, + FILE *f, const char *filename, + const struct location *loc, + const struct input_descriptor *parent_indesc) +{ + struct parser_state *state = yyget_extra(scanner); + struct input_descriptor *indesc; + YY_BUFFER_STATE b; + + b = yy_create_buffer(f, YY_BUF_SIZE, scanner); + yypush_buffer_state(b, scanner); + + indesc = xzalloc(sizeof(struct input_descriptor)); + + if (loc != NULL) + indesc->location = *loc; + indesc->type = INDESC_FILE; + indesc->name = xstrdup(filename); + indesc->f = f; + if (!parent_indesc) { + indesc->depth = 1; + } else { + indesc->depth = parent_indesc->depth + 1; + } + init_pos(indesc); + + scanner_push_indesc(state, indesc); +} + +enum nft_include_type { + NFT_INCLUDE, + NFT_CMDLINE, +}; + +static bool __is_useable(unsigned int type, enum nft_include_type t) +{ + type &= S_IFMT; + switch (type) { + case S_IFREG: return true; + case S_IFIFO: + return t == NFT_CMDLINE; /* disallow include /path/to/fifo */ + default: + break; + } + + return false; +} + +/* need to use stat() to, fopen() will block for named fifos */ +static bool filename_is_useable(const char *name) +{ + struct stat sb; + int err; + + err = stat(name, &sb); + if (err) + return false; + + return __is_useable(sb.st_mode, NFT_INCLUDE); +} + +static bool fp_is_useable(FILE *fp, enum nft_include_type t) +{ + int fd = fileno(fp); + struct stat sb; + int err; + + if (fd < 0) + return false; + + err = fstat(fd, &sb); + if (err < 0) + return false; + + return __is_useable(sb.st_mode, t); +} + +static int include_file(struct nft_ctx *nft, void *scanner, + const char *filename, const struct location *loc, + const struct input_descriptor *parent_indesc, + enum nft_include_type includetype) + +{ + struct parser_state *state = yyget_extra(scanner); + struct error_record *erec; + FILE *f; + + if (parent_indesc && parent_indesc->depth == MAX_INCLUDE_DEPTH) { + erec = error(loc, "Include nested too deeply, max %u levels", + MAX_INCLUDE_DEPTH); + goto err; + } + + if (includetype == NFT_INCLUDE && !filename_is_useable(filename)) { + erec = error(loc, "Not a regular file: \"%s\"\n", filename); + goto err; + } + + f = fopen(filename, "r"); + if (f == NULL) { + erec = error(loc, "Could not open file \"%s\": %s\n", + filename, strerror(errno)); + goto err; + } + + if (!fp_is_useable(f, includetype)) { + fclose(f); + erec = error(loc, "Not a regular file: \"%s\"\n", filename); + goto err; + } + + scanner_push_file(nft, scanner, f, filename, loc, parent_indesc); + return 0; +err: + erec_queue(erec, state->msgs); + return -1; +} + +static int include_glob(struct nft_ctx *nft, void *scanner, const char *pattern, + const struct location *loc) +{ + struct parser_state *state = yyget_extra(scanner); + struct input_descriptor *indesc = state->indesc; + struct error_record *erec = NULL; + bool wildcard = false; + glob_t glob_data; + unsigned int i; + int flags = 0; + int ret; + char *p; + + /* This function can return four meaningful values: + * + * -1 means that there was an error. + * 0 means that a single non-wildcard match was done. + * 1 means that there are no wildcards in the pattern and the + * search can continue. + * 2 means that there are wildcards in the pattern and the search + * can continue. + * + * The diffrence is needed, because there is a semantic difference + * between patterns with wildcards and no wildcards. Not finding a + * non-wildcard file is an error but not finding any matches for a + * wildcard pattern is not. + */ + + /* There shouldn't be a need to use escape characters in include paths. + */ + flags |= GLOB_NOESCAPE; + + /* Mark directories so we can filter them out (also links). */ + flags |= GLOB_MARK; + + /* If there is no match, glob() doesn't set GLOB_MAGCHAR even if there + * are wildcard characters in the pattern. We need to look for (luckily + * well-known and not likely to change) magic characters ourselves. In a + * perfect world, we could use glob() itself to figure out if there are + * wildcards in the pattern. + */ + p = (char *)pattern; + while (*p) { + if (*p == '*' || *p == '?' || *p == '[') { + wildcard = true; + break; + } + p++; + } + + ret = glob(pattern, flags, NULL, &glob_data); + if (ret == 0) { + char *path; + int len; + + /* reverse alphabetical order due to stack */ + for (i = glob_data.gl_pathc; i > 0; i--) { + + path = glob_data.gl_pathv[i-1]; + + /* ignore directories */ + len = strlen(path); + if (len == 0 || path[len - 1] == '/') + continue; + + ret = include_file(nft, scanner, path, loc, indesc, NFT_INCLUDE); + if (ret != 0) + goto err; + } + + globfree(&glob_data); + + /* If no wildcards and we found the file, stop the search (with + * 0). In case of wildcards we need to still continue the + * search, because other matches might be in other include + * directories. We handled the case with a non-wildcard pattern + * and no matches already before. + */ + return wildcard ? 2 : 0; + } else if (ret == GLOB_NOMATCH) { + globfree(&glob_data); + + /* We need to tell the caller whether wildcards were used in + * case of no match, because the semantics for handling the + * cases are different. + */ + return wildcard ? 2 : 1; + } + + erec = error(loc, "Failed to glob the pattern %s", pattern); + + /* intentional fall through */ +err: + if (erec) + erec_queue(erec, state->msgs); + globfree(&glob_data); + return -1; +} + +int scanner_read_file(struct nft_ctx *nft, const char *filename, + const struct location *loc) +{ + return include_file(nft, nft->scanner, filename, loc, NULL, NFT_CMDLINE); +} + +static bool search_in_include_path(const char *filename) +{ + return (strncmp(filename, "./", strlen("./")) != 0 && + strncmp(filename, "../", strlen("../")) != 0 && + filename[0] != '/'); +} + +int scanner_include_file(struct nft_ctx *nft, void *scanner, + const char *filename, const struct location *loc) +{ + struct parser_state *state = yyget_extra(scanner); + struct error_record *erec; + char buf[PATH_MAX]; + unsigned int i; + int ret = -1; + + if (search_in_include_path(filename)) { + for (i = 0; i < nft->num_include_paths; i++) { + ret = snprintf(buf, sizeof(buf), "%s/%s", + nft->include_paths[i], filename); + if (ret < 0 || ret >= PATH_MAX) { + erec = error(loc, "Too long file path \"%s/%s\"\n", + nft->include_paths[i], filename); + erec_queue(erec, state->msgs); + return -1; + } + + ret = include_glob(nft, scanner, buf, loc); + + /* error was already handled */ + if (ret == -1) + return -1; + /* no wildcards and file was processed: break early. */ + if (ret == 0) + return 0; + + /* else 1 (no wildcards) or 2 (wildcards): keep + * searching. + */ + } + } else { + /* an absolute path (starts with '/') */ + ret = include_glob(nft, scanner, filename, loc); + } + + /* handle the case where no file was found */ + if (ret == -1) + return -1; + else if (ret == 0 || ret == 2) + return 0; + + /* 1 means an error, because there are no more include directories to + * search, and the pattern does not have wildcard characters. + */ + erec = error(loc, "File not found: %s", filename); + 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); + struct input_descriptor *new_indesc; + YY_BUFFER_STATE b; + + new_indesc = xzalloc(sizeof(struct input_descriptor)); + memcpy(new_indesc, indesc, sizeof(*new_indesc)); + new_indesc->data = buffer; + new_indesc->name = xstrdup(indesc->name); + scanner_push_indesc(state, new_indesc); + + b = yy_scan_string(buffer, scanner); + assert(b != NULL); + init_pos(state->indesc); +} + +void *scanner_init(struct parser_state *state) +{ + yyscan_t scanner; + + yylex_init_extra(state, &scanner); + yyset_out(NULL, scanner); + + state->startcond_active = xzalloc_array(__SC_MAX, + sizeof(*state->startcond_active)); + return scanner; +} + +static void input_descriptor_destroy(const struct input_descriptor *indesc) +{ + if (indesc->name) + xfree(indesc->name); + xfree(indesc); +} + +static void input_descriptor_list_destroy(struct parser_state *state) +{ + struct input_descriptor *indesc, *next; + + list_for_each_entry_safe(indesc, next, &state->indesc_list, list) { + if (indesc->f) { + fclose(indesc->f); + indesc->f = NULL; + } + list_del(&indesc->list); + input_descriptor_destroy(indesc); + } +} + +void scanner_destroy(struct nft_ctx *nft) +{ + struct parser_state *state = yyget_extra(nft->scanner); + + input_descriptor_list_destroy(state); + xfree(state->startcond_active); + + yylex_destroy(nft->scanner); +} + +static void scanner_push_start_cond(void *scanner, enum startcond_type type) +{ + struct parser_state *state = yyget_extra(scanner); + + state->startcond_type = type; + state->startcond_active[type]++; + + yy_push_state((int)type, scanner); +} + +void scanner_pop_start_cond(void *scanner, enum startcond_type t) +{ + struct parser_state *state = yyget_extra(scanner); + + state->startcond_active[t]--; + + if (state->startcond_type != t) { + state->flex_state_pop++; + return; /* Can't pop just yet! */ + } + + while (state->flex_state_pop) { + state->flex_state_pop--; + state->startcond_type = yy_top_state(scanner); + yy_pop_state(scanner); + + t = state->startcond_type; + if (state->startcond_active[t]) + return; + } + + state->startcond_type = yy_top_state(scanner); + + yy_pop_state(scanner); +} diff --git a/src/sctp_chunk.c b/src/sctp_chunk.c new file mode 100644 index 0000000..24a07e2 --- /dev/null +++ b/src/sctp_chunk.c @@ -0,0 +1,262 @@ +/* + * Copyright Red Hat + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <exthdr.h> +#include <sctp_chunk.h> + + +#define PHT(__token, __offset, __len) \ + PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \ + __offset, __len) + +static const struct exthdr_desc sctp_chunk_data = { + .name = "data", + .type = SCTP_CHUNK_TYPE_DATA, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_DATA_TSN] = PHT("tsn", 32, 32), + [SCTP_CHUNK_DATA_STREAM] = PHT("stream", 64, 16), + [SCTP_CHUNK_DATA_SSN] = PHT("ssn", 80, 16), + [SCTP_CHUNK_DATA_PPID] = PHT("ppid", 96, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_init = { + .name = "init", + .type = SCTP_CHUNK_TYPE_INIT, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32), + [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16), + [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16), + [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_init_ack = { + .name = "init-ack", + .type = SCTP_CHUNK_TYPE_INIT_ACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_INIT_TAG] = PHT("init-tag", 32, 32), + [SCTP_CHUNK_INIT_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_INIT_OSTREAMS] = PHT("num-outbound-streams", 96, 16), + [SCTP_CHUNK_INIT_ISTREAMS] = PHT("num-inbound-streams", 112, 16), + [SCTP_CHUNK_INIT_TSN] = PHT("initial-tsn", 128, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_sack = { + .name = "sack", + .type = SCTP_CHUNK_TYPE_SACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_SACK_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32), + [SCTP_CHUNK_SACK_RWND] = PHT("a-rwnd", 64, 32), + [SCTP_CHUNK_SACK_GACK_BLOCKS] = PHT("num-gap-ack-blocks", 96, 16), + [SCTP_CHUNK_SACK_DUP_TSNS] = PHT("num-dup-tsns", 112, 16), + }, +}; + +static const struct exthdr_desc sctp_chunk_shutdown = { + .name = "shutdown", + .type = SCTP_CHUNK_TYPE_SHUTDOWN, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_SHUTDOWN_CTSN_ACK] = PHT("cum-tsn-ack", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_ecne = { + .name = "ecne", + .type = SCTP_CHUNK_TYPE_ECNE, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_cwr = { + .name = "cwr", + .type = SCTP_CHUNK_TYPE_CWR, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ECNE_CWR_MIN_TSN] = PHT("lowest-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_asconf_ack = { + .name = "asconf-ack", + .type = SCTP_CHUNK_TYPE_ASCONF_ACK, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_forward_tsn = { + .name = "forward-tsn", + .type = SCTP_CHUNK_TYPE_FORWARD_TSN, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_FORWARD_TSN_NCTSN] = PHT("new-cum-tsn", 32, 32), + }, +}; + +static const struct exthdr_desc sctp_chunk_asconf = { + .name = "asconf", + .type = SCTP_CHUNK_TYPE_ASCONF, + .templates = { + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16), + [SCTP_CHUNK_ASCONF_SEQNO] = PHT("seqno", 32, 32), + }, +}; + +#define SCTP_CHUNK_DESC_GENERATOR(descname, hname, desctype) \ +static const struct exthdr_desc sctp_chunk_##descname = { \ + .name = #hname, \ + .type = SCTP_CHUNK_TYPE_##desctype, \ + .templates = { \ + [SCTP_CHUNK_COMMON_TYPE] = PHT("type", 0, 8), \ + [SCTP_CHUNK_COMMON_FLAGS] = PHT("flags", 8, 8), \ + [SCTP_CHUNK_COMMON_LENGTH] = PHT("length", 16, 16),\ + }, \ +}; + +SCTP_CHUNK_DESC_GENERATOR(heartbeat, heartbeat, HEARTBEAT) +SCTP_CHUNK_DESC_GENERATOR(heartbeat_ack, heartbeat-ack, HEARTBEAT_ACK) +SCTP_CHUNK_DESC_GENERATOR(abort, abort, ABORT) +SCTP_CHUNK_DESC_GENERATOR(shutdown_ack, shutdown-ack, SHUTDOWN_ACK) +SCTP_CHUNK_DESC_GENERATOR(error, error, ERROR) +SCTP_CHUNK_DESC_GENERATOR(cookie_echo, cookie-echo, COOKIE_ECHO) +SCTP_CHUNK_DESC_GENERATOR(cookie_ack, cookie-ack, COOKIE_ACK) +SCTP_CHUNK_DESC_GENERATOR(shutdown_complete, shutdown-complete, SHUTDOWN_COMPLETE) + +#undef SCTP_CHUNK_DESC_GENERATOR + +static const struct exthdr_desc *sctp_chunk_protocols[] = { + [SCTP_CHUNK_TYPE_DATA] = &sctp_chunk_data, + [SCTP_CHUNK_TYPE_INIT] = &sctp_chunk_init, + [SCTP_CHUNK_TYPE_INIT_ACK] = &sctp_chunk_init_ack, + [SCTP_CHUNK_TYPE_SACK] = &sctp_chunk_sack, + [SCTP_CHUNK_TYPE_HEARTBEAT] = &sctp_chunk_heartbeat, + [SCTP_CHUNK_TYPE_HEARTBEAT_ACK] = &sctp_chunk_heartbeat_ack, + [SCTP_CHUNK_TYPE_ABORT] = &sctp_chunk_abort, + [SCTP_CHUNK_TYPE_SHUTDOWN] = &sctp_chunk_shutdown, + [SCTP_CHUNK_TYPE_SHUTDOWN_ACK] = &sctp_chunk_shutdown_ack, + [SCTP_CHUNK_TYPE_ERROR] = &sctp_chunk_error, + [SCTP_CHUNK_TYPE_COOKIE_ECHO] = &sctp_chunk_cookie_echo, + [SCTP_CHUNK_TYPE_COOKIE_ACK] = &sctp_chunk_cookie_ack, + [SCTP_CHUNK_TYPE_ECNE] = &sctp_chunk_ecne, + [SCTP_CHUNK_TYPE_CWR] = &sctp_chunk_cwr, + [SCTP_CHUNK_TYPE_SHUTDOWN_COMPLETE] = &sctp_chunk_shutdown_complete, + [SCTP_CHUNK_TYPE_ASCONF_ACK] = &sctp_chunk_asconf_ack, + [SCTP_CHUNK_TYPE_FORWARD_TSN] = &sctp_chunk_forward_tsn, + [SCTP_CHUNK_TYPE_ASCONF] = &sctp_chunk_asconf, +}; + +const struct exthdr_desc *sctp_chunk_protocol_find(const char *name) +{ + unsigned int i; + + for (i = 0; i < array_size(sctp_chunk_protocols); i++) { + if (sctp_chunk_protocols[i] && + !strcmp(sctp_chunk_protocols[i]->name, name)) + return sctp_chunk_protocols[i]; + } + return NULL; +} + +struct expr *sctp_chunk_expr_alloc(const struct location *loc, + unsigned int type, unsigned int field) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc = NULL; + struct expr *expr; + + if (type < array_size(sctp_chunk_protocols)) + desc = sctp_chunk_protocols[type]; + + if (!desc) + return NULL; + + tmpl = &desc->templates[field]; + if (!tmpl) + return NULL; + + expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, tmpl->len); + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.op = NFT_EXTHDR_OP_SCTP; + expr->exthdr.raw_type = desc->type; + expr->exthdr.offset = tmpl->offset; + + return expr; +} + +void sctp_chunk_init_raw(struct expr *expr, uint8_t type, unsigned int off, + unsigned int len, uint32_t flags) +{ + const struct proto_hdr_template *tmpl; + unsigned int i; + + assert(expr->etype == EXPR_EXTHDR); + + expr->len = len; + expr->exthdr.flags = flags; + expr->exthdr.offset = off; + expr->exthdr.op = NFT_EXTHDR_OP_SCTP; + + if (flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + else + datatype_set(expr, &integer_type); + + if (type >= array_size(sctp_chunk_protocols)) + return; + + expr->exthdr.desc = sctp_chunk_protocols[type]; + expr->exthdr.flags = flags; + 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 != off || tmpl->len != len) + continue; + + if ((flags & NFT_EXTHDR_F_PRESENT) == 0) + datatype_set(expr, tmpl->dtype); + + expr->exthdr.tmpl = tmpl; + break; + } +} diff --git a/src/segtree.c b/src/segtree.c new file mode 100644 index 0000000..28172b3 --- /dev/null +++ b/src/segtree.c @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2008-2012 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <inttypes.h> +#include <arpa/inet.h> + +#include <libnftnl/udata.h> + +#include <rule.h> +#include <expression.h> +#include <gmputil.h> +#include <utils.h> + +static enum byteorder get_key_byteorder(const struct expr *e) +{ + enum datatypes basetype = expr_basetype(e)->type; + + switch (basetype) { + case TYPE_INTEGER: + /* For ranges, integers MUST be in BYTEORDER_BIG_ENDIAN. + * If the LHS (lookup key, e.g. 'meta mark', is host endian, + * a byteorder expression is injected to convert the register + * content before lookup. + */ + return BYTEORDER_BIG_ENDIAN; + case TYPE_STRING: + return BYTEORDER_HOST_ENDIAN; + default: + break; + } + + return BYTEORDER_INVALID; +} + +static void interval_expr_copy(struct expr *dst, struct expr *src) +{ + if (src->comment) + dst->comment = xstrdup(src->comment); + if (src->timeout) + dst->timeout = src->timeout; + if (src->expiration) + dst->expiration = src->expiration; + + list_splice_init(&src->stmt_list, &dst->stmt_list); +} + +static void set_elem_add(const struct set *set, struct expr *init, mpz_t value, + uint32_t flags, enum byteorder byteorder) +{ + struct expr *expr; + + expr = constant_expr_alloc(&internal_location, set->key->dtype, + byteorder, set->key->len, NULL); + mpz_set(expr->value, value); + expr = set_elem_expr_alloc(&internal_location, expr); + expr->flags = flags; + + compound_expr_add(init, expr); +} + +struct expr *get_set_intervals(const struct set *set, const struct expr *init) +{ + enum byteorder byteorder = get_key_byteorder(set->key); + struct expr *new_init; + mpz_t low, high; + struct expr *i; + + mpz_init2(low, set->key->len); + mpz_init2(high, set->key->len); + + new_init = list_expr_alloc(&internal_location); + + list_for_each_entry(i, &init->expressions, list) { + switch (i->key->etype) { + case EXPR_VALUE: + set_elem_add(set, new_init, i->key->value, + i->flags, byteorder); + break; + case EXPR_CONCAT: + compound_expr_add(new_init, expr_clone(i)); + i->flags |= EXPR_F_INTERVAL_END; + compound_expr_add(new_init, expr_clone(i)); + break; + case EXPR_SET_ELEM_CATCHALL: + compound_expr_add(new_init, expr_clone(i)); + break; + default: + range_expr_value_low(low, i); + set_elem_add(set, new_init, low, 0, i->byteorder); + range_expr_value_high(high, i); + mpz_add_ui(high, high, 1); + set_elem_add(set, new_init, high, + EXPR_F_INTERVAL_END, i->byteorder); + break; + } + } + + mpz_clear(low); + mpz_clear(high); + + return new_init; +} + +static struct expr *get_set_interval_find(const struct set *cache_set, + struct expr *left, + struct expr *right) +{ + const struct set *set = cache_set; + struct expr *range = NULL; + struct expr *i; + mpz_t val; + + mpz_init2(val, set->key->len); + + list_for_each_entry(i, &set->init->expressions, list) { + switch (i->key->etype) { + case EXPR_VALUE: + if (expr_basetype(i->key)->type != TYPE_STRING) + break; + /* string type, check if its a range (wildcard). */ + /* fall-through */ + case EXPR_PREFIX: + case EXPR_RANGE: + range_expr_value_low(val, i); + if (left && mpz_cmp(left->key->value, val)) + break; + + range_expr_value_high(val, i); + if (right && mpz_cmp(right->key->value, val)) + break; + + range = expr_clone(i->key); + goto out; + default: + break; + } + } +out: + mpz_clear(val); + + return range; +} + +static struct expr *expr_value(struct expr *expr) +{ + switch (expr->etype) { + case EXPR_MAPPING: + return expr->left->key; + case EXPR_SET_ELEM: + return expr->key; + case EXPR_VALUE: + return expr; + default: + BUG("invalid expression type %s\n", expr_name(expr)); + } +} + +static struct expr *__expr_to_set_elem(struct expr *low, struct expr *expr) +{ + struct expr *elem = set_elem_expr_alloc(&low->location, expr); + + if (low->etype == EXPR_MAPPING) { + interval_expr_copy(elem, low->left); + + elem = mapping_expr_alloc(&low->location, elem, + expr_clone(low->right)); + } else { + interval_expr_copy(elem, low); + } + elem->flags |= EXPR_F_KERNEL; + + return elem; +} + +static struct expr *expr_to_set_elem(struct expr *e) +{ + unsigned int len = div_round_up(e->len, BITS_PER_BYTE); + unsigned int str_len; + char data[len + 1]; + struct expr *expr; + + if (expr_basetype(expr_value(e))->type != TYPE_STRING) + return expr_clone(e); + + mpz_export_data(data, expr_value(e)->value, BYTEORDER_BIG_ENDIAN, len); + + str_len = strnlen(data, len); + if (str_len >= len || str_len == 0) + return expr_clone(e); + + data[str_len] = '*'; + + expr = constant_expr_alloc(&e->location, e->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + + return __expr_to_set_elem(e, expr); +} + +int get_set_decompose(struct set *cache_set, struct set *set) +{ + struct expr *i, *next, *range; + struct expr *left = NULL; + struct expr *new_init; + + new_init = set_expr_alloc(&internal_location, set); + + list_for_each_entry_safe(i, next, &set->init->expressions, list) { + if (i->flags & EXPR_F_INTERVAL_END && left) { + list_del(&left->list); + list_del(&i->list); + mpz_sub_ui(i->key->value, i->key->value, 1); + range = get_set_interval_find(cache_set, left, i); + if (!range) { + expr_free(left); + expr_free(i); + expr_free(new_init); + errno = ENOENT; + return -1; + } + expr_free(left); + expr_free(i); + + compound_expr_add(new_init, range); + left = NULL; + } else { + if (left) { + range = get_set_interval_find(cache_set, + left, NULL); + if (range) + compound_expr_add(new_init, range); + else + compound_expr_add(new_init, + expr_to_set_elem(left)); + } + left = i; + } + } + if (left) { + range = get_set_interval_find(cache_set, left, NULL); + if (range) + compound_expr_add(new_init, range); + else + compound_expr_add(new_init, expr_to_set_elem(left)); + } + + expr_free(set->init); + set->init = new_init; + + return 0; +} + +static bool range_is_prefix(const mpz_t range) +{ + mpz_t tmp; + bool ret; + + mpz_init_set(tmp, range); + mpz_add_ui(tmp, tmp, 1); + mpz_and(tmp, range, tmp); + ret = !mpz_cmp_ui(tmp, 0); + mpz_clear(tmp); + return ret; +} + +static int expr_value_cmp(const void *p1, const void *p2) +{ + struct expr *e1 = *(void * const *)p1; + struct expr *e2 = *(void * const *)p2; + int ret; + + if (expr_value(e1)->etype == EXPR_CONCAT) + return -1; + + ret = mpz_cmp(expr_value(e1)->value, expr_value(e2)->value); + if (ret == 0) { + if (e1->flags & EXPR_F_INTERVAL_END) + return -1; + else if (e2->flags & EXPR_F_INTERVAL_END) + return 1; + } + + return ret; +} + +/* Given start and end elements of a range, check if it can be represented as + * a single netmask, and if so, how long, by returning zero or a positive value. + */ +static int range_mask_len(const mpz_t start, const mpz_t end, unsigned int len) +{ + mpz_t tmp_start, tmp_end; + int ret; + + mpz_init_set(tmp_start, start); + mpz_init_set(tmp_end, end); + + while (mpz_cmp(tmp_start, tmp_end) <= 0 && + !mpz_tstbit(tmp_start, 0) && mpz_tstbit(tmp_end, 0) && + len--) { + mpz_fdiv_q_2exp(tmp_start, tmp_start, 1); + mpz_fdiv_q_2exp(tmp_end, tmp_end, 1); + } + + ret = !mpz_cmp(tmp_start, tmp_end) ? (int)len : -1; + + mpz_clear(tmp_start); + mpz_clear(tmp_end); + + return ret; +} + +/* Given a set with two elements (start and end), transform them into a + * concatenation of ranges. That is, from a list of start expressions and a list + * of end expressions, form a list of start - end expressions. + */ +void concat_range_aggregate(struct expr *set) +{ + struct expr *i, *start = NULL, *end, *r1, *r2, *next, *r1_next, *tmp; + struct list_head *r2_next; + int prefix_len, free_r1; + mpz_t range, p; + + list_for_each_entry_safe(i, next, &set->expressions, list) { + if (!start) { + start = i; + continue; + } + end = i; + + /* Walk over r1 (start expression) and r2 (end) in parallel, + * form ranges between corresponding r1 and r2 expressions, + * store them by replacing r2 expressions, and free r1 + * expressions. + */ + r2 = list_first_entry(&expr_value(end)->expressions, + struct expr, list); + list_for_each_entry_safe(r1, r1_next, + &expr_value(start)->expressions, + list) { + bool string_type = false; + + mpz_init(range); + mpz_init(p); + + r2_next = r2->list.next; + free_r1 = 0; + + if (!mpz_cmp(r1->value, r2->value)) { + free_r1 = 1; + goto next; + } + + if (expr_basetype(r1)->type == TYPE_STRING && + expr_basetype(r2)->type == TYPE_STRING) { + string_type = true; + mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE); + mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE); + } + + mpz_sub(range, r2->value, r1->value); + mpz_sub_ui(range, range, 1); + mpz_and(p, r1->value, range); + + /* Check if we are forced, or if it's anyway preferable, + * to express the range as a wildcard string, or two points + * instead of a netmask. + */ + prefix_len = range_mask_len(r1->value, r2->value, + r1->len); + if (string_type) { + mpz_switch_byteorder(r1->value, r1->len / BITS_PER_BYTE); + mpz_switch_byteorder(r2->value, r2->len / BITS_PER_BYTE); + } + + if (prefix_len >= 0 && + (prefix_len % BITS_PER_BYTE) == 0 && + string_type) { + unsigned int str_len = prefix_len / BITS_PER_BYTE; + char data[str_len + 2]; + + mpz_export_data(data, r1->value, BYTEORDER_HOST_ENDIAN, str_len); + data[str_len] = '*'; + + tmp = constant_expr_alloc(&r1->location, r1->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + tmp->len = r2->len; + list_replace(&r2->list, &tmp->list); + r2_next = tmp->list.next; + expr_free(r2); + free_r1 = 1; + goto next; + } + + if (prefix_len < 0 || + !(r1->dtype->flags & DTYPE_F_PREFIX)) { + tmp = range_expr_alloc(&r1->location, r1, + r2); + + list_replace(&r2->list, &tmp->list); + r2_next = tmp->list.next; + } else { + tmp = prefix_expr_alloc(&r1->location, r1, + prefix_len); + tmp->len = r2->len; + + list_replace(&r2->list, &tmp->list); + r2_next = tmp->list.next; + expr_free(r2); + } + +next: + mpz_clear(p); + mpz_clear(range); + + r2 = list_entry(r2_next, typeof(*r2), list); + compound_expr_remove(start, r1); + + if (free_r1) + expr_free(r1); + } + + compound_expr_remove(set, start); + expr_free(start); + start = NULL; + } +} + +static struct expr *interval_to_prefix(struct expr *low, struct expr *i, const mpz_t range) +{ + unsigned int prefix_len; + struct expr *prefix; + + prefix_len = expr_value(i)->len - mpz_scan0(range, 0); + prefix = prefix_expr_alloc(&low->location, + expr_clone(expr_value(low)), + prefix_len); + prefix->len = expr_value(i)->len; + + return __expr_to_set_elem(low, prefix); +} + +static struct expr *interval_to_string(struct expr *low, struct expr *i, const mpz_t range) +{ + unsigned int len = div_round_up(i->len, BITS_PER_BYTE); + unsigned int prefix_len, str_len; + char data[len + 2]; + struct expr *expr; + + prefix_len = expr_value(i)->len - mpz_scan0(range, 0); + + if (prefix_len > i->len || prefix_len % BITS_PER_BYTE) + return interval_to_prefix(low, i, range); + + mpz_export_data(data, expr_value(low)->value, BYTEORDER_BIG_ENDIAN, len); + + str_len = strnlen(data, len); + if (str_len >= len || str_len == 0) + return interval_to_prefix(low, i, range); + + data[str_len] = '*'; + + expr = constant_expr_alloc(&low->location, low->dtype, + BYTEORDER_HOST_ENDIAN, + (str_len + 1) * BITS_PER_BYTE, data); + + return __expr_to_set_elem(low, expr); +} + +static struct expr *interval_to_range(struct expr *low, struct expr *i, mpz_t range) +{ + struct expr *tmp; + + tmp = constant_expr_alloc(&low->location, low->dtype, + low->byteorder, expr_value(low)->len, + NULL); + + mpz_add(range, range, expr_value(low)->value); + mpz_set(tmp->value, range); + + tmp = range_expr_alloc(&low->location, + expr_clone(expr_value(low)), + tmp); + + return __expr_to_set_elem(low, tmp); +} + +static void +add_interval(struct expr *set, struct expr *low, struct expr *i) +{ + struct expr *expr; + mpz_t range, p; + + mpz_init(range); + mpz_init(p); + + mpz_sub(range, expr_value(i)->value, expr_value(low)->value); + if (i->etype != EXPR_VALUE) + mpz_sub_ui(range, range, 1); + + mpz_and(p, expr_value(low)->value, range); + + if (!mpz_cmp_ui(range, 0)) { + if (expr_basetype(low)->type == TYPE_STRING) + mpz_switch_byteorder(expr_value(low)->value, + expr_value(low)->len / BITS_PER_BYTE); + low->flags |= EXPR_F_KERNEL; + expr = expr_get(low); + } else if (range_is_prefix(range) && !mpz_cmp_ui(p, 0)) { + + if (i->dtype->flags & DTYPE_F_PREFIX) + expr = interval_to_prefix(low, i, range); + else if (expr_basetype(i)->type == TYPE_STRING) + expr = interval_to_string(low, i, range); + else + expr = interval_to_range(low, i, range); + } else + expr = interval_to_range(low, i, range); + + compound_expr_add(set, expr); + + mpz_clear(range); + mpz_clear(p); +} + +void interval_map_decompose(struct expr *set) +{ + struct expr *i, *next, *low = NULL, *end, *catchall = NULL, *key; + struct expr **elements, **ranges; + unsigned int n, m, size; + bool interval; + + if (set->size == 0) + return; + + elements = xmalloc_array(set->size, sizeof(struct expr *)); + ranges = xmalloc_array(set->size * 2, sizeof(struct expr *)); + + /* Sort elements */ + n = 0; + list_for_each_entry_safe(i, next, &set->expressions, list) { + key = NULL; + if (i->etype == EXPR_SET_ELEM) + key = i->key; + else if (i->etype == EXPR_MAPPING) + key = i->left->key; + + if (key && key->etype == EXPR_SET_ELEM_CATCHALL) { + list_del(&i->list); + catchall = i; + continue; + } + compound_expr_remove(set, i); + elements[n++] = i; + } + qsort(elements, n, sizeof(elements[0]), expr_value_cmp); + size = n; + + /* Transform points (single values) into half-closed intervals */ + n = 0; + interval = false; + for (m = 0; m < size; m++) { + i = elements[m]; + + if (i->flags & EXPR_F_INTERVAL_END) + interval = false; + else if (interval) { + end = expr_clone(i); + end->flags |= EXPR_F_INTERVAL_END; + ranges[n++] = end; + } else + interval = true; + + ranges[n++] = i; + } + size = n; + + 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; + } + } + + add_interval(set, low, i); + + if (i->flags & EXPR_F_INTERVAL_END) { + expr_free(low); + low = NULL; + } + expr_free(i); + } + + if (!low) /* no unclosed interval at end */ + goto out; + + i = constant_expr_alloc(&low->location, low->dtype, + low->byteorder, expr_value(low)->len, NULL); + mpz_bitmask(i->value, i->len); + + if (!mpz_cmp(i->value, expr_value(low)->value)) { + compound_expr_add(set, low); + } else { + add_interval(set, low, i); + expr_free(low); + } + + expr_free(i); + +out: + if (catchall) + compound_expr_add(set, catchall); + + xfree(ranges); + xfree(elements); +} diff --git a/src/socket.c b/src/socket.c new file mode 100644 index 0000000..8a149e6 --- /dev/null +++ b/src/socket.c @@ -0,0 +1,136 @@ +/* + * Socket expression/statement related definition and types. + * + * Copyright (c) 2018 Máté Eckl <ecklm94@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <expression.h> +#include <socket.h> +#include <json.h> + +const struct socket_template socket_templates[] = { + [NFT_SOCKET_TRANSPARENT] = { + .token = "transparent", + .dtype = &integer_type, + .len = BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + }, + [NFT_SOCKET_MARK] = { + .token = "mark", + .dtype = &mark_type, + .len = 4 * BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + }, + [NFT_SOCKET_WILDCARD] = { + .token = "wildcard", + .dtype = &integer_type, + .len = BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + }, + [NFT_SOCKET_CGROUPV2] = { + .token = "cgroupv2", + .dtype = &cgroupv2_type, + .len = 8 * BITS_PER_BYTE, + .byteorder = BYTEORDER_HOST_ENDIAN, + }, +}; + +static void socket_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + nft_print(octx, "socket %s", socket_templates[expr->socket.key].token); + if (expr->socket.key == NFT_SOCKET_CGROUPV2) + nft_print(octx, " level %u", expr->socket.level); +} + +static bool socket_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->socket.key == e2->socket.key && + e1->socket.level == e2->socket.level; +} + +static void socket_expr_clone(struct expr *new, const struct expr *expr) +{ + new->socket.key = expr->socket.key; + new->socket.level = expr->socket.level; +} + +#define NFTNL_UDATA_SOCKET_KEY 0 +#define NFTNL_UDATA_SOCKET_MAX 1 + +static int socket_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_SOCKET_KEY, expr->socket.key); + + return 0; +} + +static int socket_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SOCKET_KEY: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *socket_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SOCKET_MAX + 1] = {}; + uint32_t key; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + socket_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_SOCKET_KEY]) + return NULL; + + key = nftnl_udata_get_u32(ud[NFTNL_UDATA_SOCKET_KEY]); + + return socket_expr_alloc(&internal_location, key, 0); +} + +const struct expr_ops socket_expr_ops = { + .type = EXPR_SOCKET, + .name = "socket", + .print = socket_expr_print, + .json = socket_expr_json, + .cmp = socket_expr_cmp, + .clone = socket_expr_clone, + .build_udata = socket_expr_build_udata, + .parse_udata = socket_expr_parse_udata, +}; + +struct expr *socket_expr_alloc(const struct location *loc, + enum nft_socket_keys key, uint32_t level) +{ + const struct socket_template *tmpl = &socket_templates[key]; + struct expr *expr; + + expr = expr_alloc(loc, EXPR_SOCKET, tmpl->dtype, + tmpl->byteorder, tmpl->len); + expr->socket.key = key; + expr->socket.level = level; + + return expr; +} diff --git a/src/statement.c b/src/statement.c new file mode 100644 index 0000000..4756116 --- /dev/null +++ b/src/statement.c @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <inttypes.h> +#include <syslog.h> +#include <rule.h> + +#include <arpa/inet.h> +#include <linux/netfilter.h> +#include <linux/netfilter/nf_log.h> +#include <netinet/ip_icmp.h> +#include <netinet/icmp6.h> +#include <statement.h> +#include <tcpopt.h> +#include <utils.h> +#include <list.h> +#include <xt.h> +#include <json.h> + +#include <netinet/in.h> +#include <linux/netfilter/nf_nat.h> +#include <linux/netfilter/nf_log.h> +#include <linux/netfilter/nf_synproxy.h> + +struct stmt *stmt_alloc(const struct location *loc, + const struct stmt_ops *ops) +{ + struct stmt *stmt; + + stmt = xzalloc(sizeof(*stmt)); + init_list_head(&stmt->list); + stmt->location = *loc; + stmt->ops = ops; + return stmt; +} + +void stmt_free(struct stmt *stmt) +{ + if (stmt == NULL) + return; + 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, struct output_ctx *octx) +{ + stmt->ops->print(stmt, octx); +} + +static void expr_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + expr_print(stmt->expr, octx); +} + +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, + .json = expr_stmt_json, + .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, + .json = verdict_stmt_json, + .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 const char *chain_verdict(const struct expr *expr) +{ + switch (expr->verdict) { + case NFT_JUMP: + return "jump"; + case NFT_GOTO: + return "goto"; + default: + BUG("unknown chain verdict"); + } +} + +static void chain_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "%s {\n", chain_verdict(stmt->chain.expr)); + chain_rules_print(stmt->chain.chain, octx, "\t"); + nft_print(octx, "\t\t}"); +} + +static void chain_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->chain.expr); +} + +static const struct stmt_ops chain_stmt_ops = { + .type = STMT_CHAIN, + .name = "chain", + .print = chain_stmt_print, + .destroy = chain_stmt_destroy, +}; + +struct stmt *chain_stmt_alloc(const struct location *loc, struct chain *chain, + enum nft_verdicts verdict) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &chain_stmt_ops); + stmt->chain.chain = chain; + stmt->chain.expr = verdict_expr_alloc(loc, verdict, NULL); + stmt->chain.expr->chain_id = chain->handle.chain_id; + + return stmt; +} + +static void meter_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + + nft_print(octx, "meter "); + if (stmt->meter.set) { + expr_print(stmt->meter.set, octx); + nft_print(octx, " "); + } + nft_print(octx, "size %u { ", stmt->meter.size); + expr_print(stmt->meter.key, octx); + nft_print(octx, " "); + + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + stmt_print(stmt->meter.stmt, octx); + octx->flags = flags; + + nft_print(octx, " }"); + +} + +static void meter_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->meter.key); + expr_free(stmt->meter.set); + stmt_free(stmt->meter.stmt); + xfree(stmt->meter.name); +} + +static const struct stmt_ops meter_stmt_ops = { + .type = STMT_METER, + .name = "meter", + .print = meter_stmt_print, + .json = meter_stmt_json, + .destroy = meter_stmt_destroy, +}; + +struct stmt *meter_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &meter_stmt_ops); +} + +static void connlimit_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "ct count %s%u", + stmt->connlimit.flags ? "over " : "", stmt->connlimit.count); +} + +static const struct stmt_ops connlimit_stmt_ops = { + .type = STMT_CONNLIMIT, + .name = "connlimit", + .print = connlimit_stmt_print, + .json = connlimit_stmt_json, +}; + +struct stmt *connlimit_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &connlimit_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + +static void counter_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "counter"); + + if (nft_output_stateless(octx)) + return; + + nft_print(octx, " 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, + .json = counter_stmt_json, +}; + +struct stmt *counter_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &counter_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + +static void last_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "last"); + + if (nft_output_stateless(octx)) + return; + + nft_print(octx, " used "); + + if (stmt->last.set) + time_print(stmt->last.used, octx); + else + nft_print(octx, "never"); +} + +static const struct stmt_ops last_stmt_ops = { + .type = STMT_LAST, + .name = "last", + .print = last_stmt_print, + .json = last_stmt_json, +}; + +struct stmt *last_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &last_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + +static const char *objref_type[NFT_OBJECT_MAX + 1] = { + [NFT_OBJECT_COUNTER] = "counter", + [NFT_OBJECT_QUOTA] = "quota", + [NFT_OBJECT_CT_HELPER] = "ct helper", + [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_CT_TIMEOUT] = "ct timeout", + [NFT_OBJECT_SECMARK] = "secmark", + [NFT_OBJECT_SYNPROXY] = "synproxy", + [NFT_OBJECT_CT_EXPECT] = "ct expectation", +}; + +const char *objref_type_name(uint32_t type) +{ + if (type > NFT_OBJECT_MAX) + return "unknown"; + + return objref_type[type]; +} + +static void objref_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + switch (stmt->objref.type) { + case NFT_OBJECT_CT_HELPER: + nft_print(octx, "ct helper set "); + break; + case NFT_OBJECT_CT_TIMEOUT: + nft_print(octx, "ct timeout set "); + break; + case NFT_OBJECT_CT_EXPECT: + nft_print(octx, "ct expectation set "); + break; + case NFT_OBJECT_SECMARK: + nft_print(octx, "meta secmark set "); + break; + default: + nft_print(octx, "%s name ", + objref_type_name(stmt->objref.type)); + break; + } + expr_print(stmt->objref.expr, octx); +} + +static void objref_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->objref.expr); +} + +static const struct stmt_ops objref_stmt_ops = { + .type = STMT_OBJREF, + .name = "objref", + .print = objref_stmt_print, + .json = objref_stmt_json, + .destroy = objref_stmt_destroy, +}; + +struct stmt *objref_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &objref_stmt_ops); + return stmt; +} + +static const char *syslog_level[NFT_LOGLEVEL_MAX + 1] = { + [NFT_LOGLEVEL_EMERG] = "emerg", + [NFT_LOGLEVEL_ALERT] = "alert", + [NFT_LOGLEVEL_CRIT] = "crit", + [NFT_LOGLEVEL_ERR] = "err", + [NFT_LOGLEVEL_WARNING] = "warn", + [NFT_LOGLEVEL_NOTICE] = "notice", + [NFT_LOGLEVEL_INFO] = "info", + [NFT_LOGLEVEL_DEBUG] = "debug", + [NFT_LOGLEVEL_AUDIT] = "audit" +}; + +const char *log_level(uint32_t level) +{ + if (level > NFT_LOGLEVEL_MAX) + return "unknown"; + + return syslog_level[level]; +} + +int log_level_parse(const char *level) +{ + int i; + + for (i = 0; i <= NFT_LOGLEVEL_MAX; i++) { + if (syslog_level[i] && + !strcmp(level, syslog_level[i])) + return i; + } + return -1; +} + +static void log_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "log"); + if (stmt->log.flags & STMT_LOG_PREFIX) { + char prefix[NF_LOG_PREFIXLEN] = {}; + + expr_to_string(stmt->log.prefix, prefix); + nft_print(octx, " prefix \"%s\"", prefix); + } + if (stmt->log.flags & STMT_LOG_GROUP) + nft_print(octx, " group %u", stmt->log.group); + if (stmt->log.flags & STMT_LOG_SNAPLEN) + nft_print(octx, " snaplen %u", stmt->log.snaplen); + if (stmt->log.flags & STMT_LOG_QTHRESHOLD) + nft_print(octx, " queue-threshold %u", stmt->log.qthreshold); + if ((stmt->log.flags & STMT_LOG_LEVEL) && + stmt->log.level != LOG_WARNING) + nft_print(octx, " level %s", log_level(stmt->log.level)); + + if ((stmt->log.logflags & NF_LOG_MASK) == NF_LOG_MASK) { + nft_print(octx, " flags all"); + } else { + if (stmt->log.logflags & (NF_LOG_TCPSEQ | NF_LOG_TCPOPT)) { + const char *delim = " "; + + nft_print(octx, " flags tcp"); + if (stmt->log.logflags & NF_LOG_TCPSEQ) { + nft_print(octx, " sequence"); + delim = ","; + } + if (stmt->log.logflags & NF_LOG_TCPOPT) + nft_print(octx, "%soptions", + delim); + } + if (stmt->log.logflags & NF_LOG_IPOPT) + nft_print(octx, " flags ip options"); + if (stmt->log.logflags & NF_LOG_UID) + nft_print(octx, " flags skuid"); + if (stmt->log.logflags & NF_LOG_MACDECODE) + nft_print(octx, " flags ether"); + } +} + +static void log_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->log.prefix); +} + +static const struct stmt_ops log_stmt_ops = { + .type = STMT_LOG, + .name = "log", + .print = log_stmt_print, + .json = log_stmt_json, + .destroy = log_stmt_destroy, +}; + +struct stmt *log_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &log_stmt_ops); +} + +const char *get_unit(uint64_t u) +{ + switch (u) { + case 1: return "second"; + case 60: return "minute"; + case 60 * 60: return "hour"; + case 60 * 60 * 24: return "day"; + case 60 * 60 * 24 * 7: return "week"; + } + + return "error"; +} + +static const char * const data_unit[] = { + "bytes", + "kbytes", + "mbytes", + NULL +}; + +const char *get_rate(uint64_t byte_rate, uint64_t *rate) +{ + int i; + + if (!byte_rate) { + *rate = 0; + return data_unit[0]; + } + + for (i = 0; data_unit[i + 1] != NULL; i++) { + if (byte_rate % 1024) + break; + byte_rate /= 1024; + } + + *rate = byte_rate; + return data_unit[i]; +} + +static void limit_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + bool inv = stmt->limit.flags & NFT_LIMIT_F_INV; + const char *data_unit; + uint64_t rate; + + switch (stmt->limit.type) { + case NFT_LIMIT_PKTS: + nft_print(octx, "limit rate %s%" PRIu64 "/%s", + inv ? "over " : "", stmt->limit.rate, + get_unit(stmt->limit.unit)); + nft_print(octx, " burst %u packets", stmt->limit.burst); + break; + case NFT_LIMIT_PKT_BYTES: + data_unit = get_rate(stmt->limit.rate, &rate); + + nft_print(octx, "limit rate %s%" PRIu64 " %s/%s", + inv ? "over " : "", rate, data_unit, + get_unit(stmt->limit.unit)); + if (stmt->limit.burst != 0) { + uint64_t burst; + + data_unit = get_rate(stmt->limit.burst, &burst); + nft_print(octx, " burst %" PRIu64 " %s", burst, + data_unit); + } + break; + } +} + +static const struct stmt_ops limit_stmt_ops = { + .type = STMT_LIMIT, + .name = "limit", + .print = limit_stmt_print, + .json = limit_stmt_json, +}; + +struct stmt *limit_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &limit_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + +static void queue_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + struct expr *e = stmt->queue.queue; + const char *delim = " flags "; + + nft_print(octx, "queue"); + + if (stmt->queue.flags & NFT_QUEUE_FLAG_BYPASS) { + nft_print(octx, "%sbypass", delim); + delim = ","; + } + + if (stmt->queue.flags & NFT_QUEUE_FLAG_CPU_FANOUT) + nft_print(octx, "%sfanout", delim); + + if (e) { + nft_print(octx, " to "); + expr_print(stmt->queue.queue, octx); + } else { + nft_print(octx, " to 0"); + } +} + +static void queue_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->queue.queue); +} + +static const struct stmt_ops queue_stmt_ops = { + .type = STMT_QUEUE, + .name = "queue", + .print = queue_stmt_print, + .json = queue_stmt_json, + .destroy = queue_stmt_destroy, +}; + +struct stmt *queue_stmt_alloc(const struct location *loc, struct expr *e, uint16_t flags) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &queue_stmt_ops); + stmt->queue.queue = e; + stmt->queue.flags = flags; + + return stmt; +} + +static void quota_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + bool inv = stmt->quota.flags & NFT_QUOTA_F_INV; + const char *data_unit; + uint64_t bytes, used; + + data_unit = get_rate(stmt->quota.bytes, &bytes); + nft_print(octx, "quota %s%" PRIu64 " %s", + inv ? "over " : "", bytes, data_unit); + + if (!nft_output_stateless(octx) && stmt->quota.used) { + data_unit = get_rate(stmt->quota.used, &used); + nft_print(octx, " used %" PRIu64 " %s", used, data_unit); + } +} + +static const struct stmt_ops quota_stmt_ops = { + .type = STMT_QUOTA, + .name = "quota", + .print = quota_stmt_print, + .json = quota_stmt_json, +}; + +struct stmt *quota_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, "a_stmt_ops); + stmt->flags |= STMT_F_STATEFUL; + return stmt; +} + +static void reject_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "reject"); + switch (stmt->reject.type) { + case NFT_REJECT_TCP_RST: + nft_print(octx, " with tcp reset"); + break; + case NFT_REJECT_ICMPX_UNREACH: + if (stmt->reject.icmp_code == NFT_REJECT_ICMPX_PORT_UNREACH) + break; + nft_print(octx, " with icmpx "); + expr_print(stmt->reject.expr, octx); + break; + case NFT_REJECT_ICMP_UNREACH: + switch (stmt->reject.family) { + case NFPROTO_IPV4: + if (!stmt->reject.verbose_print && + stmt->reject.icmp_code == ICMP_PORT_UNREACH) + break; + nft_print(octx, " with icmp "); + expr_print(stmt->reject.expr, octx); + break; + case NFPROTO_IPV6: + if (!stmt->reject.verbose_print && + stmt->reject.icmp_code == ICMP6_DST_UNREACH_NOPORT) + break; + nft_print(octx, " with icmpv6 "); + expr_print(stmt->reject.expr, octx); + break; + } + break; + } +} + +static void reject_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->reject.expr); +} + +static const struct stmt_ops reject_stmt_ops = { + .type = STMT_REJECT, + .name = "reject", + .print = reject_stmt_print, + .json = reject_stmt_json, + .destroy = reject_stmt_destroy, +}; + +struct stmt *reject_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &reject_stmt_ops); +} + +static void print_nf_nat_flags(uint32_t flags, struct output_ctx *octx) +{ + const char *delim = " "; + + if (flags == 0) + return; + + if (flags & NF_NAT_RANGE_PROTO_RANDOM) { + nft_print(octx, "%srandom", delim); + delim = ","; + } + + if (flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) { + nft_print(octx, "%sfully-random", delim); + delim = ","; + } + + if (flags & NF_NAT_RANGE_PERSISTENT) + nft_print(octx, "%spersistent", delim); +} + +const char *nat_etype2str(enum nft_nat_etypes type) +{ + static const char * const nat_types[] = { + [NFT_NAT_SNAT] = "snat", + [NFT_NAT_DNAT] = "dnat", + [NFT_NAT_MASQ] = "masquerade", + [NFT_NAT_REDIR] = "redirect", + }; + + return nat_types[type]; +} + +static void nat_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "%s", nat_etype2str(stmt->nat.type)); + if (stmt->nat.addr || stmt->nat.proto) { + switch (stmt->nat.family) { + case NFPROTO_IPV4: + nft_print(octx, " ip"); + break; + case NFPROTO_IPV6: + nft_print(octx, " ip6"); + break; + } + + if (stmt->nat.type_flags & STMT_NAT_F_PREFIX) + nft_print(octx, " prefix"); + + nft_print(octx, " to"); + } + + if (stmt->nat.addr) { + nft_print(octx, " "); + if (stmt->nat.proto) { + if (stmt->nat.addr->etype == EXPR_VALUE && + stmt->nat.addr->dtype->type == TYPE_IP6ADDR) { + nft_print(octx, "["); + expr_print(stmt->nat.addr, octx); + nft_print(octx, "]"); + } else if (stmt->nat.addr->etype == EXPR_RANGE && + stmt->nat.addr->left->dtype->type == TYPE_IP6ADDR) { + nft_print(octx, "["); + expr_print(stmt->nat.addr->left, octx); + nft_print(octx, "]-["); + expr_print(stmt->nat.addr->right, octx); + nft_print(octx, "]"); + } else { + expr_print(stmt->nat.addr, octx); + } + } else { + expr_print(stmt->nat.addr, octx); + } + } + + if (stmt->nat.proto) { + if (!stmt->nat.addr) + nft_print(octx, " "); + nft_print(octx, ":"); + expr_print(stmt->nat.proto, octx); + } + + print_nf_nat_flags(stmt->nat.flags, octx); +} + +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, + .json = nat_stmt_json, + .destroy = nat_stmt_destroy, +}; + +struct stmt *nat_stmt_alloc(const struct location *loc, + enum nft_nat_etypes type) +{ + struct stmt *stmt = stmt_alloc(loc, &nat_stmt_ops); + + stmt->nat.type = type; + return stmt; +} + +const char * const set_stmt_op_names[] = { + [NFT_DYNSET_OP_ADD] = "add", + [NFT_DYNSET_OP_UPDATE] = "update", + [NFT_DYNSET_OP_DELETE] = "delete", +}; + +static void set_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + struct stmt *this; + + nft_print(octx, "%s ", set_stmt_op_names[stmt->set.op]); + expr_print(stmt->set.set, octx); + nft_print(octx, " { "); + expr_print(stmt->set.key, octx); + list_for_each_entry(this, &stmt->set.stmt_list, list) { + nft_print(octx, " "); + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + stmt_print(this, octx); + octx->flags = flags; + } + nft_print(octx, " }"); +} + +static void set_stmt_destroy(struct stmt *stmt) +{ + struct stmt *this, *next; + + expr_free(stmt->set.key); + expr_free(stmt->set.set); + list_for_each_entry_safe(this, next, &stmt->set.stmt_list, list) + stmt_free(this); +} + +static const struct stmt_ops set_stmt_ops = { + .type = STMT_SET, + .name = "set", + .print = set_stmt_print, + .json = set_stmt_json, + .destroy = set_stmt_destroy, +}; + +struct stmt *set_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &set_stmt_ops); + init_list_head(&stmt->set.stmt_list); + + return stmt; +} + +static void map_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + unsigned int flags = octx->flags; + struct stmt *this; + + nft_print(octx, "%s ", set_stmt_op_names[stmt->map.op]); + expr_print(stmt->map.set, octx); + nft_print(octx, " { "); + expr_print(stmt->map.key, octx); + list_for_each_entry(this, &stmt->map.stmt_list, list) { + nft_print(octx, " "); + octx->flags |= NFT_CTX_OUTPUT_STATELESS; + stmt_print(this, octx); + octx->flags = flags; + } + nft_print(octx, " : "); + expr_print(stmt->map.data, octx); + nft_print(octx, " }"); +} + +static void map_stmt_destroy(struct stmt *stmt) +{ + struct stmt *this, *next; + + expr_free(stmt->map.key); + expr_free(stmt->map.data); + expr_free(stmt->map.set); + list_for_each_entry_safe(this, next, &stmt->map.stmt_list, list) + stmt_free(this); +} + +static const struct stmt_ops map_stmt_ops = { + .type = STMT_MAP, + .name = "map", + .print = map_stmt_print, + .destroy = map_stmt_destroy, + .json = map_stmt_json, +}; + +struct stmt *map_stmt_alloc(const struct location *loc) +{ + struct stmt *stmt; + + stmt = stmt_alloc(loc, &map_stmt_ops); + init_list_head(&stmt->map.stmt_list); + + return stmt; +} + +static void dup_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "dup"); + if (stmt->dup.to != NULL) { + nft_print(octx, " to "); + expr_print(stmt->dup.to, octx); + + if (stmt->dup.dev != NULL) { + nft_print(octx, " device "); + expr_print(stmt->dup.dev, octx); + } + } +} + +static void dup_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->dup.to); + expr_free(stmt->dup.dev); +} + +static const struct stmt_ops dup_stmt_ops = { + .type = STMT_DUP, + .name = "dup", + .print = dup_stmt_print, + .json = dup_stmt_json, + .destroy = dup_stmt_destroy, +}; + +struct stmt *dup_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &dup_stmt_ops); +} + +static const char * const nfproto_family_name_array[NFPROTO_NUMPROTO] = { + [NFPROTO_IPV4] = "ip", + [NFPROTO_IPV6] = "ip6", +}; + +static const char *nfproto_family_name(uint8_t nfproto) +{ + if (nfproto >= NFPROTO_NUMPROTO || !nfproto_family_name_array[nfproto]) + return "unknown"; + + return nfproto_family_name_array[nfproto]; +} + +static void fwd_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + if (stmt->fwd.addr) { + nft_print(octx, "fwd %s to ", + nfproto_family_name(stmt->fwd.family)); + expr_print(stmt->fwd.addr, octx); + nft_print(octx, " device "); + expr_print(stmt->fwd.dev, octx); + } else { + nft_print(octx, "fwd to "); + expr_print(stmt->fwd.dev, octx); + } +} + +static void fwd_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->fwd.addr); + expr_free(stmt->fwd.dev); +} + +static const struct stmt_ops fwd_stmt_ops = { + .type = STMT_FWD, + .name = "fwd", + .print = fwd_stmt_print, + .json = fwd_stmt_json, + .destroy = fwd_stmt_destroy, +}; + +struct stmt *fwd_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &fwd_stmt_ops); +} + +static void optstrip_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + const struct expr *expr = stmt->optstrip.expr; + + nft_print(octx, "reset "); + expr_print(expr, octx); +} + +static void optstrip_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->optstrip.expr); +} + +static const struct stmt_ops optstrip_stmt_ops = { + .type = STMT_OPTSTRIP, + .name = "optstrip", + .print = optstrip_stmt_print, + .json = optstrip_stmt_json, + .destroy = optstrip_stmt_destroy, +}; + +struct stmt *optstrip_stmt_alloc(const struct location *loc, struct expr *e) +{ + struct stmt *stmt = stmt_alloc(loc, &optstrip_stmt_ops); + + e->exthdr.flags |= NFT_EXTHDR_F_PRESENT; + stmt->optstrip.expr = e; + + return stmt; +} + +static void tproxy_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + nft_print(octx, "tproxy"); + + if (stmt->tproxy.table_family == NFPROTO_INET && + stmt->tproxy.family != NFPROTO_UNSPEC) + nft_print(octx, " %s", nfproto_family_name(stmt->tproxy.family)); + nft_print(octx, " to"); + if (stmt->tproxy.addr) { + nft_print(octx, " "); + if (stmt->tproxy.addr->etype == EXPR_VALUE && + stmt->tproxy.addr->dtype->type == TYPE_IP6ADDR) { + nft_print(octx, "["); + expr_print(stmt->tproxy.addr, octx); + nft_print(octx, "]"); + } else { + expr_print(stmt->tproxy.addr, octx); + } + } + if (stmt->tproxy.port && stmt->tproxy.port->etype == EXPR_VALUE) { + if (!stmt->tproxy.addr) + nft_print(octx, " "); + nft_print(octx, ":"); + expr_print(stmt->tproxy.port, octx); + } +} + +static void tproxy_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->tproxy.addr); + expr_free(stmt->tproxy.port); +} + +static const struct stmt_ops tproxy_stmt_ops = { + .type = STMT_TPROXY, + .name = "tproxy", + .print = tproxy_stmt_print, + .json = tproxy_stmt_json, + .destroy = tproxy_stmt_destroy, +}; + +struct stmt *tproxy_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &tproxy_stmt_ops); +} + +static void xt_stmt_print(const struct stmt *stmt, struct output_ctx *octx) +{ + xt_stmt_xlate(stmt, octx); +} + +static const struct stmt_ops xt_stmt_ops = { + .type = STMT_XT, + .name = "xt", + .print = xt_stmt_print, + .destroy = xt_stmt_destroy, + .json = xt_stmt_json, +}; + +struct stmt *xt_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &xt_stmt_ops); +} + +static const char *synproxy_sack_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_SACK_PERM) + return " sack-perm"; + + return ""; +} + +static const char *synproxy_timestamp_to_str(const uint32_t flags) +{ + if (flags & NF_SYNPROXY_OPT_TIMESTAMP) + return " timestamp"; + + return ""; +} + +static void synproxy_stmt_print(const struct stmt *stmt, + struct output_ctx *octx) +{ + uint32_t flags = stmt->synproxy.flags; + const char *ts_str = synproxy_timestamp_to_str(flags); + const char *sack_str = synproxy_sack_to_str(flags); + + if (flags & (NF_SYNPROXY_OPT_MSS | NF_SYNPROXY_OPT_WSCALE)) + nft_print(octx, "synproxy mss %u wscale %u%s%s", + stmt->synproxy.mss, stmt->synproxy.wscale, + ts_str, sack_str); + else if (flags & NF_SYNPROXY_OPT_MSS) + nft_print(octx, "synproxy mss %u%s%s", stmt->synproxy.mss, + ts_str, sack_str); + else if (flags & NF_SYNPROXY_OPT_WSCALE) + nft_print(octx, "synproxy wscale %u%s%s", stmt->synproxy.wscale, + ts_str, sack_str); + else + nft_print(octx, "synproxy%s%s", ts_str, sack_str); + +} + +static const struct stmt_ops synproxy_stmt_ops = { + .type = STMT_SYNPROXY, + .name = "synproxy", + .print = synproxy_stmt_print, + .json = synproxy_stmt_json, +}; + +struct stmt *synproxy_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &synproxy_stmt_ops); +} diff --git a/src/tcpopt.c b/src/tcpopt.c new file mode 100644 index 0000000..3fcb273 --- /dev/null +++ b/src/tcpopt.c @@ -0,0 +1,266 @@ +#include <nft.h> + +#include <stddef.h> +#include <stdio.h> +#include <netinet/in.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> + +#include <utils.h> +#include <headers.h> +#include <expression.h> +#include <tcpopt.h> + +static const struct proto_hdr_template tcpopt_unknown_template = + PROTO_HDR_TEMPLATE("unknown", &invalid_type, BYTEORDER_INVALID, 0, 0); + +#define PHT(__token, __offset, __len) \ + PROTO_HDR_TEMPLATE(__token, &integer_type, BYTEORDER_BIG_ENDIAN, \ + __offset, __len) +static const struct exthdr_desc tcpopt_eol = { + .name = "eol", + .type = TCPOPT_KIND_EOL, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + }, +}; + +static const struct exthdr_desc tcpopt_nop = { + .name = "nop", + .type = TCPOPT_KIND_NOP, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + }, +}; + +static const struct exthdr_desc tcptopt_maxseg = { + .name = "maxseg", + .type = TCPOPT_KIND_MAXSEG, + .templates = { + [TCPOPT_MAXSEG_KIND] = PHT("kind", 0, 8), + [TCPOPT_MAXSEG_LENGTH] = PHT("length", 8, 8), + [TCPOPT_MAXSEG_SIZE] = PHT("size", 16, 16), + }, +}; + +static const struct exthdr_desc tcpopt_window = { + .name = "window", + .type = TCPOPT_KIND_WINDOW, + .templates = { + [TCPOPT_WINDOW_KIND] = PHT("kind", 0, 8), + [TCPOPT_WINDOW_LENGTH] = PHT("length", 8, 8), + [TCPOPT_WINDOW_COUNT] = PHT("count", 16, 8), + }, +}; + +static const struct exthdr_desc tcpopt_sack_permitted = { + .name = "sack-perm", + .type = TCPOPT_KIND_SACK_PERMITTED, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; + +static const struct exthdr_desc tcpopt_sack = { + .name = "sack", + .type = TCPOPT_KIND_SACK, + .templates = { + [TCPOPT_SACK_KIND] = PHT("kind", 0, 8), + [TCPOPT_SACK_LENGTH] = PHT("length", 8, 8), + [TCPOPT_SACK_LEFT] = PHT("left", 16, 32), + [TCPOPT_SACK_RIGHT] = PHT("right", 48, 32), + [TCPOPT_SACK_LEFT1] = PHT("left", 80, 32), + [TCPOPT_SACK_RIGHT1] = PHT("right", 112, 32), + [TCPOPT_SACK_LEFT2] = PHT("left", 144, 32), + [TCPOPT_SACK_RIGHT2] = PHT("right", 176, 32), + [TCPOPT_SACK_LEFT3] = PHT("left", 208, 32), + [TCPOPT_SACK_RIGHT3] = PHT("right", 240, 32), + }, +}; + +static const struct exthdr_desc tcpopt_timestamp = { + .name = "timestamp", + .type = TCPOPT_KIND_TIMESTAMP, + .templates = { + [TCPOPT_TS_KIND] = PHT("kind", 0, 8), + [TCPOPT_TS_LENGTH] = PHT("length", 8, 8), + [TCPOPT_TS_TSVAL] = PHT("tsval", 16, 32), + [TCPOPT_TS_TSECR] = PHT("tsecr", 48, 32), + }, +}; + +static const struct exthdr_desc tcpopt_fastopen = { + .name = "fastopen", + .type = TCPOPT_KIND_FASTOPEN, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; + +static const struct exthdr_desc tcpopt_md5sig = { + .name = "md5sig", + .type = TCPOPT_KIND_MD5SIG, + .templates = { + [TCPOPT_COMMON_KIND] = PHT("kind", 0, 8), + [TCPOPT_COMMON_LENGTH] = PHT("length", 8, 8), + }, +}; + + +static const struct exthdr_desc tcpopt_mptcp = { + .name = "mptcp", + .type = TCPOPT_KIND_MPTCP, + .templates = { + [TCPOPT_MPTCP_KIND] = PHT("kind", 0, 8), + [TCPOPT_MPTCP_LENGTH] = PHT("length", 8, 8), + [TCPOPT_MPTCP_SUBTYPE] = PHT("subtype", 16, 4), + }, +}; +#undef PHT + +const struct exthdr_desc *tcpopt_protocols[] = { + [TCPOPT_KIND_EOL] = &tcpopt_eol, + [TCPOPT_KIND_NOP] = &tcpopt_nop, + [TCPOPT_KIND_MAXSEG] = &tcptopt_maxseg, + [TCPOPT_KIND_WINDOW] = &tcpopt_window, + [TCPOPT_KIND_SACK_PERMITTED] = &tcpopt_sack_permitted, + [TCPOPT_KIND_SACK] = &tcpopt_sack, + [TCPOPT_KIND_TIMESTAMP] = &tcpopt_timestamp, + [TCPOPT_KIND_MD5SIG] = &tcpopt_md5sig, + [TCPOPT_KIND_MPTCP] = &tcpopt_mptcp, + [TCPOPT_KIND_FASTOPEN] = &tcpopt_fastopen, +}; + +/** + * tcpopt_expr_alloc - allocate tcp option extension expression + * + * @loc: location from parser + * @kind: raw tcp option value to find in packet + * @field: highlevel field to find in the option if @kind is present in packet + * + * Allocate a new tcp option expression. + * @kind is the raw option value to find in the packet. + * Exception: SACK may use extra OOB data that is mangled here. + * + * @field is the optional field to extract from the @type option. + */ +struct expr *tcpopt_expr_alloc(const struct location *loc, + unsigned int kind, + unsigned int field) +{ + const struct proto_hdr_template *tmpl; + const struct exthdr_desc *desc = NULL; + struct expr *expr; + + switch (kind) { + case TCPOPT_KIND_SACK1: + kind = TCPOPT_KIND_SACK; + if (field == TCPOPT_SACK_LEFT) + field = TCPOPT_SACK_LEFT1; + else if (field == TCPOPT_SACK_RIGHT) + field = TCPOPT_SACK_RIGHT1; + break; + case TCPOPT_KIND_SACK2: + kind = TCPOPT_KIND_SACK; + if (field == TCPOPT_SACK_LEFT) + field = TCPOPT_SACK_LEFT2; + else if (field == TCPOPT_SACK_RIGHT) + field = TCPOPT_SACK_RIGHT2; + break; + case TCPOPT_KIND_SACK3: + kind = TCPOPT_KIND_SACK; + if (field == TCPOPT_SACK_LEFT) + field = TCPOPT_SACK_LEFT3; + else if (field == TCPOPT_SACK_RIGHT) + field = TCPOPT_SACK_RIGHT3; + break; + } + + if (kind < array_size(tcpopt_protocols)) + desc = tcpopt_protocols[kind]; + + if (!desc) { + if (field != TCPOPT_COMMON_KIND || kind > 255) + return NULL; + + expr = expr_alloc(loc, EXPR_EXTHDR, &integer_type, + BYTEORDER_BIG_ENDIAN, 8); + + desc = tcpopt_protocols[TCPOPT_NOP]; + tmpl = &desc->templates[field]; + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; + expr->exthdr.raw_type = kind; + return expr; + } + + tmpl = &desc->templates[field]; + if (!tmpl) + return NULL; + + expr = expr_alloc(loc, EXPR_EXTHDR, tmpl->dtype, + BYTEORDER_BIG_ENDIAN, tmpl->len); + expr->exthdr.desc = desc; + expr->exthdr.tmpl = tmpl; + expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; + expr->exthdr.raw_type = desc->type; + expr->exthdr.offset = tmpl->offset; + + return expr; +} + +void tcpopt_init_raw(struct expr *expr, uint8_t type, unsigned int off, + unsigned int len, uint32_t flags) +{ + const struct proto_hdr_template *tmpl; + unsigned int i; + + assert(expr->etype == EXPR_EXTHDR); + + expr->len = len; + expr->exthdr.flags = flags; + expr->exthdr.offset = off; + expr->exthdr.op = NFT_EXTHDR_OP_TCPOPT; + expr->exthdr.tmpl = &tcpopt_unknown_template; + + if (flags & NFT_EXTHDR_F_PRESENT) + datatype_set(expr, &boolean_type); + else + datatype_set(expr, &integer_type); + + if (type >= array_size(tcpopt_protocols) || + !tcpopt_protocols[type]) + return; + + expr->exthdr.desc = tcpopt_protocols[type]; + expr->exthdr.flags = flags; + 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 != off || tmpl->len != len) + continue; + + if ((flags & NFT_EXTHDR_F_PRESENT) == 0) + datatype_set(expr, tmpl->dtype); + + expr->exthdr.tmpl = tmpl; + break; + } +} + +bool tcpopt_find_template(struct expr *expr, unsigned int offset, unsigned int len) +{ + if (expr->exthdr.tmpl != &tcpopt_unknown_template) + return false; + + tcpopt_init_raw(expr, expr->exthdr.desc->type, offset, len, 0); + + if (expr->exthdr.tmpl == &tcpopt_unknown_template) + return false; + + return true; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..e6ad8b8 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <unistd.h> + +#include <nftables.h> +#include <utils.h> + +void __noreturn __memory_allocation_error(const char *filename, uint32_t line) +{ + fprintf(stderr, "%s:%u: Memory allocation failure\n", filename, line); + 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 *xmalloc_array(size_t nmemb, size_t size) +{ + assert(size != 0); + assert(nmemb != 0); + + if (nmemb > SIZE_MAX / size) + memory_allocation_error(); + + return xmalloc(nmemb * size); +} + +void *xzalloc_array(size_t nmemb, size_t size) +{ + void *ptr; + + ptr = xmalloc_array(nmemb, size); + memset(ptr, 0, nmemb * size); + + return ptr; +} + +void *xrealloc(void *ptr, size_t size) +{ + 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; +} + +void xstrunescape(const char *in, char *out) +{ + unsigned int i, k = 0; + + for (i = 0; i < strlen(in); i++) { + if (in[i] == '\\') + continue; + + out[k++] = in[i]; + } + out[k++] = '\0'; +} + +int round_pow_2(unsigned int n) +{ + return 1UL << fls(n - 1); +} diff --git a/src/xfrm.c b/src/xfrm.c new file mode 100644 index 0000000..b32b2a1 --- /dev/null +++ b/src/xfrm.c @@ -0,0 +1,184 @@ +/* + * XFRM (ipsec) expression + * + * Copyright (c) Red Hat GmbH. Author: Florian Westphal <fw@strlen.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <nftables.h> +#include <erec.h> +#include <expression.h> +#include <xfrm.h> +#include <datatype.h> +#include <gmputil.h> +#include <utils.h> + +#include <netinet/ip.h> +#include <linux/netfilter.h> +#include <linux/xfrm.h> + +#define XFRM_TEMPLATE_BE(__token, __dtype, __len) { \ + .token = (__token), \ + .dtype = (__dtype), \ + .len = (__len), \ + .byteorder = BYTEORDER_BIG_ENDIAN, \ +} + +#define XFRM_TEMPLATE_HE(__token, __dtype, __len) { \ + .token = (__token), \ + .dtype = (__dtype), \ + .len = (__len), \ + .byteorder = BYTEORDER_HOST_ENDIAN, \ +} + +const struct xfrm_template xfrm_templates[] = { + [NFT_XFRM_KEY_DADDR_IP4] = XFRM_TEMPLATE_BE("daddr", &ipaddr_type, 4 * BITS_PER_BYTE), + [NFT_XFRM_KEY_SADDR_IP4] = XFRM_TEMPLATE_BE("saddr", &ipaddr_type, 4 * BITS_PER_BYTE), + [NFT_XFRM_KEY_DADDR_IP6] = XFRM_TEMPLATE_BE("daddr", &ip6addr_type, 16 * BITS_PER_BYTE), + [NFT_XFRM_KEY_SADDR_IP6] = XFRM_TEMPLATE_BE("saddr", &ip6addr_type, 16 * BITS_PER_BYTE), + [NFT_XFRM_KEY_REQID] = XFRM_TEMPLATE_HE("reqid", &integer_type, 4 * BITS_PER_BYTE), + [NFT_XFRM_KEY_SPI] = XFRM_TEMPLATE_BE("spi", &integer_type, 4 * BITS_PER_BYTE), +}; + +static void xfrm_expr_print(const struct expr *expr, struct output_ctx *octx) +{ + switch (expr->xfrm.direction) { + case XFRM_POLICY_IN: + nft_print(octx, "ipsec in"); + break; + case XFRM_POLICY_OUT: + nft_print(octx, "ipsec out"); + break; + default: + nft_print(octx, "ipsec (unknown dir %d)", expr->xfrm.direction); + break; + } + + if (expr->xfrm.spnum) + nft_print(octx, " spnum %u", expr->xfrm.spnum); + + switch (expr->xfrm.key) { + case NFT_XFRM_KEY_DADDR_IP4: + case NFT_XFRM_KEY_SADDR_IP4: + nft_print(octx, " ip"); + break; + case NFT_XFRM_KEY_DADDR_IP6: + case NFT_XFRM_KEY_SADDR_IP6: + nft_print(octx, " ip6"); + break; + case NFT_XFRM_KEY_REQID: + case NFT_XFRM_KEY_SPI: + break; + default: + nft_print(octx, " (unknown key 0x%x)", expr->xfrm.key); + return; + } + + nft_print(octx, " %s", xfrm_templates[expr->xfrm.key].token); +} + +static bool xfrm_expr_cmp(const struct expr *e1, const struct expr *e2) +{ + return e1->xfrm.key == e2->xfrm.key && + e1->xfrm.direction == e2->xfrm.direction && + e1->xfrm.spnum == e2->xfrm.spnum; +} + +static void xfrm_expr_clone(struct expr *new, const struct expr *expr) +{ + memcpy(&new->xfrm, &expr->xfrm, sizeof(new->xfrm)); +} + +#define NFTNL_UDATA_XFRM_KEY 0 +#define NFTNL_UDATA_XFRM_SPNUM 1 +#define NFTNL_UDATA_XFRM_DIR 2 +#define NFTNL_UDATA_XFRM_MAX 3 + +static int xfrm_expr_build_udata(struct nftnl_udata_buf *udbuf, + const struct expr *expr) +{ + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_XFRM_KEY, expr->xfrm.key); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_XFRM_SPNUM, expr->xfrm.spnum); + nftnl_udata_put_u32(udbuf, NFTNL_UDATA_XFRM_DIR, expr->xfrm.direction); + + return 0; +} + +static int xfrm_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **ud = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_XFRM_KEY: + case NFTNL_UDATA_XFRM_SPNUM: + case NFTNL_UDATA_XFRM_DIR: + if (len != sizeof(uint32_t)) + return -1; + break; + default: + return 0; + } + + ud[type] = attr; + return 0; +} + +static struct expr *xfrm_expr_parse_udata(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_XFRM_MAX + 1] = {}; + uint32_t key, dir, spnum; + int err; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + xfrm_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_XFRM_KEY] || + !ud[NFTNL_UDATA_XFRM_DIR] || + !ud[NFTNL_UDATA_XFRM_SPNUM]) + return NULL; + + key = nftnl_udata_get_u32(ud[NFTNL_UDATA_XFRM_KEY]); + dir = nftnl_udata_get_u32(ud[NFTNL_UDATA_XFRM_DIR]); + spnum = nftnl_udata_get_u32(ud[NFTNL_UDATA_XFRM_SPNUM]); + + return xfrm_expr_alloc(&internal_location, dir, spnum, key); +} + +const struct expr_ops xfrm_expr_ops = { + .type = EXPR_XFRM, + .name = "xfrm", + .print = xfrm_expr_print, + .json = xfrm_expr_json, + .cmp = xfrm_expr_cmp, + .clone = xfrm_expr_clone, + .parse_udata = xfrm_expr_parse_udata, + .build_udata = xfrm_expr_build_udata, +}; + +struct expr *xfrm_expr_alloc(const struct location *loc, + uint8_t direction, + uint8_t spnum, + enum nft_xfrm_keys key) +{ + struct expr *expr; + + expr = expr_alloc(loc, EXPR_XFRM, + xfrm_templates[key].dtype, + xfrm_templates[key].byteorder, + xfrm_templates[key].len); + + expr->xfrm.direction = direction; + expr->xfrm.spnum = spnum; + expr->xfrm.key = key; + + return expr; +} diff --git a/src/xt.c b/src/xt.c new file mode 100644 index 0000000..3cb5f02 --- /dev/null +++ b/src/xt.c @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2013-2015 Pablo Neira Ayuso <pablo@netfilter.org> + * Copyright (c) 2015 Arturo Borrero Gonzalez <arturo@debian.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 (or any + * later) as published by the Free Software Foundation. + */ + +#include <nft.h> + +#include <time.h> +#include <net/if.h> +#include <getopt.h> +#include <ctype.h> /* for isspace */ +#include <statement.h> +#include <netlink.h> +#include <xt.h> +#include <erec.h> + +#include <libmnl/libmnl.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables_compat.h> +#include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_ipv6/ip6_tables.h> +#include <linux/netfilter_arp/arp_tables.h> +#include <linux/netfilter_bridge/ebtables.h> + +#ifdef HAVE_LIBXTABLES +#include <xtables.h> + +static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af); +#endif + +void xt_stmt_xlate(const struct stmt *stmt, struct output_ctx *octx) +{ + static const char *typename[NFT_XT_MAX] = { + [NFT_XT_MATCH] = "match", + [NFT_XT_TARGET] = "target", + [NFT_XT_WATCHER] = "watcher", + }; + int rc = 0; +#ifdef HAVE_LIBXTABLES + struct xt_xlate *xl = xt_xlate_alloc(10240); + struct xtables_target *tg; + struct xt_entry_target *t; + struct xtables_match *mt; + struct xt_entry_match *m; + size_t size; + void *entry; + + xtables_set_nfproto(stmt->xt.family); + entry = xt_entry_alloc(&stmt->xt, stmt->xt.family); + + switch (stmt->xt.type) { + case NFT_XT_MATCH: + mt = xtables_find_match(stmt->xt.name, XTF_TRY_LOAD, NULL); + if (!mt) { + fprintf(octx->error_fp, + "# Warning: XT match %s not found\n", + stmt->xt.name); + break; + } + size = XT_ALIGN(sizeof(*m)) + stmt->xt.infolen; + + m = xzalloc(size); + memcpy(&m->data, stmt->xt.info, stmt->xt.infolen); + + m->u.match_size = size; + m->u.user.revision = stmt->xt.rev; + + if (mt->xlate) { + struct xt_xlate_mt_params params = { + .ip = entry, + .match = m, + .numeric = 1, + }; + + rc = mt->xlate(xl, ¶ms); + } + xfree(m); + break; + case NFT_XT_WATCHER: + case NFT_XT_TARGET: + tg = xtables_find_target(stmt->xt.name, XTF_TRY_LOAD); + if (!tg) { + fprintf(octx->error_fp, + "# Warning: XT target %s not found\n", + stmt->xt.name); + break; + } + size = XT_ALIGN(sizeof(*t)) + stmt->xt.infolen; + + t = xzalloc(size); + memcpy(&t->data, stmt->xt.info, stmt->xt.infolen); + + t->u.target_size = size; + t->u.user.revision = stmt->xt.rev; + + strcpy(t->u.user.name, tg->name); + + if (tg->xlate) { + struct xt_xlate_tg_params params = { + .ip = entry, + .target = t, + .numeric = 1, + }; + + rc = tg->xlate(xl, ¶ms); + } + xfree(t); + break; + } + + if (rc == 1) + nft_print(octx, "%s", xt_xlate_get(xl)); + xt_xlate_free(xl); + xfree(entry); +#endif + if (!rc) + nft_print(octx, "xt %s \"%s\"", + typename[stmt->xt.type], stmt->xt.name); +} + +void xt_stmt_destroy(struct stmt *stmt) +{ + xfree(stmt->xt.name); + xfree(stmt->xt.info); +} + +#ifdef HAVE_LIBXTABLES +static void *xt_entry_alloc(const struct xt_stmt *xt, uint32_t af) +{ + union nft_entry { + struct ipt_entry ipt; + struct ip6t_entry ip6t; + struct arpt_entry arpt; + struct ebt_entry ebt; + } *entry; + + entry = xmalloc(sizeof(union nft_entry)); + + switch (af) { + case NFPROTO_IPV4: + entry->ipt.ip.proto = xt->proto; + break; + case NFPROTO_IPV6: + entry->ip6t.ipv6.proto = xt->proto; + break; + case NFPROTO_BRIDGE: + entry->ebt.ethproto = xt->proto; + break; + case NFPROTO_ARP: + entry->arpt.arp.arhln_mask = 0xff; + entry->arpt.arp.arhln = 6; + break; + default: + break; + } + + return entry; +} + +static uint32_t xt_proto(const struct proto_ctx *pctx) +{ + const struct proto_desc *desc = NULL; + + if (pctx->family == NFPROTO_BRIDGE) { + desc = pctx->protocol[PROTO_BASE_NETWORK_HDR].desc; + if (desc == NULL) + return 0; + if (strcmp(desc->name, "ip") == 0) + return __constant_htons(ETH_P_IP); + if (strcmp(desc->name, "ip6") == 0) + return __constant_htons(ETH_P_IPV6); + return 0; + } + + desc = pctx->protocol[PROTO_BASE_TRANSPORT_HDR].desc; + if (desc == NULL) + return 0; + if (strcmp(desc->name, "tcp") == 0) + return IPPROTO_TCP; + else if (strcmp(desc->name, "udp") == 0) + return IPPROTO_UDP; + else if (strcmp(desc->name, "udplite") == 0) + return IPPROTO_UDPLITE; + else if (strcmp(desc->name, "sctp") == 0) + return IPPROTO_SCTP; + else if (strcmp(desc->name, "dccp") == 0) + return IPPROTO_DCCP; + else if (strcmp(desc->name, "esp") == 0) + return IPPROTO_ESP; + else if (strcmp(desc->name, "ah") == 0) + return IPPROTO_AH; + + return 0; +} +#endif + +/* + * Delinearization + */ + +void netlink_parse_match(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + const char *mtinfo; + struct stmt *stmt; + uint32_t mt_len; + + mtinfo = nftnl_expr_get(nle, NFTNL_EXPR_MT_INFO, &mt_len); + + stmt = xt_stmt_alloc(loc); + stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_MT_NAME)); + stmt->xt.type = NFT_XT_MATCH; + stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_MT_REV); + stmt->xt.family = ctx->table->handle.family; + + stmt->xt.infolen = mt_len; + stmt->xt.info = xmalloc(mt_len); + memcpy(stmt->xt.info, mtinfo, mt_len); + + ctx->table->has_xt_stmts = true; + rule_stmt_append(ctx->rule, stmt); +} + +void netlink_parse_target(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nftnl_expr *nle) +{ + const void *tginfo; + struct stmt *stmt; + uint32_t tg_len; + + tginfo = nftnl_expr_get(nle, NFTNL_EXPR_TG_INFO, &tg_len); + + stmt = xt_stmt_alloc(loc); + stmt->xt.name = strdup(nftnl_expr_get_str(nle, NFTNL_EXPR_TG_NAME)); + stmt->xt.type = NFT_XT_TARGET; + stmt->xt.rev = nftnl_expr_get_u32(nle, NFTNL_EXPR_TG_REV); + stmt->xt.family = ctx->table->handle.family; + + stmt->xt.infolen = tg_len; + stmt->xt.info = xmalloc(tg_len); + memcpy(stmt->xt.info, tginfo, tg_len); + + ctx->table->has_xt_stmts = true; + rule_stmt_append(ctx->rule, stmt); +} + +#ifdef HAVE_LIBXTABLES +static bool is_watcher(uint32_t family, struct stmt *stmt) +{ + if (family != NFPROTO_BRIDGE || + stmt->xt.type != NFT_XT_TARGET) + return false; + + /* this has to be hardcoded :-( */ + if (strcmp(stmt->xt.name, "log") == 0) + return true; + else if (strcmp(stmt->xt.name, "nflog") == 0) + return true; + + return false; +} + +void stmt_xt_postprocess(struct rule_pp_ctx *rctx, struct stmt *stmt, + struct rule *rule) +{ + struct dl_proto_ctx *dl = dl_proto_ctx(rctx); + + if (is_watcher(dl->pctx.family, stmt)) + stmt->xt.type = NFT_XT_WATCHER; + + stmt->xt.proto = xt_proto(&dl->pctx); +} + +static int nft_xt_compatible_revision(const char *name, uint8_t rev, int opt) +{ + struct mnl_socket *nl; + char buf[MNL_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + uint32_t portid, seq, type, family; + struct nfgenmsg *nfg; + int ret = 0; + + switch (opt) { + case IPT_SO_GET_REVISION_MATCH: + family = NFPROTO_IPV4; + type = 0; + break; + case IPT_SO_GET_REVISION_TARGET: + family = NFPROTO_IPV4; + type = 1; + break; + case IP6T_SO_GET_REVISION_MATCH: + family = NFPROTO_IPV6; + type = 0; + break; + case IP6T_SO_GET_REVISION_TARGET: + family = NFPROTO_IPV6; + type = 1; + break; + default: /* No revision support, assume ok */ + return 1; + } + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = (NFNL_SUBSYS_NFT_COMPAT << 8) | NFNL_MSG_COMPAT_GET; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + nlh->nlmsg_seq = seq = time(NULL); + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = family; + nfg->version = NFNETLINK_V0; + nfg->res_id = 0; + + mnl_attr_put_strz(nlh, NFTA_COMPAT_NAME, name); + mnl_attr_put_u32(nlh, NFTA_COMPAT_REV, htonl(rev)); + mnl_attr_put_u32(nlh, NFTA_COMPAT_TYPE, htonl(type)); + + nl = mnl_socket_open(NETLINK_NETFILTER); + if (nl == NULL) + return 0; + + if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) + goto err; + + portid = mnl_socket_get_portid(nl); + + if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) + goto err; + + ret = mnl_socket_recvfrom(nl, buf, sizeof(buf)); + if (ret == -1) + goto err; + + ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL); + if (ret == -1) + goto err; + +err: + mnl_socket_close(nl); + + return ret < 0 ? 0 : 1; +} + +static struct option original_opts[] = { + { }, +}; + +static struct xtables_globals xt_nft_globals = { + .program_name = "nft", + .program_version = PACKAGE_VERSION, + .orig_opts = original_opts, + .compat_rev = nft_xt_compatible_revision, +}; + +void xt_init(void) +{ + static bool init_once; + + if (!init_once) { + /* libxtables is full of global variables and cannot be used + * concurrently by multiple threads. Hence, it's fine that the + * "init_once" guard is not thread-safe either. + * Don't link against xtables if you want thread safety. + */ + init_once = true; + + /* Default to IPv4, but this changes in runtime */ + xtables_init_all(&xt_nft_globals, NFPROTO_IPV4); + } +} +#endif |