summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am123
-rw-r--r--src/Makefile.in1097
-rw-r--r--src/cache.c1295
-rw-r--r--src/cli.c275
-rw-r--r--src/cmd.c495
-rw-r--r--src/ct.c593
-rw-r--r--src/datatype.c1553
-rw-r--r--src/dccpopt.c277
-rw-r--r--src/erec.c238
-rw-r--r--src/evaluate.c5857
-rw-r--r--src/expression.c1563
-rw-r--r--src/exthdr.c610
-rw-r--r--src/fib.c202
-rw-r--r--src/gmputil.c198
-rw-r--r--src/hash.c165
-rw-r--r--src/iface.c173
-rw-r--r--src/intervals.c750
-rw-r--r--src/ipopt.c145
-rw-r--r--src/json.c2090
-rw-r--r--src/libnftables.c810
-rw-r--r--src/libnftables.map40
-rw-r--r--src/main.c554
-rw-r--r--src/mergesort.c122
-rw-r--r--src/meta.c1043
-rw-r--r--src/mini-gmp.c4412
-rw-r--r--src/misspell.c114
-rw-r--r--src/mnl.c2669
-rw-r--r--src/monitor.c1014
-rw-r--r--src/netlink.c2240
-rw-r--r--src/netlink_delinearize.c3512
-rw-r--r--src/netlink_linearize.c1789
-rw-r--r--src/nfnl_osf.c396
-rw-r--r--src/nftutils.c100
-rw-r--r--src/nftutils.h20
-rw-r--r--src/numgen.c140
-rw-r--r--src/optimize.c1406
-rw-r--r--src/osf.c84
-rw-r--r--src/owner.c182
-rw-r--r--src/parser_bison.c17491
-rw-r--r--src/parser_bison.h845
-rw-r--r--src/parser_bison.y6290
-rw-r--r--src/parser_json.c4415
-rw-r--r--src/payload.c1494
-rw-r--r--src/print.c39
-rw-r--r--src/proto.c1320
-rw-r--r--src/rt.c211
-rw-r--r--src/rule.c2799
-rw-r--r--src/scanner.c8546
-rw-r--r--src/scanner.l1326
-rw-r--r--src/sctp_chunk.c262
-rw-r--r--src/segtree.c637
-rw-r--r--src/socket.c136
-rw-r--r--src/statement.c1085
-rw-r--r--src/tcpopt.c266
-rw-r--r--src/utils.c107
-rw-r--r--src/xfrm.c184
-rw-r--r--src/xt.c377
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, &notrack_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] = &etheraddr_type,
+ [TYPE_ETHERTYPE] = &ethertype_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", &ethertype_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", &ethertype_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 != &ethertype_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, &ethertype_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", &ethertype_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", &ethertype_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", &etheraddr_type, sha),
+ [ARPHDR_SADDR_IP] = ARPHDR_TYPE("saddr ip", &ipaddr_type, spa),
+ [ARPHDR_DADDR_ETHER] = ARPHDR_TYPE("daddr ether", &etheraddr_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", &ethertype_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(&ethertype_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 = &ethertype_tbl,
+};
+
+#define ETHHDR_TEMPLATE(__name, __dtype, __member) \
+ HDR_TEMPLATE(__name, __dtype, struct ether_header, __member)
+#define ETHHDR_TYPE(__name, __member) \
+ ETHHDR_TEMPLATE(__name, &ethertype_type, __member)
+#define ETHHDR_ADDR(__name, __member) \
+ PROTO_HDR_TEMPLATE(__name, &etheraddr_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", &ethertype_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", &ethertype_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, &quota_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, &params);
+ }
+ 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, &params);
+ }
+ 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